1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using Antmicro.Renode.Core; 8 using Antmicro.Renode.Exceptions; 9 #if !PLATFORM_WINDOWS 10 using System; 11 using System.Collections.Generic; 12 using System.IO; 13 using System.Linq; 14 using Antmicro.Renode.Utilities; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Migrant; 17 using Antmicro.Migrant.Hooks; 18 using AntShell.Terminal; 19 using Mono.Unix; 20 #endif 21 22 namespace Antmicro.Renode.Peripherals.Wireless 23 { 24 public static class SlipRadioExtensions 25 { CreateSlipRadio(this Emulation emulation, string name, string fileName)26 public static void CreateSlipRadio(this Emulation emulation, string name, string fileName) 27 { 28 #if !PLATFORM_WINDOWS 29 emulation.ExternalsManager.AddExternal(new SlipRadio(fileName), name); 30 #else 31 throw new RecoverableException("Creating SlipRadio is not supported on Windows."); 32 #endif 33 } 34 } 35 36 #if !PLATFORM_WINDOWS 37 public class SlipRadio : ISlipRadio 38 { SlipRadio(string linkName)39 public SlipRadio(string linkName) 40 { 41 this.linkName = linkName; 42 buffer = new List<byte>(); 43 Initialize(); 44 } 45 46 [PostDeserialization] Initialize()47 private void Initialize() 48 { 49 ptyStream = new PtyUnixStream(); 50 io = new IOProvider { Backend = new StreamIOSource(ptyStream) }; 51 io.ByteRead += CharReceived; 52 CreateSymlink(linkName); 53 } 54 ReceiveFrame(byte[] frame, IRadio sender)55 public virtual void ReceiveFrame(byte[] frame, IRadio sender) 56 { 57 EncapsulateAndSend(frame); 58 } 59 CharReceived(int value)60 public void CharReceived(int value) 61 { 62 buffer.Add((byte)value); 63 if((byte)value == END) 64 { 65 if(buffer.Count > 8) 66 { 67 HandleFrame(buffer.ToArray()); 68 } 69 buffer.Clear(); 70 } 71 } 72 Reset()73 public void Reset() 74 { 75 buffer.Clear(); 76 } 77 Dispose()78 public void Dispose() 79 { 80 io.Dispose(); 81 try 82 { 83 symlink.Delete(); 84 } 85 catch(FileNotFoundException e) 86 { 87 throw new RecoverableException(string.Format("There was an error when removing symlink `{0}': {1}", symlink.FullName, e.Message)); 88 } 89 } 90 91 public event Action<IRadio, byte[]> FrameSent; 92 93 public int Channel { get; set; } 94 HandleFrame(byte[] frame)95 protected virtual void HandleFrame(byte[] frame) 96 { 97 var fs = FrameSent; 98 if(fs != null) 99 { 100 fs.Invoke(this, frame); 101 } 102 else 103 { 104 this.Log(LogLevel.Warning, "FrameSent is not initialized. Am I connected to medium?"); 105 } 106 } 107 Encapsulate(byte[] frame)108 protected virtual byte[] Encapsulate(byte[] frame) 109 { 110 var result = new List<byte>(); 111 112 foreach(var value in frame) 113 { 114 switch(value) 115 { 116 case END: 117 result.Add(ESC); 118 result.Add(ESC_END); 119 break; 120 case ESC: 121 result.Add(ESC); 122 result.Add(ESC_ESC); 123 break; 124 default: 125 result.Add(value); 126 break; 127 } 128 } 129 var engine = new CRCEngine(CRCPolynomial.CRC32); 130 var crc = engine.Calculate(result); 131 result.AddRange(new byte[] {(byte)(crc & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 24) & 0xFF)}); 132 result.Add(END); 133 return result.ToArray(); 134 } 135 Decapsulate(byte[] frame)136 protected byte[] Decapsulate(byte[] frame) 137 { 138 var result = new List<byte>(); 139 bool isEscaped = false; 140 141 foreach(var value in frame) 142 { 143 switch(value) 144 { 145 case END: 146 return result.ToArray(); 147 case ESC: 148 isEscaped = true; 149 continue; 150 case ESC_END: 151 if(isEscaped) 152 { 153 result.Add(END); 154 isEscaped = false; 155 } 156 else 157 { 158 result.Add(ESC_END); 159 } 160 break; 161 case ESC_ESC: 162 if(isEscaped) 163 { 164 result.Add(ESC); 165 isEscaped = false; 166 } 167 else 168 { 169 result.Add(ESC_ESC); 170 } 171 break; 172 default: 173 isEscaped = false; 174 result.Add(value); 175 break; 176 } 177 } 178 179 Logger.Log(LogLevel.Error, "Received an unfinished frame of length {0}, dropping...", result.Count); 180 return new byte[0]; //TODO or nul? 181 } 182 EncapsulateAndSend(byte[] data)183 protected void EncapsulateAndSend(byte[] data) 184 { 185 var encoded = Encapsulate(data); 186 ptyStream.Write(encoded, 0, encoded.Length); 187 } 188 CreateSymlink(string linkName)189 private void CreateSymlink(string linkName) 190 { 191 if(File.Exists(linkName)) 192 { 193 try 194 { 195 File.Delete(linkName); 196 } 197 catch(Exception e) 198 { 199 throw new RecoverableException(string.Format("There was an error when removing existing `{0}' symlink: {1}", linkName, e.Message)); 200 } 201 } 202 try 203 { 204 var slavePtyFile = new UnixFileInfo(ptyStream.SlaveName); 205 symlink = slavePtyFile.CreateSymbolicLink(linkName); 206 } 207 catch(Exception e) 208 { 209 throw new RecoverableException(string.Format("There was an error when when creating a symlink `{0}': {1}", linkName, e.Message)); 210 } 211 Logger.Log(LogLevel.Info, "Created a Slip Radio pty connection to {0}", linkName); 212 } 213 214 [Transient] 215 protected PtyUnixStream ptyStream; 216 [Transient] 217 private IOProvider io; 218 219 private readonly List<byte> buffer; 220 private readonly string linkName; 221 private UnixSymbolicLinkInfo symlink; 222 223 private const byte END = 0xC0; 224 private const byte ESC = 0xDB; 225 private const byte ESC_END = 0xDC; 226 private const byte ESC_ESC = 0xDD; 227 } 228 #endif 229 } 230