Skip to content

Master Reference

Genpix SkyWalker-1 Master Hardware and Firmware Reference

Section titled “Genpix SkyWalker-1 Master Hardware and Firmware Reference”

Consolidated technical reference for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver. Derived from Linux kernel driver analysis (dvb_usb_gp8psk), Ghidra firmware reverse engineering (v2.06, v2.10 Rev.2, v2.13 FW1/FW2/FW3), Windows BDA driver source review, and custom firmware development (v3.01.0, SDCC + fx2lib).


  1. Hardware Overview
  2. USB Interface
  3. Vendor Command Reference
  4. Configuration Status Byte
  5. Boot Sequence
  6. BCM4500 Demodulator Interface
  7. Tuning Protocol
  8. GPIF Streaming Path
  9. LNB and DiSEqC Control
  10. GPIO Pin Map
  11. Firmware Versions
  12. I2C Bus Architecture
  13. Custom Firmware v3.01.0
  14. DVB-S2 Incompatibility
  15. Kernel Driver Notes
  16. Firmware Storage Formats
  17. Debugging Reference
  18. Sources

The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around two ICs:

ComponentPartRole
MCUCypress CY7C68013A (FX2LP)USB 2.0 Hi-Speed controller, 8051 core at 48 MHz
DemodulatorBroadcom BCM4500DVB-S / Turbo / DCII / DSS demodulator, 128-pin MQFP
EEPROM24Cxx-family (I2C address 0x51)FX2 firmware storage, serial number, calibration
Tuner/LNBUnknown IC (I2C address 0x10)Tuner or LNB controller on shared I2C bus

The FX2 handles USB communication, LNB control, DiSEqC signaling, and orchestrates tuning via I2C commands to the BCM4500. The BCM4500 performs RF demodulation, forward error correction, and outputs an MPEG-2 transport stream on an 8-bit parallel bus. The FX2’s GPIF engine transfers the transport stream directly into a USB bulk endpoint with zero firmware intervention in the data path.

IndexModulationConstantFEC Family
0DVB-S QPSKADV_MOD_DVB_QPSKViterbi + Reed-Solomon
1Turbo-coded QPSKADV_MOD_TURBO_QPSKTurbo
2Turbo-coded 8PSKADV_MOD_TURBO_8PSKTurbo
3Turbo-coded 16QAMADV_MOD_TURBO_16QAMTurbo
4Digicipher II ComboADV_MOD_DCII_C_QPSKDCII
5Digicipher II I-stream (split)ADV_MOD_DCII_I_QPSKDCII
6Digicipher II Q-stream (split)ADV_MOD_DCII_Q_QPSKDCII
7Digicipher II Offset QPSKADV_MOD_DCII_C_OQPSKDCII
8DSS QPSKADV_MOD_DSS_QPSKViterbi + Reed-Solomon
9DVB-S BPSKADV_MOD_DVB_BPSKViterbi + Reed-Solomon

DVB-S2 is not supported. See Section 14.

ParameterValue
IF frequency range950 — 2150 MHz
Symbol rate256 Ksps — 30 Msps
Input connectorIEC F-type female
LNB voltage13V / 18V (or 14V / 19V with USE_EXTRA_VOLT)
LNB current450 mA continuous, 750 mA burst
Switch control22 kHz, Tone Burst, DiSEqC 1.0/1.2, Legacy Dish Network
+--[ I2C EEPROM 0x51 ]
|
USB 2.0 HS | I2C Bus (400 kHz)
Host PC <----> [ CY7C68013A FX2LP ] <-----> [ BCM4500 Demod 0x08 ]
| 8051 @ 48 MHz | |
| GPIF Engine |<-----------+ 8-bit parallel TS
| EP2 Bulk IN |
| GPIO (P0/P3) |---> [ 22 kHz Osc ] ---> LNB/Coax
| |---> [ LNB Voltage Ctrl ]
+-----------------+
|
+--[ Tuner/LNB IC 0x10 ]

All Genpix products share VID 0x09C0:

PIDProductcold_idswarm_idsNotes
0x02008PSK-to-USB2 Rev.1 ColdYesNoRequires FW01 upload to RAM
0x02018PSK-to-USB2 Rev.1 WarmNoYesRequires FW02 (BCM4500 firmware)
0x02028PSK-to-USB2 Rev.2NoYesBoots from EEPROM
0x0203SkyWalker-1NoYesBoots from EEPROM
0x0204SkyWalker-1 (alternate)NoYesBoots from EEPROM
0x0205SkyWalker-2Not in kernel 6.16.5
0x0206SkyWalker CW3KNoYesRequires CW3K_INIT (0x9D)

PID 0x0203 was added to the kernel device table after v6.6.1.

2.2 USB Endpoints and Streaming Properties

Section titled “2.2 USB Endpoints and Streaming Properties”
PropertyValue
Control endpointEP0 (default, vendor requests)
Bulk IN endpointEP2 (0x82) — MPEG-2 transport stream
Generic bulk CTRL endpoint0x01 (BCM4500 FW02 upload, Rev.1 only)
URB count7
URB buffer size8192 bytes each
Stream typeUSB_BULK
FX2 controller typeCYPRESS_FX2

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 are required.

DevicePIDNeeds FW01?Needs FW02?Boot Source
Rev.1 Cold0x0200YesRAM (empty)
Rev.1 Warm0x0201NoYesRAM (FW01 loaded)
Rev.20x0202NoNoEEPROM
SkyWalker-10x0203NoNoEEPROM
SkyWalker CW3K0x0206NoNoEEPROM

The firmware files dvb-usb-gp8psk-01.fw and dvb-usb-gp8psk-02.fw were never open-sourced or included in linux-firmware.


All vendor commands use USB control transfers:

  • USB Type: USB_TYPE_VENDOR
  • Timeout: 2000 ms (kernel driver)
  • Retry: Up to 3 attempts for IN operations if partial data received
  • Data buffer maximum: 80 bytes (kernel driver state structure)

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

CmdNameDirwValuewIndexwLengthPurposev2.06Rev.2v2.13
0x80GET_8PSK_CONFIGIN001Read configuration status byteOKOKOK
0x81SET_8PSK_CONFIGOUTvaries00Set config (reserved)STALLSTALLSTALL
0x82(reserved)ReservedSTALLSTALLSTALL
0x83I2C_WRITEOUTdev_addrreg_addrNWrite to I2C deviceOKOKOK
0x84I2C_READINdev_addrreg_addrNRead from I2C deviceOKOKOK
0x85ARM_TRANSFEROUT0/100Start (1) / stop (0) MPEG-2 streamOKOKOK
0x86TUNE_8PSKOUT0010Set tuning parameters (Section 7)OKOKOK
0x87GET_SIGNAL_STRENGTHIN006Read SNR and diagnosticsOKOKChanged
0x88LOAD_BCM4500OUT100Initiate BCM4500 FW downloadSTALLSTALLSTALL
0x89BOOT_8PSKIN0/101Power on (1) / off (0) demodulatorOKOKOK
0x8ASTART_INTERSILIN0/101Enable (1) / disable (0) LNB supplyOKOKOK
0x8BSET_LNB_VOLTAGEOUT0/10013V (0) or 18V (1)OKOKOK
0x8CSET_22KHZ_TONEOUT0/100Tone off (0) or on (1)OKOKOK
0x8DSEND_DISEQC_COMMANDOUTmsg[0]0lenDiSEqC message or tone burstOKOKOK
0x8ESET_DVB_MODEOUT100Enable DVB-S modeSTALLSTALLSTALL
0x8FSET_DN_SWITCHOUTcmd7bit00Legacy Dish Network switch protocolOKOKOK
0x90GET_SIGNAL_LOCKIN001Read signal lock statusOKOKOK
0x91I2C_ADDR_ADJUSTIN0/101Inc/dec internal counter (debug)OKOKOK
0x92GET_FW_VERSIN006Read firmware version + build dateOKOKOK
0x93GET_SERIAL_NUMBERIN004Read 4-byte serial from EEPROMOKOKOK
0x94USE_EXTRA_VOLTOUT0/100Enable +1V LNB boost (14V/19V)OKOKOK
0x95GET_FPGA_VERSIN001Read EEPROM hardware/platform IDOKOKOK
0x96SET_LNB_GPIO_MODEOUT0/100Configure LNB GPIO output enablesOKOKOK
0x97SET_GPIO_PINSOUTbitmap00Direct write to LNB GPIO pinsOKOKOK
0x98GET_GPIO_STATUSIN001Read LNB feedback GPIO pinOKOKOK
0x99GET_DEMOD_STATUSIN001Read BCM4500 register 0xF9STALLProtoOK
0x9AINIT_DEMODOUT000Trigger demod re-init (3 attempts)STALLProtoOK
0x9B(reserved)ReservedSTALLN/ASTALL
0x9CDELAY_COMMANDOUTdelay00Host-controlled tuning delay + pollSTALLN/AOK
0x9DCW3K_INIT / SET_MODE_FLAGOUT0/100CW3K init or conditional demod resetOKN/AChanged

