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:
Ryan Malloy 2026-02-12 16:32:12 -07:00
parent f1d4f4f010
commit b21f4957f6
42 changed files with 15635 additions and 0 deletions

View file

@ -0,0 +1,135 @@
---
title: Boot Sequence
description: Complete power-on boot flow from USB enumeration through BCM4500 demodulator initialization.
---
import { Steps, Badge, Aside } from '@astrojs/starlight/components';
## EEPROM Boot (Hardware)
The SkyWalker-1 firmware is stored in a 24Cxx-family I2C EEPROM in Cypress C2 format. On power-up, the FX2 boot ROM reads this firmware automatically -- no host interaction is required.
### C2 EEPROM Format
| Offset | Size | Field | SkyWalker-1 Value |
|--------|------|-------|-------------------|
| 0 | 1 | Marker | 0xC2 (external memory, large code model) |
| 1 | 2 | VID (LE) | 0x09C0 |
| 3 | 2 | PID (LE) | 0x0203 |
| 5 | 2 | DID (LE) | 0x0000 |
| 7 | 1 | Config | 0x40 (400 kHz I2C) |
Code segments follow the header: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size is 1023 bytes (FX2 I2C boot ROM buffer limit). All SkyWalker-1 variants use 10 segments.
The terminator is 0x80xx (high bit set) + 2-byte entry point address (0xE600 = CPUCS register).
## Kernel Driver Boot Flow
After USB enumeration, the Linux `dvb_usb_gp8psk` driver executes this initialization sequence:
<Steps>
1. **GET_8PSK_CONFIG (0x80)** -- Read the [configuration status byte](/usb/config-status/). Check bit 0 (`bm8pskStarted`).
2. **BOOT_8PSK (0x89, wValue=1)** -- If not started, power on the BCM4500 demodulator. Then read the firmware version via **GET_FW_VERS (0x92)**.
3. **LOAD_BCM4500 (0x88)** -- If bit 1 (`bm8pskFW_Loaded`) is clear, load BCM4500 firmware. This only applies to Rev.1 Warm (PID 0x0201). On SkyWalker-1, this bit is always set and 0x88 returns STALL.
4. **START_INTERSIL (0x8A, wValue=1)** -- If bit 2 (`bmIntersilOn`) is clear, enable the LNB power supply.
5. **SET_DVB_MODE (0x8E, wValue=1)** -- Attempt to set DVB mode. This STALLs on all SkyWalker-1 firmware versions (the command is a no-op).
6. **ARM_TRANSFER (0x85, wValue=0)** -- Abort any pending MPEG-2 transfer to ensure a clean state.
7. **Device ready for tuning** -- The kernel driver reports the frontend as available.
</Steps>
## BCM4500 Demodulator Boot (BOOT_8PSK, 0x89)
The BOOT_8PSK command handler powers on the BCM4500 demodulator and writes three initialization register blocks via I2C. This sequence was reverse-engineered from stock v2.06 firmware (`FUN_CODE_1D4F` + `FUN_CODE_0ddd`) and re-implemented in custom firmware v3.01.0.
<Steps>
1. **Assert BCM4500 RESET** -- Drive P0.5 LOW. This holds the BCM4500's digital logic in reset while power is applied.
2. **Power on** -- Set P0.1 HIGH (power enable), P0.2 LOW (power disable off). The SkyWalker-1 has complementary power control pins.
3. **Wait for power settle** -- 30 ms delay. The power supply must reach regulation before releasing reset.
4. **Release RESET** -- Drive P0.5 HIGH. The BCM4500 begins its internal power-on reset (POR) and mask ROM boot sequence.
5. **Wait for BCM4500 POR** -- 50 ms delay. The chip needs time for its internal oscillator to stabilize and mask ROM to execute.
6. **I2C probe** -- Read direct register 0xA2 (status) at I2C address 0x08 to verify the chip is alive and responding. If this fails, boot aborts.
7. **Write init block 0** -- 7 bytes to indirect page 0, starting at register 0x06. Data: `{06 0b 17 38 9f d9 80}`.
8. **Write init block 1** -- 8 bytes to indirect page 0, starting at register 0x07. Data: `{07 09 39 4f 00 65 b7 10}`.
9. **Write init block 2** -- 3 bytes to indirect page 0, starting at register 0x0F. Data: `{0f 0c 09}`.
10. **Set config_status** -- OR in `BM_STARTED | BM_FW_LOADED` (0x03). Subsequent vendor commands check this flag before operating.
</Steps>
Each init block is written using the BCM4500 [indirect register protocol](/bcm4500/demodulator/): page select (0xA6 = 0x00), data to 0xA7 (multi-byte with auto-increment), trailing zero to 0xA7, then commit (0xA8 = 0x03). The firmware polls 0xA8 until the command completes before proceeding.
**Total boot time**: approximately 90 ms (30 ms power settle + 50 ms POR delay + ~10 ms I2C transactions).
### Boot Results
| Metric | Value |
|--------|-------|
| Boot time | ~90 ms total |
| config_status after boot | 0x03 (STARTED + FW_LOADED) |
| Direct registers 0xA2-0xA8 | All return 0x02 (powered, not locked -- expected without signal) |
| Signal lock | 0x00 (no lock -- no satellite signal present) |
<Aside type="danger" title="I2C STOP Corruption During Boot">
A critical bug was discovered during custom firmware development: sending `I2CS |= bmSTOP` when no I2C transaction is active (before the probe step) corrupts the FX2 I2C controller state, causing all subsequent I2C operations to fail.
The fix is to simply omit the spurious STOP condition. If the I2C bus is idle (as it should be after power-on), a new START condition succeeds without any bus reset.
See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the complete root cause analysis.
</Aside>
## Firmware Version Identification
The kernel reads the firmware version on boot via GET_FW_VERS (0x92) and logs it:
```
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
```
Kernel revision constants for hardware detection:
```c title="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 constant and trigger Rev.2 behavior.
## FX2 CPUCS Recovery
The FX2's CPUCS register at 0xE600 is accessible via the standard vendor request `bRequest=0xA0` (RAM read/write), which is handled by the FX2 boot ROM in silicon -- not by user firmware. This means firmware can be reloaded over a completely hung device without a physical USB unplug:
```bash title="Recover a hung device"
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.
## Custom Debug Boot Modes (v3.01.0)
The custom firmware extends BOOT_8PSK (0x89) with incremental debug modes for isolating boot failures:
| wValue | Action | Purpose |
|--------|--------|---------|
| 0x80 | No-op: return `config_status` and `boot_stage` | Status check without side effects |
| 0x81 | GPIO + power + delays only (no I2C) | Test power sequencing |
| 0x82 | GPIO + power + I2C probe | Test I2C after power-on |
| 0x83 | GPIO + power + probe + init block 0 | Test first register write |
| 0x84 | I2C probe only (chip already powered) | Isolate I2C from power sequencing |
| 0x85 | Same as 0x82 without bmSTOP | Confirmed the STOP corruption bug |
| 0x01 | Full boot (production) | Normal operation |
| 0x00 | Shutdown | Power down demodulator |
These modes were instrumental in identifying the [I2C STOP corruption bug](/i2c/stop-corruption-bug/) -- mode 0x82 (with bmSTOP) failed while mode 0x85 (without bmSTOP) succeeded, pinpointing the exact cause.

