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