Status key: OK = implemented. STALL = routes to stall handler. Proto = partial/prototype. N/A = out of range (Rev.2 supports 0x80—0x9A only). Changed = implementation differs between versions.

Driver usage notes:

  • The Linux driver only sends LOAD_BCM4500 (0x88) for Rev.1 Warm (PID 0x0201). On SkyWalker-1, bm8pskFW_Loaded is already set and 0x88 STALLs.
  • The Linux driver only sends CW3K_INIT (0x9D) for SkyWalker CW3K (PID 0x0206).

The vendor command dispatcher at CODE:0056 (identical code address across v2.06, v2.13, and Rev.2) follows this logic:

1. Check bmRequestType bit 6: if not set, not a vendor request -> handle standard
2. Read bRequest from SETUPDAT[1]
3. Subtract 0x80 (command base offset)
4. Compare against maximum: < 0x1E (v2.06/v2.13) or < 0x1B (Rev.2)
5. If in range: double the index (2 bytes per entry) and JMP @A+DPTR to jump table
6. If out of range: route to STALL handler

The jump table at CODE:0076 contains 2-byte AJMP targets. Each entry points to the handler for commands 0x80 through 0x9D (or 0x9A for Rev.2).

Jump table layout (first 6 entries shown, Rev.2):

CODE:0076: 01C1 ; 0x80 GET_8PSK_CONFIG -> 0x01C1
CODE:0078: 034B ; 0x81 SET_8PSK_CONFIG -> 0x034B (STALL)
CODE:007A: 034B ; 0x82 (reserved) -> 0x034B (STALL)
CODE:007C: 0103 ; 0x83 I2C_WRITE -> 0x0103
CODE:007E: 00D9 ; 0x84 I2C_READ -> 0x00D9
CODE:0080: 00C2 ; 0x85 ARM_TRANSFER -> 0x00C2
...

3.3 Custom Firmware Commands (0xB0—0xB6)

Section titled “3.3 Custom Firmware Commands (0xB0—0xB6)”

Commands added in custom firmware v3.01.0:

CmdNameDirwValuewIndexwLengthPurpose
0xB0SPECTRUM_SWEEPOUT0010Step through freq range, read SNR at each step
0xB1RAW_DEMOD_READINreg01Read BCM4500 indirect register
0xB2RAW_DEMOD_WRITEOUTregdata0Write BCM4500 indirect register
0xB3BLIND_SCANOUT0016Try symbol rates at given freq, report lock
0xB4I2C_BUS_SCANIN0016Probe all 7-bit addresses, return 16-byte bitmap
0xB5I2C_RAW_READINaddr7regNCombined write-read from any I2C device
0xB6I2C_DIAGINpage08Step-by-step indirect register diagnostic

0x87 GET_SIGNAL_STRENGTH: Returns 6 bytes. Bytes 0—1 are a 16-bit SNR value (little-endian, dBu * 256 units). Bytes 2—5 are reserved/diagnostic. SNR scaling from Windows BDA driver: if snr_raw <= 0x0F00: strength = snr_raw * 17; else strength = 0xFFFF. Version differences: v2.06 polls 3 registers (0xA2, 0xA8, 0xA4) up to 6 times; v2.13 consolidates to 1 register.

0x8D SEND_DISEQC_COMMAND: When wLength > 0, the payload is a standard DiSEqC message (3—6 bytes) with wValue = msg[0] (framing byte, typically 0xE0 or 0xE1). When wLength == 0: wValue == 0 sends tone burst A; wValue != 0 sends tone burst B. See Section 9.

0x8F SET_DN_SWITCH: wValue carries a 7-bit Dish Network switch command, bit-banged LSB-first on GPIO P0.4. The 8th bit (0x80) of the original switch command controls LNB voltage and is sent separately via SET_LNB_VOLTAGE (0x8B).

0x92 GET_FW_VERS: 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 = byte[2] << 16 | byte[1] << 8 | byte[0]. Build date = (2000 + byte[5]) / byte[4] / byte[3].

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 voltage boost on the LNB power regulator.

0x95 GET_FPGA_VERS: Reads from I2C EEPROM at 0x51. Despite the name, there is no FPGA on the SkyWalker-1 — this returns a hardware platform ID. v2.06 reads EEPROM offset 0x31 (2 bytes); v2.13/Rev.2 read offset 0x00 (1 byte).

0xB0 SPECTRUM_SWEEP: 10-byte EP0 payload: [start_freq(u32 LE kHz), stop_freq(u32 LE kHz), step_khz(u16 LE)]. Programs BCM4500 at each frequency step, reads SNR, packs u16 LE results into EP2 bulk FIFO.

0xB3 BLIND_SCAN: 16-byte EP0 payload: [freq_khz(u32 LE), sr_min(u32 LE sps), sr_max(u32 LE sps), sr_step(u32 LE sps)]. Returns 8 bytes on lock [freq_khz(4) + sr_locked(4)] or 1 byte 0x00 if no lock found.

0xB4 I2C_BUS_SCAN: Returns a 16-byte bitmap (128 bits for addresses 0x00—0x77). Each bit position corresponds to a 7-bit address; bit set = ACK received. Known devices on the SkyWalker-1 bus:

AddressIdentity
0x08BCM4500 demodulator (7-bit; wire addresses 0x10 write / 0x11 read)
0x10Tuner or LNB controller
0x51Configuration EEPROM (24Cxx-family)

Returned by GET_8PSK_CONFIG (0x80). Stored in IRAM at a version-dependent address.

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
FirmwareIRAM Address
v2.060x6D
Rev.2 v2.10.40x4E
v2.130x4F

The kernel driver checks these bits to decide which initialization steps to perform. On the SkyWalker-1 after a successful BOOT_8PSK, config_status = 0x03 (STARTED + FW_LOADED).


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.2 BCM4500 Boot Sequence (BOOT_8PSK, 0x89)

Section titled “5.2 BCM4500 Boot Sequence (BOOT_8PSK, 0x89)”

As implemented in bcm4500_boot() in custom firmware v3.01.0, reverse-engineered from stock v2.06 FUN_CODE_1D4F + FUN_CODE_0ddd:

Step Action GPIO/I2C Duration
---- ------------------------------------ ----------------- --------
1 Assert BCM4500 RESET P0.5 = LOW --
2 Power on P0.1 = HIGH --
P0.2 = LOW
3 Wait for power settle -- 30 ms
4 Release RESET P0.5 = HIGH --
5 Wait for BCM4500 POR + ROM boot -- 50 ms
6 I2C probe (read register 0xA2) I2C read 0x08:0xA2 ~0.1 ms
7 Write init block 0 to page 0 I2C write 0xA6/A7/A8 ~2 ms
8 Write init block 1 to page 0 I2C write 0xA6/A7/A8 ~2 ms
9 Write init block 2 to page 0 I2C write 0xA6/A7/A8 ~1 ms
10 Set config_status = 0x03 -- --

Total boot time: approximately 90 ms (30 ms power + 50 ms POR + ~10 ms I2C).

Three register initialization blocks are written to BCM4500 indirect registers (page 0x00) via the 0xA6/0xA7/0xA8 protocol. Data extracted from stock v2.06 firmware FUN_CODE_0ddd:

BlockStart RegisterLengthData (hex)
00x067 bytes06 0b 17 38 9f d9 80
10x078 bytes07 09 39 4f 00 65 b7 10
20x0F3 bytes0f 0c 09

Each block is written as: page select (0xA6 = 0x00), data bytes to 0xA7, trailing zero to 0xA7, then commit (0xA8 = 0x03). The firmware polls 0xA8 until the command completes before proceeding to the next block.

The FX2’s CPUCS register at 0xE600 controls the 8051 run/halt state. The standard vendor request bRequest=0xA0 (RAM read/write) is handled by the FX2 boot ROM in silicon, not by user firmware. This means fw_load.py can reload firmware over a completely hung device:

Terminal window
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3

Writing 0x01 to CPUCS halts the CPU. New code is written to RAM. Writing 0x00 restarts it. The device re-enumerates with the new firmware.


ParameterValue
7-bit I2C address0x08
8-bit write address0x10
8-bit read address0x11
Bus speed400 kHz
FX2 I2C controller SFRsI2CS, I2DAT, I2CTL
Alternate probe addresses (v2.13)0x3F, 0x7F

The custom firmware and kernel driver use the 7-bit address 0x08. The stock firmware writes addr << 1 = 0x10 for write and (addr << 1) | 1 = 0x11 for read, which is the standard I2C convention for 7-bit address 0x08.

The v2.13 firmware probes addresses 0x7F and 0x3F at startup (INT0 handler) to detect which demodulator variant is present. These may be alternative I2C address configurations or addresses for different demodulator sub-systems.

Accessed via standard I2C write/read to the BCM4500’s device address:

RegisterFunction
0xA2Status register (polled for readiness during boot)
0xA4Lock/ready register; bit 5 (0x20) = signal locked
0xA6Indirect page/address select
0xA7Indirect data register (read/write)
0xA8Indirect command register
0xF9Demod status (read by v2.13 GET_DEMOD_STATUS / INT0 polling)

The BCM4500 uses an indirect register access scheme through three directly-addressable registers:

Indirect Write Sequence:

1. I2C WRITE to 0x08, register 0xA6 <- page_number (typically 0x00)
2. I2C WRITE to 0x08, register 0xA7 <- data bytes (N bytes, auto-increment)
3. I2C WRITE to 0x08, register 0xA8 <- 0x03 (execute indirect write)
4. Poll register 0xA8 until bit 0 clear (command complete)
5. Optionally read back register 0xA7 to verify

Indirect Read Sequence:

1. I2C WRITE to 0x08, register 0xA6 <- target_register
2. I2C WRITE to 0x08, register 0xA7 <- 0x00 (placeholder)
3. I2C WRITE to 0x08, register 0xA8 <- 0x01 (execute indirect read)
4. Short delay (~1 ms)
5. I2C READ from 0x08, register 0xA7 <- result byte

The BCM4500’s data register (0xA7) supports auto-increment for multi-byte writes within a single I2C transaction. When writing N data bytes to 0xA7 in one I2C WRITE operation (without issuing STOP between bytes), the BCM4500 internally advances its data buffer pointer after each byte. This allows writing an entire initialization block in a single I2C transaction:

I2C transaction:
START -> 0x10 (write) -> 0xA7 (reg) -> data[0] -> data[1] -> ... -> data[N-1] -> STOP

The firmware exploits this for initialization blocks and tuning data, reducing I2C overhead compared to byte-by-byte writes.

Stock firmware init block write sequence (from FUN_CODE_0ddd):

1. I2C WRITE: [0x10] [0xA6] [0x00] -- Page select = 0
2. I2C WRITE: [0x10] [0xA7] [data0..dataN] -- Multi-byte data (auto-increment)
3. I2C WRITE: [0x10] [0xA7] [0x00] -- Trailing zero (stock firmware quirk)
4. I2C WRITE: [0x10] [0xA8] [0x03] -- Commit indirect write
5. Poll: I2C READ [0xA8] until bit 0 clear -- Wait for completion

The trailing zero write (step 3) appears in all stock firmware versions. Its purpose is unclear — it may zero-pad the data buffer or serve as an end-of-data marker within the BCM4500’s indirect register engine.

The tune function (stock firmware) tries up to 3 different I2C address configurations per attempt, with 3 outer retries (up to 9 total I2C programming attempts). This supports hardware variants where the BCM4500 may appear at different bus addresses.

v2.13 adds a boot-time probe: INT0 polls addresses 0x7F and 0x3F up to 40 times (0x28), setting flag _1_4 if neither responds. This prevents tuning attempts on boards with absent demodulators.

The BCM4500 contains two FEC decoder paths:

  1. Advanced Modulation Turbo FEC Decoder: Iterative turbo code decoder supporting QPSK (rates 1/4, 1/2, 3/4), 8PSK (rates 2/3, 3/4, 5/6, 8/9), 16QAM (rate 3/4), with Reed-Solomon (t=10) outer code.

  2. Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder: Concatenated Viterbi inner decoder (convolutional code, rates 1/2 through 7/8) + Reed-Solomon outer decoder.

