Using WinUSB with the USBHID Library

nonono!

As of August 14th, 2012, this is a work-in-progress!

Any comments to help guide what is covered / how it is covered are welcome ^_^! Any quick questions regarding using WinUSB with the mbed are also welcome, but most should be covered by the end of this article.

-

Also, please note that using 'goto' in any C++ file is the same as asking the C++ Gurus to add inline-QBASIC to the language - never, ever, ever, EVER use goto when programming in C++!!!!

-

Information

The scope of this article is limited to projects that target the Windows 7 platform on the PC-side. The mbed is (obviously) the device-side target, though information concerning the PC-side drivers and software should be applicable to any USB-enabled microcontroller (MCU) from our friends at NXP. Visual Studio 2010, avaliable through Microsoft, will be used to develop the PC-side program along with the WinUSB (also from Microsoft) libraries. WinUSB and the tools for creating the drivers are provided by Microsoft's Windows Driver Kit (WDK). Links and information on how to obtain the above software packages is detailed in Section 2 - Setup.

Abstract

Using the universal serial bus (USB) protocol to send data from an embedded device is almost a requirement for today's electronics. However, because most chip-to-chip communications utilize less regulated protocols such as SPI and 2-wire, many developers leave device-to-device communication for after the proof-of-concept is finished - since USB tends to be more complicated to implement than simply passing chip-to-chip serial data to another device using RS232. This project aims to give developers an alternative to the RS232 protocol for early-stage projects while still keeping the simplicity and overall appeal of standard low-speed, serial protocols.

Section 1 - Introduction

Look at any timeline for designing an embedded device and you'll find that pretty much everything is sandwiched between 'Initial USB Design' and 'Final USB Optimization'. Ask any of the engineers what protocol they used to obtain debug data and you will find that, ironically, no one uses the device's intended communication protocol to aid in the development phase. The reasons for this are universal and simple - USB generally requires more debugging itself than anything you can debug with it!

For example, let's say we're making a simple USB mouse that will use a LPC17xx MCU to take data from a touch-controller and output the formatted X and Y coordinates over USB to a PC. The time-tested method for prototyping this design would be to wire everything up and push the raw output from the touch-controller to the PC using RS232. Why RS232 and not USB? Simply put, most developers have a security-blanket affair with RS232. That is, if our USB mouse started truncating the X and Y values to 3 decimal-places, both the touch-controller and debugging interface would become suspects during troubleshooting - whereas the touch-controller would be the obvious suspect when debugging via RS232. Every embedded developer (as well as nearly every MCU manufacturer) recognizes the security we feel when using RS232 to debug our prototypes - the last thing a developer wants to debug is the debug interface!

Why should we do-away with our beloved debugging protocol? In the end, relying on a low-bandwidth serial protocol for debugging will result in a product that converts data optimized for a low-bandwidth serial protocol into something compatible with USB standards. Like giving a fiber-optic network connection to a comodore64, creating a serial-port device that gets converted to USB at the end is just asking for bottlenecks later on.

Overall, early implementation of a simple and - more importantly - a reliable USB protocol allows developers to design from the ground-up using USB-level bandwidths, develop the product's USB capabilities over the entire project's timeline, and encourages programmers to implement algorithms and data structures tailored for the final product.

Section 2 - Setup

This article will help you create simple, reliable, and reusable code for implementing USB at the first stage of development. Unlike most code detailed within the mbed community, our project has two targets - each requiring specific attention. In order to reduce the time you spend jumping from the low-level to operating-system mindset, we will first obtain everything we need before detailing why we need them or how we will use them.

2.1 - PC-Side Development Environment

PC-side code will be in C/C++ and compiled using Microsoft's Visual Studio 2010. This can be downloaded as either the Professional version or the Express (trial) version. Make sure you install the package for the C++ programming language.

Download Visual C++ 2010 Express from: http://www.microsoft.com/visualstudio/en-us/products/2010-editions/express

2.2 - PC-Side Drivers and Libraries

Drivers will be provided by Microsoft's WDK. The WinUSB libraries, includes, and tools for signing drivers are also included in this package.

