async Task HandleRequestImportAsync(CancellationToken cancellationToken) { var buf = new byte[SYSFS_BUS_ID_SIZE]; await RecvExactSizeAsync(Stream, buf, cancellationToken); var busid = Encoding.UTF8.GetString(buf).TrimEnd('\0'); var status = Status.ST_ERROR; try { var exportedDevices = await ExportedDevice.GetAll(cancellationToken); status = Status.ST_NODEV; var exportedDevice = exportedDevices.Single(x => x.BusId == busid); status = Status.ST_NA; using var mon = new VBoxUsbMon(); await mon.CheckVersion(); await mon.AddFilter(exportedDevice); await mon.RunFilters(); status = Status.ST_DEV_BUSY; ClientContext.AttachedDevice = await mon.ClaimDevice(exportedDevice); ClientContext.ConfigurationDescriptors = exportedDevice.ConfigurationDescriptors; status = Status.ST_DEV_ERR; var cfg = new byte[1] { 0 }; await ClientContext.AttachedDevice.IoControlAsync(VBoxUsb.IoControl.SUPUSB_IOCTL_USB_SET_CONFIG, cfg, null); status = Status.ST_OK; await SendOpCodeAsync(OpCode.OP_REP_IMPORT, Status.ST_OK); exportedDevice.Serialize(Stream, false); await ServiceProvider.GetRequiredService <AttachedClient>().RunAsync(cancellationToken); } catch { #pragma warning disable CA1508 // Avoid dead conditional code (false possitive) if (status != Status.ST_OK) #pragma warning restore CA1508 // Avoid dead conditional code { await SendOpCodeAsync(OpCode.OP_REP_IMPORT, status); } throw; } }
async Task HandleRequestDeviceListAsync(CancellationToken cancellationToken) { var exportedDevices = await ExportedDevice.GetAll(cancellationToken); await SendOpCodeAsync(OpCode.OP_REP_DEVLIST, Status.ST_OK); // reply count var buf = new byte[4]; BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)exportedDevices.Length); await Stream.WriteAsync(buf, cancellationToken); foreach (var exportedDevice in exportedDevices) { exportedDevice.Serialize(Stream, true); } }
public async Task <DeviceFile> ClaimDevice(ExportedDevice device) { var sw = new Stopwatch(); sw.Start(); while (true) { try { return(await ClaimDeviceOnce(device)); } catch (FileNotFoundException) { if (sw.Elapsed > TimeSpan.FromSeconds(5)) { throw; } await Task.Delay(100); } } }
public async Task AddFilter(ExportedDevice device) { var filter = UsbFilter.Create(UsbFilterType.CAPTURE); filter.SetMatch(UsbFilterIdx.VENDOR_ID, UsbFilterMatch.NUM_EXACT, device.VendorId); filter.SetMatch(UsbFilterIdx.PRODUCT_ID, UsbFilterMatch.NUM_EXACT, device.ProductId); filter.SetMatch(UsbFilterIdx.DEVICE_REV, UsbFilterMatch.NUM_EXACT, device.BcdDevice); filter.SetMatch(UsbFilterIdx.DEVICE_CLASS, UsbFilterMatch.NUM_EXACT, device.DeviceClass); filter.SetMatch(UsbFilterIdx.DEVICE_SUB_CLASS, UsbFilterMatch.NUM_EXACT, device.DeviceSubClass); filter.SetMatch(UsbFilterIdx.DEVICE_PROTOCOL, UsbFilterMatch.NUM_EXACT, device.DeviceProtocol); filter.SetMatch(UsbFilterIdx.PORT, UsbFilterMatch.NUM_EXACT, (ushort)device.DevNum); var output = new byte[Marshal.SizeOf <UsbSupFltAddOut>()]; await UsbMonitor.IoControlAsync(VBoxUsb.IoControl.SUPUSBFLT_IOCTL_ADD_FILTER, StructToBytes(filter), output); var fltAddOut = BytesToStruct <UsbSupFltAddOut>(output, 0); if (fltAddOut.rc != 0 /* VINF_SUCCESS */) { throw new UnexpectedResultException($"SUPUSBFLT_IOCTL_ADD_FILTER failed with returnCode {fltAddOut.rc}"); } }
public static async Task <ExportedDevice[]> GetAll(CancellationToken cancellationToken) { var exportedDevices = new SortedDictionary <string, ExportedDevice>(); using var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(IntPtr.Zero, "USB", IntPtr.Zero, DiGetClassFlags.DIGCF_ALLCLASSES | DiGetClassFlags.DIGCF_PRESENT); foreach (var devInfoData in EnumDeviceInfo(deviceInfoSet)) { cancellationToken.ThrowIfCancellationRequested(); var instanceId = GetDevicePropertyString(deviceInfoSet, devInfoData, DEVPKEY_Device_InstanceId); if (IsUsbHub(instanceId)) { // device is itself a USB hub, which is not supported continue; } var parentId = GetDevicePropertyString(deviceInfoSet, devInfoData, DEVPKEY_Device_Parent); if (!IsUsbHub(parentId)) { // parent is not a USB hub (which it must be for this device to be supported) continue; } // OK, so the device is directly connected to a hub, but is not a hub itself ... this looks promising GetBusId(deviceInfoSet, devInfoData, out var hubNum, out var connectionIndex); var address = GetDevicePropertyUInt32(deviceInfoSet, devInfoData, DEVPKEY_Device_Address); if (connectionIndex != address) { throw new NotSupportedException($"DEVPKEY_Device_Address ({address}) does not match DEVPKEY_Device_LocationInfo ({connectionIndex})"); } // now query the parent USB hub for device details { cancellationToken.ThrowIfCancellationRequested(); using var hubs = NativeMethods.SetupDiGetClassDevs(GUID_DEVINTERFACE_USB_HUB, parentId, IntPtr.Zero, DiGetClassFlags.DIGCF_DEVICEINTERFACE | DiGetClassFlags.DIGCF_PRESENT); var(_, interfaceData) = EnumDeviceInterfaces(hubs, GUID_DEVINTERFACE_USB_HUB).Single(); var hubPath = GetDeviceInterfaceDetail(hubs, interfaceData); cancellationToken.ThrowIfCancellationRequested(); using var hubFile = new DeviceFile(hubPath); using var cancellationTokenRegistration = cancellationToken.Register(() => hubFile.Dispose()); var data = new UsbNodeConnectionInformationEx() { ConnectionIndex = connectionIndex }; var buf = StructToBytes(data); await hubFile.IoControlAsync(IoControl.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, buf, buf); BytesToStruct(buf, 0, out data); var speed = MapWindowsSpeedToLinuxSpeed(data.Speed); var data2 = new UsbNodeConnectionInformationExV2() { ConnectionIndex = connectionIndex, Length = (uint)Marshal.SizeOf <UsbNodeConnectionInformationExV2>(), SupportedUsbProtocols = UsbProtocols.Usb110 | UsbProtocols.Usb200 | UsbProtocols.Usb300, }; var buf2 = StructToBytes(data2); await hubFile.IoControlAsync(IoControl.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, buf2, buf2); BytesToStruct(buf2, 0, out data2); if ((data2.SupportedUsbProtocols & UsbProtocols.Usb300) != 0) { if ((data2.Flags & UsbNodeConnectionInformationExV2Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher) != 0) { speed = Linux.UsbDeviceSpeed.USB_SPEED_SUPER_PLUS; } else if ((data2.Flags & UsbNodeConnectionInformationExV2Flags.DeviceIsOperatingAtSuperSpeedOrHigher) != 0) { speed = Linux.UsbDeviceSpeed.USB_SPEED_SUPER; } } var exportedDevice = new ExportedDevice() { Path = instanceId, BusNum = hubNum, DevNum = connectionIndex, Speed = speed, VendorId = data.DeviceDescriptor.idVendor, ProductId = data.DeviceDescriptor.idProduct, BcdDevice = data.DeviceDescriptor.bcdDevice, DeviceClass = data.DeviceDescriptor.bDeviceClass, DeviceSubClass = data.DeviceDescriptor.bDeviceSubClass, DeviceProtocol = data.DeviceDescriptor.bDeviceProtocol, ConfigurationValue = data.CurrentConfigurationValue, NumConfigurations = data.DeviceDescriptor.bNumConfigurations, ConfigurationDescriptors = await GetConfigurationDescriptor(hubFile, connectionIndex, data.DeviceDescriptor.bNumConfigurations), }; exportedDevices.Add(exportedDevice.BusId, exportedDevice); } } return(exportedDevices.Values.ToArray()); }
async Task <DeviceFile> ClaimDeviceOnce(ExportedDevice device) { using var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(GUID_CLASS_VBOXUSB, null, IntPtr.Zero, DiGetClassFlags.DIGCF_DEVICEINTERFACE | DiGetClassFlags.DIGCF_PRESENT); if (deviceInfoSet.IsInvalid) { throw new Win32Exception("SetupDiGetClassDevs"); } foreach (var(infoData, interfaceData) in EnumDeviceInterfaces(deviceInfoSet, GUID_CLASS_VBOXUSB)) { GetBusId(deviceInfoSet, infoData, out var hubNum, out var connectionIndex); if ((hubNum != device.BusNum) || (connectionIndex != device.DevNum)) { continue; } var path = GetDeviceInterfaceDetail(deviceInfoSet, interfaceData); var dev = new DeviceFile(path); try { { var output = new byte[Marshal.SizeOf <UsbSupVersion>()]; await dev.IoControlAsync(VBoxUsb.IoControl.SUPUSB_IOCTL_GET_VERSION, null, output); BytesToStruct(output, 0, out UsbSupVersion version); if ((version.major != USBDRV_MAJOR_VERSION) || (version.minor < USBDRV_MINOR_VERSION)) { throw new NotSupportedException($"device version not supported: {version.major}.{version.minor}, expected {USBDRV_MAJOR_VERSION}.{USBDRV_MINOR_VERSION}"); } } { await dev.IoControlAsync(VBoxUsb.IoControl.SUPUSB_IOCTL_IS_OPERATIONAL, null, null); } IntPtr hdev; { var getDev = new UsbSupGetDev(); var output = new byte[Marshal.SizeOf <UsbSupGetDev>()]; await dev.IoControlAsync(VBoxUsb.IoControl.SUPUSB_IOCTL_GET_DEVICE, StructToBytes(getDev), output); BytesToStruct(output, 0, out getDev); hdev = getDev.hDevice; } { var getDev = new UsbSupGetDev() { hDevice = hdev, }; var output = new byte[Marshal.SizeOf <UsbSupGetDevMon>()]; await UsbMonitor.IoControlAsync(VBoxUsb.IoControl.SUPUSBFLT_IOCTL_GET_DEVICE, StructToBytes(getDev), output); var getDevMon = BytesToStruct <UsbSupGetDevMon>(output, 0); } { var claimDev = new UsbSupClaimDev(); var output = new byte[Marshal.SizeOf <UsbSupClaimDev>()]; await dev.IoControlAsync(VBoxUsb.IoControl.SUPUSB_IOCTL_USB_CLAIM_DEVICE, StructToBytes(claimDev), output); BytesToStruct(output, 0, out claimDev); if (!claimDev.fClaimed) { throw new ProtocolViolationException("could not claim"); } } { var getDev = new UsbSupGetDev() { hDevice = hdev, }; var output = new byte[Marshal.SizeOf <UsbSupGetDevMon>()]; await UsbMonitor.IoControlAsync(VBoxUsb.IoControl.SUPUSBFLT_IOCTL_GET_DEVICE, StructToBytes(getDev), output); var getDevMon = BytesToStruct <UsbSupGetDevMon>(output, 0); } var result = dev; dev = null !; return(result); } finally { dev?.Dispose(); } } throw new FileNotFoundException(); }