There is no LDPC or BCH decoder hardware. See Section 14.


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.1)
[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.

The firmware reads the 10-byte payload from EP0BUF (XRAM 0xE740—0xE749) and stores:

SourceDestinationNotes
EP0BUF[8] (modulation)IRAM 0x4DDirect copy
EP0BUF[9] (FEC)IRAM 0x4FDirect copy
EP0BUF[4—7] (frequency)XRAM 0xE0DB—0xE0DEByte-reversed (LE to BE)
EP0BUF[0—3] (symbol rate)XRAM 0xE0CB—0xE0CEByte-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.

After parsing, the firmware validates the modulation type (bounds check < 10) and dispatches via a 20-byte jump table (10 entries x 2 bytes) at CODE:0873. Each handler:

  1. Validates the FEC index against the maximum for that modulation
  2. Looks up a preconfigured byte from an XRAM FEC rate table
  3. Writes configuration to four XRAM registers (0xE0EB, 0xE0EC, 0xE0F5, 0xE0F6)

Modulation jump table (from Rev.2 at CODE:0873):

EntryAJMP TargetModulation
00x08B7DVB-S QPSK
10x08DFTurbo QPSK
20x08FATurbo 8PSK
30x0915Turbo 16QAM
40x0947DCII Combo
50x094FDCII I-stream
60x0957DCII Q-stream
70x095FDCII Offset QPSK
80x0887DSS QPSK
90x0887DVB BPSK (shares DSS handler)

DSS and DVB BPSK share the same handler. Their FEC lookup uses the same table (0xE0F9) but ORs the result with 0x80 to distinguish them from DVB-S QPSK.

Populated from the CODE-space init table at boot:

XRAM BaseModulationMax FEC IndexCode Rates
0xE0F9DVB-S QPSK, DSS, BPSK71/2, 2/3, 3/4, 5/6, 7/8, auto, none
0xE0B7Turbo QPSK5Turbo-specific rates
0xE0B1Turbo 8PSK5Turbo-specific rates
0xE0BCTurbo 16QAM1Single code rate
0xE0BDDCII (all variants)9Combined code + modulation

7.5 BCM4500 XRAM Configuration After Dispatch

Section titled “7.5 BCM4500 XRAM Configuration After Dispatch”
XRAM AddrRegisterDVB-S QPSKTurbo (Q/8/16)DCIIDSS/BPSK
0xE0EBFEC Code RateTable lookupTable lookup0xFC (fixed)Table lookup OR 0x80
0xE0ECModulation Type0x090x09From DCII table0x09
0xE0F5Demod Mode0x100x100x10/0x11/0x12/0x160x10
0xE0F6Turbo Flag0x000x010x000x00

DCII Demod Mode values:

ModulationIndexXRAM 0xE0F5
DCII Combo40x10
DCII I-stream50x12
DCII Q-stream60x16
DCII Offset QPSK70x11

DSS (8) and DVB BPSK (9) share the DVB-S QPSK handler; they use the same FEC table but OR the lookup value with 0x80.

7.6 Complete Tuning Sequence (Host to Satellite)

Section titled “7.6 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

GET_SIGNAL_LOCK (0x90): Returns 1 byte from BCM4500 register 0xA4. Bit 5 (0x20) indicates signal lock. The kernel driver interprets any non-zero value as locked and reports FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER.

GET_SIGNAL_STRENGTH (0x87): Returns 6 bytes. Bytes 0—1 = 16-bit SNR (LE, dBu * 256). SNR scaling: snr_raw * 17 maps 0x0000—0x0F00 to 0—65535 (100% at SNR >= 0x0F00).


BCM4500 Cypress FX2 (CY7C68013A) USB Host
Demodulator P3.5 GPIF Engine EP2 FIFO EP2 (0x82)
(I2C:0x08) <-----> (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 re-triggers GPIF transactions when buffer space becomes available. No firmware intervention occurs in the data path after initial setup.

All values are identical across the three stock firmware versions:

RegisterAddressValueFunction
IFCONFIG0xE6010xEEInternal 48 MHz clock, GPIF master, async, debug
EP2FIFOCFG0xE6180x0CAUTOIN=1, ZEROLENIN=1, 8-bit data path
REVCTL0xE60B0x03NOAUTOARM + SKIPCOMMIT
CPUCS0xE600bits [4:3]=1048 MHz CPU clock
FLOWSTATEA0xE668OR 0x09FSEN (flow state enable) + FS[3]
GPIFIE0xE65COR 0x3DWaveform, TC, DONE, FIFO flag, WF2 interrupts

IFCONFIG decode (0xEE = 1110_1110):

BitNameValueMeaning
7IFCLKSRC1Internal clock source
63048MHZ148 MHz IFCLK frequency
5IFCLKOE1IFCLK pin drives output (clock to BCM4500)
4IFCLKPOL0Non-inverted clock polarity
3ASYNC1Asynchronous GPIF (RDY pin handshaking)
2GSTATE1Debug state output on PORTE
1:0IFCFG10GPIF internal master mode

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)
MetricValue
USB 2.0 HS bulk theoretical480 Mbps
USB 2.0 HS bulk practical~280 Mbps (~35 MB/s)
GPIF engine theoretical48 MHz x 8 bits = 384 Mbps
Typical DVB-S TS rate1—5 MB/s
Maximum DVB-S2 rate (hypothetical)~7.25 MB/s (58 Mbps)

The USB/GPIF path has approximately 5x headroom even at maximum theoretical DVB-S2 data rates. The bottleneck for supported modes is the satellite link, not the USB data path.

All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure:

FIFORESET = 0x80 ; NAKALL: NAK all host transfers during reset
FIFORESET = 0x02 ; Reset EP2 FIFO
FIFORESET = 0x04 ; Reset EP4 FIFO
FIFORESET = 0x06 ; Reset EP6 FIFO
FIFORESET = 0x08 ; Reset EP8 FIFO
FIFORESET = 0x00 ; Release NAKALL

Three NOP instructions (mandatory SYNCDELAY) are inserted between each write per Cypress TRM requirements.

EP2CFG = 0xE2; // valid=1, dir=IN, type=BULK, size=512, buf=DOUBLE
BitValueMeaning
7 (VALID)1Endpoint enabled
6 (DIR)1IN (device to host)
5:4 (TYPE)10Bulk transfer
3 (SIZE)0512-byte packets
1:0 (BUF)10Double-buffered

EP4, EP6, EP8 are disabled (&= ~bmVALID).

INT4 and INT6 (GPIF/FIFO events) share a common handler that sets a software flag (_0_1) and clears EXIF.4. 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.

Main loop structure (from v2.06 FUN_CODE_2297):

void main_loop_poll(void) {
if (_0_1) { // GPIF/FIFO event pending
_0_1 = 0; // Clear flag
if (EP2CS & bmEPFULL) { // EP2 buffer full?
// Wait for host to read EP2
}
} else {
PCON |= 0x01; // CPU idle until next interrupt
}
}

During early initialization, IFCONFIG is temporarily set to 0xCA before the final 0xEE:

ValueDecodeDifference from 0xEE
0xCA1100_1010GSTATE=0, ASYNC=0
0xEE1110_1110GSTATE=1, ASYNC=1 (final)

The temporary value disables async mode and debug state output during FIFO setup.


LNB voltage is controlled via GPIO P0.4. No I2C is involved.

wValueVoltageGPIO P0.4Polarization
013VLOWVertical / Circular-Right
118VHIGHHorizontal / 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; bit 3 is the difference).

Controlled via GPIO P0.3. P0.3 gates an external 22 kHz oscillator on the PCB. The firmware does not generate the 22 kHz carrier directly.

wValueStateGPIO P0.3Band
0OFFLOWLow band (9.75 GHz LO on universal LNB)
1ONHIGHHigh band (10.6 GHz LO on universal LNB)

All firmware versions implement DiSEqC via Timer2-based GPIO bit-bang. The algorithm is identical across versions; only the data pin differs per PCB revision.

Timer2 configuration (identical across all versions):

ParameterValue
T2CON0x04 (auto-reload, running)
RCAP2H:RCAP2L0xF82F (reload = 63535)
CKCON.T2M0 (Timer2 clock = 48 MHz / 12 = 4 MHz)
Tick period(65536 - 63535) / 4 MHz = 500.25 us

DiSEqC timing parameters:

ParameterValue
Bit period1.5 ms (3 Timer2 ticks)
Byte period13.5 ms (9 bits: 8 data + 1 parity)
Tone burst A/B12.5 ms (25 ticks)
Pre-TX settling delay7.5 ms (15 ticks)
Data ‘0’1.0 ms tone + 0.5 ms silence (2/3 duty cycle)
Data ‘1’0.5 ms tone + 1.0 ms silence (1/3 duty cycle)
Carrier frequency22 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 via P0.3 = 0)
Tick 2: carrier ON (P0.3 = 1)
Tick 3: if data_pin='1', carrier OFF early; if '0', carrier stays ON
End: carrier always OFF

DiSEqC bit waveforms:

Data '0' (2/3 tone, 1/3 silence):
Tick 1 Tick 2 Tick 3
(500 us) (500 us) (500 us)
P0.3: _____|========|========|________|
^tone ON ^tone OFF
(setup gap) (1.0 ms carrier) (0.5 ms silence)
Data '1' (1/3 tone, 2/3 silence):
Tick 1 Tick 2 Tick 3
(500 us) (500 us) (500 us)
P0.3: _____|========|________|________|
^tone ON ^tone OFF early
(setup gap) (0.5 ms carrier) (1.0 ms silence)

Decompiled bit symbol function (from Rev.2 FUN_CODE_213c):

void diseqc_bit_symbol(void) {
wait_TF2(); // Tick 1: inter-bit gap (500 us)
P0 |= 0x08; // P0.3 = 1 -> 22 kHz carrier ON
wait_TF2(); // Tick 2: carrier period (500 us)
if (data_pin != 0) { // If data = '1':
P0 &= 0xF7; // P0.3 = 0 -> carrier OFF (short pulse)
}
wait_TF2(); // Tick 3: final period (500 us)
P0 &= 0xF7; // P0.3 = 0 -> carrier always OFF at end
}

Decompiled byte transmission (from Rev.2 FUN_CODE_07d1):

void diseqc_send_byte(char first_byte, byte data) {
byte ones_count = 0;
if (first_byte == 0) TF2 = 0; // Sync timer on first byte
for (char i = 8; i > 0; i--) { // 8 bits, MSB first
if (data & 0x80) {
data_pin = 1; // Set data = '1'
diseqc_bit_symbol();
ones_count++;
} else {
data_pin = 0; // Set data = '0'
diseqc_bit_symbol();
}
data <<= 1; // Next bit
}
data_pin = ~ones_count & 1; // Odd parity
diseqc_bit_symbol(); // Transmit parity bit
}

Timing per byte: 9 bits x 1.5 ms = 13.5 ms

Tone burst (mini DiSEqC): 25 consecutive Timer2 ticks of carrier (12.5 ms). Tone burst A: wValue==0 and wLength==0. Tone burst B: wValue!=0 and wLength==0.

Timer tick wait (TF2 polling, identical across all versions):