Download the Windows Driver Kit 7.1 from: http://www.microsoft.com/en-us/download/details.aspx?id=11800

2.3 - MCU-Side Libraries

A new project created from within the mbed.org web-based compiler will need to be created and the USBDevice library must be imported to the project.

View the details of this incredible library at: http://mbed.org/handbook/USBDevice

2.4 - Pin assignment for USB-Powered I/O

One of the greatest things about USB is that D+ connects to D+. This might seem small compared to the other complexities of the USB protocol, but I'm sure whoever decided that the red wire should connect to another red wire had a few issues with 'Tx-connected-to-Tx' while using RS232... Be careful to make your wire connections as short as possible. The operating speed of a standard USB HID device transforms any uncoupled wire into a miniature transmission tower. Because of this, data packets often fail at high-speed USB standards if the cable is too noisy.

Standard mbed USB setup will be used, which is also from: http://mbed.org/handbook/USBDevice

nonono!

Never connect the USB port used to program the mbed while having any of the side-accessed USB pins connected!

PCs will detect USB over-current on a per-device basis - having both USB ports connected to the mbed will allocate it twice the current it needs!

Section 3 - Basic Component Identification

This is where the code starts! In order to have our mbed talk to a Windows program, we need assign it a driver. Actually, and more importantly, we need to have a driver which is accessible to our PC-side program. There's more literature out there on the web and in books on this subject than I'd care to list, so if driver development is your thing, then google away! This article, however, is geared towards getting your device to talk to your program with minimal effort.

And minimal effort it will be. Microsoft has seemingly already stepped up to our PC-side challenge by creating the WinUSB library - which aims to allow Windows OS programmers the ability to use custom USB devices without having to create custom USB device drivers! From the mbed community, we have USBHID on our side - which gives us the advantage of setting up the LPC's USB stack without having to, well, setup the USB stack! In fact, the only thing we need to do that's not provided by a third party is telling the mbed to send data and telling our PC-side program how to display that data.

Of course, a much deeper understanding of these libraries is required to properly implement them, but - as the mbed philosophy goes - fast and rapid prototyping is the key to successful, profitable devices!

3.1 - Custom HID Driver Creation (PC-Side)

[Driver INF file editing and signing, *.cat creation using WDK tools]

After installing WDK, we need to setup a folder that's easily accessible for the time being, somewhere near root is recommended. I used C:\myDriver\ for this example.



WinUSB requires us to provide two dll files with our distribution - they connect the library version we used to create our PC-side program with to functions that talk to the OS requesting device information and such.
The two dlls that we need in our driver's root folder are shown below. Copy and paste them into the folder you created (the one equivalent to C:\myDriver\).


/media/uploads/Fuzball/wdk_setup_1.png


/media/uploads/Fuzball/wdk_setup_2.png


/media/uploads/Fuzball/wdk_setup_3.png
These are the two dll's you need within your folder's root.



Now we need to create a .inf that provides information about the device our driver will be assigned to, as well as information about how the device will be implemented when connected to the PC. The file is a small modification to the one provided by the MSDN, the .inf used in this example is provided below and important, identifying parts are highlighted.

[Version]
Signature = "$Windows NT$"
Class     = USB Device
ClassGUID = {745a17a0-74d3-11d0-b6fe-00a0c90f57da}
Provider = %ManufacturerName%
CatalogFile=mbed_Kitty.cat
DriverVer=04/15/2012,6.2.8181.0

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%ManufacturerName% = Standard,NTamd64

[Standard.NTamd64]
%DeviceName% =USB_Install, USB\VID_1337&PID_1234

; =================== Installation ===================

[USB_Install]
Include=winusb.inf
Needs=WINUSB.NT

[USB_Install.Services]
Include=winusb.inf
AddService=WinUsb,0x00000002,WinUsb_ServiceInstall

[WinUsb_ServiceInstall]
DisplayName     = %WinUsb_SvcDesc%
ServiceType     = 1
StartType       = 3
ErrorControl    = 1
ServiceBinary   = %12%\WinUSB.sys

[USB_Install.Wdf]
KmdfService=WINUSB, WinUsb_Install

[WinUsb_Install]
KmdfLibraryVersion=1.11

