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 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Utilities;
11 using Antmicro.Renode.Utilities.Packets;
12 
13 namespace Antmicro.Renode.Core.CAN
14 {
15     public static class ISocketCANFrameExtentions
16     {
17         public static byte[] Encode<T>(this T @this, bool useNetworkByteOrder) where T : ISocketCANFrame
18         {
19             var frame = Packet.Encode<T>(@this);
20             if(useNetworkByteOrder)
21             {
22                 @this.ByteSwap(frame);
23             }
24             return frame;
25         }
26 
27         public static bool TryDecode<T>(this IList<byte> buffer, out T frame, bool useNetworkByteOrder)
28             where T : ISocketCANFrame
29         {
30             frame = default(T);
31             if(useNetworkByteOrder)
32             {
33                 if(!buffer.TryGetByteSwappedData<T>(out var data))
34                 {
35                     return false;
36                 }
37                 buffer = data;
38             }
39 
40             return Packet.TryDecode<T>(buffer, out frame);
41         }
42 
TryDecodeAsSocketCANFrame(this IList<byte> buffer, out ISocketCANFrame frame, bool useNetworkByteOrder)43         public static bool TryDecodeAsSocketCANFrame(this IList<byte> buffer, out ISocketCANFrame frame, bool useNetworkByteOrder)
44         {
45             if(!Packet.TryDecode<SocketCANFrameHeader>(buffer, out var header))
46             {
47                 frame = default(ISocketCANFrame);
48                 return false;
49             }
50 
51             if(header.extendedFrameLengthFrame)
52             {
53                 return buffer.TryDecode<XLSocketCANFrame>(out frame, useNetworkByteOrder);
54             }
55 
56             if(header.flexibleDataRateFrame)
57             {
58                 return buffer.TryDecode<FlexibleSocketCANFrame>(out frame, useNetworkByteOrder);
59             }
60 
61             return buffer.TryDecode<ClassicalSocketCANFrame>(out frame, useNetworkByteOrder);
62         }
63 
64         public static bool TryGetByteSwappedData<T>(this IList<byte> buffer, out byte[] data) where T : ISocketCANFrame
65         {
66             var prototype = default(T);
67             if(buffer.Count < prototype.Size)
68             {
69                 data = new byte[0];
70                 return false;
71             }
72             data = buffer.Take(prototype.Size).ToArray();
73             prototype.ByteSwap(data);
74             return true;
75         }
76 
ByteSwap(this ISocketCANFrame @this, byte[] frame)77         public static void ByteSwap(this ISocketCANFrame @this, byte[] frame)
78         {
79             if(frame.Length != @this.Size)
80             {
81                 throw new ArgumentException($"Number of bytes in {nameof(frame)} must match the size of a SocketCAN structure", nameof(frame));
82             }
83             foreach(var marker in @this.MultibyteFields)
84             {
85                 Misc.EndiannessSwapInPlace(frame, marker.size, marker.offset, marker.size);
86             }
87         }
88 
89         private static bool TryDecode<T>(this IList<byte> buffer, out ISocketCANFrame frame, bool useNetworkByteOrder)
90             where T : ISocketCANFrame
91         {
92             if(buffer.TryDecode<T>(out T tFrame, useNetworkByteOrder))
93             {
94                 frame = tFrame;
95                 return true;
96             }
97             frame = default(ISocketCANFrame);
98             return false;
99         }
100 
101         [LeastSignificantByteFirst]
102         private struct SocketCANFrameHeader
103         {
104 #pragma warning disable 649
105             [PacketField, Offset(doubleWords: 1, bytes: 1, bits:  2), Width(1)]
106             public bool flexibleDataRateFrame;
107             [PacketField, Offset(doubleWords: 1, bytes: 0, bits: 7), Width(1)]
108             public bool extendedFrameLengthFrame;
109 #pragma warning restore 649
110 
111             public const int Size = 8;
112         }
113     }
114 
115     public interface ISocketCANFrame
116     {
117         int Size { get; }
118         IEnumerable<FieldMarker> MultibyteFields { get; }
119     }
120 
121     public struct FieldMarker
122     {
CreateAntmicro.Renode.Core.CAN.FieldMarker123         public static FieldMarker Create(int size, int offset) =>
124             new FieldMarker { size = size, offset = offset };
125 
126         public int size;
127         public int offset;
128     }
129 
130     [LeastSignificantByteFirst]
131     public struct ClassicalSocketCANFrame : ISocketCANFrame
132     {
FromCANMessageFrameAntmicro.Renode.Core.CAN.ClassicalSocketCANFrame133         public static ClassicalSocketCANFrame FromCANMessageFrame(CANMessageFrame msg)
134         {
135             return new ClassicalSocketCANFrame
136             {
137                 id = msg.Id,
138                 errorMessageFrame = false,
139                 remoteTransmissionRequest = msg.RemoteFrame,
140                 extendedFrameFormat = msg.ExtendedFormat,
141                 length = msg.Data.Length,
142                 data = msg.Data.CopyAndResize(MaxDataLength)
143             };
144         }
145 
ToStringAntmicro.Renode.Core.CAN.ClassicalSocketCANFrame146         public override string ToString() => $@"ClassicalSocketCANFrame {{
147     id: 0x{id:X},
148     errorMessageFrame: {errorMessageFrame},
149     remoteTransmissionRequest: {remoteTransmissionRequest},
150     extendedFrameFormat: {extendedFrameFormat},
151     length: {length},
152     data: {Misc.PrettyPrintCollectionHex(data)}
153 }}";
154 
155         public IEnumerable<FieldMarker> MultibyteFields => multibyteFields;
156 
157         int ISocketCANFrame.Size => ClassicalSocketCANFrame.Size;
158 
159 #pragma warning disable 649
160         // can_id
161         [PacketField, Offset(doubleWords: 0, bits:  0), Width(29)]
162         public uint id;
163         [PacketField, Offset(doubleWords: 0, bits:  29), Width(1)]
164         public bool errorMessageFrame;
165         [PacketField, Offset(doubleWords: 0, bits:  30), Width(1)]
166         public bool remoteTransmissionRequest;
167         [PacketField, Offset(doubleWords: 0, bits:  31), Width(1)]
168         public bool extendedFrameFormat;
169 
170         // len
171         [PacketField, Offset(doubleWords: 1, bits:  0), Width(8)]
172         public int length;
173 
174         // data
175         [PacketField, Offset(quadWords: 1), Width(MaxDataLength)]
176         public byte[] data;
177 #pragma warning restore 649
178 
179         public const int MaxDataLength = 8;
180         public const int Size = MaxDataLength + 8;
181 
182         private readonly static FieldMarker[] multibyteFields = new FieldMarker[]
183         {
184             FieldMarker.Create(size: 4, offset: 0)
185         };
186     }
187 
188     [LeastSignificantByteFirst]
189     public struct FlexibleSocketCANFrame : ISocketCANFrame
190     {
FromCANMessageFrameAntmicro.Renode.Core.CAN.FlexibleSocketCANFrame191         public static FlexibleSocketCANFrame FromCANMessageFrame(CANMessageFrame msg)
192         {
193             return new FlexibleSocketCANFrame
194             {
195                 id = msg.Id,
196                 errorMessageFrame = false,
197                 remoteTransmissionRequest = msg.RemoteFrame,
198                 extendedFrameFormat = msg.ExtendedFormat,
199                 length = msg.Data.Length,
200                 bitRateSwitch = msg.BitRateSwitch,
201                 errorStateIndicator = false,
202                 flexibleDataRateFrame = true,
203                 data = msg.Data.CopyAndResize(MaxDataLength)
204             };
205         }
206 
ToStringAntmicro.Renode.Core.CAN.FlexibleSocketCANFrame207         public override string ToString() => $@"FlexibleSocketCANFrame {{
208     id: 0x{id:X},
209     errorMessageFrame: {errorMessageFrame},
210     remoteTransmissionRequest: {remoteTransmissionRequest},
211     extendedFrameFormat: {extendedFrameFormat},
212     length: {length},
213     bitRateSwitch: {bitRateSwitch},
214     errorStateIndicator: {errorStateIndicator},
215     flexibleDataRateFrame: {flexibleDataRateFrame},
216     data: {Misc.PrettyPrintCollectionHex(data)}
217 }}";
218 
219         public IEnumerable<FieldMarker> MultibyteFields => multibyteFields;
220 
221         int ISocketCANFrame.Size => FlexibleSocketCANFrame.Size;
222 
223 #pragma warning disable 649
224         // can_id
225         [PacketField, Offset(doubleWords: 0, bits:  0), Width(29)]
226         public uint id;
227         [PacketField, Offset(doubleWords: 0, bits:  29), Width(1)]
228         public bool errorMessageFrame;
229         [PacketField, Offset(doubleWords: 0, bits:  30), Width(1)]
230         public bool remoteTransmissionRequest;
231         [PacketField, Offset(doubleWords: 0, bits:  31), Width(1)]
232         public bool extendedFrameFormat;
233 
234         // len
235         [PacketField, Offset(doubleWords: 1, bytes: 0), Width(8)]
236         public int length;
237 
238         // flags
239         [PacketField, Offset(doubleWords: 1, bytes: 1, bits:  0), Width(1)]
240         public bool bitRateSwitch;
241         [PacketField, Offset(doubleWords: 1, bytes: 1, bits:  1), Width(1)]
242         public bool errorStateIndicator;
243         // should always be set for FD CAN frame
244         [PacketField, Offset(doubleWords: 1, bytes: 1, bits:  2), Width(1)]
245         public bool flexibleDataRateFrame;
246 
247         // data
248         [PacketField, Offset(quadWords: 1), Width(MaxDataLength)]
249         public byte[] data;
250 #pragma warning restore 649
251 
252         public const int MaxDataLength = 64;
253         public const int Size = MaxDataLength + 8;
254 
255         private readonly static FieldMarker[] multibyteFields = new FieldMarker[]
256         {
257             FieldMarker.Create(size: 4, offset: 0)
258         };
259     }
260 
261     [LeastSignificantByteFirst]
262     public struct XLSocketCANFrame : ISocketCANFrame
263     {
ToStringAntmicro.Renode.Core.CAN.XLSocketCANFrame264         public override string ToString() => $@"XLSocketCANFrame {{
265     priority: 0x{priority:X},
266     virtualCANNetworkId: 0x{virtualCANNetworkId:X},
267     simpleExtendedContent: {simpleExtendedContent},
268     extendedFrameLengthFrame: {extendedFrameLengthFrame},
269     serviceDataUnit: 0x{serviceDataUnit:X},
270     length: {length},
271     acceptanceField: 0x{acceptanceField:X},
272     data: {Misc.PrettyPrintCollectionHex(data)}
273 }}";
274 
275         public IEnumerable<FieldMarker> MultibyteFields => multibyteFields;
276 
277         int ISocketCANFrame.Size => XLSocketCANFrame.Size;
278 
279 #pragma warning disable 649
280         // prio
281         [PacketField, Offset(doubleWords: 0, bits: 0), Width(11)]
282         public uint priority;
283         [PacketField, Offset(doubleWords: 0, bits: 16), Width(8)]
284         public byte virtualCANNetworkId;
285 
286         // flags
287         [PacketField, Offset(doubleWords: 1, bytes: 0, bits:  0), Width(1)]
288         public bool simpleExtendedContent;
289         [PacketField, Offset(doubleWords: 1, bytes: 0, bits:  7), Width(1)]
290         public bool extendedFrameLengthFrame;
291 
292         // sdt
293         [PacketField, Offset(doubleWords: 1, bytes: 1, bits: 0), Width(8)]
294         public byte serviceDataUnit;
295 
296         // len
297         [PacketField, Offset(doubleWords: 1, words: 1), Width(16)]
298         public int length;
299 
300         // af
301         [PacketField, Offset(doubleWords: 2), Width(32)]
302         public uint acceptanceField;
303 
304         // data
305         [PacketField, Offset(doubleWords: 3), Width(MaxDataLength)]
306         public byte[] data;
307 #pragma warning restore 649
308 
309         public const int MaxDataLength = 2048;
310         public const int Size = MaxDataLength + 12;
311 
312         private readonly static FieldMarker[] multibyteFields = new FieldMarker[]
313         {
314             FieldMarker.Create(size: 4, offset: 0), // prio
315             FieldMarker.Create(size: 2, offset: 6), // len
316             FieldMarker.Create(size: 4, offset: 8)  // af
317         };
318     }
319 }
320