mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
Merge pull request #266 from hlyi/pr-tool
Tools for detecting GPIO mapping and extracting LED configure
This commit is contained in:
@@ -0,0 +1,149 @@
|
|||||||
|
# LED GPIO Utilities
|
||||||
|
|
||||||
|
This directory contains utility talking to RTL837x switch IC via I2C bus.
|
||||||
|
These scripts are designed to help with monitoring, debugging GPIO, and identifying LED configurations.
|
||||||
|
(Only tested in Linux)
|
||||||
|
|
||||||
|
## Hardware requirements
|
||||||
|
|
||||||
|
To use these utilities, you need:
|
||||||
|
|
||||||
|
- A hardware dongle that can communicate with I2C devices. One example is the [I2C-Pico-USB](https://github.com/dquadros/I2C-Pico-USB) which provides USB-to-I2C connectivity.
|
||||||
|
- Connection between the hardware dongle and the RTL837x's I2C communication port.
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
### 1. `i2c_read_rtl_gpio.py`
|
||||||
|
|
||||||
|
This script reads RTL GPIO register values via I2C and displays changes in GPIO states.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
- Reads live RTL GPIO register values via I2C (default address 0x5C)
|
||||||
|
- Monitors GPIO changes with delta detection
|
||||||
|
- Allows specification of I2C bus, sleep interval, and ignored GPIO pins
|
||||||
|
- Shows changes in real-time with GPIO index display
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Basic usage (defaults to I2C bus 1, 2s sleep interval)
|
||||||
|
python3 i2c_read_rtl_gpio.py
|
||||||
|
|
||||||
|
# Specify I2C bus
|
||||||
|
python3 i2c_read_rtl_gpio.py --i2c-bus 0
|
||||||
|
|
||||||
|
# Specify sleep interval in seconds
|
||||||
|
python3 i2c_read_rtl_gpio.py --sleep-interval 5
|
||||||
|
|
||||||
|
# Ignore specific GPIO pins
|
||||||
|
python3 i2c_read_rtl_gpio.py --ignored-ios 28 31 34 44
|
||||||
|
|
||||||
|
# Combine options
|
||||||
|
python3 i2c_read_rtl_gpio.py --i2c-bus 2 --sleep-interval 1 --ignored-ios 28 31
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Format:**
|
||||||
|
|
||||||
|
- Displays register address and data in hex format
|
||||||
|
- Shows GPIO pins that have changed since last read
|
||||||
|
- Example: `0044: 00 00 00 00 00 00 00 00`
|
||||||
|
|
||||||
|
### 2. `i2c_dump_rtl_regs.py`
|
||||||
|
|
||||||
|
This script dumps all register values from an RTL device via I2C.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
- Dumps registers sequentially from address 0x0000 to 0xFFFF
|
||||||
|
- Reads 16 bytes at a time for efficiency
|
||||||
|
- Configurable I2C bus number
|
||||||
|
- Provides complete register dump in hex format
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Basic usage (defaults to I2C bus 1)
|
||||||
|
python3 i2c_dump_rtl_regs.py >reg_dump.txt
|
||||||
|
|
||||||
|
# Specify I2C bus
|
||||||
|
python3 i2c_dump_rtl_regs.py --bus 0 >reg_dump.txt
|
||||||
|
|
||||||
|
# Or using short option
|
||||||
|
python3 i2c_dump_rtl_regs.py -b 2 >reg_dump.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Format:**
|
||||||
|
|
||||||
|
- Displays address and 16 bytes of data in hex format
|
||||||
|
- Example: `0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00`
|
||||||
|
|
||||||
|
### 3. `dec_leds_from_dump.py`
|
||||||
|
|
||||||
|
This script decodes LED configuration from a register dump file ( the output from `i2c_dump_rtl_regs.py`).
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
- Parses register dump files (reg_dump.txt format)
|
||||||
|
- Decodes LED pad configurations and LED sets
|
||||||
|
- Maps LED types to bit positions based on configuration
|
||||||
|
- Outputs LED mux configuration and set mappings
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Must be run in same directory as reg_dump.txt
|
||||||
|
python3 dec_leds_from_dump.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Format:**
|
||||||
|
|
||||||
|
- LED pad configuration (hex values for each pad)
|
||||||
|
- LED set configurations with descriptions of LED types
|
||||||
|
- Port selection mappings
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
- Requires a `reg_dump.txt` file containing the register dump output
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
All scripts require:
|
||||||
|
|
||||||
|
- Python 3
|
||||||
|
- `smbus2` Python package
|
||||||
|
|
||||||
|
(Depend upon Linux distribution) Install with:
|
||||||
|
```bash
|
||||||
|
apt install python3-smbus2
|
||||||
|
```
|
||||||
|
OR
|
||||||
|
```bash
|
||||||
|
pip3 install smbus2
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Common Use Cases
|
||||||
|
|
||||||
|
Both `i2c_read_rtl_gpio.py` and `i2c_dump_rtl_regs.py` shall run with the **original** firmware, not RTLPlayground firmware.
|
||||||
|
|
||||||
|
|
||||||
|
### Monitoring GPIO Changes
|
||||||
|
```bash
|
||||||
|
# Monitor GPIO changes on bus 1 with 1-second intervals
|
||||||
|
python3 i2c_read_rtl_gpio.py --i2c-bus 1 --sleep-interval 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Register Analysis
|
||||||
|
```bash
|
||||||
|
# Dump all device registers
|
||||||
|
python3 i2c_dump_rtl_regs.py --bus 2 > reg_dump.txt
|
||||||
|
|
||||||
|
# Analyze the register dump to understand LED configuration
|
||||||
|
python3 dec_leds_from_dump.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All scripts support command-line arguments for flexible configuration:
|
||||||
|
|
||||||
|
- `--i2c-bus` or `-b`: Specify I2C bus (default: 1)
|
||||||
|
- `--sleep-interval` or `-s`: Sleep between reads in seconds (default: 2)
|
||||||
|
- `--ignored-ios` or `-i`: GPIO pins to ignore (default: [28, 31, 34, 44])
|
||||||
Executable
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
regs = list(range(65536))
|
||||||
|
|
||||||
|
glb_mux = 0x65E0
|
||||||
|
|
||||||
|
led3_0_set1 = 0x6528
|
||||||
|
led1_0_set0 = 0x6548
|
||||||
|
led_port_sel = 0x654C
|
||||||
|
|
||||||
|
|
||||||
|
def get_word(i):
|
||||||
|
val = (regs[i + 3] << 24) + (regs[i + 2] << 16) + (regs[i + 1] << 8) + regs[i]
|
||||||
|
# print(f"READING {i:04x} : {val:08x}")
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
with open("reg_dump.txt", "r") as file:
|
||||||
|
for line in file:
|
||||||
|
addr, data = line.split(":")
|
||||||
|
data = [int(x, 16) for x in data.strip().split(" ")]
|
||||||
|
addr = int(addr, 16)
|
||||||
|
for x in range(len(data)):
|
||||||
|
regs[addr + x] = data[x]
|
||||||
|
|
||||||
|
ledstr = []
|
||||||
|
ledfmt = []
|
||||||
|
print("LED pad Configuration:")
|
||||||
|
for i in range(28):
|
||||||
|
j = i % 5
|
||||||
|
if j == 0:
|
||||||
|
idx = glb_mux + (i // 5) * 4
|
||||||
|
val = get_word(idx)
|
||||||
|
ledval = (val >> (6 * j)) & 0x3F
|
||||||
|
ledstr.append(f"{ledval:02x}")
|
||||||
|
ledfmt.append(f"{i:02x}")
|
||||||
|
print(f"{' '.join(ledfmt)}")
|
||||||
|
print(f"{' '.join(ledstr)}")
|
||||||
|
print(f".led_mux = {{ 0x{', 0x'.join(ledstr)} }},")
|
||||||
|
|
||||||
|
LED_TYPES = [
|
||||||
|
" 10G",
|
||||||
|
16,
|
||||||
|
" TWO_5G",
|
||||||
|
17,
|
||||||
|
" 5G",
|
||||||
|
18,
|
||||||
|
" TWO_2G5",
|
||||||
|
19,
|
||||||
|
" 2G5",
|
||||||
|
0,
|
||||||
|
" TWO_1G",
|
||||||
|
1,
|
||||||
|
" 1G",
|
||||||
|
2,
|
||||||
|
" 500M",
|
||||||
|
3,
|
||||||
|
" 100M",
|
||||||
|
4,
|
||||||
|
" 10M",
|
||||||
|
5,
|
||||||
|
" LINK",
|
||||||
|
6,
|
||||||
|
" LINK_FLASH",
|
||||||
|
7,
|
||||||
|
" ACT",
|
||||||
|
8,
|
||||||
|
" RX",
|
||||||
|
9,
|
||||||
|
" TX",
|
||||||
|
10,
|
||||||
|
" COL",
|
||||||
|
11,
|
||||||
|
" DUPLEX",
|
||||||
|
12,
|
||||||
|
" TRAINING",
|
||||||
|
13,
|
||||||
|
" MASTER",
|
||||||
|
14,
|
||||||
|
"",
|
||||||
|
15,
|
||||||
|
]
|
||||||
|
|
||||||
|
led_set = []
|
||||||
|
led_set_str = []
|
||||||
|
print("\nLED-set Configuration:")
|
||||||
|
print("LED-ID 0 1 2 3")
|
||||||
|
for i in range(4):
|
||||||
|
idval = []
|
||||||
|
idvalstr = []
|
||||||
|
for id in range(4):
|
||||||
|
val = 0xFFFF & (
|
||||||
|
get_word(led1_0_set0 - 8 * i - ((id >> 1) << 2)) >> (16 * (id & 1))
|
||||||
|
)
|
||||||
|
valhi = (
|
||||||
|
0xF & (get_word(led3_0_set1 - 4 * (i >> 1)) >> (16 * (i & 1) + 4 * id))
|
||||||
|
) << 16
|
||||||
|
val += valhi
|
||||||
|
idval.append(val)
|
||||||
|
valstr = "("
|
||||||
|
for bit in range(0, len(LED_TYPES), 2):
|
||||||
|
if val & (1 << (LED_TYPES[bit + 1])):
|
||||||
|
valstr += LED_TYPES[bit]
|
||||||
|
valstr += ")"
|
||||||
|
idvalstr.append(valstr)
|
||||||
|
led_set.append(idval)
|
||||||
|
led_set_str.append(idvalstr)
|
||||||
|
idstr = " ".join([f"{d:05x}" for d in idval])
|
||||||
|
print(f"SET {i}: {idstr}")
|
||||||
|
# print(f"{idvalstr}")
|
||||||
|
|
||||||
|
portsel = get_word(led_port_sel)
|
||||||
|
for i in range(3, 9):
|
||||||
|
sel = (portsel >> (i << 1)) & 3
|
||||||
|
print(f"Port {i}: SET {sel}: {', '.join(led_set_str[sel])}")
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from smbus2 import SMBus, i2c_msg
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
DEVICE_ADDR = 0x5C # Replace with your device address
|
||||||
|
NUM_BYTES = 16 # Bytes to read
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
parser = argparse.ArgumentParser(description="Dump RTL registers via I2C")
|
||||||
|
parser.add_argument(
|
||||||
|
"-b",
|
||||||
|
"--i2c-bus",
|
||||||
|
type=int,
|
||||||
|
required=True,
|
||||||
|
help="I2C bus number (use i2cdetect -l to find out the bus number of the dongle)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Open I2C bus
|
||||||
|
with SMBus(args.i2c_bus) as bus:
|
||||||
|
|
||||||
|
for addr in range(0, 65536, NUM_BYTES):
|
||||||
|
# Create write message (send register address)
|
||||||
|
write = i2c_msg.write(DEVICE_ADDR, [addr >> 8, addr & 0xFF])
|
||||||
|
|
||||||
|
# Create read message (read 2 bytes)
|
||||||
|
read = i2c_msg.read(DEVICE_ADDR, NUM_BYTES)
|
||||||
|
|
||||||
|
# Perform combined transaction
|
||||||
|
bus.i2c_rdwr(write, read)
|
||||||
|
|
||||||
|
new_data = list(read)
|
||||||
|
|
||||||
|
# Convert read message to string
|
||||||
|
datastr = " ".join([f"{x:02x}" for x in new_data])
|
||||||
|
print(f"{addr:04x}: {datastr}")
|
||||||
Executable
+83
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
from smbus2 import SMBus, i2c_msg
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
I2C_BUS = 1
|
||||||
|
DEVICE_ADDR = 0x5C # Replace with your device address
|
||||||
|
NUM_BYTES = 8 # Bytes to read
|
||||||
|
SLEEP_INTERVAL = 1
|
||||||
|
|
||||||
|
# Handle command line arguments for ignored IOs
|
||||||
|
# default to ignore IOs related to SYS_LED, UART, SMI, SPI
|
||||||
|
IGNORED_IOS = [28, 31, 32, 34, 35, 42, 43, 44, 45]
|
||||||
|
|
||||||
|
# Setup argument parser
|
||||||
|
parser = argparse.ArgumentParser(description="Read I2C data from RTL GPIO expander")
|
||||||
|
parser.add_argument(
|
||||||
|
"-b",
|
||||||
|
"--i2c-bus",
|
||||||
|
type=int,
|
||||||
|
required=True,
|
||||||
|
help="I2C bus number (use i2cdetect -l to find out the bus number of the dongle)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--sleep-interval",
|
||||||
|
type=int,
|
||||||
|
default=SLEEP_INTERVAL,
|
||||||
|
help=f"Sleep interval in seconds (default: {SLEEP_INTERVAL})",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i",
|
||||||
|
"--ignored-ios",
|
||||||
|
nargs="*",
|
||||||
|
type=int,
|
||||||
|
default=IGNORED_IOS,
|
||||||
|
help=f"List of GPIO pins to ignore (default: {IGNORED_IOS})",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
I2C_BUS = args.i2c_bus
|
||||||
|
|
||||||
|
first_read = True
|
||||||
|
last_data = [0 for x in range(NUM_BYTES)]
|
||||||
|
|
||||||
|
# Open I2C bus
|
||||||
|
with SMBus(I2C_BUS) as bus:
|
||||||
|
addr = 0x44
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Create write message (send register address)
|
||||||
|
write = i2c_msg.write(DEVICE_ADDR, [addr >> 8, addr & 0xFF])
|
||||||
|
|
||||||
|
# Create read message (read NUM_BYTES bytes)
|
||||||
|
read = i2c_msg.read(DEVICE_ADDR, NUM_BYTES)
|
||||||
|
|
||||||
|
# Perform combined transaction
|
||||||
|
bus.i2c_rdwr(write, read)
|
||||||
|
|
||||||
|
new_data = list(read)
|
||||||
|
if first_read:
|
||||||
|
delta_data = last_data
|
||||||
|
first_read = False
|
||||||
|
else:
|
||||||
|
delta_data = [new_data[x] ^ last_data[x] for x in range(NUM_BYTES)]
|
||||||
|
last_data = new_data
|
||||||
|
|
||||||
|
# Convert read message to list
|
||||||
|
datastr = " ".join([f"{x:02x}" for x in new_data])
|
||||||
|
deltastr = ""
|
||||||
|
chg_list = []
|
||||||
|
for x in range(NUM_BYTES):
|
||||||
|
if delta_data[x] == 0:
|
||||||
|
continue
|
||||||
|
for y in range(8):
|
||||||
|
if delta_data[x] & (1 << y):
|
||||||
|
idx = x * 8 + y
|
||||||
|
if idx not in args.ignored_ios:
|
||||||
|
deltastr += f"\n{' ':8s}GPIO{idx}"
|
||||||
|
# deltastr = " ".join([ f"{x:02x}" for x in delta_data])
|
||||||
|
print(f"{addr:04x}: {datastr}{deltastr}")
|
||||||
|
time.sleep(args.sleep_interval)
|
||||||
Reference in New Issue
Block a user