1 //
2 // Copyright (c) 2010-2023 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.Generic;
10 using System.Linq;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Core
15 {
16     [Convertible]
17     public sealed class GPIO : IGPIOWithHooks
18     {
GPIO()19         public GPIO()
20         {
21             sync = new object();
22             targets = new List<GPIOEndpoint>();
23             stateChangedHook = delegate {};
24         }
25 
Set(bool value)26         public void Set(bool value)
27         {
28             // yup, we're locking on self in order not to create an additional field (and object, more importantly)
29             lock(sync)
30             {
31                 if(state == value)
32                 {
33                     return;
34                 }
35                 state = value;
36                 for(var i = 0; i < targets.Count; ++i)
37                 {
38                     targets[i].Receiver.OnGPIO(targets[i].Number, state);
39                 }
40                 stateChangedHook(value);
41             }
42         }
43 
Toggle()44         public void Toggle()
45         {
46             lock(sync)
47             {
48                 Set(!IsSet);
49             }
50         }
51 
Connect(IGPIOReceiver destination, int destinationNumber)52         public void Connect(IGPIOReceiver destination, int destinationNumber)
53         {
54             if(destination == null)
55             {
56                 throw new ArgumentNullException("destination");
57             }
58             Validate(destination, destinationNumber);
59             lock(sync)
60             {
61                 if(!targets.Any(x => x.Receiver == destination && x.Number == destinationNumber))
62                 {
63                     targets.Add(new GPIOEndpoint(destination, destinationNumber));
64                     destination.OnGPIO(destinationNumber, state);
65                 }
66             }
67         }
68 
Disconnect()69         public void Disconnect()
70         {
71             lock(sync)
72             {
73                 targets.Clear();
74             }
75         }
76 
Disconnect(GPIOEndpoint endpoint)77         public void Disconnect(GPIOEndpoint endpoint)
78         {
79             lock(sync)
80             {
81                 targets.Remove(endpoint);
82             }
83         }
84 
ToString()85         public override string ToString()
86         {
87             return IsSet ? "GPIO: set" : "GPIO: unset";
88         }
89 
90         public bool IsSet
91         {
92             get
93             {
94                 lock(sync)
95                 {
96                     return state;
97                 }
98             }
99         }
100 
101         public bool IsConnected
102         {
103             get
104             {
105                 lock(sync)
106                 {
107                     return targets.Count > 0;
108                 }
109             }
110         }
111 
112         public IList<GPIOEndpoint> Endpoints
113         {
114             get
115             {
116                 lock(sync)
117                 {
118                     return targets;
119                 }
120             }
121         }
122 
AddStateChangedHook(Action<bool> hook)123         public void AddStateChangedHook(Action<bool> hook)
124         {
125             stateChangedHook += hook;
126         }
127 
RemoveStateChangedHook(Action<bool> hook)128         public void RemoveStateChangedHook(Action<bool> hook)
129         {
130             stateChangedHook -= hook;
131         }
132 
RemoveAllStateChangedHooks()133         public void RemoveAllStateChangedHooks()
134         {
135             stateChangedHook = delegate {};
136         }
137 
GetAttribute(IGPIOReceiver per)138         private static GPIOAttribute GetAttribute(IGPIOReceiver per)
139         {
140             return (GPIOAttribute)per.GetType().GetCustomAttributes(true).FirstOrDefault(x => x is GPIOAttribute);
141         }
142 
Validate(IGPIOReceiver to, int toNumber)143         private static void Validate(IGPIOReceiver to, int toNumber)
144         {
145             var destPeriAttribute = GetAttribute(to);
146             var destPeriInNum = destPeriAttribute != null ? destPeriAttribute.NumberOfInputs : 0;
147             if(destPeriInNum != 0 && toNumber >= destPeriInNum)
148             {
149                 throw new ConstructionException(string.Format(
150                     "Cannot connect {0}th input of {1}; it has only {2} GPIO inputs.",
151                     toNumber, to, destPeriInNum));
152             }
153         }
154 
155         private bool state;
156         private Action<bool> stateChangedHook;
157         private readonly object sync;
158         private readonly IList<GPIOEndpoint> targets;
159     }
160 }
161 
162