From e4117421a1494ac666b561fa3c4d2ed04eb5cb64 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Wed, 11 Feb 2026 12:18:09 -0700 Subject: [PATCH] Add GPIF streaming path and kernel firmware format analyses GPIF streaming analysis (772 lines): - IFCONFIG=0xEE: GPIF master mode, 48MHz async, BCM4500 data bus - Fully hardware-managed path: BCM4500 -> GPIF -> EP2 FIFO -> USB bulk - EP2FIFOCFG=0x0C (AUTOIN, 8-bit), FLOWSTATEA |= 0x09 (auto re-trigger) - Byte-for-byte identical register config across all firmware versions - ARM_TRANSFER (cmd 0x85) start/stop flow documented with disassembly Kernel FW01 analysis (425 lines): - FW01/FW02 files never existed in linux-firmware or any distribution - SkyWalker-1 boots from onboard EEPROM, never needs host firmware - DVB-USB binary hexline format documented (compact Intel HEX variant) - C2 EEPROM format vs kernel hexline format comparison - Binary comparison matrix for all 5 firmware dumps - Anti-tampering string found in v2.13 at offset 0x1880 --- gpif-streaming-analysis.md | 772 +++++++++++++++++++++++++++++++++++++ kernel-fw01-analysis.md | 425 ++++++++++++++++++++ 2 files changed, 1197 insertions(+) create mode 100644 gpif-streaming-analysis.md create mode 100644 kernel-fw01-analysis.md diff --git a/gpif-streaming-analysis.md b/gpif-streaming-analysis.md new file mode 100644 index 0000000..d7b9b47 --- /dev/null +++ b/gpif-streaming-analysis.md @@ -0,0 +1,772 @@ +# Genpix SkyWalker-1: GPIF MPEG-2 Streaming Path Analysis + +## Overview + +This document traces the MPEG-2 transport stream data path from the Broadcom BCM4500 satellite demodulator through the Cypress FX2's General Programmable Interface (GPIF) engine to USB bulk endpoint EP2 (0x82). Analysis covers all three firmware versions: + +| Instance | Port | Firmware | Functions | Config Byte | GPIF Ctrl Fn | +|----------|------|----------|-----------|-------------|-------------| +| v2.06 | 8193 | Original | 61 | IRAM 0x6D | 0x1919 | +| v2.13 FW1 | 8194 | Revised | 82 | IRAM 0x4F | 0x1800 | +| Rev.2 v2.10.4 | 8197 | HW Rev.2 | 107 | IRAM 0x4E | 0x0D7C | + +--- + +## Data Flow Architecture + +``` + Cypress FX2 (CY7C68013A) + +-----------------------------+ + | | + BCM4500 P3.5 TS_EN | GPIF Engine EP2 FIFO | USB 2.0 HS + Demodulator <-----------------+ (Master Read) (AUTOIN) +------------> Host + (I2C:0x3F) GPIF Data Bus | 0xE4xx wfm 4x buf | EP2 (0x82) + -------------------> CTL/RDY pins 8-bit | Bulk IN + 8-bit parallel TS | | 7 URBs x 8KB + +-----------------------------+ +``` + +**The path is: BCM4500 -> GPIF master read -> EP2 FIFO (auto-commit) -> USB bulk IN.** + +There is no software intermediary in the data path. The GPIF engine reads data directly into the EP2 FIFO buffer. The EP2FIFOCFG AUTOIN bit causes the hardware to automatically commit full packets to the USB controller for transfer to the host. The firmware only needs to start/stop the GPIF engine and handle completion interrupts. + +--- + +## 1. IFCONFIG: Interface Configuration + +**Register: IFCONFIG (0xE601) = 0xEE** + +Written in FUN_CODE_1000 (Rev.2 at CODE:1000), called during hardware initialization: + +```asm +; Rev.2 FUN_CODE_1000 at CODE:1000 +CODE:1000: 90e601 MOV DPTR,#0xe601 ; IFCONFIG register +CODE:1003: 74ee MOV A,#0xee ; 0xEE +CODE:1005: f0 MOVX @DPTR,A +``` + +**Bit decode (0xEE = 1110_1110):** + +| Bit | Name | Value | Meaning | +|-----|------|-------|---------| +| 7 | IFCLKSRC | 1 | Internal clock source | +| 6 | 3048MHZ | 1 | 48 MHz IFCLK frequency | +| 5 | IFCLKOE | 1 | IFCLK pin drives output (available to BCM4500) | +| 4 | IFCLKPOL | 0 | Non-inverted clock polarity | +| 3 | ASYNC | 1 | Asynchronous GPIF (no IFCLK-synchronized handshake) | +| 2 | GSTATE | 1 | GPIF state machine outputs visible on PORTE (debug) | +| 1:0 | IFCFG | 10 | **GPIF internal master mode** | + +The FX2 operates as a GPIF master, reading data from the BCM4500's parallel transport stream output. Asynchronous mode means the GPIF uses RDY pin handshaking rather than clock-edge sampling. The 48 MHz IFCLK output provides a reference clock to the BCM4500, though actual data capture is handshake-controlled. + +This value is identical across all three firmware versions. + +### Prior IFCONFIG Value + +During early init (FUN_CODE_10d9), IFCONFIG is temporarily set to 0xCA before FUN_CODE_1000 overwrites it: + +```asm +; Early init - temporary IFCONFIG +CODE:10f0: 90e601 MOV DPTR,#0xe601 +CODE:10f3: 74ca MOV A,#0xca ; 0xCA = internal 48MHz, IFCLKOE, no GSTATE, GPIF +CODE:10f5: f0 MOVX @DPTR,A +``` + +0xCA (1100_1010): Same as 0xEE but GSTATE=0 and ASYNC=0. The final value (0xEE) enables async mode and debug state output. + +--- + +## 2. FIFO Reset Sequence + +All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure. + +**Register: FIFORESET (0xE604)** + +```asm +; Rev.2 FUN_CODE_10d9 at CODE:1130-1152 +CODE:1130: 90e604 MOV DPTR,#0xe604 ; FIFORESET +CODE:1133: 7480 MOV A,#0x80 ; Bit 7 = NAKALL: NAK all host transfers +CODE:1135: f0 MOVX @DPTR,A +CODE:1136: 00 00 00 ; mandatory 3-NOP sync delay +CODE:1139: 7402 MOV A,#0x02 ; Reset EP2 FIFO +CODE:113b: f0 MOVX @DPTR,A +CODE:113c: 00 00 00 +CODE:113f: 7404 MOV A,#0x04 ; Reset EP4 FIFO +CODE:1141: f0 MOVX @DPTR,A +CODE:1142: 00 00 00 +CODE:1145: 7406 MOV A,#0x06 ; Reset EP6 FIFO +CODE:1147: f0 MOVX @DPTR,A +CODE:1148: 00 00 00 +CODE:114b: 7408 MOV A,#0x08 ; Reset EP8 FIFO +CODE:114d: f0 MOVX @DPTR,A +CODE:114e: 00 00 00 +CODE:1151: e4 CLR A ; NAKALL = 0 (resume) +CODE:1152: f0 MOVX @DPTR,A +``` + +The sequence: NAKALL on -> reset EP2 -> EP4 -> EP6 -> EP8 -> NAKALL off. Triple-NOP delays between writes are required by the FX2 architecture: XRAM register writes take 2 cycles to propagate, and back-to-back writes to the same register need at least 3 instruction cycles between them. + +--- + +## 3. Endpoint FIFO Configuration + +### EP2FIFOCFG (0xE618) = 0x0C + +```asm +; Rev.2 FUN_CODE_10d9 at CODE:1156 +CODE:1156: 90e618 MOV DPTR,#0xe618 ; EP2FIFOCFG +CODE:1159: 740c MOV A,#0x0c ; 0x0C +CODE:115b: f0 MOVX @DPTR,A +``` + +**Bit decode (0x0C = 0000_1100):** + +| Bit | Name | Value | Meaning | +|-----|------|-------|---------| +| 4 | INFM1 | 0 | IN endpoint: packet count not decremented | +| 3 | AUTOIN | 1 | **Auto-commit IN packets when FIFO buffer full** | +| 2 | ZEROLENIN | 1 | Allow zero-length IN packets | +| 1 | (reserved) | 0 | -- | +| 0 | WORDWIDE | 0 | **8-bit data path** (not 16-bit) | + +The AUTOIN bit is critical: when the GPIF engine fills an EP2 FIFO buffer to the configured packet size, the FX2 hardware automatically arms the buffer for USB transfer. No firmware intervention is needed in the data path. The 8-bit WORDWIDE setting matches the BCM4500's 8-bit parallel transport stream output. + +### Other Endpoint FIFOs (All Disabled) + +```asm +CODE:115f: e4 CLR A ; A = 0 +CODE:1160: 90e619 MOV DPTR,#0xe619 ; EP4FIFOCFG = 0x00 +CODE:1163: f0 MOVX @DPTR,A +CODE:1167: 90e61a MOV DPTR,#0xe61a ; EP6FIFOCFG = 0x00 +CODE:116a: f0 MOVX @DPTR,A +CODE:116e: 90e61b MOV DPTR,#0xe61b ; EP8FIFOCFG = 0x00 +CODE:1171: f0 MOVX @DPTR,A +``` + +Only EP2 is configured for streaming. EP4/EP6/EP8 FIFOs are left in default (manual/disabled) state. + +--- + +## 4. GPIF Waveform and Control Configuration + +### Init Table-Driven Configuration + +The GPIF waveform descriptors (0xE400-0xE47F), GPIFIDLECTL (0xE660), GPIFCTLCFG (0xE661), GPIFWFSELECT (0xE662), and related registers are programmed via a compressed init table processed during the main() entry point, before the main loop begins. + +| Version | Init Table Address | Parser Function | +|---------|-------------------|-----------------| +| v2.06 | CODE:0B46 | main (0x188D) | +| v2.13 FW1 | CODE:0B88 | main_entry (0x170D) | +| Rev.2 | CODE:0B48 | main_init (0x15A6) | + +The init table uses a custom encoding: +- **Control byte** bits [7:6] select the write mode (IRAM, XRAM 2-byte addr, bit manipulation, XRAM 1-byte addr) +- **Control byte** bits [5:0] encode the byte count (extended to 2 bytes if bit 5 is set) +- Data bytes follow, written sequentially to the target address range + +The table writes configuration data to the 0xE0xx scratch RAM area (device descriptors, I2C addresses, modulation parameters) and to the 0xE4xx/0xE6xx GPIF/USB register space. The GPIF waveform descriptors occupy 128 bytes at 0xE400-0xE47F and define the state machine transitions for reading data from the BCM4500. + +### GPIF Waveform Structure + +The GPIF waveform data embedded in the init table contains 4 waveform slots (32 bytes each): + +The waveform data visible in the init table (from 0x0BAD region in Rev.2) includes the pattern: +``` +01 01 01 01 01 01 07 00 01 00 00 00 00 00 00 +F0 F0 F0 F0 F0 F0 F0 F0 00 3F 00 00 00 00 3F +``` + +This is characteristic of a simple single-state GPIF read waveform: +- **States 0-5**: CTL outputs = 0x01 (single control line asserted), length = 1 IFCLK +- **State 6**: CTL = 0x07, length = 0 (idle/terminate state) +- **Opcode/branch**: 0x00 (SDP = sample data point, branch idle) +- **Output/logic**: 0xF0 pattern (FIFO write flags) + +The waveform programs a straightforward "assert read strobe, capture data, de-assert" cycle that reads one byte per GPIF transaction from the BCM4500's parallel port into the EP2 FIFO. + +### REVCTL (0xE60B) = 0x03 + +```asm +CODE:1127: 90e60b MOV DPTR,#0xe60b ; REVCTL +CODE:112a: 7403 MOV A,#0x3 +CODE:112c: f0 MOVX @DPTR,A +``` + +REVCTL = 0x03: Both NOAUTOARM and SKIPCOMMIT bits set. This disables the automatic arming of endpoint buffers on access and skips the auto-commit behavior. Combined with EP2FIFOCFG.AUTOIN=1, this means the GPIF engine explicitly controls when data is committed -- the AUTOIN triggers on FIFO fullness, not on CPU access. + +### GPIFSGLDATH (SFR 0xBB) - GPIF Trigger Register + +The SFR at address 0xBB is used as the GPIF trigger/status register: + +- **Read**: Bit 7 (DONE) = 1 when GPIF is idle +- **Write**: Bits [1:0] = endpoint select (00=EP2), Bit 2 = read direction + +```asm +; Poll for GPIF completion +CODE:0dca: e5bb MOV A,0xbb ; Read GPIFTRIG +CODE:0dcc: 30e7fb JNB 0xe7,0x0dca ; Loop until bit 7 (DONE) = 1 + +; Trigger next GPIF read into EP2 +CODE:0dd2: 75bb04 MOV 0xbb,#0x4 ; GPIFTRIG = 0x04 (read, EP2) +``` + +--- + +## 5. ARM_TRANSFER (Vendor Command 0x85) + +### Dispatch + +The host issues USB vendor command 0x85 to start or stop the MPEG-2 transport stream. The command dispatches via the vendor command jump table at CODE:0x0076: + +| Version | Jump Table Entry | Handler Address | GPIF Control Fn | +|---------|-----------------|-----------------|-----------------| +| v2.06 | `21 10` (AJMP) | CODE:0110 | LCALL 0x1919 | +| v2.13 FW1 | `21 10` (AJMP) | CODE:0110 | LCALL 0x1800 | +| Rev.2 | `01 fa` (AJMP) | CODE:00FA | LCALL 0x0D7C | + +The handler logic is identical across versions despite the address differences: + +```asm +; Rev.2 ARM_TRANSFER handler at CODE:00FA (v2.06/v2.13 at CODE:0110) +CODE:00fa: e54e MOV A,0x4e ; Read config byte +CODE:00fc: 30e00d JNB 0xe0,0x010c ; Test bit 0: demodulator active? +CODE:00ff: 90e6ba MOV DPTR,#0xe6ba ; Read USB SETUP wValue (low byte) +CODE:0102: e0 MOVX A,@DPTR +CODE:0103: 24ff ADD A,#0xff ; Set CY if wValue != 0 +CODE:0105: 9206 MOV 0x06,CY ; arm_flag = (wValue != 0) +CODE:0107: 120d7c LCALL 0x0d7c ; Call GPIF control function +CODE:010a: 8005 SJMP 0x0111 ; Skip to done +CODE:010c: c206 CLR 0x06 ; arm_flag = 0 (force stop if no demod) +CODE:010e: 120d7c LCALL 0x0d7c ; Call GPIF control function +CODE:0111: e4 CLR A +CODE:0112: 90e68b MOV DPTR,#0xe68b ; EP0BCL = 0 (acknowledge control transfer) +CODE:0115: f0 MOVX @DPTR,A +``` + +The handler checks bit 0 of the configuration byte (demodulator present/active). If active, it reads wValue from the USB SETUP packet: wValue=1 means start streaming, wValue=0 means stop. If the demodulator is not active, it forces a stop. + +### GPIF Control Function - START Path + +When arm_flag=1 (start streaming), the GPIF control function at 0x0D7C (Rev.2) / 0x1800 (v2.13) / 0x1919 (v2.06) executes: + +```c +// Decompiled from Rev.2 FUN_CODE_0d7c -- START STREAMING +if (arm_flag != 0 && config_byte.7 == 0) { + // Not already streaming -- initialize GPIF transfer + + config_byte |= 0x80; // Mark streaming active + + // 1. Set GPIF transaction count (0xE630:0xE631) + GPIFTCB3 = 0x80; // 0xE630 = 0x80 + GPIFTCB2 = 0x00; // 0xE631 = 0x00 + // Transaction count = 0x8000_0000 (2 GB) -- effectively infinite + + // 2. Clear GPIF address registers + *(0xE6D2) = 0x00; // Address high = 0 + *(0xE6D3) = 0x01; // Address low = 1 (initial) + EP2FIFOBCL = 0x00; // 0xE6F1: Reset byte count + *(0xE6D3) = 0x00; // Address low = 0 + + // 3. Initial GPIF trigger via XRAM registers + *(0xE6D0) = 0x02; // Trigger/config = EP2 read + *(0xE6D1) = 0x00; // Trigger latch + + // 4. Assert BCM4500 transport stream enable + P3 &= ~0x20; // P3.5 LOW = TS_EN asserted + + // 5. Wait for GPIF completion + while (!(GPIFTRIG & 0x80)) {} // Poll SFR 0xBB bit 7 (DONE) + + // 6. Re-enable P3.5 + P3 |= 0x20; // P3.5 HIGH + + // 7. Trigger continuous GPIF read into EP2 + GPIFTRIG = 0x04; // SFR 0xBB = read EP2 + + // 8. Signal data path active + P0 &= ~0x80; // P0.7 LOW = streaming active indicator +} +``` + +**Disassembly (Rev.2 at CODE:0D7C):** +```asm +; START path (arm_flag = 1, config.7 = 0) +CODE:0d84: 434e80 ORL 0x4e,#0x80 ; config_byte |= 0x80 (streaming flag) + +CODE:0d87: 90e630 MOV DPTR,#0xe630 ; GPIFTCB3 +CODE:0d8a: 7480 MOV A,#0x80 +CODE:0d8c: f0 MOVX @DPTR,A ; GPIFTCB3 = 0x80 + +CODE:0d90: e4 CLR A +CODE:0d91: 90e631 MOV DPTR,#0xe631 ; GPIFTCB2 +CODE:0d94: f0 MOVX @DPTR,A ; GPIFTCB2 = 0x00 + +CODE:0d98: 90e6d2 MOV DPTR,#0xe6d2 +CODE:0d9b: f0 MOVX @DPTR,A ; [0xE6D2] = 0x00 + +CODE:0d9f: 90e6d3 MOV DPTR,#0xe6d3 +CODE:0da2: 04 INC A ; A = 1 +CODE:0da3: f0 MOVX @DPTR,A ; [0xE6D3] = 0x01 + +CODE:0da7: e4 CLR A +CODE:0da8: 90e6f1 MOV DPTR,#0xe6f1 ; EP2FIFOBCL (byte count low) +CODE:0dab: f0 MOVX @DPTR,A ; EP2FIFOBCL = 0x00 + +CODE:0daf: 90e6d3 MOV DPTR,#0xe6d3 +CODE:0db2: f0 MOVX @DPTR,A ; [0xE6D3] = 0x00 + +CODE:0db6: 90e6d0 MOV DPTR,#0xe6d0 +CODE:0db9: 7402 MOV A,#0x2 +CODE:0dbb: f0 MOVX @DPTR,A ; [0xE6D0] = 0x02 + +CODE:0dbf: e4 CLR A +CODE:0dc0: 90e6d1 MOV DPTR,#0xe6d1 +CODE:0dc3: f0 MOVX @DPTR,A ; [0xE6D1] = 0x00 + +CODE:0dc7: 53b0df ANL 0xb0,#0xdf ; P3 &= 0xDF -> P3.5 = 0 (TS_EN assert) + +CODE:0dca: e5bb MOV A,0xbb ; Read GPIFTRIG status +CODE:0dcc: 30e7fb JNB 0xe7,0x0dca ; Wait for DONE bit + +CODE:0dcf: 43b020 ORL 0xb0,#0x20 ; P3 |= 0x20 -> P3.5 = 1 +CODE:0dd2: 75bb04 MOV 0xbb,#0x4 ; GPIFTRIG = 0x04 (read EP2) + +CODE:0dd8: 53807f ANL 0x80,#0x7f ; P0 &= 0x7F -> P0.7 = 0 +CODE:0ddb: 22 RET +``` + +### GPIF Control Function - STOP Path + +When arm_flag=0 (stop streaming) and config_byte.7=1 (currently streaming): + +```c +// Decompiled from Rev.2 FUN_CODE_0d7c -- STOP STREAMING +if (arm_flag == 0 && config_byte.7 == 1) { + // Currently streaming -- abort GPIF and flush + + P0 |= 0x80; // P0.7 HIGH = streaming stopped + EP2FIFOBCH = 0xFF; // 0xE6F5: Force byte count high = 0xFF + // (skip/flush current FIFO packet) + + while (!(GPIFTRIG & 0x80)) {} // Wait for GPIF to go idle + + OUTPKTEND = 0x82; // 0xE648 = 0x82: Force-commit EP2 packet + // Bit 7 = skip, bits[3:0] = EP2 + config_byte &= ~0x80; // Clear streaming flag + + P3 |= 0xE0; // P3.7:5 = 1 (de-assert all control lines) +} +``` + +**Disassembly (Rev.2 at CODE:0DDC):** +```asm +; STOP path (arm_flag = 0, config.7 = 1) +CODE:0de1: 438080 ORL 0x80,#0x80 ; P0 |= 0x80 -> P0.7 = 1 (stopped) + +CODE:0de4: 90e6f5 MOV DPTR,#0xe6f5 ; EP2FIFOBCH (byte count high) +CODE:0de7: 74ff MOV A,#0xff +CODE:0de9: f0 MOVX @DPTR,A ; = 0xFF (force flush) + +CODE:0dea: e5bb MOV A,0xbb ; Read GPIFTRIG +CODE:0dec: 30e7fb JNB 0xe7,0x0dea ; Wait for DONE + +CODE:0def: 90e648 MOV DPTR,#0xe648 ; OUTPKTEND +CODE:0df2: 7482 MOV A,#0x82 ; Skip=1, EP2 +CODE:0df4: f0 MOVX @DPTR,A ; Force-commit/skip EP2 packet + +CODE:0df5: 534e7f ANL 0x4e,#0x7f ; config_byte &= 0x7F (clear streaming) +CODE:0df8: 43b0e0 ORL 0xb0,#0xe0 ; P3 |= 0xE0 (de-assert controls) +CODE:0dfb: 22 RET +``` + +**OUTPKTEND (0xE648) = 0x82** is notable: bit 7 set means "skip" (discard partially filled packet), bits [3:0] = 2 = EP2. This ensures any partial FIFO buffer is flushed when stopping the stream. + +--- + +## 6. INT4/INT6 Interrupt Handlers (GPIF/FIFO Events) + +### Rev.2 v2.10.4: Shared Handler via Trampoline + +Both INT4 and INT6 vectors jump to the same handler: + +```asm +CODE:0043: 021200 LJMP 0x1200 ; INT4_FX2_vector -> trampoline +CODE:0053: 021200 LJMP 0x1200 ; INT6_FX2_vector -> trampoline + +; Trampoline at 0x1200 +CODE:1200: 022084 LJMP 0x2084 ; -> GPIF_INT4_INT6_handler +``` + +**GPIF_INT4_INT6_handler (CODE:2084):** +```asm +CODE:2084: c0e0 PUSH A ; Save accumulator +CODE:2086: c083 PUSH DPH ; Save DPTR high +CODE:2088: c082 PUSH DPL ; Save DPTR low + +CODE:208a: d201 SETB 0x01 ; Set bit flag _0_1 (GPIF event pending) + +CODE:208c: 5391ef ANL 0x91,#0xef ; Clear EXIF.4 (INT4/INT6 IRQ flag) + +CODE:208f: 90e65d MOV DPTR,#0xe65d ; GPIFIRQ (GPIF Interrupt Request) +CODE:2092: 7401 MOV A,#0x1 +CODE:2094: f0 MOVX @DPTR,A ; Clear GPIFIRQ bit 0 + +CODE:2095: d082 POP DPL ; Restore context +CODE:2097: d083 POP DPH +CODE:2099: d0e0 POP A +CODE:209b: 32 RETI ; Return from interrupt +``` + +The handler is minimal: it sets a software flag (_0_1) and clears the hardware interrupt. The actual GPIF processing happens in the main loop when this flag is polled. + +### v2.13 FW1: Same Pattern + +```asm +CODE:0043: 021500 LJMP 0x1500 ; INT4 -> thunk_FUN_CODE_2252 +CODE:1500: 022252 LJMP 0x2252 ; -> FUN_CODE_2252 +``` + +**FUN_CODE_2252 (CODE:2252):** identical logic, sets _0_6 flag, clears EXIF.4 and GPIFIRQ.0. + +### v2.06: Different Vector Dispatch + +In v2.06, INT4 and INT6 both jump to 0x1600 which is a jump table indexed by the interrupt source register. The GPIF-related entry jumps to the same pattern (set flag, clear IRQ, return). + +### INT2 (USB/GPIF Combined Vector) + +The INT2 vector at CODE:0033 handles USB/GPIF combined interrupts: + +```asm +; Rev.2 +CODE:0033: 02223f LJMP 0x223f ; USB_GPIF_handler + +; USB_GPIF_handler at CODE:223F +CODE:223f: c0e0 PUSH A +CODE:2241: 53d8ef ANL 0xd8,#0xef ; Clear CCON.4 (PCA counter flag) +CODE:2244: d0e0 POP A +CODE:2246: 32 RETI +``` + +This handler simply clears the PCA (Programmable Counter Array) interrupt flag. The actual USB processing is handled by polling in the main loop. + +--- + +## 7. FLOWSTATE Configuration + +During initialization (FUN_CODE_09a9 at CODE:0AEF), the GPIF flow state machine is configured: + +```asm +CODE:0aef: 90e668 MOV DPTR,#0xe668 ; FLOWSTATEA +CODE:0af2: e0 MOVX A,@DPTR +CODE:0af3: 4409 ORL A,#0x09 ; Set bits 0 and 3 +CODE:0af5: f0 MOVX @DPTR,A +``` + +**FLOWSTATEA (0xE668) |= 0x09:** + +| Bit | Value | Meaning | +|-----|-------|---------| +| 0 | 1 | FSEN: Flow State enable -- GPIF uses flow state logic | +| 3 | 1 | FS[3]: Flow state flag -- specific to waveform design | + +The flow state machine controls automated GPIF-to-FIFO data transfers. With FSEN=1, the GPIF engine can automatically re-trigger transactions when FIFO space is available, creating a continuous streaming pipeline without firmware intervention after initial setup. + +### GPIF Interrupt Enable + +```asm +CODE:0af6: 90e65c MOV DPTR,#0xe65c ; GPIFIE +CODE:0af9: e0 MOVX A,@DPTR +CODE:0afa: 443d ORL A,#0x3d ; Enable bits 0,2,3,4,5 +CODE:0afc: f0 MOVX @DPTR,A +``` + +**GPIFIE (0xE65C) |= 0x3D = 0011_1101:** + +| Bit | Name | Enabled | Purpose | +|-----|------|---------|---------| +| 0 | GPIFWF | Yes | Waveform completion interrupt | +| 1 | (reserved) | No | -- | +| 2 | GPIFTCEXP | Yes | Transaction count expired | +| 3 | GPIFGPIFDONE | Yes | GPIF operation done | +| 4 | GPIFFF | Yes | FIFO flag interrupt | +| 5 | GPIFWF2 | Yes | Waveform 2 completion | + +These interrupts drive the INT4/INT6 handlers described in section 6. The combination of TC expire, DONE, and FIFO flag interrupts allows the main loop to manage the streaming pipeline: restart GPIF when buffers become available, handle end-of-transfer, and detect error conditions. + +--- + +## 8. GPIF Pin Assignments + +### Control Outputs (CTL[5:0]) + +Based on the init table and the P3 register manipulation in the GPIF control function: + +| Pin | Direction | Function | Start State | Active State | +|-----|-----------|----------|-------------|--------------| +| P3.5 | Output | TS_EN (Transport Stream Enable) | HIGH (inactive) | LOW (active) | +| P3.6 | Output | BCM4500 control line | HIGH | LOW | +| P3.7 | Output | BCM4500 control line | HIGH | HIGH | +| P0.7 | Output | Host streaming indicator | HIGH (idle) | LOW (streaming) | + +### Data Bus + +The GPIF uses the FD[7:0] data bus (Port B/D depending on FX2 variant) in 8-bit mode (WORDWIDE=0). + +### Ready Signals (RDY[5:0]) + +The GPIF waveform references RDY pins for handshaking with the BCM4500. The specific RDY pin assignment is encoded in the waveform descriptors loaded from the init table. + +--- + +## 9. Main Loop Integration + +After initialization, the main loop (FUN_CODE_09a9 at CODE:0B0E) polls for GPIF events: + +```c +// Simplified main loop (Rev.2) +while (true) { + FUN_CODE_201e(); // Poll I2C / USB status + + if (gpif_event_flag) { // _0_1 set by INT4/INT6 handler + FUN_CODE_0319(); // Process vendor commands + gpif_event_flag = 0; + } + + if (gpif_done_flag) { // _0_3 set when GPIF transfer completes + FUN_CODE_2265(); // (stub -- no-op in Rev.2) + if (carry_set) { + gpif_done_flag = 0; + + // Re-arm GPIF or handle completion + while (true) { + FUN_CODE_1faa(); // Enter idle (PCON.0 = 1) + + if (remote_wakeup) break; + + // Check EP2CS for buffer availability + ep2cs = *(0xE682); // EP2CS register + if ((ep2cs & 0x80) && // EP2 busy + (ep2cs & 0x02)) // EP2 stall + continue; // Keep waiting + + if ((ep2cs & 0x40) && // EP2 full + (ep2cs & 0x01)) // EP2 empty + continue; // Keep waiting + + break; // Buffer available + } + + FUN_CODE_1eda(); // Handle USB re-enumerate if needed + FUN_CODE_2267(); // (stub -- no-op in Rev.2) + } + } +} +``` + +**Disassembly of the main loop polling (Rev.2 at CODE:0B0E):** +```asm +CODE:0b0e: 12201e LCALL 0x201e ; Poll I2C/USB +CODE:0b11: 300105 JNB 0x01,0x0b19 ; Check _0_1 (GPIF event flag) +CODE:0b14: 120319 LCALL 0x0319 ; Process vendor commands +CODE:0b17: c201 CLR 0x01 ; Clear flag +CODE:0b19: 3003f2 JNB 0x03,0x0b0e ; Check _0_3 (GPIF done flag) +CODE:0b1c: 122265 LCALL 0x2265 ; (no-op) +CODE:0b1f: 50ed JNC 0x0b0e ; If no carry, loop back + +CODE:0b21: c203 CLR 0x03 ; Clear GPIF done flag +CODE:0b23: 121faa LCALL 0x1faa ; Enter idle (PCON.0 = sleep) +CODE:0b26: 200016 JB 0x00,0x0b3f ; Remote wakeup -> skip + +; Poll EP2CS for buffer availability +CODE:0b29: 90e682 MOV DPTR,#0xe682 ; EP2CS register +CODE:0b2c: e0 MOVX A,@DPTR +CODE:0b2d: 30e704 JNB 0xe7,0x0b34 ; bit 7 = busy? +CODE:0b30: e0 MOVX A,@DPTR +CODE:0b31: 20e1ef JB 0xe1,0x0b23 ; bit 1 = stall? -> keep waiting +CODE:0b34: 90e682 MOV DPTR,#0xe682 +CODE:0b37: e0 MOVX A,@DPTR +CODE:0b38: 30e604 JNB 0xe6,0x0b3f ; bit 6 = full? +CODE:0b3b: e0 MOVX A,@DPTR +CODE:0b3c: 20e0e4 JB 0xe0,0x0b23 ; bit 0 = empty? -> keep waiting + +CODE:0b3f: 121eda LCALL 0x1eda ; USB re-enumerate check +CODE:0b42: 122267 LCALL 0x2267 ; (no-op) +CODE:0b45: 80c7 SJMP 0x0b0e ; Loop forever +``` + +The main loop uses CPU idle mode (PCON.0) between GPIF events, waking on interrupts. The EP2CS polling checks that EP2 buffers are available before re-arming the GPIF, preventing FIFO overrun. + +--- + +## 10. CPUCS and Clock Configuration + +**Register: CPUCS (0xE600)** + +Set during init (FUN_CODE_10d9): + +```asm +CODE:10e4: 90e600 MOV DPTR,#0xe600 ; CPUCS +CODE:10e7: e0 MOVX A,@DPTR ; Read current value +CODE:10e8: 54e5 ANL A,#0xe5 ; Clear bits 4,3,1 (CLKSPD, CLKINV) +CODE:10ea: 4410 ORL A,#0x10 ; Set bit 4 +CODE:10ec: f0 MOVX @DPTR,A ; Write back +``` + +CPUCS bits: +- Bits [4:3] CLKSPD = 10 = **48 MHz** CPU clock +- Other bits preserved from power-on defaults + +--- + +## 11. GPIO Pin Summary for Streaming + +| Pin | SFR | Direction | Streaming Active | Streaming Stopped | Function | +|-----|-----|-----------|-----------------|-------------------|----------| +| P0.2 | 0x80 | Out | Set during init | -- | BCM4500 config | +| P0.7 | 0x80 | Out | LOW | HIGH | Streaming status indicator | +| P3.5 | 0xB0 | Out | Pulsed LOW | HIGH | BCM4500 TS_EN (Transport Stream Enable) | +| P3.6 | 0xB0 | Out | Controlled | HIGH | BCM4500 control | +| P3.7 | 0xB0 | Out | Controlled | HIGH | BCM4500 control | + +During init (FUN_CODE_10d9): +```asm +CODE:111b: 758084 MOV 0x80,#0x84 ; P0 = 0x84 (bits 7,2 set) +CODE:1121: 75b0e1 MOV 0xb0,#0xe1 ; P3 = 0xE1 (bits 7,6,5,0 set) +``` + +P0 init = 0x84: P0.7=1 (streaming off), P0.2=1 (BCM4500 control) +P3 init = 0xE1: P3.7:5=1 (all control lines inactive), P3.0=1 + +--- + +## 12. Complete Streaming Sequence + +### Start (Host sends ARM_TRANSFER wValue=1): + +``` +1. Host -> USB vendor cmd 0x85, wValue=1 +2. FX2 checks demod active (config_byte bit 0) +3. FX2 sets arm_flag = 1 +4. gpif_ctrl(): + a. Set config_byte.7 = 1 (streaming active) + b. Load GPIF transaction count: GPIFTCB3:2 = 0x8000 (huge count) + c. Reset GPIF address and EP2 FIFO byte count + d. Initial GPIF setup via XRAM 0xE6D0-0xE6D3 + e. Assert P3.5 LOW -> BCM4500 transport stream output enabled + f. Wait for initial GPIF transaction to complete (poll GPIFTRIG.7) + g. De-assert P3.5 HIGH + h. Trigger continuous GPIF read: GPIFTRIG = 0x04 (read into EP2) + i. Set P0.7 LOW (streaming indicator) +5. GPIF engine now auto-reads BCM4500 data into EP2 FIFO +6. EP2FIFOCFG.AUTOIN commits full packets to USB automatically +7. Host reads EP2 (0x82) bulk IN pipe continuously +``` + +### Steady State: + +``` +BCM4500 data bus -> GPIF state machine read -> EP2 FIFO buffer + | | + | (8-bit async handshake via RDY/CTL) | AUTOIN = 1 + | | + v v + Continuous GPIF transactions Auto-commit when full + (FLOWSTATE re-triggers) (hardware, no CPU) + | + v + USB bulk IN transfer + (host polls EP2 0x82) +``` + +The FLOWSTATE engine (FLOWSTATEA bit 0 = FSEN) automatically re-triggers GPIF transactions when EP2 FIFO buffers become available after USB transfers complete. This creates a fully hardware-managed pipeline where the CPU only needs to handle exceptional conditions. + +### Stop (Host sends ARM_TRANSFER wValue=0): + +``` +1. Host -> USB vendor cmd 0x85, wValue=0 +2. gpif_ctrl(): + a. Set P0.7 HIGH (streaming stopped) + b. Write EP2FIFOBCH = 0xFF (force-flush current buffer) + c. Wait for GPIF idle (poll GPIFTRIG.7) + d. Write OUTPKTEND = 0x82 (skip/discard partial EP2 packet) + e. Clear config_byte.7 (streaming inactive) + f. Set P3.7:5 = 1 (de-assert all BCM4500 control lines) +``` + +--- + +## 13. Cross-Version Comparison + +The GPIF streaming path is functionally identical across all three firmware versions. The only differences are: + +| Aspect | v2.06 (port 8193) | v2.13 FW1 (port 8194) | Rev.2 v2.10.4 (port 8197) | +|--------|-------|-----------|---------------| +| ARM_TRANSFER handler | CODE:0110 | CODE:0110 | CODE:00FA | +| GPIF control function | CODE:1919 | CODE:1800 | CODE:0D7C | +| Config byte location | IRAM 0x6D | IRAM 0x4F | IRAM 0x4E | +| Arm bit flag | bit 0x09 | bit 0x02 | bit 0x06 | +| INT4/INT6 vector | 0x1600 (dispatch table) | 0x1500 (thunk to 0x2252) | 0x1200 (thunk to 0x2084) | +| GPIF ISR action | Set flag, clear EXIF/GPIFIRQ | Set flag, clear EXIF/GPIFIRQ | Set flag, clear EXIF/GPIFIRQ | +| EP2FIFOCFG value | 0x0C (AUTOIN, ZEROLENIN, 8-bit) | 0x0C | 0x0C | +| IFCONFIG value | 0xEE (GPIF master, async, 48MHz) | 0xEE | 0xEE | +| FLOWSTATEA setting | \|= 0x09 (FSEN + FS3) | \|= 0x09 | \|= 0x09 | +| GPIFIE setting | \|= 0x3D | \|= 0x3D | \|= 0x3D | + +The register sequences within the GPIF control function are byte-for-byte identical (the same XRAM addresses, same values, same NOP delays). Differences are limited to which IRAM locations store state variables and which bit-addressable flags are used. + +--- + +## 14. Register Reference + +### XRAM Registers Written During Streaming + +| Address | Name | Start Value | Stop Value | Notes | +|---------|------|-------------|------------|-------| +| 0xE600 | CPUCS | (val & 0xE5) \| 0x10 | -- | 48 MHz CPU clock | +| 0xE601 | IFCONFIG | 0xEE | -- | GPIF master, async, 48 MHz | +| 0xE604 | FIFORESET | 0x80/0x02/0x04/0x06/0x08/0x00 | -- | Init only | +| 0xE60B | REVCTL | 0x03 | -- | NOAUTOARM + SKIPCOMMIT | +| 0xE618 | EP2FIFOCFG | 0x0C | -- | AUTOIN, ZEROLENIN, 8-bit | +| 0xE630 | GPIFTCB3 | 0x80 | -- | TC = 0x8000xxxx | +| 0xE631 | GPIFTCB2 | 0x00 | -- | TC continued | +| 0xE648 | OUTPKTEND | -- | 0x82 | Skip EP2 (stop only) | +| 0xE65C | GPIFIE | \|= 0x3D | -- | WF, TC, DONE, FF, WF2 | +| 0xE65D | GPIFIRQ | 0x01 | -- | Clear in ISR | +| 0xE668 | FLOWSTATEA | \|= 0x09 | -- | FSEN + FS[3] | +| 0xE670 | GPIFHOLDAMOUNT | 0x00 | -- | No hold time | +| 0xE6D0 | (GPIF trigger/config) | 0x02 | -- | EP2 read setup | +| 0xE6D1 | (GPIF trigger latch) | 0x00 | -- | Trigger latch | +| 0xE6D2 | (GPIF addr high) | 0x00 | -- | Address = 0 | +| 0xE6D3 | (GPIF addr low) | 0x01 then 0x00 | -- | Address setup | +| 0xE6F1 | EP2FIFOBCL | 0x00 | -- | Reset byte count | +| 0xE6F5 | EP2FIFOBCH | -- | 0xFF | Force flush (stop only) | + +### SFR Registers + +| Address | Name | Start Value | Stop Value | Notes | +|---------|------|-------------|------------|-------| +| 0x80 | P0 | 0x84 | \|= 0x80 | Bit 7 = streaming indicator | +| 0x91 | EXIF | &= 0xEF | -- | Clear bit 4 (INT4/6 flag) | +| 0xB0 | P3 | 0xE1 | \|= 0xE0 | Bits 7:5 = BCM4500 control | +| 0xBB | GPIFTRIG | 0x04 (read EP2) | (poll) | Trigger / status | + +--- + +## 15. Timing Considerations + +- **GPIF clock**: 48 MHz internal (IFCONFIG IFCLKSRC=1, 3048MHZ=1) +- **CPU clock**: 48 MHz (CPUCS CLKSPD=10) +- **GPIF async mode**: Data capture not synchronized to IFCLK edges; uses RDY pin handshaking +- **NOP delays**: 3 NOPs between consecutive XRAM writes to same register (~62.5 ns at 48 MHz) +- **EP2 buffer commit**: Automatic via AUTOIN when FIFO reaches packet size +- **GPIF re-trigger**: Automatic via FLOWSTATE when EP2 buffer space available +- **Transport stream rate**: BCM4500 outputs at the satellite symbol rate (up to 30 Msps), but the effective byte rate depends on modulation and coding. USB 2.0 High Speed bulk bandwidth (480 Mbps theoretical, ~35 MB/s practical) is more than sufficient for DVB-S transport streams (typically 1-5 MB/s). + +--- + +## Sources + +- Ghidra decompilation/disassembly of firmware images on ports 8193, 8194, 8197 +- Cypress CY7C68013A (FX2LP) Technical Reference Manual, register map +- Linux kernel `gp8psk` driver (`drivers/media/usb/dvb-usb/gp8psk.c`) +- Prior analysis: `gp8psk-driver-analysis.md`, `firmware-analysis-v206-vs-v213.md` diff --git a/kernel-fw01-analysis.md b/kernel-fw01-analysis.md new file mode 100644 index 0000000..7a5dbd4 --- /dev/null +++ b/kernel-fw01-analysis.md @@ -0,0 +1,425 @@ +# Genpix GP8PSK Kernel Firmware File Analysis + +## 1. Firmware File Search Results + +### Standard Locations Checked + +| Path | Result | +|------|--------| +| `/lib/firmware/dvb-usb-gp8psk-01.fw` | **Not found** | +| `/lib/firmware/dvb-usb-gp8psk-02.fw` | **Not found** | +| `/usr/lib/firmware/dvb-usb-gp8psk-01.fw` | **Not found** | +| `/usr/lib/firmware/dvb-usb-gp8psk-02.fw` | **Not found** | +| `/usr/share/firmware/` | Directory does not exist | + +### Package Search + +- `linux-firmware` (v20260110-1) is installed but contains **no gp8psk files** +- The `/usr/lib/firmware/WHENCE` manifest has **no gp8psk entry** +- `pacman -F dvb-usb-gp8psk-01.fw` returns no results -- no Arch package provides it +- `pacman -Ss gp8psk` finds nothing + +### linux-firmware Git Repository + +The upstream linux-firmware repository at `git.kernel.org` was checked. The gp8psk firmware files have **never been included** in the linux-firmware collection. Other DVB-USB firmware files exist (dib0700, it9135, terratec-h5-drxk), but gp8psk is absent. + +### Kernel get_dvb_firmware Script + +The kernel's `scripts/get_dvb_firmware` helper (which downloads firmware from various vendor sites) has **no entry for gp8psk**. The original firmware was presumably distributed by Genpix Electronics directly, likely packaged with their Windows BDA driver installer. + +### Filesystem-Wide Search + +A full filesystem search (`find / -name 'dvb-usb-gp8psk*'`) found only the compiled kernel module (`.ko.zst`), no `.fw` firmware files anywhere on the system. + +### Conclusion + +**Neither `dvb-usb-gp8psk-01.fw` nor `dvb-usb-gp8psk-02.fw` exist on this system or in any standard distribution channel.** The gp8psk firmware was never open-sourced or contributed to linux-firmware. + +--- + +## 2. Why SkyWalker-1 Works Without FW01/FW02 + +### The Driver Device Table Tells the Story + +From `gp8psk.c` (kernel source): + +```c +static struct dvb_usb_device_properties gp8psk_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-gp8psk-01.fw", + // ... + .devices = { + { .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver", + .cold_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_COLD], NULL }, // <-- HAS cold_ids + .warm_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_WARM], NULL }, + }, + { .name = "Genpix 8PSK-to-USB2 Rev.2 DVB-S receiver", + .cold_ids = { NULL }, // <-- NO cold_ids + .warm_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_2], NULL }, + }, + { .name = "Genpix SkyWalker-1 DVB-S receiver", + .cold_ids = { NULL }, // <-- NO cold_ids + .warm_ids = { &gp8psk_usb_table[GENPIX_SKYWALKER_1], NULL }, + }, + // ... + } +}; +``` + +**Only Rev.1 Cold (PID 0x0200) triggers firmware download.** When `cold_ids` is NULL, the DVB-USB framework skips the firmware loading step entirely. The device is assumed to already be in "warm" state. + +### FW01 Loading Path + +``` +USB device enumeration + | + +-- DVB-USB framework matches USB IDs + +-- Checks cold_ids vs warm_ids + | | + | +-- cold_ids match? -> dvb_usb_download_firmware() + | | | -> request_firmware("dvb-usb-gp8psk-01.fw") + | | | -> usb_cypress_load_firmware() [hexline parser] + | | | -> Device re-enumerates with warm PID + | | | + | +-- warm_ids match? -> Skip firmware, proceed to frontend attach + | + +-- SkyWalker-1 (PID 0x0203) -> warm_ids only -> NO FW01 NEEDED +``` + +### FW02 Loading Path + +From `gp8psk_power_ctrl()`: +```c +if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) // Only for Rev.1! + if (!(status & bm8pskFW_Loaded)) + if (gp8psk_load_bcm4500fw(d)) + return -EINVAL; +``` + +BCM4500 firmware loading (`dvb-usb-gp8psk-02.fw`) is **only attempted for Rev.1 Warm devices** (PID 0x0201). Rev.2 and SkyWalker devices have the BCM4500 firmware burned into ROM, so `bm8pskFW_Loaded` (bit 1 of `GET_8PSK_CONFIG`) is already set at boot. + +### Summary + +| Device | PID | Needs FW01? | Needs FW02? | Boot Source | +|--------|-----|-------------|-------------|-------------| +| Rev.1 Cold | 0x0200 | **YES** | -- | RAM (empty) | +| Rev.1 Warm | 0x0201 | No | **YES** | RAM (FW01 loaded) | +| Rev.2 | 0x0202 | No | No | EEPROM | +| SkyWalker-1 | 0x0203 | No | No | EEPROM | +| SkyWalker-2 | 0x0205 | No | No | EEPROM | +| SkyWalker CW3K | 0x0206 | No | No | EEPROM | + +**The SkyWalker-1 boots entirely from its onboard EEPROM. It never requests either firmware file from the host.** + +--- + +## 3. FW01 Expected Format: DVB-USB Binary Hexline + +### Format Specification + +The DVB-USB framework's `dvb_usb_get_hexline()` parser reads a **compact binary representation** of Intel HEX records. This is NOT standard Intel HEX text (`:10000000...`), nor the kernel's `ihex_binrec` format from ``. + +**Record structure:** + +``` +Offset Size Field +------ ---- ----- +0 1 len - Number of data bytes in this record +1 1 addr_lo - Target address, low byte +2 1 addr_hi - Target address, high byte +3 1 type - Record type (0x00=data, 0x01=EOF, 0x04=extended addr) +4 len data[] - Payload bytes +4+len 1 chk - Checksum byte +``` + +Total bytes per record: `len + 5` + +The parser advances position by `len + 5` each iteration. Type 0x04 records carry extended linear address bits in `data[0]` and `data[1]`, allowing 32-bit addressing. + +### Loading Mechanism + +`usb_cypress_load_firmware()` performs: +1. Halt FX2 CPU: write `0x01` to CPUCS register (0xE600) +2. For each hexline record: write `data[0..len-1]` to FX2 RAM at `addr` via 0xA0 vendor request +3. Restart FX2 CPU: write `0x00` to CPUCS register (0xE600) + +The 0xA0 vendor request is handled by the FX2's built-in boot ROM, which writes directly to program/data RAM regardless of whether user firmware is running. + +### Comparison with Other DVB-USB Firmware Files + +Other firmware files installed on this system confirm the format: + +| File | Size | First Record | +|------|------|-------------| +| `dvb-usb-dib0700-1.20.fw` | 33,768 bytes | len=2, addr=0x0000, type=0x04 (ext addr) | +| `dvb-usb-it9135-01.fw` | 8,128 bytes | len=3, addr=0x0000, type=0x03 | +| `dvb-usb-it9135-02.fw` | 5,834 bytes | len=3, addr=0x0000, type=0x03 | + +--- + +## 4. Our Extracted Firmware Format: Cypress C2 EEPROM Boot + +### C2 Header Structure + +All extracted firmware dumps are in **Cypress C2 IIC second-stage boot format**, as stored in the device's EEPROM. This format is read by the FX2's internal boot ROM on power-up. + +**C2 header (8 bytes):** + +``` +Offset Size Field +------ ---- ----- +0 1 marker - Always 0xC2 (indicates external memory, large code model) +1 2 VID - USB Vendor ID (little-endian) -> 0x09C0 (Genpix) +3 2 PID - USB Product ID (little-endian) +5 2 DID - Device ID (little-endian) -> 0x0000 +7 1 config - 0x40 (400kHz I2C bus speed) +``` + +**Followed by code segments:** + +``` +Offset Size Field +------ ---------- ----- +0 2 seg_len - Segment length (big-endian) +2 2 seg_addr - Target RAM address (big-endian) +4 seg_len data[] - Code/data bytes +``` + +**Terminated by:** +``` +0 2 0x8001 - High bit set signals terminator +2 2 entry - Entry point address (big-endian) -> 0xE600 (CPUCS) +``` + +### Decoded C2 Headers + +| File | VID | PID | Segments | Code Size | Entry | +|------|-----|-----|----------|-----------|-------| +| `skywalker1_eeprom.bin` (v2.06) | 0x09C0 | **0x0203** | 10 | 9,472 bytes | 0xE600 | +| `sw1_v213_fw_1_c2.bin` (v2.13.1) | 0x09C0 | **0x0203** | 10 | 9,322 bytes | 0xE600 | +| `sw1_v213_fw_2_c2.bin` (v2.13.2) | 0x09C0 | **0x0203** | 10 | 9,377 bytes | 0xE600 | +| `sw1_v213_fw_3_c2.bin` (v2.13.3) | 0x09C0 | **0x0203** | 10 | 9,369 bytes | 0xE600 | +| `rev2_v210_fw_1_c2.bin` (Rev2 v2.10) | 0x09C0 | **0x0202** | 9 | 8,843 bytes | 0xE600 | + +Note: The PID in the C2 header determines the USB Product ID that the FX2 enumerates with after booting. SkyWalker-1 variants all use PID 0x0203, while Rev.2 uses 0x0202. + +### C2 Segment Layout (All SkyWalker-1 Variants) + +All SkyWalker-1 C2 files use uniform 1023-byte segments (except the last): + +``` +Segment Address Length Notes +------- ------- ------ ----- +1 0x0000 1023 Contains reset vector, interrupt handlers +2 0x03FF 1023 +3 0x07FE 1023 +4 0x0BFD 1023 +5 0x0FFC 1023 +6 0x13FB 1023 +7 0x17FA 1023 +8 0x1BF9 1023 +9 0x1FF8 1023 +10 0x23F7 varies (115-265 bytes depending on version) +``` + +The 1023-byte segment size is the maximum the FX2 I2C EEPROM boot ROM reads per transaction (limited by internal buffer). + +--- + +## 5. Format Incompatibility: C2 vs Kernel Hexline + +The firmware as stored in EEPROM (C2 format) is **structurally different** from what the kernel expects (binary hexline format). They cannot be used interchangeably. + +| Property | C2 (EEPROM) | Hexline (Kernel FW01) | +|----------|-------------|-----------------------| +| Header | 8-byte C2 with VID/PID/DID | None | +| Address encoding | Big-endian 16-bit per segment | Little-endian split (addr_lo, addr_hi) per record | +| Data chunking | 1023-byte segments | Typically 16-byte records | +| Record overhead | 4 bytes per segment | 5 bytes per record | +| Terminator | 0x80xx + entry point | Type 0x01 EOF record | +| Entry point | Explicit in terminator | Implicit (CPUCS at 0xE600) | + +However, the **payload data is identical**. The "flat" extracted firmware (C2 segments concatenated, headers stripped) contains the same 8051 machine code that a hexline-format FW01 would carry. The difference is purely container format. + +### Theoretical Conversion: C2 -> Hexline + +A C2 file can be converted to the kernel's hexline format by: +1. Strip the 8-byte C2 header +2. For each segment: emit 16-byte hexline records with type=0x00, splitting the segment data +3. Append an EOF record (len=0, type=0x01) + +The resulting file would be the equivalent of `dvb-usb-gp8psk-01.fw` for that firmware version. For the v2.06 EEPROM (9,472 code bytes), this produces approximately 12,442 bytes in hexline format. + +--- + +## 6. FW02 (BCM4500 Demodulator Firmware) Analysis + +### Loading Protocol + +From the kernel source (`gp8psk_load_bcm4500fw()`), FW02 uses a **custom chunk protocol** sent via USB bulk endpoint: + +``` +Chunk format: + Byte 0: payload_length (N) + Bytes 1-3: header/address bytes (3 bytes) + Bytes 4..N+3: payload data + + Total chunk size = ptr[0] + 4 = payload_length + 4 + Maximum chunk size: 64 bytes (USB control transfer limit) + +Terminator: single byte 0xFF +``` + +The loading sequence: +1. Send `LOAD_BCM4500` command (0x88, wValue=1) to initiate transfer mode +2. Iterate through chunks until 0xFF terminator +3. Each chunk is sent via `dvb_usb_generic_write()` (bulk endpoint 0x01) + +### Relevance to SkyWalker-1 + +**FW02 is irrelevant for SkyWalker-1.** The BCM4500 demodulator firmware is burned into ROM on all Rev.2 and later hardware. The kernel driver explicitly checks: +```c +if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) // 0x0201 only +``` + +On the SkyWalker-1 (PID 0x0203), command 0x88 (`LOAD_BCM4500`) routes to the STALL handler in the FX2 firmware -- confirmed by Ghidra disassembly of all extracted firmware versions. + +--- + +## 7. Firmware Version Identification + +### Kernel dmesg Output + +``` +gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13 +gp8psk: usb in 149 operation failed. +gp8psk: failed to get FPGA version +gp8psk_fe: Frontend attached +gp8psk: found Genpix USB device pID = 203 (hex) +``` + +The version is read via USB command `GET_FW_VERS` (0x92), which returns 6 bytes: `[minor, build, major, day, month, year-2000]`. The FX2 firmware computes and returns this at runtime from internal constants -- the version bytes are not stored as a simple searchable pattern in the binary. + +### Kernel Firmware Revision Constants + +From `gp8psk-fe.h`: +```c +#define GP8PSK_FW_REV1 0x020604 // v2.06.4 +#define GP8PSK_FW_REV2 0x020704 // v2.07.4 +``` + +The kernel only knows about two firmware revisions. Our v2.10 and v2.13 firmwares are significantly newer and unknown to the kernel. + +### FPGA Version Failure + +``` +gp8psk: usb in 149 operation failed. +gp8psk: failed to get FPGA version +``` + +Command 0x95 (`GET_FPGA_VERS`, decimal 149) fails on the SkyWalker-1. This command likely targets a separate FPGA that exists only on certain hardware revisions, or the SkyWalker-1 firmware does not implement this vendor command. The driver logs the failure but continues normally. + +### Version Strings in Firmware + +No human-readable version strings were found in the v2.06 or Rev2 v2.10 firmware binaries. The v2.13.x variants contain a single notable string: + +``` +Offset 0x1880 (all three v2.13 sub-variants): +"Tampering is detected. Attempt is logged. Warranty is voided ! \n" +``` + +Followed by bytes `01 10 aa 82 02 41 41 83` -- likely I2C register write commands related to the tampering detection/logging mechanism. This string is absent from v2.06 and v2.10 firmware, indicating Genpix added anti-tampering measures in the v2.13 firmware generation. + +--- + +## 8. Binary Comparison: Extracted Firmware Dumps + +### File Sizes + +| File | Size | Format | +|------|------|--------| +| `skywalker1_eeprom_flat.bin` (v2.06.4) | 9,472 bytes | Flat binary | +| `sw1_v213_fw_1_flat.bin` (v2.13.1) | 9,322 bytes | Flat binary | +| `sw1_v213_fw_2_flat.bin` (v2.13.2) | 9,377 bytes | Flat binary | +| `sw1_v213_fw_3_flat.bin` (v2.13.3) | 9,369 bytes | Flat binary | +| `rev2_v210_fw_1_flat.bin` (Rev2 v2.10.4) | 8,843 bytes | Flat binary | + +### Byte-Level Similarity Matrix + +Percentage of matching bytes within the shared length of each pair: + +| | v2.06 | v2.13.1 | v2.13.2 | v2.13.3 | Rev2 v2.10 | +|---|---|---|---|---|---| +| **v2.06** | -- | 4.8% | 4.3% | 4.3% | 6.0% | +| **v2.13.1** | | -- | 57.2% | 59.4% | 8.0% | +| **v2.13.2** | | | -- | 83.5% | 5.8% | +| **v2.13.3** | | | | -- | 5.8% | +| **Rev2 v2.10** | | | | | -- | + +### Differing Byte Counts + +| Pair | Different Bytes | Shared Length | Mismatch Rate | +|------|----------------|---------------|---------------| +| v2.06 vs v2.13.1 | 8,878 | 9,322 | 95.2% | +| v2.06 vs Rev2 v2.10 | 8,312 | 8,843 | 94.0% | +| v2.13.1 vs Rev2 v2.10 | 8,133 | 8,843 | 92.0% | +| v2.13.1 vs v2.13.2 | 3,994 | 9,322 | 42.8% | +| v2.13.2 vs v2.13.3 | 1,549 | 9,369 | 16.5% | + +### Longest Identical Run + +| Pair | Longest Match | Starting Offset | +|------|--------------|-----------------| +| v2.06 vs v2.13.1 | 22 bytes | 0x0055 | +| v2.06 vs Rev2 v2.10 | 36 bytes | 0x080C | + +### Interpretation + +The very low byte-level similarity between different major versions (v2.06 vs v2.10 vs v2.13) indicates **complete recompilation** with different toolchains or linker configurations. Functions are relocated to different addresses even when their logic is identical. The first byte of every flat dump already differs (the second byte of the reset vector LJMP target), confirming different code layout starting from the entry point. + +Within the v2.13 family, FW2 and FW3 share 83.5% of bytes, reflecting minor hardware-specific changes (parallel bus timing, GPIO defaults, IRAM layout). FW1 differs more substantially (57-59% match) because it targets fundamentally different hardware (I2C vs parallel bus demodulator interface). + +--- + +## 9. Other Extracted Files + +### FX2 Internal ROM (`skywalker1_fx2_internal.bin`, 8,192 bytes) + +The FX2 internal ROM dump does **not** contain program code in a recognizable 8051 format. It appears to be the FX2's built-in boot ROM (mask ROM), which handles: +- USB enumeration in "unconfigured" state +- I2C EEPROM boot loading (reading C2 format images) +- 0xA0 vendor request handling (RAM write for host firmware upload) + +This ROM is identical across all Cypress CY7C68013A chips and is not Genpix-specific. + +### FX2 External Memory (`skywalker1_fx2_external.bin`, 65,536 bytes) + +A 64KB dump of the FX2's full external address space. The first ~9.5KB contains the same code as `skywalker1_eeprom_flat.bin`; the remainder is the full 64KB memory space including RAM, SFR shadows, and unused regions. + +--- + +## 10. Summary of Findings + +1. **Firmware files `dvb-usb-gp8psk-01.fw` and `dvb-usb-gp8psk-02.fw` do not exist** on this system, in linux-firmware, or in any standard distribution. They were never open-sourced. + +2. **SkyWalker-1 does not need either file.** The kernel driver only requests FW01 for Rev.1 Cold devices (PID 0x0200) and FW02 for Rev.1 Warm devices (PID 0x0201). The SkyWalker-1 boots from its onboard EEPROM and appears directly as a "warm" device (PID 0x0203). + +3. **FW01 format** would be DVB-USB binary hexline records (a compact binary encoding of Intel HEX), loaded via Cypress FX2 0xA0 vendor requests to CPUCS. Our extracted dumps are in Cypress C2 EEPROM boot format -- different container, identical payload. + +4. **FW02 format** is a custom Genpix chunk protocol (length byte + 3 header bytes + data, terminated by 0xFF), loaded via USB bulk transfers after a LOAD_BCM4500 command (0x88). This is only relevant for Rev.1 hardware. + +5. **The currently running firmware** is v2.06.4 (build 2007/07/13), matching the kernel's `GP8PSK_FW_REV1` constant (0x020604). This is the original factory firmware burned into the SkyWalker-1 EEPROM. + +6. **The v2.13 firmware** (extracted from the Windows updater) adds anti-tampering detection and supports three different hardware sub-variants. The v2.10 Rev.2 firmware is for a different product (PID 0x0202). + +--- + +## Sources + +- Linux kernel 6.16.5 source: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h` +- Linux kernel 6.16.5 source: `drivers/media/dvb-frontends/gp8psk-fe.h` +- Linux kernel 6.16.5 source: `drivers/media/usb/dvb-usb/dvb-usb-firmware.c` +- Linux kernel 6.16.5 source: `include/linux/ihex.h` +- Firmware dumps: `/home/rpm/claude/ham/satellite/genpix/skywalker-1/firmware-dump/` +- `dmesg` output from running SkyWalker-1 hardware +- linux-firmware git repository (https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git)