#!/usr/bin/env python3
"""
Low-level device info via the DA's DEVICE_CTRL queries.

The xflash DA doesn't expose EXT_CSD PARTITION_CONFIG directly, but it does
expose other registers that answer "is this firmware, or hardware/power?":
  - GET_BATTERY_VOLTAGE  -> is the battery dead (why it won't power on)?
  - GET_EMMC_INFO        -> eMMC alive, CID, boot1/boot2/user sizes
  - GET_RPMB_STATUS / GET_OTP_LOCK_STATUS / GET_DRAM_TYPE

Run with device in BROM mode:
    sudo python3 device_info.py
"""
import os, sys, time, struct
from mtk_device import MTKDevice, MTKError
from da_parser import find_da_for_chip, load_da_regions

HERE = os.path.dirname(os.path.abspath(__file__))
DA_PATH = os.path.join(HERE, "DA_PL.bin")

DEVICE_CTRL = 0x010009
GET_EMMC_INFO = 0x040001
GET_BATTERY_VOLTAGE = 0x04000F
GET_RPMB_STATUS = 0x040010
GET_DRAM_TYPE = 0x040012
GET_DA_VERSION = 0x040005


def log(level, msg):
    print(f"[{level:5}] {msg}")


def connect(mtk):
    deadline = time.time() + 120
    while time.time() < deadline:
        try:
            mtk.connect(timeout_sec=10)
            mtk.brom_handshake()
            mtk.get_hw_code(); mtk.get_hw_sw_ver()
            try: mtk.get_target_config()
            except Exception: pass
            hw = mtk.hw_code
            da = find_da_for_chip(DA_PATH, hw) or \
                find_da_for_chip(DA_PATH, {0x0717: 0x6761, 0x0507: 0x6761}.get(hw, hw))
            if not da:
                raise MTKError(f"No DA for {hw:#06x}")
            mtk.full_init(DA_PATH, load_da_regions(DA_PATH, da))
            return True
        except Exception as e:
            log("warn", f"connect: {e}")
            mtk.disconnect(); time.sleep(0.5)
    return False


def _read_packet(mtk, timeout=5000):
    """Read one full XFlash packet, return its payload bytes."""
    hdr = mtk._read_exact(12, timeout=timeout)
    magic, _dt, length = struct.unpack("<III", hdr)
    if magic != 0xFEEEEEEF:
        return b""
    return mtk._read_exact(length, timeout=timeout) if length else b""


def devctrl(mtk, cmd):
    """Mirror mtkclient send_devctrl: DEVICE_CTRL -> status -> cmd -> status ->
    read data packet -> final status. Returns the data payload (or None)."""
    try:
        mtk.xsend(DEVICE_CTRL)
        if mtk._da_status(5000) != 0:
            return None
        mtk.xsend(cmd)
        if mtk._da_status(5000) != 0:
            return None
        data = _read_packet(mtk)
        mtk._da_status(5000)   # final status
        return data
    except Exception as e:
        log("warn", f"devctrl {cmd:#x} failed: {e}")
        return None


def main():
    print("=" * 64)
    print("  DEVICE INFO — battery / eMMC / hardware registers")
    print("=" * 64)
    mtk = MTKDevice(log_cb=lambda l, m: None)
    if not connect(mtk):
        log("FAIL", "could not connect"); return 2
    log("ok", "DA ready\n")

    # ---- BATTERY (the key question) ----
    print("-- BATTERY --")
    bv = devctrl(mtk, GET_BATTERY_VOLTAGE)
    if bv and len(bv) >= 4:
        mv = struct.unpack("<I", bv[:4])[0]
        # value is usually millivolts
        log("ok", f"battery voltage = {mv} mV ({mv/1000:.2f} V)")
        if mv < 3000:
            log("FAIL", ">>> BATTERY TOO LOW to boot. A phone needs ~3.4V+ to "
                "power on. This is why it stays black. Charge longer / check "
                "battery connector.")
        elif mv < 3400:
            log("warn", "battery low — may not boot until charged further.")
        else:
            log("ok", "battery level is healthy — NOT the boot blocker.")
    else:
        log("warn", f"no battery reading (resp={bv!r})")

    # ---- eMMC ----
    print("\n-- eMMC --")
    em = devctrl(mtk, GET_EMMC_INFO)
    if em and len(em) >= 8 + 64:
        etype, block = struct.unpack_from("<II", em, 0)
        (b1, b2, rpmb, gp1, gp2, gp3, gp4, user) = struct.unpack_from("<8Q", em, 8)
        cid = em[8 + 64:8 + 64 + 16]
        log("ok", f"eMMC type={etype} block={block:#x}")
        log("ok", f"BOOT1={b1//1024}K BOOT2={b2//1024}K RPMB={rpmb//1024}K "
            f"USER={user/1024/1024/1024:.1f}G")
        log("ok", f"CID={cid.hex()}")
        if user == 0:
            log("FAIL", "eMMC USER size 0 — eMMC fault!")
    else:
        log("warn", f"no eMMC info (resp len={len(em) if em else 0})")

    # ---- extras ----
    print("\n-- OTHER --")
    for name, cmd in (("DRAM type", GET_DRAM_TYPE),
                      ("RPMB status", GET_RPMB_STATUS),
                      ("DA version", GET_DA_VERSION)):
        d = devctrl(mtk, cmd)
        if d:
            log("ok", f"{name}: {d[:16].hex()}")

    print("\n" + "=" * 64)
    print("  If battery >= 3.4V and eMMC USER size is correct, the firmware")
    print("  and hardware are fine — the no-boot is the eMMC boot-config the")
    print("  DA can't set, or a non-eMMC hardware fault. If battery is low,")
    print("  that's the answer: charge it properly first.")
    print("=" * 64)
    return 0


if __name__ == "__main__":
    sys.exit(main())