[USB_Install.HW]
AddReg=Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{f167724d-74d3-430e-86b5-f0368910eb22}"

[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles

[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01009.dll,WdfCoInstaller","WinUsbCoInstaller2.dll"

[CoInstallers_CopyFiles]
WinUsbCoInstaller2.dll
WdfCoInstaller01009.dll

[DestinationDirs]
CoInstallers_CopyFiles=11

; ================= Source Media Section =====================

[SourceDisksNames]
1 = %DiskName%

[SourceDisksFiles]
WinUsbCoInstaller2.dll=1
WdfCoInstaller01009.dll=1

; =================== Strings ===================

[Strings]
ManufacturerName="FuzCo"
ClassName="Data Acquisition Devices"
DiskName="USB Installation Disk"
WinUsb_SvcDesc="WinUSB Driver"
DeviceName="myAwesomeDevice"



/media/uploads/Fuzball/wdk_setup_4.png
Start by creating a new text-file in your folder's root. Copy the above script into notepad. /media/uploads/Fuzball/wdk_setup_5.png
Here, the VID is set to 1337, and the device PID is 1234. This will help Windows to determine if the connected device is compatible with the driver that this file will create.

<<info>>Note that driver installation files are the only valid windows files with the extension '.cat', any filename puns must proceed the 'CatalogFile' keyword. <</info>>
/media/uploads/Fuzball/wdk_setup_6.png
Make sure that the filenames here match the dll's you copied into your folder's root. Setting Devicename will have the chosen string as the device's title in your system and will be displayed within DeviceManager.
/media/uploads/Fuzball/wdk_setup_7.png
Save the file as shown; select the extension as 'all files' and the encoding as 'unicode'.
/media/uploads/Fuzball/wdk_setup_8.png
/media/uploads/Fuzball/wdk_setup_9.png
/media/uploads/Fuzball/wdk_setup_10.png
Open up command prompt and, using the WDK tool at the indicated location, run the command as shown, or whichever is equivialant to your setup. Note that the /os flag here is for my 64-bit Windows 7 operating system. If I was running on 32-bit windows, I would have used the flag /os:7_X86.


TODO:: fix this picture!!! the actual command would be 'Inf2Cat.exe /driver:C:\myDriver /os:7_X64' - do not include the inf's filename in the path for the driver flag!
/media/uploads/Fuzball/wdk_setup_11.png


/media/uploads/Fuzball/wdk_setup_12.png
If all goes well, you will have a personally signed driver that can be used on an mbed configured with our VID and PID settings!

3.11? Setting up Visual Studio for WinUSB Development

(Descriptions to come...) /media/uploads/Fuzball/vs_setup_1.png

/media/uploads/Fuzball/vs_setup_2.png

/media/uploads/Fuzball/vs_setup_3.png

/media/uploads/Fuzball/vs_setup_4.png

/media/uploads/Fuzball/vs_setup_5.png

3.2 - Linking the mbed to the PC-Side Driver

[discuss the parameters to set which cause windows to assign our driver to our mbed]


/media/uploads/Fuzball/mbed_setup_1.png
The mbed-side initialization of the USBDevice class which ties our device to our driver.


/media/uploads/Fuzball/mbed_setup_2.png
Here is the matching ID which we setup within the driver's kitty.inf, which is inherited to the kitty.cat file.

3.3 - Testing the Driver

[installing and assigning the mbed to our driver]

3.4 - Troubleshooting

[extensive not-working-when-I-plug-it-in section]

Section 4 - Simple USB I/O

4.1 - meow

It's not finished, but if you're feeling lucky - go nuts!

  • USB_Functions.h

// File:	"USB_Functions.h"
// Author:	Chris Yan
// Date:	April 15, 2012
// Desc:	Function Declarations for USB Data Project

/* Include Windows headers */
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>

/* Include WinUSB headers */
#include <winusb.h>
#include <Usb100.h>
#include <Setupapi.h>

/* Project headers */
#include "stdafx.h"

// Constant for {f167724d-74d3-430e-86b5-f0368910eb22}
static const GUID FC_DEVICE_INTERFACE = 
{ 0xf167724d, 0x74d3, 0x430e, { 0x86, 0xb5, 0xf0, 0x36, 0x89, 0x10, 0xeb, 0x22 } };

/* Structures */
struct PIPE
{
    UCHAR  PipeInId;
    UCHAR  PipeOutId;
	UCHAR  PipeInterruptFirst;
	UCHAR  PipeInterruptLast;
	UCHAR  PipeControl;
};

/* Functions */
// PRE:		Function is called from main loop as the return value
// POST:	User is bid farewell before exiting and errors are reported if errorFlag != 0
int ExitProgram( bool errorFlag );

// PRE:		phWinUsbHandle must not represent -log(H) ... :)
// POST:	WinUSB fills out the interface handle using the device handle provided.
//				Errors are returned as any non-zero value
bool StartWinUSB( HANDLE hDeviceHandle, PWINUSB_INTERFACE_HANDLE phWinUsbHandle );

bool FindEndpoints(WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE* pipeid);

bool ReadPipe(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG cbSize);

bool WritePipe(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten);

// PRE:		hDeviceHandle must be unused and NULL
// POST:	WinUsb will search the registry for matching devices installed with the GUID.
//				If a matching device is found, the provided PHANDLE will be filled with
//				the matching device's information (location, name, etc...).
bool SearchGUID(GUID guidDeviceInterface, PHANDLE hDeviceHandle);
  • USB_Functions.cpp

// File:	"USB_Functions.cpp"
// Author:	Chris Yan
// Date:	April 15, 2012
// Desc:	Functions for USB Data Project
//			Based off of examples from the MSDN avaliable at:
//			http://msdn.microsoft.com/en-us/library/windows/hardware/ff540174(v=vs.85).aspx


// Project headers
#include "stdafx.h"
#include "USB_Functions.h"

int ExitProgram( bool errorFlag )
{
	if(errorFlag)
		printf("\n\nProgram aborted. ");
	else
		printf("\n\nProgram finished. ");

	system("PAUSE");

	return errorFlag;
}

bool StartWinUSB( HANDLE hDeviceHandle, PWINUSB_INTERFACE_HANDLE phWinUSBHandle )
{
	printf("\n\nInitializing WinUSB Interface for the device...");
	if( hDeviceHandle == INVALID_HANDLE_VALUE )
	{
		printf(" error.\n    Device Handle is invalid.");
		return 1;
	}

	if( !(WinUsb_Initialize(hDeviceHandle, phWinUSBHandle)) )
	{
		printf(" error.\n    Unable to start WinUSB for the device.");
		return 1;
	} else
		printf(" done.");
	
	return 0;
}

bool FindEndpoints(WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE* pipeid)
{
	// Variable Declarations
	USB_INTERFACE_DESCRIPTOR interfaceDesc;
	WINUSB_PIPE_INFORMATION pipeInfo;
	bool ef = 0;

	// Variable Setup
	ZeroMemory( &interfaceDesc, sizeof(USB_INTERFACE_DESCRIPTOR) );
	ZeroMemory( &pipeInfo, sizeof(WINUSB_PIPE_INFORMATION) );

	// Setup our query for the specified device
	printf("\nCreating and sending device query...");
	if( !WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &interfaceDesc) )
	{
		printf(" Error.\n    USB Interface query failed.\n");
		return 1;
	}
	printf("\n#Descriptor type   : %d", interfaceDesc.bDescriptorType);
	printf("\n#Interface Class   : %d", interfaceDesc.bInterfaceClass);
	printf("\n#Interface Number  : %d", interfaceDesc.bInterfaceNumber);
	printf("\n#Interface Protocol: %d", interfaceDesc.bInterfaceProtocol);
	printf("\n#Interface SubClass: %d", interfaceDesc.bInterfaceSubClass);
	printf("\n#iInterface        : %d", interfaceDesc.iInterface);
	printf("\n#Endpoints Found   : %d", interfaceDesc.bNumEndpoints);
	
	printf("\nFinished device query.");

	printf("\n\nCreating and sending endpoint queries...");
	for(int idx = 0; idx < interfaceDesc.bNumEndpoints; idx++)
	{
		if( WinUsb_QueryPipe(hDeviceHandle,0,idx,&pipeInfo) )
		{
			if(pipeInfo.PipeType == UsbdPipeTypeControl)
				printf("\n    Endpoint %d:\n      Type: Control\n      PipeID: %d",idx,pipeInfo.PipeId);
			if(pipeInfo.PipeType == UsbdPipeTypeIsochronous)
				printf("\n    Endpoint %d:\n      Type: Isochronous\n      PipeID: %d",idx,pipeInfo.PipeId);
			if(pipeInfo.PipeType == UsbdPipeTypeBulk)
			{
				if( USB_ENDPOINT_DIRECTION_IN(pipeInfo.PipeId) )
				{
					printf("\n    Endpoint %d:\n      Type: Bulk-In\n      PipeID: %c",idx,pipeInfo.PipeId);
					pipeid->PipeInId = pipeInfo.PipeId;
				}
				if( USB_ENDPOINT_DIRECTION_OUT(pipeInfo.PipeId) )
				{
					printf("\n    Endpoint %d:\n      Type: Bulk-Out\n      PipeID: %c",idx,pipeInfo.PipeId);
					pipeid->PipeOutId = pipeInfo.PipeId;
				}
			}
			if(pipeInfo.PipeType == UsbdPipeTypeInterrupt)
			{
				printf("\n    Endpoint %d:\n      Type: Interrupt\n      PipeID: %d",idx,pipeInfo.PipeId);
				if(pipeid->PipeInterruptFirst != 0)
				{
					pipeid->PipeInterruptLast = pipeInfo.PipeId;
					printf("\n      PipeInterruptLast set to this pipe.");
				}
				else
				{
					pipeid->PipeInterruptFirst = pipeInfo.PipeId;
					printf("\n      PipeInterruptFirst set to this pipe.");
				}
			}
			printf("\n      MaxPacket: %d", pipeInfo.MaximumPacketSize);
		}
	}
	printf("\nEndpoint query complete.");

	return ef;
}

bool ReadPipe(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG cbSize)
{
	if( hDeviceHandle == INVALID_HANDLE_VALUE )
		return 1;

	printf("\n\nAttempting to read from pipe %d...", *pID);
	UCHAR* szBuffer = (UCHAR*)LocalAlloc(LPTR, sizeof(UCHAR)*cbSize);
    
    ULONG cbRead = 0;

    if( !WinUsb_ReadPipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbRead, 0) )
	{
		printf(" error.\n    Unable to read from pipe %d.", *pID);
		LocalFree(szBuffer);
		return 1;
	}

    printf("Read from pipe %d: 0x%02X \nActual data read: %d.\n", *pID, (char)*szBuffer, cbRead);

    LocalFree(szBuffer);
    return 0;
}

