//########################################################################### // // FILE: tiusbdll.cpp // // TITLE: A thin layer over WinUSB allowing device open/close/ // read and write functionality. // // The WinUSB code in this example is based on information provided // in Microsoft's white paper, "How to Use WinUSB to Communicate // with a USB Device", WinUsb_HowTo.doc, available from URL: // // http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde- // d599bac8184a/WinUsb_HowTo.doc // //########################################################################### // // $Release Date: December 12, 2011 $ // $Copyright: // Copyright (C) 2013-2023 Texas Instruments Incorporated - http://www.ti.com/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // $ //########################################################################### #include "stdafx.h" #include #include #include #include #include #include #include "winusb.h" #include "usb100.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #include "resource.h" // main symbols // CusbdllApp // See usbdll.cpp for the implementation of this class // class CtiusbdllApp : public CWinApp { public: CtiusbdllApp(); // Overrides public: virtual BOOL InitInstance(); DECLARE_MESSAGE_MAP() }; // CtiusbdllApp BEGIN_MESSAGE_MAP(CtiusbdllApp, CWinApp) END_MESSAGE_MAP() // CusbdllApp construction CtiusbdllApp::CtiusbdllApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CusbdllApp object CtiusbdllApp theApp; // CusbdllApp initialization BOOL CtiusbdllApp::InitInstance() { CWinApp::InitInstance(); return TRUE; } //**************************************************************************** // // Buffer size definitions. // //**************************************************************************** #define MAX_DEVPATH_LENGTH 256 //**************************************************************************** // // Flag indicating that a blocking read should be performed. // //**************************************************************************** #define TIUSB_WAIT_FOREVER 0xFFFFFFFF //**************************************************************************** // // Structure containing handles and information required to communicate with // the USB bulk device. // //**************************************************************************** typedef struct { HANDLE deviceHandle; WINUSB_INTERFACE_HANDLE winUSBHandle; UCHAR deviceSpeed; UCHAR bulkInPipe; UCHAR bulkOutPipe; HANDLE hReadEvent; } tDeviceInfoWinUSB; typedef void *TIUSB_HANDLE; //**************************************************************************** // // Returns the device path associated with a provided interface GUID. // // \param dwIndex is the zero-based index of the device whose path is to be // found. // \param InterfaceGuid is a pointer to the GUID for the interface that // we are looking for. Assuming you installed the device using the sample // INF file, this will be the GUID provided in the Dev_AddReg section. // \param pcDevicePath is a pointer to a buffer into which will be written // the path to the device if the function is successful. // \param BufLen is the size of the buffer pointed to by \f pcDevicePath. // // Given an interface GUID, this function determines the path that is // necessary to open a file handle on the USB device we are interested in // talking to. It returns the path to the first device which is present in // the system and which offers the required interface. // // \return Returns one of the following Windows system error codes. // ERROR_SUCCESS if the operation completed successfully // ERROR_DEV_NOT_EXIST if the interface is not found on the system. This // implies that the driver for the USB device has not been installed. // ERROR_DEVICE_NOT_CONNECTED if the interface has been installed but no // device offering it is presently available. This typically indicates // that the USB device is not currently connected. // ERROR_NOT_ENOUGH_MEMORY if the function fails to allocate any required // buffers. // ERROR_INSUFFICIENT_BUFFER if the buffer passed is too small to hold // the device path. //**************************************************************************** static DWORD GetDevicePath(DWORD dwIndex, LPGUID InterfaceGuid, PCHAR pcDevicePath, size_t BufLen) { BOOL bResult = FALSE; HDEVINFO deviceInfo; SP_DEVICE_INTERFACE_DATA interfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL; ULONG length; ULONG requiredLength=0; HRESULT hr; // // Get a handle to the device information set containing information on // the interface GUID supplied on this PC. // deviceInfo = SetupDiGetClassDevs(InterfaceGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if(deviceInfo == INVALID_HANDLE_VALUE) { // // No device offering the required interface is present. Has the // interface been installed? Ask for the information set for all // devices and not just those that are presently installed. // deviceInfo = SetupDiGetClassDevs(InterfaceGuid, NULL, NULL, DIGCF_DEVICEINTERFACE); if(deviceInfo == INVALID_HANDLE_VALUE) { return(ERROR_DEV_NOT_EXIST); } else { SetupDiDestroyDeviceInfoList(deviceInfo); return(ERROR_DEVICE_NOT_CONNECTED); } } interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); // // SetupDiGetClassDevs returned us a valid information set handle so we // now query this set to find the find the first device offering the // interface whose GUID was supplied. // bResult = SetupDiEnumDeviceInterfaces(deviceInfo, NULL, InterfaceGuid, dwIndex, &interfaceData); if(bResult == FALSE) { // // We failed to find the first matching device so tell the caller // that no suitable device is connected. // return(ERROR_DEVICE_NOT_CONNECTED); } // // Now that we have the interface information, we need to query details // to retrieve the device path. First determine how much space we need to // hold the detail information. // SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL); // // Allocate a buffer to hold the interface details. // detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength); if(NULL == detailData) { SetupDiDestroyDeviceInfoList(deviceInfo); return(ERROR_NOT_ENOUGH_MEMORY); } detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); length = requiredLength; // // Now call once again to retrieve the actual interface detail information. // bResult = SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL); if(FALSE == bResult) { SetupDiDestroyDeviceInfoList(deviceInfo); LocalFree(detailData); return(GetLastError()); } // // Copy the device path string from the interface details structure into // the caller's buffer. // hr = StringCchCopy((LPTSTR)pcDevicePath, BufLen, (LPCTSTR)detailData->DevicePath); if(FAILED(hr)) { // // We failed to copy the device path string! // SetupDiDestroyDeviceInfoList(deviceInfo); LocalFree(detailData); return(ERROR_INSUFFICIENT_BUFFER); } // // Clean up and free locally allocated resources. // SetupDiDestroyDeviceInfoList(deviceInfo); LocalFree(detailData); return(ERROR_SUCCESS); } //**************************************************************************** // // Opens a given instance of the USB device and returns a file handle. // // \param dwIndex is the zero-based index of the device that is to be opened. // \param lpGUID is a pointer to the GUID of the interface that is to // be opened. // // This function determines whether or not the required USB device is // available and, if so, creates a file allowing access to it. The file // handle is returned on success. // // \return Returns a valid file handle on success of INVALID_HANDLE_VALUE // on failure. In cases of failure, GetLastError() can be called to determine // the cause. // //**************************************************************************** static HANDLE OpenDeviceByIndex(DWORD dwIndex, LPGUID lpGUID) { HANDLE hDev = NULL; char devicePath[MAX_DEVPATH_LENGTH]; BOOL retVal; // // Get the path needed to open a file handle on our USB device. // retVal = GetDevicePath(dwIndex, lpGUID, devicePath, sizeof(devicePath)); if(retVal != ERROR_SUCCESS) { SetLastError(retVal); return(INVALID_HANDLE_VALUE); } // // Open the file we will use to communicate with the device. // hDev = CreateFile((LPCTSTR)devicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); return(hDev); } //**************************************************************************** // // Opens the first instance of a given USB device and returns a file handle. // // \param lpGUID is a pointer to the GUID of the interface that is to // be opened. // // This function determines whether or not the required USB device is // available and, if so, creates a file allowing access to it. The file // handle is returned on success. // // \return Returns a valid file handle on success of INVALID_HANDLE_VALUE // on failure. In cases of failure, GetLastError() can be called to determine // the cause. // //**************************************************************************** static HANDLE OpenDevice(LPGUID lpGUID) { return(OpenDeviceByIndex(0, lpGUID)); } //**************************************************************************** // // Determines that the required USB device is present, opens it and gathers // required information to allow us to read and write it. // // \param usVID is the vendor ID of the device to be initialized. // \param usPID is the product ID of the device to be intialized. // \param lpGUID is the GUID of the device interface that is to be opened. // \param dwIndex is the zero-based index of the device to open. // \param bOpenDataEndpoints is \e TRUE to open endpoints for bulk transfer // of data to and from the host or \e FALSE to open the device without // attempting to open any additional endpoints. // \param pbDriverInstalled is written to \e TRUE if the device driver for the // required class is installed or \e FALSE otherwise. // // This function is called to initialize the USB device and perform one-off // setup required to allocate handles allowing the application to read and // write the device endpoints. It offers a superset of the function provided // by InitializeDevice(), allowing a caller to specify an index to differentiate // between multiple devices of the same type and also offering the ability to // open a device without opening endpoint handles for bulk data transfer. // // \return Returns a valid handle on success or NULL on failure. In failing // cases, GetLastError() can be called to determine the cause. // //**************************************************************************** extern "C" TIUSB_HANDLE PASCAL EXPORT InitializeDeviceByIndex(unsigned short usVID, unsigned short usPID, LPGUID lpGUID, DWORD dwIndex, BOOL bOpenDataEndpoints, BOOL *pbDriverInstalled) { BOOL bResult; WINUSB_INTERFACE_HANDLE usbHandle; USB_INTERFACE_DESCRIPTOR ifaceDescriptor; WINUSB_PIPE_INFORMATION pipeInfo; UCHAR speed; ULONG length; DWORD dwError; int i; tDeviceInfoWinUSB *psDevInfo; // // Check for NULL pointer parameters. // if(!lpGUID || !pbDriverInstalled) { return(NULL); } // // Allocate a new device info structure. // psDevInfo = (tDeviceInfoWinUSB *)LocalAlloc(LPTR, sizeof(tDeviceInfoWinUSB)); if(!psDevInfo) { return(NULL); } // // Determine whether the USB device is present and, if so, generate a file // handle to allow access to it. // psDevInfo->deviceHandle = OpenDeviceByIndex(dwIndex, lpGUID); if(psDevInfo->deviceHandle == INVALID_HANDLE_VALUE) { // // We were unable to access the device - is that because the device isn't // connected or has the driver not been installed? // dwError = GetLastError(); *pbDriverInstalled = (dwError == ERROR_DEV_NOT_EXIST) ? FALSE : TRUE; // // Free our instance data. // LocalFree((HLOCAL)psDevInfo); // // Return an error to the caller. // return(NULL); } // // The device is opened so we now initialize the WinUSB layer passing it // the device handle. // bResult = WinUsb_Initialize(psDevInfo->deviceHandle, &usbHandle); if(bResult) { // // If we managed to initialize the WinUSB layer, we now query the // device descriptor to determine the speed of the device. // psDevInfo->winUSBHandle = usbHandle; length = sizeof(UCHAR); bResult = WinUsb_QueryDeviceInformation(psDevInfo->winUSBHandle, DEVICE_SPEED, &length, &speed); } // // The device opened correctly. Do we need to also open pipes to allow us // to send and receive data via the bulk endpoints? // if(bOpenDataEndpoints) { // // Yes - we do need to open the endpoints for data transfer. // if(bResult) { // // If all is well, now query the interface descriptor. We ask for the // first interface only since, in the case of the generic bulk device, // this is all that is available. // psDevInfo->deviceSpeed = speed; bResult = WinUsb_QueryInterfaceSettings(psDevInfo->winUSBHandle, 0, &ifaceDescriptor); } if(bResult) { // // We got the interface descriptor so now we enumerate the endpoints // to find the two we require - one bulk IN endpoint and one bulk OUT // endpoint. // for(i=0;iwinUSBHandle, 0, (UCHAR) i, &pipeInfo); if((pipeInfo.PipeType == UsbdPipeTypeBulk) && USB_ENDPOINT_DIRECTION_IN(pipeInfo.PipeId)) { psDevInfo->bulkInPipe = pipeInfo.PipeId; } else if((pipeInfo.PipeType == UsbdPipeTypeBulk) && USB_ENDPOINT_DIRECTION_OUT(pipeInfo.PipeId)) { psDevInfo->bulkOutPipe = pipeInfo.PipeId; } else { // // Hmm... we found an endpoint that we didn't expect to see // on this interface. This tends to imply that there is a // mismatch between the device configuration and this // application so we will fail the call after setting an // appropriate error code for the caller to query. // SetLastError(ERROR_NOT_SAME_DEVICE); bResult = FALSE; break; } } } // // Did all go well so far? // if(bResult) { // // All is well - create the signal event we need. // psDevInfo->hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // // If we created the event successfully, return a good handle. // if(psDevInfo->hReadEvent) { // // Everything is good. Set the return code flag to indicate // this. // bResult = true; } } } else { // // Data transfer endpoints do not need to be opened so merely mark // this in the device info structure. // psDevInfo->bulkOutPipe = 0; psDevInfo->bulkInPipe = 0; psDevInfo->hReadEvent = NULL; } // // Did all go well? // if(bResult) { // // All is well - return the instance data pointer as a handle to the // caller. // return((TIUSB_HANDLE)psDevInfo); } else { // // If we drop through to here, something went wrong so free the // instance structure and return NULL. // LocalFree((HLOCAL)psDevInfo); return(NULL); } } //**************************************************************************** // // Determines that the required USB device is present, opens it and gathers // required information to allow us to read and write it. // // \param usVID is the vendor ID of the device to be initialized. // \param usPID is the product ID of the device to be intialized. // \param lpGUID is the GUID of the device interface that is to be opened. // \param pbDriverInstalled is written to \e TRUE if the device driver for the // required class is installed or \e FALSE otherwise. // // This function is called to initialize the USB device and perform one-off // setup required to allocate handles allowing the application to read and // write the device endpoints. // // InitializeDevice() always opens the first instance of a device and assumes // that the caller will always want to open handles allowing communication via // only IN and OUT bulk endpoints. To open an instance of a device other than // the first or to open a device without opening handles on any endpoints other // than the control endpoint, InitializeDeviceByIndex() may be used instead. // // \return Returns a valid handle on success or NULL on failure. In failing // cases, GetLastError() can be called to determine the cause. // //**************************************************************************** extern "C" TIUSB_HANDLE PASCAL EXPORT InitializeDevice(unsigned short usVID, unsigned short usPID, LPGUID lpGUID, BOOL *pbDriverInstalled) { // // Call our more capable cousin to open the first instance of the device // and set up for data transfer via bulk IN and OUT endpoints. // return(InitializeDeviceByIndex(usVID, usPID, lpGUID, 0, true, pbDriverInstalled)); } //**************************************************************************** // // Performs a control transfer on endpoint 0. // // \param hUSB is the device handle as returned from InitializeDevice(). // \param ucRequestType is the value to be placed in the bmRequestType field // of the setup data as defined in table 9-2 of the USB 2.0 specification. The // value is an ORed combination of the REQUEST_xxx flags found in tiusbdll.h. // \param ucRequest is the specific request identifier which will be placed in // the bRequest field of the setup data for the transaction. // \param usValue is the request-specific value placed into the wValue field of // the setup data for the transaction. // \param usIndex is the value placed into the wIndex field of the setup data // for the transaction. // \param usLength is the number of bytes of data to transfer from or to the // caller-supplied buffer during the transaction. This value is used to set // the wLength field in the setup data for the transaction. // \param pucBuffer is a pointer to a buffer containing at least usLength bytes. // If ucRequestType contains REQUEST_TRANSFER_IN, usLength bytes of data will // be transfered from the device and written into this buffer. If ucRequestType // contains REQUEST_TRANSFER_OUT, usLength bytes of data will be transfered // from this buffer to the device. // \param pusCount will be written with the number of bytes actually written to // or read from the device. // // This function performs a control transfer to the device. This transaction // may transfer data to or from the device depending upon the value of the // ucRequestType parameter. // // \return Returns \e TRUE on success or \e FALSE on failure. If \e FALSE is // returned, GetLastError() may be called to determine the cause of the failure. // //**************************************************************************** extern "C" BOOL PASCAL EXPORT Endpoint0Transfer(TIUSB_HANDLE hUSB, UCHAR ucRequestType, UCHAR ucRequest, USHORT usValue, USHORT usIndex, USHORT usLength, PUCHAR pucBuffer, PUSHORT pusCount) { tDeviceInfoWinUSB *psDevInfo = (tDeviceInfoWinUSB *)hUSB; WINUSB_SETUP_PACKET sSetup; ULONG ulCount; ULONG ulError; BOOL bRetcode; // // Check for invalid parameters. We require a buffer pointer if the // transfer length is non-zero. // if(!hUSB || (usLength && !pucBuffer)) { return(0); } // // Fill in the setup data structure. // sSetup.RequestType = ucRequestType; sSetup.Request = ucRequest; sSetup.Value = usValue; sSetup.Index = usIndex; sSetup.Length = usLength; // // Issue the control transaction. // bRetcode = WinUsb_ControlTransfer(psDevInfo->winUSBHandle, sSetup, pucBuffer, (ULONG)usLength, &ulCount, NULL); // // Return either the number of bytes written/read or 0 if there was an // error. // if(bRetcode) { *pusCount = (USHORT)ulCount; } else { ulError = GetLastError(); *pusCount = 0; } return(bRetcode); } //**************************************************************************** // // Cleans up and free resources associated with the USB device communication // prior to exiting the application. // // \param hUSB is the device handle as returned from InitializeDevice(). // // This function should be called prior to exiting the application to free // the resources allocated during InitializeDevice(). // // \return Returns \e TRUE on success or \e FALSE on failure. // //**************************************************************************** extern "C" BOOL PASCAL EXPORT TerminateDevice(TIUSB_HANDLE hUSB) { BOOL bRetcode; BOOL bRetcode2; tDeviceInfoWinUSB *psDevInfo = (tDeviceInfoWinUSB *)hUSB; // // Check for a bad handle. // if(!hUSB) { return(false); } // // Free WinUSB and Windows resources. // bRetcode = WinUsb_Free(psDevInfo->winUSBHandle); bRetcode2 = CloseHandle(psDevInfo->deviceHandle); // // Free the device instance structure. // LocalFree((HLOCAL)psDevInfo); // // Did all go well? // if(bRetcode & bRetcode2) { return(TRUE); } else { return(FALSE); } } //**************************************************************************** // // Writes a buffer of data to the USB device via the bulk OUT endpoint. // // \param hUSB is the device handle as returned from InitializeDevice(). // \param pcBuffer points to the first byte of data to send. // \param ulSize contains the number of bytes of data to send. // \param pulWritten is a pointer which will be written with the number of // bytes of data actually written to the device. // // This function is used to send data to the USB device via its bulk OUT endpoint. // // \return Returns \e TRUE on success or \e FALSE on failure. // //**************************************************************************** extern "C" BOOL PASCAL EXPORT WriteUSBPacket(TIUSB_HANDLE hUSB, unsigned char *pcBuffer, unsigned long ulSize, unsigned long *pulWritten) { BOOL bResult; tDeviceInfoWinUSB *psDevInfo = (tDeviceInfoWinUSB *)hUSB; // // Check for bad parameters. // if(!hUSB || !pcBuffer || !pulWritten || !ulSize) { return(false); } // // Ask WinUSB to write the data for us. // bResult = WinUsb_WritePipe(psDevInfo->winUSBHandle, psDevInfo->bulkOutPipe, pcBuffer, ulSize, pulWritten, NULL); return(bResult); } //**************************************************************************** // // Reads data from the USB device via the bulk IN endpoint. // // \param hUSB is the device handle as returned from InitializeDevice(). // \param pcBuffer points to a buffer into which the data from the device will // be written. // \param ulSize contains the number of bytes that are requested from the // device. // \param pulRead is a pointer which will be written with the number of // bytes of data actually read from the device. // \param ulTimeoutmS is the number of milliseconds to wait for the // data before returning an error. To block indefinitely, set this parameter // to INFINITE. // \param hBreak is the handle of an event that may be used to break out of // the read operation. If this is a valid event handle and the event is // signalled, the read operation will return immediately. Set this to // NULL if the additional break condition is not required. // // This function is used to receive data from the USB device via its bulk IN // endpoint. // // \return Returns \e ERROR_SUCCESS on success, WAIT_TIMEOUT if the data as // not received within ulTimeoutmS milliseconds, WAIT_OBJECT_0 if the break // event was signalled or other Windows system error codes on failure. // //**************************************************************************** extern "C" DWORD PASCAL EXPORT ReadUSBPacket(TIUSB_HANDLE hUSB, unsigned char *pcBuffer, unsigned long ulSize, unsigned long *pulRead, unsigned long ulTimeoutmS, HANDLE hBreak) { BOOL bResult; DWORD dwError; OVERLAPPED sOverlapped; HANDLE phSignals[2]; tDeviceInfoWinUSB *psDevInfo = (tDeviceInfoWinUSB *)hUSB; // // Check for bad parameters. // if(!hUSB || !pcBuffer || !pulRead || !ulSize) { return(ERROR_INVALID_PARAMETER); } // // Tell WinUSB how to signal us when reads are completed (if blocking) // sOverlapped.hEvent = psDevInfo->hReadEvent; sOverlapped.Offset = 0; sOverlapped.OffsetHigh = 0; // // Perform the read. // bResult = WinUsb_ReadPipe(psDevInfo->winUSBHandle, psDevInfo->bulkInPipe, pcBuffer, ulSize, pulRead, &sOverlapped); // // A good return code indicates success regardless of whether we performed // a blocking or non-blocking read. // if(bResult) { dwError = ERROR_SUCCESS; } else { // // An error occurred or the read will complete asynchronously. // Which is it? // dwError = GetLastError(); // // Check for error cases other than the one we expect. // if(dwError == ERROR_IO_PENDING) { // // The IO is pending so wait for it to complete or for a // timeout to occur. // phSignals[0] = psDevInfo->hReadEvent; phSignals[1] = hBreak; dwError = WaitForMultipleObjects(hBreak ? 2 : 1, phSignals, FALSE, ulTimeoutmS); // // At this stage, one of three things could have occurred. // Either we read a packet or we timed out or the break // signal was detected. Which was it? // if(dwError == WAIT_OBJECT_0) { // // The overlapped IO request completed so check to see how // many bytes we got. // bResult = GetOverlappedResult(psDevInfo->deviceHandle, &sOverlapped, pulRead, FALSE); if(bResult) { dwError = ERROR_SUCCESS; } else { // // Something went wrong. Return the Windows error code. // dwError = GetLastError(); } } else { // // Something went wrong - abort the transfer // WinUsb_AbortPipe(psDevInfo->winUSBHandle, psDevInfo->bulkInPipe); // // Was the break event signalled? // if(dwError == (WAIT_OBJECT_0 + 1)) { // // The break event was signalled - abort the read and return. // dwError = WAIT_OBJECT_0; } } } } // // Pass the result back to the caller. // return(dwError); }