diff --git a/firmware-analysis-v206-vs-v213.md b/firmware-analysis-v206-vs-v213.md index 54d3de6..69b5ead 100644 --- a/firmware-analysis-v206-vs-v213.md +++ b/firmware-analysis-v206-vs-v213.md @@ -8,7 +8,7 @@ v2.13 is a significant evolution of the v2.06 firmware with 21 additional functi 2. **Restructured INT0 handler** with active satellite front-end polling 3. **I2C-based initialization** with retry logic for the satellite demodulator 4. **Version-aware code paths** (hardware revision detection via descriptor byte) -5. **Refactored DiSEqC implementation** using I2C-based bus control +5. **Refactored DiSEqC GPIO pin assignment** (data pin moved from P0.7 to P0.0, same bit-bang algorithm) 6. **Simplified BCM4500 status polling** (consolidated from 3 register reads to 1) --- @@ -99,17 +99,19 @@ None were removed -- all commands that were STALL in v2.06 remain STALL in v2.13 - v2.06 at 0x0140: Checks DAT_INTMEM_6D bit 0 (demod active), reads three I2C status registers (0xA2, 0xA8, 0xA4) to compute signal quality, loops up to 6 iterations polling for demod readiness - v2.13 at 0x0162: Checks DAT_INTMEM_4F bit 0 (demod active), reads I2C but uses different function call chain (FUN_CODE_1278 vs FUN_CODE_0c97). Same overall logic with relocated internal variables. -**0x8D - SEND_DISEQC_COMMAND (significantly changed)** -- v2.06: `LCALL 0x23e0; LCALL 0x1e41` -- calls a GPIO-based DiSEqC implementation (0x1e41) that directly manipulates P0 port pins and uses timer-based bit-banging with FUN_CODE_2098 for tone modulation -- v2.13: `LCALL 0x231e; LCALL FUN_CODE_0dbc` -- calls an I2C-based DiSEqC implementation that: - 1. Reads wLength (0xE6BE) as message byte count - 2. Pulls P0.3 low (power enable for DiSEqC bus) - 3. Delays 15 units via FUN_CODE_14b9 - 4. If message bytes present: iterates through EP0BUF data, sending each byte via `func_0x2060` (I2C-based DiSEqC bus write) - 5. If wValue == 0 and no bytes: calls `func_0x22b0` (tone burst command) - 6. If wValue != 0 and no bytes: calls `func_0x2060(0, 0xFF)` (continuous tone) +**0x8D - SEND_DISEQC_COMMAND (GPIO pin reassignment)** +- v2.06: `LCALL 0x23e0; LCALL 0x1e41` -- GPIO bit-bang DiSEqC via FUN_CODE_2098, data on **P0.7**, carrier on P0.3 +- v2.13: `LCALL 0x231e; LCALL FUN_CODE_0dbc` -- GPIO bit-bang DiSEqC via FUN_CODE_2060, data on **P0.0**, carrier on P0.3 -This is a major architectural change: v2.06 uses GPIO bit-banging for DiSEqC, while v2.13 delegates DiSEqC to a dedicated I2C-connected controller chip. +Both versions use the identical algorithm: + 1. Read wLength (0xE6BE) as message byte count + 2. Clear P0.3 (disable 22kHz carrier) + 3. Delay 15 ticks via delay function (7.5ms settling time) + 4. If message bytes present: iterate through EP0BUF, sending each byte via Manchester-encoded bit-bang (8 data bits + odd parity, 3 Timer2 ticks per bit) + 5. If wValue == 0 and no bytes: tone burst A (25 Timer2 ticks = 12.5ms) + 6. If wValue != 0 and no bytes: tone burst B via byte transmit with 0xFF pattern + +**CORRECTION**: Earlier analysis incorrectly identified v2.13 as using "I2C-based DiSEqC." Deep decompilation of the sub-functions (FUN_CODE_2060, FUN_CODE_22f3, FUN_CODE_22b0) reveals they are GPIO bit-bang implementations identical in algorithm to v2.06's FUN_CODE_2098 and FUN_CODE_2372. The only change is the data pin assignment (P0.7 -> P0.0), reflecting a different PCB layout. **0x9D - SET_MODE_FLAG (different logic)** - v2.06: Reads byte at descriptor_base + 10, checks if value is 4, 5, or 6, and conditionally sets bit flag 0x06 based on wValue - 1 @@ -153,7 +155,7 @@ This is a major architectural change: v2.06 uses GPIO bit-banging for DiSEqC, wh | -- | `FUN_CODE_1ac6` (0x1AC6) | v2.13 tuning acquisition sequence | | -- | `FUN_CODE_1e3c` (0x1E3C) | v2.13 demodulator full reset | | -- | `FUN_CODE_10d9` (0x10D9) | v2.13 demod status polling/init | -| -- | `FUN_CODE_0dbc` (0x0DBC) | v2.13 I2C-based DiSEqC controller | +| -- | `FUN_CODE_0dbc` (0x0DBC) | v2.13 DiSEqC GPIO bit-bang wrapper (data on P0.0) | ### USB Descriptor Setup (FUN_CODE_13c3 vs FUN_CODE_11ab) @@ -339,12 +341,16 @@ Direct I2C register read of demodulator register 0xF9, returned to host. This en ### 5.4 Host-Controlled Tuning Delay (Command 0x9C) Allows the host to invoke the tuning acquisition sequence with a configurable delay parameter. In v2.06, the tuning timing is entirely firmware-controlled with no host influence. -### 5.5 I2C-Based DiSEqC Control -v2.13 uses a dedicated I2C-connected DiSEqC controller chip rather than GPIO bit-banging: -- More precise DiSEqC timing (hardware-generated rather than software-timed) -- Support for reading DiSEqC reply messages (the I2C controller can buffer responses) -- Lower CPU overhead during DiSEqC transactions -- Better compatibility with DiSEqC 1.2 motor positioning commands that require precise timing +### 5.5 DiSEqC GPIO Pin Reassignment +All firmware versions use the same GPIO bit-bang algorithm for DiSEqC signaling. The only change is the data pin assignment per PCB revision: + +| Version | Data Pin | Carrier Pin | Byte Transmit | Bit Symbol | Timer Tick | +|---------|----------|-------------|--------------|------------|------------| +| v2.06 | **P0.7** | P0.3 | 0x2098 | 0x23B5 | 0x24C6 | +| Rev.2 v2.10 | **P0.4** | P0.3 | FUN_CODE_07d1 | FUN_CODE_213c | FUN_CODE_225f | +| v2.13 FW1 | **P0.0** | P0.3 | FUN_CODE_2060 | FUN_CODE_22f3 | func_0x2431 | + +The algorithm is identical across all versions: Manchester-encoded bit-bang with Timer2-based timing, odd parity per byte, and 25-tick tone bursts for mini-commands. ### 5.6 Firmware/Descriptor Integrity Verification v2.13 validates demodulator identification (ASCII version string checksum) and descriptor block integrity (two 36-byte checksums) before proceeding. If verification fails after 20 attempts, it falls back to a recovery sequence. v2.06 does no integrity checking. @@ -371,8 +377,195 @@ v2.13's 0x9D handler can trigger a full demodulator reset sequence (FUN_CODE_1e3 | Descriptor base | 0x1200 | 0x0E00 | | Config byte (IRAM) | 0x6D | 0x4F | | INT0 purpose | USB re-enumerate | Demod probe | -| DiSEqC method | GPIO bit-bang | I2C controller | +| DiSEqC data pin | P0.7 (GPIO bit-bang) | P0.0 (GPIO bit-bang) | | Demod init | Direct, no retry | 20-attempt retry | | Integrity checks | None | Checksum verification | | HW revision support | Single | Multi-revision (flag _1_3) | | New vendor cmds | -- | 0x99, 0x9A, 0x9C | + +--- + +## 7. DiSEqC Timing Chain Analysis + +### 7.1 Timer2 Configuration (Identical Across All Versions) + +All firmware versions configure Timer2 identically during USB descriptor setup: + +``` +T2CON = 0x04 ; Auto-reload mode, internal clock, TR2=1 (running) +RCAP2H = 0xF8 ; Reload high byte +RCAP2L = 0x2F ; Reload low byte -> RCAP2 = 0xF82F = 63535 +CKCON = 0x00 ; Default (T2M=0 -> Timer2 clock = CLKOUT/12) +``` + +**Timer2 Clock Derivation:** +``` +FX2 master clock = 48 MHz +CKCON.T2M = 0 -> Timer2 clock = 48 MHz / 12 = 4 MHz +Count per overflow = 65536 - 63535 = 2001 +Tick period = 2001 / 4,000,000 = 500.25 us ~ 500 us +Tick frequency ~ 2.0 kHz +``` + +Timer2 runs continuously from power-on and is never stopped or reconfigured. It serves as a stable 500 us timebase for all DiSEqC operations. + +### 7.2 DiSEqC Signal Architecture + +``` +FX2 Firmware External Hardware Coax Cable ++------------------+ +--------------------+ +------------------+ +| | | | | | +| P0.3 (carrier) |---->| 22 kHz oscillator |---->| LNB power line | +| (enable/disable) | | (gated by P0.3) | | (13V/18V + tone) | +| | | | | | +| P0.x (data bit) | | (internal to FX2 | | | +| (firmware only) | | firmware logic) | | | ++------------------+ +--------------------+ +------------------+ +``` + +The firmware does NOT generate the 22 kHz carrier directly. P0.3 gates an external +22 kHz oscillator circuit on the PCB. The data pin (P0.7/P0.4/P0.0 depending on +version) is used only internally by the firmware to control the Manchester encoding +logic -- it tells the bit-symbol function whether to cut the carrier short or leave +it on for the full period. + +### 7.3 Manchester Encoding (DiSEqC Bit Symbol) + +Each DiSEqC bit consists of 3 Timer2 ticks (3 x 500 us = 1.5 ms): + +**Data '0' (2/3 tone, 1/3 silence):** +``` + Tick 1 Tick 2 Tick 3 + (500 us) (500 us) (500 us) +P0.3: _____|========|========|________| + ^tone ON ^tone OFF + (setup gap) (1.0 ms carrier) (0.5 ms silence) +``` + +**Data '1' (1/3 tone, 2/3 silence):** +``` + Tick 1 Tick 2 Tick 3 + (500 us) (500 us) (500 us) +P0.3: _____|========|________|________| + ^tone ON ^tone OFF early + (setup gap) (0.5 ms carrier) (1.0 ms silence) +``` + +**Implementation (decompiled from Rev.2 FUN_CODE_213c):** +```c +void diseqc_bit_symbol(void) { + wait_TF2(); // Tick 1: inter-bit gap (500 us) + P0 |= 0x08; // P0.3 = 1 -> 22 kHz carrier ON + wait_TF2(); // Tick 2: carrier period (500 us) + if (data_pin != 0) { // If data = '1': + P0 &= 0xF7; // P0.3 = 0 -> carrier OFF (short pulse) + } + wait_TF2(); // Tick 3: final period (500 us) + P0 &= 0xF7; // P0.3 = 0 -> carrier always OFF at end +} +``` + +### 7.4 Byte Transmission (8 Data Bits + Odd Parity) + +Each DiSEqC byte is 9 bits: 8 data (MSB first) + 1 parity (odd). + +**Implementation (decompiled from Rev.2 FUN_CODE_07d1):** +```c +void diseqc_send_byte(char first_byte, byte data) { + byte ones_count = 0; + if (first_byte == 0) TF2 = 0; // Sync timer on first byte + + for (char i = 8; i > 0; i--) { // 8 bits, MSB first + if (data & 0x80) { // Test MSB + data_pin = 1; // Set data = '1' + diseqc_bit_symbol(); // Transmit '1' symbol + ones_count++; + } else { + data_pin = 0; // Set data = '0' + diseqc_bit_symbol(); // Transmit '0' symbol + } + data <<= 1; // Next bit + } + + data_pin = ~ones_count & 1; // Odd parity: '1' if even count + diseqc_bit_symbol(); // Transmit parity bit +} +``` + +**Timing per byte:** 9 bits x 1.5 ms = 13.5 ms + +### 7.5 Tone Burst (Mini DiSEqC Command) + +For legacy 2-way satellite switches, a simple tone burst is used instead of a +full DiSEqC message. The burst is 25 Timer2 ticks of continuous carrier: + +```c +void tone_burst_A(void) { + TF2 = 0; // Sync timer + wait_TF2(); // One tick gap + P0 |= 0x08; // P0.3 = 1 -> carrier ON + for (char i = 25; i > 0; i--) { + wait_TF2(); // 25 x 500 us = 12.5 ms + } + P0 &= 0xF7; // P0.3 = 0 -> carrier OFF +} +``` + +**Burst duration:** 25 x 500 us = 12.5 ms (matches DiSEqC spec) + +### 7.6 Timer Tick Wait (TF2 Polling) + +The lowest-level timing primitive is a busy-wait on Timer2 overflow: + +```c +void wait_TF2(void) { + while (TF2 == 0) {} // Poll Timer2 overflow flag + TF2 = 0; // Clear flag for next tick +} +``` + +This is identical across all versions (v2.06: 0x24C6, Rev.2: FUN_CODE_225f, +v2.13: func_0x2431). Timer2 overflows every 500.25 us, providing the fundamental +DiSEqC timebase. + +### 7.7 CPU Clock Compensation (Delay Function) + +The delay function used before DiSEqC transmission adjusts for CPU clock speed: + +```c +void delay(byte high, byte low) { + byte clkspd = CPUCS & 0x18; // CPUCS[4:3] = clock speed bits + if (clkspd == 0x00) { // 12 MHz: halve the count + // Adjust high:low /= 2 + } else if (clkspd == 0x10) { // 48 MHz: double the count + // Adjust high:low *= 2 + } + // 24 MHz (0x08): use count as-is + while (high:low > 0) { + wait_TF2(); + high:low--; + } +} +``` + +The pre-DiSEqC delay call is `delay(0, 0x0F)` = 15 ticks x 500 us = 7.5 ms. +This allows the LNB voltage to stabilize before DiSEqC signaling begins. + +### 7.8 Complete DiSEqC Timing Summary + +| Parameter | Value | Source | +|-----------|-------|--------| +| Timer2 clock | 4 MHz (48 MHz / 12) | CKCON default, T2M=0 | +| Timer2 reload | 0xF82F | RCAP2H:RCAP2L | +| Tick period | 500.25 us | (65536 - 63535) / 4 MHz | +| Bit period | 1.5 ms (3 ticks) | DiSEqC Manchester encoding | +| Byte period | 13.5 ms (9 bits) | 8 data + 1 parity | +| Tone burst | 12.5 ms (25 ticks) | Mini-command A/B | +| Pre-TX delay | 7.5 ms (15 ticks) | Voltage settling | +| Data '0' | 1.0 ms tone + 0.5 ms silence | 2/3 duty cycle | +| Data '1' | 0.5 ms tone + 1.0 ms silence | 1/3 duty cycle | +| Carrier frequency | 22 kHz (external oscillator) | Gated by P0.3 | +| Carrier enable | P0.3 | All versions | +| Data pin (v2.06) | P0.7 | PCB revision A | +| Data pin (Rev.2) | P0.4 | PCB revision B | +| Data pin (v2.13) | P0.0 | PCB revision C |