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 using System;
8 using System.Collections.Generic;
9 using System.Dynamic;
10 using System.Linq;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Utilities.Packets;
15 
16 namespace Antmicro.Renode.Core.USB
17 {
18     public class USBDeviceCore : DescriptorProvider
19     {
USBDeviceCore(IUSBDevice device, USBClassCode classCode = USBClassCode.NotSpecified, byte subClassCode = 0, byte protocol = 0, USBProtocol usbProtocolVersion = USBProtocol.USB_2_0, short deviceReleaseNumber = 0, PacketSize maximalPacketSize = PacketSize.Size64, string manufacturerName = null, string productName = null, string serialNumber = null, ushort vendorId = 0, ushort productId = 0, Action<SetupPacket, byte[], Action<byte[]>> customSetupPacketHandler = null)20         public USBDeviceCore(IUSBDevice device,
21                              USBClassCode classCode = USBClassCode.NotSpecified,
22                              byte subClassCode = 0,
23                              byte protocol = 0,
24                              USBProtocol usbProtocolVersion = USBProtocol.USB_2_0,
25                              short deviceReleaseNumber = 0,
26                              PacketSize maximalPacketSize = PacketSize.Size64,
27                              string manufacturerName = null,
28                              string productName = null,
29                              string serialNumber = null,
30                              ushort vendorId = 0,
31                              ushort productId = 0,
32                              Action<SetupPacket, byte[], Action<byte[]>> customSetupPacketHandler = null) : base(18, (byte)DescriptorType.Device)
33         {
34             if(maximalPacketSize != PacketSize.Size8
35                 && maximalPacketSize != PacketSize.Size16
36                 && maximalPacketSize != PacketSize.Size32
37                 && maximalPacketSize != PacketSize.Size64)
38             {
39                 throw new ConstructionException("Unsupported maximal packet size.");
40             }
41 
42             this.customSetupPacketHandler = customSetupPacketHandler;
43             this.device = device;
44             configurations = new List<USBConfiguration>();
45 
46             CompatibleProtocolVersion = usbProtocolVersion;
47             Class = classCode;
48             SubClass = subClassCode;
49             Protocol = protocol;
50             DeviceReleaseNumber = deviceReleaseNumber;
51             MaximalPacketSize = maximalPacketSize;
52             ManufacturerName = manufacturerName;
53             ProductName = productName;
54             SerialNumber = serialNumber;
55             VendorId = vendorId;
56             ProductId = productId;
57 
58             RegisterSubdescriptors(configurations);
59         }
60 
Reset()61         public void Reset()
62         {
63             Address = 0;
64 
65             if(SelectedConfiguration == null)
66             {
67                 return;
68             }
69 
70             foreach(var iface in SelectedConfiguration.Interfaces)
71             {
72                 foreach(var epoint in iface.Endpoints)
73                 {
74                     epoint.Reset();
75                 }
76             }
77 
78             SelectedConfiguration = null;
79         }
80 
GetEndpoint(int endpointNumber, Direction direction)81         public USBEndpoint GetEndpoint(int endpointNumber, Direction direction)
82         {
83             if(SelectedConfiguration == null)
84             {
85                 return null;
86             }
87 
88             foreach(var iface in SelectedConfiguration.Interfaces)
89             {
90                 var ep = iface.Endpoints.FirstOrDefault(x => x.Identifier == endpointNumber && x.Direction == direction);
91                 if(ep != null)
92                 {
93                    return ep;
94                 }
95             }
96 
97             return null;
98         }
99 
HandleSetupPacket(SetupPacket packet, Action<byte[]> resultCallback, byte[] additionalData = null)100         public void HandleSetupPacket(SetupPacket packet, Action<byte[]> resultCallback, byte[] additionalData = null)
101         {
102             var result = BitStream.Empty;
103 
104             device.Log(LogLevel.Noisy, "Handling setup packet: {0}", packet);
105 
106             if(customSetupPacketHandler != null)
107             {
108                 customSetupPacketHandler(packet, additionalData, receivedBytes =>
109                 {
110                     SendSetupResult(resultCallback, receivedBytes);
111                 });
112             }
113             else
114             {
115                 switch(packet.Recipient)
116                 {
117                     case PacketRecipient.Device:
118                         result = HandleRequest(packet);
119                         break;
120                     case PacketRecipient.Interface:
121                         if(SelectedConfiguration == null)
122                         {
123                             device.Log(LogLevel.Warning, "Trying to access interface before selecting a configuration");
124                             resultCallback(new byte[0]);
125                             return;
126                         }
127                         var iface = SelectedConfiguration.Interfaces.FirstOrDefault(x => x.Identifier == packet.Index);
128                         if(iface == null)
129                         {
130                             device.Log(LogLevel.Warning, "Trying to access a non-existing interface #{0}", packet.Index);
131                         }
132                         result = iface.HandleRequest(packet);
133                         break;
134                     default:
135                         device.Log(LogLevel.Warning, "Unsupported recipient type: 0x{0:X}", packet.Recipient);
136                         break;
137                 }
138 
139                 SendSetupResult(resultCallback, result.AsByteArray(packet.Count * 8u));
140             }
141         }
142 
SendSetupResult(Action<byte[]> resultCallback, byte[] result)143         private void SendSetupResult(Action<byte[]> resultCallback, byte[] result)
144         {
145             device.Log(LogLevel.Noisy, "Sending setup packet response of length {0}", result.Length);
146 #if DEBUG_PACKET
147             device.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(result));
148 #endif
149             resultCallback(result);
150         }
151 
HandleRequest(SetupPacket packet)152         private BitStream HandleRequest(SetupPacket packet)
153         {
154             if(packet.Type != PacketType.Standard)
155             {
156                 device.Log(LogLevel.Warning, "Non standard requests are not supported");
157             }
158             else
159             {
160                 switch((StandardRequest)packet.Request)
161                 {
162                     case StandardRequest.SetAddress:
163                         Address = checked((byte)packet.Value);
164                         break;
165                     case StandardRequest.GetDescriptor:
166                         if(packet.Direction != Direction.DeviceToHost)
167                         {
168                             device.Log(LogLevel.Warning, "Wrong direction of Get Descriptor Standard Request");
169                             break;
170                         }
171                         return HandleGetDescriptor(packet.Value);
172                     case StandardRequest.SetConfiguration:
173                         SelectedConfiguration = Configurations.SingleOrDefault(x => x.Identifier == packet.Value);
174                         if(SelectedConfiguration == null)
175                         {
176                             device.Log(LogLevel.Warning, "Tried to select a non-existing configuration #{0}", packet.Value);
177                         }
178                         break;
179                     default:
180                         device.Log(LogLevel.Warning, "Unsupported standard request: 0x{0:X}", packet.Request);
181                         break;
182                 }
183             }
184 
185             return BitStream.Empty;
186         }
187 
HandleGetDescriptor(ushort value)188         private BitStream HandleGetDescriptor(ushort value)
189         {
190             var descriptorType = (DescriptorType)(value >> 8);
191             var descriptorIndex = (byte)value;
192 
193             switch(descriptorType)
194             {
195                 case DescriptorType.Device:
196                     return GetDescriptor(false);
197                 case DescriptorType.Configuration:
198                     if(Configurations.Count < descriptorIndex)
199                     {
200                         device.Log(LogLevel.Warning, "Tried to access a non-existing configuration #{0}", descriptorIndex);
201                         return BitStream.Empty;
202                     }
203                     return Configurations.ElementAt(descriptorIndex).GetDescriptor(true);
204                 case DescriptorType.String:
205                 {
206                     if(descriptorIndex == 0)
207                     {
208                         // special String Index returning a list of supported languages
209                         return USBString.GetSupportedLanguagesDescriptor();
210                     }
211                     else
212                     {
213                         var usbString = USBString.FromId(descriptorIndex);
214                         if(usbString == null)
215                         {
216                             device.Log(LogLevel.Warning, "Tried to get non-existing string #{0}", descriptorIndex);
217                             return BitStream.Empty;
218                         }
219 
220                         return usbString.GetDescriptor(false);
221                     }
222                 }
223                 default:
224                     device.Log(LogLevel.Warning, "Unsupported descriptor type: 0x{0:X}", descriptorType);
225                     return BitStream.Empty;
226             }
227         }
228 
WithConfiguration(string description = null, bool selfPowered = false, bool remoteWakeup = false, short maximalPower = 0, Action<USBConfiguration> configure = null)229         public USBDeviceCore WithConfiguration(string description = null, bool selfPowered = false, bool remoteWakeup = false, short maximalPower = 0, Action<USBConfiguration> configure = null)
230         {
231             var newConfiguration = new USBConfiguration(device, (byte)(configurations.Count + 1), description, selfPowered, remoteWakeup, maximalPower);
232             configurations.Add(newConfiguration);
233             configure?.Invoke(newConfiguration);
234             return this;
235         }
236 
237         public IReadOnlyCollection<USBConfiguration> Configurations => configurations;
238 
239         public USBConfiguration SelectedConfiguration { get; set; }
240         public byte Address { get; set; }
241 
242         public USBProtocol CompatibleProtocolVersion { get; }
243         public USBClassCode Class { get; }
244         public byte SubClass { get; }
245         public byte Protocol { get; }
246         public PacketSize MaximalPacketSize { get; }
247 
248         public ushort VendorId { get; }
249         public ushort ProductId { get; }
250 
251         public short DeviceReleaseNumber { get; }
252 
253         public string ManufacturerName { get; }
254         public string ProductName { get; }
255         public string SerialNumber { get; }
256 
FillDescriptor(BitStream buffer)257         protected override void FillDescriptor(BitStream buffer)
258         {
259             buffer
260                 .Append((short)CompatibleProtocolVersion)
261                 .Append((byte)Class)
262                 .Append(SubClass)
263                 .Append(Protocol)
264                 .Append((byte)MaximalPacketSize)
265                 .Append(VendorId)
266                 .Append(ProductId)
267                 .Append(DeviceReleaseNumber)
268                 .Append(USBString.FromString(ManufacturerName).Index)
269                 .Append(USBString.FromString(ProductName).Index)
270                 .Append(USBString.FromString(SerialNumber).Index)
271                 .Append((byte)Configurations.Count);
272         }
273 
274         private readonly List<USBConfiguration> configurations;
275         private readonly IUSBDevice device;
276 
277         private Action<SetupPacket, byte[], Action<byte[]>> customSetupPacketHandler;
278     }
279 }
280