mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-21 22:23:05 +00:00
aop: Stream high-power mic samples to ADMAC
Output audio format still unknown, not sure if it's garbage (see lpai commit) or some weird packed float encoding I'm not figuring out. Signed-off-by: Eileen Yoon <eyn@gmx.com>
This commit is contained in:
parent
0ebc339761
commit
2b62c08619
3 changed files with 284 additions and 310 deletions
|
@ -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("<I", cmd.rets.retcode))
|
|
||||||
fd.write(self.asc.iface.readmem(self.rxbuf, cmd.rets.rxlen))
|
|
||||||
fd.seek(0)
|
|
||||||
call.read_resp(fd)
|
|
||||||
return call
|
|
||||||
|
|
||||||
def roundtrip(self, call, chan=0x1000000d, timeout=0.3,
|
|
||||||
category=EPICCategory.NOTIFY, typ=None):
|
|
||||||
tx = call.ARGS.build(call.args)
|
|
||||||
fd = BytesIO()
|
|
||||||
fd.write(EPICHeader.build(Container(
|
|
||||||
channel=chan,
|
|
||||||
type=EPICType.NOTIFY,
|
|
||||||
version=2,
|
|
||||||
seq=self.seq,
|
|
||||||
)))
|
|
||||||
self.seq += 1
|
|
||||||
fd.write(EPICSubHeaderVer2.build(Container(
|
|
||||||
length=len(tx),
|
|
||||||
category=category,
|
|
||||||
type=typ or call.TYPE,
|
|
||||||
)))
|
|
||||||
fd.write(tx)
|
|
||||||
|
|
||||||
self.pending_call = call
|
|
||||||
self.wait_reply = True
|
|
||||||
self.send_ipc(fd.getvalue())
|
|
||||||
|
|
||||||
deadline = time.time() + timeout
|
|
||||||
while time.time() < deadline and self.wait_reply:
|
|
||||||
self.asc.work()
|
|
||||||
if self.wait_reply:
|
|
||||||
self.wait_reply = False
|
|
||||||
raise ASCTimeout("ASC reply timed out")
|
|
||||||
|
|
||||||
return call
|
|
||||||
|
|
||||||
class SPUAppEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "SPUAppep"
|
|
||||||
|
|
||||||
class AccelEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "accelep"
|
|
||||||
|
|
||||||
class GyroEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "gyroep"
|
|
||||||
|
|
||||||
class UNK23Endpoint(EPICEndpoint):
|
|
||||||
SHORT = "unk23ep"
|
|
||||||
|
|
||||||
class LASEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "lasep"
|
|
||||||
#VERBOSE = True # <--- uncomment to see lid angle measurements
|
|
||||||
|
|
||||||
class WakehintEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "wakehintep"
|
|
||||||
|
|
||||||
class UNK26Endpoint(EPICEndpoint):
|
|
||||||
SHORT = "unk26ep"
|
|
||||||
|
|
||||||
class AudioEndpoint(EPICEndpoint):
|
|
||||||
SHORT = "audioep"
|
|
||||||
|
|
||||||
|
|
||||||
class OSLogMessage(Register64):
|
|
||||||
TYPE = 63, 56
|
|
||||||
|
|
||||||
class OSLog_Init(OSLogMessage):
|
|
||||||
TYPE = 63, 56, Constant(1)
|
|
||||||
UNK = 51, 0
|
|
||||||
DVA = 7, 0
|
|
||||||
|
|
||||||
class AOPOSLogEndpoint(ASCBaseEndpoint):
|
|
||||||
BASE_MESSAGE = OSLogMessage
|
|
||||||
SHORT = "oslog"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.started = False
|
|
||||||
|
|
||||||
@msg_handler(1, OSLog_Init)
|
|
||||||
def Init(self, msg):
|
|
||||||
self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(0x1_0000)
|
|
||||||
self.send(OSLog_Init(DVA=self.iobuffer_dva//0x1000))
|
|
||||||
self.started = True
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class AOPClient(StandardASC, AOPBase):
|
|
||||||
ENDPOINTS = {
|
|
||||||
8: AOPOSLogEndpoint,
|
|
||||||
|
|
||||||
0x20: SPUAppEndpoint,
|
|
||||||
0x21: AccelEndpoint,
|
|
||||||
0x22: GyroEndpoint,
|
|
||||||
0x23: UNK23Endpoint,
|
|
||||||
0x24: LASEndpoint,
|
|
||||||
0x25: WakehintEndpoint,
|
|
||||||
0x26: UNK26Endpoint,
|
|
||||||
0x27: AudioEndpoint,
|
|
||||||
0x28: EPICEndpoint,
|
|
||||||
0x29: EPICEndpoint,
|
|
||||||
0x2a: EPICEndpoint,
|
|
||||||
0x2b: EPICEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, u, adtpath, dart=None):
|
|
||||||
node = u.adt[adtpath]
|
|
||||||
self.base = node.get_reg(0)[0]
|
|
||||||
|
|
||||||
AOPBase.__init__(self, u, node)
|
|
||||||
super().__init__(u, self.base, dart)
|
|
||||||
|
|
||||||
p.dapf_init_all()
|
|
||||||
|
|
||||||
dart = DART.from_adt(u, "/arm-io/dart-aop", iova_range=(0x2c000, 0x10_000_000))
|
|
||||||
dart.initialize()
|
|
||||||
dart.regs.TCR[0].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1)
|
|
||||||
dart.regs.TCR[7].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1)
|
|
||||||
dart.regs.TCR[15].val = 0x20100
|
|
||||||
|
|
||||||
aop = AOPClient(u, "/arm-io/aop", dart)
|
|
||||||
|
|
||||||
aop.update_bootargs({
|
|
||||||
'p0CE': 0x20000,
|
|
||||||
# 'laCn': 0x0,
|
|
||||||
# 'tPOA': 0x1,
|
|
||||||
})
|
|
||||||
|
|
||||||
aop.verbose = 4
|
|
||||||
|
|
||||||
def set_aop_audio_pstate(devid, pstate):
|
|
||||||
audep.roundtrip(SetDeviceProp(
|
|
||||||
devid=devid,
|
|
||||||
modifier=202,
|
|
||||||
data=Container(
|
|
||||||
devid=devid,
|
|
||||||
cookie=1,
|
|
||||||
target_pstate=pstate,
|
|
||||||
unk2=1,
|
|
||||||
)
|
|
||||||
)).check_retcode()
|
|
||||||
|
|
||||||
try:
|
|
||||||
aop.boot()
|
|
||||||
for epno in range(0x20, 0x2c):
|
|
||||||
aop.start_ep(epno)
|
|
||||||
|
|
||||||
timeout = 10
|
|
||||||
while (not aop.audioep.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.audioep
|
|
||||||
|
|
||||||
audep.roundtrip(AttachDevice(devid='pdm0')).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()
|
|
||||||
audep.roundtrip(AttachDevice(devid='hpai')).check_retcode()
|
|
||||||
audep.roundtrip(AttachDevice(devid='lpai')).check_retcode()
|
|
||||||
audep.roundtrip(SetDeviceProp(
|
|
||||||
devid='lpai', modifier=301, data=Container(unk1=7, unk2=7, unk3=1, unk4=7))
|
|
||||||
).check_retcode()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
run_shell(locals(), poll_func=aop.work)
|
|
170
proxyclient/experiments/aop_audio.py
Executable file
170
proxyclient/experiments/aop_audio.py
Executable file
|
@ -0,0 +1,170 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
import sys, pathlib
|
||||||
|
sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
|
import struct
|
||||||
|
from construct import *
|
||||||
|
|
||||||
|
from m1n1.setup import *
|
||||||
|
from m1n1.shell import run_shell
|
||||||
|
from m1n1.hw.dart import DART
|
||||||
|
from m1n1.fw.aop.client import AOPClient
|
||||||
|
from m1n1.fw.aop.ipc import *
|
||||||
|
|
||||||
|
# on the first terminal: M1N1DEVICE=/dev/ttyACM0 python3 experiments/aop_audio.py
|
||||||
|
# at that point (hpai at pw1), aop powers on admac and you should be dropped into a shell
|
||||||
|
# on a second terminal in parallel: M1N1DEVICE=/dev/ttyACM1 tools/admac_stream.py --node admac-aop-audio --channel 1 -v | xxd -g 4 -c 12 -e
|
||||||
|
# inside the shell (on the first terminal), aop_start()
|
||||||
|
# there should now be microphone samples streamed to admac
|
||||||
|
# see povik's commit #fc66046
|
||||||
|
|
||||||
|
# aop nodes have no clocks described in adt for j293. it does it itself
|
||||||
|
p.pmgr_adt_clocks_enable("/arm-io/aop")
|
||||||
|
p.pmgr_adt_clocks_enable("/arm-io/dart-aop")
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
pdm2 = u.adt["/arm-io/aop/iop-aop-nub/aop-audio/audio-pdm2"]
|
||||||
|
decm = u.adt["/arm-io/aop/iop-aop-nub/aop-audio/dc-2400000"]
|
||||||
|
|
||||||
|
pdm_config = Container(
|
||||||
|
bytesPerSample=pdm2.bytesPerSample, # 2 ??
|
||||||
|
clockSource=pdm2.clockSource, # 'pll '
|
||||||
|
pdmFrequency=pdm2.pdmFrequency, # 2400000
|
||||||
|
pdmcFrequency=pdm2.pdmcFrequency, # 24000000
|
||||||
|
slowClockSpeed=pdm2.slowClockSpeed, # 24000000
|
||||||
|
fastClockSpeed=pdm2.fastClockSpeed, # 24000000
|
||||||
|
channelPolaritySelect=pdm2.channelPolaritySelect, # 256
|
||||||
|
channelPhaseSelect=pdm2.channelPhaseSelect, # traces say 99 but device tree says 0
|
||||||
|
unk8=0xf7600,
|
||||||
|
unk9=0, # this should be latency (thus 15, see below) but traces say 0
|
||||||
|
ratios=Container(
|
||||||
|
r1=decm.ratios.r0,
|
||||||
|
r2=decm.ratios.r1,
|
||||||
|
r3=decm.ratios.r2,
|
||||||
|
),
|
||||||
|
filterLengths=decm.filterLengths,
|
||||||
|
coeff_bulk=120,
|
||||||
|
coefficients=GreedyRange(Int32sl).parse(decm.coefficients),
|
||||||
|
unk10=1,
|
||||||
|
micTurnOnTimeMs=pdm2.micTurnOnTimeMs, # 20
|
||||||
|
unk11=1,
|
||||||
|
micSettleTimeMs=pdm2.micSettleTimeMs, # 50
|
||||||
|
)
|
||||||
|
|
||||||
|
decimator_config = Container(
|
||||||
|
latency=decm.latency, # 15
|
||||||
|
ratios=Container(
|
||||||
|
r1=decm.ratios.r0, # 15
|
||||||
|
r2=decm.ratios.r1, # 5
|
||||||
|
r3=decm.ratios.r2, # 2
|
||||||
|
),
|
||||||
|
filterLengths=decm.filterLengths,
|
||||||
|
coeff_bulk=120,
|
||||||
|
coefficients=GreedyRange(Int32sl).parse(decm.coefficients),
|
||||||
|
)
|
||||||
|
|
||||||
|
dart = DART.from_adt(u, "/arm-io/dart-aop",
|
||||||
|
iova_range=(u.adt["/arm-io/dart-aop"].vm_base, 0x1000000000))
|
||||||
|
dart.initialize()
|
||||||
|
|
||||||
|
aop = AOPClient(u, "/arm-io/aop", dart)
|
||||||
|
aop.update_bootargs({
|
||||||
|
'p0CE': 0x20000,
|
||||||
|
'laCn': 0x0,
|
||||||
|
'tPOA': 0x1,
|
||||||
|
"gila": 0x80,
|
||||||
|
})
|
||||||
|
aop.verbose = 4
|
||||||
|
|
||||||
|
p.dapf_init_all()
|
||||||
|
aop.asc.OUTBOX_CTRL.val = 0x20001 # (FIFOCNT=0x0, OVERFLOW=0, EMPTY=1, FULL=0, RPTR=0x0, WPTR=0x0, ENABLE=1)
|
||||||
|
|
||||||
|
# incredible power state naming scheme:
|
||||||
|
# idle: in sleep state (default at boot)
|
||||||
|
# pw1 : in active state (admac powers on) but not capturing
|
||||||
|
# pwrd: start capturing
|
||||||
|
# to start capturing, we put the 'hpai' (high power audio input?) device
|
||||||
|
# from idle -> pw1 -> pwrd state. it starts capturing at pwrd
|
||||||
|
# shutdown sequence must also be pwrd -> pw1 -> idle
|
||||||
|
|
||||||
|
def aop_start():
|
||||||
|
aop.audio.send_notify(SetDeviceProp(
|
||||||
|
devid=u'hpai',
|
||||||
|
modifier=202,
|
||||||
|
data=Container(
|
||||||
|
devid=u'hpai',
|
||||||
|
cookie=2,
|
||||||
|
target_pstate=u'pwrd',
|
||||||
|
unk2=1,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
def aop_stop():
|
||||||
|
aop.audio.send_notify(SetDeviceProp(
|
||||||
|
devid='hpai',
|
||||||
|
modifier=202,
|
||||||
|
data=Container(
|
||||||
|
devid='hpai',
|
||||||
|
cookie=3,
|
||||||
|
target_pstate='pw1 ',
|
||||||
|
unk2=1,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
aop.audio.send_notify(SetDeviceProp(
|
||||||
|
devid='hpai',
|
||||||
|
modifier=202,
|
||||||
|
data=Container(
|
||||||
|
devid='hpai',
|
||||||
|
cookie=4,
|
||||||
|
target_pstate='idle',
|
||||||
|
unk2=0,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
aop.start()
|
||||||
|
for epno in [0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x27, 0x28]:
|
||||||
|
aop.start_ep(epno)
|
||||||
|
aop.work_for(0.3)
|
||||||
|
audep = aop.audio
|
||||||
|
|
||||||
|
audep.send_notify(AttachDevice(devid='hpai')) # high power audio input; actual mic
|
||||||
|
audep.send_notify(AttachDevice(devid='lpai')) # low power audio input; voice trigger mic
|
||||||
|
audep.send_notify(AttachDevice(devid='pdm0')) # leap: low-energy audio processor I think
|
||||||
|
# [syslog] * [udioPDMNodeBase.cpp:554]PDMDev<pdm0> off ->xi0 , 0->2400000 AP state 1
|
||||||
|
|
||||||
|
# initChannelControl (<7, 7, 1, 7>)
|
||||||
|
audep.send_notify(SetDeviceProp(devid='lpai', modifier=301, data=Container(unk1=7, unk2=7, unk3=1, unk4=7)))
|
||||||
|
audep.send_notify(SetDeviceProp(devid='pdm0', modifier=200, data=pdm_config))
|
||||||
|
audep.send_notify(SetDeviceProp(devid='pdm0', modifier=210, data=decimator_config))
|
||||||
|
ret = audep.send_roundtrip(AudioPropertyState(devid="hpai"))
|
||||||
|
print("hpai state: %s" % (ret.state)) # idle
|
||||||
|
|
||||||
|
audep.send_notify(SetDeviceProp(
|
||||||
|
devid='hpai',
|
||||||
|
modifier=202,
|
||||||
|
data=Container(
|
||||||
|
devid='hpai',
|
||||||
|
cookie=1,
|
||||||
|
target_pstate='pw1 ',
|
||||||
|
unk2=0,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
ret = audep.send_roundtrip(AudioPropertyState(devid="hpai"))
|
||||||
|
print("hpai state: %s" % (ret.state))
|
||||||
|
assert(ret.state == "pw1 ")
|
||||||
|
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
pass
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
run_shell(locals(), poll_func=aop.work)
|
|
@ -118,6 +118,30 @@ class WrappedCall(EPICCall):
|
||||||
self.dump()
|
self.dump()
|
||||||
raise ValueError(f"retcode {self.rets.retcode} in {str(type(self))} (call dumped, see above)")
|
raise ValueError(f"retcode {self.rets.retcode} in {str(type(self))} (call dumped, see above)")
|
||||||
|
|
||||||
|
@reg_calltype
|
||||||
|
class AudioProbeDevice(EPICCall):
|
||||||
|
TYPE = 0x20
|
||||||
|
SUBTYPE = 0xc3_00_00_01
|
||||||
|
ARGS = Struct(
|
||||||
|
"pad" / Const(0x0, Int32ul),
|
||||||
|
"unk1" / Hex(Const(0xffffffff, Int32ul)),
|
||||||
|
"subtype" / Hex(Const(0xc3000001, Int32ul)),
|
||||||
|
"pad2" / ZPadding(16),
|
||||||
|
"cookie" / Default(Hex(Int32ul), 0),
|
||||||
|
"len" / Hex(Const(0x28, Int64ul)),
|
||||||
|
"devno" / Int32ul,
|
||||||
|
)
|
||||||
|
RETS = Struct(
|
||||||
|
"retcode" / Const(0x0, Int32ul),
|
||||||
|
"devid" / FourCC,
|
||||||
|
"format" / FourCC,
|
||||||
|
"sample_rate" / Int32ul,
|
||||||
|
"channels" / Int32ul,
|
||||||
|
"bytes_per_sample" / Int32ul,
|
||||||
|
"unk_1f" / Default(Hex(Int32ul), 0),
|
||||||
|
"unk" / Array(51, Default(Hex(Int32ul), 0)),
|
||||||
|
)
|
||||||
|
|
||||||
@WrappedCall.reg_subclass
|
@WrappedCall.reg_subclass
|
||||||
class AttachDevice(WrappedCall):
|
class AttachDevice(WrappedCall):
|
||||||
CALLTYPE = 0xc3_00_00_02
|
CALLTYPE = 0xc3_00_00_02
|
||||||
|
@ -246,6 +270,96 @@ DEVPROPS = {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AudioPropertyKey(IntEnum):
|
||||||
|
STATE = 200 # 0xc8
|
||||||
|
POWER = 202 # 0xca
|
||||||
|
MAIN = 203 # 0xcb
|
||||||
|
FORMAT = 302 # 0x12e
|
||||||
|
|
||||||
|
@reg_calltype
|
||||||
|
class AudioProperty(EPICCall):
|
||||||
|
TYPE = 0x20
|
||||||
|
SUBTYPE = 0xc3000004
|
||||||
|
SUBCLASSES = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def subclass(cls, cls2):
|
||||||
|
cls.SUBCLASSES[int(cls2.SUBTYPE)] = cls2
|
||||||
|
return cls2
|
||||||
|
|
||||||
|
@AudioProperty.subclass
|
||||||
|
class AudioPropertyState(AudioProperty):
|
||||||
|
SUBSUBTYPE = AudioPropertyKey.STATE
|
||||||
|
ARGS = Struct(
|
||||||
|
"pad" / Const(0x0, Int32ul),
|
||||||
|
"unk1" / Hex(Const(0xffffffff, Int32ul)),
|
||||||
|
"calltype" / Hex(Const(0xc3000004, Int32ul)),
|
||||||
|
"blank2" / ZPadding(16),
|
||||||
|
"cookie" / Default(Hex(Int32ul), 0),
|
||||||
|
"len" / Hex(Const(0x30, Int64ul)),
|
||||||
|
"devid" / FourCC,
|
||||||
|
"modifier" / Const(AudioPropertyKey.STATE, Int32ul),
|
||||||
|
"data" / Const(0x1, Int32ul),
|
||||||
|
)
|
||||||
|
RETS = Struct(
|
||||||
|
"retcode" / Const(0x0, Int32ul),
|
||||||
|
"size" / Const(4, Int32ul),
|
||||||
|
"state" / FourCC,
|
||||||
|
)
|
||||||
|
|
||||||
|
@AudioProperty.subclass
|
||||||
|
class AudioPropertyFormat(AudioProperty):
|
||||||
|
SUBSUBTYPE = AudioPropertyKey.FORMAT
|
||||||
|
ARGS = Struct(
|
||||||
|
"pad" / Const(0x0, Int32ul),
|
||||||
|
"unk1" / Hex(Const(0xffffffff, Int32ul)),
|
||||||
|
"calltype" / Hex(Const(0xc3000004, Int32ul)),
|
||||||
|
"blank2" / ZPadding(16),
|
||||||
|
"cookie" / Default(Hex(Int32ul), 0),
|
||||||
|
"len" / Hex(Const(0x30, Int64ul)),
|
||||||
|
"devid" / FourCC,
|
||||||
|
"modifier" / Const(AudioPropertyKey.FORMAT, Int32ul),
|
||||||
|
"data" / Const(0x1, Int32ul),
|
||||||
|
)
|
||||||
|
RETS = Struct(
|
||||||
|
"retcode" / Const(0x0, Int32ul),
|
||||||
|
"format" / Int32ul, # 16 == float32?
|
||||||
|
"fourcc" / FourCC, # PCML
|
||||||
|
"sample_rate" / Int32ul, # 16000
|
||||||
|
"channels" / Int32ul, # 3
|
||||||
|
"bytes_per_sample" / Int32ul, # 2
|
||||||
|
)
|
||||||
|
|
||||||
|
AudioPowerSetting = Struct(
|
||||||
|
"devid" / FourCC,
|
||||||
|
"unk1" / Int32ul,
|
||||||
|
"cookie" / Default(Hex(Int32ul), 0),
|
||||||
|
"blank" / ZPadding(8),
|
||||||
|
"target_pstate" / FourCC,
|
||||||
|
"unk2" / Int32ul,
|
||||||
|
"blank2" / ZPadding(20),
|
||||||
|
)
|
||||||
|
|
||||||
|
@AudioProperty.subclass
|
||||||
|
class AudioPropertyPower(AudioProperty):
|
||||||
|
SUBSUBTYPE = AudioPropertyKey.POWER
|
||||||
|
ARGS = Struct(
|
||||||
|
"pad" / Const(0x0, Int32ul),
|
||||||
|
"unk1" / Hex(Const(0xffffffff, Int32ul)),
|
||||||
|
"calltype" / Hex(Const(0xc3000005, Int32ul)),
|
||||||
|
"blank2" / ZPadding(16),
|
||||||
|
"cookie" / Default(Hex(Int32ul), 0),
|
||||||
|
"len" / Hex(Const(0x30 + 0x30, Int64ul)), # len(this.data) + 0x30
|
||||||
|
"devid" / FourCC,
|
||||||
|
"modifier" / Const(AudioPropertyKey.POWER, Int32ul),
|
||||||
|
"len2" / Hex(Const(0x30, Int32ul)), # len(this.data)
|
||||||
|
"data" / AudioPowerSetting,
|
||||||
|
)
|
||||||
|
RETS = Struct(
|
||||||
|
"retcode" / Const(0x0, Int32ul),
|
||||||
|
"value" / HexDump(GreedyBytes),
|
||||||
|
)
|
||||||
|
|
||||||
@WrappedCall.reg_subclass
|
@WrappedCall.reg_subclass
|
||||||
class GetDeviceProp(WrappedCall):
|
class GetDeviceProp(WrappedCall):
|
||||||
CALLTYPE = 0xc3_00_00_04
|
CALLTYPE = 0xc3_00_00_04
|
||||||
|
|
Loading…
Reference in a new issue