void wait_TF2(void) {
while (TF2 == 0) {} // Poll Timer2 overflow flag
TF2 = 0; // Clear flag for next tick
}
FX2 Firmware External Hardware Coax Cable
+------------------+ +--------------------+ +------------------+
| P0.3 (carrier) |---->| 22 kHz oscillator |---->| LNB power line |
| (enable/disable) | | (gated by P0.3) | | (13V/18V + tone) |
| | | | | |
| P0.x (data bit) | | (internal firmware | | |
| (firmware only) | | logic only) | | |
+------------------+ +--------------------+ +------------------+

The data pin (P0.7 / P0.4 / P0.0 depending on firmware version) is used only internally by the firmware’s Manchester encoding logic. It controls whether the carrier gate signal is cut short or held for the full bit period.

The Windows driver exposes DiSEqC through a BDA extended property:

// GUID: {0B5221EB-F4C4-4976-B959-EF74427464D9}
typedef struct __DISEQC_COMMAND {
UCHAR ucMessage[6]; // Framing, Address, Command, Data[0..2]
UCHAR ucMessageLength; // 3-6 for DiSEqC; 1 for tone burst
} DISEQC_COMMAND;

For tone burst: ucMessageLength=1, ucMessage[0]=SEC_MINI_A (0x00) or SEC_MINI_B (0x01).

9.6 SET_DN_SWITCH (0x8F) — Legacy Dish Network

Section titled “9.6 SET_DN_SWITCH (0x8F) — Legacy Dish Network”

A 7-bit serial command bit-banged on GPIO P0.4:

  1. Assert P0.4 HIGH (start pulse), delay ~32 cycles
  2. De-assert P0.4, delay ~8 cycles
  3. Shift out 7 bits LSB-first via P0.4, ~8 cycle delays between bits

The kernel calls this via dishnetwork_send_legacy_command. Bit 7 (0x80) of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.


Pinv2.06Rev.2 v2.10v2.13Custom v3.01.0
P0.0LNB control (0x97)DiSEqC data
P0.1Power enablePower enablePower enablePower enable
P0.2Power disablePower disable (init=0x84)Power disablePower disable
P0.322 kHz tone22 kHz tone22 kHz tone22 kHz tone
P0.4LNB 13V/18VLNB 13V/18V + DiSEqC dataLNB 13V/18VLNB 13V/18V
P0.5BCM4500 RESETGPIO status input (0x98)BCM4500 RESETBCM4500 RESET
P0.6GPIO control (0x97)
P0.7DiSEqC dataStreaming indicatorStreaming indicatorDiSEqC data + streaming
PinFunctionNotes
P3.0Init HIGH
P3.4GPIO controlUsed by Rev.2 FUN_CODE_1fcf
P3.5TS_ENTransport stream enable: LOW=active, HIGH=idle
P3.6DVB modeBCM4500 mode select; DiSEqC direction (Rev.2)
P3.7BCM4500 controlDe-asserted (HIGH) when streaming stops

Used by internal debug commands 0x96—0x98:

Pinv2.06/v2.13Rev.2
IOB.0GPIO status input (0x98)
IOB.1LNB control (0x97)
IOB.2LNB control (0x97)
IOB.3LNB GPIO mode (0x96)
IOB.4LNB GPIO mode (0x96) + control (0x97)
Firmware VersionData PinCarrier Pin
v2.06P0.7P0.3
Rev.2 v2.10P0.4P0.3
v2.13P0.0P0.3
Custom v3.01.0P0.7P0.3

The carrier pin (P0.3) is the same across all versions.

RegisterValueDecode
IOA (P0)0x84P0.7=1 (idle), P0.2=1 (power disable active)
IOD (P3)0xE1P3.7:5=1 (controls idle), P3.0=1
OEA0xBEP0.1-5,7 as outputs

FirmwareVersion IDBuild DatePIDFunctionsBinary SizeSP
v2.06.040x0206042007-07-130x0203619,472 bytes0x72
Rev.2 v2.10.040x020A042010-03-120x02021078,843 bytes0x4F
v2.13.01 (FW1)0x020D012010-03-120x020382-889,322 bytes0x50
v2.13.02 (FW2)0x020D012010-03-120x0203839,377 bytes0x50
v2.13.03 (FW3)0x020D012010-03-120x0203839,369 bytes0x52
Custom v3.01.00x0301002026-02-120x0203N/A~3 KB (RAM)N/A

Rev.2 v2.10 targets PID 0x0202 (different product). The v2.13 sub-variants target different SkyWalker-1 hardware sub-revisions. Custom v3.01.0 is compiled with SDCC + fx2lib and loaded into FX2 RAM (not flashed to EEPROM).

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.

Featurev2.06Rev.2 v2.10v2.13
Vendor commands30 (0x80—0x9D)27 (0x80—0x9A)30 (0x80—0x9D)
INT0 handlerUSB re-enumerationUSB re-enumerationDemod availability polling
Demod probe at bootNoNoYes (40 attempts at 0x7F + 0x3F)
Retry loopsNoNoYes (20-attempt with checksum verify)
HW revision detectionNoYes (descriptor walker)Yes (flag _1_3)
DiSEqC data pinP0.7P0.4P0.0
Config byte IRAM0x6D0x4E0x4F
Descriptor base0x12000x0E000x0E00
Init table addressCODE:0B46CODE:0B48CODE:0B88
BCM4500 status poll3 registers3 registers1 register (consolidated)
Anti-tampering stringNoNoYes (at firmware offset 0x1880)
New commands0x99/0x9A proto0x99, 0x9A, 0x9C
0x9D behaviorHW revision modeN/A (out of range)Conditional demod reset

The three v2.13 sub-variants target fundamentally different hardware interfaces:

AspectFW1 (v2.13.1)FW2 (v2.13.2)FW3 (v2.13.3)
Demod interfaceI2C busParallel bus (P0/P1)Parallel bus (enhanced)
Bus protocolI2C START/STOP/ACKSingle-phase P1 readDual-phase P1 read + OR accumulate
Stack pointer0x500x500x52
P0 init0xa40xa40xa0
Status registerINTMEM 0x4FINTMEM 0x4FINTMEM 0x51
Config sourceHardcodedExternal (0xE080-0xE08E)External (0xE080-0xE08E)
Binary distance from FW13,993 bytes3,789 bytes
Binary distance from FW23,993 bytes1,525 bytes

FW1 uses standard I2C master-mode transactions. FW2/FW3 use a parallel data bus with P0 for control signals (chip select, read strobe) and P1 for 8-bit data. FW3 adds dual-phase reading with OR-accumulation, likely for a demodulator chip with different bus timing. The updater program selects the correct sub-variant based on hardware detection.

Byte-level similarity (percentage of matching bytes within shared length):

v2.06v2.13.1v2.13.2v2.13.3Rev.2
v2.064.8%4.3%4.3%6.0%
v2.13.157.2%59.4%8.0%
v2.13.283.5%5.8%
v2.13.35.8%
Rev.2

The very low similarity between major versions (4—8%) indicates complete recompilation with different linker configurations. Functions relocate even when logic is identical.

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.

Rev.2 v2.10.4 sits architecturally between v2.06 and v2.13:

  • Adopted v2.13’s descriptor base (0x0E00) and similar stack pointer
  • Retained v2.06’s INT0 USB re-enumeration behavior
  • Has the most functions (107) but smallest binary (~8.8 KB) due to granular decomposition
  • Lacks v2.13’s demodulator polling, retry loops, and additional vendor commands

11.8 Key Function Correspondence Across Versions

