afk.epic: Handle EPIC replies by subtype

DCP only gets two reply types: a u32 retcode or a command reply. It
previously checked for retcode by checking length(data) == 4 and
assuming command otherwise. AOP can get other reply types (e.g. STRING
after probing device). It actually sends us the reply subtype in the
EPICSubHeader, so use that information and handle replies accordingly.

Signed-off-by: Eileen Yoon <eyn@gmx.com>
This commit is contained in:
Eileen Yoon 2024-01-23 20:09:14 +09:00 committed by Hector Martin
parent 6426bdf6a5
commit 5f59c5460c

View file

@ -26,7 +26,10 @@ EPICCategory = "EPICCategory" / Enum(Int8ul,
EPICSubtype = "EPICSubtype" / Enum(Int16ul, EPICSubtype = "EPICSubtype" / Enum(Int16ul,
ANNOUNCE = 0x30, ANNOUNCE = 0x30,
TEARDOWN = 0x32, TEARDOWN = 0x32,
RETCODE_WITH_PAYLOAD = 0xa0,
STD_SERVICE = 0xc0, STD_SERVICE = 0xc0,
RETCODE = 0x84,
STRING = 0x8a,
) )
EPICHeader = Struct( EPICHeader = Struct(
@ -132,24 +135,44 @@ class EPICService:
def handle_reply(self, category, type, seq, fd): def handle_reply(self, category, type, seq, fd):
off = fd.tell() off = fd.tell()
data = fd.read() data = fd.read()
if len(data) == 4:
retcode = struct.unpack("<I", data)[0] ret = data
if retcode: if (hasattr(self, "last_call") and hasattr(self.last_call, "RETS")):
raise EPICError(f"IOP returned errcode {retcode:#x}") call = getattr(self, "last_call")
else: item = call.RETS.parse(data)
self.reply = retcode ret = item
return self.reply = ret
fd.seek(off) return
cmd = EPICCmd.parse_stream(fd)
payload = fd.read() if (type == EPICSubtype.RETCODE):
self.log(f"Response {type:#x} #{seq}: {cmd.retcode:#x}") rc = struct.unpack("<I", data)[0]
if cmd.retcode != 0: self.log(f"Retcode #{seq}: {rc:#x}")
raise EPICError(f"IOP returned errcode {cmd.retcode:#x}") self.reply = ret
if payload: return
self.log("Inline payload:") elif (type == EPICSubtype.STD_SERVICE):
chexdump(payload) fd.seek(off)
assert cmd.rxbuf == self.rxbuf_dva cmd = EPICCmd.parse_stream(fd)
self.reply = self.iface.readmem(self.rxbuf, cmd.rxlen) payload = fd.read()
self.log(f"Response {int(type):#x} #{seq}: {cmd.retcode:#x}")
if cmd.retcode != 0:
raise EPICError(f"IOP returned errcode {cmd.retcode:#x}")
if payload:
self.log("Inline payload:")
chexdump(payload)
assert cmd.rxbuf == self.rxbuf_dva
self.reply = self.iface.readmem(self.rxbuf, cmd.rxlen)
return
elif (type == EPICSubtype.RETCODE_WITH_PAYLOAD):
rc = struct.unpack("<I", data[:4])[0]
self.log(f"Retcode #{seq}: {rc:#x}")
assert(rc == 0)
self.reply = ret
return
else:
self.log(f"#{seq}: Unknown reply type: {int(type):#x}")
chexdump(data)
self.reply = ret
return
def handle_cmd(self, category, type, seq, fd): def handle_cmd(self, category, type, seq, fd):
cmd = EPICCmd.parse_stream(fd) cmd = EPICCmd.parse_stream(fd)