xref: /aosp_15_r20/external/pigweed/pw_hdlc/py/pw_hdlc/protocol.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2020 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Module for low-level HDLC protocol features."""
15
16
17import zlib
18
19# Special flag character for delimiting HDLC frames.
20FLAG = 0x7E
21
22# Special character for escaping other special characters in a frame.
23ESCAPE = 0x7D
24
25# Characters allowed after a 0x7d escape character.
26VALID_ESCAPED_BYTES = 0x5D, 0x5E
27
28# Maximum allowed HDLC address (uint64_t in C++).
29MAX_ADDRESS = 2**64 - 1
30
31
32def escape(byte: int) -> int:
33    """Escapes or unescapes a byte, which should have been preceeded by 0x7d."""
34    return byte ^ 0x20
35
36
37def frame_check_sequence(data: bytes) -> bytes:
38    return zlib.crc32(data).to_bytes(4, 'little')
39
40
41def encode_address(address: int) -> bytes:
42    """Encodes an HDLC address as a one-terminated LSB varint."""
43    result = bytearray()
44
45    while True:
46        result += bytes([(address & 0x7F) << 1])
47
48        address >>= 7
49        if address == 0:
50            break
51
52    result[-1] |= 0x1
53    return result
54
55
56def decode_address(frame: bytes) -> tuple[int, int]:
57    """Decodes an HDLC address from a frame, returning it and its size."""
58    result = 0
59    length = 0
60
61    while length < len(frame):
62        byte = frame[length]
63        result |= (byte >> 1) << (length * 7)
64        length += 1
65
66        if byte & 0x1 == 0x1:
67            break
68
69    if result > MAX_ADDRESS:
70        return -1, 0
71
72    return result, length
73
74
75class UFrameControl:
76    def __init__(self, frame_type: int):
77        self._data: bytes = bytes([0x03 | frame_type])
78
79    @property
80    def data(self):
81        return self._data
82
83    @classmethod
84    def unnumbered_information(cls):
85        return UFrameControl(0x00)
86