#!/usr/bin/env python3 """BCM4500 I2C address gateway verification test. Compares register reads at address 0x08 (BCM4500 direct) vs 0x10 (BCM3440 tuner gateway) to confirm the stock firmware disassembly finding: all BCM4500 register access goes through the tuner at 0x10. HYPOTHESIS: - Reads at 0x08 return the same status byte for all registers - Reads at 0x10 return different, register-specific values - Writes at 0x10 actually reach BCM4500 registers - The indirect register protocol (A6/A7/A8) works at 0x10 Run with custom firmware v3.02+ (needs 0xB5 raw I2C read). """ import sys sys.path.insert(0, 'tools') from skywalker_lib import SkyWalker1 CMD_I2C_RAW_READ = 0xB5 ADDR_DIRECT = 0x08 # BCM4500 direct ADDR_GATEWAY = 0x10 # BCM3440 tuner gateway REGS = [0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB] sw = SkyWalker1() sw.open() print('=== BCM4500 I2C Address Gateway Test ===') print(f'Firmware: {sw.get_fw_version()}') # Boot first print('\n--- Booting BCM4500 ---') result = sw._vendor_in(0x89, value=1, index=0, length=3) cfg, stage = result[0], result[1] print(f' Config: 0x{cfg:02X}, Stage: 0x{stage:02X}') # Test 1: Read registers via direct address (0x08) print(f'\n=== Test 1: Registers via direct address 0x{ADDR_DIRECT:02X} (BCM4500) ===') direct_vals = {} for reg in REGS: try: data = sw._vendor_in(CMD_I2C_RAW_READ, value=ADDR_DIRECT, index=reg, length=1) direct_vals[reg] = data[0] print(f' 0x{reg:02X}: 0x{data[0]:02X}') except Exception as e: direct_vals[reg] = None print(f' 0x{reg:02X}: FAILED ({e})') # Test 2: Read registers via gateway address (0x10) print(f'\n=== Test 2: Registers via gateway address 0x{ADDR_GATEWAY:02X} (BCM3440) ===') gw_vals = {} for reg in REGS: try: data = sw._vendor_in(CMD_I2C_RAW_READ, value=ADDR_GATEWAY, index=reg, length=1) gw_vals[reg] = data[0] print(f' 0x{reg:02X}: 0x{data[0]:02X}') except Exception as e: gw_vals[reg] = None print(f' 0x{reg:02X}: FAILED ({e})') # Test 3: Read tuner-specific registers (low range) at 0x10 print(f'\n=== Test 3: BCM3440 tuner registers (0x00-0x0F) at 0x{ADDR_GATEWAY:02X} ===') for reg in range(0x00, 0x10): try: data = sw._vendor_in(CMD_I2C_RAW_READ, value=ADDR_GATEWAY, index=reg, length=1) print(f' 0x{reg:02X}: 0x{data[0]:02X}', end='') except Exception: print(f' 0x{reg:02X}: FAIL', end='') if (reg + 1) % 8 == 0: print() # Test 4: Write to A6 via gateway, then read back print(f'\n=== Test 4: Write A6=0x42 via gateway, read back ===') try: # Write using vendor OUT (need to construct this) # Use bcm_direct_write equivalent through 0xB2 (RAW_DEMOD_WRITE) # Actually, we need a raw I2C write command... # Let's use the B6 diagnostic which writes A6 and reads back diag = sw._vendor_in(0xB6, value=0x42, index=0x01, length=8) print(f' B6 diag (via current FW addr):') print(f' A6 write ok: {diag[0]}') print(f' A6 readback: 0x{diag[1]:02X}') print(f' A8 write ok: {diag[2]}') print(f' A8 immediate: 0x{diag[3]:02X}') print(f' A8 after 2ms: 0x{diag[4]:02X}') print(f' A7 data: 0x{diag[5]:02X}') print(f' A6 final: 0x{diag[6]:02X}') print(f' Cmd sent: 0x{diag[7]:02X}') except Exception as e: print(f' FAILED: {e}') # Analysis print('\n' + '=' * 60) print('ANALYSIS') print('=' * 60) direct_unique = set(v for v in direct_vals.values() if v is not None) gw_unique = set(v for v in gw_vals.values() if v is not None) print(f'\nDirect (0x{ADDR_DIRECT:02X}): {len(direct_unique)} unique values: ' f'{", ".join(f"0x{v:02X}" for v in sorted(direct_unique))}') print(f'Gateway (0x{ADDR_GATEWAY:02X}): {len(gw_unique)} unique values: ' f'{", ".join(f"0x{v:02X}" for v in sorted(gw_unique))}') if len(direct_unique) <= 2 and len(gw_unique) > 2: print('\n >> HYPOTHESIS CONFIRMED!') print(' >> Direct 0x08 returns same status for all regs') print(' >> Gateway 0x10 returns register-specific values') print(' >> FIX: BCM4500_ADDR should be 0x10 (tuner gateway)') elif len(gw_unique) <= 2: print('\n >> Gateway also returns uniform values -- may need different timing') print(' >> or the tuner gateway requires initialization first') else: print('\n >> Unexpected results -- check values above') # Show side-by-side comparison print('\n Reg Direct(0x08) Gateway(0x10) Different?') for reg in REGS: d = direct_vals.get(reg) g = gw_vals.get(reg) d_str = f'0x{d:02X}' if d is not None else 'FAIL' g_str = f'0x{g:02X}' if g is not None else 'FAIL' diff = ' <--' if d != g else '' print(f' 0x{reg:02X} {d_str:>12} {g_str:>13} {diff}') sw.close() print('\n=== Done ===')