xref: /aosp_15_r20/external/pigweed/pw_hdlc/ts/protocol.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker// Copyright 2021 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker//
3*61c4878aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker// use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker// the License at
6*61c4878aSAndroid Build Coastguard Worker//
7*61c4878aSAndroid Build Coastguard Worker//     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker//
9*61c4878aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker// License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker// the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker/** Low-level HDLC protocol features. */
16*61c4878aSAndroid Build Coastguard Workerimport { crc32 } from './crc32';
17*61c4878aSAndroid Build Coastguard Worker
18*61c4878aSAndroid Build Coastguard Worker/** Special flag character for delimiting HDLC frames. */
19*61c4878aSAndroid Build Coastguard Workerexport const FLAG = 0x7e;
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker/** Special character for escaping other special characters in a frame. */
22*61c4878aSAndroid Build Coastguard Workerexport const ESCAPE = 0x7d;
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard Worker/** Characters allowed after a 0x7d escape character. */
25*61c4878aSAndroid Build Coastguard Workerexport const VALID_ESCAPED_BYTES = [0x5d, 0x5e];
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker/** Frame control for unnumbered information */
28*61c4878aSAndroid Build Coastguard Workerexport const UI_FRAME_CONTROL = frameControl(0x00);
29*61c4878aSAndroid Build Coastguard Worker
30*61c4878aSAndroid Build Coastguard Worker/** Maximum allowed HDLC address (uint64_t in C++). */
31*61c4878aSAndroid Build Coastguard Workerconst MAX_ADDRESS = 2 ** 64 - 1;
32*61c4878aSAndroid Build Coastguard Worker
33*61c4878aSAndroid Build Coastguard Worker/**
34*61c4878aSAndroid Build Coastguard Worker * Bitwise OR operation on numbers up to MAX_ADDRESS size.
35*61c4878aSAndroid Build Coastguard Worker * Native bitwise operators only support signed Int32.
36*61c4878aSAndroid Build Coastguard Worker */
37*61c4878aSAndroid Build Coastguard Workerfunction bitwiseOr(x: number, y: number) {
38*61c4878aSAndroid Build Coastguard Worker  const highMask = 0x80000000;
39*61c4878aSAndroid Build Coastguard Worker  const lowMask = 0x7fffffff;
40*61c4878aSAndroid Build Coastguard Worker  const highX = ~~(x / highMask);
41*61c4878aSAndroid Build Coastguard Worker  const highY = ~~(y / highMask);
42*61c4878aSAndroid Build Coastguard Worker  const lowX = x & lowMask;
43*61c4878aSAndroid Build Coastguard Worker  const lowY = y & lowMask;
44*61c4878aSAndroid Build Coastguard Worker  const highOr = highX | highY;
45*61c4878aSAndroid Build Coastguard Worker  const lowOr = lowX | lowY;
46*61c4878aSAndroid Build Coastguard Worker  return highOr * highMask + lowOr;
47*61c4878aSAndroid Build Coastguard Worker}
48*61c4878aSAndroid Build Coastguard Worker
49*61c4878aSAndroid Build Coastguard Workerfunction getUint8ArraySubset(array: Uint8Array, start: number, end: number) {
50*61c4878aSAndroid Build Coastguard Worker  const subset = new Uint8Array(array.buffer, start, end - start);
51*61c4878aSAndroid Build Coastguard Worker  return subset;
52*61c4878aSAndroid Build Coastguard Worker}
53*61c4878aSAndroid Build Coastguard Worker
54*61c4878aSAndroid Build Coastguard Worker/** Calculates the CRC32 of |data| */
55*61c4878aSAndroid Build Coastguard Workerexport function frameCheckSequence(data: Uint8Array): Uint8Array {
56*61c4878aSAndroid Build Coastguard Worker  const crc = crc32(
57*61c4878aSAndroid Build Coastguard Worker    getUint8ArraySubset(data, data.byteOffset, data.byteLength),
58*61c4878aSAndroid Build Coastguard Worker  );
59*61c4878aSAndroid Build Coastguard Worker  const arr = new ArrayBuffer(4);
60*61c4878aSAndroid Build Coastguard Worker  const view = new DataView(arr);
61*61c4878aSAndroid Build Coastguard Worker  view.setUint32(0, crc, true); // litteEndian = true
62*61c4878aSAndroid Build Coastguard Worker  return new Uint8Array(arr);
63*61c4878aSAndroid Build Coastguard Worker}
64*61c4878aSAndroid Build Coastguard Worker
65*61c4878aSAndroid Build Coastguard Worker/** Escapes or unescapes a byte, which should have been preceeded by 0x7d */
66*61c4878aSAndroid Build Coastguard Workerexport function escape(byte: number): number {
67*61c4878aSAndroid Build Coastguard Worker  return byte ^ 0x20;
68*61c4878aSAndroid Build Coastguard Worker}
69*61c4878aSAndroid Build Coastguard Worker
70*61c4878aSAndroid Build Coastguard Worker/** Encodes an HDLC address as a one-terminated LSB varint. */
71*61c4878aSAndroid Build Coastguard Workerexport function encodeAddress(address: number): Uint8Array {
72*61c4878aSAndroid Build Coastguard Worker  const byteList = [];
73*61c4878aSAndroid Build Coastguard Worker  // eslint-disable-next-line no-constant-condition
74*61c4878aSAndroid Build Coastguard Worker  while (true) {
75*61c4878aSAndroid Build Coastguard Worker    byteList.push((address & 0x7f) << 1);
76*61c4878aSAndroid Build Coastguard Worker    address >>= 7;
77*61c4878aSAndroid Build Coastguard Worker    if (address === 0) {
78*61c4878aSAndroid Build Coastguard Worker      break;
79*61c4878aSAndroid Build Coastguard Worker    }
80*61c4878aSAndroid Build Coastguard Worker  }
81*61c4878aSAndroid Build Coastguard Worker
82*61c4878aSAndroid Build Coastguard Worker  const result = Uint8Array.from(byteList);
83*61c4878aSAndroid Build Coastguard Worker  result[result.length - 1] |= 0x1;
84*61c4878aSAndroid Build Coastguard Worker  return result;
85*61c4878aSAndroid Build Coastguard Worker}
86*61c4878aSAndroid Build Coastguard Worker
87*61c4878aSAndroid Build Coastguard Worker/** Decodes an HDLC address from a frame, returning it and its size. */
88*61c4878aSAndroid Build Coastguard Workerexport function decodeAddress(frame: Uint8Array): [number, number] {
89*61c4878aSAndroid Build Coastguard Worker  let result = 0;
90*61c4878aSAndroid Build Coastguard Worker  let length = 0;
91*61c4878aSAndroid Build Coastguard Worker
92*61c4878aSAndroid Build Coastguard Worker  while (length < frame.length) {
93*61c4878aSAndroid Build Coastguard Worker    const byte = frame[length];
94*61c4878aSAndroid Build Coastguard Worker    const shift = (byte >> 1) * 2 ** (length * 7);
95*61c4878aSAndroid Build Coastguard Worker    result = bitwiseOr(result, shift);
96*61c4878aSAndroid Build Coastguard Worker    length += 1;
97*61c4878aSAndroid Build Coastguard Worker
98*61c4878aSAndroid Build Coastguard Worker    if (shift > MAX_ADDRESS || result > MAX_ADDRESS) {
99*61c4878aSAndroid Build Coastguard Worker      return [-1, 0];
100*61c4878aSAndroid Build Coastguard Worker    }
101*61c4878aSAndroid Build Coastguard Worker    if ((byte & 0x1) === 0x1) {
102*61c4878aSAndroid Build Coastguard Worker      break;
103*61c4878aSAndroid Build Coastguard Worker    }
104*61c4878aSAndroid Build Coastguard Worker  }
105*61c4878aSAndroid Build Coastguard Worker  return [result, length];
106*61c4878aSAndroid Build Coastguard Worker}
107*61c4878aSAndroid Build Coastguard Worker
108*61c4878aSAndroid Build Coastguard Workerfunction frameControl(frameType: number): Uint8Array {
109*61c4878aSAndroid Build Coastguard Worker  return Uint8Array.from([0x03 | frameType]);
110*61c4878aSAndroid Build Coastguard Worker}
111