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 // uncomment the line below to get more detailed logs 9 // note: dumping packets may severely lower performance 10 // #define DEBUG_PACKETS 11 12 using System; 13 using System.Collections.Generic; 14 using System.Linq; 15 using System.Text.RegularExpressions; 16 using System.Threading; 17 using Antmicro.Renode.Core; 18 using Antmicro.Renode.Core.Structure; 19 using Antmicro.Renode.Core.USB; 20 using Antmicro.Renode.Debugging; 21 using Antmicro.Renode.Logging; 22 using Antmicro.Renode.Utilities; 23 using Antmicro.Renode.Utilities.Packets; 24 25 namespace Antmicro.Renode.Extensions.Utilities.USBIP 26 { 27 public static class USBIPServerExtensions 28 { CreateUSBIPServer(this Emulation emulation, int port = 3240, string address = R, string name = R)29 public static void CreateUSBIPServer(this Emulation emulation, int port = 3240, string address = "127.0.0.1", string name = "usb") 30 { 31 var server = new USBIPServer(address, port); 32 server.Run(); 33 34 emulation.HostMachine.AddHostMachineElement(server, name); 35 } 36 37 // this is just a simple wrapper method allowing to register devices from monitor Register(this USBIPServer @this, IUSBDevice device, int? port = null)38 public static void Register(this USBIPServer @this, IUSBDevice device, int? port = null) 39 { 40 if(!port.HasValue) 41 { 42 port = @this.Children.Any() 43 ? @this.Children.Max(x => x.RegistrationPoint.Address) + 1 44 : 0; 45 } 46 47 @this.Register(device, new NumberRegistrationPoint<int>(port.Value)); 48 } 49 } 50 51 public class USBIPServer : SimpleContainerBase<IUSBDevice>, IHostMachineElement, IDisposable 52 { USBIPServer(string address, int port)53 public USBIPServer(string address, int port) 54 { 55 this.port = port; 56 57 server = new SocketServerProvider(false, serverName: "USBIP"); 58 server.DataReceived += HandleIncomingData; 59 server.ConnectionClosed += Reset; 60 61 // setting initial size is just an optimization 62 buffer = new List<byte>(Packet.CalculateLength<URBRequest>()); 63 cancellationToken = new CancellationTokenSource(); 64 } 65 Dispose()66 public override void Dispose() 67 { 68 base.Dispose(); 69 Shutdown(); 70 } 71 Run()72 public void Run() 73 { 74 server.Start(port); 75 } 76 Reset()77 public void Reset() 78 { 79 state = State.WaitForCommand; 80 urbHeader = default(URBHeader); 81 additionalDataCount = 0; 82 83 buffer.Clear(); 84 cancellationToken = new CancellationTokenSource(); 85 } 86 SendResponse(IEnumerable<byte> bytes)87 private void SendResponse(IEnumerable<byte> bytes) 88 { 89 server.Send(bytes); 90 91 #if DEBUG_PACKETS 92 this.Log(LogLevel.Noisy, "Count {0}: {1}", bytes.Count(), Misc.PrettyPrintCollectionHex(bytes)); 93 #endif 94 } 95 Shutdown()96 private void Shutdown() 97 { 98 this.Log(LogLevel.Info, "Shutting down the server..."); 99 100 cancellationToken.Cancel(); 101 server.Stop(); 102 buffer.Clear(); 103 } 104 HandleIncomingData(int b)105 private void HandleIncomingData(int b) 106 { 107 #if DEBUG_PACKETS 108 this.Log(LogLevel.Noisy, "Incoming byte: 0x{0:X}; state = {1}; buffer size = {2}", b, state, buffer.Count); 109 #endif 110 111 if(b < 0) 112 { 113 // the current connection is closed - reset the state and prepare for the next one 114 Reset(); 115 return; 116 } 117 118 buffer.Add((byte)b); 119 120 switch(state) 121 { 122 case State.WaitForCommand: 123 { 124 DebugHelper.Assert(buffer.Count <= Packet.CalculateLength<USBIP.Header>()); 125 if(buffer.Count == Packet.CalculateLength<USBIP.Header>()) 126 { 127 var header = Packet.Decode<USBIP.Header>(buffer); 128 buffer.Clear(); 129 this.Log(LogLevel.Debug, "Received USB/IP header: {0}", header.ToString()); 130 131 switch(header.Command) 132 { 133 case USBIP.Command.ListDevices: 134 { 135 SendResponse(HandleListDevicesCommand()); 136 137 state = State.WaitForCommand; 138 break; 139 } 140 141 case USBIP.Command.AttachDevice: 142 { 143 state = State.WaitForBusId; 144 break; 145 } 146 147 default: 148 { 149 this.Log(LogLevel.Error, "Unexpected packet command: 0x{0:X}", header.Command); 150 Shutdown(); 151 break; 152 } 153 } 154 } 155 break; 156 } 157 158 case State.WaitForBusId: 159 { 160 DebugHelper.Assert(buffer.Count <= Packet.CalculateLength<USBIP.AttachDeviceCommandDescriptor>()); 161 if(buffer.Count == Packet.CalculateLength<USBIP.AttachDeviceCommandDescriptor>()) 162 { 163 var busId = System.Text.Encoding.ASCII.GetString(buffer.ToArray()); 164 buffer.Clear(); 165 166 state = TryHandleDeviceAttachCommand(busId, out var response) 167 ? State.WaitForURBHeader 168 : State.WaitForCommand; 169 170 SendResponse(response); 171 } 172 break; 173 } 174 175 case State.WaitForURBHeader: 176 { 177 DebugHelper.Assert(buffer.Count <= Packet.CalculateLength<URBHeader>()); 178 if(buffer.Count == Packet.CalculateLength<URBHeader>()) 179 { 180 urbHeader = Packet.Decode<URBHeader>(buffer); 181 this.Log(LogLevel.Debug, "Received URB header: {0}", urbHeader.ToString()); 182 buffer.Clear(); 183 184 switch(urbHeader.Command) 185 { 186 case URBCommand.URBRequest: 187 state = State.WaitForURBRequest; 188 break; 189 case URBCommand.Unlink: 190 state = State.HandleUnlinkCommand; 191 break; 192 default: 193 this.Log(LogLevel.Error, "Unexpected URB command: 0x{0:X}", urbHeader.Command); 194 Shutdown(); 195 break; 196 } 197 } 198 break; 199 } 200 201 case State.WaitForURBRequest: 202 { 203 if(buffer.Count < Packet.CalculateLength<URBRequest>() + additionalDataCount) 204 { 205 break; 206 } 207 208 var packet = Packet.Decode<URBRequest>(buffer); 209 if(additionalDataCount == 0) 210 { 211 this.Log(LogLevel.Debug, "Received URB request: {0}", packet.ToString()); 212 213 if(urbHeader.Direction == URBDirection.Out && packet.TransferBufferLength > 0) 214 { 215 additionalDataCount = (int)packet.TransferBufferLength; 216 this.Log(LogLevel.Debug, "Packet comes with {0} bytes of additional data. Waiting for it", additionalDataCount); 217 break; 218 } 219 } 220 else 221 { 222 if(urbHeader.Direction == URBDirection.Out && packet.TransferBufferLength > 0) 223 { 224 this.Log(LogLevel.Debug, "Collected {0} bytes of additional data - moving forward", additionalDataCount); 225 } 226 } 227 228 if(urbHeader.BusId != ExportedBusId) 229 { 230 this.Log(LogLevel.Warning, "URB command directed to a non-existing bus 0x{0:X}", urbHeader.BusId); 231 } 232 else if(!TryGetByAddress((int)urbHeader.DeviceId, out var device)) 233 { 234 this.Log(LogLevel.Warning, "URB command directed to a non-existing device 0x{0:X}", urbHeader.DeviceId); 235 } 236 else if(urbHeader.EndpointNumber == 0) 237 { 238 // setup packet is passed in URB Request in a single `ulong` field 239 var setupPacket = Packet.Decode<SetupPacket>(buffer, Packet.CalculateOffset<URBRequest>(nameof(URBRequest.Setup))); 240 var additionalData = (additionalDataCount > 0) 241 ? buffer.Skip(buffer.Count - additionalDataCount).Take(additionalDataCount).ToArray() 242 : null; 243 var replyHeader = urbHeader; 244 device.USBCore.HandleSetupPacket(setupPacket, additionalData: additionalData, resultCallback: response => 245 { 246 SendResponse(GenerateURBReply(replyHeader, packet, response)); 247 }); 248 } 249 else 250 { 251 var ep = device.USBCore.GetEndpoint((int)urbHeader.EndpointNumber, urbHeader.Direction == URBDirection.Out ? Direction.HostToDevice : Direction.DeviceToHost); 252 if(ep == null) 253 { 254 this.Log(LogLevel.Warning, "URB command directed to a non-existing endpoint 0x{0:X}", urbHeader.EndpointNumber); 255 } 256 else if(ep.Direction == Direction.DeviceToHost) 257 { 258 this.Log(LogLevel.Noisy, "Reading from endpoint #{0}", ep.Identifier); 259 var response = ep.Read(packet.TransferBufferLength, cancellationToken.Token); 260 #if DEBUG_PACKETS 261 this.Log(LogLevel.Noisy, "Count {0}: {1}", response.Length, Misc.PrettyPrintCollectionHex(response)); 262 #endif 263 SendResponse(GenerateURBReply(urbHeader, packet, response)); 264 } 265 else 266 { 267 var additionalData = buffer.Skip(buffer.Count - additionalDataCount).Take(additionalDataCount).ToArray(); 268 269 ep.WriteData(additionalData); 270 SendResponse(GenerateURBReply(urbHeader, packet)); 271 } 272 } 273 274 additionalDataCount = 0; 275 state = State.WaitForURBHeader; 276 buffer.Clear(); 277 278 break; 279 } 280 281 case State.HandleUnlinkCommand: 282 { 283 DebugHelper.Assert(buffer.Count <= Packet.CalculateLength<URBRequest>()); 284 if(buffer.Count == Packet.CalculateLength<URBRequest>()) 285 { 286 // this command is practically ignored 287 buffer.Clear(); 288 state = State.WaitForURBHeader; 289 } 290 break; 291 } 292 293 default: 294 throw new ArgumentException(string.Format("Unexpected state: {0}", state)); 295 } 296 } 297 GenerateURBReply(URBHeader hdr, URBRequest req, IEnumerable<byte> data = null)298 private IEnumerable<byte> GenerateURBReply(URBHeader hdr, URBRequest req, IEnumerable<byte> data = null) 299 { 300 var header = new URBHeader 301 { 302 Command = URBCommand.URBReply, 303 SequenceNumber = hdr.SequenceNumber, 304 BusId = hdr.BusId, 305 DeviceId = hdr.DeviceId, 306 Direction = hdr.Direction, 307 EndpointNumber = hdr.EndpointNumber, 308 }; 309 310 var reply = new URBReply 311 { 312 // this is intentional: 313 // - report size of TransferBufferLength when returning no additional data, 314 // - report additional data size (and not TransferBufferLenght) otherwise 315 ActualLength = (data == null) 316 ? req.TransferBufferLength 317 : (uint)data.Count() 318 }; 319 320 var result = Packet.Encode(header).Concat(Packet.Encode(reply)).AsEnumerable(); 321 if(data != null) 322 { 323 result = result.Concat(data); 324 } 325 326 return result; 327 } 328 329 // using this blocking helper method simplifies the logic of other methods 330 // + it seems to be harmless, as this logic is not executed as a result of 331 // intra-emulation communication (where it could lead to deadlocks) HandleSetupPacketSync(IUSBDevice device, SetupPacket setupPacket)332 private byte[] HandleSetupPacketSync(IUSBDevice device, SetupPacket setupPacket) 333 { 334 byte[] result = null; 335 336 var mre = new ManualResetEvent(false); 337 device.USBCore.HandleSetupPacket(setupPacket, received => 338 { 339 result = received; 340 mre.Set(); 341 }); 342 343 mre.WaitOne(); 344 return result; 345 } 346 ReadDeviceDescriptor(IUSBDevice device)347 private USB.DeviceDescriptor ReadDeviceDescriptor(IUSBDevice device) 348 { 349 var setupPacket = new SetupPacket 350 { 351 Recipient = PacketRecipient.Device, 352 Type = PacketType.Standard, 353 Direction = Direction.DeviceToHost, 354 Request = (byte)StandardRequest.GetDescriptor, 355 Value = ((int)DescriptorType.Device << 8), 356 Count = (ushort)Packet.CalculateLength<USB.DeviceDescriptor>() 357 }; 358 359 return Packet.Decode<USB.DeviceDescriptor>(HandleSetupPacketSync(device, setupPacket)); 360 } 361 ReadConfigurationDescriptor(IUSBDevice device, byte configurationId, out USB.InterfaceDescriptor[] interfaceDescriptors)362 private USB.ConfigurationDescriptor ReadConfigurationDescriptor(IUSBDevice device, byte configurationId, out USB.InterfaceDescriptor[] interfaceDescriptors) 363 { 364 var setupPacket = new SetupPacket 365 { 366 Recipient = PacketRecipient.Device, 367 Type = PacketType.Standard, 368 Direction = Direction.DeviceToHost, 369 Request = (byte)StandardRequest.GetDescriptor, 370 Value = (ushort)(((int)DescriptorType.Configuration << 8) | configurationId), 371 Count = (ushort)Packet.CalculateLength<USB.ConfigurationDescriptor>() 372 }; 373 // first ask for the configuration descriptor non-recursively ... 374 var configurationDescriptorBytes = HandleSetupPacketSync(device, setupPacket); 375 var result = Packet.Decode<USB.ConfigurationDescriptor>(configurationDescriptorBytes); 376 377 interfaceDescriptors = new USB.InterfaceDescriptor[result.NumberOfInterfaces]; 378 // ... read the total length of a recursive structure ... 379 setupPacket.Count = result.TotalLength; 380 // ... and only then read the whole structure again. 381 var recursiveBytes = HandleSetupPacketSync(device, setupPacket); 382 383 var currentOffset = Packet.CalculateLength<USB.ConfigurationDescriptor>(); 384 for(var i = 0; i < interfaceDescriptors.Length; i++) 385 { 386 // the second byte of each descriptor contains the type 387 while(recursiveBytes[currentOffset + 1] != (byte)DescriptorType.Interface) 388 { 389 // the first byte of each descriptor contains the length in bytes 390 currentOffset += recursiveBytes[currentOffset]; 391 } 392 393 interfaceDescriptors[i] = Packet.Decode<USB.InterfaceDescriptor>(recursiveBytes, currentOffset); 394 // skip until the next interface descriptor 395 currentOffset += Packet.CalculateLength<USB.InterfaceDescriptor>(); 396 } 397 398 return result; 399 } 400 GenerateDeviceDescriptor(IUSBDevice device, uint deviceNumber, bool includeInterfaces)401 private IEnumerable<byte> GenerateDeviceDescriptor(IUSBDevice device, uint deviceNumber, bool includeInterfaces) 402 { 403 var deviceDescriptor = ReadDeviceDescriptor(device); 404 var interfaceDescriptors = new List<USB.InterfaceDescriptor>(); 405 406 if(includeInterfaces) 407 { 408 for(var i = 0; i < deviceDescriptor.NumberOfConfigurations; i++) 409 { 410 ReadConfigurationDescriptor(device, (byte)i, out var ifaces); 411 interfaceDescriptors.AddRange(ifaces); 412 } 413 } 414 415 var devDescriptor = new USBIP.DeviceDescriptor 416 { 417 Path = new byte[256], 418 BusId = new byte[32], 419 420 BusNumber = ExportedBusId, 421 DeviceNumber = deviceNumber, 422 Speed = (int)USBSpeed.High, // this is hardcoded, but I don't know if that's good 423 424 IdVendor = deviceDescriptor.VendorId, 425 IdProduct = deviceDescriptor.ProductId, 426 427 DeviceClass = deviceDescriptor.Class, 428 DeviceSubClass = deviceDescriptor.Subclass, 429 DeviceProtocol = deviceDescriptor.Protocol, 430 431 NumberOfConfigurations = deviceDescriptor.NumberOfConfigurations, 432 NumberOfInterfaces = (byte)interfaceDescriptors.Count 433 }; 434 435 SetText(devDescriptor.BusId, "{0}-{1}", ExportedBusId, deviceNumber); 436 SetText(devDescriptor.Path, "/renode/virtual/{0}-{1}", ExportedBusId, deviceNumber); 437 438 var result = Packet.Encode(devDescriptor).AsEnumerable(); 439 440 foreach(var iface in interfaceDescriptors) 441 { 442 var intDescriptor = new USBIP.InterfaceDescriptor 443 { 444 InterfaceClass = iface.Class, 445 InterfaceSubClass = iface.Subclass, 446 InterfaceProtocol = iface.Protocol 447 }; 448 449 result = result.Concat(Packet.Encode(intDescriptor)); 450 } 451 452 return result; 453 } 454 SetText(byte[] destination, string format, params object[] param)455 private void SetText(byte[] destination, string format, params object[] param) 456 { 457 var textBuffer = System.Text.Encoding.ASCII.GetBytes(string.Format(format, param)); 458 Array.Copy(textBuffer, destination, textBuffer.Length); 459 } 460 HandleListDevicesCommand()461 private IEnumerable<byte> HandleListDevicesCommand() 462 { 463 var header = new USBIP.Header 464 { 465 Version = ProtocolVersion, 466 Command = USBIP.Command.ListDevicesReply, 467 }; 468 469 var regCount = new USBIP.DeviceListCount 470 { 471 NumberOfExportedDevices = (uint)ChildCollection.Count 472 }; 473 474 var result = Packet.Encode(header).Concat(Packet.Encode(regCount)); 475 476 foreach(var child in ChildCollection) 477 { 478 result = result.Concat(GenerateDeviceDescriptor(child.Value, (uint)child.Key, true)); 479 } 480 481 return result; 482 } 483 TryHandleDeviceAttachCommand(string deviceIdString, out IEnumerable<byte> response)484 private bool TryHandleDeviceAttachCommand(string deviceIdString, out IEnumerable<byte> response) 485 { 486 var success = true; 487 var deviceId = 0; 488 IUSBDevice device = null; 489 490 var m = Regex.Match(deviceIdString, "([0-9]+)-([0-9]+)"); 491 if(m == null) 492 { 493 this.Log(LogLevel.Warning, "Unexpected device string when handling attach command: {0}. It should be in format '1-4'", deviceIdString); 494 success = false; 495 } 496 else 497 { 498 var busId = int.Parse(m.Groups[1].Value); 499 deviceId = int.Parse(m.Groups[2].Value); 500 501 if(busId != ExportedBusId) 502 { 503 this.Log(LogLevel.Warning, "Trying to attach to a non-existing bus 0x{0:X}", busId); 504 success = false; 505 } 506 else if(!TryGetByAddress(deviceId, out device)) 507 { 508 this.Log(LogLevel.Warning, "Trying to attach to a non-existing device 0x{0:X}", deviceId); 509 success = false; 510 } 511 } 512 513 var header = new USBIP.Header 514 { 515 Version = ProtocolVersion, 516 Command = USBIP.Command.AttachDeviceReply, 517 Status = success ? 0 : 1u 518 }; 519 520 response = Packet.Encode(header).AsEnumerable(); 521 if(success) 522 { 523 response = response.Concat(GenerateDeviceDescriptor(device, (uint)deviceId, false)); 524 } 525 526 return success; 527 } 528 529 private State state; 530 private URBHeader urbHeader; 531 private int additionalDataCount; 532 private CancellationTokenSource cancellationToken; 533 534 private readonly int port; 535 private readonly List<byte> buffer; 536 private readonly SocketServerProvider server; 537 538 private const uint ExportedBusId = 1; 539 private const ushort ProtocolVersion = 0x0111; 540 541 private enum USBSpeed 542 { 543 Unknown = 0, 544 Low = 1, 545 Full = 2, 546 High = 3, 547 Wireless = 4, 548 Super = 5, 549 SuperPlus = 6, 550 } 551 552 private enum State 553 { 554 WaitForCommand, 555 WaitForBusId, 556 WaitForURBHeader, 557 HandleUnlinkCommand, 558 WaitForURBRequest, 559 } 560 } 561 } 562