1 //
2 // Copyright (c) 2010-2022 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 System.Collections;
10 using Antmicro.Renode.Peripherals.UART;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Peripherals;
13 using Antmicro.Renode.Time;
14 
15 namespace Antmicro.Renode.Backends.Terminals
16 {
17     public abstract class BackendTerminal : IExternal, IConnectable<IUART>
18     {
BackendTerminal()19         public BackendTerminal()
20         {
21             buffer = new Queue();
22         }
23 
24         public virtual event Action<byte> CharReceived;
25 
WriteChar(byte value)26         public abstract void WriteChar(byte value);
27 
BufferStateChanged(BufferState state)28         public virtual void BufferStateChanged(BufferState state)
29         {
30             lock(innerLock)
31             {
32                 if(state == BufferState.Full || pendingTimeDomainEvent)
33                 {
34                     return;
35                 }
36                 pendingTimeDomainEvent = true;
37             }
38             HandleExternalTimeDomainEvent<object>(_ => WriteBufferToUART(), null);
39         }
40 
AttachTo(IUART uart)41         public virtual void AttachTo(IUART uart)
42         {
43             this.uart = uart;
44             this.machine = uart.GetMachine();
45 
46             var uartWithBuffer = uart as IUARTWithBufferState;
47             if(uartWithBuffer != null)
48             {
49                 CharReceived += EnqueueWriteToUART;
50                 uartWithBuffer.BufferStateChanged += BufferStateChanged;
51             }
52             else
53             {
54                 CharReceived += WriteToUART;
55             }
56 
57             uart.CharReceived += WriteChar;
58         }
59 
DetachFrom(IUART uart)60         public virtual void DetachFrom(IUART uart)
61         {
62             var uartWithBuffer = uart as IUARTWithBufferState;
63             if(uartWithBuffer != null)
64             {
65                 CharReceived -= EnqueueWriteToUART;
66                 uartWithBuffer.BufferStateChanged -= BufferStateChanged;
67             }
68             else
69             {
70                 CharReceived -= WriteToUART;
71             }
72 
73             uart.CharReceived -= WriteChar;
74 
75             this.uart = null;
76             this.machine = null;
77             buffer.Clear();
78         }
79 
CallCharReceived(byte value)80         protected void CallCharReceived(byte value)
81         {
82             var charReceived = CharReceived;
83             if(charReceived != null)
84             {
85                 charReceived(value);
86             }
87         }
88 
EnqueueWriteToUART(byte value)89         private void EnqueueWriteToUART(byte value)
90         {
91             lock(innerLock)
92             {
93                 buffer.Enqueue(value);
94                 if(!pendingTimeDomainEvent)
95                 {
96                     pendingTimeDomainEvent = true;
97                     HandleExternalTimeDomainEvent<object>(_ => WriteBufferToUART(), null);
98                 }
99             }
100         }
101 
WriteBufferToUART()102         private void WriteBufferToUART()
103         {
104             lock(innerLock)
105             {
106                 var uartWithBuffer = uart as IUARTWithBufferState;
107                 while(buffer.Count > 0 && uartWithBuffer.BufferState != BufferState.Full)
108                 {
109                     uart.WriteChar((byte)buffer.Dequeue());
110                 }
111                 pendingTimeDomainEvent = false;
112             }
113         }
114 
WriteToUART(byte value)115         private void WriteToUART(byte value)
116         {
117             HandleExternalTimeDomainEvent(uart.WriteChar, value);
118         }
119 
HandleExternalTimeDomainEvent(Action<T> handler, T handlerValue)120         private void HandleExternalTimeDomainEvent<T>(Action<T> handler, T handlerValue)
121         {
122             if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts))
123             {
124                 vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld);
125             }
126             machine.HandleTimeDomainEvent(handler, handlerValue, vts);
127         }
128 
129         private readonly Queue buffer;
130         private readonly object innerLock = new object();
131 
132         private IUART uart;
133         private IMachine machine;
134         private bool pendingTimeDomainEvent;
135     }
136 }
137 
138