Auto-detect the diag port in efs2.py
Probe each candidate serial port with a DIAG version request and keep the one that answers, interface 0 (-if00-) first, instead of hardcoding ttyUSB0. Use /dev/serial/by-id for stable naming; DIAGPORT overrides. Claude-Session: https://claude.ai/code/session_01Ctisr9uXe4H8XsscZ2exGE
This commit is contained in:
parent
8318606f34
commit
b42dc4c3f1
2 changed files with 58 additions and 9 deletions
|
|
@ -4,9 +4,7 @@
|
||||||
Packet layouts and opcodes replicate JohnBel/EfsTools exactly. Used to
|
Packet layouts and opcodes replicate JohnBel/EfsTools exactly. Used to
|
||||||
back up and replace policyman RF-band config on a rooted Sony Xperia 1 VI.
|
back up and replace policyman RF-band config on a rooted Sony Xperia 1 VI.
|
||||||
"""
|
"""
|
||||||
import sys, os, time, struct, serial
|
import sys, os, time, struct, glob, serial
|
||||||
|
|
||||||
PORT = os.environ.get("DIAGPORT", "/dev/ttyUSB0")
|
|
||||||
|
|
||||||
DIAG_SUBSYS = 0x4B
|
DIAG_SUBSYS = 0x4B
|
||||||
SUBSYS_EFS = 19
|
SUBSYS_EFS = 19
|
||||||
|
|
@ -50,9 +48,57 @@ def hdlc_decode(frame):
|
||||||
else: out.append(b)
|
else: out.append(b)
|
||||||
return bytes(out)
|
return bytes(out)
|
||||||
|
|
||||||
|
def _responds_to_diag(port, timeout=1.0):
|
||||||
|
"""Return True if port answers a DIAG version request (cmd 0x00)."""
|
||||||
|
try:
|
||||||
|
s = serial.Serial(port, 115200, timeout=0.3)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
s.reset_input_buffer()
|
||||||
|
s.write(hdlc_encode([0x00]))
|
||||||
|
buf, deadline = bytearray(), time.time() + timeout
|
||||||
|
while time.time() < deadline:
|
||||||
|
b = s.read(1)
|
||||||
|
if not b:
|
||||||
|
continue
|
||||||
|
if b[0] == 0x7E:
|
||||||
|
if buf:
|
||||||
|
dec = hdlc_decode(bytes(buf))
|
||||||
|
buf = bytearray()
|
||||||
|
if len(dec) >= 3 and dec[0] == 0x00 and \
|
||||||
|
fcs16(dec[:-2]) == struct.unpack("<H", dec[-2:])[0]:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
buf.append(b[0])
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
def find_diag_port():
|
||||||
|
"""Find the DIAG serial port. DIAGPORT wins; otherwise probe candidates,
|
||||||
|
interface 0 (-if00-) first, and return the one that answers DIAG."""
|
||||||
|
env = os.environ.get("DIAGPORT")
|
||||||
|
if env:
|
||||||
|
return env
|
||||||
|
candidates = (sorted(glob.glob("/dev/serial/by-id/*-if00-port0")) +
|
||||||
|
sorted(glob.glob("/dev/serial/by-id/*")) +
|
||||||
|
sorted(glob.glob("/dev/ttyUSB*")))
|
||||||
|
seen = set()
|
||||||
|
for p in candidates:
|
||||||
|
real = os.path.realpath(p)
|
||||||
|
if real in seen:
|
||||||
|
continue
|
||||||
|
seen.add(real)
|
||||||
|
if _responds_to_diag(p):
|
||||||
|
return p
|
||||||
|
raise IOError("no responding DIAG port found; is the phone in diag mode "
|
||||||
|
"(setprop sys.usb.config diag,serial_cdev,rmnet,adb)?")
|
||||||
|
|
||||||
class Diag:
|
class Diag:
|
||||||
def __init__(self, port=PORT):
|
def __init__(self, port=None):
|
||||||
self.s = serial.Serial(port, 115200, timeout=0.4)
|
self.port = port or find_diag_port()
|
||||||
|
self.s = serial.Serial(self.port, 115200, timeout=0.4)
|
||||||
self.s.reset_input_buffer()
|
self.s.reset_input_buffer()
|
||||||
|
|
||||||
def _read_frame(self, timeout=4.0):
|
def _read_frame(self, timeout=4.0):
|
||||||
|
|
@ -206,6 +252,7 @@ BACKUP = "/tmp/claude-1000/-home-grmrgecko-Downloads-XperiFirm-5-8-1--by-Igor-Ei
|
||||||
def main():
|
def main():
|
||||||
cmd = sys.argv[1] if len(sys.argv) > 1 else "probe"
|
cmd = sys.argv[1] if len(sys.argv) > 1 else "probe"
|
||||||
d = Diag()
|
d = Diag()
|
||||||
|
print("using diag port %s" % d.port)
|
||||||
ver = d.hello()
|
ver = d.hello()
|
||||||
print("EFS hello ok, version=%d" % ver)
|
print("EFS hello ok, version=%d" % ver)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,12 +115,14 @@ be redone.
|
||||||
'setprop sys.usb.config diag,serial_cdev,rmnet,adb'`. Phone re-enumerates
|
'setprop sys.usb.config diag,serial_cdev,rmnet,adb'`. Phone re-enumerates
|
||||||
as USB `05c6:9091` (adb survives). Bind the serial driver (needs root on
|
as USB `05c6:9091` (adb survives). Bind the serial driver (needs root on
|
||||||
the PC): `sudo modprobe option && echo 05c6 9091 | sudo tee
|
the PC): `sudo modprobe option && echo 05c6 9091 | sudo tee
|
||||||
/sys/bus/usb-serial/drivers/option1/new_id`. Diag port = `/dev/ttyUSB0`
|
/sys/bus/usb-serial/drivers/option1/new_id`. The diag channel is USB
|
||||||
(the one that answers a DIAG version request; ttyUSB1 is silent).
|
interface 0, stable as `/dev/serial/by-id/usb-Sony_XQ-EC72_*-if00-port0`
|
||||||
|
(maps to a ttyUSB whose number is not stable). efs2.py auto-detects it by
|
||||||
|
probing each port with a DIAG version request; set `DIAGPORT` to override.
|
||||||
3. Talk EFS2 over DIAG. EfsTools (JohnBel) is the usual tool but its
|
3. Talk EFS2 over DIAG. EfsTools (JohnBel) is the usual tool but its
|
||||||
libnserial hits EIO on the option-driver port; the reliable path was a
|
libnserial hits EIO on the option-driver port; the reliable path was a
|
||||||
small pyserial EFS2 client (HDLC + FCS-16, subsys 0x4B/EFS 19). Scripts
|
small pyserial EFS2 client (HDLC + FCS-16, subsys 0x4B/EFS 19). Script is
|
||||||
kept at `~/.../scratchpad/efs2.py` (+ `diag_probe.py`). It does:
|
`5g-policyman/efs2.py` in this skill. It does:
|
||||||
hello → list `/policyman` → **back up** the 3 files → write new ones
|
hello → list `/policyman` → **back up** the 3 files → write new ones
|
||||||
(unlink then open `O_WRONLY|O_CREAT` perm 0777, 1 KB write chunks) →
|
(unlink then open `O_WRONLY|O_CREAT` perm 0777, 1 KB write chunks) →
|
||||||
read back and byte-compare to verify.
|
read back and byte-compare to verify.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue