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 System.Collections.Generic;
10 using System.Linq;
11 using System.Text;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Peripherals;
16 using AntShell.Commands;
17 
18 namespace Antmicro.Renode.UserInterface.Commands
19 {
20     public class PeripheralsCommand : Command
21     {
22         [Runnable]
Run(ICommandInteraction writer)23         public void Run(ICommandInteraction writer)
24         {
25             var currentMachine = GetCurrentMachine();
26             if(currentMachine == null)
27             {
28                 writer.WriteError("Select active machine.");
29                 return;
30             }
31             writer.WriteLine("Available peripherals:");
32             writer.WriteLine();
33 
34             var peripheralEntries = currentMachine.GetPeripheralsWithAllRegistrationPoints();
35             var sysbusEntry = peripheralEntries.First(x => x.Key.Name == Machine.SystemBusName);
36             var sysbusNode = new PeripheralNode(sysbusEntry);
37             var nodeQueue = new Queue<PeripheralNode>(peripheralEntries.Where(x => x.Key != sysbusEntry.Key).Select(x => new PeripheralNode(x)));
38 
39             while(nodeQueue.Count > 0)
40             {
41                 var x = nodeQueue.Dequeue();
42                 // Adding nodes to sysbusNode is successful only if the current node's parent was already added.
43                 // This code effectively sorts the nodes topologically.
44                 if(!sysbusNode.AddChild(x))
45                 {
46                     nodeQueue.Enqueue(x);
47                 }
48             }
49             sysbusNode.PrintTree(writer);
50         }
51 
52 
PeripheralsCommand(Monitor monitor, Func<IMachine> getCurrentMachine)53         public PeripheralsCommand(Monitor monitor, Func<IMachine> getCurrentMachine) : base(monitor, "peripherals", "prints list of registered and named peripherals.", "peri")
54         {
55             GetCurrentMachine = getCurrentMachine;
56         }
57 
58         private Func<IMachine> GetCurrentMachine;
59 
60         private class PeripheralNode
61         {
PeripheralNode(KeyValuePair<PeripheralTreeEntry, IEnumerable<IRegistrationPoint>> rawNode)62             public PeripheralNode(KeyValuePair<PeripheralTreeEntry, IEnumerable<IRegistrationPoint>> rawNode)
63             {
64                 PeripheralEntry = rawNode.Key;
65                 RegistrationPoints = rawNode.Value;
66                 Children = new HashSet<PeripheralNode>();
67             }
68 
AddChild(PeripheralNode newChild)69             public bool AddChild(PeripheralNode newChild)
70             {
71                 if(newChild.PeripheralEntry.Parent == PeripheralEntry.Peripheral)
72                 {
73                     Children.Add(newChild);
74                     return true;
75                 }
76                 foreach(var child in Children)
77                 {
78                     if(child.AddChild(newChild))
79                     {
80                         return true;
81                     }
82                 }
83                 return false;
84             }
85 
Contains(IPeripheral peripherial)86             public bool Contains(IPeripheral peripherial)
87             {
88                 if(PeripheralEntry.Peripheral == peripherial)
89                 {
90                     return true;
91                 }
92                 foreach(var item in Children)
93                 {
94                     if(item.Contains(peripherial))
95                     {
96                         return true;
97                     }
98                 }
99                 return false;
100             }
101 
PrintTree(ICommandInteraction writer, TreeViewBlock[] pattern = null)102             public void PrintTree(ICommandInteraction writer, TreeViewBlock[] pattern = null)
103             {
104                 if(pattern == null)
105                 {
106                     pattern = new TreeViewBlock[0];
107                 }
108                 var indent = GetIndentString(pattern);
109                 writer.WriteLine(String.Format("{0}{1} ({2})", indent, PeripheralEntry.Name, PeripheralEntry.Type.Name));
110 
111                 if(PeripheralEntry.Parent != null)
112                 {
113                     var newIndent = GetIndentString(UpdatePattern(pattern, Children.Count > 0 ? TreeViewBlock.Straight : TreeViewBlock.Empty));
114                     if(!(PeripheralEntry.RegistrationPoint is ITheOnlyPossibleRegistrationPoint))
115                     {
116                         foreach(var registerPlace in RegistrationPoints)
117                         {
118                             writer.WriteLine(String.Format("{0}{1}", newIndent, registerPlace.PrettyString));
119                         }
120                     }
121                     writer.WriteLine(newIndent);
122                 }
123                 else
124                 {
125                     writer.WriteLine(GetIndentString(new TreeViewBlock[] { TreeViewBlock.Straight }));
126                 }
127 
128                 var lastChild = Children.LastOrDefault();
129                 foreach(var child in Children)
130                 {
131                     child.PrintTree(writer, UpdatePattern(pattern, child != lastChild ? TreeViewBlock.Full : TreeViewBlock.End));
132                 }
133             }
134 
135             private PeripheralTreeEntry PeripheralEntry;
136             private IEnumerable<IRegistrationPoint> RegistrationPoints;
137             private HashSet<PeripheralNode> Children;
138 
GetIndentString(TreeViewBlock[] rawSignPattern)139             private static String GetIndentString(TreeViewBlock[] rawSignPattern)
140             {
141                 var indentBuilder = new StringBuilder(DefaultPadding);
142                 foreach(var tmp in rawSignPattern)
143                 {
144                     indentBuilder.Append(GetSingleIndentString(tmp));
145                 }
146                 return indentBuilder.ToString();
147             }
148 
GetSingleIndentString(TreeViewBlock rawSignPattern)149             private static String GetSingleIndentString(TreeViewBlock rawSignPattern)
150             {
151                 switch(rawSignPattern)
152                 {
153                 case TreeViewBlock.Full:
154                     return "├── ";
155                 case TreeViewBlock.End:
156                     return "└── ";
157                 case TreeViewBlock.Straight:
158                     return "│   ";
159                 case TreeViewBlock.Empty:
160                     return "    ";
161                 default:
162                     throw new ArgumentException();
163                 }
164             }
165 
UpdatePattern(TreeViewBlock[] oldPattern, TreeViewBlock newSign)166             private static TreeViewBlock[] UpdatePattern(TreeViewBlock[] oldPattern, TreeViewBlock newSign)
167             {
168                 FixLastSign(oldPattern);
169                 var newPattern = new TreeViewBlock[oldPattern.Length + 1];
170                 Array.Copy(oldPattern, newPattern, oldPattern.Length);
171                 newPattern[newPattern.Length - 1] = newSign;
172                 return newPattern;
173             }
174 
FixLastSign(TreeViewBlock[] pattern)175             private static void FixLastSign(TreeViewBlock[] pattern)
176             {
177                 if(pattern.Length < 1)
178                 {
179                     return;
180                 }
181                 if(pattern[pattern.Length - 1] == TreeViewBlock.Full)
182                 {
183                     pattern[pattern.Length - 1] = TreeViewBlock.Straight;
184                 }
185                 else if(pattern[pattern.Length - 1] == TreeViewBlock.End)
186                 {
187                     pattern[pattern.Length - 1] = TreeViewBlock.Empty;
188                 }
189             }
190 
191             private const String DefaultPadding = "  ";
192 
193             internal enum TreeViewBlock { Empty, Straight, End, Full };
194         }
195     }
196 }
197