bool WritePipe(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten)
{
	printf("\n\nAttempting to write to pipe %d.", *pID);
	if (hDeviceHandle==INVALID_HANDLE_VALUE || !pID || !pcbWritten)
    {
		printf("\n    Error while checking arguments.");
        return 1;
    }

    BOOL bResult = TRUE;

    UCHAR szBuffer[] = "Hello World, my name is fuz - nice to meet ya =^.^=!";
    ULONG cbSize = strlen((char*)szBuffer);
    ULONG cbSent = 0;

    bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0);
    if(!bResult)
    {
        return 1;
    }

    printf("Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID, szBuffer, cbSent);
    *pcbWritten = cbSent;


done:
    return 0;
}

bool SearchGUID(GUID guidDeviceInterface, PHANDLE hDeviceHandle)
{
	// Variable Declarations
	PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL;
	SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
	SP_DEVINFO_DATA deviceInfoData;
	LPTSTR lpDevicePath = NULL;
	HDEVINFO deviceInfo;

	ULONG requiredLength=0;
	DWORD numDevices = 0;
	bool ef = 0;
	
	// Variable setup
	deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

	// Get info about installed devices using our GUID's Interface
	printf("GUID: {%02X-%02X-%02X-%02X}",
		guidDeviceInterface.Data1, guidDeviceInterface.Data2,
		guidDeviceInterface.Data3, guidDeviceInterface.Data4);
	printf("\n\nSearching installed interfaces...");
	deviceInfo = SetupDiGetClassDevs(
		&guidDeviceInterface, NULL, NULL,
		DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	if(deviceInfo == INVALID_HANDLE_VALUE)
	{
		printf(" done.\n    Error(%d): INVALID_HANDLE_VALUE",deviceInfo);
		printf("\n    No devices have been installed with the FuzCor Driver.");
		ef = true;
	}
	else
		printf(" done.\n    GUID Matched. Device Info = 0x%02X", deviceInfo);

	// Enumerate the device interfaces
	printf("\n\nLocating the matched devices...");
	while( SetupDiEnumDeviceInfo(deviceInfo, numDevices, &deviceInfoData) )
	{
		if(lpDevicePath)
			LocalFree(lpDevicePath);
		if(pInterfaceDetailData)
			LocalFree(pInterfaceDetailData);

		deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

		SetupDiEnumDeviceInterfaces(
			deviceInfo,
			&deviceInfoData,
			&guidDeviceInterface,
			0,
			&deviceInterfaceData);

		if(GetLastError() == ERROR_NO_MORE_ITEMS)
		{
			printf(" done.");
			break;
		}

		SetupDiGetDeviceInterfaceDetail(
			deviceInfo,
			&deviceInterfaceData,
			NULL,
			0,
			&requiredLength,
			NULL);

		if( (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (requiredLength > 0) )
		{
			printf("\n    Device interface found.\n    Allocating buffer (size = %d)...",requiredLength);
			pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR,requiredLength);
			if(!pInterfaceDetailData)
			{
				printf("\n    Buffer allocation error.\nHalting enumeration.");
				ef = true;
				break;
			}
			printf(" done.");
		} 
		else
		{
			printf("\nEnumeration error.\nHalting enumeration.");
			ef = true;
			break;
		}

		pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
		printf("\n    Filling buffer...");
		SetupDiGetDeviceInterfaceDetail(
			deviceInfo,
			&deviceInterfaceData,
			pInterfaceDetailData,
			requiredLength,
			NULL,
			&deviceInfoData);
		printf(" done.");

		printf("\n    Copying device path...");
		size_t nLength = wcslen( pInterfaceDetailData->DevicePath ) + 1;
		lpDevicePath = (TCHAR*)LocalAlloc( LPTR, nLength * sizeof(TCHAR) );
		StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData->DevicePath);
		lpDevicePath[nLength-1] = 0;
		printf(" done.");
		
		printf("\n    Device path: ");
		for(unsigned int idx=0; idx<nLength; idx++)
			printf("%c",(char*)lpDevicePath[idx]);
		
		numDevices++;
	}
	if(!numDevices)
	{
		printf(" error.\n    *No device interfaces avaliable.");
		printf("\n    *Make sure your device is connected and installed using the FuzCor driver.");
		printf("\n\n    Note that the FuzCor driver is what ");
		printf("\n    matches your device to the installed interface\n");
		ef = true;
	}
	else
		printf("\n    Total of %d devices located.", numDevices);
	printf("\nEnumeration complete.");

	// Open the device
	if(numDevices > 0)
	{
		printf("\n\nOpening device...");
		*hDeviceHandle = CreateFile(
			lpDevicePath,
			GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL,
			OPEN_EXISTING,
			FILE_FLAG_OVERLAPPED,
			NULL);
		if(hDeviceHandle == INVALID_HANDLE_VALUE)
		{
			printf(" done.\n    Error opening device. Aborting.");
			ef = true;
		}
		else
			printf(" done.");
	}

	// Clean up unneeded variables
	printf("\n\nCleaning up...");
	if(lpDevicePath)
		LocalFree(lpDevicePath);
	if(pInterfaceDetailData)
		LocalFree(pInterfaceDetailData);

	if( !(SetupDiDestroyDeviceInfoList(deviceInfo)) )
		printf(" done.\n    Error while deleting temp variables.");
	else
		printf(" done.");

	return ef;
}
  • USB_Data.cpp

