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.Runtime.InteropServices;
9 using System.Text;
10 #if PLATFORM_LINUX
11 using Mono.Unix.Native;
12 using Mono.Unix;
13 #endif
14 
15 namespace Antmicro.Renode.Utilities
16 {
17     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
18     public struct InterfaceRequest
19     {
20         // This class represents ifreq structure
InterfaceRequestAntmicro.Renode.Utilities.InterfaceRequest21         public InterfaceRequest(string name, int interfaceIndex = 0)
22         {
23             if(name.Length >= InterfaceNameSize)
24             {
25                 throw new ArgumentException($"Interface name must be no longer than {InterfaceNameSize - 1} characters", nameof(name));
26             }
27             Name = Encoding.ASCII.GetBytes(name).CopyAndResize(InterfaceNameSize);
28             InterfaceIndex = interfaceIndex;
29         }
30 
31         // NOTE: layout of this stucture is deliberate
32         [MarshalAs(UnmanagedType.ByValArray, SizeConst=InterfaceNameSize)]
33         public byte[] Name;
34         public int InterfaceIndex;
35 
36         public const int InterfaceNameSize = 16;
37     }
38 
39     [StructLayout(LayoutKind.Sequential)]
40     public struct SocketAddressCan
41     {
42         // This class represents sockaddr_can structure
SocketAddressCanAntmicro.Renode.Utilities.SocketAddressCan43         public SocketAddressCan(int interfaceIndex)
44         {
45             CanFamily = AddressFamilyCan;
46             CanInterfaceIndex = interfaceIndex;
47         }
48 
49         // NOTE: layout of this stucture is deliberate
50         // sockaddr_can.can_family
51         public readonly ushort CanFamily;
52         // sockaddr_can.can_ifindex
53         public int CanInterfaceIndex;
54 
55         // AF_CAN
56         private const int AddressFamilyCan = 29;
57     }
58 
59     public class LibCWrapper
60     {
Open(string path, int mode)61         public static int Open(string path, int mode)
62         {
63 #if !PLATFORM_LINUX
64             throw new NotSupportedException("This API is available on Linux only!");
65 #else
66             var marshalledPath = Marshal.StringToHGlobalAnsi(path);
67             var result = open(marshalledPath, mode);
68             Marshal.FreeHGlobal(marshalledPath);
69             return result;
70 #endif
71         }
72 
Close(int fd)73         public static int Close(int fd)
74         {
75 #if !PLATFORM_LINUX
76             throw new NotSupportedException("This API is available on Linux only!");
77 #else
78             return close(fd);
79 #endif
80         }
81 
Write(int fd, IntPtr buffer, int count)82         public static bool Write(int fd, IntPtr buffer, int count)
83         {
84 #if !PLATFORM_LINUX
85             throw new NotSupportedException("This API is available on Linux only!");
86 #else
87             var written = 0;
88             while(written < count)
89             {
90                 int writtenThisTime = write(fd, buffer + written, count - written);
91                 if(writtenThisTime <= 0)
92                 {
93                     return false;
94                 }
95 
96                 written += writtenThisTime;
97             }
98 
99             return true;
100 #endif
101         }
102 
Read(int fd, int count)103         public static byte[] Read(int fd, int count)
104         {
105 #if !PLATFORM_LINUX
106             throw new NotSupportedException("This API is available on Linux only!");
107 #else
108             byte[] result = null;
109             var buffer = Marshal.AllocHGlobal(count);
110             var r = read(fd, buffer, count);
111             if(r > 0)
112             {
113                 result = new byte[r];
114                 Marshal.Copy(buffer, result, 0, r);
115             }
116             Marshal.FreeHGlobal(buffer);
117             return result ?? new byte[0];
118 #endif
119         }
120 
Read(int fd, int count, int timeout, Func<bool> shouldCancel)121         public static byte[] Read(int fd, int count, int timeout, Func<bool> shouldCancel)
122         {
123 #if !PLATFORM_LINUX
124             throw new NotSupportedException("This API is available on Linux only!");
125 #else
126             int pollResult;
127             var pollData = new Pollfd {
128                 fd = fd,
129                 events = PollEvents.POLLIN
130             };
131 
132             do
133             {
134                 pollResult = Syscall.poll(new [] { pollData }, timeout);
135                 if(shouldCancel())
136                 {
137                     return null;
138                 }
139             }
140             while(UnixMarshal.ShouldRetrySyscall(pollResult));
141 
142             if(pollResult > 0)
143             {
144                 return Read(fd, count);
145             }
146             else
147             {
148                 return null;
149             }
150 #endif
151         }
152 
Ioctl(int fd, int request, int arg)153         public static int Ioctl(int fd, int request, int arg)
154         {
155 #if !PLATFORM_LINUX
156             throw new NotSupportedException("This API is available on Linux only!");
157 #else
158             return ioctl(fd, request, arg);
159 #endif
160         }
161 
Ioctl(int fd, int request, IntPtr arg)162         public static int Ioctl(int fd, int request, IntPtr arg)
163         {
164 #if !PLATFORM_LINUX
165             throw new NotSupportedException("This API is available on Linux only!");
166 #else
167             return ioctl(fd, request, arg);
168 #endif
169         }
170 
Ioctl(int fd, int request, ref InterfaceRequest ifreq)171         public static int Ioctl(int fd, int request, ref InterfaceRequest ifreq)
172         {
173 #if !PLATFORM_LINUX
174             throw new NotSupportedException("This API is available on Linux only!");
175 #else
176             return ioctl(fd, request, ref ifreq);
177 #endif
178         }
179 
Strcpy(IntPtr dst, IntPtr src)180         public static IntPtr Strcpy(IntPtr dst, IntPtr src)
181         {
182 #if !PLATFORM_LINUX
183             throw new NotSupportedException("This API is available on Linux only!");
184 #else
185             return strcpy(dst, src);
186 #endif
187         }
188 
Strerror(int id)189         public static string Strerror(int id)
190         {
191 #if !PLATFORM_LINUX
192             throw new NotSupportedException("This API is available on Linux only!");
193 #else
194             return Marshal.PtrToStringAuto(strerror(id));
195 #endif
196         }
197 
GetLastError()198         public static string GetLastError()
199         {
200             return Strerror(Marshal.GetLastWin32Error());
201         }
202 
Socket(int domain, int type, int protocol)203         public static int Socket(int domain, int type, int protocol)
204         {
205 #if !PLATFORM_LINUX
206             throw new NotSupportedException("This API is available on Linux only!");
207 #else
208             return socket(domain, type, protocol);
209 #endif
210         }
211 
SetSocketOption(int socket, int level, int optionName, ref int optionValue)212         public static int SetSocketOption(int socket, int level, int optionName, ref int optionValue)
213         {
214 #if !PLATFORM_LINUX
215             throw new NotSupportedException("This API is available on Linux only!");
216 #else
217             return setsockopt(socket, level, optionName, ref optionValue, 4);
218 #endif
219         }
220 
Bind(int domain, SocketAddressCan addr, int addrSize)221         public static int Bind(int domain, SocketAddressCan addr, int addrSize)
222         {
223 #if !PLATFORM_LINUX
224             throw new NotSupportedException("This API is available on Linux only!");
225 #else
226             return bind(domain, ref addr, addrSize);
227 #endif
228         }
229 
230         #region Externs
231 
232         [DllImport("libc", EntryPoint = "open", SetLastError = true)]
open(IntPtr pathname, int flags)233         private static extern int open(IntPtr pathname, int flags);
234 
235         [DllImport("libc", EntryPoint = "strcpy")]
strcpy(IntPtr dst, IntPtr src)236         private static extern IntPtr strcpy(IntPtr dst, IntPtr src);
237 
238         [DllImport("libc", EntryPoint = "ioctl")]
ioctl(int d, int request, IntPtr a)239         private static extern int ioctl(int d, int request, IntPtr a);
240 
241         [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
ioctl(int d, int request, int a)242         private static extern int ioctl(int d, int request, int a);
243 
244         [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
ioctl(int d, int request, ref InterfaceRequest ifreq)245         public static extern int ioctl(int d, int request, ref InterfaceRequest ifreq);
246 
247         [DllImport("libc", EntryPoint = "socket", SetLastError = true)]
socket(int domain, int type, int protocol)248         private static extern int socket(int domain, int type, int protocol);
249 
250         [DllImport("libc", EntryPoint = "setsockopt", SetLastError = true)]
setsockopt(int socket, int level, int optionName, ref int optionValue, int optionLength)251         private static extern int setsockopt(int socket, int level, int optionName, ref int optionValue, int optionLength);
252 
253         [DllImport("libc", EntryPoint = "bind", SetLastError = true)]
bind(int sockfd, ref SocketAddressCan addr, int addrSize)254         public static extern int bind(int sockfd, ref SocketAddressCan addr, int addrSize);
255 
256         [DllImport("libc", EntryPoint = "close")]
close(int fd)257         private static extern int close(int fd);
258 
259         [DllImport("libc", EntryPoint = "write", SetLastError = true)]
write(int fd, IntPtr buf, int count)260         private static extern int write(int fd, IntPtr buf, int count);
261 
262         [DllImport("libc", EntryPoint = "read")]
read(int fd, IntPtr buf, int count)263         private static extern int read(int fd, IntPtr buf, int count);
264 
265         [DllImport("libc", EntryPoint = "strerror")]
strerror(int fd)266         private static extern IntPtr strerror(int fd);
267 
268         #endregion
269     }
270 }
271