Section titled “11.8 Key Function Correspondence Across Versions”
v2.06 FunctionRev.2 Functionv2.13 FunctionRole
main (0x188D)main (0x155F)main_entry (0x170D)RESET vector: clear IRAM, process init table
FUN_CODE_09a7FUN_CODE_09a9FUN_CODE_0800Main init + main loop
FUN_CODE_13c3FUN_CODE_10d9FUN_CODE_11abUSB/peripheral descriptor setup
FUN_CODE_032aFUN_CODE_0319FUN_CODE_034eStandard USB request handler
FUN_CODE_0056vendor_cmd_dispatchFUN_CODE_0056Vendor request dispatcher (identical code)
FUN_CODE_2297FUN_CODE_21ecMain loop poll (USB IRQ processing)
FUN_CODE_1919FUN_CODE_0d7cFUN_CODE_1800GPIF/FIFO management
FUN_CODE_1d4fv2.06 demod init (GPIO-based)
FUN_CODE_1d4bv2.13 demod init (I2C write to 0x7F/0xF0)
FUN_CODE_0dddFUN_CODE_0c64FUN_CODE_0ca4BCM4500 firmware loader
FUN_CODE_2000FUN_CODE_208dBCM4500 status polling
FUN_CODE_1dfbFUN_CODE_1bdaFUN_CODE_14b9Delay loop (clock-speed-aware)
INT0_vec (0x0003)INT0_ISR (0x0003)INT0_vector (0x0003)INT0 handler (different purpose)
FUN_CODE_2239v2.13 I2C single-byte read helper
FUN_CODE_2031v2.13 USB reconnect function
FUN_CODE_1799v2.13 demod signature verification
FUN_CODE_1ac6v2.13 tuning acquisition sequence

The INT0 interrupt vector (CODE:0003) was repurposed between firmware versions:

v2.06 and Rev.2 — USB Re-enumeration:

void INT0_vec(void) {
if (flag == 0) CPUCS |= 0x08; // CPUCS bit 3
else CPUCS |= 0x0A; // CPUCS bits 3+1
delay(5, 0xDC); // ~1500 cycles
EPIRQ = 0xFF; // Clear endpoint IRQs
USBIRQ = 0xFF; // Clear USB IRQs
EXIF &= 0xEF; // Clear external interrupt flag
CPUCS &= 0xF7; // Clear CPUCS bit 3
}

Pulses CPUCS.3 to trigger a controlled USB re-enumeration, then clears all pending interrupts.

v2.13 — Demodulator Availability Polling:

void INT0_vector(void) {
for (counter = 0x28; counter != 0; counter--) { // 40 attempts
byte result = I2C_read(0x7F); // Demod address A
if (result != 0x01) {
result = I2C_read(0x3F); // Demod address B
if (result != 0x01) break;
}
}
no_demod_flag = (counter == 0); // Set if loop exhausted
}

Polls two I2C addresses (0x7F, 0x3F) to detect which demodulator variant is present. The no_demod_flag prevents tuning attempts on boards with absent or failed demodulators.

In v2.13, the USB re-enumeration code was moved to FUN_CODE_2031 and called as a normal function before the main loop starts, freeing INT0 for demodulator polling.

v2.13 performs two integrity checks during initialization, absent from v2.06 and Rev.2:

Demodulator Signature Verification (FUN_CODE_1799):

  1. Writes 4 bytes to I2C device 0x7F, register 0xF0
  2. Reads 5 bytes from register 0x0A (stepping by 2), each character
  3. Subtracts 0x30 (‘0’) from each byte (ASCII to binary)
  4. Sums values and compares against expected parameter (0x021C)
  5. Up to 20 retry attempts

Descriptor Checksum Verification (FUN_CODE_1ca0):

  1. Iterates bytes 6 through 0x29 (36 bytes) of a descriptor block
  2. Computes running sum, compares against 0x0706
  3. Iterates bytes 0x2C through 0x4F (36 bytes) of same block
  4. Computes second sum, compares against 0x0686
  5. Up to 20 retry attempts

Both checks call FUN_CODE_1ac6(100) (tuning acquisition with 100 ms delay) as a recovery action if verification fails after all attempts.

All firmware versions initialize XRAM peripheral registers from a table stored in CODE space. The table is processed at startup before entering the main loop.

Table format (all versions):

Each entry: [addr_hi] [addr_lo] [data_byte]
Terminator: [0x00] [0x00] (address 0x0000)

The parser reads 3 bytes at a time: a 16-bit XRAM address (big-endian) and a data byte. It writes the byte to the address until it encounters address 0x0000.

Key XRAM registers initialized from the table:

XRAM AddressRegisterTypical ValuePurpose
0xE604FIFORESET0x80Start FIFO reset sequence
0xE601IFCONFIG0xCAInitial interface config (overwritten later)
0xE610EP2CFG0xE2EP2 bulk IN, 512-byte, double-buffered
0xE612EP4CFG0x00EP4 disabled
0xE618EP2FIFOCFG0x0CAUTOIN, ZEROLENIN, 8-bit
0xE620REVCTL0x03NOAUTOARM + SKIPCOMMIT
0xE67AI2CTL0x01I2C 400 kHz
0xE68AEP0BCH0x00EP0 byte count high = 0

Init table addresses by version:

FirmwareTable Address
v2.06CODE:0B46
Rev.2CODE:0B48
v2.13CODE:0B88

All firmware versions use the same main loop structure: poll the SUDAV (setup data available) interrupt flag, process vendor commands, then idle the CPU until the next interrupt.

v2.06 (simplified decompilation):

void main_loop(void) { // FUN_CODE_09a7
// 1. Process init table from CODE:0B46
// 2. Call FUN_CODE_13c3 (USB/peripheral setup)
// 3. EA = 1 (global interrupts enable)
while (1) {
if (sudav_flag) {
handle_setupdata(); // Process USB SETUP packet
sudav_flag = 0;
}
if (gpif_flag) {
handle_gpif_event();
gpif_flag = 0;
} else {
PCON |= 0x01; // CPU idle until next interrupt
}
}
}

The SUDAV ISR simply sets sudav_flag = 1 and clears the interrupt. All actual USB processing happens in the main loop context.


The FX2’s I2C master controller is a hardware peripheral accessed through SFRs:

SFRAddressFunction
I2CS0xE678 (XRAM)I2C control/status register
I2DAT0xE679 (XRAM)I2C data register
I2CTL0xE67A (XRAM)I2C control (speed selection)

Key I2CS bits: bmSTART (initiate START), bmSTOP (initiate STOP), bmLASTRD (signal last read byte), bmDONE (transaction byte complete), bmACK (ACK received), bmBERR (bus error).

The I2C bus speed is 400 kHz, set via:

  • C2 EEPROM header config byte = 0x40 (at boot)
  • I2CTL = bm400KHZ (in custom firmware)
7-bit AddressWire Write/ReadIdentity
0x080x10 / 0x11BCM4500 demodulator
0x100x20 / 0x21Tuner or LNB controller
0x510xA2 / 0xA3Configuration EEPROM (24Cxx-family)

The EEPROM at 0x51 stores: device serial number (read by GET_SERIAL_NUMBER 0x93), hardware platform ID (read by GET_FPGA_VERS 0x95), and calibration data.

12.4 Combined Write-Read (Repeated START) Protocol

Section titled “12.4 Combined Write-Read (Repeated START) Protocol”

All BCM4500 register reads use the I2C combined write-read protocol with a repeated START condition. This is required because the BCM4500 uses a register-addressed protocol where the register number must be sent as a write phase before the read phase:

Complete I2C transaction for reading register 0xA2 from device 0x08:
Phase 1 (Write):
[S] [0x10] [ACK] [0xA2] [ACK]
| | | | |
| | | | +-- BCM4500 ACKs register address
| | | +--------- Register address
| | +---------------- BCM4500 ACKs its address
| +----------------------- Device address (0x08 << 1) = 0x10 (write)
+---------------------------- START condition
Phase 2 (Read with Repeated START):
[Sr] [0x11] [ACK] [DATA] [NACK] [P]
| | | | | |
| | | | | +-- STOP condition
| | | | +--------- Master NACKs (last byte)
| | | +---------------- Register data
| | +----------------------- BCM4500 ACKs its address
| +------------------------------ Device address (0x08 << 1 | 1) = 0x11 (read)
+------------------------------------ REPEATED START (no STOP between phases)

The repeated START (Sr) is essential. A STOP between phases would release the bus, and the BCM4500 would lose the register address context.

FX2 I2C SFR sequence for combined read (from custom firmware):

I2CS |= bmSTART; // Generate START
I2DAT = 0x10; // Write: device addr + W
// wait bmDONE, check bmACK
I2DAT = 0xA2; // Write: register address
// wait bmDONE, check bmACK
I2CS |= bmSTART; // Generate REPEATED START (no STOP first!)
I2DAT = 0x11; // Write: device addr + R
// wait bmDONE, check bmACK
I2CS |= bmLASTRD; // Signal this is the last read byte
tmp = I2DAT; // Dummy read (triggers first clock burst)
// wait bmDONE
I2CS |= bmSTOP; // Generate STOP after reading
data = I2DAT; // Read actual data byte
// wait bmSTOP to clear

