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