Information

oya!! i would never NEVER use a goto in a C++ program... those came from the msdn example and i never got to weeding them out... i swear! NEVER!!!

// File:	"USB Data.cpp"
// Author:	Chris Yan
// Date:	April 15, 2012
// Desc:	Program-flow for reading/writing data to my mbed

// Project headers
#include "stdafx.h"
#include "USB_Functions.h"

// Linked libraries
#pragma comment (lib , "setupapi.lib" )
#pragma comment (lib , "winusb.lib" )

// Main entry-point
int _tmain(int argc, _TCHAR* argv[])
{
	// Variable declarations
	WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE;
	HANDLE hDeviceHandle = INVALID_HANDLE_VALUE;
	GUID deviceGUID = FC_DEVICE_INTERFACE;
	PIPE pipes;
	
	bool ef = 0;

	// Search for connected devices matching our GUID and exit if not successful
	ef = SearchGUID( FC_DEVICE_INTERFACE, &hDeviceHandle );
	if(ef)
		goto cleanup;

	// Initialize the WinUSB interface using the device found above
	ef = StartWinUSB( hDeviceHandle, &hWinUSBHandle );
	if(ef)
		goto cleanup;

	// Enumerate and evaluate the avaliable endpoints
	ef = FindEndpoints( hWinUSBHandle, &pipes );
	if(ef)
		goto cleanup;

	// Try and read and write something from the interrupt pipe 
	UCHAR x = 129;
	UCHAR y = 1;
	ULONG cbSize = 0;
	int idy = 0;
	while( idy < 10 )
	{
		printf("\n");
		system("PAUSE");
		ef = ReadPipe( hWinUSBHandle, &x, 8 );
		ef = WritePipe( hWinUSBHandle, &y, &cbSize);
		if(ef)
			goto cleanup;
		idy++;
	}

cleanup:
	if(hDeviceHandle != INVALID_HANDLE_VALUE)
		CloseHandle(hDeviceHandle);
	if(hWinUSBHandle != INVALID_HANDLE_VALUE)
		WinUsb_Free(hWinUSBHandle);
	return ExitProgram(ef);
}
  • Possibly the wrong project for this, but maybe the right one? It should do something at least...