Sending I2CS |= bmSTOP when no I2C transaction is active (no prior START issued, bus idle) corrupts the FX2 I2C controller’s internal state machine. The bmSTOP bit may not self-clear, and subsequent START conditions fail to detect ACK from slaves.

This was the root cause of the firmware hang in custom v3.01.0 during boot. The stock firmware’s “bus reset” step:

/* BROKEN: */
I2CS |= bmSTOP;
i2c_wait_stop();

was removed. The correct approach is to simply proceed with a new START condition. If the bus is idle (after power-on or after the previous transaction completed normally), the START succeeds and the controller enters its normal operating state. The Cypress TRM does not document STOP as a standalone bus-reset mechanism.

The fx2lib I2C functions poll bmDONE with no timeout:

while (!(I2CS & bmDONE) && !cancel_i2c_trans);

Since cancel_i2c_trans is never set during normal operation, these loops are effectively infinite. The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:

#define I2C_TIMEOUT 6000
static BOOL i2c_wait_done(void) {
WORD timeout = I2C_TIMEOUT;
while (!(I2CS & bmDONE)) {
if (--timeout == 0) return FALSE;
}
return TRUE;
}

A WORD counter of 6000 decremented in a tight SDCC-compiled loop at 48 MHz gives approximately 5—10 ms per wait. At 400 kHz I2C, a single byte transfer takes 22.5 us, so the timeout provides over 200x margin for normal operations.


Custom replacement firmware built with SDCC and fx2lib. Loaded into FX2 RAM for testing via fw_load.py (not flashed to EEPROM).

PropertyValue
ToolchainSDCC + fx2lib
Sourcefirmware/skywalker1.c (1351 lines)
Version ID0x030100
Build date2026-02-12
Load methodRAM upload via tools/fw_load.py

The custom firmware implements all commands needed for the kernel driver: GET_8PSK_CONFIG (0x80), ARM_TRANSFER (0x85), TUNE_8PSK (0x86), GET_SIGNAL_STRENGTH (0x87), BOOT_8PSK (0x89), START_INTERSIL (0x8A), SET_LNB_VOLTAGE (0x8B), SET_22KHZ_TONE (0x8C), SEND_DISEQC (0x8D), GET_SIGNAL_LOCK (0x90), GET_FW_VERS (0x92), USE_EXTRA_VOLT (0x94).

CommandFunction
SPECTRUM_SWEEP (0xB0)Step through frequency range reading signal energy
RAW_DEMOD_READ (0xB1)Read any BCM4500 indirect register
RAW_DEMOD_WRITE (0xB2)Write any BCM4500 indirect register
BLIND_SCAN (0xB3)Try symbol rates at a frequency looking for lock
I2C_BUS_SCAN (0xB4)Probe all 7-bit I2C addresses
I2C_RAW_READ (0xB5)Read from any I2C device address
I2C_DIAG (0xB6)Step-by-step indirect register read diagnostic

The BOOT_8PSK (0x89) command supports incremental debug modes via wValue:

wValueActionResult
0x80No-op: return config_status and boot_stageWorks
0x81GPIO + power + delays only (no I2C)Works
0x82GPIO + power + I2C probe (bmSTOP removed)Works
0x83GPIO + power + probe + init block 0Works
0x84I2C-only probe (chip already powered)Works
0x85Same as 0x82 without bmSTOPWorks
0x01Full boot (production)Works
0x00ShutdownWorks

These modes were used to isolate the I2C STOP corruption bug (see Section 12.4).

I2C Combined Read (repeated START):

static BOOL i2c_combined_read(BYTE addr, BYTE reg, BYTE len, BYTE *buf) {
I2CS |= bmSTART;
I2DAT = addr << 1; // START + write address
// ... wait for DONE, check ACK ...
I2DAT = reg; // Register address
// ... wait for DONE, check ACK ...
I2CS |= bmSTART;
I2DAT = (addr << 1) | 1; // REPEATED START + read address
// ... read len bytes with LASTRD/STOP on final byte ...
}

BCM4500 Init Block Write:

static BOOL bcm_write_init_block(const __code BYTE *data, BYTE len) {
bcm_direct_write(BCM_REG_PAGE, 0x00); // Page select
i2c_write_multi_timeout(BCM4500_ADDR, BCM_REG_DATA, len, data); // Data
bcm_direct_write(BCM_REG_DATA, 0x00); // Trailing zero
bcm_direct_write(BCM_REG_CMD, BCM_CMD_WRITE); // Commit (0x03)
return bcm_poll_ready(); // Wait for completion
}

The SkyWalker-1’s inability to receive DVB-S2 is a fundamental hardware limitation of the BCM4500 demodulator silicon. The BCM4500 was designed before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware. No firmware update can add DVB-S2 support.

FeatureBCM4500 (SkyWalker-1)DVB-S2 Requirement
Inner FECViterbi (DVB-S) or Turbo (proprietary)LDPC
Outer FECReed-Solomon (t=10)BCH
Block sizeConvolutional / short turbo blocks64,800 or 16,200 bits
Decoder typeTrellis (Viterbi) or iterative turboIterative belief propagation
Hardware IPHardwired Viterbi + turbo siliconRequires dedicated LDPC engine

From firmware analysis:

  1. The firmware modulation dispatch table has exactly 10 entries (0—9), with no DVB-S2-specific modes. The bounds check at CODE:0866 rejects values >= 10.
  2. No LDPC/BCH code rate values exist in any FEC lookup table. The XRAM tables at 0xE0B1, 0xE0B7, 0xE0BC, 0xE0BD, and 0xE0F9 contain only Viterbi rates (1/2 through 7/8), turbo rates, and DCII combined codes.
  3. No DVB-S2-specific register addresses appear in any I2C traffic. The BCM4500 is programmed exclusively through indirect registers 0xA6/0xA7/0xA8 with page 0x00.

From Windows BDA driver source:

  1. SkyWalker1TunerFilter.cpp (line 1070): else if(ulNewInnerFecType == BDA_FEC_VITERBI) — only Viterbi FEC is accepted; any other type returns STATUS_INVALID_PARAMETER.
  2. SkyWalker1Control.cpp (line 292): ucCommand[8] = ADV_MOD_DVB_QPSK; — the driver hardcodes modulation type 0 (DVB-S QPSK) regardless of application request.
  3. SkyWalker1Control.h (lines 64—74): modulation constants cap at ADV_MOD_DVB_BPSK (9). No value 10+ exists.

From datasheets:

  1. The BCM4500 datasheet describes exactly two FEC paths: “an advanced modulation turbo decoder” and “a DVB/DIRECTV/DCII-compliant FEC decoder.” No third path for LDPC/BCH.
  2. BCM4500 specification: 128-pin MQFP, 3.3V I/O, 1.8V digital, symbol rate 256 Ksps to 30 Msps. No mention of LDPC, BCH, or DVB-S2.
ChipYearDVB-S2?Notes
BCM4500~2003NoTurbo FEC + legacy Viterbi/RS
BCM45012006YesFirst dual-tuner DVB-S2; LDPC/BCH
BCM45052007YesSingle-channel, 65nm
BCM45062007YesDual-channel, 65nm

Broadcom restricted BCM4501/4505/4506 sales to set-top box manufacturers, preventing Genpix from using them.

Released the SkyWalker-3, replacing the BCM4500 with a different demodulator (likely STMicroelectronics STV0903). The trade-off: gained DVB-S2 LDPC/BCH support, lost proprietary turbo-FEC support (turbo codes are Broadcom/EchoStar proprietary).

The GPIF/USB 2.0 path has approximately 5x headroom for DVB-S2 rates (~58 Mbps max vs ~280 Mbps USB practical throughput). The 8-bit transport stream interface uses the same MPEG-TS format (188-byte packets). The bottleneck is the demodulator silicon.


  • dvb_usb_gp8psk — USB transport and device management
  • gp8psk_fe — DVB frontend (demodulation, tuning)

