rajhlinux

Untitled

Jan 26th, 2025
360
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.09 KB | Source Code | 0 0
  1. /*
  2.  
  3. How to compile:
  4.  
  5. cl /EHsc /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\winrt" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt" main.cpp /link /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64" /LIBPATH:"F:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\lib\x64" winusb.lib SetupAPI.lib cfgmgr32.lib
  6.  
  7. */
  8.  
  9. #include <windows.h>
  10.  
  11. #include <winusb.h>
  12.  
  13. #include <setupapi.h>
  14.  
  15. #include <usb.h>
  16.  
  17. #include <iostream>
  18.  
  19. #include <string>
  20.  
  21. #include <stdio.h> // For printf
  22.  
  23. #define BUFFER_SIZE 3072
  24.  
  25. #define ISOCH_DATA_SIZE_MS 1 // 1 millisecond of isochronous data
  26.  
  27. #define ISOCH_TRANSFER_COUNT 1 // Number of transfers to perform
  28.  
  29. typedef struct _DEVICE_DATA {
  30.  
  31.     BOOL                    HandlesOpen;
  32.  
  33.     WINUSB_INTERFACE_HANDLE WinusbHandle;
  34.  
  35.     HANDLE                  DeviceHandle;
  36.  
  37.     TCHAR                   DevicePath[MAX_PATH];
  38.  
  39.     UCHAR                   IsochOutPipe;
  40.  
  41.     UCHAR                   IsochInPipe;
  42.  
  43.     ULONG                   IsochInTransferSize;
  44.  
  45.     ULONG                   IsochOutTransferSize;
  46.  
  47.     ULONG                   IsochInPacketCount;
  48.  
  49.     WINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle; // Add this
  50.  
  51.     WINUSB_PIPE_INFORMATION_EX PipeInfo; // Add this
  52.  
  53. } DEVICE_DATA, *PDEVICE_DATA;
  54.  
  55. void ParseDescriptors(UCHAR* buffer, ULONG length) {
  56.  
  57.     UCHAR* ptr = buffer;
  58.  
  59.     UCHAR* end = buffer + length;
  60.  
  61.     while (ptr < end)
  62.  
  63.     {
  64.  
  65.         PUSB_COMMON_DESCRIPTOR commonDesc = (PUSB_COMMON_DESCRIPTOR)ptr;
  66.  
  67.         if (commonDesc->bLength < 2) {
  68.  
  69.             std::cerr << "Invalid descriptor length" << std::endl;
  70.  
  71.             break;
  72.  
  73.         }
  74.  
  75.         // Move to the next descriptor
  76.  
  77.         ptr += commonDesc->bLength;
  78.  
  79.     }
  80.  
  81. }
  82.  
  83. std::string GetErrorMessage(HRESULT hr) {
  84.  
  85.     LPSTR messageBuffer = nullptr;
  86.  
  87.     DWORD bufferLength = FormatMessageA(
  88.  
  89.         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  90.  
  91.         nullptr,
  92.  
  93.         hr,
  94.  
  95.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  96.  
  97.         reinterpret_cast<LPSTR>(&messageBuffer),
  98.  
  99.         0,
  100.  
  101.         nullptr);
  102.  
  103.     if (bufferLength == 0) {
  104.  
  105.         return "Unknown error";
  106.  
  107.     }
  108.  
  109.     std::string errorMessage(messageBuffer, bufferLength);
  110.  
  111.     LocalFree(messageBuffer);
  112.  
  113.     return errorMessage;
  114.  
  115. }
  116.  
  117. HRESULT GetIsochPipes(_Inout_ PDEVICE_DATA DeviceData) {
  118.  
  119.     BOOL result;
  120.  
  121.     USB_INTERFACE_DESCRIPTOR usbInterface;
  122.  
  123.     HRESULT hr = S_OK;
  124.  
  125.     UCHAR i;
  126.  
  127.     // Use the DeviceData->AssociatedInterfaceHandle instead of a local variable
  128.  
  129.     result = WinUsb_GetAssociatedInterface(DeviceData->WinusbHandle, 0, &DeviceData->AssociatedInterfaceHandle);
  130.  
  131.     if (result == FALSE) {
  132.  
  133.         hr = HRESULT_FROM_WIN32(GetLastError());
  134.  
  135.         std::cerr << "WinUsb_GetAssociatedInterface failed to get handle for interface 0." << std::endl;
  136.  
  137.         return hr;
  138.  
  139.     }
  140.  
  141.     result = WinUsb_QueryInterfaceSettings(DeviceData->AssociatedInterfaceHandle, 1, &usbInterface);
  142.  
  143.     if (result == FALSE) {
  144.  
  145.         hr = HRESULT_FROM_WIN32(GetLastError());
  146.  
  147.         std::cerr << "WinUsb_QueryInterfaceSettings failed to get USB interface for Alternate Setting 1." << std::endl;
  148.  
  149.         WinUsb_Free(DeviceData->AssociatedInterfaceHandle);
  150.  
  151.         return hr;
  152.  
  153.     }
  154.  
  155.     std::cout << "\n\nChecking Alternate Setting 1 with [" << (int)usbInterface.bNumEndpoints << "] endpoints:" << std::endl;
  156.  
  157.     for (i = 0; i < usbInterface.bNumEndpoints; i++) {
  158.  
  159.         // Use the DeviceData->PipeInfo instead of a local variable
  160.  
  161.         result = WinUsb_QueryPipeEx(
  162.  
  163.             DeviceData->AssociatedInterfaceHandle,
  164.  
  165.             1,
  166.  
  167.             i,
  168.  
  169.             &DeviceData->PipeInfo);
  170.  
  171.         if (result == FALSE) {
  172.  
  173.             hr = HRESULT_FROM_WIN32(GetLastError());
  174.  
  175.             std::cerr << "WinUsb_QueryPipeEx failed to get USB pipe for endpoint " << (int)i << " in Alternate Setting 1." << std::endl;
  176.  
  177.             CloseHandle(DeviceData->DeviceHandle);
  178.  
  179.             return hr;
  180.  
  181.         }
  182.  
  183.         if ((DeviceData->PipeInfo.PipeType == UsbdPipeTypeIsochronous) && (!(DeviceData->PipeInfo.PipeId == 0x82))) {
  184.  
  185.             DeviceData->IsochOutPipe = DeviceData->PipeInfo.PipeId;
  186.  
  187.             if ((DeviceData->PipeInfo.MaximumBytesPerInterval == 0) || (DeviceData->PipeInfo.Interval == 0)) {
  188.  
  189.                 hr = E_INVALIDARG;
  190.  
  191.                 printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
  192.  
  193.                 CloseHandle(DeviceData->DeviceHandle);
  194.  
  195.                 return hr;
  196.  
  197.             } else {
  198.  
  199.                 DeviceData->IsochOutTransferSize = ISOCH_DATA_SIZE_MS * DeviceData->PipeInfo.MaximumBytesPerInterval * (8 / DeviceData->PipeInfo.Interval);
  200.  
  201.                 std::cout << "Isochronous Out Pipe Transfer Size: " << DeviceData->IsochOutTransferSize << std::endl;
  202.  
  203.             }
  204.  
  205.         } else if (DeviceData->PipeInfo.PipeType == UsbdPipeTypeIsochronous) {
  206.  
  207.             DeviceData->IsochInPipe = DeviceData->PipeInfo.PipeId;
  208.  
  209.             if (DeviceData->PipeInfo.MaximumBytesPerInterval == 0 || (DeviceData->PipeInfo.Interval == 0)) {
  210.  
  211.                 hr = E_INVALIDARG;
  212.  
  213.                 printf("Isoch In: MaximumBytesPerInterval or Interval value is 0.\n");
  214.  
  215.                 CloseHandle(DeviceData->DeviceHandle);
  216.  
  217.                 return hr;
  218.  
  219.             } else {
  220.  
  221.                 DeviceData->IsochInTransferSize = ISOCH_DATA_SIZE_MS * DeviceData->PipeInfo.MaximumBytesPerInterval * (8 / DeviceData->PipeInfo.Interval);
  222.  
  223.                 DeviceData->IsochInPacketCount = DeviceData->IsochInTransferSize / DeviceData->PipeInfo.MaximumBytesPerInterval;
  224.  
  225.                 std::cout << "Isochronous In Pipe Transfer Size: " << DeviceData->IsochInTransferSize << std::endl;
  226.  
  227.                 std::cout << "Isochronous In Pipe Packet Count: " << DeviceData->IsochInPacketCount << std::endl;
  228.  
  229.             }
  230.  
  231.         }
  232.  
  233.         std::cout << "Pipe " << (int)i << " in Alternate Setting 1: "
  234.  
  235.         << "PipeId = " << (int)DeviceData->PipeInfo.PipeId << ", PipeType = " << (int)DeviceData->PipeInfo.PipeType
  236.  
  237.         << ", MaximumBytesPerInterval = " << DeviceData->PipeInfo.MaximumBytesPerInterval
  238.  
  239.         << ", Interval = " << (int)DeviceData->PipeInfo.Interval << "\n" << std::endl;
  240.  
  241.     }
  242.  
  243.     return hr;
  244.  
  245. }
  246.  
  247. VOID SendIsochInTransfer(
  248.  
  249.     _Inout_ PDEVICE_DATA DeviceData,
  250.  
  251.     _In_ BOOL AsapTransfer
  252.  
  253. ) {
  254.  
  255.     PUCHAR readBuffer = nullptr;
  256.  
  257.     LPOVERLAPPED overlapped = nullptr;
  258.  
  259.     ULONG numBytes = 0;
  260.  
  261.     BOOL result = FALSE;
  262.  
  263.     DWORD lastError = 0;
  264.  
  265.     WINUSB_ISOCH_BUFFER_HANDLE isochReadBufferHandle;
  266.  
  267.     PUSBD_ISO_PACKET_DESCRIPTOR isochPackets = nullptr;
  268.  
  269.     ULONG i = 0;
  270.  
  271.     ULONG j = 0;
  272.  
  273.     ULONG totalTransferSize;
  274.  
  275.    
  276.  
  277.     readBuffer = NULL;
  278.  
  279.     isochPackets = NULL;
  280.  
  281.     overlapped = NULL;
  282.  
  283.     isochReadBufferHandle = INVALID_HANDLE_VALUE;
  284.  
  285.     printf("\n\nInitiating Read transfer.\n\n");
  286.  
  287.     totalTransferSize = DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT;
  288.  
  289.     if (totalTransferSize % DeviceData->IsochInTransferSize != 0) {
  290.  
  291.         printf("Error: Transfer size must end at a frame boundary.\n");
  292.  
  293.         goto Error;
  294.  
  295.     }
  296.  
  297.     // Allocate aligned buffer
  298.  
  299.     readBuffer = new UCHAR[totalTransferSize];
  300.  
  301.     if (readBuffer == nullptr) {
  302.  
  303.         printf("Failed to allocate memory for aligned buffer.\n");
  304.  
  305.         goto Error;
  306.  
  307.     }
  308.  
  309.     ZeroMemory(readBuffer, totalTransferSize);
  310.  
  311.     // Print transfer parameters for debugging:
  312.  
  313.     printf("\nParameters, BEFORE calling `WinUsb_RegisterIsochBuffer`:\n");
  314.  
  315.     printf("Original Buffer Address: %p\n", readBuffer);
  316.  
  317.     printf("Buffer Size in Memory (Bytes): %zu\n", sizeof(readBuffer));
  318.  
  319.     printf("WinusbHandle Address: %p\n", DeviceData->WinusbHandle);
  320.  
  321.     printf("IsochReadBufferHandle: Address %p\n", isochReadBufferHandle);
  322.  
  323.     // Allocate overlapped structures
  324.  
  325.     overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
  326.  
  327.     ZeroMemory(overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));
  328.  
  329.     for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) {
  330.  
  331.         overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  332.  
  333.        
  334.  
  335.         if (overlapped[i].hEvent == NULL) {
  336.  
  337.             printf("Unable to set event for overlapped operation.\n");
  338.  
  339.             goto Error;
  340.  
  341.         }
  342.  
  343.     }
  344.  
  345.     // Allocate packet descriptors using new
  346.  
  347.     isochPackets = new USBD_ISO_PACKET_DESCRIPTOR[DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT];
  348.  
  349.     if (isochPackets == nullptr) {
  350.  
  351.         printf("Failed to allocate packet descriptors.\n");
  352.  
  353.         goto Error;
  354.  
  355.     }
  356.  
  357.     // Zero-initialize the allocated memory
  358.  
  359.     ZeroMemory(isochPackets, DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT);
  360.  
  361.     // Register isochronous buffer
  362.  
  363.     result = WinUsb_RegisterIsochBuffer(
  364.  
  365.         DeviceData->WinusbHandle,
  366.  
  367.         DeviceData->IsochInPipe,
  368.  
  369.         readBuffer,
  370.  
  371.         DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT,
  372.  
  373.         &isochReadBufferHandle);
  374.  
  375.     // Print transfer parameters for debugging
  376.  
  377.     printf("\nWinUsb_ReadIsochPipeAsap Parameters, AFTER calling `WinUsb_RegisterIsochBuffer`:\n");
  378.  
  379.     printf("IsochReadBufferHandle: Address %p\n", isochReadBufferHandle);
  380.  
  381.     printf("TransferSize: %lu\n", DeviceData->IsochInTransferSize);
  382.  
  383.     printf("PacketCount: %lu\n", DeviceData->IsochInPacketCount);
  384.  
  385.     printf("Buffer Address: %p\n", readBuffer);
  386.  
  387.     printf("Buffer Size in Memory (Bytes): %zu\n", sizeof(readBuffer));
  388.  
  389.     printf("WinusbHandle Address: %p\n", DeviceData->WinusbHandle);
  390.  
  391.     printf("BufferLength: %lu\n\n", DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT);
  392.  
  393.     if (!result) {
  394.  
  395.         // Convert the DWORD error code to an HRESULT
  396.  
  397.         HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
  398.  
  399.         // Get the human-readable error message
  400.  
  401.         std::string errorMessage = GetErrorMessage(hr);
  402.  
  403.         // Print the error message
  404.  
  405.         printf("Isoch buffer registration failed. Error Code: [%d], Meaning: [%s]\n", GetLastError(), errorMessage.c_str());
  406.  
  407.         goto Error;
  408.  
  409.     }
  410.  
  411.     // Send read requests
  412.  
  413.     for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) {
  414.  
  415.         printf("Transfer %d: Offset = %lu\n\n", i + 1, DeviceData->IsochInTransferSize * i);
  416.  
  417.         if (AsapTransfer) {
  418.  
  419.             result = WinUsb_ReadIsochPipeAsap(
  420.  
  421.                 isochReadBufferHandle,
  422.  
  423.                 DeviceData->IsochInTransferSize * i, // Offset should start at 0
  424.  
  425.                 DeviceData->IsochInTransferSize,
  426.  
  427.                 (i == 0) ? FALSE : TRUE, // ASAP flag for subsequent transfers
  428.  
  429.                 DeviceData->IsochInPacketCount,
  430.  
  431.                 &isochPackets[i * DeviceData->IsochInPacketCount],
  432.  
  433.                 &overlapped[i]);
  434.  
  435.             printf("Read transfer sent by using ASAP flag.\n\n");
  436.  
  437.         }
  438.  
  439.         if (!result) {
  440.  
  441.             lastError = GetLastError();
  442.  
  443.             if (lastError != ERROR_IO_PENDING) {
  444.  
  445.                 // Convert the DWORD error code to an HRESULT
  446.  
  447.                 HRESULT hr = HRESULT_FROM_WIN32(lastError);
  448.  
  449.                 // Get the human-readable error message
  450.  
  451.                 std::string errorMessage = GetErrorMessage(hr);
  452.  
  453.                 // Trim trailing newline characters
  454.  
  455.                 errorMessage.erase(errorMessage.find_last_not_of("\r\n") + 1);
  456.  
  457.                 // Print the error message:
  458.  
  459.                 printf("Failed to start a `WinUsb_ReadIsochPipe` request. ---->: Error Code: [ %x ], Error Meaning: [ %s ]\n\n", lastError, errorMessage.c_str());
  460.  
  461.             }
  462.  
  463.         }
  464.  
  465.     }
  466.  
  467.     // Wait for transfers to complete
  468.  
  469.     for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) {
  470.  
  471.         result = WinUsb_GetOverlappedResult(
  472.  
  473.             DeviceData->WinusbHandle,
  474.  
  475.             &overlapped[i],
  476.  
  477.             &numBytes,
  478.  
  479.             TRUE);
  480.  
  481.         if (!result) {
  482.  
  483.             lastError = GetLastError();
  484.  
  485.             printf("Failed to read with error %x\n", lastError);
  486.  
  487.         } else {
  488.  
  489.             numBytes = 0;
  490.  
  491.             for (j = 0; j < DeviceData->IsochInPacketCount; j++) {
  492.  
  493.                 numBytes += isochPackets[j].Length;
  494.  
  495.             }
  496.  
  497.             printf("Requested %d bytes in %d packets per transfer.\n", DeviceData->IsochInTransferSize, DeviceData->IsochInPacketCount);
  498.  
  499.         }
  500.  
  501.         printf("Transfer %d completed. Read %d bytes. \n\n", i + 1, numBytes);
  502.  
  503.     }
  504.  
  505. Error:
  506.  
  507.     if (isochReadBufferHandle != INVALID_HANDLE_VALUE) {
  508.  
  509.         result = WinUsb_UnregisterIsochBuffer(isochReadBufferHandle);
  510.  
  511.         if (!result) {
  512.  
  513.             printf("Failed to unregister isoch read buffer. \n");
  514.  
  515.         }
  516.  
  517.     }
  518.  
  519.     if (readBuffer != nullptr) {
  520.  
  521.         delete [] readBuffer;
  522.  
  523.     }
  524.  
  525.     if (isochPackets != nullptr) {
  526.  
  527.         delete [] isochPackets;
  528.  
  529.     }
  530.  
  531.     for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) {
  532.  
  533.         if (overlapped[i].hEvent != NULL) {
  534.  
  535.             CloseHandle(overlapped[i].hEvent);
  536.  
  537.         }
  538.  
  539.     }
  540.  
  541.     if (overlapped != nullptr) {
  542.  
  543.         delete[] overlapped;
  544.  
  545.     }
  546.  
  547.     return;
  548.  
  549. }
  550.  
  551. int main()
  552.  
  553. {
  554.  
  555.     DEVICE_DATA deviceData = {0};
  556.  
  557.     deviceData.DeviceHandle = INVALID_HANDLE_VALUE;
  558.  
  559.     deviceData.WinusbHandle = INVALID_HANDLE_VALUE;
  560.  
  561.     const wchar_t* devicePath = L"\\\\.\\USB#VID_EBA4&PID_7588&MI_02#6&63DE0D9&0&0002#{99DA2DEC-C919-4F9D-9384-DAA7EBBD40EC}";
  562.  
  563.     deviceData.DeviceHandle = CreateFileW(
  564.  
  565.         devicePath,
  566.  
  567.         GENERIC_WRITE | GENERIC_READ,
  568.  
  569.         FILE_SHARE_WRITE | FILE_SHARE_READ,
  570.  
  571.         NULL,
  572.  
  573.         OPEN_EXISTING,
  574.  
  575.         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
  576.  
  577.         NULL);
  578.  
  579.     if (deviceData.DeviceHandle == INVALID_HANDLE_VALUE)
  580.  
  581.     {
  582.  
  583.         std::cerr << "Error opening device: " << GetLastError() << std::endl;
  584.  
  585.         return 1;
  586.  
  587.     }
  588.  
  589.     if (!WinUsb_Initialize(deviceData.DeviceHandle, &deviceData.WinusbHandle))
  590.  
  591.     {
  592.  
  593.         std::cerr << "Error initializing WinUSB: " << GetLastError() << std::endl;
  594.  
  595.         CloseHandle(deviceData.DeviceHandle);
  596.  
  597.         return 1;
  598.  
  599.     }
  600.  
  601.     UCHAR buffer[BUFFER_SIZE];
  602.  
  603.     ULONG lengthTransferred;
  604.  
  605.     USB_DEVICE_DESCRIPTOR deviceDesc;
  606.  
  607.     if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
  608.  
  609.         memcpy(&deviceDesc, buffer, sizeof(USB_DEVICE_DESCRIPTOR));
  610.  
  611.     }
  612.  
  613.    
  614.  
  615.     else
  616.  
  617.    
  618.  
  619.     {
  620.  
  621.         std::cerr << "Error getting device descriptor: " << GetLastError() << std::endl;
  622.  
  623.     }
  624.  
  625.     USB_CONFIGURATION_DESCRIPTOR configDesc;
  626.  
  627.     if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
  628.  
  629.         memcpy(&configDesc, buffer, sizeof(USB_CONFIGURATION_DESCRIPTOR));
  630.  
  631.     }
  632.  
  633.    
  634.  
  635.     else
  636.  
  637.     {
  638.  
  639.         std::cerr << "Error getting configuration descriptor: " << GetLastError() << std::endl;
  640.  
  641.     }
  642.  
  643.     UCHAR* configBuffer = new UCHAR[configDesc.wTotalLength];
  644.  
  645.     if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, configBuffer, configDesc.wTotalLength, &lengthTransferred)) {
  646.  
  647.         ParseDescriptors(configBuffer, configDesc.wTotalLength);
  648.  
  649.     }
  650.  
  651.     else
  652.  
  653.     {
  654.  
  655.         std::cerr << "Error getting interface descriptors: " << GetLastError() << std::endl;
  656.  
  657.     }
  658.  
  659.     delete[] configBuffer;
  660.  
  661.     HRESULT hr = GetIsochPipes(&deviceData);
  662.  
  663.     if (FAILED(hr))
  664.  
  665.     {
  666.  
  667.         std::cerr << "Error getting isochronous pipes: " << GetErrorMessage(hr) << std::endl;
  668.  
  669.     }
  670.  
  671.    
  672.  
  673.     else
  674.  
  675.     {
  676.  
  677.         // Send isochronous IN transfer
  678.  
  679.         SendIsochInTransfer(&deviceData, TRUE);  // <--- `True` if using ASAP.
  680.  
  681.     }
  682.  
  683.     WinUsb_Free(deviceData.WinusbHandle);
  684.  
  685.     CloseHandle(deviceData.DeviceHandle);
  686.  
  687.     return 0;
  688.  
  689. }
Advertisement
Add Comment
Please, Sign In to add comment