1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using System.Security.Cryptography; 13 using System.Linq; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.Wireless 17 { 18 public class EmberRadio : IDoubleWordPeripheral, IRadio 19 { EmberRadio(IMachine machine)20 public EmberRadio(IMachine machine) 21 { 22 this.machine = machine; 23 sysbus = machine.GetSystemBus(this); 24 25 currentKey = new byte[16]; 26 currentData = new byte[16]; 27 currentValue = new byte[16]; 28 Tx = new GPIO(); 29 Rx = new GPIO(); 30 Tim = new GPIO(); 31 irqStatus = new uint[256]; 32 Reset(); 33 } 34 35 public int Channel { get; set; } 36 37 uint macRxConfig; 38 Reset()39 public void Reset() 40 { 41 Array.Clear(currentKey, 0, currentKey.Length); 42 Array.Clear(currentData, 0, currentData.Length); 43 dataPointer = 0; 44 valuePointer = 0; 45 // TODO: 46 } 47 48 [ConnectionRegion("encryptor")] ReadDoubleWordEncryptor(long offset)49 public uint ReadDoubleWordEncryptor(long offset) 50 { 51 switch((EncryptorRegister)offset) 52 { 53 case EncryptorRegister.Value: 54 if(valueToEncryptHasChanged) 55 { 56 Encrypt(); 57 valueToEncryptHasChanged = false; 58 } 59 var result = 0u; 60 for(var i = 0; i < 3; i++) 61 { 62 result |= currentValue[valuePointer + i]; 63 result <<= 8; 64 } 65 result |= currentValue[valuePointer + 3]; 66 valuePointer = (valuePointer + 4) % 16; 67 return result; 68 } 69 this.LogUnhandledRead(offset); 70 return 0; 71 } 72 73 [ConnectionRegion("encryptor")] WriteDoubleWordEncryptor(long offset, uint value)74 public void WriteDoubleWordEncryptor(long offset, uint value) 75 { 76 if(offset >= EncryptorKeyRegisterBegin && offset < EncryptorKeyRegisterEnd) 77 { 78 valueToEncryptHasChanged = true; 79 var localOffset = offset - EncryptorKeyRegisterBegin; 80 for(var i = 0; i < 4; i++) 81 { 82 currentKey[15 - localOffset - i] = (byte)value; 83 value >>= 8; 84 } 85 } 86 else 87 { 88 switch((EncryptorRegister)offset) 89 { 90 case EncryptorRegister.Data: 91 valueToEncryptHasChanged = true; 92 for(var i = 0; i < 4; i++) 93 { 94 currentData[dataPointer + 3 - i] = (byte)value; 95 value >>= 8; 96 } 97 dataPointer = (dataPointer + 4) % 16; 98 break; 99 case EncryptorRegister.Control: 100 // we currently ignore it 101 break; 102 default: 103 this.LogUnhandledWrite(offset, value); 104 break; 105 } 106 } 107 } 108 109 [ConnectionRegion("irq")] ReadDoubleWordIRQ(long offset)110 public uint ReadDoubleWordIRQ(long offset) 111 { 112 this.Log(LogLevel.Warning, "ReadDoubleWordIRQ({0:X})", offset); 113 switch(offset) 114 { 115 case 0x0: 116 return Rx.IsSet ? 0x20u : 0; 117 case 0x4: 118 return Tx.IsSet ? 0x400u : 0; 119 case 0x40: 120 return 0xFF; 121 /* case 0x018: 122 lastFlipVal = 1 - lastFlipVal; 123 return lastFlipVal * 0xFFFFFFFF;*/ 124 case 0x01C: 125 return (uint)((int)((machine.ElapsedVirtualTime.TimeElapsed).TotalMilliseconds * 1000) & 0xFFFFFFFF); 126 case 0x18: 127 return INT_MGMTFLAG; 128 } 129 return 0xFF;//irqStatus[offset]; 130 } 131 ReadDoubleWord(long offset)132 public uint ReadDoubleWord(long offset) 133 { 134 this.Log(LogLevel.Noisy, "ReadDoubleWord({0:X})", offset); 135 switch(offset) 136 { 137 case 0x1010: 138 return MAC_TX_ST_ADDR_A; 139 case 0x1014: 140 return MAC_TX_END_ADDR_A; 141 case 0x1020: 142 this.Log(LogLevel.Warning, "packlength = {0} (0x{0:X})", packLength); 143 return 14; // > 4 144 case 0x1024: 145 return 0x0; 146 case 0x1038: 147 // MAC_TIMER 148 mac_timer += 1; 149 return mac_timer; 150 case 0x1050: 151 // Tx.Set(); 152 return 0xF; 153 case 0x1054: 154 return 0xFFFF; 155 case 0x1064: // MAC_ACK_STROBE 156 return 1; 157 case 0x1068: // MAC_STATUS 158 return 0x423; 159 case 0x107C: // MAC_TX_ACK_FRAME 160 return 0x10; 161 //return 0xCC20; 162 case 0x1084: // MAC_RX_CONFIG 163 this.Log(LogLevel.Warning, "MAC_RX_CONFIG {0:X}", macRxConfig & 0xFFFFFFFE); 164 return macRxConfig & 0xFFFFFFFE; 165 default: 166 this.LogUnhandledRead(offset); 167 return 0; 168 } 169 } 170 171 uint[] irqStatus; 172 uint INT_MGMTFLAG; 173 174 /* public void ReceiveFakePacket() 175 { 176 //ReceivePacket("41C8003412FFFFBA1F000002E1800081000080E10200001FBA48656C6C6F001F5B"); 177 ReceivePacket("41C8003412FFFFBA1F000002E1800081000080E10200001FBA48656C6C6F001F5B");//208041C8 178 }*/ 179 180 [ConnectionRegion("irq")] WriteDoubleWordIRQ(long offset, uint value)181 public void WriteDoubleWordIRQ(long offset, uint value) 182 { 183 switch(offset) 184 { 185 case 0x18: 186 INT_MGMTFLAG = value; 187 break; 188 189 } 190 191 irqStatus[offset] = value; 192 193 Rx.Unset(); 194 Tim.Unset(); 195 Tx.Unset(); 196 197 this.Log(LogLevel.Warning, "WriteDoubleWordIRQ({0:X}, {1:X})", offset, value); 198 } 199 200 WriteDoubleWord(long offset, uint value)201 public void WriteDoubleWord(long offset, uint value) 202 { 203 this.Log(LogLevel.Warning, "WriteDoubleWord ({0:X}, 0x{1:X})", offset, value); 204 switch(offset) 205 { 206 // ////////////// 207 // i think the decision on what addr to use to rx/tx (ADDR_A or ADDR_B) is based on register MAC_DMA_CONFIG_xX_LOAD_A/B somehow @ 0x40002030 208 // ////////////// 209 // 210 case 0x1000: // MAC_RX_ST_ADDR_A 211 MAC_RX_ST_ADDR_A = value; 212 break; 213 /*case 0x1004: // MAC_RX_END_ADDR_A 214 MAC_RX_END_ADDR_A = value; 215 break;*/ 216 case 0x1008: // MAC_RX_ST_ADDR_B 217 MAC_RX_ST_ADDR_B = value; 218 break; 219 /* case 0x100C: // MAC_RX_END_ADDR_B 220 MAC_RX_END_ADDR_B = value; 221 break;*/ 222 case 0x1010: // MAC_TX_ST_ADDR_A 223 MAC_TX_ST_ADDR_A = value; 224 this.Log(LogLevel.Warning, "Setting MAC_TX_ST_A to {0:X}", value); 225 break; 226 case 0x1014: // MAC_TX_END_ADDR_A 227 MAC_TX_END_ADDR_A = value; 228 this.Log(LogLevel.Noisy, "Setting MAC_TX_END_A to {0:X}", value); 229 break; 230 /* case 0x1018: // MAC_TX_ST_ADDR_B 231 MAC_TX_ST_ADDR_B = value; 232 break; 233 case 0x101C: // MAC_TX_END_ADDR_B 234 MAC_TX_END_ADDR_B = value; 235 break;*/ 236 237 case 0x1060: 238 // MAC_TX_STROBE 239 if((value & 0x1) > 0) 240 { 241 // TODO: we should deterimine which ADDR to use 242 int packet_len = sysbus.ReadByte(MAC_TX_ST_ADDR_A); 243 sysbus.WriteByte(MAC_TX_ST_ADDR_A, 0); // clear len 244 if(packet_len == 0) 245 { 246 break; 247 } 248 this.Log(LogLevel.Warning, "Sending packet of size {0}", packet_len); 249 //string s = ""; 250 var dataToSend = new byte[packet_len]; 251 for(uint j = 0; j < packet_len; j++) 252 { 253 dataToSend[j] = sysbus.ReadByte(MAC_TX_ST_ADDR_A + j + 1); 254 // s = s + string.Format("{0:X2}", sysbus.ReadByte(MAC_TX_ST_ADDR_A + j + 1)); 255 } 256 this.Log(LogLevel.Info, "data = {0}", dataToSend.Select(x => x.ToString("X")).Stringify()); 257 var frameSent = FrameSent; 258 if(frameSent != null) 259 { 260 frameSent(this, dataToSend); 261 } 262 } 263 264 if((value & 0x8) > 0) 265 { 266 int packet_len = sysbus.ReadByte(MAC_TX_ST_ADDR_A); 267 this.Log(LogLevel.Noisy, "Adding CRC."); 268 269 if(packet_len == 0) 270 { 271 break; 272 } 273 this.Log(LogLevel.Noisy, "Adding CRC to packet of size {0}", packet_len); 274 275 var data = new byte[packet_len]; 276 for(uint j = 0; j < packet_len; j++) 277 data[j] = sysbus.ReadByte(MAC_TX_ST_ADDR_A + 1 + j); 278 279 ushort crc = count_crc(data); 280 this.Log(LogLevel.Noisy, "Counted CRC = {0:X}", crc); 281 282 sysbus.WriteByte((ulong)(MAC_TX_ST_ADDR_A + packet_len - 1), (byte)(crc & 0xFF)); 283 sysbus.WriteByte((ulong)(MAC_TX_ST_ADDR_A + packet_len), (byte)((crc >> 8) & 0xFF)); 284 285 } 286 break; 287 case 0x1084: 288 macRxConfig = value; 289 break; 290 default: 291 this.Log(LogLevel.Warning, "WriteDoubleWord missed ({0:X}, {1:X})", offset, value); 292 break; 293 } 294 } 295 296 #region IRadio implementation 297 298 public event Action<IRadio, byte[]> FrameSent; 299 ReceiveFrame(byte[] frame, IRadio sender)300 public void ReceiveFrame(byte[] frame, IRadio sender) 301 { 302 this.Log(LogLevel.Warning, "packet as bytes '{0}' of len {1}", frame.Select(x => String.Format("0x{0:X}", x)).Aggregate((x, y) => x + " " + y), frame.Length); 303 packLength = (uint)frame.Length; 304 sysbus.WriteByte(MAC_RX_ST_ADDR_A, (byte)(frame.Length)); 305 sysbus.WriteByte(MAC_RX_ST_ADDR_B, (byte)(frame.Length)); 306 307 for(int i = 0; i < frame.Length; ++i) 308 { 309 sysbus.WriteByte((ulong)(MAC_RX_ST_ADDR_A + i + 1), frame[i]); 310 sysbus.WriteByte((ulong)(MAC_RX_ST_ADDR_B + i + 1), frame[i]); 311 } 312 313 /* ushort crc = count_crc(data); 314 this.Log(LogType.Noisy, "Counted CRC = {0:X}", crc); 315 this.SystemBus.WriteByte(MAC_RX_ST_ADDR_A + packet_data.Length - 1, (byte)(crc & 0xFF)); 316 this.SystemBus.WriteByte(MAC_RX_ST_ADDR_A + packet_data.Length, (byte)((crc >> 8) & 0xFF)); 317 this.SystemBus.WriteByte(MAC_RX_ST_ADDR_B + packet_data.Length - 1, (byte)(crc & 0xFF)); 318 this.SystemBus.WriteByte(MAC_RX_ST_ADDR_B + packet_data.Length, (byte)((crc >> 8) & 0xFF)); 319 */ 320 Rx.Set(); 321 Tim.Set(); 322 } 323 324 #endregion 325 count_crc(byte[] data)326 private static ushort count_crc(byte[] data) 327 { 328 ushort crc = 0; 329 uint j; 330 for(j = 0; j < data.Length; j++) 331 { 332 333 crc = (ushort)(crc ^ data[j] << 8); 334 var b = 8; 335 do 336 { 337 if((crc & 0x8000) > 0) 338 { 339 crc = (ushort)(crc << 1 ^ 0x1021); 340 } 341 else 342 { 343 crc = (ushort)(crc << 1); 344 } 345 } 346 while(--b > 0); 347 } 348 return crc; 349 } 350 351 /* public void ReceivePacket(string packetData) 352 { 353 // TODO: have some mechanism to determine to what ADDR we should put the data (ADDR_A/ADDR_B) 354 this.Log(LogLevel.Warning, "TODO: should put packet '{0}' of len {3} to bufs @ {1:X} & @ {2:X}", packetData, MAC_RX_ST_ADDR_A, MAC_RX_ST_ADDR_B, packetData.Length); 355 356 byte[] data; 357 var ind = 0; 358 data = packetData.GroupBy(x => ind++ / 2).Select(x => Convert.ToByte(string.Format("{0}{1}", x.First(), x.Skip(1).First()), 16)).ToArray(); 359 ReceiveFrame(data); 360 }*/ 361 362 Encrypt()363 private void Encrypt() 364 { 365 var aes = Aes.Create(); 366 aes.KeySize = 128; 367 aes.BlockSize = 128; 368 aes.Mode = CipherMode.CBC; 369 aes.Padding = PaddingMode.None; 370 var encryptor = aes.CreateEncryptor(currentKey, new byte[16]); 371 encryptor.TransformFinalBlock(currentData, 0, 16).CopyTo(currentValue, 0); 372 } 373 374 public GPIO Tx { get; private set; } 375 376 public GPIO Rx { get; private set; } 377 378 public GPIO Tim { get; private set; } 379 380 private uint mac_timer; 381 382 private readonly byte[] currentKey; 383 private readonly byte[] currentData; 384 private readonly byte[] currentValue; 385 private bool valueToEncryptHasChanged; 386 private int dataPointer; 387 private int valuePointer; 388 389 private uint packLength; 390 391 private uint MAC_TX_ST_ADDR_A = 0x20000000; 392 private uint MAC_TX_END_ADDR_A = 0x20000000; 393 private uint MAC_RX_ST_ADDR_A = 0x20000000; 394 private uint MAC_RX_ST_ADDR_B = 0x20000000; 395 396 private readonly IMachine machine; 397 private readonly IBusController sysbus; 398 399 // public event Action<string> SendPacket; 400 401 private enum EncryptorRegister 402 { 403 Control = 0x0, 404 Data = 0x28, 405 Value = 0x30 406 } 407 408 private const int EncryptorKeyRegisterBegin = 0x38; 409 private const int EncryptorKeyRegisterEnd = 0x48; 410 411 } 412 } 413 414