Add vendor command decode and tuning protocol deep analysis
Complete reverse engineering of all unknown vendor commands (0x8F, 0x91-0x98) across v2.06, v2.13, and Rev.2 firmware versions. Full TUNE_8PSK (cmd 0x86) protocol analysis including EP0BUF format, modulation dispatch jump table, BCM4500 I2C indirect register sequences, and FEC lookup tables.
This commit is contained in:
parent
e4117421a1
commit
76f0439576
895
tuning-protocol-analysis.md
Normal file
895
tuning-protocol-analysis.md
Normal file
@ -0,0 +1,895 @@
|
|||||||
|
# Genpix SkyWalker-1 TUNE_8PSK (0x86) Protocol Deep-Dive
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This document presents a complete reverse engineering analysis of the TUNE_8PSK vendor command (0x86) as implemented in three Genpix SkyWalker-1 FX2 firmware versions. The analysis traces the full signal path from USB control transfer through EP0BUF parameter parsing, modulation-specific BCM4500 configuration, I2C register programming, and signal acquisition polling. Supporting commands (LNB voltage, 22 kHz tone, signal lock, signal strength) are fully documented.
|
||||||
|
|
||||||
|
**Firmware versions analyzed:**
|
||||||
|
- v2.06 (Ghidra port 8193) -- 61 functions, earliest revision
|
||||||
|
- v2.13 FW1 (Ghidra port 8194) -- 88 functions, latest revision
|
||||||
|
- Rev.2 v2.10.4 (Ghidra port 8197) -- 107 functions, intermediate revision
|
||||||
|
|
||||||
|
**Primary analysis target:** Rev.2 v2.10.4 (clearest decompilation, most granular function decomposition)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. TUNE_8PSK Command Handler (0x86)
|
||||||
|
|
||||||
|
### 1.1 Vendor Dispatch Entry Points
|
||||||
|
|
||||||
|
All three firmware versions use identical vendor command dispatch logic at CODE:0056. The dispatcher checks `bmRequestType` bit 6 (vendor request), validates `bRequest` is in range, and performs an indexed jump via `JMP @A+DPTR` at the jump table starting at CODE:0076.
|
||||||
|
|
||||||
|
| Firmware | TUNE_8PSK Jump Target | Config Status Byte | Tune Function |
|
||||||
|
|----------|----------------------|-------------------|---------------|
|
||||||
|
| v2.06 | 0x012E | IRAM 0x6D | 0x0800 (via FUN_CODE_09a7) |
|
||||||
|
| v2.13 | 0x012E | IRAM 0x4F | 0x09DF |
|
||||||
|
| Rev.2 v2.10.4 | 0x0118 | IRAM 0x4E | 0x0800 |
|
||||||
|
|
||||||
|
### 1.2 Handler Inline Code
|
||||||
|
|
||||||
|
The TUNE_8PSK handler at the jump table target performs two operations:
|
||||||
|
|
||||||
|
1. **Wait for EP0 data phase** -- calls the EP0 flush function to ensure the 10-byte command payload has arrived in EP0BUF
|
||||||
|
2. **Check device status** -- reads the config status byte and verifies bit 0 (bm8pskStarted) is set before proceeding
|
||||||
|
3. **Call tune function** -- if the device is started, calls the main tune function
|
||||||
|
|
||||||
|
**Rev.2 v2.10.4 (0x0118):**
|
||||||
|
```
|
||||||
|
CODE:0118 LCALL 0x2167 ; EP0 flush -- wait for EP0BUF data ready
|
||||||
|
CODE:011B MOV A, 0x4E ; Read config status byte (IRAM 0x4E)
|
||||||
|
CODE:011D JNB ACC.0, +3 ; Skip tune if device not started
|
||||||
|
CODE:0120 LCALL 0x0800 ; Call main tune function (FUN_CODE_0800)
|
||||||
|
```
|
||||||
|
|
||||||
|
**v2.06 (0x012E):**
|
||||||
|
```
|
||||||
|
CODE:012E LCALL 0x23E0 ; EP0 flush function
|
||||||
|
CODE:0131 MOV A, 0x6D ; Config status byte (IRAM 0x6D)
|
||||||
|
CODE:0133 JNB ACC.0, +3 ; Skip if not started
|
||||||
|
CODE:0136 LCALL 0x0800 ; Main tune function
|
||||||
|
```
|
||||||
|
|
||||||
|
**v2.13 (0x012E):**
|
||||||
|
```
|
||||||
|
CODE:012E LCALL 0x231E ; EP0 flush function
|
||||||
|
CODE:0131 MOV A, 0x4F ; Config status byte (IRAM 0x4F)
|
||||||
|
CODE:0133 JNB ACC.0, +3 ; Skip if not started
|
||||||
|
CODE:0136 LCALL 0x09DF ; Main tune function (different address)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. EP0BUF Parameter Parsing
|
||||||
|
|
||||||
|
### 2.1 10-Byte Command Format
|
||||||
|
|
||||||
|
The host (Windows BDA driver or Linux dvb-usb-gp8psk) sends a 10-byte payload via USB control transfer OUT:
|
||||||
|
|
||||||
|
```
|
||||||
|
USB Setup Packet:
|
||||||
|
bmRequestType = 0x40 (Vendor, Host-to-Device)
|
||||||
|
bRequest = 0x86 (TUNE_8PSK)
|
||||||
|
wValue = 0x0000
|
||||||
|
wIndex = 0x0000
|
||||||
|
wLength = 10
|
||||||
|
|
||||||
|
EP0BUF Layout (0xE740-0xE749):
|
||||||
|
Offset XRAM Addr Content Encoding
|
||||||
|
------ --------- ------------------- ------------------
|
||||||
|
[0] 0xE740 Symbol Rate byte 0 Little-endian LSB
|
||||||
|
[1] 0xE741 Symbol Rate byte 1
|
||||||
|
[2] 0xE742 Symbol Rate byte 2
|
||||||
|
[3] 0xE743 Symbol Rate byte 3 Little-endian MSB
|
||||||
|
[4] 0xE744 Frequency byte 0 Little-endian LSB
|
||||||
|
[5] 0xE745 Frequency byte 1
|
||||||
|
[6] 0xE746 Frequency byte 2
|
||||||
|
[7] 0xE747 Frequency byte 3 Little-endian MSB
|
||||||
|
[8] 0xE748 Modulation Type 0-9 (see section 3)
|
||||||
|
[9] 0xE749 Inner FEC Rate See FEC tables
|
||||||
|
```
|
||||||
|
|
||||||
|
**Symbol Rate** is in samples per second (sps). The Windows driver converts from ksps: `ulTempSymbolRate = pDeviceParameter->ulSymbolRate * 1000`
|
||||||
|
|
||||||
|
**Frequency** is in kHz, computed as `(CarrierFrequency - LO_Frequency) * FrequencyMultiplier`. Valid range: 800,000 - 2,250,000 kHz (after LNB downconversion: 950-2150 MHz IF).
|
||||||
|
|
||||||
|
### 2.2 Firmware EP0BUF Read Sequence (Rev.2 v2.10.4 Disassembly)
|
||||||
|
|
||||||
|
The tune function at 0x0800 begins with direct EP0BUF reads:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Read modulation type and FEC rate
|
||||||
|
CODE:0802 MOV DPTR, #0xE748 ; EP0BUF[8] = modulation type
|
||||||
|
CODE:0805 MOVX A, @DPTR
|
||||||
|
CODE:0806 MOV 0x4D, A ; Store to IRAM 0x4D (DAT_INTMEM_4d)
|
||||||
|
CODE:0808 INC DPTR ; DPTR = 0xE749
|
||||||
|
CODE:0809 MOVX A, @DPTR ; EP0BUF[9] = FEC rate
|
||||||
|
CODE:080A MOV 0x4F, A ; Store to IRAM 0x4F (DAT_INTMEM_4f)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frequency bytes** (EP0BUF[4-7]) are byte-reversed from little-endian to big-endian for BCM4500:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Loop: i = 0..3
|
||||||
|
; EP0BUF[4+i] -> XRAM[0xE0DE - i] (frequency, LE -> BE)
|
||||||
|
CODE:0816 MOV A, #0x44 ; 0x44 = EP0BUF offset for freq[0] (0xE740+4=0xE744)
|
||||||
|
CODE:0818 ADD A, 0x3B ; + loop index i
|
||||||
|
CODE:081A MOV DPL, A
|
||||||
|
CODE:081C CLR A
|
||||||
|
CODE:081D ADDC A, #0xE7 ; DPH = 0xE7
|
||||||
|
CODE:0821 MOVX A, @DPTR ; Read EP0BUF[4+i]
|
||||||
|
CODE:0822 MOV R7, A ; Save byte
|
||||||
|
|
||||||
|
; Compute destination: 0xE0D8 + (6 - i) = 0xE0DE - i
|
||||||
|
CODE:0827 MOV A, #0x06
|
||||||
|
CODE:0829 SUBB A, R5 ; 6 - i
|
||||||
|
CODE:082F MOV A, #0xD8 ; Base = 0xE0D8
|
||||||
|
CODE:0831 ADD A, R5
|
||||||
|
CODE:0832 MOV DPL, A
|
||||||
|
CODE:0834 MOV A, #0xE0
|
||||||
|
CODE:0837 MOV DPH, A
|
||||||
|
CODE:0839 MOV A, R7
|
||||||
|
CODE:083A MOVX @DPTR, A ; Store byte-reversed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Symbol rate bytes** (EP0BUF[0-3]) follow the same pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
; EP0BUF[i] -> XRAM[0xE0C7 + (7 - i)] = XRAM[0xE0CE - i] (symbol rate, LE -> BE)
|
||||||
|
CODE:083B MOV A, #0x40 ; 0x40 = EP0BUF offset for SR[0] (0xE740+0=0xE740)
|
||||||
|
CODE:083D ADD A, 0x3B ; + loop index i
|
||||||
|
CODE:0846 MOVX A, @DPTR ; Read EP0BUF[i]
|
||||||
|
|
||||||
|
; Destination: 0xE0C7 + (7 - i) = 0xE0CE - i
|
||||||
|
CODE:084C MOV A, #0x07
|
||||||
|
CODE:084E SUBB A, R5 ; 7 - i
|
||||||
|
CODE:0854 MOV A, #0xC7 ; Base = 0xE0C7
|
||||||
|
CODE:0856 ADD A, R5
|
||||||
|
CODE:085F MOVX @DPTR, A ; Store byte-reversed
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 XRAM Storage Map After Parsing
|
||||||
|
|
||||||
|
```
|
||||||
|
IRAM Variables:
|
||||||
|
0x4D Modulation type (0-9) [from EP0BUF[8]]
|
||||||
|
0x4F Inner FEC rate index [from EP0BUF[9]]
|
||||||
|
|
||||||
|
XRAM Frequency (big-endian, 4 bytes):
|
||||||
|
0xE0DB Frequency byte 3 (MSB) [from EP0BUF[7]]
|
||||||
|
0xE0DC Frequency byte 2 [from EP0BUF[6]]
|
||||||
|
0xE0DD Frequency byte 1 [from EP0BUF[5]]
|
||||||
|
0xE0DE Frequency byte 0 (LSB) [from EP0BUF[4]]
|
||||||
|
|
||||||
|
XRAM Symbol Rate (big-endian, 4 bytes):
|
||||||
|
0xE0CB Symbol Rate byte 3 (MSB) [from EP0BUF[3]]
|
||||||
|
0xE0CC Symbol Rate byte 2 [from EP0BUF[2]]
|
||||||
|
0xE0CD Symbol Rate byte 1 [from EP0BUF[1]]
|
||||||
|
0xE0CE Symbol Rate byte 0 (LSB) [from EP0BUF[0]]
|
||||||
|
```
|
||||||
|
|
||||||
|
The byte reversal converts the host's little-endian format to the BCM4500's big-endian register format, so the values can be written directly to the demodulator via I2C without further conversion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Modulation Mode Dispatch
|
||||||
|
|
||||||
|
### 3.1 Dispatch Table (Rev.2 at CODE:0873)
|
||||||
|
|
||||||
|
After parsing EP0BUF, the firmware validates the modulation type (IRAM 0x4D) is < 10, then dispatches via a jump table:
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:0864 MOV A, 0x4D ; Load modulation type
|
||||||
|
CODE:0866 CJNE A, #0x0A, +0 ; Compare with 10
|
||||||
|
CODE:0869 JC 0x086D ; If < 10, proceed to dispatch
|
||||||
|
CODE:086B AJMP 0x098E ; If >= 10, skip to post-tune (invalid mod)
|
||||||
|
CODE:086D MOV DPTR, #0x0873 ; Jump table base
|
||||||
|
CODE:0870 ADD A, A ; Double index (2 bytes per entry)
|
||||||
|
CODE:0872 JMP @A+DPTR ; Dispatch
|
||||||
|
```
|
||||||
|
|
||||||
|
**Jump Table Memory (CODE:0873, 20 bytes):**
|
||||||
|
|
||||||
|
```
|
||||||
|
Offset Bytes AJMP Target Modulation Type
|
||||||
|
------ ------ ----------- ---------------
|
||||||
|
+0x00 01 B7 0x08B7 0 = ADV_MOD_DVB_QPSK
|
||||||
|
+0x02 01 DF 0x08DF 1 = ADV_MOD_TURBO_QPSK
|
||||||
|
+0x04 01 FA 0x08FA 2 = ADV_MOD_TURBO_8PSK
|
||||||
|
+0x06 21 15 0x0915 3 = ADV_MOD_TURBO_16QAM
|
||||||
|
+0x08 21 47 0x0947 4 = ADV_MOD_DCII_C_QPSK (Combo)
|
||||||
|
+0x0A 21 4F 0x094F 5 = ADV_MOD_DCII_I_QPSK (I-stream)
|
||||||
|
+0x0C 21 57 0x0957 6 = ADV_MOD_DCII_Q_QPSK (Q-stream)
|
||||||
|
+0x0E 21 5F 0x095F 7 = ADV_MOD_DCII_C_OQPSK (Offset)
|
||||||
|
+0x10 01 87 0x0887 8 = ADV_MOD_DSS_QPSK
|
||||||
|
+0x12 01 87 0x0887 9 = ADV_MOD_DVB_BPSK
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Modulations 8 (DSS) and 9 (DVB BPSK) share the same handler at 0x0887.
|
||||||
|
|
||||||
|
### 3.2 Modulation Handler Details
|
||||||
|
|
||||||
|
Each handler reads the FEC rate from IRAM 0x4F, validates it against a maximum count, looks up a preconfigured value from an XRAM table, and writes it to XRAM 0xE0EB (the BCM4500 FEC configuration register). The handlers then set additional XRAM configuration registers.
|
||||||
|
|
||||||
|
#### Modulation 0: DVB-S QPSK (0x08B7)
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:08B7 MOV A, 0x4F ; FEC index
|
||||||
|
CODE:08BA SUBB A, #0x07 ; Max 7 FEC rates
|
||||||
|
CODE:08BC JNC 0x08D0 ; Out of range -> return error
|
||||||
|
CODE:08BE MOV A, #0xF9 ; XRAM table base = 0xE0F9
|
||||||
|
CODE:08C0 ADD A, 0x4F ; + FEC index
|
||||||
|
CODE:08C2 MOV DPL, A ; -> DPTR = 0xE0F9 + i
|
||||||
|
CODE:08C9 MOVX A, @DPTR ; Read FEC lookup value
|
||||||
|
CODE:08CA MOV DPTR, #0xE0EB ; BCM4500 FEC register
|
||||||
|
CODE:08CD MOVX @DPTR, A ; Write FEC value
|
||||||
|
|
||||||
|
; Set remaining config:
|
||||||
|
CODE:08D2 XRAM[0xE0EC] = 0x09 ; Modulation type register
|
||||||
|
CODE:08D9 XRAM[0xE0F6] = 0x00 ; Turbo mode flag = OFF
|
||||||
|
; Falls through to 0x093C:
|
||||||
|
CODE:093C XRAM[0xE0F5] = 0x10 ; Demod mode = standard
|
||||||
|
CODE:0942 IRAM[0x4E] &= 0xBF ; Clear bmDCtuned flag
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Modulations 8/9: DSS QPSK / DVB BPSK (0x0887)
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:0887 CLR 0x04 ; Clear _0_4 flag (non-turbo indicator)
|
||||||
|
CODE:0889 MOV A, 0x4F ; FEC index
|
||||||
|
CODE:088C SUBB A, #0x07 ; Max 7 FEC rates
|
||||||
|
CODE:088E JNC 0x08A4 ; Out of range -> use default
|
||||||
|
; In range:
|
||||||
|
CODE:0890 XRAM[0xE0EB] = XRAM[0xE0F9 + FEC_index] | 0x80 ; FEC with bit 7 set
|
||||||
|
; Out of range:
|
||||||
|
CODE:08A4 XRAM[0xE0EB] = 0x8C ; Default FEC value
|
||||||
|
; Common:
|
||||||
|
CODE:08AA XRAM[0xE0EC] = 0x09 ; Mod type register
|
||||||
|
CODE:08B1 XRAM[0xE0F6] = 0x00 ; Turbo flag = OFF
|
||||||
|
; Falls through to 0x093C (same as DVB-S QPSK)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Modulation 1: Turbo QPSK (0x08DF)
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:08DF MOV A, 0x4F ; FEC index
|
||||||
|
CODE:08E2 SUBB A, #0x05 ; Max 5 Turbo QPSK FEC rates
|
||||||
|
CODE:08E4 JNC 0x08F8 ; Out of range -> error
|
||||||
|
CODE:08E6 XRAM[0xE0EB] = XRAM[0xE0B7 + FEC_index] ; Turbo QPSK FEC table
|
||||||
|
; Falls through to 0x0930:
|
||||||
|
CODE:0930 XRAM[0xE0EC] = 0x09 ; Mod type
|
||||||
|
CODE:0936 XRAM[0xE0F6] = 0x01 ; Turbo flag = ON
|
||||||
|
; Falls through to 0x093C:
|
||||||
|
CODE:093C XRAM[0xE0F5] = 0x10 ; Demod mode = Turbo
|
||||||
|
CODE:0942 IRAM[0x4E] &= 0xBF ; Clear bmDCtuned
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Modulation 2: Turbo 8PSK (0x08FA)
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:08FA MOV A, 0x4F ; FEC index
|
||||||
|
CODE:08FD SUBB A, #0x05 ; Max 5 Turbo 8PSK FEC rates
|
||||||
|
CODE:08FF JNC 0x0913 ; Out of range -> error
|
||||||
|
CODE:0901 XRAM[0xE0EB] = XRAM[0xE0B1 + FEC_index] ; Turbo 8PSK FEC table
|
||||||
|
; Falls through to 0x0930 (same as Turbo QPSK)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Modulation 3: Turbo 16QAM (0x0915)
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE:0915 MOV A, 0x4F ; FEC index
|
||||||
|
CODE:0918 SUBB A, #0x01 ; Max 1 Turbo 16QAM FEC rate
|
||||||
|
CODE:091A JNC 0x092E ; Out of range -> error
|
||||||
|
CODE:091C XRAM[0xE0EB] = XRAM[0xE0BC + FEC_index] ; Turbo 16QAM FEC table
|
||||||
|
; Falls through to 0x0930 (same as Turbo QPSK/8PSK)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Modulations 4-7: Digicipher II Variants (0x0947-0x095F)
|
||||||
|
|
||||||
|
All four DCII handlers share common post-processing but set different demod modes:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Mod 4: DCII Combo (0x0947)
|
||||||
|
CODE:0947 XRAM[0xE0F5] = 0x10 ; Demod mode = DCII Combo
|
||||||
|
CODE:094D SJMP 0x0965 ; -> common DCII handler
|
||||||
|
|
||||||
|
; Mod 5: DCII I-stream (0x094F)
|
||||||
|
CODE:094F XRAM[0xE0F5] = 0x12 ; Demod mode = DCII I-stream (split)
|
||||||
|
CODE:0955 SJMP 0x0965
|
||||||
|
|
||||||
|
; Mod 6: DCII Q-stream (0x0957)
|
||||||
|
CODE:0957 XRAM[0xE0F5] = 0x16 ; Demod mode = DCII Q-stream (split)
|
||||||
|
CODE:095D SJMP 0x0965
|
||||||
|
|
||||||
|
; Mod 7: DCII Offset QPSK (0x095F)
|
||||||
|
CODE:095F XRAM[0xE0F5] = 0x11 ; Demod mode = DCII Offset QPSK
|
||||||
|
CODE:0964 ; Falls through to 0x0965
|
||||||
|
|
||||||
|
; Common DCII handler (0x0965):
|
||||||
|
CODE:0965 MOV A, 0x4F ; FEC index
|
||||||
|
CODE:0968 SUBB A, #0x09 ; Max 9 DCII FEC rates
|
||||||
|
CODE:096A JNC 0x097E ; Out of range -> error
|
||||||
|
CODE:096C XRAM[0xE0EC] = XRAM[0xE0BD + FEC_index] ; DCII FEC/mod table
|
||||||
|
CODE:0980 XRAM[0xE0EB] = 0xFC ; Fixed DCII FEC code
|
||||||
|
CODE:0987 XRAM[0xE0F6] = 0x00 ; Turbo flag = OFF
|
||||||
|
CODE:098B IRAM[0x4E] |= 0x40 ; SET bmDCtuned flag
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 BCM4500 XRAM Configuration Summary
|
||||||
|
|
||||||
|
After modulation dispatch completes, four XRAM registers hold the BCM4500 configuration:
|
||||||
|
|
||||||
|
| XRAM Address | Register Name | DVB-S | Turbo QPSK/8PSK/16QAM | DCII | DSS/BPSK |
|
||||||
|
|-------------|---------------|-------|----------------------|------|----------|
|
||||||
|
| 0xE0EB | FEC Code Rate | Lookup from 0xE0F9+i | Lookup from tables | 0xFC (fixed) | Lookup | 0x80 from 0xE0F9+i |
|
||||||
|
| 0xE0EC | Modulation Type | 0x09 | 0x09 | From 0xE0BD+i table | 0x09 |
|
||||||
|
| 0xE0F5 | Demod Mode | 0x10 | 0x10 | 0x10/0x11/0x12/0x16 | 0x10 |
|
||||||
|
| 0xE0F6 | Turbo Flag | 0x00 | 0x01 | 0x00 | 0x00 |
|
||||||
|
|
||||||
|
### 3.4 FEC Rate Lookup Tables
|
||||||
|
|
||||||
|
The FEC lookup tables are populated at boot from the init data table in CODE space, copied to XRAM during startup:
|
||||||
|
|
||||||
|
| Table Base | Modulation | Max Index | FEC Rates |
|
||||||
|
|-----------|------------|-----------|-----------|
|
||||||
|
| XRAM 0xE0F9 | DVB-S QPSK / DSS / BPSK | 7 | Standard Viterbi rates (1/2, 2/3, 3/4, 5/6, 7/8, auto, none) |
|
||||||
|
| XRAM 0xE0B7 | Turbo QPSK | 5 | Turbo QPSK code rates |
|
||||||
|
| XRAM 0xE0B1 | Turbo 8PSK | 5 | Turbo 8PSK code rates |
|
||||||
|
| XRAM 0xE0BC | Turbo 16QAM | 1 | Single 16QAM code rate |
|
||||||
|
| XRAM 0xE0BD | DCII (all variants) | 9 | DCII code rate + modulation combined |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. BCM4500 I2C Write Sequence
|
||||||
|
|
||||||
|
### 4.1 Post-Dispatch: DVB Mode and I2C Programming
|
||||||
|
|
||||||
|
After the modulation dispatch sets XRAM configuration registers, the tune function enters the common post-processing path at CODE:098E:
|
||||||
|
|
||||||
|
```
|
||||||
|
; Copy _0_4 flag to _0_5 (DVB/non-DVB indicator)
|
||||||
|
CODE:098E MOV CY, 0x04 ; Read _0_4 flag
|
||||||
|
CODE:0990 MOV 0x05, CY ; Copy to _0_5
|
||||||
|
|
||||||
|
; Configure DVB mode pin
|
||||||
|
CODE:0992 LCALL 0x21D3 ; FUN_CODE_21d3: SET_DVB_MODE
|
||||||
|
; Sets P3.6 based on _0_4
|
||||||
|
|
||||||
|
; Attempt I2C programming (up to 3 tries)
|
||||||
|
CODE:0995 MOV 0x3B, #0x03 ; Retry counter = 3
|
||||||
|
CODE:0998 MOV A, 0x3B
|
||||||
|
CODE:099A JZ 0x09A7 ; If 0 retries left, return failure
|
||||||
|
CODE:099C LCALL 0x1DD0 ; FUN_CODE_1dd0: Demod I2C write sequence
|
||||||
|
CODE:099F JC 0x09A3 ; If carry set (success), return
|
||||||
|
CODE:09A1 SETB CY ; Set carry = success
|
||||||
|
CODE:09A2 RET
|
||||||
|
CODE:09A3 DEC 0x3B ; Decrement retry counter
|
||||||
|
CODE:09A5 SJMP 0x0998 ; Try again
|
||||||
|
CODE:09A7 CLR CY ; Clear carry = failure
|
||||||
|
CODE:09A8 RET
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 FUN_CODE_1DD0: Demod Scan (3 I2C Address Attempts)
|
||||||
|
|
||||||
|
The demod scan function tries to program the BCM4500 at up to three different I2C device addresses. This supports hardware variants where the BCM4500 may appear at different I2C addresses:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Pseudocode for FUN_CODE_1dd0 (Rev.2 v2.10.4)
|
||||||
|
char demod_scan(void) {
|
||||||
|
for (DAT_INTMEM_3c = 0; DAT_INTMEM_3c < 3; DAT_INTMEM_3c++) {
|
||||||
|
// Compute I2C parameters from iteration index
|
||||||
|
// The multiplication by 0x11 and offset calculations produce:
|
||||||
|
// Iteration 0: device addr derived, register set A
|
||||||
|
// Iteration 1: device addr derived, register set B
|
||||||
|
// Iteration 2: device addr derived, register set C
|
||||||
|
result = FUN_CODE_1670(buf_addr, device_param, data_len);
|
||||||
|
if (result == success) {
|
||||||
|
return success; // Carry set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failure; // Carry clear
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 FUN_CODE_1670: BCM4500 Indirect Register Write
|
||||||
|
|
||||||
|
This is the core I2C programming function. The BCM4500 uses an indirect register access protocol through three I2C-accessible registers:
|
||||||
|
|
||||||
|
```
|
||||||
|
BCM4500 I2C Registers (accessed at device address 0x10):
|
||||||
|
0xA6 = Page/Address register (indirect address high byte)
|
||||||
|
0xA7 = Data register (indirect data)
|
||||||
|
0xA8 = Command register (indirect command: 0x03 = write)
|
||||||
|
```
|
||||||
|
|
||||||
|
**FUN_CODE_1670 decompiled (Rev.2 v2.10.4):**
|
||||||
|
|
||||||
|
```c
|
||||||
|
void bcm4500_indirect_write(byte buf_hi, byte device_param, byte data_count) {
|
||||||
|
// Step 1: Wait for BCM4500 ready (poll regs 0xA2, 0xA8, 0xA4)
|
||||||
|
FUN_CODE_1e73(); // 3-register bus wait
|
||||||
|
if (!carry) return; // BCM4500 not ready
|
||||||
|
|
||||||
|
// Step 2: Write page address (0x00) to register 0xA6
|
||||||
|
XRAM[0xE111] = 0x00; // Page = 0
|
||||||
|
IRAM[0x45] = 0xE1; // Buffer pointer high
|
||||||
|
IRAM[0x46] = 0x11; // Buffer pointer low -> XRAM 0xE111
|
||||||
|
FUN_CODE_136c(1, 0, 0xA6, 0x10); // I2C write 1 byte to device 0x10, reg 0xA6
|
||||||
|
|
||||||
|
// Step 3: Write data to register 0xA7
|
||||||
|
IRAM[0x45] = buf_hi; // Source buffer high byte
|
||||||
|
IRAM[0x46] = device_param; // Source buffer low byte
|
||||||
|
FUN_CODE_136c(data_count, 0, 0xA7, 0x10); // I2C write N bytes to reg 0xA7
|
||||||
|
|
||||||
|
// Step 4: Write command (0x03 = indirect write) to register 0xA8
|
||||||
|
XRAM[0xE111] = 0x03; // Command = write
|
||||||
|
IRAM[0x45] = 0xE1;
|
||||||
|
IRAM[0x46] = 0x11;
|
||||||
|
FUN_CODE_136c(1, 0, 0xA8, 0x10); // I2C write 1 byte to reg 0xA8
|
||||||
|
|
||||||
|
// Step 5: Wait for write completion (poll regs 0xA8, 0xA2)
|
||||||
|
FUN_CODE_1ea9();
|
||||||
|
|
||||||
|
// Step 6: Verify -- read back from 0xA7 and compare
|
||||||
|
XRAM[0xE111] = 0x00;
|
||||||
|
FUN_CODE_136c(1, 0, 0xA6, 0x10); // Re-select page 0
|
||||||
|
FUN_CODE_20cb(); // Read reg 0xA7
|
||||||
|
// Compare read-back with expected value
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 FUN_CODE_136C: I2C Multi-Byte Write Primitive
|
||||||
|
|
||||||
|
This is the lowest-level I2C write function that talks to the FX2's I2C controller:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Pseudocode for FUN_CODE_136c (Rev.2 v2.10.4)
|
||||||
|
void i2c_write(byte byte_count, byte flags, byte register_addr, byte device_addr) {
|
||||||
|
// Setup I2C controller at XRAM 0xE678 (FX2 I2C register)
|
||||||
|
FUN_CODE_2000(); // Wait for I2C bus ready
|
||||||
|
FUN_CODE_2206(device_addr << 1); // Send I2C start + device address (write)
|
||||||
|
FUN_CODE_2224(register_addr); // Send register address
|
||||||
|
|
||||||
|
// Transfer data bytes from buffer at IRAM[0x45:0x46]
|
||||||
|
for (i = 0; i < byte_count; i++) {
|
||||||
|
byte data = XRAM[IRAM[0x45]:IRAM[0x46] + i];
|
||||||
|
FUN_CODE_2224(data); // Send each data byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send I2C stop condition
|
||||||
|
FUN_CODE_1aa3(); // I2C stop + cleanup
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Complete I2C Register Write Sequence for a Tune Operation
|
||||||
|
|
||||||
|
Combining all the above, a complete tune operation produces the following I2C bus transactions:
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Phase 1: Pre-Tune LNB/Tone Configuration ===
|
||||||
|
(These happen before TUNE_8PSK, via separate vendor commands)
|
||||||
|
|
||||||
|
1. SET_LNB_VOLTAGE (0x8B): GPIO P0.4 high/low (no I2C)
|
||||||
|
2. SET_22KHZ_TONE (0x8C): GPIO P0.3 high/low (no I2C)
|
||||||
|
|
||||||
|
=== Phase 2: Tune Parameter Setup ===
|
||||||
|
(EP0BUF parsing -- no I2C, just XRAM writes)
|
||||||
|
|
||||||
|
3. EP0BUF[8] -> IRAM[0x4D] (modulation)
|
||||||
|
4. EP0BUF[9] -> IRAM[0x4F] (FEC rate)
|
||||||
|
5. EP0BUF[4-7] -> XRAM[0xE0DB-0xE0DE] (frequency, byte-reversed)
|
||||||
|
6. EP0BUF[0-3] -> XRAM[0xE0CB-0xE0CE] (symbol rate, byte-reversed)
|
||||||
|
|
||||||
|
=== Phase 3: Modulation Dispatch ===
|
||||||
|
(XRAM configuration writes -- no I2C)
|
||||||
|
|
||||||
|
7. XRAM[0xE0EB] = FEC lookup value (from modulation-specific table)
|
||||||
|
8. XRAM[0xE0EC] = modulation type (usually 0x09)
|
||||||
|
9. XRAM[0xE0F5] = demod mode (0x10/0x11/0x12/0x16)
|
||||||
|
10. XRAM[0xE0F6] = turbo flag (0x00 or 0x01)
|
||||||
|
|
||||||
|
=== Phase 4: DVB Mode GPIO ===
|
||||||
|
|
||||||
|
11. P3.6 set/clear based on _0_4 flag (DVB vs non-DVB mode)
|
||||||
|
|
||||||
|
=== Phase 5: BCM4500 I2C Programming (x3 retry, x3 addresses) ===
|
||||||
|
|
||||||
|
For each attempt (up to 3 retries, each trying up to 3 I2C addresses):
|
||||||
|
|
||||||
|
--- Wait for BCM4500 ready ---
|
||||||
|
12. I2C READ device 0x10, reg 0xA2 (poll status register)
|
||||||
|
13. I2C READ device 0x10, reg 0xA8 (poll command register)
|
||||||
|
14. I2C READ device 0x10, reg 0xA4 (poll lock/ready register)
|
||||||
|
|
||||||
|
--- Write page address ---
|
||||||
|
15. I2C WRITE device 0x10, reg 0xA6 <- 0x00 (select page 0)
|
||||||
|
|
||||||
|
--- Write configuration data ---
|
||||||
|
16. I2C WRITE device 0x10, reg 0xA7 <- [N config bytes from XRAM buffer]
|
||||||
|
Data includes: frequency, symbol rate, FEC, modulation, demod mode,
|
||||||
|
turbo flag -- assembled from XRAM 0xE0EB/EC/F5/F6 and 0xE0CB-CE/DB-DE
|
||||||
|
|
||||||
|
--- Issue indirect write command ---
|
||||||
|
17. I2C WRITE device 0x10, reg 0xA8 <- 0x03 (execute indirect write)
|
||||||
|
|
||||||
|
--- Wait for completion ---
|
||||||
|
18. I2C READ device 0x10, reg 0xA8 (poll for command done)
|
||||||
|
19. I2C READ device 0x10, reg 0xA2 (verify status)
|
||||||
|
|
||||||
|
--- Verify write ---
|
||||||
|
20. I2C WRITE device 0x10, reg 0xA6 <- 0x00 (re-select page)
|
||||||
|
21. I2C READ device 0x10, reg 0xA7 (read back data)
|
||||||
|
22. Compare read-back with expected value
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.6 I2C Device Address Note
|
||||||
|
|
||||||
|
The BCM4500 demodulator is accessed at I2C device address **0x10** (7-bit address, shifted to 0x20 for write, 0x21 for read on the wire). In some firmware versions, the device also responds at alternate addresses:
|
||||||
|
- 0x3F (used by v2.13 for status polling in INT0)
|
||||||
|
- 0x7F (used by v2.13 for status polling in INT0)
|
||||||
|
|
||||||
|
The use of 0x3F and 0x7F in v2.13's INT0 handler (demod availability probing) suggests these may be alternate I2C addresses on different hardware revisions, or they may access different internal register banks of the BCM4500.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Signal Acquisition
|
||||||
|
|
||||||
|
### 5.1 GET_SIGNAL_LOCK (Vendor Command 0x90)
|
||||||
|
|
||||||
|
**Jump table targets:**
|
||||||
|
- v2.06: 0x020B
|
||||||
|
- v2.13: 0x022D
|
||||||
|
- Rev.2: 0x0217
|
||||||
|
|
||||||
|
The signal lock handler reads BCM4500 register 0xA4 and returns the result to the host via EP0BUF.
|
||||||
|
|
||||||
|
**Rev.2 implementation chain:**
|
||||||
|
|
||||||
|
```
|
||||||
|
; Vendor command handler at 0x0217:
|
||||||
|
CODE:0217 MOV A, 0x4E ; Config status byte
|
||||||
|
CODE:0219 JNB ACC.0, ... ; Check device started
|
||||||
|
CODE:021C LCALL 0x2236 ; FUN_CODE_2236: Read lock register
|
||||||
|
|
||||||
|
; FUN_CODE_2236:
|
||||||
|
CODE:2236 MOV R7, #0xA4 ; Register = 0xA4
|
||||||
|
CODE:2238 LCALL 0x20CB ; FUN_CODE_20cb: I2C read
|
||||||
|
|
||||||
|
; FUN_CODE_20cb: I2C single register read
|
||||||
|
; Sets up buffer at XRAM 0xE114
|
||||||
|
; Calls FUN_CODE_0f00(device=0x0F, offset=0, reg=param, addr=0x10)
|
||||||
|
; Returns byte from XRAM[0xE114]
|
||||||
|
```
|
||||||
|
|
||||||
|
**I2C transaction:**
|
||||||
|
```
|
||||||
|
I2C READ device 0x10, register 0xA4, 1 byte -> XRAM[0xE114]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Register 0xA4 bit fields:**
|
||||||
|
```
|
||||||
|
Bit 5 (0x20): Signal locked (demod has achieved lock)
|
||||||
|
```
|
||||||
|
|
||||||
|
The firmware returns the raw register byte to EP0BUF. The host driver (both Linux and Windows) interprets any non-zero value as "locked":
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Linux kernel driver (gp8psk-fe.c):
|
||||||
|
gp8psk_usb_in_op(d, GET_SIGNAL_LOCK, 0, 0, &lock, 1);
|
||||||
|
if (lock)
|
||||||
|
*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI |
|
||||||
|
FE_HAS_SIGNAL | FE_HAS_CARRIER;
|
||||||
|
|
||||||
|
// Windows BDA driver (SkyWalker1Control.cpp):
|
||||||
|
ControlUsbDevice(pKSDeviceObject, GET_SIGNAL_LOCK, 0, 0, &ucSignalStatus, 1, true);
|
||||||
|
if (ucSignalStatus)
|
||||||
|
*pbSignalLockStatus = TRUE;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 GET_SIGNAL_STRENGTH (Vendor Command 0x87)
|
||||||
|
|
||||||
|
**Jump table targets:**
|
||||||
|
- v2.06: 0x0140
|
||||||
|
- v2.13: 0x0162
|
||||||
|
- Rev.2: 0x014C
|
||||||
|
|
||||||
|
This command returns 6 bytes of signal quality data. The first two bytes contain the SNR value.
|
||||||
|
|
||||||
|
**Rev.2 implementation (FUN_CODE_15eb at 0x15EB):**
|
||||||
|
|
||||||
|
The signal strength reader performs a more complex sequence than simple lock detection:
|
||||||
|
|
||||||
|
1. Writes configuration to XRAM buffer via FUN_CODE_067e (multi-mode data access)
|
||||||
|
2. Performs BCM4500 indirect register write via FUN_CODE_1670 (same protocol as tuning)
|
||||||
|
3. Reads back BCM4500 register 0xA7 via FUN_CODE_0f00
|
||||||
|
4. Compares write value with read-back for validity check
|
||||||
|
5. If valid, copies 6 bytes of signal data to EP0BUF
|
||||||
|
|
||||||
|
**I2C transactions during signal strength read:**
|
||||||
|
```
|
||||||
|
1. I2C WRITE device 0x10, reg 0xA6 <- 0x00 (page select)
|
||||||
|
2. I2C WRITE device 0x10, reg 0xA7 <- [config] (request signal data)
|
||||||
|
3. I2C WRITE device 0x10, reg 0xA8 <- 0x03 (execute)
|
||||||
|
4. I2C READ device 0x10, reg 0xA7, 1 byte (verify/read)
|
||||||
|
5. Copy 6-byte result buffer to EP0BUF
|
||||||
|
```
|
||||||
|
|
||||||
|
**EP0BUF response format (6 bytes):**
|
||||||
|
```
|
||||||
|
Byte 0: SNR low byte (LSB)
|
||||||
|
Byte 1: SNR high byte (MSB)
|
||||||
|
Bytes 2-5: Reserved/diagnostic (BCM4500 internal registers)
|
||||||
|
```
|
||||||
|
|
||||||
|
**SNR scaling (from Windows BDA driver):**
|
||||||
|
```c
|
||||||
|
ulSignalStrength = (int)(ucBuffer[1]) << 8 | ucBuffer[0];
|
||||||
|
// SNR is in dBu * 256 units
|
||||||
|
// SNR * 17 maps to 0-65535 range (100% at SNR >= 0x0F00)
|
||||||
|
if (ulSignalStrength <= 0x0F00)
|
||||||
|
*pulSigStrength = (ulSignalStrength << 4) + ulSignalStrength; // * 17
|
||||||
|
else
|
||||||
|
*pulSigStrength = 0xFFFF;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 Differences Between Firmware Versions
|
||||||
|
|
||||||
|
**v2.06 GET_SIGNAL_STRENGTH (0x0140):**
|
||||||
|
- Checks IRAM[0x6D] bit 0 (demod active)
|
||||||
|
- Reads three I2C status registers (0xA2, 0xA8, 0xA4) to compute quality
|
||||||
|
- Loops up to 6 iterations polling for demod readiness
|
||||||
|
- Uses FUN_CODE_0c97 for I2C reads
|
||||||
|
|
||||||
|
**v2.13 GET_SIGNAL_STRENGTH (0x0162):**
|
||||||
|
- Checks IRAM[0x4F] bit 0 (demod active)
|
||||||
|
- Uses different function call chain (FUN_CODE_1278 vs v2.06's FUN_CODE_0c97)
|
||||||
|
- Same overall logic with relocated internal variables
|
||||||
|
- Simplified polling (uses consolidated BCM4500 status register)
|
||||||
|
|
||||||
|
**Rev.2 GET_SIGNAL_STRENGTH (0x014C -> FUN_CODE_15eb):**
|
||||||
|
- Checks IRAM[0x4E] bit 0
|
||||||
|
- Uses FUN_CODE_067e for multi-mode data access
|
||||||
|
- Validates read-back against written configuration
|
||||||
|
- Most granular implementation with explicit verify step
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. LNB and Tone Control
|
||||||
|
|
||||||
|
### 6.1 SET_LNB_VOLTAGE (Vendor Command 0x8B)
|
||||||
|
|
||||||
|
**Jump table targets:**
|
||||||
|
- v2.06: 0x01CB
|
||||||
|
- v2.13: 0x01ED
|
||||||
|
- Rev.2: 0x01D7
|
||||||
|
|
||||||
|
LNB voltage selection is pure GPIO -- no I2C transactions. The FX2 pin P0.4 directly controls a voltage regulator on the PCB.
|
||||||
|
|
||||||
|
**Rev.2 FUN_CODE_21b1 decompiled:**
|
||||||
|
```c
|
||||||
|
void set_lnb_voltage(void) {
|
||||||
|
if (_0_4 != 0) {
|
||||||
|
// wValue = 1: Set 18V (horizontal / circular-left polarization)
|
||||||
|
P0 |= 0x10; // P0.4 = HIGH -> 18V
|
||||||
|
DAT_INTMEM_4e |= 0x20; // Set bmSEL18V in config status
|
||||||
|
} else {
|
||||||
|
// wValue = 0: Set 13V (vertical / circular-right polarization)
|
||||||
|
P0 &= 0xEF; // P0.4 = LOW -> 13V
|
||||||
|
DAT_INTMEM_4e &= 0xDF; // Clear bmSEL18V in config status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pin mapping across versions:**
|
||||||
|
|
||||||
|
| Version | 18V Pin | Config Status Bit |
|
||||||
|
|---------|---------|------------------|
|
||||||
|
| v2.06 | P0.4 | IRAM[0x6D] bit 5 |
|
||||||
|
| v2.13 | P0.4 | IRAM[0x4F] bit 5 |
|
||||||
|
| Rev.2 | P0.4 | IRAM[0x4E] bit 5 |
|
||||||
|
|
||||||
|
The `wValue` parameter from the USB SETUP packet is passed via the `_0_4` bit flag (IRAM bit-addressable area). The Windows driver sends:
|
||||||
|
```c
|
||||||
|
ControlUsbDevice(device, SET_LNB_VOLTAGE, (ucVoltage == SEC_VOLTAGE_18), 0, NULL, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Polarization mapping:**
|
||||||
|
- Horizontal / Circular-Left: 18V (wValue=1)
|
||||||
|
- Vertical / Circular-Right: 13V (wValue=0)
|
||||||
|
|
||||||
|
### 6.2 SET_22KHZ_TONE (Vendor Command 0x8C)
|
||||||
|
|
||||||
|
**Jump table targets:**
|
||||||
|
- v2.06: 0x01DD
|
||||||
|
- v2.13: 0x01FF
|
||||||
|
- Rev.2: 0x01E9
|
||||||
|
|
||||||
|
Like LNB voltage, the 22 kHz tone is pure GPIO. P0.3 enables/disables an external 22 kHz oscillator on the PCB.
|
||||||
|
|
||||||
|
**Rev.2 FUN_CODE_21c2 decompiled:**
|
||||||
|
```c
|
||||||
|
void set_22khz_tone(void) {
|
||||||
|
if (_0_4 != 0) {
|
||||||
|
// wValue = 1: Tone ON (high band)
|
||||||
|
P0 |= 0x08; // P0.3 = HIGH -> 22 kHz oscillator enabled
|
||||||
|
DAT_INTMEM_4e |= 0x10; // Set bm22kHz in config status
|
||||||
|
} else {
|
||||||
|
// wValue = 0: Tone OFF (low band)
|
||||||
|
P0 &= 0xF7; // P0.3 = LOW -> 22 kHz oscillator disabled
|
||||||
|
DAT_INTMEM_4e &= 0xEF; // Clear bm22kHz in config status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pin mapping across versions:**
|
||||||
|
|
||||||
|
| Version | Tone Pin | Config Status Bit |
|
||||||
|
|---------|----------|------------------|
|
||||||
|
| v2.06 | P0.3 | IRAM[0x6D] bit 4 |
|
||||||
|
| v2.13 | P0.3 | IRAM[0x4F] bit 4 |
|
||||||
|
| Rev.2 | P0.3 | IRAM[0x4E] bit 4 |
|
||||||
|
|
||||||
|
The 22 kHz tone is used for satellite band selection:
|
||||||
|
- **Tone ON**: Select high-band LNB oscillator (universal LNB: 10.6 GHz LO)
|
||||||
|
- **Tone OFF**: Select low-band LNB oscillator (universal LNB: 9.75 GHz LO)
|
||||||
|
|
||||||
|
**Windows driver tone logic:**
|
||||||
|
```c
|
||||||
|
// SEC_TONE_ON = 0, SEC_TONE_OFF = 1
|
||||||
|
ControlUsbDevice(device, SET_22KHZ_TONE, (ucTone == 0), 0, NULL, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the inverted logic: `SEC_TONE_ON = 0` results in `wValue = 1` (tone active).
|
||||||
|
|
||||||
|
### 6.3 ConfigureTuner: Full Tuning Sequence (Windows BDA Driver)
|
||||||
|
|
||||||
|
The Windows driver's `ConfigureTuner()` function shows the complete host-side tuning sequence:
|
||||||
|
|
||||||
|
```c
|
||||||
|
NTSTATUS ConfigureTuner(PKSDEVICE device, PBDATUNER_DEVICE_PARAMETER config) {
|
||||||
|
// Step 1: Set LNB voltage based on polarization
|
||||||
|
if (config->Polarity == BDA_POLARISATION_LINEAR_H ||
|
||||||
|
config->Polarity == BDA_POLARISATION_CIRCULAR_L) {
|
||||||
|
SetLnbVoltage(device, SEC_VOLTAGE_18); // 0x8B, wValue=1
|
||||||
|
} else {
|
||||||
|
SetLnbVoltage(device, SEC_VOLTAGE_13); // 0x8B, wValue=0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Disable tone during tune
|
||||||
|
SetTunerTone(device, SEC_TONE_OFF); // 0x8C, wValue=0
|
||||||
|
|
||||||
|
// Step 3: Send tune command
|
||||||
|
TuneDevice(device, config); // 0x86, 10 bytes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. GPIO Pin Summary
|
||||||
|
|
||||||
|
All LNB, tone, and DVB mode control is performed through direct GPIO manipulation with no I2C involvement:
|
||||||
|
|
||||||
|
```
|
||||||
|
FX2 Port 0 Pin Assignments (Rev.2 v2.10.4):
|
||||||
|
P0.0 -- (unused in Rev.2; DiSEqC data in v2.13)
|
||||||
|
P0.1 -- (unused)
|
||||||
|
P0.2 -- (set during init, purpose TBD)
|
||||||
|
P0.3 -- 22 kHz tone oscillator enable (all versions)
|
||||||
|
P0.4 -- LNB 13V/18V voltage select (all versions)
|
||||||
|
Also DiSEqC data pin in Rev.2
|
||||||
|
P0.5 -- (unused)
|
||||||
|
P0.6 -- GPIO control (FUN_CODE_1fcf)
|
||||||
|
P0.7 -- DiSEqC data pin (v2.06 only)
|
||||||
|
|
||||||
|
FX2 Port 3 Pin Assignments (Rev.2 v2.10.4):
|
||||||
|
P3.4 -- GPIO control (FUN_CODE_1fcf)
|
||||||
|
P3.6 -- DVB mode select (SET_DVB_MODE, FUN_CODE_21d3)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Cross-Version Comparison
|
||||||
|
|
||||||
|
### 8.1 Tune Function Correspondence
|
||||||
|
|
||||||
|
| Component | v2.06 | v2.13 | Rev.2 v2.10.4 |
|
||||||
|
|-----------|-------|-------|---------------|
|
||||||
|
| Vendor dispatch | 0x0056 | 0x0056 | 0x0056 |
|
||||||
|
| TUNE_8PSK handler | 0x012E | 0x012E | 0x0118 |
|
||||||
|
| EP0 flush | 0x23E0 | 0x231E | 0x2167 |
|
||||||
|
| Main tune function | 0x0800 | 0x09DF | 0x0800 |
|
||||||
|
| Config status byte | IRAM 0x6D | IRAM 0x4F | IRAM 0x4E |
|
||||||
|
| Modulation storage | IRAM 0x4D* | IRAM 0x4D | IRAM 0x4D |
|
||||||
|
| FEC storage | IRAM 0x4F* | IRAM 0x4F | IRAM 0x4F |
|
||||||
|
| Mod dispatch table | 0x0873* | embedded in 0x09DF | 0x0873 |
|
||||||
|
| BCM4500 indirect write | similar | FUN_CODE_15b8 chain | FUN_CODE_1670 |
|
||||||
|
| I2C write primitive | FUN_CODE_1556 | FUN_CODE_15b8 | FUN_CODE_136c |
|
||||||
|
| Signal lock read | 0x020B | 0x022D | 0x0217 -> FUN_CODE_2236 |
|
||||||
|
| Signal strength | 0x0140 | 0x0162 | 0x014C -> FUN_CODE_15eb |
|
||||||
|
| LNB voltage | 0x01CB | 0x01ED | 0x01D7 -> FUN_CODE_21b1 |
|
||||||
|
| 22 kHz tone | 0x01DD | 0x01FF | 0x01E9 -> FUN_CODE_21c2 |
|
||||||
|
|
||||||
|
*v2.06 shares the same IRAM layout as Rev.2 for modulation/FEC but uses different XRAM offsets.
|
||||||
|
|
||||||
|
### 8.2 Key Architectural Differences
|
||||||
|
|
||||||
|
**v2.06:**
|
||||||
|
- Simplest implementation, single code path
|
||||||
|
- No demod verification or retry logic during tune
|
||||||
|
- BCM4500 status polling reads 3 registers (0xA2, 0xA8, 0xA4)
|
||||||
|
- Signal strength handler loops up to 6 times
|
||||||
|
|
||||||
|
**Rev.2 v2.10.4:**
|
||||||
|
- 3 I2C address attempts per tune (FUN_CODE_1dd0)
|
||||||
|
- 3 outer retries for the whole demod scan
|
||||||
|
- BCM4500 indirect write with read-back verification (FUN_CODE_1670)
|
||||||
|
- Most granular function decomposition
|
||||||
|
- Smallest binary despite highest function count
|
||||||
|
|
||||||
|
**v2.13:**
|
||||||
|
- Adds host-controlled DELAY_COMMAND (0x9C) for tuning timing
|
||||||
|
- Adds INIT_DEMOD (0x9A) for host-triggered re-initialization
|
||||||
|
- Adds GET_DEMOD_STATUS (0x99) for diagnostic register reads
|
||||||
|
- INT0 repurposed for demod availability polling
|
||||||
|
- Most robust error recovery (20-attempt retry loops for init)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Data Flow Diagram
|
||||||
|
|
||||||
|
```
|
||||||
|
Host Application (DVB app / szap / w_scan)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Linux dvb-usb-gp8psk / Windows BDA Driver
|
||||||
|
|
|
||||||
|
| USB Control Transfer (EP0)
|
||||||
|
| bmRequestType = 0x40 (Vendor OUT)
|
||||||
|
| bRequest = 0x86 (TUNE_8PSK)
|
||||||
|
| wValue = 0, wIndex = 0, wLength = 10
|
||||||
|
| Data: [SR0 SR1 SR2 SR3 F0 F1 F2 F3 MOD FEC]
|
||||||
|
|
|
||||||
|
v
|
||||||
|
FX2 Microcontroller (Cypress CY7C68013A)
|
||||||
|
|
|
||||||
|
| 1. EP0BUF (XRAM 0xE740-0xE749) receives 10 bytes
|
||||||
|
| 2. Parse: mod->IRAM[0x4D], fec->IRAM[0x4F]
|
||||||
|
| 3. Byte-reverse freq->XRAM[0xE0DB-DE], sr->XRAM[0xE0CB-CE]
|
||||||
|
| 4. Dispatch on mod type (jump table at CODE:0873)
|
||||||
|
| 5. Set XRAM[0xE0EB/EC/F5/F6] per modulation
|
||||||
|
| 6. GPIO: P3.6 for DVB mode
|
||||||
|
|
|
||||||
|
| I2C Bus (FX2 I2C controller at XRAM 0xE678)
|
||||||
|
| Device address: 0x10 (BCM4500)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
BCM4500 Demodulator (Broadcom DVB-S/8PSK)
|
||||||
|
|
|
||||||
|
| Indirect register protocol:
|
||||||
|
| reg 0xA6 <- page (0x00)
|
||||||
|
| reg 0xA7 <- config data (freq, SR, FEC, mod params)
|
||||||
|
| reg 0xA8 <- command (0x03 = write)
|
||||||
|
|
|
||||||
|
| After programming:
|
||||||
|
| reg 0xA4 bit 5 = signal lock status
|
||||||
|
| reg 0xA7 = SNR / signal quality readback
|
||||||
|
|
|
||||||
|
v
|
||||||
|
RF Front End
|
||||||
|
|
|
||||||
|
| LNB Control (GPIO, no I2C):
|
||||||
|
| P0.4 = voltage (13V/18V)
|
||||||
|
| P0.3 = 22 kHz tone (band select)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Satellite LNB -> Dish -> Satellite
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- Ghidra firmware disassembly: v2.06 (port 8193), v2.13 FW1 (port 8194), Rev.2 v2.10.4 (port 8197)
|
||||||
|
- Windows BDA driver source: `SkyWalker1_Final_Release/Source/SkyWalker1Control.cpp`
|
||||||
|
- Windows BDA driver headers: `SkyWalker1_Final_Release/Include/SkyWalker1Control.h`, `SkyWalker1CommonDef.h`
|
||||||
|
- Linux kernel driver: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`
|
||||||
|
- Companion documents: `gp8psk-driver-analysis.md`, `firmware-analysis-v206-vs-v213.md`, `rev2-deep-analysis.md`
|
||||||
571
vendor-commands-unknown.md
Normal file
571
vendor-commands-unknown.md
Normal file
@ -0,0 +1,571 @@
|
|||||||
|
# Genpix SkyWalker-1 Vendor Commands: Full Decode of "Unknown" Commands
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- Firmware disassembly via Ghidra MCP: v2.06 (port 8193), v2.13 FW1 (port 8194), Rev.2 v2.10.4 (port 8197)
|
||||||
|
- Linux kernel `gp8psk-fe.h` (kernel 4.9.227 and 6.18.6) -- provides definitive names for 0x8F, 0x92, 0x93, 0x94, 0x95
|
||||||
|
- Linux kernel `gp8psk.c` and `gp8psk-fe.c` -- shows driver usage
|
||||||
|
- Windows driver `SkyWalker1Control.h` -- confirms 0x8F, 0x93, 0x94
|
||||||
|
|
||||||
|
## Jump Table Corrections
|
||||||
|
|
||||||
|
The original addresses for Rev.2 were shifted by one command index. The corrected jump table targets (decoded from AJMP opcodes at CODE:0x0076) are:
|
||||||
|
|
||||||
|
| Cmd | v2.06 (8193) | v2.13 (8194) | Rev.2 (8197) |
|
||||||
|
|------|-------------|-------------|-------------|
|
||||||
|
| 0x8F | 0x01FC | 0x021E | 0x0208 |
|
||||||
|
| 0x90 | 0x020B | 0x022D | 0x0217 |
|
||||||
|
| 0x91 | 0x022C | 0x024E | 0x0238 |
|
||||||
|
| 0x92 | 0x024A | 0x026C | 0x0256 |
|
||||||
|
| 0x93 | 0x026F | 0x0293 | 0x027D |
|
||||||
|
| 0x94 | 0x01B9 | 0x01DB | 0x01C5 |
|
||||||
|
| 0x95 | 0x02DF | 0x0303 | 0x02ED |
|
||||||
|
| 0x96 | 0x02B4 | 0x02D8 | 0x02C2 |
|
||||||
|
| 0x97 | 0x02C1 | 0x02E5 | 0x02CF |
|
||||||
|
| 0x98 | 0x02CB | 0x02EF | 0x02D9 |
|
||||||
|
|
||||||
|
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`
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Direction | OUT (host-to-device) |
|
||||||
|
| wValue | Switch command byte (7 bits, LSB-first) |
|
||||||
|
| wIndex | 0x0000 |
|
||||||
|
| wLength | 0 (no data phase) |
|
||||||
|
| Purpose | Send legacy Dish Network switch command via GPIO bit-bang |
|
||||||
|
|
||||||
|
### Firmware Behavior
|
||||||
|
|
||||||
|
All three versions implement the same algorithm:
|
||||||
|
|
||||||
|
1. Read `wValueL` from SETUPDAT[2] (0xE6BA)
|
||||||
|
2. Store value, then call a GPIO bit-bang subroutine:
|
||||||
|
- Assert P0.4 high (start pulse)
|
||||||
|
- Delay ~32 cycles
|
||||||
|
- Deassert P0.4
|
||||||
|
- Delay ~8 cycles
|
||||||
|
- Loop 7 times, shifting out each bit LSB-first via P0.4
|
||||||
|
- Between bits: delay ~8 cycles
|
||||||
|
3. ACK with EP0BCL = 0 (no data returned)
|
||||||
|
|
||||||
|
### Subroutine Addresses
|
||||||
|
|
||||||
|
| Version | Handler | Bit-bang Routine | Delay Routine |
|
||||||
|
|---------|---------|-----------------|---------------|
|
||||||
|
| v2.06 | 0x01FC | 0x1F04 | 0x1DFB |
|
||||||
|
| v2.13 | 0x021E | 0x1ECD | 0x14B9 |
|
||||||
|
| Rev.2 | 0x0208 | 0x1CE6 | 0x1BDA |
|
||||||
|
|
||||||
|
### Linux Driver Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// gp8psk-fe.c: gp8psk_fe_send_legacy_dish_cmd()
|
||||||
|
u8 cmd = sw_cmd & 0x7f;
|
||||||
|
st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0);
|
||||||
|
// Then sets LNB voltage based on bit 7 of sw_cmd
|
||||||
|
st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), 0, NULL, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
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]`
|
||||||
|
Build date: `(2000 + fw_vers[5]) / fw_vers[4] / fw_vers[3]`
|
||||||
|
|
||||||
|
### Values Per Firmware
|
||||||
|
|
||||||
|
| Version | Raw Bytes | Version | Build Date | FW_VERS macro |
|
||||||
|
|---------|-----------|---------|-----------|---------------|
|
||||||
|
| v2.06 | `04 06 02 0D 07 07` | 2.06.04 | 2007-07-13 | 0x020604 (= GP8PSK_FW_REV1) |
|
||||||
|
| v2.13 | `01 0D 02 0C 03 0A` | 2.13.01 | 2010-03-12 | 0x020D01 |
|
||||||
|
| Rev.2 | `04 0A 02 0C 03 0A` | 2.10.04 | 2010-03-12 | 0x020A04 (>= GP8PSK_FW_REV2) |
|
||||||
|
|
||||||
|
### Linux Driver Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// gp8psk.c: gp8psk_get_fw_version()
|
||||||
|
gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6);
|
||||||
|
|
||||||
|
// gp8psk.c: gp8psk_info() -- prints on boot:
|
||||||
|
// "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.
|
||||||
|
|
||||||
|
### Subroutine Addresses
|
||||||
|
|
||||||
|
| Version | Handler | EEPROM Init | Byte Extract |
|
||||||
|
|---------|---------|------------|-------------|
|
||||||
|
| v2.06 | 0x026F | 0x1DA8 | 0x077C |
|
||||||
|
| v2.13 | 0x0293 | 0x1D4B | 0x078A |
|
||||||
|
| Rev.2 | 0x027D | 0x1A50 | 0x0798 |
|
||||||
|
|
||||||
|
### Cross-Version Differences
|
||||||
|
|
||||||
|
None in behavior. The EEPROM device address (0x51) and bit-offset scheme are identical. Only subroutine addresses differ.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command 0x94: USE_EXTRA_VOLT
|
||||||
|
|
||||||
|
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Direction | OUT (host-to-device) in v2.06/v2.13; OUT with 1-byte ACK in Rev.2 |
|
||||||
|
| wValue | 0 = normal voltage (13V/18V), non-zero = high voltage (14V/19V) |
|
||||||
|
| wIndex | 0x0000 |
|
||||||
|
| wLength | 0 (v2.06/v2.13); 1 byte returned in Rev.2 |
|
||||||
|
| Purpose | Enable/disable +1V LNB voltage boost for long cable runs |
|
||||||
|
|
||||||
|
### Firmware Behavior
|
||||||
|
|
||||||
|
1. Read `wValueL` from SETUPDAT[2]
|
||||||
|
2. Compute carry flag: `CY = (wValueL >= 1)` via `ADD A,#0xFF`
|
||||||
|
3. Store carry to a bit-addressable IRAM flag
|
||||||
|
4. Call LNB voltage apply 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.
|
||||||
|
|
||||||
|
### Flag Bit Addresses
|
||||||
|
|
||||||
|
| Version | Bit Address | IRAM Byte.Bit |
|
||||||
|
|---------|------------|---------------|
|
||||||
|
| v2.06 | bit 0x07 | byte 0x20, bit 7 |
|
||||||
|
| v2.13 | bit 0x00 | byte 0x20, bit 0 |
|
||||||
|
| Rev.2 | bit 0x04 | byte 0x20, bit 4 |
|
||||||
|
|
||||||
|
### Subroutine Addresses
|
||||||
|
|
||||||
|
| Version | Handler | Voltage Apply |
|
||||||
|
|---------|---------|--------------|
|
||||||
|
| v2.06 | 0x01B9 | 0x2481 |
|
||||||
|
| v2.13 | 0x01DB | 0x23BF |
|
||||||
|
| Rev.2 | 0x01C5 | 0x21F5 |
|
||||||
|
|
||||||
|
### Linux Driver Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// gp8psk-fe.c: gp8psk_fe_enable_high_lnb_voltage()
|
||||||
|
// Mapped to .enable_high_lnb_voltage frontend ops callback
|
||||||
|
st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cross-Version Differences
|
||||||
|
|
||||||
|
- 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`)
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Direction | IN (device-to-host) |
|
||||||
|
| wValue | 0x0000 |
|
||||||
|
| wIndex | 0x0000 |
|
||||||
|
| wLength | 1 byte (v2.13/Rev.2); 2 bytes (v2.06) |
|
||||||
|
| Returns | EEPROM-stored hardware/FPGA revision identifier |
|
||||||
|
| Purpose | Read hardware platform identifier from onboard EEPROM |
|
||||||
|
|
||||||
|
### Firmware Behavior
|
||||||
|
|
||||||
|
All versions read from the I2C EEPROM at device address 0x51, but the EEPROM offset and return size differ:
|
||||||
|
|
||||||
|
**v2.06:**
|
||||||
|
1. Call EEPROM read at offset 0x31, read 2 bytes
|
||||||
|
2. Combine into 16-bit value (R7:R6)
|
||||||
|
3. Return 2 bytes in EP0BUF[0..1]
|
||||||
|
|
||||||
|
**v2.13 and Rev.2:**
|
||||||
|
1. Call EEPROM read at offset 0x00, read 2 bytes
|
||||||
|
2. Compute a combined value
|
||||||
|
3. Return 1 byte in EP0BUF[0]
|
||||||
|
|
||||||
|
### Subroutine Addresses
|
||||||
|
|
||||||
|
| Version | Handler | EEPROM Read |
|
||||||
|
|---------|---------|------------|
|
||||||
|
| v2.06 | 0x02DF | 0x11DD |
|
||||||
|
| v2.13 | 0x0303 | 0x21AE |
|
||||||
|
| Rev.2 | 0x02ED | 0x0FDD |
|
||||||
|
|
||||||
|
### Linux Driver Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
// gp8psk.c: gp8psk_get_fpga_version()
|
||||||
|
gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, &fpga_vers, 1);
|
||||||
|
|
||||||
|
// gp8psk.c: gp8psk_info() -- prints on boot:
|
||||||
|
// "FPGA Version = %i"
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command 0x96: SET_LNB_GPIO_MODE (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 | 0 = default mode, non-zero = extra-volt pin mapping |
|
||||||
|
| wIndex | 0x0000 |
|
||||||
|
| wLength | 0 (no data phase) |
|
||||||
|
| Purpose | Configure LNB power supply GPIO output enable pins |
|
||||||
|
|
||||||
|
### Firmware Behavior
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| Cmd | Name | Dir | wValue | wLength | Purpose |
|
||||||
|
|------|------|-----|--------|---------|---------|
|
||||||
|
| 0x8F | **SET_DN_SWITCH** | OUT | 7-bit switch cmd | 0 | Legacy Dish Network switch via GPIO bit-bang on P0.4 |
|
||||||
|
| 0x91 | **I2C_ADDR_ADJUST** | IN | 0=dec, else inc | 1 | Inc/dec internal IRAM counter (debug) |
|
||||||
|
| 0x92 | **GET_FW_VERS** | IN | 0 | 6 | Read firmware version + build date (hardcoded) |
|
||||||
|
| 0x93 | **GET_SERIAL_NUMBER** | IN | 0 | 4 | Read 4-byte serial from I2C EEPROM (0x51) |
|
||||||
|
| 0x94 | **USE_EXTRA_VOLT** | OUT | 0=off, 1=on | 0 | Enable +1V LNB boost (13->14V, 18->19V) via XRAM 0xE0B6 |
|
||||||
|
| 0x95 | **GET_FPGA_VERS** | IN | 0 | 1 | Read EEPROM-stored hardware/platform ID |
|
||||||
|
| 0x96 | **SET_LNB_GPIO_MODE** | OUT | 0=default, 1=active | 0 | Configure LNB GPIO output enables (IOB/OEB) |
|
||||||
|
| 0x97 | **SET_GPIO_PINS** | OUT | pin bitmap | 0 | Direct write to LNB GPIO pins |
|
||||||
|
| 0x98 | **GET_GPIO_STATUS** | IN | 0 | 1 | Read LNB feedback GPIO pin (0 or 1) |
|
||||||
|
|
||||||
|
### Driver Usage
|
||||||
|
|
||||||
|
| Cmd | Linux Kernel | Windows Driver | Firmware |
|
||||||
|
|-----|-------------|----------------|----------|
|
||||||
|
| 0x8F | `dishnetwork_send_legacy_command` callback | Defined in header, not called in source | All versions |
|
||||||
|
| 0x91 | Not used | Not defined | All versions |
|
||||||
|
| 0x92 | `gp8psk_get_fw_version()` on boot | Not defined | All versions |
|
||||||
|
| 0x93 | Not called directly (defined in header) | Defined in header, not called in source | All versions |
|
||||||
|
| 0x94 | `enable_high_lnb_voltage` callback | Defined in header, not called in source | All versions |
|
||||||
|
| 0x95 | `gp8psk_get_fpga_version()` on boot | Not defined | All versions |
|
||||||
|
| 0x96 | Not used | Not defined | All versions |
|
||||||
|
| 0x97 | Not used | Not defined | All versions |
|
||||||
|
| 0x98 | Not used | Not defined | All versions |
|
||||||
|
|
||||||
|
### Cross-Version Change Summary
|
||||||
|
|
||||||
|
| Cmd | Changed? | Details |
|
||||||
|
|-----|----------|---------|
|
||||||
|
| 0x8F | No | Identical bit-bang algorithm, same GPIO pin P0.4 |
|
||||||
|
| 0x91 | Minor | IRAM counter address: 0x66 (v2.06) vs 0x18 (v2.13/Rev.2) |
|
||||||
|
| 0x92 | Data only | Different hardcoded version bytes per build |
|
||||||
|
| 0x93 | No | Same EEPROM read logic, same device 0x51 |
|
||||||
|
| 0x94 | Minor | Rev.2 returns 1 byte (ack); v2.06/v2.13 return nothing |
|
||||||
|
| 0x95 | Yes | v2.06 reads EEPROM offset 0x31, returns 2 bytes; v2.13/Rev.2 read offset 0x00, return 1 byte |
|
||||||
|
| 0x96 | Yes | Rev.2 uses different GPIO pins (IOB.4 + Port A) vs v2.06/v2.13 (IOB.3/2/1) |
|
||||||
|
| 0x97 | Yes | Rev.2 maps bits to individual pins across two ports; v2.06/v2.13 bulk-writes Port B |
|
||||||
|
| 0x98 | Yes | Rev.2 reads P0.5 (Port A); v2.06/v2.13 read IOB.0 (Port B) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FX2 Register Reference
|
||||||
|
|
||||||
|
| Address | Name | Purpose |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 0xE6B8 | SETUPDAT[0] | bmRequestType |
|
||||||
|
| 0xE6B9 | SETUPDAT[1] | bRequest |
|
||||||
|
| 0xE6BA | SETUPDAT[2] | wValueL |
|
||||||
|
| 0xE6BB | SETUPDAT[3] | wValueH |
|
||||||
|
| 0xE68A | EP0BCH | EP0 byte count high |
|
||||||
|
| 0xE68B | EP0BCL | EP0 byte count low (write triggers transfer) |
|
||||||
|
| 0xE740 | EP0BUF | EP0 data buffer start |
|
||||||
|
| 0xE0B6 | (custom) | LNB voltage control register |
|
||||||
|
| SFR 0x80 | P0 / IOA | Port A (Port 0) data register |
|
||||||
|
| SFR 0xB0 | P3 / IOB | Port B data register |
|
||||||
|
| SFR 0xB2 | OEA | Port A output enable |
|
||||||
|
| SFR 0xB5 | OEB | Port B output enable |
|
||||||
Loading…
x
Reference in New Issue
Block a user