diff --git a/gp8psk-driver-analysis.md b/gp8psk-driver-analysis.md new file mode 100644 index 0000000..acb10f2 --- /dev/null +++ b/gp8psk-driver-analysis.md @@ -0,0 +1,280 @@ +# Genpix SkyWalker-1 DVB-S Linux Kernel Driver Analysis + +## Overview + +The Genpix GP8PSK driver is a Linux kernel DVB-USB compliant driver (found at `drivers/media/usb/dvb-usb/gp8psk.c`) that supports multiple Genpix satellite receiver models. The driver communicates with a Cypress FX2 microcontroller via USB vendor control requests to manage DVB-S satellite reception including demodulation, LNB power control, DiSEqC switching, and signal monitoring. + +--- + +## 1. VENDOR USB CONTROL COMMANDS (0x80-0x9D) + +All vendor commands use USB control transfers with the following pattern: +- **USB Type**: `USB_TYPE_VENDOR` (vendor-specific request) +- **Direction**: `USB_DIR_IN` (device-to-host) or `USB_DIR_OUT` (host-to-device) +- **Timeout**: 2000ms +- **Retry Logic**: Up to 3 attempts for IN operations if partial data received +- **Data Buffer**: 80 bytes maximum (kernel driver state structure) + +### Complete Vendor Command Map + +| Cmd | Name | Direction | wValue | wIndex | wLength | Purpose | +|-----|------|-----------|--------|--------|---------|---------| +| 0x80 | **GET_8PSK_CONFIG** | IN | 0x0000 | 0x0000 | 1 byte | Read device configuration status byte | +| 0x81 | SET_8PSK_CONFIG | OUT | varies | 0x0000 | 0 | Set config (STALL - not implemented) | +| 0x82 | (reserved) | -- | -- | -- | -- | STALL - not used | +| 0x83 | **I2C_WRITE** | OUT | dev_addr | reg_addr | N bytes | Write to I2C device (BCM4500 demod) | +| 0x84 | **I2C_READ** | IN | dev_addr | reg_addr | N bytes | Read from I2C device (BCM4500 demod) | +| 0x85 | **ARM_TRANSFER** | OUT | onoff (0/1) | 0x0000 | 0 | Start/stop MPEG-2 stream transfer | +| 0x86 | **TUNE_8PSK** | OUT | 0x0000 | 0x0000 | 10 bytes | Set tuning parameters | +| 0x87 | **GET_SIGNAL_STRENGTH** | IN | 0x0000 | 0x0000 | 6 bytes | Read SNR (signal quality) | +| 0x88 | **LOAD_BCM4500** | OUT | 1 (start) | 0x0000 | 0 | Initiate BCM4500 firmware download | +| 0x89 | **BOOT_8PSK** | IN | onoff (0/1) | 0x0000 | 1 byte | Power on/off 8PSK demodulator | +| 0x8A | **START_INTERSIL** | IN | onoff (0/1) | 0x0000 | 1 byte | Enable/disable LNB power supply | +| 0x8B | **SET_LNB_VOLTAGE** | OUT | voltage (0/1) | 0x0000 | 0 | Set LNB to 13V (0) or 18V (1) | +| 0x8C | **SET_22KHZ_TONE** | OUT | onoff (0/1) | 0x0000 | 0 | Enable/disable 22 kHz DiSEqC tone | +| 0x8D | **SEND_DISEQC_COMMAND** | OUT | msg[0] | 0x0000 | len | Send DiSEqC message to dish switch | +| 0x8E | SET_DVB_MODE | OUT | 1 | 0x0000 | 0 | Enable DVB-S mode (STALL on some revisions) | +| 0x8F | (unknown) | -- | -- | -- | -- | Unknown/internal use | +| 0x90 | **GET_SIGNAL_LOCK** | IN | 0x0000 | 0x0000 | 1 byte | Read signal lock status bit | +| 0x91-0x98 | (internal use) | -- | -- | -- | -- | I2C/diagnostic reads | +| 0x99 | **GET_DEMOD_STATUS** (v2.13+) | IN | 0x0000 | 0x0000 | 1 byte | Read BCM4500 register 0xF9 via I2C | +| 0x9A | **INIT_DEMOD** (v2.13+) | OUT | 0x0000 | 0x0000 | 0 | Trigger demodulator re-init (up to 3 attempts) | +| 0x9B | (reserved) | -- | -- | -- | -- | STALL - not used | +| 0x9C | **DELAY_COMMAND** (v2.13+) | OUT | delay_param | 0x0000 | 0 | Perform tuning/acquisition delay with polling | +| 0x9D | **CW3K_INIT** | OUT | onoff (0/1) | 0x0000 | 0 | Initialize SkyWalker CW3K model | + +### Configuration Status Byte (GET_8PSK_CONFIG - 0x80) + +The returned byte is a bit-mapped status register: + +``` +Bit 0 (0x01): bm8pskStarted - Device booted and running +Bit 1 (0x02): bm8pskFW_Loaded - BCM4500 firmware loaded +Bit 2 (0x04): bmIntersilOn - LNB power supply enabled +Bit 3 (0x08): bmDVBmode - DVB mode enabled +Bit 4 (0x10): bm22kHz - 22 kHz tone active +Bit 5 (0x20): bmSEL18V - 18V LNB voltage selected (else 13V) +Bit 6 (0x40): bmDCtuned - DC offset tuning complete +Bit 7 (0x80): bmArmed - MPEG-2 stream transfer armed +``` + +--- + +## 2. FIRMWARE LOADING SEQUENCE + +### Two-Stage Firmware Loading + +The device requires **two separate firmware files**: + +#### Stage 1: FX2 RAM Code (Bootloader Phase) +- **File**: `dvb-usb-gp8psk-01.fw` +- **Purpose**: Cypress FX2 microcontroller firmware +- **When**: Loaded automatically by DVB-USB framework during device enumeration (cold-to-warm boot transition) +- **How**: Standard USB firmware upload via control endpoint (0xA0 vendor request to CPUCS) +- **Device Mode**: Cold boot (VID 0x09C0, PID 0x0200) + +#### Stage 2: BCM4500 Demodulator Firmware (Tuner Phase) +- **File**: `dvb-usb-gp8psk-02.fw` +- **Purpose**: Broadcom BCM4500 DVB-S demodulator chip firmware +- **When**: Loaded after device power-on via LOAD_BCM4500 command +- **How**: Custom binary protocol via USB control transfers +- **Conditions**: Only for Rev.1 Warm devices; Rev.2 and SkyWalker have firmware burned in ROM + +### BCM4500 Firmware Loading Protocol + +```c +// Step 1: Initiate load +gp8psk_usb_out_op(device, LOAD_BCM4500, 1, 0, NULL, 0); // 0x88 cmd, wValue=1 + +// Step 2: Download firmware chunks +// Format: [chunk_length] [data...] repeated, terminated by 0xFF +ptr = fw_data; +while (ptr[0] != 0xFF) { + chunk_size = ptr[0] + 4; // length byte + 3 extra bytes + if (chunk_size > 64) ERROR("chunk too large"); + buf = copy chunk_size bytes from ptr; + usb_control_msg(device, USB_SNDCTRLPIPE, 0, ..., buf, chunk_size, 2000ms); + ptr += chunk_size; +} +``` + +### Boot Sequence Flow (Power-On) + +``` +1. Read device config (GET_8PSK_CONFIG - 0x80) + +-- Check bit 0: Device started? + +2. If not started: + +-- Boot device (BOOT_8PSK - 0x89, wValue=1) + +-- Get firmware version (via 0x0B: FW_VERSION_READ) + +3. If firmware not loaded (check bit 1): + +-- Load BCM4500 firmware (LOAD_BCM4500 - 0x88) + +4. If LNB not powered (check bit 2): + +-- Enable LNB supply (START_INTERSIL - 0x8A, wValue=1) + +5. Set DVB mode (on some revisions): + +-- SET_DVB_MODE - 0x8E, wValue=1 + +6. Abort any pending MPEG transfer: + +-- ARM_TRANSFER - 0x85, wValue=0 (cancel) + +7. Ready for tuning/reception +``` + +--- + +## 3. TUNING AND DEMODULATION FLOW + +### Frequency Tuning (TUNE_8PSK - 0x86) + +The tuning command transmits a 10-byte parameter structure: + +``` +Bytes 0-3: Symbol Rate (Little-Endian 32-bit, in sps) +Bytes 4-7: Tuner Frequency (Little-Endian 32-bit, in kHz) +Byte 8: Modulation Type (see table below) +Byte 9: Inner FEC Rate / reserved +``` + +### Modulation Types + +``` +ADV_MOD_DVB_QPSK = 0 // DVB-S standard QPSK +ADV_MOD_TURBO_QPSK = 1 // Turbo QPSK +ADV_MOD_TURBO_8PSK = 2 // Turbo 8PSK (Trellis) +ADV_MOD_TURBO_16QAM = 3 // Turbo 16QAM +ADV_MOD_DCII_C_QPSK = 4 // Digicipher II Combo +ADV_MOD_DCII_I_QPSK = 5 // Digicipher II I-stream (split) +ADV_MOD_DCII_Q_QPSK = 6 // Digicipher II Q-stream (split) +ADV_MOD_DCII_C_OQPSK = 7 // Digicipher II offset QPSK +ADV_MOD_DSS_QPSK = 8 // DSS/DIRECTV QPSK +ADV_MOD_DVB_BPSK = 9 // DVB-S BPSK +``` + +### Complete Tuning Sequence + +``` +1. Configure LNB voltage based on polarization: + H/CIRCULAR_L -> 18V (SET_LNB_VOLTAGE - 0x8B, wValue=1) + V/CIRCULAR_R -> 13V (SET_LNB_VOLTAGE - 0x8B, wValue=0) + +2. Set 22 kHz tone: + SET_22KHZ_TONE - 0x8C, wValue=0/1 + +3. Send DiSEqC switch command if needed (see section 4) + +4. Send tuning command: + TUNE_8PSK - 0x86, 10-byte parameter buffer + +5. Poll for lock: + GET_SIGNAL_LOCK - 0x90 (returns 1 byte, non-zero = locked) +``` + +### Signal Quality Monitoring + +**GET_SIGNAL_STRENGTH (0x87)** returns 6-byte buffer: +``` +Bytes 0-1: SNR value (LE 16-bit, dBu*256 units) +Bytes 2-5: Reserved/diagnostics +``` + +SNR scaling: `snr * 17` maps to 0-65535 (100% at SNR >= 0x0F00) + +--- + +## 4. DiSEqC COMMAND HANDLING (0x8D) + +### Tone Burst (Mini DiSEqC) +```c +// For legacy 2-way switches +gp8psk_usb_out_op(device, SEND_DISEQC_COMMAND, + burst_value, // SEC_MINI_A=0x00 or SEC_MINI_B=0x01 + 0, NULL, 0); +``` + +### Full DiSEqC Message (3-6 bytes) +```c +// Standard DiSEqC 1.0/1.1/1.2 messages +gp8psk_usb_out_op(device, SEND_DISEQC_COMMAND, + msg[0], // Framing byte as wValue + 0, + msg, // Full message buffer + msg_len); // 3-6 bytes +``` + +Common framing bytes: 0xE0 (broadcast, no reply), 0xE1 (addressed, no reply) + +--- + +## 5. DEVICE TABLE AND USB IDS + +### Kernel Module Aliases (Linux 6.16.5) + +``` +usb:v09C0p0200 - Rev.1 Cold boot (requires FX2 firmware upload) +usb:v09C0p0201 - Rev.1 Warm (requires BCM4500 FW via 0x88) +usb:v09C0p0202 - Rev.2 (BCM4500 in ROM) +usb:v09C0p0203 - SkyWalker-1 (PID confirmed on actual hardware) +usb:v09C0p0204 - SkyWalker-1 (alternate revision) +usb:v09C0p0206 - SkyWalker CW3K (requires CW3K_INIT 0x9D) +``` + +Note: PID 0x0205 (SkyWalker-2) is absent from the 6.16.5 kernel build. +Note: PID 0x0203 was not in earlier kernel versions (e.g., v6.6.1). + +### Device Properties + +```c +gp8psk_properties { + usb_ctrl = CYPRESS_FX2; + firmware = "dvb-usb-gp8psk-01.fw"; + num_adapters = 1; + endpoint = 0x82 (IN bulk); + stream = USB_BULK; + count = 7 URBs; + buffersize = 8192 bytes per URB; + generic_bulk_ctrl_endpoint = 0x01; +} +``` + +--- + +## 6. CORRELATION: KERNEL DRIVER vs FIRMWARE ANALYSIS + +### Commands Used by Kernel Driver + +| Command | Driver Uses | v2.06 FW | v2.13 FW | Rev.2 FW | +|---------|-------------|----------|----------|----------| +| 0x80 GET_8PSK_CONFIG | Boot check | OK | OK | OK | +| 0x83 I2C_WRITE | BCM4500 reg writes | OK | OK | OK | +| 0x84 I2C_READ | BCM4500 reg reads | OK | OK | OK | +| 0x85 ARM_TRANSFER | Stream start/stop | OK | OK | OK | +| 0x86 TUNE_8PSK | Frequency tuning | OK | OK | OK | +| 0x87 GET_SIGNAL_STRENGTH | SNR readback | OK | Changed | OK | +| 0x88 LOAD_BCM4500 | BCM4500 FW load | **STALL** | **STALL** | **STALL** | +| 0x89 BOOT_8PSK | Power on/off | OK | OK | OK | +| 0x8A START_INTERSIL | LNB power | OK | OK | OK | +| 0x8B SET_LNB_VOLTAGE | 13V/18V | OK | OK | OK | +| 0x8C SET_22KHZ_TONE | Tone control | OK | OK | OK | +| 0x8D SEND_DISEQC | DiSEqC messages | OK (GPIO) | OK (I2C) | OK | +| 0x90 GET_SIGNAL_LOCK | Lock status | OK | OK | OK | +| 0x9D CW3K_INIT | CW3K only | N/A | v2.13 only | N/A | + +### Key Finding: LOAD_BCM4500 (0x88) STALLs + +Command 0x88 routes to the STALL handler in all extracted firmware versions. +The kernel driver only sends this command for Rev.1 Warm (PID 0x0201) devices, +after checking that `bm8pskFW_Loaded` (bit 1) is NOT set. On Rev.2/SkyWalker +hardware, this bit is already set at boot (BCM4500 firmware in ROM), so the +driver never attempts the load. The STALL is a safety net. + +--- + +## Sources + +- Linux kernel 6.16.5 `dvb-usb-gp8psk` module (installed on analysis system) +- Linux kernel source: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c` +- Windows BDA driver source: `SkyWalker1_Final_Release/Include/SkyWalker1Control.h` +- Firmware reverse engineering via Ghidra (ports 8193-8197)