Windows API USB IO (winusb.dll) - PullRequest
       127

Windows API USB IO (winusb.dll)

0 голосов
/ 09 декабря 2018

Редактировать: Этот вопрос развивался с течением времени.Основной вопрос был о том, как подключиться к устройству USB и читать / записывать на него / с него в Windows.В конце концов я ответил на вопрос с помощью @ benvoigt.

Я написал библиотеку Hid, которая записывает и читает с / на USB-устройства Hid.Это работает хорошо.Однако устройство, к которому я подключаюсь, переключилось с Hid на USB-порт.Код Hid не работает при подключении к другому типу устройства.Теперь моя цель - подключиться к интерфейсу USB (в отличие от интерфейса Hid) и выполнять чтение / запись на него / с него.

На всех платформах, имеющих доступ к USB, мы должны запросить интерфейсы, которые существуют дляUSB-устройство, а затем «претендовать» на интерфейс для чтения и записи.Вот некоторый код из образца LibUsbDotNet (LibUsb - это работающая библиотека C USB, и LibUsbDotNet оборачивает ее) https://github.com/LibUsbDotNet/LibUsbDotNet/blob/master/src/Examples/Read.Write/ReadWrite.cs.

            using (var context = new UsbContext())
            {
                context.SetDebugLevel(LogLevel.Info);

                //Get a list of all connected devices
                var usbDeviceCollection = context.List();

                //Narrow down the device by vendor and pid
                var selectedDevice = usbDeviceCollection.FirstOrDefault(d => d.ProductId == ProductId && d.VendorId == VendorId);

                //Open the device
                selectedDevice.Open();

                //Get the first config number of the interface
                selectedDevice.ClaimInterface(selectedDevice.Configs[0].Interfaces[0].Number);

                //Open up the endpoints
                var writeEndpoint = selectedDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
                var readEnpoint = selectedDevice.OpenEndpointReader(ReadEndpointID.Ep01);

                //Create a buffer with some data in it
                var buffer = new byte[64];
                buffer[0] = 0x3f;
                buffer[1] = 0x23;
                buffer[2] = 0x23;

                //Write three bytes
                writeEndpoint.Write(buffer, 3000, out var bytesWritten);

                var readBuffer = new byte[64];

                //Read some data
                readEnpoint.Read(readBuffer, 3000, out var readBytes);
            }
}

У меня такое ощущение, что LibUsb достигает открытия интерфейсов / конечных точек в C, какthis (https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2199). Здесь он вызывает Initialize: https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2225, где мой код не работает.

Небольшой фрагмент информации о том, что это определенно устройство WinUSB.Я вижу это здесь:

enter image description here

На основании комментариев других людей и примера кода, я вижу, что мне нужно использовать winusb.dll.в состоянии вызвать CreateFile, чтобы получить дескриптор от устройства. В соответствии с другим примером кода, который я видел, следующим шагом является вызов WinUsb_Initialize. Однако, когда я вызываю это, я получаю код ошибки 8 (ERROR_NOT_ENOUGH_MEMORY). Есть некоторыеинформация здесь https://docs.microsoft.com/en-us/windows/desktop/api/winusb/nf-winusb-winusb_initialize. Но я не совсем понимаю, что он просит меня сделать. Это мой код:

    public override async Task InitializeAsync()
    {
        Dispose();

        if (string.IsNullOrEmpty(DeviceId))
        {
            throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
        }

        _DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);

        var errorCode = Marshal.GetLastWin32Error();

        if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");

        if (_DeviceHandle.IsInvalid) throw new Exception("Device handle no good");

        var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var interfaceHandle);

        errorCode = Marshal.GetLastWin32Error();

        if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");

        IsInitialized = true;

        RaiseConnected();
    }

Вы можете клонировать ветку этого репо здесь: https://github.com/MelbourneDeveloper/Device.Net/tree/WindowsUsbDevice.Просто запустите проект Usb.Net.WindowsSample.

Я также попробовал это и получил точно такой же результат:

public override async Task InitializeAsync()
{
    Dispose();

    if (string.IsNullOrEmpty(DeviceId))
    {
        throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
    }

    _DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);

    var errorCode = Marshal.GetLastWin32Error();

    if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");

    var interfaceHandle = new IntPtr();

    var pDll = NativeMethods.LoadLibrary(@"C:\GitRepos\Device.Net\src\Usb.Net.WindowsSample\bin\Debug\net452\winusb.dll");

    var pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "WinUsb_Initialize");

    var initialize = (WinUsb_Initialize)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(WinUsb_Initialize));

    var isSuccess = initialize(_DeviceHandle, ref interfaceHandle);

    errorCode = Marshal.GetLastWin32Error();

    if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");

    IsInitialized = true;

    RaiseConnected();
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle);

