1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure;
13 using Antmicro.Renode.Core.USB;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Time;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Utilities.Collections;
18 
19 namespace Antmicro.Renode.Peripherals.USB
20 {
21     public class USBHost : SimpleContainerBase<IUSBDevice>
22     {
USBHost(uint defaultDelay = 1000)23         public USBHost(uint defaultDelay = 1000)
24         {
25             this.defaultDelay = defaultDelay;
26             timeSource = EmulationManager.Instance.CurrentEmulation.MasterTimeSource; // We use time as a hack for now
27             devices = new TwoWayDictionary<byte, IUSBDevice>();
28             addressCounter = 2; // Zero is reserved, 1 is host, so we start from 2
29         }
30 
Reset()31         public void Reset()
32         {
33             devices.Clear();
34             addressCounter = 2;
35         }
36 
Register(IUSBDevice peripheral, NumberRegistrationPoint<int> registrationPoint)37         public override void Register(IUSBDevice peripheral, NumberRegistrationPoint<int> registrationPoint)
38         {
39             TryInitializeConnectedDevice(peripheral);
40         }
41 
Unregister(IUSBDevice peripheral)42         public override void Unregister(IUSBDevice peripheral)
43         {
44             bool found = devices.TryGetValue(peripheral, out var address);
45             if(found)
46             {
47                 devices.Remove(peripheral);
48                 ChildCollection.Remove(address);
49             }
50         }
51 
DeviceEnumerated(IUSBDevice device)52         protected virtual void DeviceEnumerated(IUSBDevice device)
53         {
54             // Intentionally left empty
55         }
56 
TryGetDevice(byte address, out IUSBDevice device)57         protected bool TryGetDevice(byte address, out IUSBDevice device)
58         {
59             devices.TryGetValue(address, out device);
60             return device != null;
61         }
62 
EnumerateDevice(IUSBDevice device)63         protected void EnumerateDevice(IUSBDevice device)
64         {
65             SetAddress(device);
66             GetDescriptor(device);
67             SetConfiguration(device);
68             // Note: We wait for endpoints to fully enable, because if we
69             // do it too early, then the data will be lost
70             ExecuteWithDelay(() => { DeviceEnumerated(device); });
71         }
72 
GetDescriptor(IUSBDevice device)73         private void GetDescriptor(IUSBDevice device)
74         {
75             var setupPacket = new SetupPacket();
76             setupPacket.Recipient = PacketRecipient.Device;
77             setupPacket.Type = PacketType.Standard;
78             setupPacket.Request = (byte)StandardRequest.GetDescriptor;
79             setupPacket.Direction = Core.USB.Direction.DeviceToHost;
80             setupPacket.Value = 0x0200;
81             setupPacket.Index = 0;
82             setupPacket.Count = 9;
83             device.USBCore.HandleSetupPacket(setupPacket, _ => {}, null);
84         }
85 
SetAddress(IUSBDevice device)86         private void SetAddress(IUSBDevice device)
87         {
88             var setupPacket = new SetupPacket();
89             setupPacket.Recipient = PacketRecipient.Device;
90             setupPacket.Type = PacketType.Standard;
91             setupPacket.Request = (byte)StandardRequest.SetAddress;
92             setupPacket.Direction = Core.USB.Direction.HostToDevice;
93             setupPacket.Value = addressCounter;
94             setupPacket.Index = 0;
95             setupPacket.Count = 0;
96 
97             // This should respond with empty data packet, but we dont check it for now as it is not necessary
98             device.USBCore.HandleSetupPacket(setupPacket, _ =>
99             {
100                 devices.Add(addressCounter, device);
101                 addressCounter++;
102             }, null);
103         }
104 
SetConfiguration(IUSBDevice device)105         private void SetConfiguration(IUSBDevice device)
106         {
107             var setupPacket = new SetupPacket();
108             setupPacket.Recipient = PacketRecipient.Device;
109             setupPacket.Type = PacketType.Standard;
110             setupPacket.Request = (byte)StandardRequest.SetConfiguration;
111             setupPacket.Direction = Core.USB.Direction.HostToDevice;
112             setupPacket.Value = 1; // Set to first configuration for now
113             setupPacket.Index = 0;
114             setupPacket.Count = 0;
115             device.USBCore.HandleSetupPacket(setupPacket, _ => {}, null);
116         }
117 
TryInitializeConnectedDevice(IUSBDevice peripheral)118         private bool TryInitializeConnectedDevice(IUSBDevice peripheral)
119         {
120             lock(devices)
121             {
122                 // 0 is special address for newly detected device
123                 // as soon as device get's detected it should get enumerated and get different address
124                 if(devices.Exists(0))
125                 {
126                     return false;
127                 }
128                 // Add to child collection. It's not yet fully connected (not in devices)
129                 // When the enumeration fails, or device is needed before enumeration (e.g CDC ACM UART)
130                 // then we can still access the device and it doesn't get lost.
131                 ChildCollection.Add(addressCounter, peripheral);
132                 // Initialize device with it's first configuration
133                 // Note: Delay here is necessary, as if we start to do anything before
134                 // the USB device gets to pullup, then we'll stall endpoints
135                 ExecuteWithDelay(() => { EnumerateDevice(peripheral); });
136                 return true;
137             }
138         }
139 
ExecuteWithDelay(Action action)140         private void ExecuteWithDelay(Action action)
141         {
142             var now = timeSource.ElapsedVirtualTime;
143             var calculatedDelay = now + TimeInterval.FromMilliseconds(defaultDelay);
144             var calculatedTimestamp = new TimeStamp(calculatedDelay, timeSource.Domain);
145             timeSource.ExecuteInSyncedState(_ =>
146             {
147                 action();
148             }, calculatedTimestamp);
149         }
150 
151         private byte addressCounter;
152         private readonly TwoWayDictionary<byte, IUSBDevice> devices;
153         private TimeSourceBase timeSource;
154         private readonly uint defaultDelay;
155     }
156 }
157