diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py
index b2be0eef6c824a208f02611c83928969d3051c5b..c3b927474d67ad34fe674b87902270aa9ac2e87c 100644
--- a/ebpfcat/ebpfcat.py
+++ b/ebpfcat/ebpfcat.py
@@ -49,11 +49,31 @@ class PacketDesc:
         else:
             return ret.get(device)
 
-    def __set__(self, instance, value):
-        offset = instance.position_offset[self.sm]
-        ret = PacketVar(instance.terminal, self.sm, self.position + offset,
-                        self.size)
-        return ret.set(instance.device, value)
+
+class ProcessDesc:
+    def __init__(self, index, subindex, size=None):
+        self.index = index
+        self.subindex = subindex
+        self.size = size
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        index = self.index + instance.position_offset[3]
+        if isinstance(instance, Struct):
+            terminal = instance.terminal
+            device = instance.device
+        else:
+            terminal = instance
+            device = None
+        sm, offset, size = terminal.pdos[index, self.subindex]
+        if self.size is not None:
+            size = self.size
+        ret = PacketVar(terminal, sm, offset, size)
+        if device is None:
+            return ret
+        else:
+            return ret.get(device)
 
 
 class PacketVar(MemoryDesc):
@@ -218,6 +238,10 @@ class EBPFTerminal(Terminal):
             raise RuntimeError(
                 f"Incompatible Terminal: {self.vendorId}:{self.productCode} "
                 f"({relative}, {absolute})")