Я твердо верю, что что-то не так с самой реализацией WinUSB устройства.,Он работает с другими библиотеками, такими как LibUsb, UWP и Android, но WinUsb, похоже, не нравится.Я попробовал эту библиотеку: https://github.com/madwizard-thomas/winusbnet, и она также не работает на тот же вызов с тем же кодом ошибки.Строка, в которой происходит сбой и появляется код ошибки 8, находится здесь: https://github.com/madwizard-thomas/winusbnet/blob/8f62d751a99be1e31d34b91115715d60aeff2dfc/WinUSBNet/API/WinUSBDevice.cs#L225

Что не так с моим кодом здесь?Что мне нужно сделать, чтобы выделить память для вызова WinUsb_Initialize?

Как использовать winusb.dll для Windows API?Какие вызовы API мне нужно сделать, чтобы запросить и интерфейс или конечную точку для чтения и записи?

Было бы очень полезно, если бы кто-то мог указать мне на простой пример C или C #, который читаети записывает на USB-устройство и фактически работает .

Выход WinDBG:

************* Сводка проверки пути ************** Время ответа (мс) Местоположение отложено
srv * Путь поиска символа: srv * Путь поиска исполняемого файла: ModLoad: 00000236 157c0000 00000236 157c8000 Usb.Net.WindowsSample.exe ModLoad: 00007ffb 62880000 00007ffb 62a61000 ntdll.dll ModLoad: 00007ffb 60f40000 00007ffb 610d0000 C: \ WINDOWS \ System32 \ user32.dll ModLoad: 00007ffb 5ed00000 00007ffb 5ed20000
C: \ WINDOWS \ System32 \ win32u.dll Загрузка модуля: 00007ffb 4e1b0000 00007ffb 4e214000 C: \ WINDOWS \ SYSTEM32 \ MSCOREE.DLL ModLoad: 00007ffb 612a0000 00007ffb 612c8000 C: \ WINDOWS \ System32 \ GDI32.dll onecore \ windows \ core \ console \ open \ src \ renderer \ gdi \ invalidate.cpp (121) \ conhost.exe! 00007FF7169FE2AF: (абонент: 00007FF7169FF414) ReturnHr (1) tid (4230) 80070578 Неверный дескриптор окна.ModLoad: 00007ffb 60990000 00007ffb 60a42000
C: \ WINDOWS \ System32 \ KERNEL32.dll ModLoad: 00007ffb 5f000000 00007ffb 5f192000 C: \ WINDOWS \ System32 \ gdi32full.dll ModLoad: 00007ffb 60d90000 00007ffb 60f03000 C \ WINDOWS\ MSCTF.dll ModLoad: 00007ffb 5ed80000 00007ffb 5eff3000
C: \ WINDOWS \ System32 \ KERNELBASE.dll ModLoad: 00007ffb 60610000 00007ffb 606d2000 C: \ WINDOWS \ System32 \ OLEAUT32.dll, загрузка модуля: 00007ffb 60f10000 00007ffb 60f: 60f * 60f * 60ff\ WINDOWS \ System32 \ IMM32.DLL

