1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.Linq;
10 using System.Collections.Generic;
11 
12 namespace Antmicro.Renode.Utilities
13 {
14     public interface ILINEntry
15     {
16         LINMode Mode { get; set; }
17         int FrameLength { get; set; }
18         bool ValidateFrame { get; set; }
19         bool ExtendedChecksum { get; set; }
20 
21         event Action<byte[], bool> DataReady;
22         event Action StartedTransmission;
23     }
24 
25     public enum LINMode
26     {
27         Source,
28         Sink,
29     }
30 
31     public class LINDecoder
32     {
LINDecoder()33         public LINDecoder()
34         {
35             entries = new SortedList<byte, LINEntry>();
36             rxQueue = new Queue<byte>();
37             txQueue = new Queue<byte>();
38         }
39 
Register(byte protectedIdentifier)40         public ILINEntry Register(byte protectedIdentifier)
41         {
42             if(ValidateProtectedIdentifier && !IsProtectedIdentifierValid(protectedIdentifier))
43             {
44                 throw new ArgumentException("invalid protected identifier", "protectedIdentifier");
45             }
46 
47             entries.Add(protectedIdentifier, new LINEntry(protectedIdentifier));
48             return entries[protectedIdentifier];
49         }
50 
Unregister(byte protectedIdentifier)51         public void Unregister(byte protectedIdentifier)
52         {
53             entries.Remove(protectedIdentifier);
54         }
55 
TryGetEntry(byte protectedIdentifier, out ILINEntry entry)56         public bool TryGetEntry(byte protectedIdentifier, out ILINEntry entry)
57         {
58             if(entries.TryGetValue(protectedIdentifier, out var linEntry))
59             {
60                 entry = (ILINEntry)linEntry;
61                 return true;
62             }
63 
64             entry = null;
65             return false;
66         }
67 
GetEntry(byte protectedIdentifier)68         public ILINEntry GetEntry(byte protectedIdentifier)
69         {
70             if(TryGetEntry(protectedIdentifier, out var entry))
71             {
72                 return entry;
73             }
74             throw new ArgumentNullException($"no entry with pid 0x{protectedIdentifier:X02}");
75         }
76 
Feed(byte value)77         public void Feed(byte value)
78         {
79             var previousState = CurrentState;
80             switch(CurrentState)
81             {
82                 case State.Armed:
83                 {
84                     // We are waiting for synchronization
85                     if(value != SynchronizationPattern)
86                     {
87                         CurrentState = State.SynchronizationFailed;
88                         break;
89                     }
90                     CurrentState = State.Synchronized;
91                     break;
92                 }
93 
94                 case State.Synchronized:
95                 {
96                     // We are awaiting Protected Identifier
97                     if(!entries.TryGetValue(value, out var entry))
98                     {
99                         // This transaction is not for us
100                         CurrentState = State.Ignoring;
101                         break;
102                     }
103                     currentProtectedIdentifier = value;
104                     CurrentState = entry.Mode == LINMode.Source ? State.Writing : State.Reading;
105                     break;
106                 }
107 
108                 case State.SynchronizationFailed:
109                     // NOTE: Intentionally left empty
110                     break;
111 
112                 case State.Writing:
113                     // NOTE: Intentionally left empty
114                     break;
115 
116                 case State.Reading:
117                 {
118                     rxQueue.Enqueue(value);
119                     var entry = CurrentEntry;
120                     if(rxQueue.Count != entry.FrameLength + 1)
121                     {
122                         return;
123                     }
124 
125                     var frame = rxQueue.Take(entry.FrameLength).ToArray();
126                     var crc = rxQueue.Skip(entry.FrameLength).First();
127                     var frameValid = !entry.ValidateFrame || entry.IsFrameValid(frame, crc);
128 
129                     FrameReceived?.Invoke(currentProtectedIdentifier, frame, frameValid);
130                     entry.InvokeDataReady(frame, frameValid);
131 
132                     CurrentState = State.Ignoring;
133                     break;
134                 }
135 
136                 case State.Ignoring:
137                     // NOTE: Intentionally left empty
138                     return;
139 
140                 default:
141                     throw new Exception("unreachable");
142             }
143 
144             if(CurrentState == State.Writing)
145             {
146                 CurrentEntry.InvokeStartedTransmission();
147             }
148         }
149 
Transmit(byte[] data)150         public void Transmit(byte[] data)
151         {
152             txQueue.EnqueueRange(data);
153         }
154 
Transmit(byte b)155         public void Transmit(byte b)
156         {
157             txQueue.Enqueue(b);
158         }
159 
Finalize()160         public IEnumerable<byte> Finalize()
161         {
162             if(CurrentState != State.Ignoring)
163             {
164                 CurrentState = State.Ignoring;
165             }
166 
167             var dataToTransmit = txQueue.ToList();
168             txQueue.Clear();
169 
170             var checksum = CurrentEntry.CalculateChecksum(dataToTransmit);
171             dataToTransmit.Add(checksum);
172             return dataToTransmit;
173         }
174 
Finalize(Action<byte> writer)175         public void Finalize(Action<byte> writer)
176         {
177             var finalizedData = Finalize();
178             foreach(var b in finalizedData)
179             {
180                 writer(b);
181             }
182         }
183 
TransmitAndFinalize(byte[] data, Action<byte> writer)184         public void TransmitAndFinalize(byte[] data, Action<byte> writer)
185         {
186             Transmit(data);
187             Finalize(writer);
188         }
189 
TransmitAndFinalize(byte[] data)190         public IEnumerable<byte> TransmitAndFinalize(byte[] data)
191         {
192             Transmit(data);
193             return Finalize();
194         }
195 
Break()196         public void Break()
197         {
198             CurrentState = State.Armed;
199         }
200 
201         public event Action<State, State> StateChanged;
202         public event Action<byte, byte[], bool> FrameReceived;
203 
204         public State CurrentState
205         {
206             get => currentState;
207             protected set
208             {
209                 if(currentState == value)
210                 {
211                     return;
212                 }
213                 var previousState = currentState;
214                 currentState = value;
215                 StateChanged?.Invoke(previousState, CurrentState);
216             }
217         }
218 
219         public bool ValidateProtectedIdentifier { get; set; }
220 
IsProtectedIdentifierValid(byte pid)221         protected bool IsProtectedIdentifierValid(byte pid)
222         {
223             var frameIdentifier = (byte)(pid & 0x7F);
224             var checksum = pid >> 6;
225             var frameIdentifierBits = BitHelper.GetBits(frameIdentifier).Select(b => b ? 1 : 0).ToArray();
226             var computedChecksum =
227                 ((1 ^ frameIdentifierBits[1] ^ frameIdentifierBits[3] ^ frameIdentifierBits[4] ^ frameIdentifierBits[5]) << 1) |
228                 ((frameIdentifierBits[0] ^ frameIdentifierBits[1] ^ frameIdentifierBits[2] ^ frameIdentifierBits[4]) << 0);
229 
230             return computedChecksum == checksum;
231         }
232 
233         protected LINEntry CurrentEntry => entries.TryGetValue(currentProtectedIdentifier, out var entry) ? entry : null;
234 
235         protected readonly IDictionary<byte, LINEntry> entries;
236         protected readonly Queue<byte> rxQueue;
237         protected readonly Queue<byte> txQueue;
238 
239         protected const byte SynchronizationPattern = 0x55;
240 
241         protected byte currentProtectedIdentifier;
242         protected State currentState;
243 
244         public enum State
245         {
246             Ignoring,
247             Armed,
248             Synchronized,
249             SynchronizationFailed,
250             Writing,
251             Reading,
252         }
253 
254         protected class LINEntry : ILINEntry
255         {
LINEntry(byte protectedIdentifier)256             public LINEntry(byte protectedIdentifier)
257             {
258                 ProtectedIdentifier = protectedIdentifier;
259             }
260 
CalculateChecksum(IEnumerable<byte> data)261             public byte CalculateChecksum(IEnumerable<byte> data)
262             {
263                 var checksum = ExtendedChecksum ? ProtectedIdentifier : 0;
264                 foreach(var b in data)
265                 {
266                     checksum += b;
267                     if(checksum > 0xFF)
268                     {
269                         checksum -= 0xFF;
270                     }
271                 }
272                 return (byte)(checksum ^ 0xFF);
273             }
274 
IsFrameValid(IEnumerable<byte> data, byte checksum)275             public bool IsFrameValid(IEnumerable<byte> data, byte checksum)
276             {
277                 return checksum == CalculateChecksum(data);
278             }
279 
InvokeDataReady(byte[] frame, bool checksumValid)280             public void InvokeDataReady(byte[] frame, bool checksumValid) => DataReady?.Invoke(frame, checksumValid);
InvokeStartedTransmission()281             public void InvokeStartedTransmission() => StartedTransmission?.Invoke();
282 
283             public byte ProtectedIdentifier { get; }
284             public LINMode Mode { get; set; }
285             public int FrameLength { get; set; }
286             public bool ValidateFrame { get; set; }
287             public bool ExtendedChecksum { get; set; }
288 
289             public event Action<byte[], bool> DataReady;
290             public event Action StartedTransmission;
291         }
292     }
293 }
294