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