965 lines
32 KiB
C++
965 lines
32 KiB
C++
|
|
//###########################################################################
|
||
|
|
//
|
||
|
|
// 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) 2014-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 <windows.h>
|
||
|
|
#include <setupapi.h>
|
||
|
|
#include <devguid.h>
|
||
|
|
#include <regstr.h>
|
||
|
|
#include <strsafe.h>
|
||
|
|
#include <initguid.h>
|
||
|
|
#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;i<ifaceDescriptor.bNumEndpoints;i++)
|
||
|
|
{
|
||
|
|
bResult = WinUsb_QueryPipe(psDevInfo->winUSBHandle, 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
|