skywalker-1/skywalker1-hardware-reference.md
Ryan Malloy a2845c37fb Add SkyWalker-1 tuning tool and consolidated hardware reference
Python tool (tools/tune.py) implements all vendor USB control
commands for tuning, LNB control, DiSEqC switching, and MPEG-2
transport stream capture via pyusb. Includes CLI subcommands for
status, tune, stream, diseqc, and lnb operations.

Consolidated hardware reference merges all Phase 1 analysis into
a single 12-section document covering the complete USB interface,
all 30 vendor commands, BCM4500 demodulator protocol, GPIF
streaming path, DiSEqC timing, and cross-version firmware
comparison.
2026-02-11 12:30:05 -07:00

812 lines
35 KiB
Markdown

# Genpix SkyWalker-1 Hardware Reference
Consolidated technical reference for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver, derived from Linux kernel driver analysis, firmware reverse engineering (Ghidra), and Windows BDA driver source review.
---
## 1. Overview
The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around two ICs:
- **Cypress CY7C68013A** (FX2LP) -- USB 2.0 Hi-Speed microcontroller (8051 core, 48 MHz)
- **Broadcom BCM4500** -- DVB-S / 8PSK satellite demodulator
The FX2 handles USB communication, LNB control, DiSEqC signaling, and orchestrates tuning via I2C commands to the BCM4500. The BCM4500 performs RF demodulation, FEC decoding, and outputs an MPEG-2 transport stream on an 8-bit parallel bus. The GPIF engine inside the FX2 transfers the transport stream directly into a USB bulk endpoint with zero firmware intervention in the data path.
### Supported Modulations
| Index | Modulation | Constant |
|-------|-----------|----------|
| 0 | DVB-S QPSK | `ADV_MOD_DVB_QPSK` |
| 1 | Turbo-coded QPSK | `ADV_MOD_TURBO_QPSK` |
| 2 | Turbo-coded 8PSK | `ADV_MOD_TURBO_8PSK` |
| 3 | Turbo-coded 16QAM | `ADV_MOD_TURBO_16QAM` |
| 4 | Digicipher II Combo | `ADV_MOD_DCII_C_QPSK` |
| 5 | Digicipher II I-stream (split) | `ADV_MOD_DCII_I_QPSK` |
| 6 | Digicipher II Q-stream (split) | `ADV_MOD_DCII_Q_QPSK` |
| 7 | Digicipher II Offset QPSK | `ADV_MOD_DCII_C_OQPSK` |
| 8 | DSS QPSK | `ADV_MOD_DSS_QPSK` |
| 9 | DVB-S BPSK | `ADV_MOD_DVB_BPSK` |
DVB-S2 is **not** supported (incompatible FEC architecture).
### RF Specifications
| Parameter | Value |
|-----------|-------|
| IF frequency range | 950 -- 2150 MHz |
| Symbol rate | 256 Ksps -- 30 Msps |
| Input connector | IEC F-type female |
| LNB voltage | 13/18V (or 14/19V with USE_EXTRA_VOLT) |
| LNB current | 450 mA continuous / 750 mA burst |
| Switch control | 22 kHz, Tone Burst, DiSEqC 1.0/1.2, Legacy Dish Network |
---
## 2. USB Interface
### VID/PID Table
All Genpix products share VID `0x09C0`:
| PID | Product | cold_ids | warm_ids | Kernel Module | Notes |
|-----|---------|----------|----------|---------------|-------|
| 0x0200 | 8PSK-to-USB2 Rev.1 Cold | Yes | No | gp8psk | Requires FW01 upload to RAM |
| 0x0201 | 8PSK-to-USB2 Rev.1 Warm | No | Yes | gp8psk | Requires FW02 (BCM4500) |
| 0x0202 | 8PSK-to-USB2 Rev.2 | No | Yes | gp8psk | Boots from EEPROM |
| 0x0203 | **SkyWalker-1** | No | Yes | gp8psk | Boots from EEPROM |
| 0x0204 | SkyWalker-1 (alternate) | No | Yes | gp8psk | Boots from EEPROM |
| 0x0205 | SkyWalker-2 | -- | -- | -- | Not in kernel 6.16.5 |
| 0x0206 | SkyWalker CW3K | No | Yes | gp8psk | Requires CW3K_INIT (0x9D) |
PID 0x0203 was added to the kernel device table after v6.6.1.
### USB Endpoints and Streaming Properties
| Property | Value |
|----------|-------|
| Control endpoint | EP0 (default, vendor requests) |
| Bulk IN endpoint | EP2 (0x82) -- MPEG-2 transport stream |
| Generic bulk CTRL endpoint | 0x01 (BCM4500 FW02 upload only) |
| URB count | 7 |
| URB buffer size | 8192 bytes each |
| Stream type | USB_BULK |
| FX2 controller type | CYPRESS_FX2 |
### Warm Boot Behavior
The SkyWalker-1 (PID 0x0203) enumerates directly as a "warm" device. The DVB-USB framework skips firmware download when `cold_ids` is NULL. No host-side firmware files (`dvb-usb-gp8psk-01.fw` or `dvb-usb-gp8psk-02.fw`) are required. These files were never open-sourced or included in the `linux-firmware` package.
| Device | 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 CW3K (0x0206) | No | No | EEPROM |
---
## 3. Complete Vendor Command Reference
All vendor commands use USB control transfers:
- **USB Type**: `USB_TYPE_VENDOR`
- **Timeout**: 2000 ms
- **Retry**: Up to 3 attempts for IN operations if partial data received
- **Data buffer maximum**: 80 bytes (kernel driver)
### 3.1 Command Table
The vendor command dispatcher at CODE:0056 validates `bRequest` in the range 0x80--0x9D (30 entries) and dispatches via an indexed jump table at CODE:0076. Rev.2 supports only 0x80--0x9A (27 entries).
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose | Linux | Windows | v2.06 | Rev.2 | v2.13 |
|-----|------|-----|--------|--------|---------|---------|-------|---------|-------|-------|-------|
| 0x80 | GET_8PSK_CONFIG | IN | 0 | 0 | 1 | Read configuration status byte | Yes | Yes | OK | OK | OK |
| 0x81 | SET_8PSK_CONFIG | OUT | varies | 0 | 0 | Set config (reserved) | No | No | STALL | STALL | STALL |
| 0x82 | (reserved) | -- | -- | -- | -- | Reserved | No | No | STALL | STALL | STALL |
| 0x83 | I2C_WRITE | OUT | dev_addr | reg_addr | N | Write to I2C device | Yes | Yes | OK | OK | OK |
| 0x84 | I2C_READ | IN | dev_addr | reg_addr | N | Read from I2C device | Yes | Yes | OK | OK | OK |
| 0x85 | ARM_TRANSFER | OUT | 0/1 | 0 | 0 | Start (1) / stop (0) MPEG-2 stream | Yes | Yes | OK | OK | OK |
| 0x86 | TUNE_8PSK | OUT | 0 | 0 | 10 | Set tuning parameters (see Section 6) | Yes | Yes | OK | OK | OK |
| 0x87 | GET_SIGNAL_STRENGTH | IN | 0 | 0 | 6 | Read SNR and diagnostics | Yes | Yes | OK | OK | Changed |
| 0x88 | LOAD_BCM4500 | OUT | 1 | 0 | 0 | Initiate BCM4500 FW download | Yes* | No | STALL | STALL | STALL |
| 0x89 | BOOT_8PSK | IN | 0/1 | 0 | 1 | Power on (1) / off (0) demodulator | Yes | Yes | OK | OK | OK |
| 0x8A | START_INTERSIL | IN | 0/1 | 0 | 1 | Enable (1) / disable (0) LNB supply | Yes | Yes | OK | OK | OK |
| 0x8B | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | 0 | 13V (0) or 18V (1) | Yes | Yes | OK | OK | OK |
| 0x8C | SET_22KHZ_TONE | OUT | 0/1 | 0 | 0 | Tone off (0) or on (1) | Yes | Yes | OK | OK | OK |
| 0x8D | SEND_DISEQC_COMMAND | OUT | msg[0] | 0 | len | DiSEqC message or tone burst | Yes | Yes | OK | OK | OK |
| 0x8E | SET_DVB_MODE | OUT | 1 | 0 | 0 | Enable DVB-S mode | Yes | No | STALL | STALL | STALL |
| 0x8F | SET_DN_SWITCH | OUT | cmd7bit | 0 | 0 | Legacy Dish Network switch protocol | Yes | Yes | OK | OK | OK |
| 0x90 | GET_SIGNAL_LOCK | IN | 0 | 0 | 1 | Read signal lock status | Yes | Yes | OK | OK | OK |
| 0x91 | I2C_ADDR_ADJUST | IN | 0/1 | 0 | 1 | Inc/dec internal IRAM counter (debug) | No | No | OK | OK | OK |
| 0x92 | GET_FW_VERS | IN | 0 | 0 | 6 | Read firmware version + build date | Yes | No | OK | OK | OK |
| 0x93 | GET_SERIAL_NUMBER | IN | 0 | 0 | 4 | Read 4-byte serial from I2C EEPROM | No | Yes | OK | OK | OK |
| 0x94 | USE_EXTRA_VOLT | OUT | 0/1 | 0 | 0 | Enable +1V LNB boost (14V/19V) | Yes | Yes | OK | OK | OK |
| 0x95 | GET_FPGA_VERS | IN | 0 | 0 | 1 | Read EEPROM hardware/platform ID | Yes | No | OK | OK | OK |
| 0x96 | SET_LNB_GPIO_MODE | OUT | 0/1 | 0 | 0 | Configure LNB GPIO output enables | No | No | OK | OK | OK |
| 0x97 | SET_GPIO_PINS | OUT | bitmap | 0 | 0 | Direct write to LNB GPIO pins | No | No | OK | OK | OK |
| 0x98 | GET_GPIO_STATUS | IN | 0 | 0 | 1 | Read LNB feedback GPIO pin | No | No | OK | OK | OK |
| 0x99 | GET_DEMOD_STATUS | IN | 0 | 0 | 1 | Read BCM4500 register 0xF9 | No | No | STALL | Proto | OK |
| 0x9A | INIT_DEMOD | OUT | 0 | 0 | 0 | Trigger demod re-init (3 attempts) | No | No | STALL | Proto | OK |
| 0x9B | (reserved) | -- | -- | -- | -- | Reserved | No | No | STALL | N/A | STALL |
| 0x9C | DELAY_COMMAND | OUT | delay | 0 | 0 | Host-controlled tuning delay + poll | No | No | STALL | N/A | OK |
| 0x9D | CW3K_INIT / SET_MODE_FLAG | OUT | 0/1 | 0 | 0 | CW3K init or conditional demod reset | Yes** | No | OK | N/A | Changed |
\* Linux driver only sends LOAD_BCM4500 for Rev.1 Warm (PID 0x0201). On SkyWalker-1, `bm8pskFW_Loaded` is already set and 0x88 routes to STALL.
\*\* Linux driver only sends CW3K_INIT for SkyWalker CW3K (PID 0x0206).
**Status key**: OK = implemented, STALL = routes to stall handler (endpoint stall returned), Proto = partial/prototype implementation, N/A = command index out of range (Rev.2 only supports 0x80--0x9A), Changed = implementation differs between versions.
### 3.2 Detailed Parameter Formats
**0x8D SEND_DISEQC_COMMAND**: When `wLength > 0`, the payload is a standard DiSEqC message (3--6 bytes) with `wValue` set to `msg[0]` (framing byte, typically 0xE0 or 0xE1). When `wLength == 0` and `wValue == 0`, a tone burst A is sent. When `wLength == 0` and `wValue != 0`, a tone burst B is sent.
**0x8F SET_DN_SWITCH**: `wValue` carries a 7-bit Dish Network switch command (LSB-first), bit-banged on GPIO P0.4 with specific timing. The 8th bit of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.
**0x92 GET_FW_VERS**: Returns 6 bytes: `[minor_minor, minor, major, day, month, year-2000]`. Full version = `major<<16 | minor<<8 | minor_minor`. Build date = `(2000+year)/month/day`.
**0x93 GET_SERIAL_NUMBER**: Returns 4 bytes read from I2C EEPROM at device address 0x51 (7-bit), extracted at 8-bit intervals using a shift/rotate routine.
**0x94 USE_EXTRA_VOLT**: `wValue=1` writes 0x6A to XRAM 0xE0B6; `wValue=0` writes 0x62. The difference is bit 3 (0x08), which controls the extra voltage boost on the LNB power regulator.
**0x95 GET_FPGA_VERS**: Reads from I2C EEPROM at 0x51. Despite the name, there is no FPGA -- this returns the EEPROM-stored hardware platform ID. v2.06 reads offset 0x31 (2 bytes); v2.13/Rev.2 read offset 0x00 (1 byte).
**0x96--0x98**: Internal debug commands for LNB GPIO control. 0x96 configures output enables (OEB/OEA), 0x97 writes pin states, 0x98 reads a feedback pin. GPIO assignments differ between v2.06/v2.13 (Port B) and Rev.2 (Port A + Port B). See Section 10 for pin details.
---
## 4. Configuration Status Byte
Returned by GET_8PSK_CONFIG (0x80). Stored in IRAM at version-dependent addresses.
```
Bit 7 (0x80): bmArmed - MPEG-2 stream transfer armed / GPIF active
Bit 6 (0x40): bmDCtuned - DC offset tuning complete (set for DCII modes)
Bit 5 (0x20): bmSEL18V - 18V LNB voltage selected (else 13V)
Bit 4 (0x10): bm22kHz - 22 kHz tone active
Bit 3 (0x08): bmDVBmode - DVB mode enabled
Bit 2 (0x04): bmIntersilOn - LNB power supply enabled
Bit 1 (0x02): bm8pskFW_Loaded - BCM4500 firmware loaded (always set on SkyWalker-1)
Bit 0 (0x01): bm8pskStarted - Device booted and running
```
| Version | IRAM Address |
|---------|-------------|
| v2.06 | 0x6D |
| Rev.2 v2.10.4 | 0x4E |
| v2.13 | 0x4F |
---
## 5. Boot Sequence
### 5.1 Two-Stage Firmware Architecture
The FX2 supports two firmware sources:
1. **Host RAM upload** (Rev.1 Cold only): The host writes 8051 code to FX2 RAM via USB 0xA0 vendor requests, using the built-in boot ROM. This requires `dvb-usb-gp8psk-01.fw` in binary hexline format.
2. **EEPROM boot** (Rev.2, SkyWalker-1, CW3K): The FX2 boot ROM reads firmware from an external I2C EEPROM in Cypress C2 format on power-up. No host interaction needed.
### 5.2 C2 EEPROM Format
SkyWalker-1 firmware is stored in Cypress C2 IIC second-stage boot format:
**Header (8 bytes):**
| Offset | Size | Field | SkyWalker-1 Value |
|--------|------|-------|-------------------|
| 0 | 1 | Marker | 0xC2 (external memory, large code model) |
| 1 | 2 | VID (LE) | 0x09C0 |
| 3 | 2 | PID (LE) | 0x0203 |
| 5 | 2 | DID (LE) | 0x0000 |
| 7 | 1 | Config | 0x40 (400 kHz I2C) |
**Code segments** follow: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size is 1023 bytes (FX2 I2C boot ROM buffer limit). All SkyWalker-1 variants use 10 segments.
**Terminator**: 0x80xx (high bit set) + 2-byte entry point address (0xE600 = CPUCS).
### 5.3 Power-On Boot Sequence
```
1. GET_8PSK_CONFIG (0x80) -- read config status byte
|-- Check bit 0: bm8pskStarted?
2. If not started:
|-- BOOT_8PSK (0x89, wValue=1)
|-- GET_FW_VERS (0x92) -- read firmware version
3. If bit 1 clear (bm8pskFW_Loaded):
|-- LOAD_BCM4500 (0x88) -- Rev.1 Warm only; STALLs on SkyWalker-1
4. If bit 2 clear (bmIntersilOn):
|-- START_INTERSIL (0x8A, wValue=1) -- enable LNB power supply
5. SET_DVB_MODE (0x8E, wValue=1) -- STALLs on all SkyWalker-1 FW versions
6. ARM_TRANSFER (0x85, wValue=0) -- abort any pending MPEG transfer
7. Device ready for tuning
```
### 5.4 Firmware Version Identification
The kernel reads firmware version on boot via GET_FW_VERS (0x92) and logs:
```
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
```
Kernel revision constants (from `gp8psk-fe.h`):
```
GP8PSK_FW_REV1 = 0x020604 (v2.06.4)
GP8PSK_FW_REV2 = 0x020704 (v2.07.4)
```
If `fw_vers >= GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. The v2.10 and v2.13 firmwares are newer than either kernel constant.
---
## 6. Tuning Protocol
### 6.1 TUNE_8PSK Command Format (0x86)
The host sends a 10-byte OUT payload via USB control transfer:
```
USB SETUP: bmRequestType=0x40, bRequest=0x86, wValue=0, wIndex=0, wLength=10
EP0BUF Layout:
Byte Content Encoding
---- ------------------ ----------------
[0] Symbol Rate byte 0 Little-endian LSB
[1] Symbol Rate byte 1
[2] Symbol Rate byte 2
[3] Symbol Rate byte 3 Little-endian MSB
[4] Frequency byte 0 Little-endian LSB
[5] Frequency byte 1
[6] Frequency byte 2
[7] Frequency byte 3 Little-endian MSB
[8] Modulation Type 0--9 (see Section 1 table)
[9] Inner FEC Rate Index into modulation-specific table
```
**Symbol Rate** is in samples per second (sps). The Windows driver multiplies ksps by 1000.
**Frequency** is the IF frequency in kHz (950000--2150000), computed by the host as `(RF_freq - LO_freq) * multiplier`.
### 6.2 Firmware EP0BUF Parsing
The firmware reads the 10-byte payload from EP0BUF (XRAM 0xE740--0xE749) and stores:
| Source | Destination | Notes |
|--------|-------------|-------|
| EP0BUF[8] (mod) | IRAM 0x4D | Direct copy |
| EP0BUF[9] (FEC) | IRAM 0x4F | Direct copy |
| EP0BUF[4--7] (freq) | XRAM 0xE0DB--0xE0DE | Byte-reversed (LE to BE) |
| EP0BUF[0--3] (SR) | XRAM 0xE0CB--0xE0CE | Byte-reversed (LE to BE) |
The byte reversal converts host little-endian to BCM4500 big-endian so values can be written directly to the demodulator via I2C.
### 6.3 Modulation Dispatch
After parsing, the firmware validates the modulation type (< 10) and dispatches via a jump table to modulation-specific handlers. Each handler:
1. Validates the FEC index against the maximum for that modulation
2. Looks up a preconfigured byte from an XRAM table
3. Writes configuration to four XRAM registers
**FEC Rate Lookup Tables** (populated from the CODE-space init table at boot):
| XRAM Base | Modulation | Max FEC Index | Notes |
|-----------|-----------|---------------|-------|
| 0xE0F9 | DVB-S QPSK, DSS, BPSK | 7 | Standard Viterbi rates (1/2, 2/3, 3/4, 5/6, 7/8, auto, none) |
| 0xE0B7 | Turbo QPSK | 5 | Turbo code rates |
| 0xE0B1 | Turbo 8PSK | 5 | Turbo code rates |
| 0xE0BC | Turbo 16QAM | 1 | Single code rate |
| 0xE0BD | DCII (all variants) | 9 | Combined code rate + modulation |
**BCM4500 XRAM Configuration after dispatch:**
| XRAM Addr | Register | DVB-S QPSK | Turbo (Q/8/16) | DCII | DSS/BPSK |
|-----------|----------|-----------|---------------|------|----------|
| 0xE0EB | FEC Code Rate | Table lookup | Table lookup | 0xFC (fixed) | Table lookup OR 0x80 |
| 0xE0EC | Modulation Type | 0x09 | 0x09 | From DCII table | 0x09 |
| 0xE0F5 | Demod Mode | 0x10 | 0x10 | 0x10/0x11/0x12/0x16 | 0x10 |
| 0xE0F6 | Turbo Flag | 0x00 | 0x01 | 0x00 | 0x00 |
**DCII Demod Mode values:**
| Modulation | XRAM 0xE0F5 Value |
|-----------|-------------------|
| DCII Combo (4) | 0x10 |
| DCII Offset QPSK (7) | 0x11 |
| DCII I-stream (5) | 0x12 |
| DCII Q-stream (6) | 0x16 |
DSS (8) and DVB BPSK (9) share the same handler; they use the DVB-S QPSK FEC table but OR the lookup value with 0x80.
### 6.4 Complete Tuning Sequence (Host to Satellite)
```
=== Phase 1: LNB Configuration (separate vendor commands) ===
1. SET_LNB_VOLTAGE (0x8B) -- GPIO P0.4 (no I2C)
H / Circular-L -> wValue=1 (18V)
V / Circular-R -> wValue=0 (13V)
2. SET_22KHZ_TONE (0x8C) -- GPIO P0.3 (no I2C)
High band -> wValue=1 (tone on)
Low band -> wValue=0 (tone off)
3. SEND_DISEQC_COMMAND (0x8D) -- if multi-switch needed
=== Phase 2: Tune Command ===
4. TUNE_8PSK (0x86) -- 10-byte payload
=== Phase 3: Firmware Internal Processing ===
5. EP0BUF parsing: mod/FEC to IRAM, freq/SR byte-reversed to XRAM
6. Modulation dispatch: FEC lookup, XRAM config registers set
7. GPIO P3.6: DVB mode select
=== Phase 4: BCM4500 I2C Programming (3 outer retries x 3 I2C addresses) ===
8. Poll BCM4500 ready: I2C READ regs 0xA2, 0xA8, 0xA4
9. Write page: I2C WRITE reg 0xA6 <- 0x00
10. Write config: I2C WRITE reg 0xA7 <- [freq, SR, FEC, mod, demod params]
11. Execute: I2C WRITE reg 0xA8 <- 0x03 (indirect write command)
12. Poll completion: I2C READ regs 0xA8, 0xA2
13. Verify: I2C READ reg 0xA7 (read-back compare)
=== Phase 5: Signal Acquisition (host polling) ===
14. GET_SIGNAL_LOCK (0x90) -- poll until non-zero
15. GET_SIGNAL_STRENGTH (0x87) -- read SNR
```
### 6.5 Signal Lock (GET_SIGNAL_LOCK, 0x90)
Returns 1 byte from BCM4500 register 0xA4. Bit 5 (0x20) indicates signal lock. Both the Linux and Windows drivers interpret any non-zero value as locked and report full lock status (`FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER`).
### 6.6 Signal Strength (GET_SIGNAL_STRENGTH, 0x87)
Returns 6 bytes. The first two bytes contain a 16-bit SNR value (little-endian, in dBu * 256 units):
```
Byte 0: SNR low byte (LSB)
Byte 1: SNR high byte (MSB)
Bytes 2-5: Reserved / BCM4500 diagnostic registers
```
**SNR scaling formula** (from Windows BDA driver):
```
snr_raw = (buf[1] << 8) | buf[0]
if snr_raw <= 0x0F00:
signal_strength = snr_raw * 17 // maps to 0--65535
else:
signal_strength = 0xFFFF // 100% at SNR >= 0x0F00
```
The firmware performs a multi-step I2C transaction to read signal quality: BCM4500 indirect register write/read via 0xA6/0xA7/0xA8, with read-back verification.
Version differences:
- v2.06: polls 3 registers (0xA2, 0xA8, 0xA4), loops up to 6 times
- v2.13: simplified polling (consolidated to 1 register), different call chain
- Rev.2: explicit write/read-back verification step
---
## 7. BCM4500 Demodulator Interface
### 7.1 I2C Bus
The BCM4500 is accessed via the FX2's I2C controller at XRAM 0xE678.
| Parameter | Value |
|-----------|-------|
| Primary I2C address | 0x10 (7-bit) |
| Alternate addresses | 0x3F, 0x7F (probed by v2.13 INT0 handler) |
| Bus speed | 400 kHz (set via C2 header config byte 0x40) |
| EEPROM address | 0x51 (7-bit, 24Cxx-family, for serial number / platform ID) |
The FX2 I2C controller is accessed through XRAM registers:
- 0xE678: I2C control/status register
- 0xE679: I2C data register
### 7.2 Indirect Register Protocol
The BCM4500 uses an indirect register access scheme through three I2C-accessible registers:
```
Register 0xA6: Page/address select (write page number)
Register 0xA7: Data register (read/write indirect data)
Register 0xA8: Command register (write 0x03 to execute indirect write)
```
**Write sequence:**
```
1. I2C WRITE device 0x10, reg 0xA6 <- page_number (typically 0x00)
2. I2C WRITE device 0x10, reg 0xA7 <- data_bytes (N bytes)
3. I2C WRITE device 0x10, reg 0xA8 <- 0x03 (execute)
4. Poll reg 0xA8 until command complete
5. Read back reg 0xA7 to verify
```
### 7.3 Status Registers
| Register | Function |
|----------|----------|
| 0xA2 | BCM4500 status (polled for readiness) |
| 0xA4 | Lock/ready register; bit 5 = signal locked |
| 0xA8 | Command register; bit 0 = command done (polled) |
| 0xF9 | Demod status (read by v2.13 GET_DEMOD_STATUS / INT0 polling) |
### 7.4 Demod Scan
The tune function tries up to 3 different I2C address configurations per attempt, with 3 outer retries (total: up to 9 I2C programming attempts). This supports hardware variants where the BCM4500 may appear at different bus addresses.
The demod scan function (Rev.2 FUN_CODE_1dd0) iterates through parameter sets computed from the iteration index (address offsets multiplied by 0x11), calling the indirect write function (FUN_CODE_1670) for each.
v2.13 adds a more sophisticated probe at boot: INT0 polls addresses 0x7F and 0x3F up to 40 times (0x28), setting a "no demod found" flag (`_1_4`) if neither responds. This flag prevents tuning attempts on boards with absent or failed demodulators.
---
## 8. GPIF Streaming Path
### 8.1 Data Flow
```
BCM4500 Cypress FX2 (CY7C68013A) USB Host
Demodulator P3.5 GPIF Engine EP2 FIFO EP2 (0x82)
(I2C:0x10) <-----> (Master Read) (AUTOIN) ------------> Bulk IN
8-bit 0xE4xx wfm 4x buffer 7 URBs
parallel 8-bit x 8KB
```
The path is fully hardware-managed. The GPIF engine reads data from the BCM4500's 8-bit parallel transport stream output directly into the EP2 FIFO. The AUTOIN bit causes automatic USB commit when the FIFO buffer is full. The FLOWSTATE engine automatically re-triggers GPIF transactions when buffer space becomes available. No firmware intervention occurs in the data path after initial setup.
### 8.2 Key Register Configuration
All values are identical across the three firmware versions:
| Register | Address | Value | Function |
|----------|---------|-------|----------|
| IFCONFIG | 0xE601 | 0xEE | Internal 48 MHz clock, GPIF master mode, async, debug output |
| EP2FIFOCFG | 0xE618 | 0x0C | AUTOIN=1, ZEROLENIN=1, 8-bit data path |
| REVCTL | 0xE60B | 0x03 | NOAUTOARM + SKIPCOMMIT |
| CPUCS | 0xE600 | bits [4:3]=10 | 48 MHz CPU clock |
| FLOWSTATEA | 0xE668 | OR= 0x09 | FSEN (flow state enable) + FS[3] |
| GPIFIE | 0xE65C | OR= 0x3D | Waveform, TC, DONE, FIFO flag, WF2 interrupts |
**IFCONFIG decode (0xEE = 1110_1110):**
- Bit 7: IFCLKSRC=1 (internal clock)
- Bit 6: 3048MHZ=1 (48 MHz)
- Bit 5: IFCLKOE=1 (clock output to BCM4500)
- Bit 4: IFCLKPOL=0 (non-inverted)
- Bit 3: ASYNC=1 (RDY pin handshaking, not clock-edge sampling)
- Bit 2: GSTATE=1 (debug state output on PORTE)
- Bits 1:0: IFCFG=10 (GPIF internal master)
### 8.3 ARM_TRANSFER Sequences
**Start streaming (wValue=1):**
1. Set config_byte bit 7 (streaming active)
2. Load GPIF transaction count: GPIFTCB3:2 = 0x8000 (effectively infinite)
3. Reset GPIF address and EP2 FIFO byte count
4. Assert P3.5 LOW (BCM4500 transport stream enable)
5. Wait for initial GPIF transaction (poll GPIFTRIG bit 7)
6. De-assert P3.5 HIGH
7. Trigger continuous GPIF read: GPIFTRIG = 0x04 (read into EP2)
8. Set P0.7 LOW (streaming indicator)
**Stop streaming (wValue=0):**
1. Set P0.7 HIGH (streaming stopped)
2. Write EP2FIFOBCH = 0xFF (force-flush current buffer)
3. Wait for GPIF idle (poll GPIFTRIG bit 7)
4. Write OUTPKTEND = 0x82 (skip/discard partial EP2 packet)
5. Clear config_byte bit 7 (streaming inactive)
6. Set P3 bits 7:5 = 1 (de-assert all BCM4500 control lines)
### 8.4 Interrupt Handling
INT4 and INT6 (GPIF/FIFO events) share a common handler that sets a software flag and clears the hardware interrupt. The main loop polls this flag, enters CPU idle mode (PCON.0) between events, and checks EP2CS for buffer availability before re-arming the GPIF.
---
## 9. LNB and DiSEqC Control
### 9.1 LNB Voltage
LNB voltage is controlled via GPIO P0.4 on all firmware versions. No I2C is involved.
| wValue | Voltage | GPIO P0.4 | Polarization |
|--------|---------|-----------|-------------|
| 0 | 13V | LOW | Vertical / Circular-Right |
| 1 | 18V | HIGH | Horizontal / Circular-Left |
**USE_EXTRA_VOLT** (0x94) enables a +1V boost (13V->14V, 18V->19V) for long cable runs, by writing to XRAM 0xE0B6 (0x62=normal, 0x6A=boosted; difference is bit 3).
### 9.2 22 kHz Tone
The 22 kHz tone is controlled via GPIO P0.3 on all firmware versions. P0.3 gates an external 22 kHz oscillator on the PCB. The firmware does not generate the 22 kHz signal directly.
| wValue | State | GPIO P0.3 | Band |
|--------|-------|-----------|------|
| 0 | OFF | LOW | Low band (9.75 GHz LO on universal LNB) |
| 1 | ON | HIGH | High band (10.6 GHz LO on universal LNB) |
### 9.3 DiSEqC Protocol
All firmware versions implement DiSEqC via Timer2-based GPIO bit-bang. The algorithm is identical across versions; only the data pin assignment differs (see Section 10).
**Timer2 configuration (identical across all versions):**
| Parameter | Value |
|-----------|-------|
| T2CON | 0x04 (auto-reload, running) |
| RCAP2H:RCAP2L | 0xF82F (reload = 63535) |
| CKCON.T2M | 0 (Timer2 clock = 48 MHz / 12 = 4 MHz) |
| Tick period | (65536 - 63535) / 4 MHz = 500.25 us |
**DiSEqC timing:**
| Parameter | Value |
|-----------|-------|
| Bit period | 1.5 ms (3 Timer2 ticks) |
| Byte period | 13.5 ms (9 bits: 8 data + 1 parity) |
| Tone burst | 12.5 ms (25 ticks) |
| Pre-TX settling delay | 7.5 ms (15 ticks) |
| Data '0' | 1.0 ms tone + 0.5 ms silence (2/3 duty) |
| Data '1' | 0.5 ms tone + 1.0 ms silence (1/3 duty) |
| Carrier frequency | 22 kHz (external oscillator gated by P0.3) |
**Manchester encoding** (decompiled from Rev.2 FUN_CODE_213c):
```
Each DiSEqC bit = 3 Timer2 ticks:
Tick 1: inter-bit gap (carrier OFF)
Tick 2: carrier ON via P0.3
Tick 3: if data='1', carrier OFF early; if data='0', carrier stays ON
End: carrier OFF
```
**Byte transmission**: 8 data bits MSB-first + 1 odd parity bit, each encoded as a Manchester symbol. The parity bit is '1' when the number of '1' data bits is even.
**Tone burst** (mini DiSEqC): 25 consecutive Timer2 ticks of carrier (12.5 ms).
### 9.4 SET_DN_SWITCH (0x8F) -- Legacy Dish Network Protocol
A 7-bit serial command bit-banged on GPIO P0.4 with specific timing:
1. Assert P0.4 HIGH (start pulse)
2. Delay ~32 cycles
3. De-assert P0.4
4. Delay ~8 cycles
5. Shift out 7 bits LSB-first via P0.4, with ~8 cycle delays between bits
The Linux kernel calls this via the `dishnetwork_send_legacy_command` frontend callback. The 8th bit (0x80) of the original switch command controls LNB voltage and is sent separately via SET_LNB_VOLTAGE.
---
## 10. GPIO Pin Map
### Port 0 (SFR 0x80, a.k.a. IOA)
| Pin | v2.06 | Rev.2 v2.10 | v2.13 | Notes |
|-----|-------|-------------|-------|-------|
| P0.0 | -- | LNB control (0x97) | **DiSEqC data** | DiSEqC data pin moved across versions |
| P0.1 | -- | -- | -- | |
| P0.2 | Init set | Init set (0x84) | Init set | BCM4500 control |
| P0.3 | **22 kHz tone** | **22 kHz tone** | **22 kHz tone** | Gates external 22 kHz oscillator |
| P0.4 | **LNB 13V/18V** | **LNB 13V/18V** + DiSEqC data | **LNB 13V/18V** | Also SET_DN_SWITCH bit-bang (all versions) |
| P0.5 | -- | GPIO status (0x98) input | -- | LNB feedback on Rev.2 |
| P0.6 | -- | GPIO control (0x97) | -- | LNB control on Rev.2 |
| P0.7 | **DiSEqC data** | Streaming indicator | Streaming indicator | DiSEqC data on v2.06 only |
### Port 3 (SFR 0xB0)
| Pin | Function | Notes |
|-----|----------|-------|
| P3.0 | Init HIGH | |
| P3.4 | GPIO control | Used by FUN_CODE_1fcf (Rev.2) |
| P3.5 | **TS_EN** | Transport stream enable: LOW=active, HIGH=idle |
| P3.6 | **DVB mode** | BCM4500 mode select; DiSEqC port direction (Rev.2) |
| P3.7 | BCM4500 control | De-asserted (HIGH) when streaming stops |
### Port B (XRAM-mapped IOB)
Used by internal debug commands 0x96--0x98:
| Pin | v2.06/v2.13 | Rev.2 |
|-----|-------------|-------|
| IOB.0 | GPIO status input (0x98) | -- |
| IOB.1 | LNB control (0x97) | -- |
| IOB.2 | LNB control (0x97) | -- |
| IOB.3 | LNB GPIO mode (0x96) | -- |
| IOB.4 | -- | LNB GPIO mode (0x96) + control (0x97) |
**Init values (Rev.2):**
- P0 = 0x84 (P0.7=1, P0.2=1)
- P3 = 0xE1 (P3.7:5=1, P3.0=1)
- IPL1 = 0x9E
### DiSEqC Data Pin Summary
| Version | Data Pin | Carrier Pin |
|---------|----------|-------------|
| v2.06 | P0.7 | P0.3 |
| Rev.2 v2.10 | P0.4 | P0.3 |
| v2.13 | P0.0 | P0.3 |
The carrier pin (P0.3) is the same across all versions. The data pin is used only internally by the firmware's Manchester encoding logic to control whether the carrier is cut short or held for the full bit period.
---
## 11. Firmware Versions
### 11.1 Version Table
| Firmware | Version | Build Date | PID | Functions | Binary Size | Stack Ptr |
|----------|---------|------------|-----|-----------|-------------|-----------|
| v2.06.04 | 0x020604 | 2007-07-13 | 0x0203 | 61 | ~9,472 bytes | SP=0x72 |
| Rev.2 v2.10.04 | 0x020A04 | 2010-03-12 | 0x0202 | 107 | ~8,843 bytes | SP=0x4F |
| v2.13.01 | 0x020D01 | 2010-03-12 | 0x0203 | 88 | ~9,322 bytes | SP=0x50 |
Note: Rev.2 v2.10 targets a different product (PID 0x0202). The v2.13 family has three sub-variants (FW1/FW2/FW3) targeting different SkyWalker-1 hardware sub-revisions.
### 11.2 GET_FW_VERS (0x92) Format
Returns 6 bytes of hardcoded constants:
```
Byte 0: version minor_minor (e.g., 0x04)
Byte 1: version minor (e.g., 0x06)
Byte 2: version major (e.g., 0x02)
Byte 3: build day (e.g., 0x0D = 13)
Byte 4: build month (e.g., 0x07 = July)
Byte 5: build year - 2000 (e.g., 0x07 = 2007)
```
Full version number: `byte[2] << 16 | byte[1] << 8 | byte[0]`
Kernel constants for comparison:
```
GP8PSK_FW_REV1 = 0x020604
GP8PSK_FW_REV2 = 0x020704
```
### 11.3 Binary Comparison Matrix
Byte-level similarity (percentage of matching bytes within shared length):
| | v2.06 | v2.13.1 | v2.13.2 | v2.13.3 | Rev.2 |
|---|---|---|---|---|---|
| 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% |
| Rev.2 | | | | | -- |
The very low similarity between major versions (4--8%) indicates complete recompilation with different linker configurations. Functions are relocated even when logic is identical. Within the v2.13 family, FW2 and FW3 are 83.5% similar (minor hardware tuning), while FW1 differs more (57--59%, different demod interface).
### 11.4 Key Differences Between Versions
| Feature | v2.06 | Rev.2 v2.10 | v2.13 |
|---------|-------|-------------|-------|
| Vendor commands | 30 (0x80--0x9D) | 27 (0x80--0x9A) | 30 (0x80--0x9D) |
| INT0 purpose | USB re-enumeration | USB re-enumeration | Demod availability polling |
| Demod probe at init | No | No | Yes (40 attempts at 0x7F + 0x3F) |
| Retry loops | No | No | Yes (20-attempt with checksum verification) |
| HW revision detection | No | Yes (descriptor walker) | Yes (flag `_1_3`) |
| DiSEqC data pin | P0.7 | P0.4 | P0.0 |
| Config byte IRAM | 0x6D | 0x4E | 0x4F |
| Descriptor base | 0x1200 | 0x0E00 | 0x0E00 |
| Init table address | CODE:0B46 | CODE:0B48 | CODE:0B88 |
| BCM4500 status poll | 3 registers | 3 registers | 1 register (consolidated) |
| Anti-tampering string | No | No | Yes (at offset 0x1880) |
| New commands | -- | 0x99/0x9A proto | 0x99 GET_DEMOD_STATUS, 0x9A INIT_DEMOD, 0x9C DELAY_COMMAND |
| 0x9D behavior | HW revision mode switch | N/A (out of range) | Conditional demod reset |
### 11.5 EEPROM Format Details
All SkyWalker-1 C2 files use uniform 1023-byte segments:
```
Segment Address Length
------- ------- ------
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)
```
### 11.6 Anti-Tampering (v2.13 only)
At firmware offset 0x1880, all v2.13 sub-variants contain:
```
"Tampering is detected. Attempt is logged. Warranty is voided ! \n"
```
Followed by I2C register write commands (`01 10 aa 82 02 41 41 83`). This string and mechanism are absent from v2.06 and Rev.2 firmware.
### 11.7 Rev.2 as Transitional Firmware
Rev.2 v2.10.4 sits architecturally between v2.06 and v2.13:
- Adopted v2.13's stack pointer (SP=0x4F) and descriptor base (0x0E00)
- Retained v2.06's INT0 USB re-enumeration behavior
- Lacks v2.13's demodulator polling, retry loops, and 3 additional vendor commands
- Has the most functions (107) but smallest binary (~8.7 KB) due to granular decomposition
- The INT0 repurposing was the last major architectural change between Rev.2 and v2.13
---
## 12. Internal Debug Commands
Commands 0x91 and 0x96--0x98 are not used by any driver (Linux or Windows). They appear to be manufacturing/debug interfaces.
### 0x91 I2C_ADDR_ADJUST
Increments (wValue != 0) or decrements (wValue == 0) an internal IRAM counter and returns its current value (1 byte). The counter is at IRAM 0x66 (v2.06) or IRAM 0x18 (v2.13/Rev.2). Likely used for I2C bus address or tuner register index adjustment during development.
### 0x96 SET_LNB_GPIO_MODE
Configures GPIO output enable registers for LNB voltage regulator hardware:
| Mode | v2.06/v2.13 | Rev.2 |
|------|-------------|-------|
| Default (wValue=0) | OEB=0xF0 | OEB=0xE7, OEA=0x9E |
| Active (wValue=1) | IOB=(IOB & 0xF7) OR 0x06; OEB=0xFE | IOB.4 clear; P0.6, P0.0 set; OEA OR= 0x41 |
### 0x97 SET_GPIO_PINS
Direct GPIO pin write for LNB control:
| wValue Bit | v2.06/v2.13 Target | Rev.2 Target |
|-----------|-------------------|-------------|
| bit 1 | IOB.1 | P0.6 (Port A) |
| bit 2 | IOB.2 | P0.0 (Port A) |
| bit 3 | IOB.3 | IOB.4 (Port B) |
### 0x98 GET_GPIO_STATUS
Returns 1 byte (0 or 1) from a single GPIO input pin -- likely an LNB overcurrent or power-good feedback signal:
| Version | Pin Read |
|---------|----------|
| v2.06/v2.13 | IOB.0 (Port B bit 0) |
| Rev.2 | P0.5 (Port A bit 5) |
---
## Sources
### Firmware Analysis
- Ghidra decompilation/disassembly of three firmware images:
- v2.06.04 (Ghidra port 8193) -- extracted from SkyWalker-1 EEPROM
- Rev.2 v2.10.04 (Ghidra port 8197) -- extracted from Rev.2 hardware
- v2.13.01 FW1 (Ghidra port 8194) -- extracted from Windows updater
- Firmware dumps: `/home/rpm/claude/ham/satellite/genpix/skywalker-1/firmware-dump/`
### Driver Source
- Linux kernel 6.16.5: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`, `gp8psk-fe.h`
- Linux kernel: `drivers/media/usb/dvb-usb/dvb-usb-firmware.c`
- Windows BDA driver: `SkyWalker1_Final_Release/Source/SkyWalker1Control.cpp`
- Windows BDA driver: `SkyWalker1_Final_Release/Include/SkyWalker1Control.h`, `SkyWalker1CommonDef.h`
### Hardware Documentation
- Cypress CY7C68013A (FX2LP) Technical Reference Manual
- Genpix Electronics official site: https://www.genpix-electronics.com/index.php?act=viewDoc&docId=9
- Device `dmesg` output from running SkyWalker-1 hardware (v2.06.04 firmware)
### Phase 1 Analysis Reports
1. `gp8psk-driver-analysis.md` -- Linux kernel driver analysis
2. `firmware-analysis-v206-vs-v213.md` -- Cross-version firmware comparison
3. `rev2-deep-analysis.md` -- Rev.2 deep function inventory (107 functions)
4. `gpif-streaming-analysis.md` -- GPIF/MPEG-2 streaming path
5. `kernel-fw01-analysis.md` -- Kernel firmware format and EEPROM boot
6. `vendor-commands-unknown.md` -- Complete vendor command decode (0x8F, 0x91--0x98)
7. `tuning-protocol-analysis.md` -- TUNE_8PSK protocol deep dive