Skip to content

Custom Firmware v3.01

The custom firmware is an open-source replacement for the stock SkyWalker-1 FX2 firmware, built with the SDCC compiler and fx2lib library. It implements all stock vendor commands for kernel driver compatibility and adds diagnostic, spectrum sweep, blind scan, and raw register access capabilities. Custom

See also: v3.02 adds signal monitoring, tune-and-measure, and batch register read commands on top of this base.

  • Directoryfirmware/
    • skywalker1.c Main firmware source (1351 lines)
    • Makefile SDCC build rules
    • skywalker1.ihx Compiled Intel HEX output
  • Directorytools/
    • fw_load.py FX2 RAM loader utility
    • eeprom_flash.py EEPROM flash tool
PropertyValue
ToolchainSDCC 4.x + fx2lib
TargetCypress CY7C68013A (FX2LP)
Load methodRAM upload via fw_load.py (USB 0xA0 vendor request)
Binary size~3 KB
Source lines1351
DiSEqC data pinP0.7 (matches v2.06 hardware)
BCM4500 I2C address0x08 (7-bit); wire address 0x10/0x11

All stock vendor commands (0x800x94) are implemented for full compatibility with the Linux dvb_usb_gp8psk kernel driver:

CommandNameImplementation
0x80GET_8PSK_CONFIGReturns config_status byte (1 byte)
0x85ARM_TRANSFERCalls gpif_start() / gpif_stop()
0x86TUNE_8PSKParses 10-byte EP0 payload, programs BCM4500
0x87GET_SIGNAL_STRENGTHReads 6 BCM4500 indirect registers
0x89BOOT_8PSKFull BCM4500 boot sequence (see below)
0x8ASTART_INTERSILEnables/disables LNB power supply
0x8BSET_LNB_VOLTAGESets P0.4 for 13V/18V selection
0x8CSET_22KHZ_TONESets P0.3 for 22 kHz oscillator gate
0x8DSEND_DISEQCDiSEqC tone burst via Timer2 bit-bang
0x90GET_SIGNAL_LOCKReads BCM4500 lock register 0xA4
0x92GET_FW_VERSReturns version 0x030100, build date
0x94USE_EXTRA_VOLTWrites 0x62/0x6A to XRAM 0xE0B6

Seven new vendor commands (0xB00xB6) extend the firmware with capabilities absent from all stock versions: New

CommandNameDirectionPayloadPurpose
0xB0SPECTRUM_SWEEPOUT+Bulk10 bytes EP0Step through frequencies, return power readings via EP2
0xB1RAW_DEMOD_READIN2 bytesRead arbitrary BCM4500 indirect register
0xB2RAW_DEMOD_WRITEOUT3 bytesWrite arbitrary BCM4500 indirect register
0xB3BLIND_SCANOUT+EP016 bytes EP0Sweep symbol rates at a frequency, report lock
0xB4I2C_SCANINN bytesScan I2C bus for responsive devices
0xB5GET_BOOT_STAGEIN2 bytesRead config_status + boot_stage
0xB6GET_GPIO_STATEIN3 bytesRead IOA, IOB, IOD port registers

Steps through frequencies from start to stop, reading BCM4500 signal energy at each step. Results are packed as u16 LE values into EP2 bulk endpoint.

Spectrum sweep EP0 payload (10 bytes)
EP0BUF[0..3] start_freq (u32 LE, kHz)
EP0BUF[4..7] stop_freq (u32 LE, kHz)
EP0BUF[8..9] step_khz (u16 LE, default 1000 if 0)

At each step, the firmware programs the BCM4500 frequency register via indirect write, waits 10 ms for settling, reads the SNR register pair, and packs the result into EP2 FIFO. When the buffer reaches 512 bytes, it is committed to the host.

Sweeps symbol rates from sr_min to sr_max at a given frequency, checking for signal lock at each step.

Blind scan EP0 payload (16 bytes)
EP0BUF[0..3] freq_khz (u32 LE)
EP0BUF[4..7] sr_min (u32 LE, sps)
EP0BUF[8..11] sr_max (u32 LE, sps)
EP0BUF[12..15] sr_step (u32 LE, sps, default 1000000 if 0)

Returns 8 bytes on lock (freq_khz[4] + sr_locked[4]), or 1 byte 0x00 if no lock found.

Direct access to any BCM4500 indirect register, bypassing the stock firmware’s limited register set:

Raw demod read (0xB1)
// wValue = register page, wIndex = register number
// Returns 1 byte in EP0
bcm_indirect_read(page, &val);
EP0BUF[0] = val;
Raw demod write (0xB2)
// wValue = register page, wIndex = register number
// EP0 data = 1 byte value
bcm_indirect_write(page, val);

The bcm4500_boot() function replicates the stock firmware’s initialization with added diagnostic instrumentation. The boot_stage variable tracks progress for debugging failed boots.

  1. GPIO setup (boot_stage = 1): Set P3.7/P3.6/P3.5 HIGH (control lines idle), assert BCM4500 RESET (P0.5 LOW)

  2. Power on (boot_stage = 2): Enable power supply (P0.1 HIGH, P0.2 LOW), wait 30 ms for settling, release RESET (P0.5 HIGH), wait 50 ms for BCM4500 POR

  3. I2C probe (boot_stage = 3): Read BCM4500 status register 0xA2 to verify the chip is alive on the I2C bus

  4. Init block 0 (boot_stage = 4): Write 7-byte configuration block to BCM4500 page 0 indirect registers

  5. Init block 1 (boot_stage = 5): Write 8-byte configuration block

  6. Init block 2 (boot_stage = 6): Write 3-byte configuration block

  7. Success (boot_stage = 0xFF): Set BM_STARTED | BM_FW_LOADED in config status