#include "mbed.h"
#include "USBHID.h"

//We declare a USBHID device. By default input and output reports are 64 bytes long.
USBHID hid(8, 8, 0x1337, 0x1234, 0x0002, false);

Serial pc(USBTX, USBRX);

//This report will contain data to be sent
HID_REPORT send_report;
HID_REPORT recv_report;

DigitalOut l1(LED1);

int main(void) {

    bool firstRun = true;
    pc.printf("\nConnect USB to begin...");
    hid.connect();
    pc.printf(" Connected.\n");
    pc.printf("\nStarting application code...");

    
    send_report.length = 8;
    char datum = 0x00;
    
    while (1) 
    {
        if(firstRun)
            pc.printf(" Code Started.\nSending Report...");
        //Fill the report
        datum = rand() & 0xff;
        for (int i = 0; i < send_report.length; i++)
            send_report.data[i] = datum;

        //Send the report
        hid.send(&send_report);
        pc.printf("\n    0x%02X sent.\n", datum);
        if(firstRun)
            pc.printf("Report sent.\n Checking for message...");

        //try to read a msg
        if(hid.readNB(&recv_report)) 
        {
            if(firstRun)
                pc.printf(" Message found.\n");
            l1 = !l1;
            for(int i = 0; i < recv_report.length; i++) 
            {
                l1 = !l1;
                pc.printf("%c", (char)recv_report.data[i]);
            }
            pc.printf("\r\n");
            l1 = 0;
        }
        else
            if(firstRun)
                pc.printf(" No message found.\n");
        if(firstRun)
        {
            pc.printf("\nProgram will now loop silently.\n");
            l1 = 1;
            firstRun = false;
        }
    }
}


2 comments on Using WinUSB with the USBHID Library:

06 Oct 2014

Hi,

I am following the example which looks useful but I am getting the following error "error C4430: missing type specifier - int assumed. Note: C++ does not support default-int". Do you know how to resolve such error?

Thanks !

25 Dec 2014

Hi,

Problem resolved ! Now my application is working well !

Please log in to post comments.