The kernel module auto-loads via udev when VID:PID 09C0:0203 appears on the USB bus (every FX2 re-enumeration after firmware load). The driver races with test tools and sends its own BOOT_8PSK command.

Symptoms:

  • “resource busy” or “entity not found” errors from test scripts
  • BCM4500 enters unexpected state from partial kernel initialization
  • Kernel driver detaches mid-test

Fix: Blacklist the module:

/etc/modprobe.d/blacklist-gp8psk.conf
blacklist dvb_usb_gp8psk
blacklist gp8psk_fe

Then unload: sudo modprobe -r dvb_usb_gp8psk gp8psk_fe

gp8psk: usb in 149 operation failed.
gp8psk: failed to get FPGA version

Command 0x95 (GET_FPGA_VERS, decimal 149) fails on some SkyWalker-1 units. The driver logs the failure but continues normally.

CommandUsageNotes
0x80 GET_8PSK_CONFIGBoot checkAlways
0x83 I2C_WRITEBCM4500 reg writesVia frontend ops
0x84 I2C_READBCM4500 reg readsVia frontend ops
0x85 ARM_TRANSFERStream start/stopAlways
0x86 TUNE_8PSKFrequency tuningVia frontend ops
0x87 GET_SIGNAL_STRENGTHSNR readbackVia frontend ops
0x88 LOAD_BCM4500BCM4500 FW loadRev.1 Warm only (STALLs on SW-1)
0x89 BOOT_8PSKPower on/offAlways
0x8A START_INTERSILLNB powerAlways
0x8B SET_LNB_VOLTAGE13V/18VVia frontend ops
0x8C SET_22KHZ_TONETone controlVia frontend ops
0x8D SEND_DISEQCDiSEqC messagesVia frontend ops
0x8F SET_DN_SWITCHLegacy Dish switchVia send_legacy_dish_cmd callback
0x90 GET_SIGNAL_LOCKLock statusVia frontend ops
0x92 GET_FW_VERSVersion checkBoot only
0x94 USE_EXTRA_VOLT+1V boostVia enable_high_lnb_voltage callback
0x95 GET_FPGA_VERSPlatform IDBoot only
0x9D CW3K_INITCW3K initPID 0x0206 only

The SkyWalker-1 firmware is stored in Cypress C2 IIC second-stage boot format, read by the FX2’s internal boot ROM on power-up.

Header (8 bytes):

OffsetSizeFieldSkyWalker-1 Value
01Marker0xC2 (external memory, large code model)
12VID (LE)0x09C0
32PID (LE)0x0203
52DID (LE)0x0000
71Config0x40 (400 kHz I2C)

Code segments: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size: 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).

Segment layout (all SkyWalker-1 variants):

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)
FileVIDPIDSegmentsCode SizeEntry
skywalker1_eeprom.bin (v2.06)0x09C00x0203109,472 bytes0xE600
sw1_v213_fw_1_c2.bin (v2.13.1)0x09C00x0203109,322 bytes0xE600
sw1_v213_fw_2_c2.bin (v2.13.2)0x09C00x0203109,377 bytes0xE600
sw1_v213_fw_3_c2.bin (v2.13.3)0x09C00x0203109,369 bytes0xE600
rev2_v210_fw_1_c2.bin (Rev.2)0x09C00x020298,843 bytes0xE600

16.3 DVB-USB Binary Hexline Format (Kernel FW01)

Section titled “16.3 DVB-USB Binary Hexline Format (Kernel FW01)”

The format the kernel expects for dvb-usb-gp8psk-01.fw (only needed for Rev.1 Cold, PID 0x0200):

Record structure:
Offset Size Field
0 1 len - Number of data bytes
1 1 addr_lo - Target address low byte
2 1 addr_hi - Target address high byte
3 1 type - 0x00=data, 0x01=EOF, 0x04=extended addr
4 len data[] - Payload bytes
4+len 1 chk - Checksum byte

Only needed for Rev.1 Warm (PID 0x0201):

Chunk format:
Byte 0: payload_length (N)
Bytes 1-3: header/address bytes
Bytes 4..N+3: payload data
Terminator: single byte 0xFF
Maximum chunk size: 64 bytes (USB control transfer limit)

Command 0x88 (LOAD_BCM4500) initiates the transfer. Each chunk is sent via bulk endpoint 0x01. On the SkyWalker-1, 0x88 routes to STALL (BCM4500 firmware is in ROM).

C2 (EEPROM) and hexline (kernel FW01) are structurally different containers. They cannot be used interchangeably, but the payload data is identical. A C2 file can be converted to hexline by stripping the 8-byte header, splitting segments into 16-byte records, and appending an EOF record.


The root cause of the initial firmware hang was traced through incremental debug modes:

wValueActionResultDiagnosis
0x82GPIO + power + bmSTOP + probeFailsbmSTOP corrupts controller
0x85GPIO + power + probe (no bmSTOP)WorksConfirms bmSTOP is the cause
0x84I2C probe only (chip already powered)WorksBCM4500 is alive; I2C function is correct

Key finding: mode 0x84 succeeds immediately after 0x82 fails, proving the BCM4500 was alive the whole time. The FX2 I2C controller was in a bad state, not the bus or slave.

MetricValue
Boot time~90 ms total
config_status0x03 (STARTED + FW_LOADED)
boot_stage0xFF (COMPLETE)
Direct registers 0xA2-0xA8All return 0x02 (powered, not locked)
Signal lock0x00 (no lock — dish not aimed)
USB responsivenessNo hang; fully responsive throughout

Located in tools/ directory:

ScriptPurpose
test_boot_debug.pySends debug modes 0x80—0x83 sequentially
test_i2c_debug.pyPowers on via 0x81, runs bus scans, tests probe timing
test_i2c_isolate.pyTests re-reset and insufficient delay as failure causes
test_i2c_pinpoint.pyDefinitive test: compares 0x84, 0x85, and 0x82
fw_load.pyRAM firmware loader (halt CPU, write, restart)
AddressNameNotes
0xE600CPUCSCPU control/status; write 0x01 to halt, 0x00 to run
0xE601IFCONFIGInterface configuration (GPIF mode, clock)
0xE60BREVCTLRevision control (NOAUTOARM, SKIPCOMMIT)
0xE618EP2FIFOCFGEP2 FIFO configuration (AUTOIN, 8-bit)
0xE678I2CSI2C control/status
0xE679I2DATI2C data
0xE67AI2CTLI2C speed control
0xE6B8SETUPDAT[0]bmRequestType
0xE6B9SETUPDAT[1]bRequest
0xE6BASETUPDAT[2]wValueL
0xE6BBSETUPDAT[3]wValueH
0xE6BCSETUPDAT[4]wIndexL
0xE6BDSETUPDAT[5]wIndexH
0xE6BESETUPDAT[6]wLengthL
0xE6BFSETUPDAT[7]wLengthH
0xE68AEP0BCHEP0 byte count high
0xE68BEP0BCLEP0 byte count low (write triggers transfer)
0xE740EP0BUFEP0 data buffer start
0xE0B6(custom)LNB voltage control register (XRAM)

  • Ghidra decompilation/disassembly of five 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
    • v2.13.02 FW2 (Ghidra port 8195) — extracted from Windows updater
    • v2.13.03 FW3 (Ghidra port 8196) — extracted from Windows updater
  • Firmware dumps: firmware-dump/
  • 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
  1. gp8psk-driver-analysis.md — Linux kernel driver analysis
  2. firmware-analysis-v206-vs-v213.md — v2.06 vs v2.13 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. tuning-protocol-analysis.md — TUNE_8PSK protocol deep dive
  6. vendor-commands-unknown.md — Vendor command decode (0x8F, 0x91—0x98)
  7. kernel-fw01-analysis.md — Kernel firmware format and EEPROM boot
  8. firmware-dump/fw_v213_comparison_report.md — v2.13 sub-variant comparison
  9. dvb-s2-investigation.md — DVB-S2 incompatibility investigation
  10. docs/boot-debug-findings.md — Boot/I2C debugging findings
  11. docs/diseqc/diseqc-skywalker-1.md — DiSEqC Windows BDA interface
  12. firmware/skywalker1.c — Custom firmware v3.01.0 source