#!/usr/bin/env python3 """Pinpoint which element in mode 0x82 causes bcm_direct_read to fail. Test sequence: 1. Power on via 0x81, confirm alive with raw read 2. 0x84: bcm_direct_read ONLY (no GPIO, no reset, no bus reset) 3. 0x85: GPIO + reset + power but NO I2C bus reset (no bmSTOP) 4. 0x82: GPIO + I2C bus reset + reset + power + probe (the one that fails) """ import usb.core import usb.util import sys import time BOOT_8PSK = 0x89 def find_device(): dev = usb.core.find(idVendor=0x09C0, idProduct=0x0203) if not dev: print("Device not found!") sys.exit(1) return dev def setup_device(dev): try: if dev.is_kernel_driver_active(0): dev.detach_kernel_driver(0) except Exception: pass try: dev.set_configuration() except usb.core.USBError: pass def decode_stage(stage): names = { 0x00: "NOT_STARTED", 0xA1: "GPIO_OK", 0xA2: "PROBE_OK(0x82)", 0xA3: "BLK0_OK", 0xA4: "PROBE_OK(0x84)", 0xA5: "PROBE_OK(0x85)", 0xE3: "PROBE_FAIL", 0xE4: "BLK0_FAIL", } return names.get(stage, f"0x{stage:02X}") def test_boot_mode(dev, wval, label, timeout_ms=3000): print(f"\n{'─' * 55}") print(f" Mode 0x{wval:02X}: {label}") print(f"{'─' * 55}") t0 = time.monotonic() try: ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, wval, 0, 3, timeout=timeout_ms) except usb.core.USBError as e: elapsed = (time.monotonic() - t0) * 1000 print(f" TIMEOUT after {elapsed:.0f}ms: {e}") return None elapsed = (time.monotonic() - t0) * 1000 stage = ret[1] probe = ret[2] ok = stage not in (0xE3, 0xE4) status_str = "SUCCESS" if ok else "FAILED" print(f" {status_str} in {elapsed:.0f}ms") print(f" stage=0x{stage:02X} [{decode_stage(stage)}] probe=0x{probe:02X}") return ret def raw_read(dev, addr, reg): try: r = dev.ctrl_transfer(0xC0, 0xB5, addr, reg, 1, timeout=1000) return r[0] except: return None def main(): dev = find_device() setup_device(dev) ret = dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6, timeout=2000) major, minor, patch = ret[2], ret[1], ret[0] print(f"Firmware: v{major}.{minor:02d}.{patch}") # Step 1: Power on via GPIO-only mode print("\n=== STEP 1: Power on BCM4500 (mode 0x81) ===") ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, 0x81, 0, 3, timeout=3000) print(f" GPIO setup done, stage=0x{ret[1]:02X}") time.sleep(1.0) # Confirm alive val = raw_read(dev, 0x08, 0xA2) print(f" Raw read 0x08:0xA2 = 0x{val:02X}" if val is not None else " Raw read FAILED") # Step 2: Test 0x84 (I2C read ONLY, no GPIO manipulation) test_boot_mode(dev, 0x84, "bcm_direct_read ONLY (no GPIO, chip already powered)") # Confirm still alive val = raw_read(dev, 0x08, 0xA2) print(f" Raw read after 0x84: 0x{val:02X}" if val is not None else " Raw read FAILED") # Step 3: Test 0x85 (GPIO + reset but NO I2C bus reset) test_boot_mode(dev, 0x85, "GPIO + reset + power, NO bmSTOP (no I2C bus reset)") # Confirm still alive time.sleep(0.1) val = raw_read(dev, 0x08, 0xA2) print(f" Raw read after 0x85: 0x{val:02X}" if val is not None else " Raw read FAILED") # Step 4: For comparison, test 0x82 (the one that fails) test_boot_mode(dev, 0x82, "GPIO + I2C bmSTOP + reset + power + probe") # Confirm still alive val = raw_read(dev, 0x08, 0xA2) print(f" Raw read after 0x82: 0x{val:02X}" if val is not None else " Raw read FAILED") print(f"\n{'=' * 55}") print("Analysis complete.") print() print("If 0x84 works → bcm_direct_read is fine, issue is in reset/GPIO sequence") print("If 0x84 fails → bcm_direct_read itself has a bug") print("If 0x85 works → I2CS bmSTOP (I2C bus reset) is the culprit in 0x82") print("If 0x85 fails → re-reset of BCM4500 needs more delay") if __name__ == "__main__": main()