+        await self.to_operational()
+        self.pdos = {}
+        if self.has_mailbox():
+            await self.parse_pdos()
 
     def allocate(self, packet, readonly):
         """allocate space in packet for the pdos of this terminal
@@ -355,7 +379,6 @@ class SyncGroup(SyncGroupBase):
     current_data = False  # None is used to indicate FastSyncGroup
 
     async def run(self):
-        await gather(*[t.to_operational() for t in self.terminals])
         self.current_data = self.asm_packet
         while True:
             self.ec.send_packet(self.current_data)
@@ -404,9 +427,6 @@ class FastSyncGroup(SyncGroupBase, XDP):
     def start(self):
         self.allocate()
         self.ec.register_sync_group(self, self.packet)
-        self.monitor = ensure_future(gather(*[t.to_operational()
-                                              for t in self.terminals]))
-        return self.monitor
 
     def allocate(self):
         self.packet = Packet()
diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py
index a4d211d4398c98ef263b1e1a92e1b75caa48982f..6a2353b64b88e174077fa99a0b34dca31d3c02dc 100644
--- a/ebpfcat/ethercat.py
+++ b/ebpfcat/ethercat.py
@@ -30,7 +30,7 @@ from asyncio import ensure_future, Event, Future, gather, get_event_loop, Protoc
 from enum import Enum
 from random import randint
 from socket import socket, AF_PACKET, SOCK_DGRAM
-from struct import pack, unpack, calcsize
+from struct import pack, unpack, unpack_from, calcsize
 
 class ECCmd(Enum):
    NOP = 0  # No Operation
@@ -428,22 +428,76 @@ class Terminal:
         s = await self.read(0x800, data=0x80)
         print(absolute, " ".join(f"{c:02x} {'|' if i % 8 == 7 else ''}" for i, c in enumerate(s)))
 
-    def parse_pdos(self):
-        def parse_pdo(s):
+    async def parse_pdos(self):
+        async def parse_eeprom(s):
             i = 0
+            bitpos = 0
             while i < len(s):
-                idx, e, sm, u1, u2, u3 = unpack("<HBBBBH", s[i:i+8])
-                print(f"idx {idx:x} sm {sm} {u1:x} {u2:x} {u3:x}")
+                idx, e, sm, u1, u2, u3 = unpack_from("<HBbBBH", s, i)
                 i += 8
                 for er in range(e):
-                    bitsize, = unpack("<5xB2x", s[i:i+8])
-                    print("  bs", bitsize, s[i:i+8])
+                    idx, subidx, k1, k2, bits, = unpack("<HBBBB2x", s[i:i+8])
+                    if sm > 0:
+                        yield idx, subidx, sm, bits
                     i += 8
 
-        if 50 in self.eeprom:
-            parse_pdo(self.eeprom[50])
-        if 51 in self.eeprom:
-            parse_pdo(self.eeprom[51])
+        async def parse_sdo(index, sm):
+            assignment = await self.sdo_read(index)
+            bitpos = 0
+            for i in range(0, len(assignment), 2):
+                pdo, = unpack_from("<H", assignment, i)
+                if pdo == 0:
+                    continue
+                count, = unpack("B", await self.sdo_read(pdo, 0))
+                for j in range(1, count + 1):
+                    bits, subidx, idx = unpack("<BBH", await self.sdo_read(pdo, j))
+                    yield idx, subidx, sm, bits
+
+        async def parse(func):
+            bitpos = 0
+            async for idx, subidx, sm, bits in func:
+                #print("k", idx, subidx, sm, bits)
+                if idx == 0:
+                    pass
+                elif bits < 8:
+                    self.pdos[idx, subidx] = (sm, bitpos // 8, bitpos % 8)
+                elif (bits % 8) or (bitpos % 8):
+                    raise RuntimeError("PDOs must be byte-aligned")
+                else:
+                    self.pdos[idx, subidx] = \
+                        (sm, bitpos // 8,
+                         {8: "B", 16: "H", 32: "I", 64: "Q"}[bits])
+                bitpos += bits
+
+        self.pdos = {}
+        if self.has_mailbox():
+            await parse(parse_sdo(0x1c12, 2))
+            await parse(parse_sdo(0x1c13, 3))
+        else:
+            if 50 in self.eeprom:
+                await parse(parse_eeprom(self.eeprom[50]))
+            if 51 in self.eeprom:
+                await parse(parse_eeprom(self.eeprom[51]))
+
+    async def parse_pdo(self, index, sm):
+        assignment = await self.sdo_read(index)
+        bitpos = 0
+        for i in range(0, len(assignment), 2):
+            pdo, = unpack_from("<H", assignment, i)
+            count, = unpack("B", await self.sdo_read(pdo, 0))
+            for j in range(1, count + 1):
+                bits, subidx, idx = unpack("<BBH", await self.sdo_read(pdo, j))
+                if idx == 0:
+                    pass
+                elif bits < 8:
+                    self.pdos[idx, subidx] = (sm, bitpos // 8, bitpos % 8)
+                elif (bits % 8) or (bitpos % 8):
+                    raise RuntimeError("PDOs must be byte-aligned")
+                else:
+                    self.pdos[idx, subidx] = \
+                        (sm, bitpos // 8,
+                         {8: "B", 16: "H", 32: "I", 64: "Q"}[bits])
+                bitpos += bits
 
     async def set_state(self, state):
         """try to set the state, and return the new state"""
@@ -592,6 +646,8 @@ class Terminal:
                 raise RuntimeError(f"expected CoE, got {type}")
             coecmd, sdocmd, idx, subidx, size = unpack("<HBHBI", data[:10])
             if coecmd >> 12 != CoECmd.SDORES.value:
+                if subindex is None and coecmd >> 12 == CoECmd.SDOREQ.value:
+                    return b""  # if there is no data, the terminal fails
                 raise RuntimeError(
                     f"expected CoE SDORES (3), got {coecmd>>12:x} "
                     f"for {index:X}:{9 if subindex is None else subindex:02X}")
diff --git a/ebpfcat/scripts.py b/ebpfcat/scripts.py
index 5b0ee2cbaa6afb831ae41cc459c074197a949a27..887bcbe0ad37beaec48cf618ac14f66f1286a771 100644
--- a/ebpfcat/scripts.py
+++ b/ebpfcat/scripts.py
@@ -80,4 +80,7 @@ async def info():
                          else:
                              print(f"        {r}")
         if args.pdo:
-            t.parse_pdos()
+            await t.to_operational()
+            await t.parse_pdos()
+            for (idx, subidx), (sm, pos, fmt) in t.pdos.items():
+                print(f"{idx:4X}:{subidx:02X} {sm} {pos} {fmt}")
diff --git a/ebpfcat/terminals.py b/ebpfcat/terminals.py
index 331a15f786ac7963b1f50b9839394c41308b3057..085138cde094c48ef96487d6ca7e836b747d6602 100644
--- a/ebpfcat/terminals.py
+++ b/ebpfcat/terminals.py
@@ -15,7 +15,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from .ebpfcat import EBPFTerminal, PacketDesc, Struct
+from .ebpfcat import EBPFTerminal, PacketDesc, ProcessDesc, Struct
 
 
 class Generic(EBPFTerminal):
@@ -54,21 +54,21 @@ class EL2808(EBPFTerminal):
 
 
 class EL4104(EBPFTerminal):
-    ch1_value = PacketDesc(2, 0, 'H')
-    ch2_value = PacketDesc(2, 2, 'H')
-    ch3_value = PacketDesc(2, 4, 'H')
-    ch4_value = PacketDesc(2, 6, 'H')
+    ch1_value = ProcessDesc(0x7000, 1)
+    ch2_value = ProcessDesc(0x7010, 1)
+    ch3_value = ProcessDesc(0x7020, 1)
+    ch4_value = ProcessDesc(0x7030, 1)
 
 
 class EL3164(EBPFTerminal):
     class Channel(Struct):
-        attrs = PacketDesc(3, 0, 'H')
-        value = PacketDesc(3, 2, 'H')
+        attrs = ProcessDesc(0x6000, 1, 'H')
+        value = ProcessDesc(0x6000, 0x11)
 
     channel1 = Channel(0)
-    channel2 = Channel(4)
-    channel3 = Channel(8)
-    channel4 = Channel(12)
+    channel2 = Channel(0x10)
+    channel3 = Channel(0x20)
+    channel4 = Channel(0x30)
 
 
 class EK1101(EBPFTerminal):