1 //
2 // Copyright (c) 2010-2024 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 System.Linq;
11 using Antmicro.Renode.Time;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Migrant.Hooks;
14 using System.Collections.Generic;
15 
16 namespace Antmicro.Renode.Peripherals.UART
17 {
18     public static class UARTHubExtensions
19     {
CreateUARTHub(this Emulation emulation, string name, bool loopback = false)20         public static void CreateUARTHub(this Emulation emulation, string name, bool loopback = false)
21         {
22             emulation.ExternalsManager.AddExternal(new UARTHub(loopback), name);
23         }
24     }
25 
26     public sealed class UARTHub : UARTHubBase<IUART>
27     {
UARTHub(bool loopback)28         public UARTHub(bool loopback) : base(loopback) {}
29     }
30 
31     public class UARTHubBase<I> : IExternal, IHasOwnLife, IConnectable<I>
32         where I: class, IUART
33     {
UARTHubBase(bool loopback)34         public UARTHubBase(bool loopback)
35         {
36             uarts = new Dictionary<I, Action<byte>>();
37             locker = new object();
38             shouldLoopback = loopback;
39         }
40 
AttachTo(I uart)41         public virtual void AttachTo(I uart)
42         {
43             lock(locker)
44             {
45                 if(uarts.ContainsKey(uart))
46                 {
47                     throw new RecoverableException("Cannot attach to the provided UART as it is already registered in this hub.");
48                 }
49 
50                 var d = (Action<byte>)(x => HandleCharReceived(x, uart));
51                 uarts.Add(uart, d);
52                 uart.CharReceived += d;
53             }
54         }
55 
Start()56         public void Start()
57         {
58             Resume();
59         }
60 
Pause()61         public void Pause()
62         {
63             started = false;
64         }
65 
Resume()66         public void Resume()
67         {
68             started = true;
69         }
70 
DetachFrom(I uart)71         public virtual void DetachFrom(I uart)
72         {
73             lock(locker)
74             {
75                 if(!uarts.ContainsKey(uart))
76                 {
77                     throw new RecoverableException("Cannot detach from the provided UART as it is not registered in this hub.");
78                 }
79 
80                 uart.CharReceived -= uarts[uart];
81                 uarts.Remove(uart);
82             }
83         }
84 
85         public bool IsPaused => !started;
86 
HandleCharReceived(byte obj, I sender)87         private void HandleCharReceived(byte obj, I sender)
88         {
89             if(!started)
90             {
91                 return;
92             }
93 
94             lock(locker)
95             {
96                 foreach(var item in uarts.Where(x => shouldLoopback || x.Key != sender).Select(x => x.Key))
97                 {
98                     item.GetMachine().HandleTimeDomainEvent(item.WriteChar, obj, TimeDomainsManager.Instance.VirtualTimeStamp);
99                 }
100             }
101         }
102 
103         [PostDeserialization]
ReattachUARTsAfterDeserialization()104         private void ReattachUARTsAfterDeserialization()
105         {
106             lock(locker)
107             {
108                 foreach(var uart in uarts)
109                 {
110                     uart.Key.CharReceived += uart.Value;
111                 }
112             }
113         }
114 
115         protected bool started;
116         protected readonly bool shouldLoopback;
117         protected readonly Dictionary<I, Action<byte>> uarts;
118         protected readonly object locker;
119     }
120 }
121 
122