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