Note: v2.06 and v2.13 have 30 jump table entries (0x80-0x9D, range check `< 0x1E`). Rev.2 has only 27 entries (0x80-0x9A, range check `< 0x1B`). Commands 0x9B-0x9D do not exist on Rev.2 hardware.
---
## Command 0x8F: SET_DN_SWITCH
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
This is the `dishnetwork_send_legacy_command` callback in the DVB frontend ops. The 7-bit command value is a legacy Dish Network satellite switch protocol word, bit-banged on GPIO P0.4 with specific timing. The 8th bit (0x80) of the original command selects LNB voltage (13V or 18V) and is sent separately.
### Cross-Version Differences
None. All three versions use identical logic (same GPIO pin P0.4, same timing, same 7-bit protocol). Only the subroutine addresses differ due to code relocation.
---
## Command 0x91: I2C_ADDR_ADJUST (internal debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0 = decrement, non-zero = increment |
| wIndex | 0x0000 |
| wLength | 1 byte |
| Returns | Current value of internal counter byte |
| Purpose | Increment/decrement an internal IRAM counter; return its value |
### Firmware Behavior
```
if wValueL != 0:
IRAM[counter]++
else:
IRAM[counter]--
EP0BUF[0] = IRAM[counter]
EP0BCL = 1
```
### Counter IRAM Addresses
| Version | IRAM Address |
|---------|-------------|
| v2.06 | 0x66 |
| v2.13 | 0x18 |
| Rev.2 | 0x18 |
### Purpose
This appears to be a diagnostic/debug command for adjusting an internal pointer or counter, possibly related to I2C bus addressing or tuner register indexing. The counter is used by other internal routines but is not exposed through any standard driver interface. The name "I2C_ADDR_ADJUST" is inferred from its proximity to I2C commands and the inc/dec readback pattern.
### Cross-Version Differences
- v2.06 uses IRAM address 0x66; v2.13 and Rev.2 use 0x18
- Logic is otherwise identical
---
## Command 0x92: GET_FW_VERS
**Named by**: Linux kernel `gp8psk-fe.h` (`#define GET_FW_VERS 0x92`)
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 6 bytes |
| Returns | Firmware version and build date (6 bytes, fixed per firmware build) |
| Purpose | Read firmware version identifier |
### Data Format
```
Byte 0: Version minor-minor (fw_vers[0])
Byte 1: Version minor (fw_vers[1])
Byte 2: Version major (fw_vers[2])
Byte 3: Build day
Byte 4: Build month
Byte 5: Build year (offset from 2000)
```
Full version number: `fw_vers[2] << 16 | fw_vers[1] << 8 | fw_vers[0]`
// "FW Version = 2.06.4 (0x20604) Build 2007/07/13"
// gp8psk-fe.h: Used for hardware revision detection:
#define GP8PSK_FW_REV1 0x020604
#define GP8PSK_FW_REV2 0x020704
// if GP8PSK_FW_VERS(fw) >= GP8PSK_FW_REV2 -> Rev.2 hardware
```
### Firmware Implementation
The handler writes 6 hardcoded immediate values to EP0BUF[0..5] and sets EP0BCL = 6. There is no I2C or EEPROM access -- the version bytes are compiled directly into the firmware binary.
### Cross-Version Differences
Only the embedded constant values differ (they reflect each firmware's own version). The handler structure is identical across all three versions.
---
## Command 0x93: GET_SERIAL_NUMBER
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 4 bytes |
| Returns | 4-byte device serial number read from I2C EEPROM |
| Purpose | Read unique device serial number from onboard EEPROM |
### Firmware Behavior
1. Call EEPROM initialization routine (sets up I2C for device address 0x51)
2. Read byte at EEPROM bit-offset 0x08 -> EP0BUF[0]
3. Read byte at EEPROM bit-offset 0x10 -> EP0BUF[1]
4. Read byte at EEPROM bit-offset 0x18 -> EP0BUF[2]
5. Read byte at EEPROM bit-offset (from init) -> EP0BUF[3]
6. Set EP0BCL = 4
The I2C EEPROM at address 0x51 (7-bit) is a standard 24Cxx-family serial EEPROM. The serial number bytes are extracted at 8-bit intervals using a shift/rotate extraction routine.
- If flag set: write 0x6A to XRAM 0xE0B6 (LNB control register)
- If flag clear: write 0x62 to XRAM 0xE0B6
5. ACK (v2.06/v2.13: EP0BCL=0; Rev.2: returns 1 byte with result)
The values 0x6A and 0x62 differ in bit 3 (0x08), which controls the extra voltage boost on the LNB power regulator IC. The register at XRAM 0xE0B6 is a hardware control register in the FX2's XRAM-mapped I/O space.
- v2.06/v2.13: Pure OUT command (EP0BCL=0, no data returned)
- **Rev.2**: Returns 1 byte (carry result from the voltage apply subroutine), making it partially an IN command. The Linux driver sends it as OUT and ignores any returned data, so this is backward-compatible.
- The bit-addressable flag location changes between versions but the functional behavior is identical.
---
## Command 0x95: GET_FPGA_VERS
**Named by**: Linux kernel `gp8psk-fe.h` (`#define GET_FPGA_VERS 0x95`)
Despite the name "FPGA," this reads a version/ID byte from the I2C EEPROM. On these devices there is no separate FPGA -- the name is a legacy artifact from the Genpix product line where some models had an FPGA for signal processing. Here it returns the EEPROM-stored hardware platform identifier.
### Cross-Version Differences
- **v2.06**: Reads EEPROM offset 0x31, returns 2 bytes. The Linux driver only requests 1 byte, so it gets EP0BUF[0] (low byte of the 16-bit value).
- **v2.13/Rev.2**: Reads EEPROM offset 0x00, returns 1 byte. This is a cleaner implementation that matches what the driver actually requests.
- The change from offset 0x31 to offset 0x00 suggests the EEPROM layout was reorganized between Rev.1 and Rev.2 hardware.
1. Read `wValueL`, compute carry flag (same pattern as USE_EXTRA_VOLT)
2. Store flag to bit-addressable IRAM location
3. Call GPIO configuration routine:
**If flag set (v2.06/v2.13):**
```
IOB = (IOB & 0xF7) | 0x06 ; clear IOB.3, set IOB.2 and IOB.1
OEB = 0xFE ; enable IOB[7:1] as outputs, IOB.0 as input
```
**If flag clear (v2.06/v2.13):**
```
OEB = 0xF0 ; enable IOB[7:4] as outputs, IOB[3:0] as inputs
```
### Flag Bit Addresses
| Version | Bit Address |
|---------|------------|
| v2.06 | bit 0x07 |
| v2.13 | bit 0x04 |
| Rev.2 | bit 0x06 |
### Subroutine Addresses
| Version | Handler | GPIO Config |
|---------|---------|------------|
| v2.06 | 0x02B4 | 0x2406 |
| v2.13 | 0x02D8 | 0x2344 |
| Rev.2 | 0x02C2 | 0x20F9 |
### Cross-Version Differences
- **v2.06/v2.13**: Controls Port B (IOB/OEB) pins only. Clears IOB.3, sets IOB.2 and IOB.1 for voltage mode.
- **Rev.2**: Uses different GPIO pins reflecting the Rev.2 PCB layout:
- Clears IOB.4 (instead of IOB.3)
- Sets OEB with IOB.4 enabled
- Additionally sets Port A pins: P0.6 and P0.0 (via `ORL P0, #0x41` and `ORL OEA, #0x41`)
- Default mode: `OEB = 0xE7`, `OEA = 0x9E`
This command configures the GPIO output enable registers that control the LNB voltage regulator hardware. It works in conjunction with USE_EXTRA_VOLT (0x94) and SET_LNB_VOLTAGE (0x8B) -- cmd 0x96 sets which pins are active outputs, while 0x8B and 0x94 set the pin states.
---
## Command 0x97: SET_GPIO_PINS (internal/debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | OUT (host-to-device) |
| wValue | GPIO pin state bitmap |
| wIndex | 0x0000 |
| wLength | 0 (no data phase) |
| Purpose | Direct GPIO pin write for LNB/switch hardware control |
### Firmware Behavior
**v2.06/v2.13:**
```
IOB = (IOB & 0xF1) | (wValueL & 0x0E)
```
Clears IOB bits [3:1] and sets them to the corresponding bits from `wValueL`. This gives direct control over the 3 LNB-related GPIO pins on Port B.
**Rev.2:**
Maps individual bits of `wValueL` to different GPIO pins:
```
if wValueL.bit1: P0.6 = 1 else P0.6 = 0 ; Port A pin 6
if wValueL.bit2: P0.0 = 1 else P0.0 = 0 ; Port A pin 0
if wValueL.bit3: IOB.4 = 1 else IOB.4 = 0 ; Port B pin 4
```
### Subroutine Addresses
| Version | Handler | GPIO Write |
|---------|---------|-----------|
| v2.06 | 0x02C1 | 0x24BE |
| v2.13 | 0x02E5 | 0x2429 |
| Rev.2 | 0x02CF | 0x1FCF |
### wValue Bit Mapping
**v2.06/v2.13 (Port B bulk write):**
| wValue Bit | GPIO Pin | Function |
|-----------|---------|----------|
| bit 1 | IOB.1 | LNB regulator control line 1 |
| bit 2 | IOB.2 | LNB regulator control line 2 |
| bit 3 | IOB.3 | LNB regulator control line 3 |
**Rev.2 (individual pin mapping):**
| wValue Bit | GPIO Pin | Function |
|-----------|---------|----------|
| bit 1 | P0.6 (Port A) | LNB control line |
| bit 2 | P0.0 (Port A) | LNB control line |
| bit 3 | IOB.4 (Port B) | LNB control line |
### Cross-Version Differences
Major hardware difference: v2.06/v2.13 use a simple Port B mask write, while Rev.2 maps each bit to different pins on two different ports (A and B). This reflects the Rev.2 PCB redesign that moved LNB control circuitry to different FX2 GPIO pins.
---
## Command 0x98: GET_GPIO_STATUS (internal/debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 1 byte |
| Returns | GPIO input pin state (0 or 1) |
| Purpose | Read LNB-related GPIO input pin state |
### Firmware Behavior
**v2.06/v2.13:**
```
A = IOB & 0x01 ; read Port B bit 0
EP0BUF[0] = A ; return 0 or 1
EP0BCL = 1
```
**Rev.2:**
```
A = P0 ; read Port A
if P0.5 == 1:
R7 = 1
else:
R7 = 0
EP0BUF[0] = R7
EP0BCL = 1
```
### Subroutine Addresses
| Version | Handler | GPIO Read |
|---------|---------|----------|
| v2.06 | 0x02CB | 0x24CC |
| v2.13 | 0x02EF | 0x2437 |
| Rev.2 | 0x02D9 | 0x0046 |
### GPIO Pin Read
| Version | Pin | SFR |
|---------|-----|-----|
| v2.06 | IOB.0 (Port B bit 0) | SFR 0xB0, bit 0 |
| v2.13 | IOB.0 (Port B bit 0) | SFR 0xB0, bit 0 |
| Rev.2 | P0.5 (Port A bit 5) | SFR 0x80, bit 5 |
### Purpose
This reads a single GPIO input pin, likely a feedback/status signal from the LNB power supply circuitry (overcurrent detect, power-good, or similar). The pin assignment changed with the Rev.2 PCB redesign.
### Cross-Version Differences
- v2.06/v2.13: Reads Port B pin 0 (IOB.0) via mask `& 0x01`
- Rev.2: Reads Port A pin 5 (P0.5) via bit test `JNB ACC.5`
- Different GPIO pin due to Rev.2 hardware redesign