************* Сводка проверки пути ************** Время отклика (мс) Местоположение отложено
srv * Путь поиска символа:srv * Путь поиска исполняемого файла: ModLoad: 00007ff7 169f0000 00007ff7 16a8f000 conhost.exe ModLoad: 00007ffb 61340000 00007ffb 62780000 C: \ WINDOWS \ System32 \ shell32.dll ModLoad: 00007ffb 5cd80000 00007ffb 5cda9000
C: \ WINDOWS \ system32\ dwmapi.dll ModLoad: 00007ffb 62880000 00007ffb 62a61000 ntdll.dll ModLoad: 00007ffb 5fcc0000 00007ffb 5fd09000 C: \ WINDOWS \ System32 \ cfgmgr32.dll ModLoad: 00007ffb 5f530000 00007ffb 5fc3d000
C: \ WINDOWS \ Systemstorage.dll onecore \ windows \ core \ console \ open \ src \ renderer \ gdi \ invalidate.cpp (121) \ conhost.exe! 00007FF7169FE2AF: (абонент: 00007FF7169FF414) ReturnHr (2) tid (4230) 80070578 Недопустимый дескриптор окна.ModLoad: 00007ffb 61140000 00007ffb 61191000
C: \ WINDOWS \ System32 \ shlwapi.dll ModLoad: 00007ffb 60990000 00007ffb 60a42000 C: \ WINDOWS \ System32 \ KERNEL32.DLL Загрузка модуля: 00007ffb 5ec30000 00007ffb 5ec41000
C:WINDOWS \ System32 \ kernel.appcore.dll ModLoad: 00007ffb 5ed80000 00007ffb 5eff3000 C: \ WINDOWS \ System32 \ KERNELBASE.dll ModLoad: 00007ffb 5ec10000 00007ffb 5ec2f000 C: \ WINDOWS \ System32 \ profapi.dll Модулевая загрузка: 00007ffb 5ebc0000 00007ffb 1090 *
C: \ WINDOWS \ System32 \ powrprof.dll ModLoad: 00007ffb 5ebb0000 00007ffb 5ebba000 C: \ WINDOWS \ System32 \ FLTLIB.DLL ModLoad: 00007ffb 5f490000 00007ffb 5f52f000
C: \ WINDOWS \ System32 \ msvcp_win.dllModLoad: 00007ffb 5f1a0000 00007ffb 5f29a000 C: \ WINDOWS \ System32 \ ucrtbase.dll ModLoad: 00007ffb 606e0000 00007ffb 60789000 C: \ WINDOWS \ System32 \ shcore.dll ModLoad: 00007ffb 4e290000 00007ffb 4e4f9000
C: WINSS\ amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.17134.472_none_fb3f9af53068156d \ comctl32.DLL ModLoad: 00007ffb 5ca60000 00007ffb 5caf8000
C: \ WINDOWS \ system32 \ u64OW * system32: каталог-системная папка * * *msvcrt.dll ModLoad: 00007ffb 601e0000 00007ffb 60304000 C: \ WINDOWS \ System32 \ RPCRT4.dll ModLoad: 00007ffb 60a60000 00007ffb 60d82000
C: \ WINDOWS \ System32 \ combase.dll ModLoad: 00007ffb 5fc40000 00007ffb 5fcba000 C: \ WINDOWS \ System32 \ bcryptPrimitives.dll Мод загрузки:00007ffb 627a0000 00007ffb 62841000 C: \ WINDOWS \ System32 \ advapi32.dll ModLoad: 00007ffb 610d0000 00007ffb 6112b000
C: \ WINDOWS \ System32 \ sechost.dll ModLoad: 00007ffb 57b30000 00007ffb 57bc6000 C: \ WINDOWS \ System32 \ TextInput.dll (3d80.256c): исключение инструкции разрыва - код 80000003 (первый шанс) ntdll! LdrpDoDebuggerBreak + 0x30: 00007ffb`6294c93c cc
int 3

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Ниже описано, как подключиться к USB-устройству и читать / записывать его с USB-устройства через библиотеку WinUSB

Весь этот код содержится в репозитории Device.Net: https://github.com/MelbourneDeveloper/Device.Net.Здесь есть образец.Он автоматически переключается между Hid и UWP в зависимости от того, какое устройство подключено. https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net.WindowsSample/Program.cs

Подключение и получение информации

Запись и чтение

Вызовы API

public static class Kernel32APICalls
{
    //Abridged

    #region Kernel32
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    #endregion    
}

public static partial class WinUsbApiCalls
{
    public const uint DEVICE_SPEED = 1;
    public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
    public const int WritePipeId = 0x80;

    /// <summary>
    /// Not sure where this constant is defined...
    /// </summary>
    public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
}

Особая благодарность @benvoigt за то, что я решил эту проблему.

0 голосов
/ 27 декабря 2018

Хорошо, странность, которую вы видите вокруг 64-байтовых передач, хорошо известна:

Разграничение операций записи при записи с короткими пакетами

Драйвер стека драйвера USB не накладывает те же ограниченияна размер пакета при записи на устройство, который он накладывает при чтении с устройства.Некоторые драйверы клиентов должны часто передавать небольшие объемы контрольных данных для управления своими устройствами.В таких случаях нецелесообразно ограничивать передачу данных пакетами одинакового размера.Поэтому стек драйверов не придает никакого особого значения пакетам с размером, меньшим, чем максимальный размер конечной точки во время записи данных.Это позволяет клиентскому драйверу разбить большую передачу на устройство на несколько URB любого размера, меньшего или равного максимальному.

Драйвер должен либо прекратить передачу с помощью пакета меньше максимального размераили ограничить конец передачи с помощью пакета нулевой длины.Передача не завершена, пока драйвер не отправит пакет, меньший, чем wMaxPacketSize. Если размер передачи является точным кратным максимума, драйвер должен отправить пакет с ограничителем нулевой длины, чтобы явно прекратить передачу

Разграничение передачи данных с пакетами нулевой длины, кактребуется спецификацией USB, является обязанностью драйвера клиента.Стек драйверов USB не генерирует эти пакеты автоматически.

С Размеры передачи USB и пакетов на MSDN


Что касается остальных ...в зависимости от того, объявляет ли устройство себя составным устройством или нет, загрузчик драйверов Windows выберет один или несколько драйверов устройств для подключения к устройству.Эти драйверы сами выбирают, с какой конечной точкой на устройстве разговаривать.Поэтому из пользовательского пространства обычно достаточно открыть интерфейс драйвера и начать делать ввод-вывод.Например, драйвер класса HID знает, как определить и включить конечную точку HID.

Поскольку у вас есть вещи, работающие с UWP, вполне вероятно, что у вас загружен драйвер WinUSB (так как это Step #1 ).Поэтому вы будете использовать WinUSB API для общения с ним.

Вот документация для C и C ++ API для WinUSB .В нем есть примеры настройки конечной точки, и он выглядит немного менее грязно, чем код libusb, который вы цитировали (хотя это может иметь отношение и к форматированию и стилю кода).

...