xref: /aosp_15_r20/system/nfc/tools/casimir/scripts/rf_packets.py (revision 7eba2f3b06c51ae21384f6a4f14577b668a869b3)
1# File generated from <stdin>, with the command:
2#  /home/henrichataing/Projects/github/google/pdl/pdl-compiler/scripts/generate_python_backend.py
3# /!\ Do not edit by hand.
4from dataclasses import dataclass, field, fields
5from typing import Optional, List, Tuple, Union
6import enum
7import inspect
8import math
9
10@dataclass
11class Packet:
12    payload: Optional[bytes] = field(repr=False, default_factory=bytes, compare=False)
13
14    @classmethod
15    def parse_all(cls, span: bytes) -> 'Packet':
16        packet, remain = getattr(cls, 'parse')(span)
17        if len(remain) > 0:
18            raise Exception('Unexpected parsing remainder')
19        return packet
20
21    @property
22    def size(self) -> int:
23        pass
24
25    def show(self, prefix: str = ''):
26        print(f'{self.__class__.__name__}')
27
28        def print_val(p: str, pp: str, name: str, align: int, typ, val):
29            if name == 'payload':
30                pass
31
32            # Scalar fields.
33            elif typ is int:
34                print(f'{p}{name:{align}} = {val} (0x{val:x})')
35
36            # Byte fields.
37            elif typ is bytes:
38                print(f'{p}{name:{align}} = [', end='')
39                line = ''
40                n_pp = ''
41                for (idx, b) in enumerate(val):
42                    if idx > 0 and idx % 8 == 0:
43                        print(f'{n_pp}{line}')
44                        line = ''
45                        n_pp = pp + (' ' * (align + 4))
46                    line += f' {b:02x}'
47                print(f'{n_pp}{line} ]')
48
49            # Enum fields.
50            elif inspect.isclass(typ) and issubclass(typ, enum.IntEnum):
51                print(f'{p}{name:{align}} = {typ.__name__}::{val.name} (0x{val:x})')
52
53            # Struct fields.
54            elif inspect.isclass(typ) and issubclass(typ, globals().get('Packet')):
55                print(f'{p}{name:{align}} = ', end='')
56                val.show(prefix=pp)
57
58            # Array fields.
59            elif getattr(typ, '__origin__', None) == list:
60                print(f'{p}{name:{align}}')
61                last = len(val) - 1
62                align = 5
63                for (idx, elt) in enumerate(val):
64                    n_p  = pp + ('├── ' if idx != last else '└── ')
65                    n_pp = pp + ('│   ' if idx != last else '    ')
66                    print_val(n_p, n_pp, f'[{idx}]', align, typ.__args__[0], val[idx])
67
68            # Custom fields.
69            elif inspect.isclass(typ):
70                print(f'{p}{name:{align}} = {repr(val)}')
71
72            else:
73                print(f'{p}{name:{align}} = ##{typ}##')
74
75        last = len(fields(self)) - 1
76        align = max(len(f.name) for f in fields(self) if f.name != 'payload')
77
78        for (idx, f) in enumerate(fields(self)):
79            p  = prefix + ('├── ' if idx != last else '└── ')
80            pp = prefix + ('│   ' if idx != last else '    ')
81            val = getattr(self, f.name)
82
83            print_val(p, pp, f.name, align, f.type, val)
84
85class Technology(enum.IntEnum):
86    NFC_A = 0x0
87    NFC_B = 0x1
88    NFC_F = 0x2
89    NFC_V = 0x3
90    RAW = 0x7
91
92    @staticmethod
93    def from_int(v: int) -> Union[int, 'Technology']:
94        try:
95            return Technology(v)
96        except ValueError as exn:
97            raise exn
98
99
100class Protocol(enum.IntEnum):
101    UNDETERMINED = 0x0
102    T1T = 0x1
103    T2T = 0x2
104    T3T = 0x3
105    ISO_DEP = 0x4
106    NFC_DEP = 0x5
107    T5T = 0x6
108    NDEF = 0x7
109
110    @staticmethod
111    def from_int(v: int) -> Union[int, 'Protocol']:
112        try:
113            return Protocol(v)
114        except ValueError as exn:
115            raise exn
116
117
118class RfPacketType(enum.IntEnum):
119    DATA = 0x0
120    POLL_COMMAND = 0x1
121    POLL_RESPONSE = 0x2
122    SELECT_COMMAND = 0x3
123    SELECT_RESPONSE = 0x4
124    DEACTIVATE_NOTIFICATION = 0x5
125
126    @staticmethod
127    def from_int(v: int) -> Union[int, 'RfPacketType']:
128        try:
129            return RfPacketType(v)
130        except ValueError as exn:
131            raise exn
132
133
134@dataclass
135class RfPacket(Packet):
136    sender: int = field(kw_only=True, default=0)
137    receiver: int = field(kw_only=True, default=0)
138    technology: Technology = field(kw_only=True, default=Technology.NFC_A)
139    protocol: Protocol = field(kw_only=True, default=Protocol.UNDETERMINED)
140    packet_type: RfPacketType = field(kw_only=True, default=RfPacketType.DATA)
141
142    def __post_init__(self):
143        pass
144
145    @staticmethod
146    def parse(span: bytes) -> Tuple['RfPacket', bytes]:
147        fields = {'payload': None}
148        if len(span) < 7:
149            raise Exception('Invalid packet size')
150        value_ = int.from_bytes(span[0:2], byteorder='little')
151        fields['sender'] = value_
152        value_ = int.from_bytes(span[2:4], byteorder='little')
153        fields['receiver'] = value_
154        fields['technology'] = Technology.from_int(span[4])
155        fields['protocol'] = Protocol.from_int(span[5])
156        fields['packet_type'] = RfPacketType.from_int(span[6])
157        span = span[7:]
158        payload = span
159        span = bytes([])
160        fields['payload'] = payload
161        try:
162            return PollCommand.parse(fields.copy(), payload)
163        except Exception as exn:
164            pass
165        try:
166            return NfcAPollResponse.parse(fields.copy(), payload)
167        except Exception as exn:
168            pass
169        try:
170            return T4ATSelectCommand.parse(fields.copy(), payload)
171        except Exception as exn:
172            pass
173        try:
174            return T4ATSelectResponse.parse(fields.copy(), payload)
175        except Exception as exn:
176            pass
177        try:
178            return NfcDepSelectCommand.parse(fields.copy(), payload)
179        except Exception as exn:
180            pass
181        try:
182            return NfcDepSelectResponse.parse(fields.copy(), payload)
183        except Exception as exn:
184            pass
185        try:
186            return SelectCommand.parse(fields.copy(), payload)
187        except Exception as exn:
188            pass
189        try:
190            return DeactivateNotification.parse(fields.copy(), payload)
191        except Exception as exn:
192            pass
193        try:
194            return Data.parse(fields.copy(), payload)
195        except Exception as exn:
196            pass
197        return RfPacket(**fields), span
198
199    def serialize(self, payload: bytes = None) -> bytes:
200        _span = bytearray()
201        if self.sender > 65535:
202            print(f"Invalid value for field RfPacket::sender: {self.sender} > 65535; the value will be truncated")
203            self.sender &= 65535
204        _span.extend(int.to_bytes((self.sender << 0), length=2, byteorder='little'))
205        if self.receiver > 65535:
206            print(f"Invalid value for field RfPacket::receiver: {self.receiver} > 65535; the value will be truncated")
207            self.receiver &= 65535
208        _span.extend(int.to_bytes((self.receiver << 0), length=2, byteorder='little'))
209        _span.append((self.technology << 0))
210        _span.append((self.protocol << 0))
211        _span.append((self.packet_type << 0))
212        _span.extend(payload or self.payload or [])
213        return bytes(_span)
214
215    @property
216    def size(self) -> int:
217        return len(self.payload) + 7
218
219@dataclass
220class PollCommand(RfPacket):
221    data : bytearray = field(kw_only=True, default_factory=bytearray)
222
223    def __post_init__(self):
224        self.packet_type = RfPacketType.POLL_COMMAND
225
226    @staticmethod
227    def parse(fields: dict, span: bytes) -> Tuple['PollCommand', bytes]:
228        if fields['packet_type'] != RfPacketType.POLL_COMMAND:
229            raise Exception("Invalid constraint field values")
230        data = span[0:]
231        fields['data'] = data
232        return PollCommand(**fields), span
233
234    def serialize(self, payload: bytes = None) -> bytes:
235        _span = bytearray()
236        _span.extend(self.data)
237        return RfPacket.serialize(self, payload = bytes(_span))
238
239    @property
240    def size(self) -> int:
241        return len(self.data)
242
243@dataclass
244class NfcAPollResponse(RfPacket):
245    nfcid1: bytearray = field(kw_only=True, default_factory=bytearray)
246    int_protocol: int = field(kw_only=True, default=0)
247    bit_frame_sdd: int = field(kw_only=True, default=0)
248
249    def __post_init__(self):
250        self.technology = Technology.NFC_A
251        self.packet_type = RfPacketType.POLL_RESPONSE
252
253    @staticmethod
254    def parse(fields: dict, span: bytes) -> Tuple['NfcAPollResponse', bytes]:
255        if fields['technology'] != Technology.NFC_A or fields['packet_type'] != RfPacketType.POLL_RESPONSE:
256            raise Exception("Invalid constraint field values")
257        if len(span) < 1:
258            raise Exception('Invalid packet size')
259        nfcid1_size = span[0]
260        span = span[1:]
261        if len(span) < nfcid1_size:
262            raise Exception('Invalid packet size')
263        fields['nfcid1'] = list(span[:nfcid1_size])
264        span = span[nfcid1_size:]
265        if len(span) < 2:
266            raise Exception('Invalid packet size')
267        fields['int_protocol'] = (span[0] >> 0) & 0x3
268        fields['bit_frame_sdd'] = span[1]
269        span = span[2:]
270        return NfcAPollResponse(**fields), span
271
272    def serialize(self, payload: bytes = None) -> bytes:
273        _span = bytearray()
274        _span.append(((len(self.nfcid1) * 1) << 0))
275        _span.extend(self.nfcid1)
276        if self.int_protocol > 3:
277            print(f"Invalid value for field NfcAPollResponse::int_protocol: {self.int_protocol} > 3; the value will be truncated")
278            self.int_protocol &= 3
279        _span.append((self.int_protocol << 0))
280        if self.bit_frame_sdd > 255:
281            print(f"Invalid value for field NfcAPollResponse::bit_frame_sdd: {self.bit_frame_sdd} > 255; the value will be truncated")
282            self.bit_frame_sdd &= 255
283        _span.append((self.bit_frame_sdd << 0))
284        return RfPacket.serialize(self, payload = bytes(_span))
285
286    @property
287    def size(self) -> int:
288        return len(self.nfcid1) * 1 + 3
289
290@dataclass
291class T4ATSelectCommand(RfPacket):
292    param: int = field(kw_only=True, default=0)
293
294    def __post_init__(self):
295        self.technology = Technology.NFC_A
296        self.protocol = Protocol.ISO_DEP
297        self.packet_type = RfPacketType.SELECT_COMMAND
298
299    @staticmethod
300    def parse(fields: dict, span: bytes) -> Tuple['T4ATSelectCommand', bytes]:
301        if fields['technology'] != Technology.NFC_A or fields['protocol'] != Protocol.ISO_DEP or fields['packet_type'] != RfPacketType.SELECT_COMMAND:
302            raise Exception("Invalid constraint field values")
303        if len(span) < 1:
304            raise Exception('Invalid packet size')
305        fields['param'] = span[0]
306        span = span[1:]
307        return T4ATSelectCommand(**fields), span
308
309    def serialize(self, payload: bytes = None) -> bytes:
310        _span = bytearray()
311        if self.param > 255:
312            print(f"Invalid value for field T4ATSelectCommand::param: {self.param} > 255; the value will be truncated")
313            self.param &= 255
314        _span.append((self.param << 0))
315        return RfPacket.serialize(self, payload = bytes(_span))
316
317    @property
318    def size(self) -> int:
319        return 1
320
321@dataclass
322class T4ATSelectResponse(RfPacket):
323    rats_response: bytearray = field(kw_only=True, default_factory=bytearray)
324
325    def __post_init__(self):
326        self.technology = Technology.NFC_A
327        self.protocol = Protocol.ISO_DEP
328        self.packet_type = RfPacketType.SELECT_RESPONSE
329
330    @staticmethod
331    def parse(fields: dict, span: bytes) -> Tuple['T4ATSelectResponse', bytes]:
332        if fields['technology'] != Technology.NFC_A or fields['protocol'] != Protocol.ISO_DEP or fields['packet_type'] != RfPacketType.SELECT_RESPONSE:
333            raise Exception("Invalid constraint field values")
334        if len(span) < 1:
335            raise Exception('Invalid packet size')
336        rats_response_size = span[0]
337        span = span[1:]
338        if len(span) < rats_response_size:
339            raise Exception('Invalid packet size')
340        fields['rats_response'] = list(span[:rats_response_size])
341        span = span[rats_response_size:]
342        return T4ATSelectResponse(**fields), span
343
344    def serialize(self, payload: bytes = None) -> bytes:
345        _span = bytearray()
346        _span.append(((len(self.rats_response) * 1) << 0))
347        _span.extend(self.rats_response)
348        return RfPacket.serialize(self, payload = bytes(_span))
349
350    @property
351    def size(self) -> int:
352        return len(self.rats_response) * 1 + 1
353
354@dataclass
355class NfcDepSelectCommand(RfPacket):
356    lr: int = field(kw_only=True, default=0)
357
358    def __post_init__(self):
359        self.protocol = Protocol.NFC_DEP
360        self.packet_type = RfPacketType.SELECT_COMMAND
361
362    @staticmethod
363    def parse(fields: dict, span: bytes) -> Tuple['NfcDepSelectCommand', bytes]:
364        if fields['protocol'] != Protocol.NFC_DEP or fields['packet_type'] != RfPacketType.SELECT_COMMAND:
365            raise Exception("Invalid constraint field values")
366        if len(span) < 1:
367            raise Exception('Invalid packet size')
368        fields['lr'] = (span[0] >> 0) & 0x3
369        span = span[1:]
370        return NfcDepSelectCommand(**fields), span
371
372    def serialize(self, payload: bytes = None) -> bytes:
373        _span = bytearray()
374        if self.lr > 3:
375            print(f"Invalid value for field NfcDepSelectCommand::lr: {self.lr} > 3; the value will be truncated")
376            self.lr &= 3
377        _span.append((self.lr << 0))
378        return RfPacket.serialize(self, payload = bytes(_span))
379
380    @property
381    def size(self) -> int:
382        return 1
383
384@dataclass
385class NfcDepSelectResponse(RfPacket):
386    atr_response: bytearray = field(kw_only=True, default_factory=bytearray)
387
388    def __post_init__(self):
389        self.protocol = Protocol.NFC_DEP
390        self.packet_type = RfPacketType.SELECT_RESPONSE
391
392    @staticmethod
393    def parse(fields: dict, span: bytes) -> Tuple['NfcDepSelectResponse', bytes]:
394        if fields['protocol'] != Protocol.NFC_DEP or fields['packet_type'] != RfPacketType.SELECT_RESPONSE:
395            raise Exception("Invalid constraint field values")
396        if len(span) < 1:
397            raise Exception('Invalid packet size')
398        atr_response_size = span[0]
399        span = span[1:]
400        if len(span) < atr_response_size:
401            raise Exception('Invalid packet size')
402        fields['atr_response'] = list(span[:atr_response_size])
403        span = span[atr_response_size:]
404        return NfcDepSelectResponse(**fields), span
405
406    def serialize(self, payload: bytes = None) -> bytes:
407        _span = bytearray()
408        _span.append(((len(self.atr_response) * 1) << 0))
409        _span.extend(self.atr_response)
410        return RfPacket.serialize(self, payload = bytes(_span))
411
412    @property
413    def size(self) -> int:
414        return len(self.atr_response) * 1 + 1
415
416@dataclass
417class SelectCommand(RfPacket):
418
419
420    def __post_init__(self):
421        self.packet_type = RfPacketType.SELECT_COMMAND
422
423    @staticmethod
424    def parse(fields: dict, span: bytes) -> Tuple['SelectCommand', bytes]:
425        if fields['packet_type'] != RfPacketType.SELECT_COMMAND:
426            raise Exception("Invalid constraint field values")
427        return SelectCommand(**fields), span
428
429    def serialize(self, payload: bytes = None) -> bytes:
430        _span = bytearray()
431        return RfPacket.serialize(self, payload = bytes(_span))
432
433    @property
434    def size(self) -> int:
435        return 0
436
437class DeactivateType(enum.IntEnum):
438    IDLE_MODE = 0x0
439    SLEEP_MODE = 0x1
440    SLEEP_AF_MODE = 0x2
441    DISCOVERY = 0x3
442
443    @staticmethod
444    def from_int(v: int) -> Union[int, 'DeactivateType']:
445        try:
446            return DeactivateType(v)
447        except ValueError as exn:
448            raise exn
449
450
451class DeactivateReason(enum.IntEnum):
452    DH_REQUEST = 0x0
453    ENDPOINT_REQUEST = 0x1
454    RF_LINK_LOSS = 0x2
455    NFC_B_BAD_AFI = 0x3
456    DH_REQUEST_FAILED = 0x4
457
458    @staticmethod
459    def from_int(v: int) -> Union[int, 'DeactivateReason']:
460        try:
461            return DeactivateReason(v)
462        except ValueError as exn:
463            raise exn
464
465
466@dataclass
467class DeactivateNotification(RfPacket):
468    type_: DeactivateType = field(kw_only=True, default=DeactivateType.IDLE_MODE)
469    reason: DeactivateReason = field(kw_only=True, default=DeactivateReason.DH_REQUEST)
470
471    def __post_init__(self):
472        self.packet_type = RfPacketType.DEACTIVATE_NOTIFICATION
473
474    @staticmethod
475    def parse(fields: dict, span: bytes) -> Tuple['DeactivateNotification', bytes]:
476        if fields['packet_type'] != RfPacketType.DEACTIVATE_NOTIFICATION:
477            raise Exception("Invalid constraint field values")
478        if len(span) < 2:
479            raise Exception('Invalid packet size')
480        fields['type_'] = DeactivateType.from_int(span[0])
481        fields['reason'] = DeactivateReason.from_int(span[1])
482        span = span[2:]
483        return DeactivateNotification(**fields), span
484
485    def serialize(self, payload: bytes = None) -> bytes:
486        _span = bytearray()
487        _span.append((self.type_ << 0))
488        _span.append((self.reason << 0))
489        return RfPacket.serialize(self, payload = bytes(_span))
490
491    @property
492    def size(self) -> int:
493        return 2
494
495@dataclass
496class Data(RfPacket):
497    data: bytearray = field(kw_only=True, default_factory=bytearray)
498
499    def __post_init__(self):
500        self.packet_type = RfPacketType.DATA
501
502    @staticmethod
503    def parse(fields: dict, span: bytes) -> Tuple['Data', bytes]:
504        if fields['packet_type'] != RfPacketType.DATA:
505            raise Exception("Invalid constraint field values")
506        fields['data'] = list(span)
507        span = bytes()
508        return Data(**fields), span
509
510    def serialize(self, payload: bytes = None) -> bytes:
511        _span = bytearray()
512        _span.extend(self.data)
513        return RfPacket.serialize(self, payload = bytes(_span))
514
515    @property
516    def size(self) -> int:
517        return len(self.data) * 1
518