Add Starlight documentation site (32 pages, 9 sidebar groups)
Astro 5 + Starlight 0.37 site at site/ with teal/steel theme. Content sourced from 14 reverse engineering docs, master reference, and custom firmware source. Includes Tabs, Badge, Steps, Aside, FileTree, and CardGrid components throughout. DiSEqC SVGs with click-to-zoom via starlight-image-zoom. All internal links validated. Pagefind search indexes all 32 pages.
This commit is contained in:
parent
f1d4f4f010
commit
b21f4957f6
42 changed files with 15635 additions and 0 deletions
287
site/src/content/docs/firmware/custom-v301.mdx
Normal file
287
site/src/content/docs/firmware/custom-v301.mdx
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
---
|
||||
title: Custom Firmware v3.01.0
|
||||
description: Open-source SDCC + fx2lib replacement firmware with diagnostic commands, spectrum sweep, and blind scan.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
The custom v3.01.0 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 new diagnostic, spectrum sweep, and blind scan capabilities. <Badge text="Custom" variant="success" />
|
||||
|
||||
## Project Structure
|
||||
|
||||
<FileTree>
|
||||
- firmware/
|
||||
- skywalker1.c Main firmware source (1351 lines)
|
||||
- Makefile SDCC build rules
|
||||
- skywalker1.ihx Compiled Intel HEX output
|
||||
- tools/
|
||||
- fw_load.py FX2 RAM loader utility
|
||||
- eeprom_flash.py EEPROM flash tool
|
||||
</FileTree>
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Toolchain | SDCC 4.x + fx2lib |
|
||||
| Target | Cypress CY7C68013A (FX2LP) |
|
||||
| Load method | RAM upload via `fw_load.py` (USB 0xA0 vendor request) |
|
||||
| Binary size | ~3 KB |
|
||||
| Source lines | 1351 |
|
||||
| DiSEqC data pin | P0.7 (matches v2.06 hardware) |
|
||||
| BCM4500 I2C address | `0x08` (7-bit); wire address `0x10`/`0x11` |
|
||||
|
||||
<Aside type="tip">
|
||||
To build the firmware from source:
|
||||
|
||||
```bash
|
||||
cd firmware/
|
||||
make # Produces skywalker1.ihx
|
||||
```
|
||||
|
||||
To load into FX2 RAM (does not persist across power cycles):
|
||||
|
||||
```bash
|
||||
python tools/fw_load.py firmware/skywalker1.ihx
|
||||
```
|
||||
|
||||
To make the firmware persistent, flash it to the onboard EEPROM using `eeprom_flash.py`.
|
||||
</Aside>
|
||||
|
||||
## Stock-Compatible Commands
|
||||
|
||||
All stock vendor commands (`0x80`--`0x94`) are implemented for full compatibility with the Linux `dvb_usb_gp8psk` kernel driver:
|
||||
|
||||
| Command | Name | Implementation |
|
||||
|---------|------|---------------|
|
||||
| `0x80` | GET_8PSK_CONFIG | Returns `config_status` byte (1 byte) |
|
||||
| `0x85` | ARM_TRANSFER | Calls `gpif_start()` / `gpif_stop()` |
|
||||
| `0x86` | TUNE_8PSK | Parses 10-byte EP0 payload, programs BCM4500 |
|
||||
| `0x87` | GET_SIGNAL_STRENGTH | Reads 6 BCM4500 indirect registers |
|
||||
| `0x89` | BOOT_8PSK | Full BCM4500 boot sequence (see below) |
|
||||
| `0x8A` | START_INTERSIL | Enables/disables LNB power supply |
|
||||
| `0x8B` | SET_LNB_VOLTAGE | Sets P0.4 for 13V/18V selection |
|
||||
| `0x8C` | SET_22KHZ_TONE | Sets P0.3 for 22 kHz oscillator gate |
|
||||
| `0x8D` | SEND_DISEQC | DiSEqC tone burst via Timer2 bit-bang |
|
||||
| `0x90` | GET_SIGNAL_LOCK | Reads BCM4500 lock register `0xA4` |
|
||||
| `0x92` | GET_FW_VERS | Returns version `0x030100`, build date |
|
||||
| `0x94` | USE_EXTRA_VOLT | Writes `0x62`/`0x6A` to XRAM `0xE0B6` |
|
||||
|
||||
## Custom Diagnostic Commands
|
||||
|
||||
Seven new vendor commands (`0xB0`--`0xB6`) extend the firmware with capabilities absent from all stock versions: <Badge text="New" variant="success" />
|
||||
|
||||
| Command | Name | Direction | Payload | Purpose |
|
||||
|---------|------|-----------|---------|---------|
|
||||
| `0xB0` | SPECTRUM_SWEEP | OUT+Bulk | 10 bytes EP0 | Step through frequencies, return power readings via EP2 |
|
||||
| `0xB1` | RAW_DEMOD_READ | IN | 2 bytes | Read arbitrary BCM4500 indirect register |
|
||||
| `0xB2` | RAW_DEMOD_WRITE | OUT | 3 bytes | Write arbitrary BCM4500 indirect register |
|
||||
| `0xB3` | BLIND_SCAN | OUT+EP0 | 16 bytes EP0 | Sweep symbol rates at a frequency, report lock |
|
||||
| `0xB4` | I2C_SCAN | IN | N bytes | Scan I2C bus for responsive devices |
|
||||
| `0xB5` | GET_BOOT_STAGE | IN | 2 bytes | Read `config_status` + `boot_stage` |
|
||||
| `0xB6` | GET_GPIO_STATE | IN | 3 bytes | Read IOA, IOB, IOD port registers |
|
||||
|
||||
### Spectrum Sweep (0xB0)
|
||||
|
||||
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.
|
||||
|
||||
```c title="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.
|
||||
|
||||
### Blind Scan (0xB3)
|
||||
|
||||
Sweeps symbol rates from `sr_min` to `sr_max` at a given frequency, checking for signal lock at each step.
|
||||
|
||||
```c title="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.
|
||||
|
||||
### Raw Demod Access (0xB1 / 0xB2)
|
||||
|
||||
Direct access to any BCM4500 indirect register, bypassing the stock firmware's limited register set:
|
||||
|
||||
```c title="Raw demod read (0xB1)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// Returns 1 byte in EP0
|
||||
bcm_indirect_read(page, &val);
|
||||
EP0BUF[0] = val;
|
||||
```
|
||||
|
||||
```c title="Raw demod write (0xB2)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// EP0 data = 1 byte value
|
||||
bcm_indirect_write(page, val);
|
||||
```
|
||||
|
||||
## BCM4500 Boot Sequence
|
||||
|
||||
The `bcm4500_boot()` function replicates the stock firmware's initialization with added diagnostic instrumentation. The `boot_stage` variable tracks progress for debugging failed boots.
|
||||
|
||||
<Steps>
|
||||
|
||||
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
|
||||
|
||||
</Steps>
|
||||
|
||||
### BCM4500 Init Data
|
||||
|
||||
Three initialization blocks extracted from stock v2.06 firmware (`FUN_CODE_0ddd`):
|
||||
|
||||
```c title="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.
|
||||
|
||||
### Debug Boot Modes
|
||||
|
||||
The BOOT_8PSK command (`0x89`) accepts debug wValue parameters that execute partial boot sequences for incremental hardware debugging:
|
||||
|
||||
| wValue | Stage | What It Does | Success Marker |
|
||||
|--------|-------|-------------|----------------|
|
||||
| `0x80` | None | No-op, return current state | -- |
|
||||
| `0x81` | GPIO only | GPIO setup + power + reset, no I2C | `0xA1` |
|
||||
| `0x82` | GPIO + probe | GPIO + I2C read of status register | `0xA2` |
|
||||
| `0x83` | GPIO + probe + block 0 | GPIO + I2C + first init block | `0xA3` |
|
||||
| `0x84` | I2C only | Probe without GPIO (chip must be powered) | `0xA4` |
|
||||
| `0x85` | GPIO + probe (no bus reset) | Same as `0x82` without I2CS bmSTOP | `0xA5` |
|
||||
| `0x01` | Full boot | Complete `bcm4500_boot()` sequence | `0xFF` |
|
||||
| `0x00` | Shutdown | Power off BCM4500 | -- |
|
||||
|
||||
<Aside type="note">
|
||||
Debug mode `0x85` was created to isolate a critical bug: sending an I2C STOP when no transaction is active corrupts the FX2 I2C controller state, causing subsequent START+ACK detection to fail. The spurious STOP was present in early custom firmware revisions and was the root cause of BCM4500 boot failures. Removing it fixed the issue.
|
||||
</Aside>
|
||||
|
||||
## I2C Implementation
|
||||
|
||||
The custom firmware implements I2C from scratch rather than using fx2lib's I2C functions, providing full timeout protection:
|
||||
|
||||
```c title="I2C timeout constant"
|
||||
#define I2C_TIMEOUT 6000 // ~5ms at 48MHz (4 clocks/cycle, ~12 MIPS)
|
||||
```
|
||||
|
||||
Key I2C functions:
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `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 |
|
||||
|
||||
<Aside type="caution">
|
||||
Stock firmware has no I2C timeout -- if the BCM4500 holds SCL low (clock stretching), the FX2 spins forever. The custom firmware's 6000-count timeout provides ~5 ms margin at 48 MHz, which is more than 200x the time needed for a single I2C byte at 400 kHz.
|
||||
</Aside>
|
||||
|
||||
### BCM4500 Register Access
|
||||
|
||||
The BCM4500 uses an indirect register protocol through three I2C registers:
|
||||
|
||||
| Register | Address | Purpose |
|
||||
|----------|---------|---------|
|
||||
| BCM_REG_PAGE | `0xA6` | Page/register select |
|
||||
| BCM_REG_DATA | `0xA7` | Data read/write |
|
||||
| BCM_REG_CMD | `0xA8` | Command trigger (`0x01` = read, `0x03` = write) |
|
||||
|
||||
```c title="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
|
||||
```
|
||||
|
||||
## GPIF Streaming
|
||||
|
||||
Transport stream data from the BCM4500 flows through the FX2's GPIF engine into USB endpoint EP2:
|
||||
|
||||
```c title="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 Map
|
||||
|
||||
```c title="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)
|
||||
|
||||
## Differences from Stock Firmware
|
||||
|
||||
| Feature | Stock v2.06 | Custom v3.01 |
|
||||
|---------|-------------|--------------|
|
||||
| Toolchain | Unknown (proprietary) | SDCC + fx2lib (open source) |
|
||||
| I2C timeout | None (infinite spin) | 6000-count (~5 ms) |
|
||||
| Boot diagnostics | None | Incremental debug modes |
|
||||
| New commands | None | 7 commands (`0xB0`--`0xB6`) |
|
||||
| Spectrum sweep | Not possible | `0xB0` with configurable step |
|
||||
| Blind scan | Not possible | `0xB3` with SR sweep |
|
||||
| Raw register access | Not possible | `0xB1`/`0xB2` |
|
||||
| I2C bus scan | Not possible | `0xB4` |
|
||||
| GPIO read | Not possible | `0xB6` |
|
||||
| Anti-tampering | Present (v2.13) | Removed |
|
||||
| Source available | No | Yes (`firmware/skywalker1.c`) |
|
||||
|
||||
## Tuning Implementation
|
||||
|
||||
The `do_tune()` function parses the same 10-byte EP0 payload as the stock firmware:
|
||||
|
||||
```c title="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`
|
||||
213
site/src/content/docs/firmware/fw213-variants.mdx
Normal file
213
site/src/content/docs/firmware/fw213-variants.mdx
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
---
|
||||
title: FW2.13 Sub-Variant Comparison
|
||||
description: Binary and functional comparison of v2.13 firmware sub-variants FW1, FW2, and FW3.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The v2.13 firmware was distributed as three sub-variants via the `SW1_update_2_13_x.exe` Windows updater tool. Ghidra analysis reveals that these target fundamentally different hardware interfaces, not just minor revisions.
|
||||
|
||||
## Overview
|
||||
|
||||
| Aspect | FW1 (v2.13.1) | FW2 (v2.13.2) | FW3 (v2.13.3) |
|
||||
|--------|---------------|---------------|---------------|
|
||||
| Version ID | `0x020D01` | `0x020D01` | `0x020D01` |
|
||||
| Build date | 2010-03-12 | 2010-03-12 | 2010-03-12 |
|
||||
| Functions | 82 | 83 | 83 |
|
||||
| Binary size | 9,322 bytes | 9,377 bytes | 9,369 bytes |
|
||||
| Stack pointer | `0x50` | `0x50` | **`0x52`** |
|
||||
| P0 init | `0xA4` | `0xA4` | **`0xA0`** |
|
||||
| Status register | INTMEM `0x4F` | INTMEM `0x4F` | **INTMEM `0x51`** |
|
||||
| Demod interface | **I2C bus** | **Parallel bus (P0/P1)** | **Parallel bus (enhanced)** |
|
||||
| Config source | Hardcoded | External (`0xE080`--`0xE08E`) | External (`0xE080`--`0xE08E`) |
|
||||
|
||||
<Aside type="note">
|
||||
All three sub-variants report the same version ID (`0x020D01`) to the host. The updater program selects the correct sub-variant based on hardware detection (likely a GPIO strap or I2C device ID read at flash time).
|
||||
</Aside>
|
||||
|
||||
## Hardware Interface Evolution
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="FW1 -- I2C Bus">
|
||||
|
||||
### FW1 (v2.13.1) <Badge text="I2C" variant="note" />
|
||||
|
||||
FW1 targets the original SkyWalker-1 PCB with an **I2C-connected demodulator**. The FX2 communicates with the demod entirely through standard I2C master-mode transactions.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Uses `FUN_CODE_23ae` (I2C START), `FUN_CODE_23ee` (I2C byte write), `FUN_CODE_23d0` (I2C address)
|
||||
- Standard I2C retry with NACK detection
|
||||
- Timer2-based I2C timeout (TR2 check in vendor handler)
|
||||
- Reads back via `FUN_CODE_2164`
|
||||
|
||||
**Unique functions:**
|
||||
- `FUN_CODE_0fc7` -- I2C write-with-retry (20 attempts via I2C bus)
|
||||
- `FUN_CODE_1405` -- Tuner/demodulator identification via I2C + P1 port reads with signature matching
|
||||
- `FUN_CODE_14b9` -- Calibrated delay function with CPUCS clock divider awareness
|
||||
|
||||
**Demodulator type detection** (from `FUN_CODE_1405`):
|
||||
| Type Code | P1 Signature |
|
||||
|-----------|--------------|
|
||||
| Type 3 | `0xA5` or `0xB5` |
|
||||
| Type 4 | `0x5A` |
|
||||
| Type 5 | `0x5B` |
|
||||
| Type 6 | `0x5C` |
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW2 -- Parallel Bus">
|
||||
|
||||
### FW2 (v2.13.2) <Badge text="Parallel" variant="caution" />
|
||||
|
||||
FW2 targets a revised PCB with a **parallel-bus connected demodulator**. The demod data port is connected directly to the FX2's P1, with P0 bits controlling bus signals.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Reads demod type from address table (BANK1 pointer + offset)
|
||||
- Uses `FUN_CODE_11b6` for demod selection
|
||||
- Toggles P0 bits 6/7 for bus control (P0.6 = chip select, P0.7 = read strobe)
|
||||
- Reads data from **P1 port** (8-bit parallel data bus)
|
||||
- Single-phase read: one P1 read per bus cycle
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW2 parallel bus read"
|
||||
uVar1 = P1; // Read data with one bus state
|
||||
P0 |= 0x40; // Change control line
|
||||
uVar2 = P1; // Read again with new state
|
||||
FUN_CODE_1b2a(uVar2, uVar1); // Process both samples
|
||||
```
|
||||
|
||||
**Configuration loading:**
|
||||
- Loads 15 configuration bytes from external memory (`0xE080`--`0xE08E`) into demod registers (`0xE6C0`--`0xE6CD`)
|
||||
- Same device signature matching as FW1 but via parallel bus (`P1 ^ 0x1D` check)
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW3 -- Enhanced Parallel">
|
||||
|
||||
### FW3 (v2.13.3) <Badge text="Enhanced" variant="success" />
|
||||
|
||||
FW3 targets a further revised PCB with the same parallel-bus architecture as FW2 but with a **different bus timing protocol**. Uses dual-phase reads with OR-accumulation.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Initializes OR-accumulators: `DAT_INTMEM_3f = 0; DAT_INTMEM_40 = 0`
|
||||
- Sets P0 | 0x80 once at start (not per-iteration like FW2)
|
||||
- Two separate P1 reads per cycle with different P0.6 states
|
||||
- OR-accumulates results before processing
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW3 dual-phase parallel bus read"
|
||||
DAT_INTMEM_3f = 0;
|
||||
DAT_INTMEM_40 = 0; // Clear accumulators
|
||||
|
||||
// Phase 1: P0.6 high
|
||||
P0 |= 0x44;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_3f |= bVar2; // OR-accumulate
|
||||
|
||||
// Phase 2: P0.6 low
|
||||
P0 &= ~0x40;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_40 |= bVar2; // OR-accumulate
|
||||
|
||||
FUN_CODE_1b2a(0, DAT_INTMEM_3f, DAT_INTMEM_40);
|
||||
```
|
||||
|
||||
**Why OR-accumulation?** This pattern suggests the demod chip variant has either:
|
||||
- Open-drain outputs requiring multiple read cycles
|
||||
- Bus settling time issues on the newer PCB layout
|
||||
- A chip revision that serializes data across multiple bus phases
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Binary Distance Matrix
|
||||
|
||||
Byte-level differences between sub-variants:
|
||||
|
||||
| Pair | Different Bytes | Percentage Different |
|
||||
|------|----------------:|---------------------:|
|
||||
| FW1 vs FW2 | 3,993 | 42.8% |
|
||||
| FW1 vs FW3 | 3,789 | 40.6% |
|
||||
| FW2 vs FW3 | **1,525** | **16.5%** |
|
||||
|
||||
FW2 and FW3 are 83.5% identical at the byte level, confirming they share the same parallel-bus architecture. FW1 diverges significantly because it uses a completely different bus interface (I2C vs. parallel).
|
||||
|
||||
## Memory Comparison at Key Offsets
|
||||
|
||||
### Identical Regions
|
||||
|
||||
These regions are byte-identical across all three sub-variants:
|
||||
|
||||
| Address Range | Content |
|
||||
|---------------|---------|
|
||||
| `0x0000`--`0x000F` | RESET vector (`LJMP 0x170D`), INT0 handler |
|
||||
| `0x0B88`--`0x0B9F` | Init table (same XRAM register initialization) |
|
||||
| `0x06D9`--`0x06F0` | Generic memory access utilities |
|
||||
| `0x1740`--`0x174F` | Bit manipulation lookup table |
|
||||
|
||||
### Critical Divergence: `CODE:0EEA`
|
||||
|
||||
This is where the three sub-variants diverge most dramatically:
|
||||
|
||||
```
|
||||
FW1: 8f44 8c45 8d46 8b47 754a14 e544 b451...
|
||||
(I2C transfer parameters in registers)
|
||||
|
||||
FW2: 753e14 e50d 240a f582 e435 0cf5 83e0...
|
||||
(reads from DPTR+offset table)
|
||||
|
||||
FW3: 753e14 e4f5 3ff5 40 e50d 240a f582...
|
||||
(similar to FW2 + accumulator initialization)
|
||||
```
|
||||
|
||||
FW1's `FUN_CODE_0eea` is a standard I2C master transfer function. FW2/FW3's version is a parallel bus demodulator interface.
|
||||
|
||||
### Thunk Target Divergence (`CODE:1500`)
|
||||
|
||||
```
|
||||
FW1: 02 2252 00 02 22dd 00 02 22c7 00 02 226a 00
|
||||
FW2: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
FW3: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
```
|
||||
|
||||
FW2 and FW3 share identical interrupt handler targets. FW1 jumps to different addresses, reflecting its different internal function layout.
|
||||
|
||||
## Detailed Differences
|
||||
|
||||
### Stack Pointer and Status Register
|
||||
|
||||
| Property | FW1 | FW2 | FW3 |
|
||||
|----------|-----|-----|-----|
|
||||
| SP value | `0x50` | `0x50` | `0x52` |
|
||||
| Status IRAM | `0x4F` | `0x4F` | `0x51` |
|
||||
| I2C buffer IRAM | `0x48`/`0x49` | `0x48`/`0x49` | `0x4A`/`0x4B` |
|
||||
|
||||
FW3 pushes the stack pointer up by 2 bytes to make room for the additional status register at IRAM `0x51`. The 2-byte SP difference exactly accounts for moving the status register from `0x4F` to `0x51`.
|
||||
|
||||
### P0 Init Value
|
||||
|
||||
| Variant | P0 Init | Binary | Difference |
|
||||
|---------|---------|--------|------------|
|
||||
| FW1/FW2 | `0xA4` | `1010 0100` | Bit 2 = 1 |
|
||||
| FW3 | `0xA0` | `1010 0000` | Bit 2 = 0 |
|
||||
|
||||
P0 bit 2 controls a GPIO signal likely related to demodulator interface mode or reset polarity on the FW3 target PCB.
|
||||
|
||||
### Vendor Handler Differences
|
||||
|
||||
| Feature | FW1 | FW2/FW3 |
|
||||
|---------|-----|---------|
|
||||
| Case `0x3D3` | TR2 timer check (I2C timeout) | OR operation (parallel bus) |
|
||||
| Case `0x421`-`0x423` | Simple check | P2.1 write + rotate-left (bus direction) |
|
||||
| Error path | `func_0x06e4` | `DAT=0x0` |
|
||||
|
||||
FW1's timer-based case is used for I2C bus timeout recovery. FW2/FW3's rotate-left and P2.1 write is a parallel bus data direction control.
|
||||
|
||||
## Hardware Progression Theory
|
||||
|
||||
The three sub-variants represent an evolutionary progression:
|
||||
|
||||
1. **FW1 (v2.13.1)**: Original design with I2C-connected demodulator. Simple interface but limited in bandwidth. The FX2 acts purely as an I2C master bridge.
|
||||
|
||||
2. **FW2 (v2.13.2)**: Redesigned with parallel-bus demodulator for higher throughput. P1 carries 8-bit data, P0 provides control signals. External calibration data at `0xE080`--`0xE08E`.
|
||||
|
||||
3. **FW3 (v2.13.3)**: Refined parallel interface for a newer demod silicon revision. Dual-phase reads with OR-accumulation handle bus timing differences. Additional IRAM state tracking (SP bumped to `0x52`).
|
||||
|
||||
All three support the same modulation types (DVB-S QPSK, Turbo QPSK/8PSK/16QAM, DCII, DSS) and the same demod type codes (3--6). The differences are purely hardware interface, not feature set.
|
||||
184
site/src/content/docs/firmware/kernel-fw01.mdx
Normal file
184
site/src/content/docs/firmware/kernel-fw01.mdx
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
title: Kernel FW01 Analysis
|
||||
description: Analysis of the dvb-usb-gp8psk-01.fw firmware format, loading mechanism, and why SkyWalker-1 does not need it.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
The Linux kernel `dvb_usb_gp8psk` driver references two firmware files: `dvb-usb-gp8psk-01.fw` (FX2 microcontroller code) and `dvb-usb-gp8psk-02.fw` (BCM4500 demodulator code). Neither file was ever open-sourced or included in the `linux-firmware` repository. The SkyWalker-1 does not need them.
|
||||
|
||||
## Firmware File Status
|
||||
|
||||
| File | Purpose | Available? | Needed by SkyWalker-1? |
|
||||
|------|---------|------------|----------------------|
|
||||
| `dvb-usb-gp8psk-01.fw` | FX2 RAM code | **Not in linux-firmware** | No |
|
||||
| `dvb-usb-gp8psk-02.fw` | BCM4500 demod code | **Not in linux-firmware** | No |
|
||||
|
||||
Standard locations checked:
|
||||
|
||||
| Path | Result |
|
||||
|------|--------|
|
||||
| `/lib/firmware/dvb-usb-gp8psk-01.fw` | Not found |
|
||||
| `/lib/firmware/dvb-usb-gp8psk-02.fw` | Not found |
|
||||
| `linux-firmware` WHENCE manifest | No gp8psk entry |
|
||||
| Kernel `scripts/get_dvb_firmware` | No gp8psk handler |
|
||||
| `pacman -F dvb-usb-gp8psk-01.fw` | No package provides it |
|
||||
|
||||
<Aside type="note">
|
||||
The gp8psk firmware was presumably distributed by Genpix Electronics with their Windows BDA driver installer. It was never contributed to the Linux firmware collection.
|
||||
</Aside>
|
||||
|
||||
## Why SkyWalker-1 Works Without Firmware Files
|
||||
|
||||
The answer is in the kernel driver's device table. Only Rev.1 Cold devices (PID `0x0200`) have a `cold_ids` entry, which triggers firmware download:
|
||||
|
||||
```c title="Kernel device properties (from gp8psk.c)"
|
||||
.devices = {
|
||||
{ .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver",
|
||||
.cold_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_COLD], NULL },
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_WARM], NULL },
|
||||
},
|
||||
{ .name = "Genpix SkyWalker-1 DVB-S receiver",
|
||||
.cold_ids = { NULL }, // <-- NO cold_ids: skip firmware
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_SKYWALKER_1], NULL },
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
When `cold_ids` is NULL, the DVB-USB framework skips firmware download entirely. The SkyWalker-1 boots from its onboard EEPROM and enumerates directly as a "warm" device.
|
||||
|
||||
| Device | PID | 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 |
|
||||
|
||||
## FW01 Loading Mechanism
|
||||
|
||||
For Rev.1 Cold devices, the firmware loading follows this sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **DVB-USB framework** matches the USB VID:PID against `cold_ids` and calls `dvb_usb_download_firmware()`
|
||||
|
||||
2. **Kernel requests firmware** via `request_firmware("dvb-usb-gp8psk-01.fw", ...)` from the userspace firmware loader
|
||||
|
||||
3. **Cypress FX2 loader** (`usb_cypress_load_firmware()`) halts the FX2 CPU by writing `0x01` to CPUCS register (`0xE600`) via the 0xA0 vendor request
|
||||
|
||||
4. **Hexline records** are parsed and written to FX2 RAM via USB control transfers (0xA0 vendor request)
|
||||
|
||||
5. **FX2 CPU restarted** by writing `0x00` to CPUCS. The device re-enumerates with a new PID (0x0201, "warm")
|
||||
|
||||
6. **DVB-USB framework** re-matches the new PID against `warm_ids` and proceeds to frontend attach
|
||||
|
||||
</Steps>
|
||||
|
||||
The 0xA0 vendor request is handled by the FX2's built-in silicon boot ROM, which provides RAM read/write access regardless of whether user firmware is running. This is the same mechanism used by the custom firmware's `fw_load.py` tool.
|
||||
|
||||
## FW01 Binary Hexline Format
|
||||
|
||||
The kernel's `dvb_usb_get_hexline()` parser expects a compact binary representation of Intel HEX records. This is **not** standard Intel HEX text (`:10000000...`), nor the kernel's `ihex_binrec` format from `<linux/ihex.h>`.
|
||||
|
||||
### 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 - Record type
|
||||
4 len data[] - Payload bytes
|
||||
4+len 1 chk - Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Code/data bytes for FX2 RAM |
|
||||
| `0x01` | EOF | End of file |
|
||||
| `0x04` | Extended Address | Sets upper 16 bits of target address |
|
||||
|
||||
## FW02 Chunk Format (BCM4500 Firmware)
|
||||
|
||||
FW02 is only relevant for Rev.1 Warm devices (PID `0x0201`). It uses a custom chunk protocol:
|
||||
|
||||
```
|
||||
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)
|
||||
```
|
||||
|
||||
The loading sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Initiate transfer**: Send `LOAD_BCM4500` command (`0x88`, `wValue=1`)
|
||||
|
||||
2. **Download chunks**: Iterate through firmware data, sending each chunk via `dvb_usb_generic_write()` on bulk endpoint `0x01`
|
||||
|
||||
3. **Detect end**: Stop when a byte with value `0xFF` is encountered
|
||||
|
||||
</Steps>
|
||||
|
||||
```c title="BCM4500 firmware loading (from gp8psk.c)"
|
||||
ptr = fw_data;
|
||||
while (ptr[0] != 0xFF) {
|
||||
chunk_size = ptr[0] + 4;
|
||||
if (chunk_size > 64) {
|
||||
// Error: chunk too large
|
||||
}
|
||||
usb_control_msg(device, USB_SNDCTRLPIPE, 0, ...,
|
||||
buf, chunk_size, 2000);
|
||||
ptr += chunk_size;
|
||||
}
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
On the SkyWalker-1, command `0x88` (`LOAD_BCM4500`) routes to the STALL handler in all firmware versions -- the BCM4500 firmware is burned into ROM. The kernel driver only attempts this command for Rev.1 Warm devices after checking that `bm8pskFW_Loaded` (bit 1 of `GET_8PSK_CONFIG`) is not set.
|
||||
</Aside>
|
||||
|
||||
## C2 EEPROM Format vs Kernel Hexline
|
||||
|
||||
The firmware as stored in the SkyWalker-1's EEPROM uses Cypress C2 format, which is structurally different from the kernel's binary hexline format. They carry identical payload data but are different containers.
|
||||
|
||||
| Property | C2 (EEPROM) | Hexline (Kernel FW01) |
|
||||
|----------|-------------|-----------------------|
|
||||
| Header | 8-byte C2 with VID/PID/DID | None |
|
||||
| Address encoding | Big-endian 16-bit per segment | Little-endian split (lo, hi) per record |
|
||||
| Data chunking | 1023-byte segments | Typically 16-byte records |
|
||||
| Record overhead | 4 bytes per segment | 5 bytes per record |
|
||||
| Terminator | `0x80xx` + entry point | Type `0x01` EOF record |
|
||||
| Entry point | Explicit in terminator | Implicit (CPUCS at `0xE600`) |
|
||||
|
||||
A C2 file can theoretically be converted to hexline format by:
|
||||
1. Stripping the 8-byte C2 header
|
||||
2. Splitting each segment into 16-byte records with type `0x00`
|
||||
3. Appending an EOF record (len=0, type=`0x01`)
|
||||
|
||||
For the v2.06 EEPROM (9,472 code bytes), this would produce approximately 12,442 bytes in hexline format.
|
||||
|
||||
See the [Storage Formats](/firmware/storage-formats/) page for detailed C2 format documentation.
|
||||
|
||||
## Kernel dmesg Output
|
||||
|
||||
When the SkyWalker-1 is connected, the kernel logs:
|
||||
|
||||
```
|
||||
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
|
||||
gp8psk: usb in 149 operation failed.
|
||||
gp8psk: failed to get FPGA version
|
||||
gp8psk_fe: Frontend attached
|
||||
gp8psk: found Genpix USB device pID = 203 (hex)
|
||||
```
|
||||
|
||||
The "failed to get FPGA version" error is command `0x95` (`GET_FPGA_VERS`, decimal 149) returning an error on some units. Despite the name, there is no FPGA on the SkyWalker-1 -- this command reads a hardware platform ID from the EEPROM. The driver logs the failure but continues normally.
|
||||
210
site/src/content/docs/firmware/rev2-analysis.mdx
Normal file
210
site/src/content/docs/firmware/rev2-analysis.mdx
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
title: Rev.2 Firmware Analysis
|
||||
description: Deep analysis of the Rev.2 v2.10.4 firmware variant with 107 functions and transitional architecture.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The Rev.2 v2.10.4 firmware targets the Rev.2 hardware variant (PID `0x0202`) and contains **107 functions** -- the most of any firmware version. Despite this, it produces the smallest binary (8,843 bytes) due to aggressive function decomposition into small helper routines.
|
||||
|
||||
## Architectural Position
|
||||
|
||||
Rev.2 sits architecturally between v2.06 and v2.13:
|
||||
|
||||
| Aspect | v2.06 | Rev.2 v2.10 | v2.13 |
|
||||
|--------|-------|-------------|-------|
|
||||
| INT0 behavior | USB re-enumeration | USB re-enumeration | Demod polling |
|
||||
| Descriptor base | `0x1200` | `0x0E00` | `0x0E00` |
|
||||
| Stack pointer | `0x72` | `0x4F` | `0x50` |
|
||||
| Vendor command range | `0x80`--`0x9D` (30) | `0x80`--`0x9A` (27) | `0x80`--`0x9D` (30) |
|
||||
| Demod probe at boot | No | No | Yes |
|
||||
| Retry loops | No | No | Yes |
|
||||
| Function count | 61 | **107** | 82-88 |
|
||||
| Binary size | 9,472 bytes | **8,843 bytes** | 9,322 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
The Rev.2 already adopted v2.13's descriptor base offset (`0x0E00`) and similar stack pointer pattern, but retained v2.06's INT0 USB re-enumeration behavior. It lacks v2.13's demodulator polling, retry logic, and the three additional vendor commands (`0x9B`--`0x9D` are out of range).
|
||||
</Aside>
|
||||
|
||||
## Why 107 Functions?
|
||||
|
||||
The high function count is driven by three factors:
|
||||
|
||||
1. **Granular decomposition**: Rev.2 breaks large operations into many small helper functions (10-30 bytes each), where v2.06 inlines the same logic and v2.13 recombines it differently.
|
||||
|
||||
2. **Massive configuration dispatcher**: `FUN_CODE_0800` is 874 bytes and contains an embedded copy of the main loop, causing Ghidra to count additional entry points as separate functions.
|
||||
|
||||
3. **Extra I2C/demodulator helper chains**: GPIO control primitives, hardware-polling wait loops, and I2C bus management exist as individual callable units rather than being inlined.
|
||||
|
||||
## Function Inventory Overview
|
||||
|
||||
The 107 functions are organized into logical groups:
|
||||
|
||||
### Vector Table and ISR Region (0x0000--0x0055)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0000` | `RESET_vector` | 3 | Jump to `main` at `0x155F` |
|
||||
| `0x0003` | `INT0_ISR` | 12 | INT0 handler -- USB re-enumeration |
|
||||
| `0x000F` | `INT0_ISR_bit_clear` | 36 | CPUCS pulse, IRQ clear, delay |
|
||||
| `0x0033` | `INT2_USB_GPIF_vector` | 3 | Clears CCON.4 (PCA timer) |
|
||||
| `0x0036` | `i2c_exchange_byte` | 5 | I2C byte exchange primitive |
|
||||
| `0x003B` | `I2C_ISR` | 8 | I2C interrupt handler |
|
||||
| `0x0043` | `INT4_FX2_vector` | 8 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
| `0x004B` | `INT5_FX2_vector` | 3 | Empty (RETI) |
|
||||
| `0x0053` | `INT6_FX2_vector` | 3 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
|
||||
### Vendor Command Dispatch (0x0056--0x0319)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0056` | `vendor_cmd_dispatch` | 342 | Range check `0x80`--`0x9A`, jump table at `0x0076` |
|
||||
| `0x01AC` | GET_8PSK_CONFIG handler | 361 | Reads config byte, calls LNB probe |
|
||||
| `0x0315` | `vendor_cmd_stall` | 2 | Stall handler (empty RET) |
|
||||
| `0x0319` | Standard USB request handler | 869 | Switch for `bRequest` `0x00`--`0x0B` |
|
||||
|
||||
### Configuration and Tuning (0x0800--0x09A8)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0800` | Config/tuning dispatcher | **874** | 128-entry switch on demod type, embedded main loop |
|
||||
| `0x09A9` | Main init + main loop | 699 | Hardware init, infinite poll loop |
|
||||
|
||||
### BCM4500 and GPIF (0x0C64--0x0F00)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0C64` | BCM4500 firmware loader | 280 | I2C block transfer with address tracking |
|
||||
| `0x0D7C` | GPIF/slave FIFO config | 128 | Enable/disable streaming mode |
|
||||
| `0x0F00` | I2C multi-byte read | 256 | Parameter setup for bus transfer |
|
||||
|
||||
### DiSEqC Implementation (0x07D1, 0x1D5E--0x1E3D)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x07D1` | `DiSEqC byte transmit` | 45 | 8 data bits + odd parity via **P0.4** |
|
||||
| `0x1D5E` | DiSEqC message sender | 59 | Iterates bytes, calls bit-bang per byte |
|
||||
| `0x1E3D` | DiSEqC byte wrapper | 54 | Sets P0.2, adds inter-byte delay |
|
||||
| `0x213C` | DiSEqC bit symbol | 22 | Carrier on/off via P0.3, data via P0.4 |
|
||||
| `0x20E2` | 22 kHz tone burst | 23 | P0.3 ON, 25 ticks, P0.3 OFF |
|
||||
| `0x225F` | Timer2 tick wait | 6 | TF2 poll and clear (500 us tick) |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses **P0.4** as the DiSEqC data pin, unlike v2.06 (P0.7) and v2.13 (P0.0). The carrier pin P0.3 remains the same across all versions. This pin reassignment reflects the different PCB layout of the Rev.2 board.
|
||||
</Aside>
|
||||
|
||||
### LNB and GPIO Control (0x1F5C--0x2038)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x1F5C` | LNB voltage I2C select | 41 | Probes I2C device `0x60` or address from `0xE0B6` |
|
||||
| `0x1FCF` | GPIO pin controller | 46 | Sets P0.6, P0.0, P3.4 based on parameter bits |
|
||||
| `0x2038` | GPIO clock strobe | 51 | Calls pin controller 3 times (setup, clock, cleanup) |
|
||||
| `0x21B1` | LNB voltage select | 17 | Sets/clears P0.4, updates config bit 5 |
|
||||
| `0x21C2` | 22 kHz tone enable | 17 | Sets/clears P0.3, updates config bit 4 |
|
||||
| `0x21D3` | DiSEqC port direction | 17 | Sets/clears P3.6, updates config bit 3 |
|
||||
|
||||
### I2C Bus Management (0x19F4--0x1B90)
|
||||
|
||||
Rev.2 decomposes I2C operations into particularly fine-grained functions:
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x19F4` | I2C bus controller | 92 | Manages SDA/SCL via XRAM `0xE678` |
|
||||
| `0x1A50` | I2C address select + start | 83 | START condition generation |
|
||||
| `0x1AA3` | I2C stop + cleanup | 82 | STOP condition and bus release |
|
||||
| `0x1AF5` | I2C address write helper | 9 | Writes device address byte |
|
||||
| `0x1B01` | I2C byte-level transfer | 67 | Single byte send/receive |
|
||||
| `0x1B44` | I2C ACK/NAK handling | 76 | Acknowledge detection |
|
||||
| `0x1B90` | I2C bus reset/recovery | 74 | Error recovery sequence |
|
||||
| `0x1F06` | I2C completion wait | 43 | Polls XRAM `0xE678` bit 0 with 16-bit timeout |
|
||||
| `0x1F85` | I2C completion wait (2-flag) | 37 | Polls bits 0 and 2 |
|
||||
| `0x2000` | I2C busy wait | 30 | Polls XRAM `0xE678` bit 6 with timeout |
|
||||
|
||||
## Rev.2-Specific Features
|
||||
|
||||
### GPIO Pin Controller (`FUN_CODE_1fcf`)
|
||||
|
||||
A unique function that provides parameterized GPIO control through a bit-field interface:
|
||||
|
||||
```c title="GPIO pin controller (decompiled)"
|
||||
void gpio_pin_controller(BYTE param) {
|
||||
if (param & 0x02) P0 |= 0x01; // P0.0
|
||||
else P0 &= ~0x01;
|
||||
|
||||
if (param & 0x04) P0 |= 0x40; // P0.6
|
||||
else P0 &= ~0x40;
|
||||
|
||||
if (param & 0x08) P3 |= 0x10; // P3.4
|
||||
else P3 &= ~0x10;
|
||||
}
|
||||
```
|
||||
|
||||
This function is called via `FUN_CODE_2038` (GPIO clock strobe) which invokes it three times per cycle -- setup, clock edge, and cleanup -- suggesting it controls a clocked peripheral interface.
|
||||
|
||||
### Descriptor Version Checker (`FUN_CODE_1f31`)
|
||||
|
||||
Walks a USB descriptor chain checking whether `descriptor_byte + 1 == 0x03`, enabling hardware-revision-aware code paths. This mechanism appears in a simplified form in v2.13 as the `_1_3` flag check.
|
||||
|
||||
### Prototype Commands 0x99/0x9A
|
||||
|
||||
Commands 0x99 and 0x9A exist in Rev.2 as partial prototype implementations, before becoming fully functional in v2.13:
|
||||
|
||||
| Command | Rev.2 Behavior | v2.13 Behavior |
|
||||
|---------|---------------|---------------|
|
||||
| `0x99` | Prototype -- limited status read | Full GET_DEMOD_STATUS (reads BCM4500 reg `0xF9`) |
|
||||
| `0x9A` | Prototype -- basic init call | Full INIT_DEMOD (3-attempt re-init with flag check) |
|
||||
|
||||
## Cross-Version Function Mapping
|
||||
|
||||
Key Rev.2 functions and their counterparts in other versions:
|
||||
|
||||
| Rev.2 Function | Role | v2.06 | v2.13 |
|
||||
|----------------|------|-------|-------|
|
||||
| `0x155F` main | RESET entry, IRAM clear | `0x188D` | `0x170D` |
|
||||
| `0x09A9` main init | Init + main loop | `0x09A7` | `0x0800` |
|
||||
| `0x10D9` USB setup | Descriptor/peripheral init | `0x13C3` | `0x11AB` |
|
||||
| `0x0056` vendor dispatch | Vendor command dispatcher | `0x0056` | `0x0056` |
|
||||
| `0x0C64` BCM4500 loader | Firmware block transfer | `0x0DDD` | `0x0CA4` |
|
||||
| `0x0D7C` GPIF/FIFO | Streaming management | `0x1919` | `0x1800` |
|
||||
| `0x1BDA` delay | Clock-compensated delay | `0x1DFB` | `0x14B9` |
|
||||
|
||||
## GPIO Differences from SkyWalker-1
|
||||
|
||||
The Rev.2 board has a different GPIO assignment from the standard SkyWalker-1:
|
||||
|
||||
| Pin | Rev.2 v2.10 | v2.06 / v2.13 |
|
||||
|-----|-------------|---------------|
|
||||
| P0.0 | LNB control (cmd `0x97`) | DiSEqC data (v2.13) / unused (v2.06) |
|
||||
| P0.4 | LNB voltage **+ DiSEqC data** | LNB voltage only |
|
||||
| P0.5 | GPIO status input (cmd `0x98`) | BCM4500 RESET |
|
||||
| P0.6 | GPIO control (cmd `0x97`) | Unused |
|
||||
| P0.7 | Streaming indicator | DiSEqC data (v2.06) / streaming (v2.13) |
|
||||
|
||||
The most significant difference is that Rev.2 multiplexes DiSEqC data onto the LNB voltage pin (P0.4), and the BCM4500 RESET function on P0.5 is replaced by a GPIO status input.
|
||||
|
||||
## Main Loop Structure
|
||||
|
||||
The Rev.2 main loop follows the same pattern as other versions but with the event handling delegated to `FUN_CODE_201e`:
|
||||
|
||||
```c title="Main loop poll (simplified)"
|
||||
void main_loop(void) {
|
||||
// Process init table from CODE:0B48
|
||||
// Call USB/peripheral setup
|
||||
// Enable interrupts
|
||||
|
||||
while (1) {
|
||||
if (sudav_flag) {
|
||||
handle_setupdata();
|
||||
sudav_flag = 0;
|
||||
}
|
||||
FUN_CODE_201e(); // Delegated I2C config read/write
|
||||
if (gpif_flag) {
|
||||
handle_gpif_event();
|
||||
gpif_flag = 0;
|
||||
} else {
|
||||
PCON |= 0x01; // CPU idle
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
256
site/src/content/docs/firmware/storage-formats.mdx
Normal file
256
site/src/content/docs/firmware/storage-formats.mdx
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
---
|
||||
title: Firmware Storage Formats
|
||||
description: Cypress C2 EEPROM boot format, kernel hexline format, and flat binary extraction.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Aside, FileTree, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 firmware exists in multiple container formats depending on context: the onboard EEPROM uses the Cypress C2 IIC boot format, the Linux kernel expects a custom binary hexline format, and analysis tools work with flat extracted binaries.
|
||||
|
||||
## Format Overview
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="C2 EEPROM">
|
||||
|
||||
### Cypress C2 IIC Second-Stage Boot Format <Badge text="Device Storage" variant="note" />
|
||||
|
||||
This is the native format stored in the SkyWalker-1's onboard I2C EEPROM. The FX2's internal boot ROM reads this format on power-up.
|
||||
|
||||
The `0xC2` marker byte in the header identifies this as "external memory, large code model" -- it tells the boot ROM to load code from the EEPROM into internal RAM using the segment map that follows.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Hexline">
|
||||
|
||||
### Kernel Binary Hexline Format <Badge text="Linux Driver" variant="caution" />
|
||||
|
||||
Used only by `dvb-usb-gp8psk-01.fw` for Rev.1 Cold devices. This is a compact binary representation of Intel HEX records parsed by `dvb_usb_get_hexline()` in the kernel. It is NOT standard Intel HEX text.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Flat Binary">
|
||||
|
||||
### Flat Extracted Binary <Badge text="Analysis" variant="success" />
|
||||
|
||||
Raw 8051 machine code extracted from C2 segments. No headers or framing -- just code bytes at their target RAM addresses. Used for Ghidra analysis and binary diffing.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## C2 EEPROM Format
|
||||
|
||||
### Header Structure (8 bytes)
|
||||
|
||||
```
|
||||
Offset Size Field Value (SkyWalker-1)
|
||||
------ ---- ---------- -------------------
|
||||
0x00 1 marker 0xC2 (external memory, large code model)
|
||||
0x01 2 VID 0xC009 -> 0x09C0 (little-endian, Genpix)
|
||||
0x03 2 PID 0x0302 -> 0x0203 (little-endian, SkyWalker-1)
|
||||
0x05 2 DID 0x0000 (device ID, unused)
|
||||
0x07 1 config 0x40 (400 kHz I2C bus speed)
|
||||
```
|
||||
|
||||
The VID and PID in the C2 header determine the USB identifiers that the FX2 enumerates with after boot. This is how the kernel driver identifies the device model.
|
||||
|
||||
### Config Byte (offset 0x07)
|
||||
|
||||
| Value | I2C Speed | Notes |
|
||||
|-------|-----------|-------|
|
||||
| `0x00` | 100 kHz | Default if EEPROM missing |
|
||||
| `0x20` | 200 kHz | |
|
||||
| `0x40` | **400 kHz** | Used by SkyWalker-1 |
|
||||
|
||||
### Code Segment Structure
|
||||
|
||||
Following the 8-byte header, one or more code segments:
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---------- -----
|
||||
0 2 seg_len (big-endian) -- number of data bytes
|
||||
2 2 seg_addr (big-endian) -- target RAM address
|
||||
4 seg_len data[] -- code/data bytes
|
||||
```
|
||||
|
||||
Segments are packed contiguously. The 1023-byte maximum segment size is the limit of the FX2 boot ROM's internal I2C read buffer.
|
||||
|
||||
### Terminator
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 2 0x8001 -- high bit set signals terminator (length with MSB set)
|
||||
2 2 entry -- entry point address (big-endian) = 0xE600 (CPUCS)
|
||||
```
|
||||
|
||||
Writing to CPUCS (`0xE600`) with value `0x00` releases the CPU from reset and begins execution at the reset vector (`0x0000`).
|
||||
|
||||
## Decoded C2 Files
|
||||
|
||||
### Header Comparison
|
||||
|
||||
| File | VID | PID | DID | I2C Speed | Code Size |
|
||||
|------|-----|-----|-----|-----------|-----------|
|
||||
| `skywalker1_eeprom.bin` (v2.06) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,472 bytes |
|
||||
| `sw1_v213_fw_1_c2.bin` (v2.13.1) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,322 bytes |
|
||||
| `sw1_v213_fw_2_c2.bin` (v2.13.2) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,377 bytes |
|
||||
| `sw1_v213_fw_3_c2.bin` (v2.13.3) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,369 bytes |
|
||||
| `rev2_v210_fw_1_c2.bin` (Rev.2) | `0x09C0` | **`0x0202`** | `0x0000` | 400 kHz | 8,843 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses PID `0x0202` while all SkyWalker-1 variants use `0x0203`. This PID difference is the primary mechanism by which the kernel driver distinguishes hardware revisions. The DID field is unused (always `0x0000`).
|
||||
</Aside>
|
||||
|
||||
### Segment Layout (SkyWalker-1 Variants)
|
||||
|
||||
All SkyWalker-1 C2 files use uniform 1023-byte segments (except the last):
|
||||
|
||||
| Segment | Address | Length | Notes |
|
||||
|---------|---------|-------:|-------|
|
||||
| 1 | `0x0000` | 1023 | 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 |
|
||||
|
||||
The address of each segment is exactly `previous_addr + 1023`, creating a contiguous mapping from `0x0000` to the end of the firmware image.
|
||||
|
||||
### Rev.2 Segment Layout
|
||||
|
||||
Rev.2 has only 9 segments (one fewer than SkyWalker-1 variants) because its firmware is smaller:
|
||||
|
||||
| Segment | Address | Length |
|
||||
|---------|---------|-------:|
|
||||
| 1 | `0x0000` | 1023 |
|
||||
| 2 | `0x03FF` | 1023 |
|
||||
| 3 | `0x07FE` | 1023 |
|
||||
| 4 | `0x0BFD` | 1023 |
|
||||
| 5 | `0x0FFC` | 1023 |
|
||||
| 6 | `0x13FB` | 1023 |
|
||||
| 7 | `0x17FA` | 1023 |
|
||||
| 8 | `0x1BF9` | 1023 |
|
||||
| 9 | `0x1FF8` | 659 |
|
||||
|
||||
## Kernel Binary Hexline Format
|
||||
|
||||
This format is used exclusively by `dvb-usb-gp8psk-01.fw` for loading firmware into Rev.1 Cold devices (PID `0x0200`). The SkyWalker-1 never uses this format at runtime.
|
||||
|
||||
### Record Structure
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 1 len Number of data bytes in this record
|
||||
1 1 addr_lo Target address, low byte
|
||||
2 1 addr_hi Target address, high byte
|
||||
3 1 type Record type
|
||||
4 len data[] Payload bytes
|
||||
4+len 1 chk Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Write data bytes to FX2 RAM at address `(addr_hi * 256 + addr_lo)` |
|
||||
| `0x01` | EOF | End of file, no more records |
|
||||
| `0x04` | Extended Linear Address | `data[0]:data[1]` = upper 16 bits of target address |
|
||||
|
||||
### Comparison With Other DVB-USB Firmware
|
||||
|
||||
Files from other DVB-USB devices confirm the format:
|
||||
|
||||
| File | Size | First Record |
|
||||
|------|-----:|-------------|
|
||||
| `dvb-usb-dib0700-1.20.fw` | 33,768 | len=2, addr=0x0000, type=0x04 |
|
||||
| `dvb-usb-it9135-01.fw` | 8,128 | len=3, addr=0x0000, type=0x03 |
|
||||
| `dvb-usb-it9135-02.fw` | 5,834 | len=3, addr=0x0000, type=0x03 |
|
||||
|
||||
## 64K Full EEPROM Dump
|
||||
|
||||
The complete EEPROM can be dumped as a 65,536-byte raw image. The firmware occupies the first ~10 KB; the remainder contains:
|
||||
|
||||
| Offset Range | Content |
|
||||
|-------------|---------|
|
||||
| `0x0000`--`0x0007` | C2 header (8 bytes) |
|
||||
| `0x0008`--`0x25xx` | Code segments (varies by version) |
|
||||
| `0x25xx`--`0x26xx` | C2 terminator |
|
||||
| `0x2700`--`0x3FFF` | Padding / unused (typically `0xFF`) |
|
||||
| `0x4000`--`0x7FFF` | Mirror/unused (some EEPROMs wrap at 32K) |
|
||||
|
||||
<Aside type="caution">
|
||||
Not all 64K images are identical even for the same firmware version. The "unused" region after the terminator may contain artifacts from previous firmware flashes or EEPROM test patterns. Only the data from the C2 header through the terminator is meaningful.
|
||||
</Aside>
|
||||
|
||||
## Format Conversion
|
||||
|
||||
### C2 to Flat Binary
|
||||
|
||||
Extract raw code from a C2 file by stripping headers:
|
||||
|
||||
```python title="C2 to flat binary extraction"
|
||||
def c2_to_flat(c2_data):
|
||||
"""Extract flat binary from C2 EEPROM format."""
|
||||
pos = 8 # Skip 8-byte C2 header
|
||||
flat = bytearray(0x10000) # 64K address space
|
||||
|
||||
while pos < len(c2_data):
|
||||
seg_len = (c2_data[pos] << 8) | c2_data[pos + 1]
|
||||
seg_addr = (c2_data[pos + 2] << 8) | c2_data[pos + 3]
|
||||
pos += 4
|
||||
|
||||
if seg_len & 0x8000: # Terminator (high bit set)
|
||||
break
|
||||
|
||||
flat[seg_addr:seg_addr + seg_len] = c2_data[pos:pos + seg_len]
|
||||
pos += seg_len
|
||||
|
||||
return flat
|
||||
```
|
||||
|
||||
### C2 to Kernel Hexline
|
||||
|
||||
Theoretical conversion for producing a `dvb-usb-gp8psk-01.fw` equivalent:
|
||||
|
||||
```python title="C2 to kernel hexline conversion"
|
||||
def c2_to_hexline(c2_data):
|
||||
"""Convert C2 EEPROM format to kernel binary hexline."""
|
||||
records = bytearray()
|
||||
flat = c2_to_flat(c2_data)
|
||||
code_end = find_code_end(flat)
|
||||
|
||||
for addr in range(0, code_end, 16):
|
||||
chunk = flat[addr:addr + 16]
|
||||
rec_len = len(chunk)
|
||||
addr_lo = addr & 0xFF
|
||||
addr_hi = (addr >> 8) & 0xFF
|
||||
rec_type = 0x00 # Data record
|
||||
chk = (rec_len + addr_lo + addr_hi + rec_type
|
||||
+ sum(chunk)) & 0xFF
|
||||
records.extend([rec_len, addr_lo, addr_hi, rec_type])
|
||||
records.extend(chunk)
|
||||
records.append((~chk + 1) & 0xFF)
|
||||
|
||||
# EOF record
|
||||
records.extend([0x00, 0x00, 0x00, 0x01, 0xFF])
|
||||
return records
|
||||
```
|
||||
|
||||
For the v2.06 firmware (9,472 code bytes), this produces approximately 12,442 bytes in hexline format.
|
||||
|
||||
## File Identification
|
||||
|
||||
Quick format identification by examining the first byte:
|
||||
|
||||
| First Byte | Format | Notes |
|
||||
|------------|--------|-------|
|
||||
| `0xC2` | C2 EEPROM | Standard SkyWalker-1 EEPROM dump |
|
||||
| `0xC0` | C0 EEPROM | VID/PID-only header (no code segments) |
|
||||
| `0x02` | Flat binary | Likely starts with `LJMP` instruction (`0x02 xx xx`) |
|
||||
| Other | Hexline or unknown | Check record structure |
|
||||
287
site/src/content/docs/firmware/version-comparison.mdx
Normal file
287
site/src/content/docs/firmware/version-comparison.mdx
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
---
|
||||
title: Firmware Version Comparison
|
||||
description: Side-by-side comparison of all known SkyWalker-1 firmware revisions including stock and custom builds.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Five firmware versions have been analyzed through Ghidra reverse engineering and source code review. This page compares their architecture, features, and binary characteristics.
|
||||
|
||||
## Version Summary
|
||||
|
||||
| Firmware | Version ID | Build Date | Target PID | Functions | Binary Size | Stack Pointer |
|
||||
|----------|-----------|------------|------------|-----------|-------------|---------------|
|
||||
| v2.06.04 | `0x020604` | 2007-07-13 | `0x0203` | 61 | 9,472 bytes | `0x72` |
|
||||
| Rev.2 v2.10.04 | `0x020A04` | 2010-03-12 | `0x0202` | 107 | 8,843 bytes | `0x4F` |
|
||||
| v2.13.01 (FW1) | `0x020D01` | 2010-03-12 | `0x0203` | 82-88 | 9,322 bytes | `0x50` |
|
||||
| v2.13.02 (FW2) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,377 bytes | `0x50` |
|
||||
| v2.13.03 (FW3) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,369 bytes | `0x52` |
|
||||
| Custom v3.01.0 | `0x030100` | 2026-02-12 | `0x0203` | N/A | ~3 KB (RAM) | N/A |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 v2.10 targets PID `0x0202` (a different product line). All other versions target `0x0203` (SkyWalker-1). The custom v3.01.0 is compiled with SDCC + fx2lib and loaded into FX2 RAM, not flashed to EEPROM.
|
||||
</Aside>
|
||||
|
||||
## Version-by-Version Details
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06">
|
||||
|
||||
### v2.06.04 <Badge text="Stock" variant="note" />
|
||||
|
||||
The original SkyWalker-1 firmware extracted from the device's onboard EEPROM.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020604` |
|
||||
| Build date | 2007-07-13 |
|
||||
| Functions | 61 |
|
||||
| Binary size | 9,472 bytes |
|
||||
| Stack pointer | `0x72` |
|
||||
| Config byte IRAM | `0x6D` |
|
||||
| Descriptor base | `0x1200` |
|
||||
| Init table address | `CODE:0B46` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.7 |
|
||||
|
||||
**Characteristics:**
|
||||
- Simplest firmware with the fewest functions
|
||||
- INT0 handler performs USB re-enumeration (CPUCS pulse)
|
||||
- No demodulator probe at boot
|
||||
- No retry loops or integrity verification
|
||||
- BCM4500 status polling reads 3 registers (0xA2, 0xA8, 0xA4) up to 6 times
|
||||
- Commands 0x99, 0x9A, 0x9C route to STALL
|
||||
- Command 0x9D reads descriptor byte and sets mode flag based on hardware revision (4, 5, or 6)
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Rev.2 v2.10">
|
||||
|
||||
### Rev.2 v2.10.04 <Badge text="Transitional" variant="caution" />
|
||||
|
||||
Firmware for the Rev.2 hardware variant (PID `0x0202`). Architecturally sits between v2.06 and v2.13.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020A04` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 107 (most of any version) |
|
||||
| Binary size | 8,843 bytes (smallest) |
|
||||
| Stack pointer | `0x4F` |
|
||||
| Config byte IRAM | `0x4E` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B48` |
|
||||
| Vendor commands | 27 (`0x80`--`0x9A`) |
|
||||
| DiSEqC data pin | P0.4 |
|
||||
|
||||
**Characteristics:**
|
||||
- Highest function count due to granular decomposition (10-30 byte helper functions)
|
||||
- Smallest binary despite having the most functions
|
||||
- Retained v2.06's INT0 USB re-enumeration behavior
|
||||
- Already adopted v2.13's descriptor base (`0x0E00`) and similar stack pointer
|
||||
- Contains `FUN_CODE_0800`: a massive 874-byte configuration dispatcher with embedded main loop
|
||||
- Lacks v2.13's demodulator polling, retry loops, and additional vendor commands (0x9B--0x9D out of range)
|
||||
- 0x99/0x9A present as prototype implementations
|
||||
|
||||
See the [Rev.2 Analysis](/firmware/rev2-analysis/) page for the complete 107-function inventory.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13 FW1">
|
||||
|
||||
### v2.13.01 (FW1) <Badge text="Stock" variant="note" />
|
||||
|
||||
The most feature-complete stock firmware, targeting the original I2C-connected SkyWalker-1 hardware.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020D01` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 82-88 |
|
||||
| Binary size | 9,322 bytes |
|
||||
| Stack pointer | `0x50` |
|
||||
| Config byte IRAM | `0x4F` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B88` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.0 |
|
||||
|
||||
**New features over v2.06:**
|
||||
- Three new vendor commands: `0x99` (GET_DEMOD_STATUS), `0x9A` (INIT_DEMOD), `0x9C` (DELAY_COMMAND)
|
||||
- INT0 repurposed for demodulator availability polling (40 attempts at addresses 0x7F and 0x3F)
|
||||
- USB re-enumeration moved to `FUN_CODE_2031` (called as normal function before main loop)
|
||||
- Demodulator signature verification (`FUN_CODE_1799`) with 20 retry attempts
|
||||
- Descriptor checksum verification (`FUN_CODE_1ca0`) with 20 retry attempts
|
||||
- Hardware revision detection via descriptor byte (flag `_1_3`)
|
||||
- Consolidated BCM4500 status polling (1 register instead of 3)
|
||||
- Anti-tampering string at firmware offset 0x1880
|
||||
|
||||
See the [FW2.13 Variants](/firmware/fw213-variants/) page for sub-variant comparison.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Custom v3.01">
|
||||
|
||||
### Custom v3.01.0 <Badge text="Custom" variant="success" />
|
||||
|
||||
Open-source replacement firmware built with SDCC + fx2lib. RAM-loaded for testing.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030100` |
|
||||
| Build date | 2026-02-12 |
|
||||
| Toolchain | SDCC + fx2lib |
|
||||
| Source | `firmware/skywalker1.c` (1351 lines) |
|
||||
| Binary size | ~3 KB |
|
||||
| Load method | RAM upload via `tools/fw_load.py` |
|
||||
| DiSEqC data pin | P0.7 (v2.06 assignment) |
|
||||
|
||||
**Additions over stock:**
|
||||
- Seven new diagnostic commands (`0xB0`--`0xB6`)
|
||||
- Incremental debug boot modes (wValue `0x80`--`0x85` for BOOT_8PSK)
|
||||
- I2C timeout protection (6000-iteration countdown vs. infinite spin)
|
||||
- I2C bus scan for device discovery
|
||||
- Spectrum sweep and blind scan capabilities
|
||||
- Raw BCM4500 register access
|
||||
|
||||
See the [Custom v3.01](/firmware/custom-v301/) page for full details.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Architectural Differences
|
||||
|
||||
| Feature | v2.06 | Rev.2 v2.10 | v2.13 | Custom v3.01 |
|
||||
|---------|-------|-------------|-------|--------------|
|
||||
| Vendor commands | 30 | 27 | 30 | 30 stock + 7 custom |
|
||||
| INT0 handler | USB re-enum | USB re-enum | Demod polling | N/A (fx2lib ISR) |
|
||||
| Demod probe at boot | No | No | Yes (40 attempts) | Yes (with timeout) |
|
||||
| Retry loops | No | No | Yes (20-attempt) | Yes (with timeout) |
|
||||
| HW revision detect | No | Yes (descriptor walker) | Yes (flag `_1_3`) | No |
|
||||
| DiSEqC data pin | P0.7 | P0.4 | P0.0 | P0.7 |
|
||||
| Config byte IRAM addr | `0x6D` | `0x4E` | `0x4F` | C variable |
|
||||
| BCM4500 status poll | 3 registers | 3 registers | 1 register | 1 register |
|
||||
| I2C timeout | None | None | None | 6000-count |
|
||||
| Anti-tampering | No | No | Yes | No |
|
||||
| New commands | -- | 0x99/0x9A proto | 0x99, 0x9A, 0x9C | 0xB0--0xB6 |
|
||||
| 0x9D behavior | HW revision mode | N/A (out of range) | Conditional demod reset | N/A |
|
||||
|
||||
## Kernel Version Constants
|
||||
|
||||
The Linux kernel driver defines two firmware version thresholds in `gp8psk-fe.h`:
|
||||
|
||||
```c title="Kernel firmware version constants"
|
||||
GP8PSK_FW_REV1 = 0x020604 // v2.06.4
|
||||
GP8PSK_FW_REV2 = 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
If the firmware version reported by `GET_FW_VERS` (command `0x92`) is >= `GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. All v2.10 and v2.13 firmwares are newer than either constant.
|
||||
|
||||
## Binary Similarity Matrix
|
||||
|
||||
Byte-level comparison across the shared code length (percentage of identical bytes):
|
||||
|
||||
| | 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** | | | | | -- |
|
||||
|
||||
<Aside type="note">
|
||||
The extremely low similarity between major versions (4--8%) indicates complete recompilation with different linker configurations. Functions relocate to entirely different addresses even when the logic is identical. Only the vendor command dispatcher at `CODE:0056` maintains the same address across all versions.
|
||||
</Aside>
|
||||
|
||||
## Key Function Correspondence
|
||||
|
||||
Functions that serve the same role but reside at different addresses:
|
||||
|
||||
| Role | v2.06 | Rev.2 | v2.13 |
|
||||
|------|-------|-------|-------|
|
||||
| RESET vector / main | `0x188D` | `0x155F` | `0x170D` |
|
||||
| Main init + loop | `0x09A7` | `0x09A9` | `0x0800` |
|
||||
| USB descriptor setup | `0x13C3` | `0x10D9` | `0x11AB` |
|
||||
| Standard USB handler | `0x032A` | `0x0319` | `0x034E` |
|
||||
| Vendor cmd dispatch | `0x0056` | `0x0056` | `0x0056` |
|
||||
| Main loop poll | `0x2297` | -- | `0x21EC` |
|
||||
| GPIF/FIFO management | `0x1919` | `0x0D7C` | `0x1800` |
|
||||
| BCM4500 firmware loader | `0x0DDD` | `0x0C64` | `0x0CA4` |
|
||||
| BCM4500 status polling | `0x2000` | -- | `0x208D` |
|
||||
| Delay loop | `0x1DFB` | `0x1BDA` | `0x14B9` |
|
||||
|
||||
## INT0 Handler Evolution
|
||||
|
||||
The INT0 interrupt vector (`CODE:0003`) was repurposed between firmware generations:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06 / Rev.2">
|
||||
|
||||
**USB Re-enumeration** -- pulses CPUCS bit 3 to trigger controlled USB disconnect/reconnect:
|
||||
|
||||
```c title="INT0 handler (v2.06 and Rev.2)"
|
||||
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
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13">
|
||||
|
||||
**Demodulator Availability Polling** -- probes two I2C addresses up to 40 times:
|
||||
|
||||
```c title="INT0 handler (v2.13)"
|
||||
void INT0_vector(void) {
|
||||
for (counter = 0x28; counter != 0; counter--) {
|
||||
byte result = I2C_read(0x7F);
|
||||
if (result != 0x01) {
|
||||
result = I2C_read(0x3F);
|
||||
if (result != 0x01) break;
|
||||
}
|
||||
}
|
||||
no_demod_flag = (counter == 0);
|
||||
}
|
||||
```
|
||||
|
||||
The USB re-enumeration logic was moved to `FUN_CODE_2031` and called as a normal function before the main loop.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## XRAM Initialization Table
|
||||
|
||||
All versions initialize FX2 peripheral registers from a CODE-space table at startup. The table format is identical: `[addr_hi] [addr_lo] [data_byte]` triplets terminated by `0x0000`.
|
||||
|
||||
| Firmware | Table Address | Key Registers Set |
|
||||
|----------|--------------|-------------------|
|
||||
| v2.06 | `CODE:0B46` | IFCONFIG, EP2CFG, EP2FIFOCFG, REVCTL, I2CTL |
|
||||
| Rev.2 | `CODE:0B48` | Same set, 2 bytes later |
|
||||
| v2.13 | `CODE:0B88` | Same set, different offsets |
|
||||
|
||||
All versions set the same final values: `IFCONFIG=0xEE`, `EP2CFG=0xE2`, `EP2FIFOCFG=0x0C`, `REVCTL=0x03`, `I2CTL=0x01`.
|
||||
|
||||
## Anti-Tampering (v2.13 Only)
|
||||
|
||||
All v2.13 sub-variants contain this string at firmware offset `0x1880`:
|
||||
|
||||
```
|
||||
"Tampering is detected. Attempt is logged. Warranty is voided ! \n"
|
||||
```
|
||||
|
||||
This is followed by I2C register write commands (`01 10 aa 82 02 41 41 83`). The mechanism is absent from v2.06, Rev.2, and the custom firmware.
|
||||
|
||||
## Version Identification
|
||||
|
||||
The `GET_FW_VERS` command (`0x92`) 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]`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue