Add Linux gp8psk kernel driver analysis and firmware-driver correlation

Maps all vendor USB control commands (0x80-0x9D) used by the kernel driver
against firmware implementations across all 4 extracted versions.

Key findings:
- PID 0x0203 confirmed in kernel 6.16.5 module aliases (our device)
- PID 0x0204 is a separate SkyWalker-1 hardware revision
- LOAD_BCM4500 (0x88) intentionally STALLs on Rev.2/SkyWalker hardware
- BCM4500 firmware loading protocol documented (64-byte chunked via EP0)
- Complete boot, tuning, DiSEqC, and streaming sequences mapped
This commit is contained in:
Ryan Malloy 2026-02-11 10:29:43 -07:00
parent ec4bdb8493
commit da08d1b099

280
gp8psk-driver-analysis.md Normal file
View File

@ -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)