コード例 #1
0
        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;
            }
        }
コード例 #2
0
        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);
            }
        }
コード例 #3
0
        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);
                }
            }
        }
コード例 #4
0
        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}");
            }
        }
コード例 #5
0
        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());
        }
コード例 #6
0
        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();
        }