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