diff --git a/proxyclient/experiments/aop.py b/proxyclient/experiments/aop.py deleted file mode 100755 index 0dba36fb0..000000000 --- a/proxyclient/experiments/aop.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: MIT -import sys, pathlib -sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) - -import struct -import traceback -from construct import * - -from m1n1.setup import * -from m1n1.shell import run_shell -from m1n1.hw.dart import DART, DARTRegs -from m1n1.fw.asc import StandardASC, ASCDummyEndpoint -from m1n1.fw.asc.base import * -from m1n1.fw.aop import * -from m1n1.fw.aop.ipc import * -from m1n1.fw.afk.rbep import * -from m1n1.fw.afk.epic import * - -# Set up a secondary proxy channel so that we can stream -# the microphone samples -p.usb_iodev_vuart_setup(p.iodev_whoami()) -p.iodev_set_usage(IODEV.USB_VUART, USAGE.UARTPROXY) - -p.pmgr_adt_clocks_enable("/arm-io/dart-aop") - -adt_dc = u.adt["/arm-io/aop/iop-aop-nub/aop-audio/dc-2400000"] - -pdm_config = Container( - unk1=2, - clockSource=u'pll ', - pdmFrequency=2400000, - unk3_clk=24000000, - unk4_clk=24000000, - unk5_clk=24000000, - channelPolaritySelect=256, - unk7=99, - unk8=1013248, - unk9=0, - ratios=Container( - r1=15, - r2=5, - r3=2, - ), - filterLengths=0x542c47, - coeff_bulk=120, - coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients), - unk10=1, - micTurnOnTimeMs=20, - unk11=1, - micSettleTimeMs=50, -) - -decimator_config = Container( - latency=15, - ratios=Container( - r1=15, - r2=5, - r3=2, - ), - filterLengths=0x542c47, - coeff_bulk=120, - coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients), -) - -class AFKEP_Hello(AFKEPMessage): - TYPE = 63, 48, Constant(0x80) - UNK = 7, 0 - -class AFKEP_Hello_Ack(AFKEPMessage): - TYPE = 63, 48, Constant(0xa0) - -class EPICEndpoint(AFKRingBufEndpoint): - BUFSIZE = 0x1000 - - def __init__(self, *args, **kwargs): - self.seq = 0x0 - self.wait_reply = False - self.ready = False - super().__init__(*args, **kwargs) - - @msg_handler(0x80, AFKEP_Hello) - def Hello(self, msg): - self.rxbuf, self.rxbuf_dva = self.asc.ioalloc(self.BUFSIZE) - self.txbuf, self.txbuf_dva = self.asc.ioalloc(self.BUFSIZE) - - self.send(AFKEP_Hello_Ack()) - - def handle_hello(self, hdr, sub, fd): - if sub.type != 0xc0: - return False - - payload = fd.read() - name = payload.split(b"\0")[0].decode("ascii") - self.log(f"Hello! (endpoint {name})") - self.ready = True - return True - - def handle_reply(self, hdr, sub, fd): - if self.wait_reply: - self.pending_call.read_resp(fd) - self.wait_reply = False - return True - return False - - def handle_ipc(self, data): - fd = BytesIO(data) - hdr = EPICHeader.parse_stream(fd) - sub = EPICSubHeaderVer2.parse_stream(fd) - - handled = False - - if sub.category == EPICCategory.REPORT: - handled = self.handle_hello(hdr, sub, fd) - if sub.category == EPICCategory.REPLY: - handled = self.handle_reply(hdr, sub, fd) - - if not handled and getattr(self, 'VERBOSE', False): - self.log(f"< 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") - self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") - self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") - chexdump(fd.read()) - - def indirect(self, call, chan=0x1000000d, timeout=0.1): - tx = call.ARGS.build(call.args) - self.asc.iface.writemem(self.txbuf, tx[4:]) - - cmd = self.roundtrip(IndirectCall( - txbuf=self.txbuf_dva, txlen=len(tx) - 4, - rxbuf=self.rxbuf_dva, rxlen=self.BUFSIZE, - retcode=0, - ), category=EPICCategory.COMMAND, typ=call.TYPE) - fd = BytesIO() - fd.write(struct.pack(" pw1 -> pwrd state. it starts capturing at pwrd +# shutdown sequence must also be pwrd -> pw1 -> idle + +def aop_start(): + aop.audio.indirect(SetDeviceProp( + devid=u'hpai', + modifier=202, + data=Container( + devid=u'hpai', + cookie=2, + target_pstate=u'pwrd', + unk2=1, + ) + )).check_retcode() + +def aop_stop(): + aop.audio.indirect(SetDeviceProp( + devid='hpai', + modifier=202, + data=Container( + devid='hpai', + cookie=3, + target_pstate='pw1 ', + unk2=1, + ) + )).check_retcode() + + aop.audio.indirect(SetDeviceProp( + devid='hpai', + modifier=202, + data=Container( + devid='hpai', + cookie=4, + target_pstate='idle', + unk2=0, + ) + )).check_retcode() + +def main(): + aop.start() + for epno in [0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x27, 0x28]: + aop.start_ep(epno) + timeout = 5 + while (not aop.audio.ready) and timeout: + aop.work_for(0.1) + timeout -= 1 + if not timeout: + raise Exception("Timed out waiting on audio endpoint") + print("Finished boot") + audep = aop.audio + + audep.roundtrip(AttachDevice(devid='pdm0')).check_retcode() # leap signal processor and etc; leap is low-energy audio processor I think + audep.roundtrip(AttachDevice(devid='hpai')).check_retcode() # high power audio input? actual mic + audep.roundtrip(AttachDevice(devid='lpai')).check_retcode() # low power audio input? seems to be voice trigger mic + audep.roundtrip(SetDeviceProp( + devid='lpai', modifier=301, data=Container(unk1=7, unk2=7, unk3=1, unk4=7)) + ).check_retcode() + audep.indirect(SetDeviceProp( + devid='pdm0', modifier=200, data=pdm_config) + ).check_retcode() + audep.indirect(SetDeviceProp( + devid='pdm0', modifier=210, data=decimator_config) + ).check_retcode() + + dump = """ +00000000 08 00 00 10 00 00 00 00 02 2d 00 00 00 00 00 00 |.........-......| +00000010 00 00 00 00 00 00 00 00 34 00 00 00 02 10 20 00 |........4..... .| +00000020 2d 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 |-...............| +00000030 00 00 00 00 ff ff ff ff 04 00 00 c3 00 00 00 00 |................| +00000040 00 00 00 00 00 00 00 00 00 00 00 00 a0 61 70 68 |.............aph| +00000050 30 00 00 00 00 00 00 00 69 61 70 68 c8 00 00 00 |0.......iaph....| +00000060 01 00 00 00 |.... | +""" + audep.send_ipc(chexundump(dump)) # optional hello. gets "idle" for response + aop.work_for(0.1) + + audep.indirect(SetDeviceProp( + devid='hpai', + modifier=202, + data=Container( + devid='hpai', + cookie=1, + target_pstate='pw1 ', + unk2=0, + ) + )).check_retcode() + +try: + main() + pass +except KeyboardInterrupt: + pass +except Exception: + print(traceback.format_exc()) + +run_shell(locals(), poll_func=aop.work) diff --git a/proxyclient/hv/trace_aop.py b/proxyclient/hv/trace_aop.py index 489c9b34e..100c40618 100644 --- a/proxyclient/hv/trace_aop.py +++ b/proxyclient/hv/trace_aop.py @@ -1,6 +1,5 @@ # SPDX-License-Identifier: MIT -from m1n1.trace import Tracer from m1n1.trace.dart import DARTTracer from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR, EPContainer from m1n1.utils import * @@ -8,11 +7,13 @@ from m1n1.fw.afk.rbep import * from m1n1.fw.afk.epic import * from m1n1.fw.aop import * +from m1n1.fw.aop.afkrb import * +from m1n1.fw.aop.base import * from m1n1.fw.aop.ipc import * import sys -class AFKRingBufSniffer(AFKRingBuf): +class AFKRingBufSniffer(AOPAFKRingBuf): def __init__(self, ep, state, base, size): super().__init__(ep, base, size) self.state = state @@ -24,9 +25,6 @@ def update_rptr(self, rptr): def update_wptr(self): raise NotImplementedError() - def get_wptr(self): - return struct.unpack("= 3: - if True: - self.log(f"===TX DATA=== epid={self.epid} rptr={self.txbuf.state.rptr:#x}") + if getattr(self, 'VERBOSE', False): + self.log(f"===TX DATA=== epid={self.epid:x} rptr={self.txbuf.state.rptr:#x}") chexdump(data) self.log(f"===END DATA===") self.log("Backtrace on TX data:") - self.hv.bt() self.handle_ipc(data, dir=">") return True @@ -96,10 +93,11 @@ def Send(self, msg): def Recv(self, msg): for data in self.rxbuf.read(): #if self.state.verbose >= 3: - if True: - self.log(f"===RX DATA=== epid={self.epid} rptr={self.rxbuf.state.rptr:#x}") + if getattr(self, 'VERBOSE', False): + self.log(f"===RX DATA=== epid={self.epid:x} rptr={self.rxbuf.state.rptr:#x}") chexdump(data) self.log(f"===END DATA===") + #self.hv.bt() self.handle_ipc(data, dir="<") return True @@ -170,16 +168,16 @@ def dispatch_ipc(self, dir, hdr, sub, fd): return self.handle_reply(hdr, sub, fd) def handle_ipc(self, data, dir=None): + if not getattr(self, 'VERBOSE', False): + return + fd = BytesIO(data) hdr = EPICHeader.parse_stream(fd) sub = EPICSubHeaderVer2.parse_stream(fd) - if not getattr(self, 'VERBOSE', False): - return - self.log(f"{dir} 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") - self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") + #self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") if self.dispatch_ipc(dir, hdr, sub, fd): return @@ -196,7 +194,7 @@ def trace_call(self, call): call.dump(self.log) class SPUAppEp(EPICEp): - SHORT = "SPUApp" + SHORT = "spu" class AccelEp(EPICEp): SHORT = "accel" @@ -205,7 +203,8 @@ class GyroEp(EPICEp): SHORT = "gyro" class LASEp(EPICEp): - SHORT = "las" + SHORT = "als" + VERBOSE = True class WakeHintEp(EPICEp): SHORT = "wakehint" @@ -214,12 +213,11 @@ class UNK26Ep(EPICEp): SHORT = "unk26" class AudioEp(EPICEp): - SHORT = "aop-audio" - VERBOSE = True + SHORT = "audio" + #VERBOSE = True class VoiceTriggerEp(EPICEp): - SHORT = "aop-voicetrigger" - VERBOSE = True + SHORT = "voicetrigger" class AOPTracer(ASCTracer, AOPBase): @@ -238,7 +236,7 @@ def __init__(self, hv, devpath, verbose=False): self.default_bootargs = None super().__init__(hv, devpath, verbose) self.u = hv.u - AOPBase.__init__(self, hv.u, self.dev) + AOPBase.__init__(self, hv.u) def start(self, *args): self.default_bootargs = self.read_bootargs() @@ -248,6 +246,7 @@ def w_CPU_CONTROL(self, val): if val.RUN: self.bootargs = self.read_bootargs() self.log("Bootargs patched by AP:") + self.bootargs.dump(self.log) self.default_bootargs.dump_diff(self.bootargs, self.log) self.log("(End of list)") super().w_CPU_CONTROL(val) @@ -326,7 +325,7 @@ def read_rxbuf(icall, ep): data, annot = readdump(l, hdr, f) epid = int(annot["epid"]) epmap[epid].handle_ipc(data, dir) - + if __name__ == "__main__": # We can replay traces by saving the textual output of live tracing @@ -335,15 +334,10 @@ def read_rxbuf(icall, ep): AOPTracer.replay(f) sys.exit(0) -dart_aop_tracer = DARTTracer(hv, "/arm-io/dart-aop", verbose=4) +dart_aop_tracer = DARTTracer(hv, "/arm-io/dart-aop", verbose=0) dart_aop_tracer.start() - -dart_aop_base = u.adt["/arm-io/dart-aop"].get_reg(0)[0] - -#hv.trace_range(irange(*u.adt["/arm-io/dart-aop"].get_reg(1))) -#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(1))) -#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(3))) -#hv.trace_range(irange(*u.adt["/arm-io/admac-aop-audio"].get_reg(0))) - -aop_tracer = AOPTracer(hv, "/arm-io/aop", verbose=1) +aop_tracer = AOPTracer(hv, "/arm-io/aop", verbose=4) aop_tracer.start(dart_aop_tracer.dart) +node = hv.adt["/arm-io/aop"] +for irq in getattr(node, "interrupts"): + hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) diff --git a/proxyclient/m1n1/fw/aop/__init__.py b/proxyclient/m1n1/fw/aop/__init__.py index 682823252..548d2d447 100644 --- a/proxyclient/m1n1/fw/aop/__init__.py +++ b/proxyclient/m1n1/fw/aop/__init__.py @@ -1,31 +1 @@ # SPDX-License-Identifier: MIT -from .bootargs import ASCArgumentSection - -class AOPBase: - def __init__(self, u, adtnode): - self.fw_base, self.fw_len = adtnode.get_reg(2) - if u.adt["arm-io"].compatible[0] == "arm-io,t6000": - # argh - self.fw_base -= 0x2_0000_0000 - - @property - def _bootargs_span(self): - base = self.fw_base + self.u.proxy.read32(self.fw_base + 0x224) - length = self.u.proxy.read32(self.fw_base + 0x228) - - return (base, length) - - def read_bootargs(self): - blob = self.u.proxy.iface.readmem(*self._bootargs_span) - return ASCArgumentSection(blob) - - def write_bootargs(self, args): - base, _ = self._bootargs_span - self.u.proxy.iface.writemem(base, args.to_bytes()) - - def update_bootargs(self, keyvals): - args = self.read_bootargs() - args.update(keyvals) - self.write_bootargs(args) - -__all__ = ["ASCArgumentSection", "AOPBase"] diff --git a/proxyclient/m1n1/fw/aop/afkrb.py b/proxyclient/m1n1/fw/aop/afkrb.py new file mode 100644 index 000000000..f5e2b9f0b --- /dev/null +++ b/proxyclient/m1n1/fw/aop/afkrb.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: MIT +import struct +from ..common import * +from ...utils import * + +AFKRingBufItem = Struct( + "magic" / PaddedString(4, "utf8"), + "size" / Hex(Int32ul), + #"channel" / Hex(Int32ul), # truncated EPIC header + #"type" / EPICType, +) + +# macos reserves first block in ringbuf for r/w pointers +""" bufsize unk +00000000 00007e80 00070006 00000000 00000000 00000000 00000000 00000000 00000000 +00000020 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +00000040 * rptr +00000080 00000600 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +000000a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +000000c0 * wptr +00000100 00000680 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +00000120 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +00000140 * +""" + +class AOPAFKRingBuf(Reloadable): + BLOCK_SIZE = 0x40 + BLOCK_COUNT = 3 + + def __init__(self, ep, base, size): + self.ep = ep + self.base = base + + bs, unk = struct.unpack(" (self.bufsize - self.rptr)): + hdr = self.read_buf(stride, 0x10) + item = AFKRingBufItem.parse(hdr) + self.rptr = 0x10 + assert magic in ["IOP ", "AOP "] + + payload = self.read_buf(stride + self.rptr, item.size) + self.rptr = (align_up(self.rptr + item.size, self.BLOCK_SIZE * 2)) % self.bufsize + self.update_rptr(self.rptr) + yield hdr[8:] + payload + self.wptr = self.get_wptr() + + self.update_rptr(self.rptr) + + def write(self, data): + stride = self.BLOCK_COUNT * self.BLOCK_SIZE * 2 + + self.rptr = self.get_rptr() + + if self.wptr < self.rptr and self.wptr + 0x10 >= self.rptr: + raise AFKError("Ring buffer is full") + + hdr2, data = data[:8], data[8:] + hdr = struct.pack("<4sI", b"IOP ", len(data)) + hdr2 + self.write_buf(stride + self.wptr, hdr) + + if len(data) > (self.bufsize - self.wptr - 0x10): + if self.rptr < 0x10: + raise AFKError("Ring buffer is full") + self.write_buf(stride, hdr) + self.wptr = 0 + + if self.wptr < self.rptr and self.wptr + 0x10 + len(data) >= self.rptr: + raise AFKError("Ring buffer is full") + + self.write_buf(stride + self.wptr + 0x10, data) + self.wptr = align_up(self.wptr + 0x10 + len(data), self.BLOCK_SIZE * 2) % self.bufsize + + self.update_wptr(self.wptr) + return self.wptr diff --git a/proxyclient/m1n1/fw/aop/aopep.py b/proxyclient/m1n1/fw/aop/aopep.py new file mode 100644 index 000000000..f06728648 --- /dev/null +++ b/proxyclient/m1n1/fw/aop/aopep.py @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: MIT +import time +from construct import * +from ..afk.epic import * +from .afkrb import * +from .ipc import * + +class AOPEPICEndpoint(AFKRingBufEndpoint): + RBCLS = AOPAFKRingBuf + BUFSIZE = 0x1000 + + def __init__(self, *args, **kwargs): + self.chan = 0x0 + self.seq = 0x0 + self.wait_reply = False + self.ready = False + super().__init__(*args, **kwargs) + + def handle_hello(self, hdr, sub, fd): + if sub.type != 0xc0: + return False + payload = fd.read() + name = payload.split(b"\0")[0].decode("ascii") + self.log(f"Hello! (endpoint {name})") + self.ready = True + return True + + def handle_reply(self, hdr, sub, fd): + if self.wait_reply: + self.pending_call.read_resp(fd) + self.wait_reply = False + return True + return False + + def handle_ipc(self, data): + fd = BytesIO(data) + hdr = EPICHeader.parse_stream(fd) + sub = EPICSubHeaderVer2.parse_stream(fd) + + handled = False + if sub.category == EPICCategory.REPORT: + handled = self.handle_hello(hdr, sub, fd) + self.chan = hdr.channel + if sub.category == EPICCategory.REPLY: + handled = self.handle_reply(hdr, sub, fd) + + self.log(f"< 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") + self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") + self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") + chexdump(fd.read()) + + return handled + + def indirect(self, call, timeout=0.1): + tx = call.ARGS.build(call.args) + self.asc.iface.writemem(self.txbuf, tx[4:]) + + cmd = self.roundtrip(IndirectCall( + txbuf=self.txbuf_dva, txlen=len(tx) - 4, + rxbuf=self.rxbuf_dva, rxlen=self.BUFSIZE, + retcode=0, + ), category=EPICCategory.COMMAND, typ=call.TYPE) + fd = BytesIO() + fd.write(struct.pack(" len(blob): + raise ValueError('blob overran during parsing') + return dict(fields) + + def items(self): + for key, span in self.index.items(): + off, length = span + yield key, self.blob[off:off + length] + + def __getitem__(self, key): + off, length = self.index[key] + return bytes(self.blob[off:off + length]) + + def __setitem__(self, key, value): + off, length = self.index[key] + if type(value) is int: + value = int.to_bytes(value, length, byteorder='little') + elif type(value) is str: + value = value.encode('ascii') + if len(value) > length: + raise ValueError(f'field {key:s} overflown') + self.blob[off:off + length] = value + + def update(self, keyvals): + for key, val in keyvals.items(): + self[key] = val + + def keys(self): + return self.index.keys() + + def dump(self, logger): + for key, val in self.items(): + logger(f"{key:4s} = {val}") + + def dump_diff(self, other, logger): + assert self.index == other.index + for key in self.keys(): + if self[key] != other[key]: + logger(f"\t{key:4s} = {self[key]} -> {other[key]}") + + def to_bytes(self): + return bytes(self.blob) + +class AOPBase: + def __init__(self, u): + self.u = u + self.nub_base = u.adt["/arm-io/aop/iop-aop-nub"].region_base + if u.adt["arm-io"].compatible[0] == "arm-io,t6000": + # argh + self.nub_base -= 0x2_0000_0000 + + @property + def _bootargs_span(self): + """ + [cpu1] MMIO: R.4 0x24ac0022c (aop[2], offset 0x22c) = 0xaffd8 // offset + [cpu1] MMIO: R.4 0x24ac00230 (aop[2], offset 0x230) = 0x2ae // size + [cpu1] MMIO: R.4 0x24ac00234 (aop[2], offset 0x234) = 0x82000 // va? low + [cpu1] MMIO: R.4 0x24ac00238 (aop[2], offset 0x238) = 0x0 // va? high + [cpu1] MMIO: R.4 0x24ac0023c (aop[2], offset 0x23c) = 0x4ac82000 // phys low + [cpu1] MMIO: R.4 0x24ac00240 (aop[2], offset 0x240) = 0x2 // phys high + [cpu1] MMIO: W.4 0x24acaffd8 (aop[2], offset 0xaffd8) = 0x53544b47 // start of bootargs + [cpu1] MMIO: W.4 0x24acaffdc (aop[2], offset 0xaffdc) = 0x8 + [cpu1] MMIO: W.4 0x24acaffe0 (aop[2], offset 0xaffe0) = 0x73eed2a3 + ... + [cpu1] MMIO: W.4 0x24acb0280 (aop[2], offset 0xb0280) = 0x10000 + [cpu1] MMIO: W.4 0x24acb0284 (aop[2], offset 0xb0284) = 0x0 // end of bootargs + """ + offset = self.u.proxy.read32(self.nub_base + 0x22c) # 0x224 in 12.3 + size = self.u.proxy.read32(self.nub_base + 0x230) # 0x228 in 12.3 + return (self.nub_base + offset, size) + + def read_bootargs(self): + addr, size = self._bootargs_span + blob = self.u.proxy.iface.readmem(addr, size) + return AOPBootargs(blob) + + def write_bootargs(self, args): + base, _ = self._bootargs_span + self.u.proxy.iface.writemem(base, args.to_bytes()) + + def update_bootargs(self, keyval, logger=print): + args = self.read_bootargs() + old = deepcopy(args) + args.update(keyval) + self.write_bootargs(args) + old.dump_diff(args, logger) diff --git a/proxyclient/m1n1/fw/aop/bootargs.py b/proxyclient/m1n1/fw/aop/bootargs.py deleted file mode 100644 index 100e4f304..000000000 --- a/proxyclient/m1n1/fw/aop/bootargs.py +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-License-Identifier: MIT - -class ASCArgumentSection: - def __init__(self, bytes_): - self.blob = bytearray(bytes_) - self.index = self.build_index() - - def build_index(self): - off = 0 - fields = [] - while off < len(self.blob): - snip = self.blob[off:] - key = snip[0:4] - length = int.from_bytes(snip[4:8], byteorder='little') - fields.append((key.decode('ascii'), (off + 8, length))) - off += 8 + length - - if off > len(self.blob): - raise ValueError('blob overran during parsing') - - return dict(fields) - - def items(self): - for key, span in self.index.items(): - off, length = span - yield key, self.blob[off:off + length] - - def __getitem__(self, key): - off, length = self.index[key] - return bytes(self.blob[off:off + length]) - - def __setitem__(self, key, value): - off, length = self.index[key] - - if type(value) is int: - value = int.to_bytes(value, length, byteorder='little') - elif type(value) is str: - value = value.encode('ascii') - - if len(value) > length: - raise ValueError(f'field {key:s} overflown') - - self.blob[off:off + length] = value - - def update(self, keyvals): - for key, val in keyvals.items(): - self[key] = val - - def keys(self): - return self.index.keys() - - def dump(self): - for key, val in self.items(): - print(f"{key:4s} = {val}") - - def dump_diff(self, other, logger): - assert self.index == other.index - - for key in self.keys(): - if self[key] != other[key]: - logger(f"\t{key:4s} = {self[key]} -> {other[key]}") - - def to_bytes(self): - return bytes(self.blob) diff --git a/proxyclient/m1n1/fw/aop/client.py b/proxyclient/m1n1/fw/aop/client.py new file mode 100644 index 000000000..4596471fa --- /dev/null +++ b/proxyclient/m1n1/fw/aop/client.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +from ...utils import * + +from ..asc import StandardASC +from .aopep import * +from .base import AOPBase + +class AOPClient(StandardASC, AOPBase): + ENDPOINTS = { + 0x20: AOPSPUEndpoint, + 0x21: AOPAccelEndpoint, + 0x22: AOPGyroEndpoint, + 0x24: AOPALSEndpoint, + 0x25: AOPWakehintEndpoint, + 0x26: AOPUNK26Endpoint, + 0x27: AOPAudioEndpoint, + 0x28: AOPVoiceTriggerEndpoint, + } + def __init__(self, u, dev_path, dart=None): + node = u.adt[dev_path] + asc_base = node.get_reg(0)[0] + AOPBase.__init__(self, u) + super().__init__(u, asc_base, dart) + self.dart = dart diff --git a/proxyclient/m1n1/fw/aop/ipc.py b/proxyclient/m1n1/fw/aop/ipc.py index d26315d1d..4b5e3cc76 100644 --- a/proxyclient/m1n1/fw/aop/ipc.py +++ b/proxyclient/m1n1/fw/aop/ipc.py @@ -4,8 +4,7 @@ from m1n1.utils import FourCC, chexdump from m1n1.constructutils import ZPadding -from m1n1.fw.afk.epic import EPICCmd, EPICCategory - +from m1n1.fw.afk.epic import * EPICSubHeaderVer2 = Struct( "length" / Int32ul, @@ -17,22 +16,28 @@ "unk2" / Default(Hex(Int32ul), 0), ) -class AOPAudioPropKey(IntEnum): - IS_READY = 0x01 - - UNK_11 = 0x11 - PLACEMENT = 0x1e - UNK_21 = 0x21 - ORIENTATION = 0x2e - LOCATION_ID = 0x30 - SERIAL_NO = 0x3e - VENDOR_ID = 0x5a - PRODUCT_ID = 0x5b +AOPEPICHeader = Struct( + # ringbuf header + # "magic" / PaddedString(4, "utf8"), + # "size" / Hex(Int32ul), + # EPIC header + "header" / EPICHeader, + "subheader" / EPICSubHeaderVer2, +) +class AOPAudioPropKey(IntEnum): + IS_READY = 0x01 + UNK_11 = 0x11 + PLACEMENT = 0x1e + UNK_21 = 0x21 + ORIENTATION = 0x2e + LOCATION_ID = 0x30 + SERIAL_NO = 0x3e + VENDOR_ID = 0x5a + PRODUCT_ID = 0x5b SERVICE_CONTROLLER = 0x64 DEVICE_COUNT = 0x65 - - VERSION = 0x67 + VERSION = 0x67 class EPICCall: @classmethod @@ -155,38 +160,38 @@ class ProbeDevice(WrappedCall): "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000001, Int32ul)), - "blank2" / ZPadding(16), - "pad" / Padding(4), + "pad" / ZPadding(16), + "unk" / Hex(Int32ul), "len" / Hex(Const(0x28, Int64ul)), "devno" / Int32ul, ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "devid" / FourCC, - "blank2" / Const(0x0, Int32ul), - "unk1" / Const(8, Int32ul), - "blank3" / Const(0x0, Int32ul), - "unk2" / Hex(Const(0x01_0d_1c_20, Int32ul)), - "blank4" / Const(0x0, Int32ul), + "unk0" / Default(Hex(Int32ul), 0), + "unk1" / Default(Hex(Int32ul), 0), + "blank3" / Default(Hex(Int32ul), 0), + "unk2" / Default(Hex(Int32ul), 0), + "blank4" / Default(Hex(Int32ul), 0), "remainder" / HexDump(GreedyBytes), ) PDMConfig = Struct( - "unk1" / Int32ul, + "bytesPerSample" / Int32ul, "clockSource" / FourCC, "pdmFrequency" / Int32ul, - "unk3_clk" / Int32ul, - "unk4_clk" / Int32ul, - "unk5_clk" / Int32ul, - "channelPolaritySelect" / Hex(Int32ul), - "unk7" / Hex(Int32ul), + "pdmcFrequency" / Int32ul, + "slowClockSpeed" / Int32ul, + "fastClockSpeed" / Int32ul, + "channelPolaritySelect" / Int32ul, + "channelPhaseSelect" / Int32ul, "unk8" / Hex(Int32ul), "unk9" / Hex(Int16ul), "ratios" / Struct( "r1" / Int8ul, "r2" / Int8ul, "r3" / Int8ul, - "pad" / Default(Int8ul, 0), + "pad" / Const(0, Int8ul), ), "filterLengths" / Hex(Int32ul), "coeff_bulk" / Int32ul, @@ -240,7 +245,7 @@ class ProbeDevice(WrappedCall): PowerSetting = Struct( "devid" / FourCC, "cookie" / Int32ul, - "pad" / Padding(4), + "unk" / Default(Hex(Int32ul), 0), "blank" / ZPadding(8), "target_pstate" / FourCC, "unk2" / Int32ul, @@ -270,7 +275,7 @@ class GetDeviceProp(WrappedCall): "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000004, Int32ul)), "blank2" / ZPadding(16), - "pad" / Padding(4), + "unk" / Hex(Int32ul), "len" / Hex(Const(0x30, Int64ul)), "devid" / FourCC, "modifier" / Int32ul, @@ -297,7 +302,7 @@ class SetDeviceProp(WrappedCall): "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000005, Int32ul)), "blank2" / ZPadding(16), - "pad" / Padding(4), + "unk" / Default(Hex(Int32ul), 0), "len" / Hex(Int64ul), # len(this.data) + 0x30 "devid" / FourCC, "modifier" / Int32ul,