View file

@ -0,0 +1,79 @@
---
title: Configuration Status Byte
description: Bit-field breakdown of the configuration status byte returned by GET_8PSK_CONFIG (0x80).
---
import { Badge, Aside } from '@astrojs/starlight/components';
The configuration status byte is returned by the GET_8PSK_CONFIG vendor command (0x80) and reflects the current device state. The kernel driver checks these bits during initialization to determine which boot steps have already completed.
## Bit Field Map
| Bit | Mask | Name | Meaning |
|-----|------|------|---------|
| 7 | 0x80 | bmArmed | MPEG-2 stream transfer armed / GPIF active |
| 6 | 0x40 | bmDCtuned | DC offset tuning complete (set for DCII modes) |
| 5 | 0x20 | bmSEL18V | 18V LNB voltage selected (else 13V) |
| 4 | 0x10 | bm22kHz | 22 kHz tone active |
| 3 | 0x08 | bmDVBmode | DVB mode enabled |
| 2 | 0x04 | bmIntersilOn | LNB power supply enabled |
| 1 | 0x02 | bm8pskFW_Loaded | BCM4500 firmware loaded |
| 0 | 0x01 | bm8pskStarted | Device booted and running |
## IRAM Storage Address
The status byte is stored at a different IRAM address depending on firmware version:
| Firmware | IRAM Address |
|----------|-------------|
| v2.06 | 0x6D |
| Rev.2 v2.10.4 | 0x4E |
| v2.13 | 0x4F |
## Bit Details
### Bit 0: bm8pskStarted <Badge text="0x01" variant="note" />
Set when BOOT_8PSK (0x89, wValue=1) completes successfully. The kernel driver checks this bit first. If clear, it sends BOOT_8PSK to power on the demodulator.
### Bit 1: bm8pskFW_Loaded <Badge text="0x02" variant="note" />
Indicates that the BCM4500 firmware has been loaded. On the SkyWalker-1, the BCM4500 runs from internal mask ROM, so this bit is **always set** after boot. The kernel driver checks this to decide whether to send LOAD_BCM4500 (0x88), which STALLs on the SkyWalker-1 since it is unnecessary.
### Bit 2: bmIntersilOn <Badge text="0x04" variant="note" />
Set when START_INTERSIL (0x8A, wValue=1) enables the LNB power supply. The name "Intersil" refers to the LNB voltage regulator IC manufacturer.
### Bit 3: bmDVBmode <Badge text="0x08" variant="note" />
Set when DVB-S mode is enabled via SET_DVB_MODE (0x8E). On SkyWalker-1, this command STALLs -- the bit is managed internally by the tuning dispatch logic.
### Bit 4: bm22kHz <Badge text="0x10" variant="note" />
Reflects the current state of the 22 kHz tone (SET_22KHZ_TONE, 0x8C). Set when the tone is active (high band), clear when inactive (low band).
### Bit 5: bmSEL18V <Badge text="0x20" variant="note" />
Reflects the current LNB voltage selection (SET_LNB_VOLTAGE, 0x8B). Set for 18V (horizontal/circular-left), clear for 13V (vertical/circular-right).
### Bit 6: bmDCtuned <Badge text="0x40" variant="note" />
Set when tuning to a Digicipher II (DCII) modulation mode. Cleared for all other modulation types (DVB-S, Turbo, DSS, BPSK). The tuning dispatch logic manages this bit during TUNE_8PSK (0x86) processing.
### Bit 7: bmArmed <Badge text="0x80" variant="note" />
Set when ARM_TRANSFER (0x85, wValue=1) starts the MPEG-2 transport stream. Cleared when ARM_TRANSFER (0x85, wValue=0) stops it. While set, the [GPIF engine](/bcm4500/gpif-streaming/) is continuously reading data from the BCM4500 into the EP2 FIFO.
## Typical Values
| State | Value | Bits Set |
|-------|-------|----------|
| After power-on (before boot) | 0x00 | None |
| After BOOT_8PSK | 0x03 | bm8pskStarted + bm8pskFW_Loaded |
| After LNB enable + 18V + tone | 0x37 | Started + FW + Intersil + 18V + 22kHz |
| Streaming DVB-S | 0xB7 | Above + bmArmed |
| Streaming DCII | 0xF7 | Above + bmDCtuned |
<Aside type="note" title="Kernel Driver Behavior">
The kernel driver reads this byte during initialization and uses it to skip already-completed steps. On a warm reboot (without USB disconnect), some bits may already be set from the previous session, causing the driver to skip BOOT_8PSK, START_INTERSIL, or LOAD_BCM4500 as appropriate.
</Aside>