Three initialization blocks extracted from stock v2.06 firmware (FUN_CODE_0ddd):

BCM4500 register initialization data
static const __code BYTE bcm_init_block0[] = {
0x06, 0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
};
static const __code BYTE bcm_init_block1[] = {
0x07, 0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
};
static const __code BYTE bcm_init_block2[] = {
0x0f, 0x0c, 0x09
};

Each block is written to BCM4500 page 0 via the indirect register protocol: page select to 0xA6, data bytes to 0xA7, trailing zero to 0xA7, commit 0x03 to 0xA8, then poll for completion.

The BOOT_8PSK command (0x89) accepts debug wValue parameters that execute partial boot sequences for incremental hardware debugging:

wValueStageWhat It DoesSuccess Marker
0x80NoneNo-op, return current state
0x81GPIO onlyGPIO setup + power + reset, no I2C0xA1
0x82GPIO + probeGPIO + I2C read of status register0xA2
0x83GPIO + probe + block 0GPIO + I2C + first init block0xA3
0x84I2C onlyProbe without GPIO (chip must be powered)0xA4
0x85GPIO + probe (no bus reset)Same as 0x82 without I2CS bmSTOP0xA5
0x01Full bootComplete bcm4500_boot() sequence0xFF
0x00ShutdownPower off BCM4500

The custom firmware implements I2C from scratch rather than using fx2lib’s I2C functions, providing full timeout protection:

I2C timeout constant
#define I2C_TIMEOUT 6000 // ~5ms at 48MHz (4 clocks/cycle, ~12 MIPS)

Key I2C functions:

FunctionPurpose
i2c_wait_done()Poll I2CS.bmDONE with 6000-count timeout
i2c_wait_stop()Poll I2CS.bmSTOP clear with timeout
i2c_combined_read()Write-then-read with repeated START (no intermediate STOP)
i2c_write_timeout()Single-byte write with timeout on each phase
i2c_write_multi_timeout()Multi-byte write with timeout

The BCM4500 uses an indirect register protocol through three I2C registers:

RegisterAddressPurpose
BCM_REG_PAGE0xA6Page/register select
BCM_REG_DATA0xA7Data read/write
BCM_REG_CMD0xA8Command trigger (0x01 = read, 0x03 = write)
Indirect register read sequence
// 1. Write [page, 0x00, 0x01] to A6/A7/A8 in one I2C transaction
// 2. Wait for command completion (poll A8 bit 0 == 0)
// 3. Read result from A7

Transport stream data from the BCM4500 flows through the FX2’s GPIF engine into USB endpoint EP2:

GPIF configuration
IFCONFIG = 0xEE; // Internal 48MHz, GPIF master, async, clock output
EP2FIFOCFG = 0x0C; // AUTOIN, ZEROLENIN, 8-bit
FLOWSTATE |= 0x09; // Enable flow state + FS[3]
GPIFTCB3 = 0x80; // Transaction count = 0x80000000 (effectively infinite)

The gpif_start() function arms the GPIF for continuous read into EP2, while gpif_stop() flushes the FIFO and de-asserts the BCM4500 control lines on P3.

GPIO pin definitions (v2.06 hardware)
#define PIN_PWR_EN 0x02 // P0.1 -- power supply enable
#define PIN_PWR_DIS 0x04 // P0.2 -- power supply disable
#define PIN_22KHZ 0x08 // P0.3 -- 22kHz oscillator gate
#define PIN_LNB_VOLT 0x10 // P0.4 -- LNB voltage select
#define PIN_BCM_RESET 0x20 // P0.5 -- BCM4500 hardware reset
#define PIN_DISEQC 0x80 // P0.7 -- DiSEqC data

Initial state after TD_Init():

  • IOA = 0x84 (P0.7 HIGH, P0.2 HIGH — power disabled, streaming off)
  • OEA = 0xBE (P0.1 through P0.5 and P0.7 as outputs)
FeatureStock v2.06Custom v3.01
ToolchainUnknown (proprietary)SDCC + fx2lib (open source)
I2C timeoutNone (infinite spin)6000-count (~5 ms)
Boot diagnosticsNoneIncremental debug modes
Custom commandsNone7 (0xB00xB6)
Spectrum sweepNot possible0xB0 via EP2 bulk
Blind scanNot possible0xB3 with SR sweep
Raw register accessNot possible0xB1/0xB2
I2C bus scanNot possible0xB4
GPIO readNot possible0xB6
Anti-tamperingPresent (v2.13)Removed
Source availableNoYes (firmware/skywalker1.c)

See the v3.02 comparison table for signal monitoring and batch register improvements added on top of v3.01.

The do_tune() function parses the same 10-byte EP0 payload as the stock firmware:

Tune command payload parsing
// Byte-reverse symbol rate and frequency from LE to BE
for (i = 0; i < 4; i++) {
tune_data[i] = EP0BUF[3 - i]; // Symbol rate (BE)
tune_data[4 + i] = EP0BUF[7 - i]; // Frequency (BE)
}
tune_data[8] = EP0BUF[8]; // Modulation type (0-9)
tune_data[9] = EP0BUF[9]; // FEC index
tune_data[10] = 0x10; // Demod mode (standard)
tune_data[11] = 0x00; // Turbo flag

Modulation-specific handling:

  • Modulation types 1—3 (turbo modes): Set turbo flag tune_data[11] = 0x01
  • Modulation type 5: DCII I-stream, demod mode 0x12
  • Modulation type 6: DCII Q-stream, demod mode 0x16
  • Modulation type 7: DCII offset QPSK, demod mode 0x11