Compare commits
2 Commits
6e353c351f
...
aecad367a0
| Author | SHA1 | Date | |
|---|---|---|---|
| aecad367a0 | |||
| 834c2bd9ee |
@ -33,6 +33,7 @@
|
|||||||
/* BCM4500 status registers */
|
/* BCM4500 status registers */
|
||||||
#define BCM_REG_STATUS 0xA2
|
#define BCM_REG_STATUS 0xA2
|
||||||
#define BCM_REG_LOCK 0xA4
|
#define BCM_REG_LOCK 0xA4
|
||||||
|
#define BCM_LOCK_BIT 0x20 /* BCM4500 lock detect bit in register 0xA4 */
|
||||||
|
|
||||||
/* BCM commands */
|
/* BCM commands */
|
||||||
#define BCM_CMD_READ 0x01
|
#define BCM_CMD_READ 0x01
|
||||||
@ -73,6 +74,14 @@
|
|||||||
#define ERR_I2C_ARB_LOST 0x03
|
#define ERR_I2C_ARB_LOST 0x03
|
||||||
#define ERR_BCM_NOT_READY 0x04
|
#define ERR_BCM_NOT_READY 0x04
|
||||||
#define ERR_BCM_TIMEOUT 0x05
|
#define ERR_BCM_TIMEOUT 0x05
|
||||||
|
#define ERR_TUNE_FAIL 0x06
|
||||||
|
#define ERR_EP0_TIMEOUT 0x07
|
||||||
|
#define ERR_GPIF_TIMEOUT 0x08
|
||||||
|
#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 */
|
/* configuration status byte bits */
|
||||||
#define BM_STARTED 0x01
|
#define BM_STARTED 0x01
|
||||||
@ -136,6 +145,11 @@ static __xdata BYTE sd_had_sync; /* had sync in previous poll */
|
|||||||
/* Main loop timing: USB frame counter for periodic tasks */
|
/* Main loop timing: USB frame counter for periodic tasks */
|
||||||
static __xdata WORD hp_last_frame; /* frame counter at last I2C scan */
|
static __xdata WORD hp_last_frame; /* frame counter at last I2C scan */
|
||||||
|
|
||||||
|
/* Software watchdog: Timer0 ISR decrements; on zero, LNB power is cut.
|
||||||
|
* volatile: shared between ISR (interrupt 1) and main loop. */
|
||||||
|
static volatile __xdata BYTE wdt_counter;
|
||||||
|
static volatile __xdata BYTE wdt_armed;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BCM4500 register initialization data extracted from stock v2.06 firmware.
|
* BCM4500 register initialization data extracted from stock v2.06 firmware.
|
||||||
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
|
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
|
||||||
@ -163,6 +177,8 @@ static const __code BYTE bcm_init_block2[] = {
|
|||||||
* SCL low (clock stretching), the master waits forever without this.
|
* SCL low (clock stretching), the master waits forever without this.
|
||||||
*/
|
*/
|
||||||
#define I2C_TIMEOUT 6000
|
#define I2C_TIMEOUT 6000
|
||||||
|
#define GPIF_TIMEOUT 60000 /* GPIF idle wait (~15ms at 4MHz tick) */
|
||||||
|
#define EP2_TIMEOUT 60000 /* EP2 drain wait */
|
||||||
|
|
||||||
static BOOL i2c_wait_done(void) {
|
static BOOL i2c_wait_done(void) {
|
||||||
WORD timeout = I2C_TIMEOUT;
|
WORD timeout = I2C_TIMEOUT;
|
||||||
@ -178,8 +194,25 @@ static BOOL i2c_wait_done(void) {
|
|||||||
static BOOL i2c_wait_stop(void) {
|
static BOOL i2c_wait_stop(void) {
|
||||||
WORD timeout = I2C_TIMEOUT;
|
WORD timeout = I2C_TIMEOUT;
|
||||||
while (I2CS & bmSTOP) {
|
while (I2CS & bmSTOP) {
|
||||||
if (--timeout == 0)
|
if (--timeout == 0) {
|
||||||
|
last_error = ERR_I2C_TIMEOUT;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for EP0 data phase to complete (host -> device transfer).
|
||||||
|
* Replaces bare `while (EP0CS & bmEPBUSY)` spin loops with timeout.
|
||||||
|
*/
|
||||||
|
static BOOL ep0_wait_data(void) {
|
||||||
|
WORD timeout = I2C_TIMEOUT;
|
||||||
|
while (EP0CS & bmEPBUSY) {
|
||||||
|
if (--timeout == 0) {
|
||||||
|
last_error = ERR_EP0_TIMEOUT;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -314,6 +347,39 @@ fail:
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Software watchdog ---------- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kick the watchdog timer — resets the countdown to ~2 seconds.
|
||||||
|
* Must be called from the main loop at least once per period.
|
||||||
|
*/
|
||||||
|
static void wdt_kick(void) {
|
||||||
|
if (wdt_armed == 1) wdt_counter = 122;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize Timer0 as a ~16ms periodic interrupt for the software
|
||||||
|
* watchdog. At 48MHz/12 = 4MHz timer clock with 16-bit overflow,
|
||||||
|
* period = 65536 / 4MHz = 16.384ms. 122 decrements × 16.384ms ≈ 2s.
|
||||||
|
*
|
||||||
|
* Mode 1 (16-bit) does NOT auto-reload. After overflow the counter
|
||||||
|
* wraps to 0x0000 and keeps counting — which is exactly the reload
|
||||||
|
* value we want, so no manual reload is needed in the ISR. If the
|
||||||
|
* start value is ever changed to non-zero, add a reload in the ISR.
|
||||||
|
*
|
||||||
|
* CKCON bit 3 (T0M) controls Timer0 clock; bit 5 (T2M) is Timer2.
|
||||||
|
* DiSEqC uses Timer2 — do not touch bit 3 from DiSEqC code.
|
||||||
|
*/
|
||||||
|
static void wdt_init(void) {
|
||||||
|
TMOD = (TMOD & 0xF0) | 0x01; /* Timer0 Mode 1 (16-bit) */
|
||||||
|
CKCON &= ~0x08; /* Timer0 clk = 48MHz/12 = 4MHz (bit 3 only) */
|
||||||
|
TH0 = 0x00; TL0 = 0x00; /* full-count: 0x0000 to 0xFFFF = 16.384ms */
|
||||||
|
wdt_armed = 1;
|
||||||
|
wdt_kick();
|
||||||
|
ET0 = 1; /* Enable Timer0 interrupt */
|
||||||
|
TR0 = 1; /* Start Timer0 */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write one byte to a BCM4500 direct I2C register (subaddr).
|
* Write one byte to a BCM4500 direct I2C register (subaddr).
|
||||||
*/
|
*/
|
||||||
@ -522,13 +588,8 @@ static void bcm4500_shutdown(void) {
|
|||||||
static void i2c_hotplug_scan(void) {
|
static void i2c_hotplug_scan(void) {
|
||||||
static __xdata BYTE hp_a, hp_byte, hp_bit, hp_diff;
|
static __xdata BYTE hp_a, hp_byte, hp_bit, hp_diff;
|
||||||
|
|
||||||
/* Save current as previous before starting new scan.
|
/* Clear current scan buffer (hp_prev retains last SUCCESSFUL scan
|
||||||
* This keeps hp_prev valid between scans so the host can read
|
* so aborted scans don't corrupt the comparison baseline) */
|
||||||
* 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 */
|
|
||||||
for (hp_a = 0; hp_a < 16; hp_a++)
|
for (hp_a = 0; hp_a < 16; hp_a++)
|
||||||
hp_curr[hp_a] = 0;
|
hp_curr[hp_a] = 0;
|
||||||
|
|
||||||
@ -570,13 +631,19 @@ static void i2c_hotplug_scan(void) {
|
|||||||
/* Count per-device changes (not per-byte) */
|
/* Count per-device changes (not per-byte) */
|
||||||
hp_byte = hp_diff;
|
hp_byte = hp_diff;
|
||||||
while (hp_byte) {
|
while (hp_byte) {
|
||||||
hp_changes++;
|
if (hp_changes < 0xFFFF) hp_changes++;
|
||||||
hp_byte &= (hp_byte - 1);
|
hp_byte &= (hp_byte - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
hp_scan_ok = 1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -613,22 +680,23 @@ static void stream_diag_poll(void) {
|
|||||||
|
|
||||||
/* Rate-limited BCM4500 I2C reads for sync tracking.
|
/* Rate-limited BCM4500 I2C reads for sync tracking.
|
||||||
* Only attempt if BCM is booted and interval elapsed. */
|
* 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) &&
|
if ((config_status & BM_FW_LOADED) &&
|
||||||
((WORD)sd_poll_count & (SD_I2C_INTERVAL - 1)) == 0) {
|
((WORD)sd_poll_count & (SD_I2C_INTERVAL - 1)) == 0) {
|
||||||
sd_rd[0] = 0;
|
sd_rd[0] = 0;
|
||||||
sd_rd[1] = 0;
|
sd_rd[1] = 0;
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_STATUS, 1, &sd_rd[0]);
|
if (i2c_combined_read(BCM4500_ADDR, BCM_REG_STATUS, 1, &sd_rd[0]))
|
||||||
i2c_combined_read(BCM4500_ADDR, BCM_REG_LOCK, 1, &sd_rd[1]);
|
sd_last_status = sd_rd[0];
|
||||||
|
if (i2c_combined_read(BCM4500_ADDR, BCM_REG_LOCK, 1, &sd_rd[1]))
|
||||||
sd_last_status = sd_rd[0];
|
sd_last_lock = sd_rd[1];
|
||||||
sd_last_lock = sd_rd[1];
|
|
||||||
|
|
||||||
/* Detect sync loss: had lock (bit 5) previously, lost it now */
|
/* Detect sync loss: had lock (bit 5) previously, lost it now */
|
||||||
if (sd_had_sync && !(sd_last_lock & 0x20)) {
|
if (sd_had_sync && !(sd_last_lock & BCM_LOCK_BIT)) {
|
||||||
if (sd_sync_loss < 0xFFFF)
|
if (sd_sync_loss < 0xFFFF)
|
||||||
sd_sync_loss++;
|
sd_sync_loss++;
|
||||||
}
|
}
|
||||||
sd_had_sync = (sd_last_lock & 0x20) ? 1 : 0;
|
sd_had_sync = (sd_last_lock & BCM_LOCK_BIT) ? 1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,9 +732,18 @@ static void gpif_start(void) {
|
|||||||
/* Assert P3.5 low (BCM4500 TS enable) briefly */
|
/* Assert P3.5 low (BCM4500 TS enable) briefly */
|
||||||
IOD &= ~0x20;
|
IOD &= ~0x20;
|
||||||
|
|
||||||
/* Wait for GPIF idle */
|
/* Wait for GPIF idle with timeout */
|
||||||
while (!(GPIFTRIG & 0x80))
|
{
|
||||||
;
|
WORD gp_timeout = GPIF_TIMEOUT;
|
||||||
|
while (!(GPIFTRIG & 0x80)) {
|
||||||
|
if (--gp_timeout == 0) {
|
||||||
|
last_error = ERR_GPIF_TIMEOUT;
|
||||||
|
IOD |= 0x20;
|
||||||
|
config_status &= ~BM_ARMED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IOD |= 0x20;
|
IOD |= 0x20;
|
||||||
|
|
||||||
@ -688,9 +765,16 @@ static void gpif_stop(void) {
|
|||||||
EP2FIFOBCH = 0xFF;
|
EP2FIFOBCH = 0xFF;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
|
|
||||||
/* Wait for GPIF idle */
|
/* Wait for GPIF idle with timeout */
|
||||||
while (!(GPIFTRIG & 0x80))
|
{
|
||||||
;
|
WORD gp_timeout = GPIF_TIMEOUT;
|
||||||
|
while (!(GPIFTRIG & 0x80)) {
|
||||||
|
if (--gp_timeout == 0) {
|
||||||
|
last_error = ERR_GPIF_TIMEOUT;
|
||||||
|
break; /* proceed with cleanup regardless */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Skip/discard partial EP2 packet */
|
/* Skip/discard partial EP2 packet */
|
||||||
OUTPKTEND = 0x82;
|
OUTPKTEND = 0x82;
|
||||||
@ -702,6 +786,10 @@ static void gpif_stop(void) {
|
|||||||
IOD |= 0xE0;
|
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 ---------- */
|
/* ---------- DiSEqC tone burst ---------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -712,45 +800,31 @@ static void gpif_stop(void) {
|
|||||||
* Uses Timer2 for timing as the stock firmware does.
|
* Uses Timer2 for timing as the stock firmware does.
|
||||||
*/
|
*/
|
||||||
static void diseqc_tone_burst(BYTE sat_b) {
|
static void diseqc_tone_burst(BYTE sat_b) {
|
||||||
BYTE i;
|
if (sat_b) { last_error = ERR_NOT_SUPPORTED; return; }
|
||||||
|
|
||||||
(void)sat_b; /* both A and B send 22kHz burst for now */
|
/* Configure Timer2 auto-reload.
|
||||||
|
* CKCON bit 5 (T2M) only — do not touch bit 3 (Timer0/watchdog). */
|
||||||
/* Configure Timer2 auto-reload */
|
|
||||||
/* CKCON.T2M = 0 -> Timer2 clk = 48MHz/12 = 4MHz */
|
|
||||||
CKCON &= ~0x20;
|
CKCON &= ~0x20;
|
||||||
T2CON = 0x04; /* auto-reload, running */
|
T2CON = 0x04; /* auto-reload, running */
|
||||||
RCAP2H = 0xF8;
|
RCAP2H = 0xF8;
|
||||||
RCAP2L = 0x2F; /* reload = 63535 -> ~500us tick */
|
RCAP2L = 0x2F; /* reload = 63535 -> ~500us tick */
|
||||||
TL2 = 0xFF;
|
TL2 = 0xFF;
|
||||||
TH2 = 0xFF; /* force immediate overflow */
|
TH2 = 0xFF; /* force immediate overflow */
|
||||||
|
TF2 = 0;
|
||||||
|
|
||||||
/* Pre-burst settling: 15 ticks (~7.5ms) with carrier off */
|
/* Pre-burst settling: 15 ticks (~7.5ms) with carrier off */
|
||||||
IOA &= ~PIN_22KHZ;
|
IOA &= ~PIN_22KHZ;
|
||||||
TF2 = 0;
|
diseqc_wait_ticks(15);
|
||||||
for (i = 0; i < 15; i++) {
|
|
||||||
while (!TF2)
|
|
||||||
;
|
|
||||||
TF2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst: 25 ticks (~12.5ms) with carrier on */
|
/* Burst: 25 ticks (~12.5ms) with carrier on */
|
||||||
IOA |= PIN_22KHZ;
|
IOA |= PIN_22KHZ;
|
||||||
for (i = 0; i < 25; i++) {
|
diseqc_wait_ticks(25);
|
||||||
while (!TF2)
|
|
||||||
;
|
|
||||||
TF2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Carrier off */
|
/* Carrier off */
|
||||||
IOA &= ~PIN_22KHZ;
|
IOA &= ~PIN_22KHZ;
|
||||||
|
|
||||||
/* Post-burst settling: 5 ticks (~2.5ms) */
|
/* Post-burst settling: 5 ticks (~2.5ms) */
|
||||||
for (i = 0; i < 5; i++) {
|
diseqc_wait_ticks(5);
|
||||||
while (!TF2)
|
|
||||||
;
|
|
||||||
TF2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop Timer2 */
|
/* Stop Timer2 */
|
||||||
TR2 = 0;
|
TR2 = 0;
|
||||||
@ -774,9 +848,15 @@ static void diseqc_tone_burst(BYTE sat_b) {
|
|||||||
|
|
||||||
static void diseqc_wait_ticks(BYTE count) {
|
static void diseqc_wait_ticks(BYTE count) {
|
||||||
static __xdata BYTE dt_i;
|
static __xdata BYTE dt_i;
|
||||||
|
WORD dt_timeout;
|
||||||
for (dt_i = 0; dt_i < count; dt_i++) {
|
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;
|
TF2 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -822,8 +902,10 @@ static void diseqc_send_byte(BYTE val) {
|
|||||||
static void diseqc_send_message(BYTE len) {
|
static void diseqc_send_message(BYTE len) {
|
||||||
static __xdata BYTE dm_i, dm_saved_tone;
|
static __xdata BYTE dm_i, dm_saved_tone;
|
||||||
|
|
||||||
if (len < 3 || len > 6)
|
if (len < 3 || len > 6) {
|
||||||
|
last_error = ERR_DISEQC_LEN;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Save current 22 kHz tone state */
|
/* Save current 22 kHz tone state */
|
||||||
dm_saved_tone = IOA & PIN_22KHZ;
|
dm_saved_tone = IOA & PIN_22KHZ;
|
||||||
@ -874,6 +956,10 @@ static void diseqc_send_message(BYTE len) {
|
|||||||
IOA &= ~PIN_22KHZ;
|
IOA &= ~PIN_22KHZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Forward declaration: do_tune() is defined after the sweep functions
|
||||||
|
* but called from do_param_sweep(). */
|
||||||
|
static void do_tune(void);
|
||||||
|
|
||||||
/* ---------- Parameterized sweep (0xBA) ---------- */
|
/* ---------- Parameterized sweep (0xBA) ---------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -918,6 +1004,7 @@ static void do_param_sweep(void) {
|
|||||||
ps_cur = ps_start;
|
ps_cur = ps_start;
|
||||||
|
|
||||||
while (ps_cur <= ps_stop) {
|
while (ps_cur <= ps_stop) {
|
||||||
|
wdt_kick(); /* sweep is progressing, not hung */
|
||||||
/*
|
/*
|
||||||
* Set up a tune payload in EP0BUF for do_tune():
|
* Set up a tune payload in EP0BUF for do_tune():
|
||||||
* [0..3] = symbol_rate (LE), [4..7] = freq (LE), [8] = mod, [9] = fec
|
* [0..3] = symbol_rate (LE), [4..7] = freq (LE), [8] = mod, [9] = fec
|
||||||
@ -957,8 +1044,15 @@ static void do_param_sweep(void) {
|
|||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
ps_buf_idx = 0;
|
ps_buf_idx = 0;
|
||||||
|
|
||||||
while (EP2CS & bmEPFULL)
|
{
|
||||||
;
|
WORD ep2_to = EP2_TIMEOUT;
|
||||||
|
while (EP2CS & bmEPFULL) {
|
||||||
|
if (--ep2_to == 0) {
|
||||||
|
last_error = ERR_EP2_TIMEOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ps_cur += ps_step;
|
ps_cur += ps_step;
|
||||||
@ -1016,20 +1110,30 @@ static BOOL do_adaptive_blind_scan(void) {
|
|||||||
|
|
||||||
abs_sr_cur = abs_sr_min;
|
abs_sr_cur = abs_sr_min;
|
||||||
while (abs_sr_cur <= abs_sr_max) {
|
while (abs_sr_cur <= abs_sr_max) {
|
||||||
|
wdt_kick(); /* scan is progressing, not hung */
|
||||||
/* Program SR and frequency into BCM4500 */
|
/* Program SR and frequency into BCM4500 */
|
||||||
i2c_buf[0] = (BYTE)(abs_sr_cur >> 24);
|
i2c_buf[0] = (BYTE)(abs_sr_cur >> 24);
|
||||||
i2c_buf[1] = (BYTE)(abs_sr_cur >> 16);
|
i2c_buf[1] = (BYTE)(abs_sr_cur >> 16);
|
||||||
i2c_buf[2] = (BYTE)(abs_sr_cur >> 8);
|
i2c_buf[2] = (BYTE)(abs_sr_cur >> 8);
|
||||||
i2c_buf[3] = (BYTE)(abs_sr_cur);
|
i2c_buf[3] = (BYTE)(abs_sr_cur);
|
||||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
if (!bcm_indirect_write_block(0x00, i2c_buf, 4)) {
|
||||||
|
abs_sr_cur += abs_sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
i2c_buf[0] = (BYTE)(abs_freq >> 24);
|
i2c_buf[0] = (BYTE)(abs_freq >> 24);
|
||||||
i2c_buf[1] = (BYTE)(abs_freq >> 16);
|
i2c_buf[1] = (BYTE)(abs_freq >> 16);
|
||||||
i2c_buf[2] = (BYTE)(abs_freq >> 8);
|
i2c_buf[2] = (BYTE)(abs_freq >> 8);
|
||||||
i2c_buf[3] = (BYTE)(abs_freq);
|
i2c_buf[3] = (BYTE)(abs_freq);
|
||||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
if (!bcm_indirect_write_block(0x00, i2c_buf, 4)) {
|
||||||
|
abs_sr_cur += abs_sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
|
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE)) {
|
||||||
|
abs_sr_cur += abs_sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Quick AGC pre-check if enabled */
|
/* Quick AGC pre-check if enabled */
|
||||||
if (abs_quick_dwell > 0) {
|
if (abs_quick_dwell > 0) {
|
||||||
@ -1056,7 +1160,7 @@ static BOOL do_adaptive_blind_scan(void) {
|
|||||||
/* Check lock */
|
/* Check lock */
|
||||||
abs_lock_val = 0;
|
abs_lock_val = 0;
|
||||||
bcm_direct_read(BCM_REG_LOCK, &abs_lock_val);
|
bcm_direct_read(BCM_REG_LOCK, &abs_lock_val);
|
||||||
if (abs_lock_val & 0x20) {
|
if (abs_lock_val & BCM_LOCK_BIT) {
|
||||||
EP0BUF[0] = (BYTE)(abs_freq);
|
EP0BUF[0] = (BYTE)(abs_freq);
|
||||||
EP0BUF[1] = (BYTE)(abs_freq >> 8);
|
EP0BUF[1] = (BYTE)(abs_freq >> 8);
|
||||||
EP0BUF[2] = (BYTE)(abs_freq >> 16);
|
EP0BUF[2] = (BYTE)(abs_freq >> 16);
|
||||||
@ -1120,6 +1224,7 @@ static void do_spectrum_sweep(void) {
|
|||||||
cur_freq = start_freq;
|
cur_freq = start_freq;
|
||||||
|
|
||||||
while (cur_freq <= stop_freq) {
|
while (cur_freq <= stop_freq) {
|
||||||
|
wdt_kick(); /* sweep is progressing, not hung */
|
||||||
/*
|
/*
|
||||||
* Program frequency into BCM4500 via indirect write.
|
* Program frequency into BCM4500 via indirect write.
|
||||||
* The BCM4500 expects big-endian frequency bytes at page 0.
|
* The BCM4500 expects big-endian frequency bytes at page 0.
|
||||||
@ -1129,7 +1234,10 @@ static void do_spectrum_sweep(void) {
|
|||||||
i2c_buf[1] = (BYTE)(cur_freq >> 16);
|
i2c_buf[1] = (BYTE)(cur_freq >> 16);
|
||||||
i2c_buf[2] = (BYTE)(cur_freq >> 8);
|
i2c_buf[2] = (BYTE)(cur_freq >> 8);
|
||||||
i2c_buf[3] = (BYTE)(cur_freq);
|
i2c_buf[3] = (BYTE)(cur_freq);
|
||||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
if (!bcm_indirect_write_block(0x00, i2c_buf, 4)) {
|
||||||
|
cur_freq += step_khz;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for demod to settle */
|
/* Wait for demod to settle */
|
||||||
delay(10);
|
delay(10);
|
||||||
@ -1155,8 +1263,15 @@ static void do_spectrum_sweep(void) {
|
|||||||
ss_buf_idx = 0;
|
ss_buf_idx = 0;
|
||||||
|
|
||||||
/* Wait for the buffer to be taken by host */
|
/* Wait for the buffer to be taken by host */
|
||||||
while (EP2CS & bmEPFULL)
|
{
|
||||||
;
|
WORD ep2_to = EP2_TIMEOUT;
|
||||||
|
while (EP2CS & bmEPFULL) {
|
||||||
|
if (--ep2_to == 0) {
|
||||||
|
last_error = ERR_EP2_TIMEOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_freq += step_khz;
|
cur_freq += step_khz;
|
||||||
@ -1212,6 +1327,7 @@ static BOOL do_blind_scan(void) {
|
|||||||
|
|
||||||
sr_cur = sr_min;
|
sr_cur = sr_min;
|
||||||
while (sr_cur <= sr_max) {
|
while (sr_cur <= sr_max) {
|
||||||
|
wdt_kick(); /* scan is progressing, not hung */
|
||||||
/*
|
/*
|
||||||
* Program frequency (BE) and symbol rate (BE) into BCM4500.
|
* Program frequency (BE) and symbol rate (BE) into BCM4500.
|
||||||
* We write both in a single block: 4 bytes SR + 4 bytes freq.
|
* We write both in a single block: 4 bytes SR + 4 bytes freq.
|
||||||
@ -1220,16 +1336,25 @@ static BOOL do_blind_scan(void) {
|
|||||||
i2c_buf[1] = (BYTE)(sr_cur >> 16);
|
i2c_buf[1] = (BYTE)(sr_cur >> 16);
|
||||||
i2c_buf[2] = (BYTE)(sr_cur >> 8);
|
i2c_buf[2] = (BYTE)(sr_cur >> 8);
|
||||||
i2c_buf[3] = (BYTE)(sr_cur);
|
i2c_buf[3] = (BYTE)(sr_cur);
|
||||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
if (!bcm_indirect_write_block(0x00, i2c_buf, 4)) {
|
||||||
|
sr_cur += sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
i2c_buf[0] = (BYTE)(freq_khz >> 24);
|
i2c_buf[0] = (BYTE)(freq_khz >> 24);
|
||||||
i2c_buf[1] = (BYTE)(freq_khz >> 16);
|
i2c_buf[1] = (BYTE)(freq_khz >> 16);
|
||||||
i2c_buf[2] = (BYTE)(freq_khz >> 8);
|
i2c_buf[2] = (BYTE)(freq_khz >> 8);
|
||||||
i2c_buf[3] = (BYTE)(freq_khz);
|
i2c_buf[3] = (BYTE)(freq_khz);
|
||||||
bcm_indirect_write_block(0x00, i2c_buf, 4);
|
if (!bcm_indirect_write_block(0x00, i2c_buf, 4)) {
|
||||||
|
sr_cur += sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Issue tune command */
|
/* Issue tune command */
|
||||||
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
|
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE)) {
|
||||||
|
sr_cur += sr_step;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for acquisition attempt */
|
/* Wait for acquisition attempt */
|
||||||
delay(100);
|
delay(100);
|
||||||
@ -1237,7 +1362,7 @@ static BOOL do_blind_scan(void) {
|
|||||||
/* Check lock */
|
/* Check lock */
|
||||||
bs_lock_val = 0;
|
bs_lock_val = 0;
|
||||||
bcm_direct_read(BCM_REG_LOCK, &bs_lock_val);
|
bcm_direct_read(BCM_REG_LOCK, &bs_lock_val);
|
||||||
if (bs_lock_val & 0x20) {
|
if (bs_lock_val & BCM_LOCK_BIT) {
|
||||||
/* Locked -- report back via EP0 */
|
/* Locked -- report back via EP0 */
|
||||||
EP0BUF[0] = (BYTE)(freq_khz);
|
EP0BUF[0] = (BYTE)(freq_khz);
|
||||||
EP0BUF[1] = (BYTE)(freq_khz >> 8);
|
EP0BUF[1] = (BYTE)(freq_khz >> 8);
|
||||||
@ -1276,8 +1401,10 @@ static void do_tune(void) {
|
|||||||
static __xdata BYTE tune_i;
|
static __xdata BYTE tune_i;
|
||||||
static __xdata BYTE tune_data[13]; /* 12 data + 1 scratch for reg addr */
|
static __xdata BYTE tune_data[13]; /* 12 data + 1 scratch for reg addr */
|
||||||
|
|
||||||
if (!(config_status & BM_STARTED))
|
if (!(config_status & BM_STARTED)) {
|
||||||
|
last_error = ERR_BCM_NOT_READY;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Byte-reverse symbol rate (LE->BE) into tune_data[0..3]
|
* Byte-reverse symbol rate (LE->BE) into tune_data[0..3]
|
||||||
@ -1309,17 +1436,21 @@ static void do_tune(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Poll BCM4500 for readiness */
|
/* Poll BCM4500 for readiness */
|
||||||
bcm_poll_ready();
|
if (!bcm_poll_ready())
|
||||||
|
return;
|
||||||
|
|
||||||
/* Write page 0 */
|
/* Write page 0 */
|
||||||
bcm_direct_write(BCM_REG_PAGE, 0x00);
|
if (!bcm_direct_write(BCM_REG_PAGE, 0x00))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Write all configuration data to BCM4500 data register */
|
/* Write all 12 configuration bytes to BCM4500 data register (0xA7).
|
||||||
tune_data[12] = BCM_REG_DATA; /* borrow byte past data (safe: 13 bytes in xdata) */
|
* Uses our timeout-protected multi-byte write instead of fx2lib i2c_write(). */
|
||||||
i2c_write(BCM4500_ADDR, 1, &tune_data[12], 12, tune_data);
|
if (!i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, 12, tune_data))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Execute indirect write */
|
/* Execute indirect write */
|
||||||
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE);
|
if (!bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Wait for command completion */
|
/* Wait for command completion */
|
||||||
bcm_poll_ready();
|
bcm_poll_ready();
|
||||||
@ -1355,8 +1486,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
/* EP0 data phase: wait for 10 bytes from host */
|
/* EP0 data phase: wait for 10 bytes from host */
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 10) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_tune();
|
do_tune();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1370,6 +1501,9 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
EP0BCL = 6;
|
EP0BCL = 6;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
/* Zero-fill before reads so I2C failures return zeros, not stale data */
|
||||||
|
EP0BUF[0] = 0; EP0BUF[1] = 0; EP0BUF[2] = 0;
|
||||||
|
EP0BUF[3] = 0; EP0BUF[4] = 0; EP0BUF[5] = 0;
|
||||||
/* Read signal quality via indirect registers */
|
/* Read signal quality via indirect registers */
|
||||||
bcm_indirect_read(0x00, &EP0BUF[0]);
|
bcm_indirect_read(0x00, &EP0BUF[0]);
|
||||||
bcm_indirect_read(0x01, &EP0BUF[1]);
|
bcm_indirect_read(0x01, &EP0BUF[1]);
|
||||||
@ -1549,8 +1683,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
/* EP0 data phase: receive message bytes */
|
/* EP0 data phase: receive message bytes */
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < (BYTE)wlen) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
/* Copy message from EP0BUF to diseqc_msg buffer */
|
/* Copy message from EP0BUF to diseqc_msg buffer */
|
||||||
{
|
{
|
||||||
BYTE di;
|
BYTE di;
|
||||||
@ -1575,10 +1709,10 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
|
|
||||||
/* 0x92: GET_FW_VERS -- return firmware version and build date */
|
/* 0x92: GET_FW_VERS -- return firmware version and build date */
|
||||||
case GET_FW_VERS:
|
case GET_FW_VERS:
|
||||||
EP0BUF[0] = 0x00; /* patch -> version 3.04.0 */
|
EP0BUF[0] = 0x00; /* patch -> version 3.05.0 */
|
||||||
EP0BUF[1] = 0x04; /* minor */
|
EP0BUF[1] = 0x05; /* minor */
|
||||||
EP0BUF[2] = 0x03; /* major */
|
EP0BUF[2] = 0x03; /* major */
|
||||||
EP0BUF[3] = 0x0F; /* day = 15 */
|
EP0BUF[3] = 0x10; /* day = 16 */
|
||||||
EP0BUF[4] = 0x02; /* month = 2 */
|
EP0BUF[4] = 0x02; /* month = 2 */
|
||||||
EP0BUF[5] = 0x1A; /* year - 2000 = 26 */
|
EP0BUF[5] = 0x1A; /* year - 2000 = 26 */
|
||||||
EP0BCH = 0;
|
EP0BCH = 0;
|
||||||
@ -1597,8 +1731,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
/* EP0 data phase: wait for 10 bytes from host */
|
/* EP0 data phase: wait for 10 bytes from host */
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 10) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_spectrum_sweep();
|
do_spectrum_sweep();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1624,8 +1758,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
/* EP0 data phase: wait for 16 bytes from host */
|
/* EP0 data phase: wait for 16 bytes from host */
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 16) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_blind_scan();
|
do_blind_scan();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1640,8 +1774,7 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
/* Try START + address + write, see if ACK comes back */
|
/* Try START + address + write, see if ACK comes back */
|
||||||
I2CS |= bmSTART;
|
I2CS |= bmSTART;
|
||||||
I2DAT = a << 1; /* write direction */
|
I2DAT = a << 1; /* write direction */
|
||||||
while (!(I2CS & bmDONE))
|
if (!i2c_wait_done()) break;
|
||||||
;
|
|
||||||
if (I2CS & bmACK) {
|
if (I2CS & bmACK) {
|
||||||
/* Device responded at this address */
|
/* Device responded at this address */
|
||||||
byte_idx = a >> 3;
|
byte_idx = a >> 3;
|
||||||
@ -1649,8 +1782,7 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
EP0BUF[byte_idx] |= (1 << bit);
|
EP0BUF[byte_idx] |= (1 << bit);
|
||||||
}
|
}
|
||||||
I2CS |= bmSTOP;
|
I2CS |= bmSTOP;
|
||||||
while (I2CS & bmSTOP)
|
if (!i2c_wait_stop()) break;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
EP0BCH = 0;
|
EP0BCH = 0;
|
||||||
EP0BCL = 16;
|
EP0BCL = 16;
|
||||||
@ -1739,6 +1871,9 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
* in a single USB transfer instead of 3 separate reads. */
|
* in a single USB transfer instead of 3 separate reads. */
|
||||||
case SIGNAL_MONITOR: {
|
case SIGNAL_MONITOR: {
|
||||||
BYTE sm_val;
|
BYTE sm_val;
|
||||||
|
/* Zero-fill before reads so I2C failures return zeros, not stale data */
|
||||||
|
EP0BUF[0] = 0; EP0BUF[1] = 0; EP0BUF[2] = 0;
|
||||||
|
EP0BUF[3] = 0; EP0BUF[4] = 0; EP0BUF[5] = 0;
|
||||||
/* SNR: indirect regs 0x00-0x01 */
|
/* SNR: indirect regs 0x00-0x01 */
|
||||||
bcm_indirect_read(0x00, &EP0BUF[0]);
|
bcm_indirect_read(0x00, &EP0BUF[0]);
|
||||||
bcm_indirect_read(0x01, &EP0BUF[1]);
|
bcm_indirect_read(0x01, &EP0BUF[1]);
|
||||||
@ -1778,8 +1913,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
BYTE dwell = (BYTE)wval;
|
BYTE dwell = (BYTE)wval;
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 10) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_tune();
|
do_tune();
|
||||||
if (dwell > 0)
|
if (dwell > 0)
|
||||||
delay(dwell);
|
delay(dwell);
|
||||||
@ -1822,8 +1957,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
case PARAM_SWEEP:
|
case PARAM_SWEEP:
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 16) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_param_sweep();
|
do_param_sweep();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1831,8 +1966,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
case ADAPTIVE_BLIND_SCAN:
|
case ADAPTIVE_BLIND_SCAN:
|
||||||
EP0BCL = 0;
|
EP0BCL = 0;
|
||||||
SYNCDELAY;
|
SYNCDELAY;
|
||||||
while (EP0CS & bmEPBUSY)
|
if (!ep0_wait_data()) return TRUE;
|
||||||
;
|
if (EP0BCL < 18) { last_error = ERR_EP0_TIMEOUT; return TRUE; }
|
||||||
do_adaptive_blind_scan();
|
do_adaptive_blind_scan();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -1866,7 +2001,9 @@ BOOL handle_vendorcommand(BYTE cmd) {
|
|||||||
EP0BUF[9] = sd_last_lock;
|
EP0BUF[9] = sd_last_lock;
|
||||||
EP0BUF[10] = (config_status & BM_ARMED) ? 1 : 0;
|
EP0BUF[10] = (config_status & BM_ARMED) ? 1 : 0;
|
||||||
EP0BUF[11] = sd_had_sync;
|
EP0BUF[11] = sd_had_sync;
|
||||||
/* Reset counters if wValue=1 */
|
/* Reset counters if wValue=1.
|
||||||
|
* Non-atomic read+reset is safe: single-threaded main loop,
|
||||||
|
* ISR only sets got_sud (never touches diag counters). */
|
||||||
if (wval == 1) {
|
if (wval == 1) {
|
||||||
sd_poll_count = 0;
|
sd_poll_count = 0;
|
||||||
sd_overflow_count = 0;
|
sd_overflow_count = 0;
|
||||||
@ -1960,6 +2097,27 @@ void hispeed_isr(void) __interrupt (HISPEED_ISR) {
|
|||||||
CLEAR_HISPEED();
|
CLEAR_HISPEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Software watchdog Timer0 ISR: fires every ~16.384ms.
|
||||||
|
* If the main loop stops kicking, cut LNB power for safety.
|
||||||
|
* wdt_armed states: 0=off, 1=armed, 2=fired (power was cut). */
|
||||||
|
void timer0_isr(void) __interrupt (1) {
|
||||||
|
if (wdt_armed != 1) return;
|
||||||
|
if (wdt_counter > 0) {
|
||||||
|
wdt_counter--;
|
||||||
|
} else {
|
||||||
|
/* Main loop stalled — cut LNB power for safety.
|
||||||
|
* IOA RMW race note: if the main loop is genuinely hung (which
|
||||||
|
* it must be for wdt_counter to reach 0), it is not executing
|
||||||
|
* IOA modifications. If by rare coincidence we interrupt an IOA
|
||||||
|
* RMW, the main loop's stale write re-enables power — but then
|
||||||
|
* wdt_armed=2 prevents wdt_kick() from rearming, so the next
|
||||||
|
* ISR tick exits immediately and the main loop's own guard
|
||||||
|
* checks (BM_STARTED etc.) will prevent further I2C activity. */
|
||||||
|
IOA = (IOA & ~PIN_PWR_EN) | PIN_PWR_DIS;
|
||||||
|
wdt_armed = 2; /* fired — main loop must not re-arm */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Main ---------- */
|
/* ---------- Main ---------- */
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
@ -2048,7 +2206,20 @@ void main(void) {
|
|||||||
|
|
||||||
EA = 1; /* global interrupt enable */
|
EA = 1; /* global interrupt enable */
|
||||||
|
|
||||||
|
wdt_init(); /* start software watchdog (~2s timeout) */
|
||||||
|
|
||||||
while (TRUE) {
|
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) {
|
if (got_sud) {
|
||||||
handle_setupdata();
|
handle_setupdata();
|
||||||
got_sud = FALSE;
|
got_sud = FALSE;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user