View file

@ -0,0 +1,105 @@
---
title: USB Interface
description: USB VID/PID table, endpoint map, descriptors, and warm boot behavior for the SkyWalker-1.
---
import { Badge, Aside } from '@astrojs/starlight/components';
## VID/PID Table
All Genpix products share USB Vendor ID `0x09C0`:
| PID | Product | State | Notes |
|-----|---------|-------|-------|
| 0x0200 | 8PSK-to-USB2 Rev.1 | <Badge text="Cold" variant="caution" /> | Requires FW01 upload to RAM |
| 0x0201 | 8PSK-to-USB2 Rev.1 | <Badge text="Warm" variant="success" /> | Requires FW02 (BCM4500 firmware) |
| 0x0202 | 8PSK-to-USB2 Rev.2 | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
| 0x0203 | **SkyWalker-1** | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
| 0x0204 | SkyWalker-1 (alternate) | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
| 0x0205 | SkyWalker-2 | -- | Not in kernel 6.16.5 |
| 0x0206 | SkyWalker CW3K | <Badge text="Warm" variant="success" /> | Requires CW3K_INIT (0x9D) |
PID `0x0203` was added to the Linux kernel `dvb_usb_gp8psk` device table after v6.6.1.
## Endpoint Map
| Property | Value |
|----------|-------|
| Control endpoint | EP0 (default pipe, vendor requests) |
| Bulk IN endpoint | EP2 (address 0x82) -- MPEG-2 transport stream |
| Generic bulk CTRL endpoint | 0x01 (BCM4500 FW02 upload, Rev.1 only) |
## Streaming Properties
| Property | Value |
|----------|-------|
| URB count | 7 |
| URB buffer size | 8192 bytes each |
| Stream type | USB_BULK |
| FX2 controller type | CYPRESS_FX2 |
## EP2 Endpoint Configuration
```c title="EP2CFG Register (0xE610)"
EP2CFG = 0xE2; // valid=1, dir=IN, type=BULK, size=512, buf=DOUBLE
```
| Bit | Value | Meaning |
|-----|-------|---------|
| 7 (VALID) | 1 | Endpoint enabled |
| 6 (DIR) | 1 | IN (device to host) |
| 5:4 (TYPE) | 10 | Bulk transfer |
| 3 (SIZE) | 0 | 512-byte packets |
| 1:0 (BUF) | 10 | Double-buffered |
EP4, EP6, and EP8 are disabled (`&= ~bmVALID`). Only EP2 is used for data streaming.
## Warm Boot Behavior
The SkyWalker-1 (PID 0x0203) enumerates directly as a "warm" device. The DVB-USB framework skips firmware download when `cold_ids` is NULL. No host-side firmware files are required.
| 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 |
<Aside type="note" title="Firmware Files Not Available">
The firmware files `dvb-usb-gp8psk-01.fw` and `dvb-usb-gp8psk-02.fw` were never open-sourced or included in the `linux-firmware` package. They are only needed for the older Rev.1 hardware (PID 0x0200/0x0201), not for the SkyWalker-1.
</Aside>
## USB Control Transfer Protocol
All vendor commands use USB control transfers with these common parameters:
| Parameter | Value |
|-----------|-------|
| bmRequestType | `USB_TYPE_VENDOR` (0x40 for OUT, 0xC0 for IN) |
| Timeout | 2000 ms |
| Retry | Up to 3 attempts for IN operations if partial data received |
| Data buffer maximum | 80 bytes (kernel driver) |
See [Vendor Commands](/usb/vendor-commands/) for the complete command reference.
## Kernel Driver Modules
The Linux kernel uses two modules for the SkyWalker-1:
| Module | Function |
|--------|----------|
| `dvb_usb_gp8psk` | USB transport layer, device management, firmware loading |
| `gp8psk_fe` | DVB frontend operations (demodulation, tuning, signal status) |
<Aside type="tip" title="Blacklisting for Development">
When developing or testing custom firmware, blacklist both kernel modules to prevent the driver from racing with your test tools:
```
# /etc/modprobe.d/blacklist-gp8psk.conf
blacklist dvb_usb_gp8psk
blacklist gp8psk_fe
```
Then unload: `sudo modprobe -r dvb_usb_gp8psk gp8psk_fe`
</Aside>

View file

@ -0,0 +1,177 @@
---
title: Vendor Commands
description: Complete USB vendor command reference with bRequest codes, parameters, and firmware version compatibility.
---
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
All vendor commands use USB control transfers with `USB_TYPE_VENDOR` (bmRequestType bit 6 set). The vendor command dispatcher at CODE:0056 validates `bRequest` in the range 0x80--0x9D (30 entries for v2.06/v2.13) or 0x80--0x9A (27 entries for Rev.2) and dispatches via an indexed jump table at CODE:0076.
<Aside type="note" title="Status Key">
<Badge text="OK" variant="success" /> = Implemented and functional. <Badge text="STALL" variant="danger" /> = Routes to stall handler (endpoint stall returned). <Badge text="Proto" variant="caution" /> = Partial/prototype implementation. <Badge text="N/A" variant="note" /> = Command index out of range (Rev.2 only supports 0x80--0x9A). <Badge text="Changed" variant="caution" /> = Implementation differs between versions.
</Aside>
<Tabs>
<TabItem label="Documented Commands">
## Stock Command Table (0x80--0x9D)
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|-----|------|-----|--------|--------|---------|---------|-------|-------|-------|
| 0x80 | GET_8PSK_CONFIG | IN | 0 | 0 | 1 | Read [configuration status byte](/usb/config-status/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x81 | SET_8PSK_CONFIG | OUT | varies | 0 | 0 | Set config (reserved) | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
| 0x82 | (reserved) | -- | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
| 0x83 | I2C_WRITE | OUT | dev_addr | reg_addr | N | Write to I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x84 | I2C_READ | IN | dev_addr | reg_addr | N | Read from I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x85 | ARM_TRANSFER | OUT | 0/1 | 0 | 0 | Start (1) / stop (0) MPEG-2 stream | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x86 | TUNE_8PSK | OUT | 0 | 0 | 10 | Set [tuning parameters](/bcm4500/tuning-protocol/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x87 | GET_SIGNAL_STRENGTH | IN | 0 | 0 | 6 | Read [SNR and diagnostics](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="Changed" variant="caution" /> |
| 0x88 | LOAD_BCM4500 | OUT | 1 | 0 | 0 | Initiate BCM4500 FW download | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
| 0x89 | BOOT_8PSK | IN | 0/1 | 0 | 1 | Power on (1) / off (0) demodulator | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x8A | START_INTERSIL | IN | 0/1 | 0 | 1 | Enable (1) / disable (0) LNB supply | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x8B | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | 0 | 13V (0) or 18V (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x8C | SET_22KHZ_TONE | OUT | 0/1 | 0 | 0 | Tone off (0) or on (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x8D | SEND_DISEQC_COMMAND | OUT | msg[0] | 0 | len | DiSEqC message or tone burst | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x8E | SET_DVB_MODE | OUT | 1 | 0 | 0 | Enable DVB-S mode | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
| 0x8F | SET_DN_SWITCH | OUT | cmd7bit | 0 | 0 | Legacy Dish Network switch protocol | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x90 | GET_SIGNAL_LOCK | IN | 0 | 0 | 1 | Read [signal lock status](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x92 | GET_FW_VERS | IN | 0 | 0 | 6 | Read firmware version + build date | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x93 | GET_SERIAL_NUMBER | IN | 0 | 0 | 4 | Read 4-byte serial from EEPROM | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x94 | USE_EXTRA_VOLT | OUT | 0/1 | 0 | 0 | Enable +1V LNB boost (14V/19V) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x95 | GET_FPGA_VERS | IN | 0 | 0 | 1 | Read EEPROM hardware/platform ID | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
### Detailed Parameter Formats
**0x87 GET_SIGNAL_STRENGTH**: Returns 6 bytes. Bytes 0--1 contain a 16-bit SNR value (little-endian, dBu x 256 units). Bytes 2--5 are reserved/diagnostic BCM4500 registers. Version differences: v2.06 polls 3 registers (0xA2, 0xA8, 0xA4) up to 6 times; v2.13 consolidates to 1 register with a simplified poll.
**0x8D SEND_DISEQC_COMMAND**: When `wLength > 0`, the payload is a standard DiSEqC message (3--6 bytes) with `wValue` set to `msg[0]` (framing byte, typically 0xE0 or 0xE1). When `wLength == 0` and `wValue == 0`, tone burst A is sent. When `wLength == 0` and `wValue != 0`, tone burst B is sent.
**0x8F SET_DN_SWITCH**: `wValue` carries a 7-bit Dish Network switch command (LSB-first), bit-banged on GPIO P0.4 with specific timing. The 8th bit (0x80) of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.
**0x92 GET_FW_VERS**: Returns 6 bytes of hardcoded constants:
```c title="GET_FW_VERS Response Format"
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).
</TabItem>
<TabItem label="Debug / Internal Commands">
## Debug Commands (0x91, 0x96--0x98)
These commands are not used by any driver (Linux or Windows). They appear to be manufacturing/debug interfaces.
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|-----|------|-----|--------|---------|---------|-------|-------|-------|
| 0x91 | I2C_ADDR_ADJUST | IN | 0=dec, 1=inc | 1 | Inc/dec internal IRAM counter | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x96 | SET_LNB_GPIO_MODE | OUT | 0/1 | 0 | Configure LNB GPIO output enables | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x97 | SET_GPIO_PINS | OUT | bitmap | 0 | Direct write to LNB GPIO pins | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
| 0x98 | GET_GPIO_STATUS | IN | 0 | 1 | Read LNB feedback GPIO pin | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
### 0x91 I2C_ADDR_ADJUST
Increments (`wValue != 0`) or decrements (`wValue == 0`) an internal IRAM counter and returns its current value (1 byte). The counter lives at IRAM 0x66 (v2.06) or IRAM 0x18 (v2.13/Rev.2). Likely used for I2C address or tuner register index adjustment during development.
### 0x96 SET_LNB_GPIO_MODE
Configures GPIO output enable registers for the LNB voltage regulator hardware:
| Mode | v2.06/v2.13 | Rev.2 |
|------|-------------|-------|
| Default (wValue=0) | OEB=0xF0 | OEB=0xE7, OEA=0x9E |
| Active (wValue=1) | IOB=(IOB & 0xF7) OR 0x06; OEB=0xFE | IOB.4 clear; P0.6, P0.0 set; OEA OR= 0x41 |
### 0x97 SET_GPIO_PINS
Direct GPIO pin write for LNB control:
| wValue Bit | v2.06/v2.13 Target | Rev.2 Target |
|-----------|-------------------|-------------|
| bit 1 | IOB.1 (Port B) | P0.6 (Port A) |
| bit 2 | IOB.2 (Port B) | P0.0 (Port A) |
| bit 3 | IOB.3 (Port B) | IOB.4 (Port B) |
### 0x98 GET_GPIO_STATUS
Returns 1 byte (0 or 1) from a single GPIO input pin -- likely an LNB overcurrent or power-good feedback signal:
| Version | Pin Read |
|---------|----------|
| v2.06/v2.13 | IOB.0 (Port B bit 0) |
| Rev.2 | P0.5 (Port A bit 5) |
</TabItem>
<TabItem label="Extended Commands (v2.13+)">
## Extended Commands (0x99--0x9D)
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|-----|------|-----|--------|---------|---------|-------|-------|-------|
| 0x99 | GET_DEMOD_STATUS | IN | 0 | 1 | Read BCM4500 register 0xF9 | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
| 0x9A | INIT_DEMOD | OUT | 0 | 0 | Trigger demod re-init (3 attempts) | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
| 0x9B | (reserved) | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="STALL" variant="danger" /> |
| 0x9C | DELAY_COMMAND | OUT | delay | 0 | Host-controlled tuning delay + poll | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="OK" variant="success" /> |
| 0x9D | CW3K_INIT / SET_MODE_FLAG | OUT | 0/1 | 0 | CW3K init or conditional demod reset | <Badge text="OK" variant="success" /> | <Badge text="N/A" variant="note" /> | <Badge text="Changed" variant="caution" /> |
### 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 routes to STALL.
- The Linux driver only sends CW3K_INIT (0x9D) for SkyWalker CW3K (PID 0x0206).
- Rev.2 supports only commands 0x80--0x9A (27 entries). Commands 0x9B--0x9D are out of range and produce undefined behavior.
</TabItem>
<TabItem label="Custom Firmware">
## Custom Firmware Commands (0xB0--0xB6)
Commands added in custom firmware v3.01.0 for development and diagnostics:
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose |
|-----|------|-----|--------|--------|---------|---------|
| 0xB0 | SPECTRUM_SWEEP | OUT | 0 | 0 | 10 | Step through freq range, read SNR at each step |
| 0xB1 | RAW_DEMOD_READ | IN | reg | 0 | 1 | Read BCM4500 indirect register |
| 0xB2 | RAW_DEMOD_WRITE | OUT | reg | data | 0 | Write BCM4500 indirect register |
| 0xB3 | BLIND_SCAN | OUT | 0 | 0 | 16 | Try symbol rates at given freq, report lock |
| 0xB4 | I2C_BUS_SCAN | IN | 0 | 0 | 16 | Probe all 7-bit addresses, return 16-byte bitmap |
| 0xB5 | I2C_RAW_READ | IN | addr7 | reg | N | Combined write-read from any I2C device |
| 0xB6 | I2C_DIAG | IN | page | 0 | 8 | Step-by-step indirect register diagnostic |
### Parameter Formats
**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 set = ACK received at that 7-bit address.
</TabItem>
</Tabs>
## Vendor Command Dispatch Mechanism
The dispatch logic at CODE:0056 (identical address across all stock versions):
```
1. Check bmRequestType bit 6 -> vendor request?
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 AJMP) -> JMP @A+DPTR
6. If out of range: route to STALL handler
```
The jump table at CODE:0076 contains 2-byte AJMP instruction targets, one per command from 0x80 upward.