40
Writing Your First WinCE USB Device Driver ETP301 by Joe Tykowski [email protected] Embedded Systems Conference – San Francisco 2005

First Usb Driver Wince

Embed Size (px)

Citation preview

Page 1: First Usb Driver Wince

Writing Your First WinCE

USB Device Driver

ETP­301

by Joe Tykowski

[email protected]

Embedded Systems Conference – San Francisco 2005

Page 2: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 2 of 40

Introduction:................................................................................. 3 WinCE Stream Driver Overview................................................... 3 Installing the Device Driver ......................................................... 5 Loading the Client Driver............................................................. 8 How to Read and Write via the USB .......................................... 10 Recovering from Unplanned Loss of Power .............................. 12 Platforms and Platform Builder................................................. 13 Microsoft® Windows® CE .NET Test Kit.................................. 14 Summary..................................................................................... 14 Bibliography ............................................................................... 14 Appendix – Source Code ............................................................ 15 Project File Overview............................................................. 15 AesUsb.h.................................................................................. 15 AesUsb.cpp.............................................................................. 18 AesIo.cpp................................................................................. 29 AesUsb.reg .............................................................................. 40

Page 3: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 3 of 40

Introduction:

The information required to write a USB device driver is plentiful, if you have the time and patience to find it. This paper demonstrates, through the use of a practical example, a simple approach to writing a WinCE 5.0 USB device driver. The example includes the basic structure and requirements for implementing a device driver for a commercially available USB fingerprint sensor. The approach details how to write a WinCE stream driver, how to communicate via the USB bus, how to install the driver on a platform, and how to ensure proper communications between the application and the device driver.

This paper exists, not because you need to see anymore examples of how to implement a USB serial port driver, but because you need a device driver that has not yet entered the mainstream, such as a portable electron microscope connected to your PDA, or a particle accelerator controlled from your cell phone.

If you are implementing a driver for a printer, mouse, or keyboard, there are plenty of examples for adding a new device driver, and there are established classes for developing these devices. But if Microsoft® doesn’t have a predefined class to describe your device, you’ll need to create your own custom driver. Never the less, Microsoft® has made it relatively easy once you know where to start.

The tool to use for developing a device driver is Platform Builder. The object of this paper is not to teach you how to use Platform Builder, but rather the structure of the code for the driver itself. By breaking out some of the more commonly used functions and structures you can construct a custom device driver to suit your end device.

This paper will guide you through implementing the lower layer of the driver called the PDD – the Platform Dependent Driver. The upper layer, MDD – Model Device Driver, is provided as common code from Microsoft® and thus does not need to be rewritten.

WinCE Stream Driver Overview

Under Windows CE, device drivers are DLL’s loaded by the Device Manager. For a stream driver, there are only a handful of specific entry points that need to be exported. Not all of the entry points shown may be required for an implementation. You can either omit or stub out the entry points that are not required.

For additional information on any function shown in Courier boldface, refer to MSDN or the Platform Builder online help.

The following two tables serve as the skeleton of code that you need to provide in your device driver DLL. The application level functions shown provide the methods of accessing your device driver from a user program. Naturally, the user code can be developed with Microsoft® eMbedded Visual C++® or with Platform Builder. In the stream driver model up to 10 devices can be enumerated using the same prefix. The devices are named “XXX1:”, “XXX2:”, … “XXX9:”, “XXX0:”.

Page 4: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 4 of 40

Stream specific driver functions Driver Level Function Application Level Function Purpose XXX_Init( ) N/A – called during

ActivateDevice( ) Device initialization

XXX_Deinit( ) N/A – called during DeactivateDevice( )

Device de­initialization

XXX_Open( ) CreateFile(_T(“XXX1:”)) Obtains a context (handle) for communicating with the device

XXX_Close( ) CloseHandle( ) Releases the context (handle) used to communicate with the device.

XXX_Read( ) ReadFile( ) Reads data from the device

XXX_Write( ) WriteFile( ) Writes data to the device XXX_IOControl( ) DeviceIoControl( ) Commands the device or

driver to perform a specific operation

XXX_Seek( ) SetFilePointer( ) Data pointer manipulation

XXX_PowerUp( ) N/A (Performed via Power Manager)

Turns on power to a device

XXX_PowerDown( ) N/A (Performed via Power Manager)

Removes power from a device

USB specific driver functions USB Driver Level Function Purpose USBInstallDriver( ) Accessed when a USB device is plugged in and no

suitable device driver has been located in the system registry. Allows the driver to be associated to a device without pre­existing registry entries.

USBDeviceAttach( ) Called to load the client driver when the USB device is plugged into the system based on existing registry key settings.

NotifyRoutine( ) Client drivers are required to process the USB_CLOSE_DEVICE message, indicating that the USB device has been unplugged. This function is registered as a WINAPI callback routine that gets exercised when the USB device is unplugged. This function does not need to be exported.

Page 5: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 5 of 40

The actual entry point names should be “decorated” with the prefix for your device instead of “XXX_” as shown. If you wanted to use an application level program to control a device installed as “AES1:”, there would be a corresponding function in the device driver called “AES_”. The remainder of this paper shows how to implement a driver for a finger print sensor that can be accessed via the “AES” prefix.

The definitions file for exporting functions for a device driver using the “AES” prefix is shown in the following table. Note that the AES_Seek( ), AES_PowerUp( ), and AES_PowerDown( ) functions have been omitted from the reference code. The exports include both the USB specific and stream specific functions that are exposed for this device.

Contents of AesUsb.def file EXPORTS

USBInstallDriver USBDeviceAttach AES_Init AES_Deinit AES_Open AES_Close AES_Read AES_Write AES_IOControl

In short, it takes only a Baker’s dozen (or fewer) of functions to create your first WinCE USB device driver.

Installing the Device Driver

The mechanism of running a USB device driver depend on the type of access you have to the platform. If you are building a custom platform, you can incorporate the appropriate registry entries so that your device driver is loaded when the device is detected, either during or after booting. However, if you cannot preload the registry settings because your device is an after market product, the driver can still set up the registry for future use.

When a USB device is plugged in, the system tries to locate a suitable device driver based on the registry settings. If one cannot be found, the user will be prompted for the name of the driver as shown in Figure 1. The USBInstallDriver( ) function is responsible for making the required registry entries before the driver is loaded.

Page 6: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 6 of 40

Figure 1 Prompt to Load an Unknown USB Device Driver

Consider the following piece of code for installing the device driver. A new class of device, FPS_Class, is being established with this code. It will be responsible for handling a specific class of devices, namely Finger Print Sensors. The szDriverLibFile parameter is the name of the DLL that the user entered when prompted, for example “AESUSB.DLL”. It is the name of the DLL containing this device driver. The USB_DRIVER_SETTINGS are initialized to cause any USB device made by a specific company (AuthenTec) to load this driver. If a product ID is added, the driver becomes more selective about loading and can differentiate between various models of finger print sensors. The choice for handling this depends on what makes sense in your situation:

1. Provide one generic driver for an entire product line. 2. Provide multiple drivers, each specially suited to one or more products.

BOOL USBInstallDriver(LPCWSTR szDriverLibFile)

// registry keys that need to be setup: const WCHAR wsUsbDeviceID[] = TEXT("FPS_Class");

// register a unique string to identify this USB client driver: // creates HKLM\Drivers\USB\ClientDrivers\FPS_Class key... if ( !RegisterClientDriverID( wsUsbDeviceID ) )

return FALSE;

// registry key that need to be setup: WCHAR wsSubClassRegKey[] =

TEXT("Drivers\\USB\\ClientDrivers\\FPS_Class");

// registry values that need to be written: REG_VALUE_DESCR usbKeyValues[] =

(TEXT("Dll")), REG_SZ, 0, (PBYTE)(szDriverLibFile), (TEXT("Prefix")), REG_SZ, 0, (PBYTE)(TEXT("AES")), NULL, 0, 0, NULL

;

// Add information needed to load client driver... GetSetKeyValues( wsSubClassRegKey,

&usbKeyValues[0], SET, TRUE );

Page 7: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 7 of 40

// Settings that cause this driver to be loaded in the future: USB_DRIVER_SETTINGS usbDriverSettings =

sizeof(USB_DRIVER_SETTINGS), // dwCount; USB_FPS_DEVICE_VENDOR_ID, // dwVendorId; USB_NO_INFO, // dwProductId; USB_NO_INFO, // dwReleaseNumber; USB_NO_INFO, // dwDeviceClass; USB_NO_INFO, // dwDeviceSubClass; USB_NO_INFO, // dwDeviceProtocol; USB_NO_INFO, // dwInterfaceClass; USB_NO_INFO, // dwInterfaceSubClass; USB_NO_INFO // dwInterfaceProtocol;

;

// Establish the USB driver load settings for this driver. Once // registered, this device driver will load automatically without // prompting the user for the driver name. The // RegisterClientSettings function creates a registry key // HKLM\Drivers\USB\LoadClients\2303\Default\Default\FPS_Class // and adds an entry for the name of this driver DLL... if ( !RegisterClientSettings( szDriverLibFile,

wsUsbDeviceID, NULL, &usbDriverSettings ) )

return FALSE; else

return TRUE;

The GetSetKeyValues()function is a handy utility provided by the usbclient.lib for setting and retrieving registry key values. In this example, it creates a key:

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\FPS_Class] and generates two entries:

"DLL"="AESUSB.DLL" "Prefix"="AES".

These entries are used to determine the name of the device and the DLL to load when an FPS_Class device is installed on the system.

As Figure 2 demonstrates, you can choose three different ways to identify your device and associate it with your device driver. The sample code shown above makes the required registry entries to load the device driver. Simply modify the USB_DRIVER_SETTINGS to correspond with your implementation.

Page 8: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 8 of 40

Figure 2 Registering a USB Client Driver

The RegisterClientSettings( ) function creates a registry key [HKLM\Drivers\USB\LoadClients\2303\default\default\FPS_Class] and adds an entry for the name of this driver DLL. This will also cause the USB driver to load using USBDeviceAttach( ).

Loading the Client Driver

Once the USB device is plugged in, and registry settings are matched to a client driver, the USBDeviceAttach( ) function will be called. At this point, the driver needs to determine whether it can control the device and activate the appropriate driver code. In order to control the device, the driver should open any required pipes that are used for communication.

One of the first items that needs to be addressed is context. Throughout the life of this driver, the values of various handles, pipes, states, etc. must be maintained. In order to preserve this information, a structure is allocated to hold any information that the driver may require. The pointer to this structure is THE CONTEXT that will be passed in to the stream driver functions. Throughout this paper it will be represented by a pointer named pUsbFps.

The process of opening the pipes is not difficult; however, it is not obvious either. Figure 3 illustrates the steps required to find the USB endpoints by obtaining a pointer to the USB interface.

Page 9: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 9 of 40

Figure 3 Steps to Finding USB Device Endpoints

Armed with a pointer to the USB interface, the list of end points can be navigated. The finger print sensor requires a bulk input pipe for reading, and a bulk output pipe for writing. Figure 4 shows how the structures are navigated to determine which endpoints to use.

USB_ENDPOINT_DESCRIPTOR

USB_ENDPOINT_DESCRIPTOR

LPCUSB_ENDPOINT

LPCUSB_ENDPOINT

USB_INTERFACE_DESCRIPTOR

USB_INTERFACE

lpUsbInterface

Descriptor

bNumEndpoints

Descriptor

bEndpointAddress: Input Endpoint if bit 7 is set Output Endpoint if bit 7 is cleared

bmAttributes 0 = Control Endpoint 1 = Isochronous Endpoint 2 = Bulk Endpoint 3 = Interrupt­driven Endpoint

Descriptor

bEndpointAddress:

bmAttributes

End Point Array

lpEndpoints

Figure 4 Navigating the USB Structures to Examine the Endpoints

Page 10: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 10 of 40

By examining the address and attributes of each end point, it becomes a simple matter to open up pipes for input and output. Associated with each of the pipes is an event that will be used when reading or writing data to the device. Figure 5 illustrates how to open the pipes and create the events required for communication.

Figure 5 Opening Pipes using the Endpoint Descriptors

Once the pipes have been opened, the ActivateDevice( ) function is invoked. This loads the device driver DLL in the process space of the Device Manager and causes the stream driver AES_Init( ) function to execute. ActivateDevice( ) sets a DWORD registry key value, “ClientInfo”, in the [HKLM\ Drivers\Active\xx] registry key. This value is in fact the pointer to the driver context that is used for all subsequent stream functions. AES_Init( ) retrieves the value from the registry and returns it to indicate that the driver has been loaded successfully.

Finally, the notification routine must be registered for the USB device. The notification routine will run when the device is unplugged, receiving the device context and a reason code of USB_CLOSE_DEVICE. The notification routine is registered through the USB function pointer as lpUsbFuncs­>lpRegisterNotificationRoutine().

How to Read and Write via the USB

Reading and writing form the core of a stream driver. This is the primary method of getting data to and from the device. The driver entry points for implementing this are AES_Read( ) and AES_Write( ).

Page 11: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 11 of 40

The usbclient.lib implements a useful method for transferring data, namely the “IssueBulkTransfer( )” function. Using the USB function pointer, the handle to the pipe, and the associated event, a USB transfer can be established with a single function call. The following table compares the function call for reading and writing the USB device.

Comparison of USB Read/Write Code USB Read Operation USB Write Operation

uBufferPhysicalAddress = 0; dwFlags = USB_IN_TRANSFER |

USB_SHORT_TRANSFER_OK;

dwErr = IssueBulkTransfer( pUsbFps­>lpUsbFuncs, pUsbFps­>BulkIn.hPipe, DefaultTransferComplete, pUsbFps­>BulkIn.hEvent, dwFlags, pBuffer, uBufferPhysicalAddress, dwBufferLength, &dwBytesTransferred, dwTimeout, &dwUsbErr );

uBufferPhysicalAddress = 0; dwFlags = USB_OUT_TRANSFER;

dwErr = IssueBulkTransfer( pUsbFps­>lpUsbFuncs, pUsbFps­>BulkOut.hPipe, DefaultTransferComplete, pUsbFps­>BulkOut.hEvent, dwFlags, pBuffer, uBufferPhysicalAddress, dwBufferLength, &dwBytesTransferred, dwTimeout, &dwUsbErr );

Where: pUsbFps­>lpUsbFuncs Is a pointer to the USB functions pUsbFps­>BulkIn/Out.hPipe Is a handle to the Bulk In or Bulk Out pipe DefaultTransferComplete Is the address of a callback function pUsbFps­>BulkIn/Out.hEvent Is an event associated with the pipe dwFlags Contains the direction of transfer pBuffer Is a pointer to the input / output buffer in virtual

memory uBufferPhysicalAddress Is the physical address, which may be NULL, of

the data buffer dwBufferLength Is the length of the input / output buffer &dwBytesTransferred Is the number of bytes transferred via the USB dwTimeout Is the timeout setting in milliseconds &dwUsbErr Is the error associated with the USB transfer

The main differences between the read / write code are the pipes used, the events, and the flags passed down to the IssueBulkTransfer( ) function. Note that the library version provides a wrapper around the USB function pointer routine by the same name. The library version from the usbclient.lib conveniently handles timeouts by providing an

Page 12: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 12 of 40

event and a default notification routine. The DefaultTransferComplete( ) function provided by the library sets the event that is passed in when the transfer is complete. If the event times out before the transfer is complete, the returned status can be examined and the operation can be retried.

The dwFlags variable indicates the direction of the transfer. In the case of a read transfer, the USB_SHORT_TRANSFER_OK allows the transfer to be less than the full length of the buffer. This allows the function to return when a transfer is complete rather than waiting for a timeout condition. Note, however, that transfers that occur in integer multiples of 32­bytes will not be detected as short packets. If you expect transfers that are integer multiples of 32­bytes, you might want to consider using the exact size of the transfer that you expect instead of a generic buffer.

The stream driver functions for reading and writing do not contain parameters to determine appropriate timeout values. If you want to implement timeouts, then you will have to implement control code handlers such as IOCTL_SERIAL_SET_TIMEOUTS, and IOCTL_SERIAL_GET_TIMEOUTS in your XXX_IOControl( ) entry point. These control codes can be sent to the device driver through the DeviceIoControl() call at the application level, or they can be set up more conveniently through the GetCommTimeouts( ) and SetCommTimeouts( ) APIs.

Recovering from Unplanned Loss of Power

Like my wife is always yelling at my son “Clean up after yourself before someone trips”, so is it true that the device driver must take care of itself. The device driver must be able to (self­) recover from a device disconnect. There are two scenarios that must be contemplated when the USB cord is yanked from the system. And you know it’s going to happen.

In either case, the notification routine, registered in the USBDeviceAttach( ) with a call to lpUsbFuncs­>lpRegisterNotificationRoutine( ) will receive a USB_CLOSE_DEVICE code. This is where you need to write code to clean up after the driver.

In the first scenario, the device has not currently been opened by a program. Clean up is the reverse of the install process. For every memory allocation there is a free, for every open handle there is a close. The following table indicates the functions that need to be used to uninstall your device driver cleanly.

Installation Function Corresponding De­Installation Function ActivateDevice( ) DeactivateDevice( )

lpUsbFuncs­> lpRegisterNotificationRoutine()

lpUsbFuncs­> lpUnRegisterNotificationRoutine( )

Page 13: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 13 of 40

see RegisterNotificationRoutine()

see UnRegisterNotificationRoutine()

lpUsbFuncs­>lpOpenPipe( )

see OpenPipe( )

lpUsbFuncs­>lpClosePipe( )

see ClosePipe( )

CreateEvent( ) CloseHandle( )

InitializeCriticalSection( ) DeleteCriticalSection( )

LocalAlloc( ) LocalFree( )

In the second scenario, the more insidious one, an application is happily exercising your fusion reactor when the janitor trips over your USB cord that was carefully left dangling between your PDA and a cylinder of enriched plutonium. In this scenario, you have worse problems than shutting down your driver, but why add fuel to the fire?

You should set some flags in your context to indicate that the device driver is closed, and that the driver is waiting to unload. Any further access attempts to the device driver should result in an error, giving the application a clue that the device is no longer present so that it can close the driver.

Once the flags are set, you can wait for an event to indicate that the device driver has indeed been closed by the application. Then your code can proceed with the orderly shut down of the device driver (while you high tail it out of Dodge). It’s important to prevent exceptions caused by your driver accessing a pointer to the device context that has been freed from memory. (Wouldn’t want the Blue Screen ‘O Death to be the last thing you saw in this scenario, now would you?)

Platforms and Platform Builder

There are a few things you will want to consider when building a USB driver for your end platform.

There are two standard host­controller designs, open host controller interface (OHCI), and universal host controller interface (UHCI). You will need to determine which type of interface you have on your platform and include the appropriate one in your system build in order to use the USB. Just adding USB support doesn’t necessarily provide this component.

There are two libraries that you will want to link in with your device driver, the USB Driver Library, usbd.lib, and the USB Client Library, usbclient.lib. As shown, the USB

Page 14: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 14 of 40

Client library provides some useful utility functions to make your development work easier.

Microsoft® Windows® CE .NET Test Kit

Platform Builder provides a test framework, the TUX DLL. You can create a new TUX DLL project in Platform Builder and customize it to exercise your device driver. If you are using a standard class of device, you can use the pre­existing tests to validate your driver. If your driver is not for a standard class of device, however, customization of the TUX DLL involves modifying two of the files generated to create custom tests to exercise your driver.

You can use the application level functions, such as CreateFile(), ReadFile(), and WriteFile() to create your tests. You will need to modify the test.cpp file to add your test code. Modify the ft.h file to build a function table list of your unique test routines. If you are creating a test TUX from scratch, look at similar type devices to determine which kinds of tests that you should code.

Once you have developed a TUX DLL for your device driver, you can use the Microsoft® Windows® CE .NET Test Kit (CETK) to test it on your Windows CE– based platform.

Summary

This paper provides the basic information needed to implement a working WinCE 5.0 USB device driver. It shows how a non­standard class of device can be built and used on a WinCE target platform. It lists the core set of functions that a USB device driver writer needs in order to begin development. Functions have been highlighted to reduce the amount of searching that is often required when developing a new piece of code.

A complete source listing for the AesUsb.dll device driver is included in the Appendix. It provides a working USB device driver skeleton that can be used in your development of a non­standard device driver.

Bibliography

"Macallan Help” " Microsoft, 27 Oct. 2003. Wcelib40.chm.

Page 15: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 15 of 40

Appendix – Source Code

Project File Overview

The AesUsb.dll is created using the following files:

Source Files: AesUsb.cpp AesIo.cpp

Header Files: AesUsb.h

Library Files: usbd.lib

usbclient.lib Export Definitions:

AesUsb.def

AesUsb.h /* /=======================================================================\

* | THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF | * | ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO | * | THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND/OR FITNESS FOR A | * | PARTICULAR PURPOSE. | * \=======================================================================/ */

/****************************************************************************

File: AesUsb.h

Description: This file provides structures, definitions, and prototypes for a USB Client Driver for the Finger Print Sensor Device Class.

Author: Joe Tykowski

*****************************************************************************/ #ifndef _AESUSB_ #define _AESUSB_

/** include files **/ #include <windows.h> #include <devload.h> #include <usbdi.h> #include <pegdser.h> #include <pegdpar.h>

#include <ntcompat.h> #include <Pkfuncs.h> #include <Msgqueue.h>

#include "usbclient.h" #if defined(_WIN32_WCE) && (_WIN32_WCE < 500) #include "warning.h" #include "utils.h" #endif

Page 16: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 16 of 40

/** local definitions **/

// USB Sensor Interface Descriptor #define USB_FPS_DEVICE_VENDOR_ID 0x8FF // AuthenTec

#define DEVICE_PREFIX TEXT("AES") #define CLASS_NAME_SZ TEXT("FPS_Class") #define CLIENT_REGKEY_SZ TEXT("Drivers\\USB\\ClientDrivers\\FPS_Class")

#define USB_FPS_SIG 0x46505323 // "FPS#"

#define MANUAL_RESET_EVENT TRUE #define AUTO_RESET_EVENT FALSE

// structure used to indicate driver status: typedef struct _FLAGS

UCHAR Open : 1; // bits 0 UCHAR UnloadPending : 1; // bits 1 UCHAR Reserved : 6; // bits 2­7

FLAGS, *PFLAGS;

// Pipe information: typedef struct _PIPE

USB_PIPE hPipe; // USB Pipe handle HANDLE hEvent; // Completion Event UCHAR bIndex; // Endpoint's Address

PIPE, *PPIPE;

// Everything that you ever wanted to know about a USB interface // to a finger print sensor is contained in this structure: typedef struct _USBFPS_CONTEXT

ULONG ulSignature; // sanity check

FLAGS Flags; // open/unloading status

COMMTIMEOUTS Timeouts; // Comm Timeouts

CRITICAL_SECTION Lock; // sync object for this instance

HANDLE hStreamDevice; // Handle for Stream interface. HANDLE hUsbDevice; // USB handle to the device HANDLE hCloseEvent; // Signals the device has been closed.

LPCUSB_FUNCS lpUsbFuncs; // USBD Function table

PIPE BulkOut; // Bulk OUT Pipe PIPE BulkIn; // Bulk IN Pipe

USBFPS_CONTEXT, *PUSBFPS_CONTEXT;

// Is the pointer a valid context? // It's valid if we can read our signature #define VALID_CONTEXT( pContext ) \

(pContext && (USB_FPS_SIG == pContext­>ulSignature))

// Can the device accept any I/O requests? // It can accept I/O IF: // it's a valid context AND // the driver is open AND // the device is still plugged in #define ACCEPT_IO( pContext ) \

( VALID_CONTEXT( pContext ) && \ pContext­>Flags.Open && \

!pContext­>Flags.UnloadPending )

Page 17: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 17 of 40

/** default settings **/

#if DEBUG

#define ZONE_ERR DEBUGZONE(0) #define ZONE_WARN DEBUGZONE(1) #define ZONE_INIT DEBUGZONE(2) #define ZONE_TRACE DEBUGZONE(3)

#define ZONE_AES_INIT DEBUGZONE(4) #define ZONE_AES_READ DEBUGZONE(5) #define ZONE_AES_WRITE DEBUGZONE(6) #define ZONE_AES_IOCTL DEBUGZONE(7)

#define ZONE_USB_INIT DEBUGZONE(8)

#endif // DEBUG

/** public functions **/

#endif

Page 18: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 18 of 40

AesUsb.cpp /* /=======================================================================\

* | THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF | * | ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO | * | THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND/OR FITNESS FOR A | * | PARTICULAR PURPOSE. | * \=======================================================================/ */

/****************************************************************************

File: AesUsb.cpp

Description: This file implements the code for a USB Client Driver for a Finger Print Sensor Device Class.

Author: Joe Tykowski

*****************************************************************************/

/** include files **/ #include "AesUsb.h"

/** local definitions **/

#ifdef DEBUG DBGPARAM dpCurSettings =

TEXT("AESUSB"),

TEXT("Errors"), TEXT("Warnings"), TEXT("Init"), TEXT("Trace"), TEXT("AES_INIT"), TEXT("AES_READ"), TEXT("AES_WRITE"), TEXT("AES_IOCTL"), TEXT("USB_INIT"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined")

, 0x0003 // ZONE_WRN|ZONE_ERR

; #endif // DEBUG

/** default settings **/

/** external functions **/

/** external data **/

/** internal functions **/

/** public data **/

/** private data **/

/** private functions **/

/*++ ===============================================================================

Function RemoveDeviceContext

This function deallocates memory, closes handles, and deletes critical sections associated with a specific device context of a Finger Print Sensor.

Parameters

Page 19: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 19 of 40

pUsbFps [in] Pointer to the device context information

Return Value void =============================================================================== ­­*/

VOID RemoveDeviceContext(PUSBFPS_CONTEXT pUsbFps)

DEBUGMSG(ZONE_INIT,(TEXT(">RemoveDeviceContext(%p)\n"), pUsbFps));

if ( VALID_CONTEXT( pUsbFps ) )

if ( pUsbFps­>Flags.Open )

DEBUGMSG(ZONE_ERR,(TEXT("RemoveDeviceContext on open device!\n"))); return;

// close each of the pipes that are open... if (pUsbFps­>BulkIn.hPipe && pUsbFps­>lpUsbFuncs)

pUsbFps­>lpUsbFuncs­>lpClosePipe(pUsbFps­>BulkIn.hPipe);

// close each of the events associated to the pipes... if (pUsbFps­>BulkIn.hEvent)

CloseHandle(pUsbFps­>BulkIn.hEvent);

// close each of the pipes that are open... if (pUsbFps­>BulkOut.hPipe && pUsbFps­>lpUsbFuncs)

pUsbFps­>lpUsbFuncs­>lpClosePipe(pUsbFps­>BulkOut.hPipe);

// close each of the events associated to the pipes... if (pUsbFps­>BulkOut.hEvent)

CloseHandle(pUsbFps­>BulkOut.hEvent);

// close the event for closing down the driver... if (pUsbFps­>hCloseEvent)

CloseHandle(pUsbFps­>hCloseEvent);

// free up memory... if (&pUsbFps­>Lock)

DeleteCriticalSection( &pUsbFps­>Lock );

// free the memory used by this driver... LocalFree(pUsbFps);

else

DEBUGMSG(ZONE_ERR,(TEXT("Invalid Parameter\n")));

DEBUGMSG(ZONE_INIT,(TEXT("<RemoveDeviceContext\n")));

/*++ ===============================================================================

Page 20: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 20 of 40

Function SetUsbInterface

This function associates pipe handles with the current device. There should be two "EndPoints" for this device, the Bulk input, and the Bulk output. This routine scans the known endpoints of this device and opens pipes to talk to each one.

Parameters pUsbFps [in] Pointer to the device context information

Return Value TRUE if both of the pipes were opened, FALSE otherwise. =============================================================================== ­­*/

BOOL SetUsbInterface(PUSBFPS_CONTEXT pUsbFps)

LPCUSB_INTERFACE lpUsbInterface; LPCUSB_ENDPOINT pEndpoint; LPCUSB_DEVICE pDevice; DWORD dwIndex; BOOL bRc;

DEBUGMSG( ZONE_USB_INIT, (TEXT(">SetUsbInterface\n")));

if ( !VALID_CONTEXT( pUsbFps ) )

DEBUGMSG( ZONE_ERR, (TEXT("Invalid parameter\n"))); return FALSE;

// gets a pointer to a device information structure, LPCUSB_DEVICE... pDevice = pUsbFps­>lpUsbFuncs­>lpGetDeviceInfo( pUsbFps­>hUsbDevice );

if ( !pDevice )

DEBUGMSG( ZONE_ERR, (TEXT("No Device Information available!\n"))); return FALSE;

// search for a specific interface on a USB device. Get a // LPCUSB_INTERFACE back if the interface was found... lpUsbInterface = pUsbFps­>lpUsbFuncs­>lpFindInterface(pDevice, 0, 0);

// did we get the pointer to the interface? if ( !lpUsbInterface )

// no interface found, can't associate pipes with one, so... DEBUGMSG( ZONE_ERR, (TEXT("Invalid parameter lpUsbInterface\n"))); return FALSE;

// parse through the endpoints and try to find input and output pipes... for ( dwIndex = 0;

dwIndex < lpUsbInterface­>Descriptor.bNumEndpoints; dwIndex++)

// point to the next endpoint for this device in its list... pEndpoint = lpUsbInterface­>lpEndpoints + dwIndex;

// Finger Print Sensor Class supports 1 Bulk OUT and 1 Bulk IN, // detect each one by their direction bit...

// is this the output endpoint? if ( USB_ENDPOINT_DIRECTION_OUT(pEndpoint­>Descriptor.bEndpointAddress) )

// do we already have a handle to the output pipe? // is this is a bulk output connection point? if ((NULL == pUsbFps­>BulkOut.hPipe) &&

(pEndpoint­>Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK)

Page 21: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 21 of 40

// open the Bulk OUT pipe for the USB device... pUsbFps­>BulkOut.hPipe =

pUsbFps­>lpUsbFuncs­>lpOpenPipe(pUsbFps­>hUsbDevice, &pEndpoint­>Descriptor);

// were we successful in obtaining the handle to the pipe? if ( !pUsbFps­>BulkOut.hPipe )

DEBUGMSG(ZONE_ERR, (TEXT("OpenPipe error:%d\n"), GetLastError()));

break;

// setup an endpoint specific event... pUsbFps­>BulkOut.hEvent = CreateEvent(NULL,

MANUAL_RESET_EVENT, FALSE, NULL);

if ( !pUsbFps­>BulkOut.hEvent )

DEBUGMSG(ZONE_ERR, (TEXT("CreateEvent error:%d\n"), GetLastError()));

return FALSE;

// save the endpoint address for future reference... pUsbFps­>BulkOut.bIndex = pEndpoint­>Descriptor.bEndpointAddress;

// is this the input endpoint? else if (USB_ENDPOINT_DIRECTION_IN( pEndpoint­>Descriptor.bEndpointAddress ) )

// do we already have a handle to the input pipe? // is this is a bulk input connection point? if ((NULL == pUsbFps­>BulkIn.hPipe) &&

(pEndpoint­>Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK)

// open the Bulk IN pipe for the USB device... pUsbFps­>BulkIn.hPipe =

pUsbFps­>lpUsbFuncs­>lpOpenPipe( pUsbFps­>hUsbDevice, &pEndpoint­>Descriptor );

if ( !pUsbFps­>BulkIn.hPipe )

DEBUGMSG(ZONE_ERR, (TEXT("OpenPipe error: %d\n"), GetLastError()));

break;

// setup an endpoint specific event... pUsbFps­>BulkIn.hEvent = CreateEvent(NULL,

MANUAL_RESET_EVENT, FALSE, NULL);

if ( !pUsbFps­>BulkIn.hEvent )

DEBUGMSG(ZONE_ERR, (TEXT("CreateEvent error:%d\n"), GetLastError()));

return FALSE;

// save the endpoint address for future reference... pUsbFps­>BulkIn.bIndex = pEndpoint­>Descriptor.bEndpointAddress;

else

Page 22: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 22 of 40

DEBUGMSG(ZONE_WARN, (TEXT("Unsupported Endpoint:0x%x\n"), pEndpoint­>Descriptor.bEndpointAddress));

// did we find our endpoints? Did we get handles to our pipes? // if we failed to find all of our endpoints then cleanup will occur later bRc = (pUsbFps­>BulkOut.hPipe && pUsbFps­>BulkIn.hPipe) ? TRUE : FALSE;

DEBUGMSG( ZONE_USB_INIT, (TEXT("<SetUsbInterface:%d\n"), bRc));

return (bRc);

/*++ ===============================================================================

Function DeviceNotify

This function is registered with the system using the RegisterNotificationRoutine(). It provides the system with a way of notifying the device driver when the USB Finger Print Sensor is being removed from the system so that it can clean up / uninstall the driver.

Parameters lpvNotifyParameter [in] This is the value that was passed to the

RegisterNotificationRoutine() as the lpvNotifyParameter when this device was installed.

dwCode [in] USB_CLOSE_DEVICE is currently the only supported notification in WinCE.

*lpdwInfoX [in] Not used.

Return Value Returns TRUE if the USB_CLOSE_DEVICE notification was processed, FALSE otherwise. =============================================================================== ­­*/

BOOL WINAPI DeviceNotify(LPVOID lpvNotifyParameter, DWORD dwCode, LPDWORD *lpdwInfo1, LPDWORD *lpdwInfo2, LPDWORD *lpdwInfo3, LPDWORD *lpdwInfo4)

PUSBFPS_CONTEXT pUsbFps = (PUSBFPS_CONTEXT) lpvNotifyParameter; DWORD dwWaitReturn; BOOL bRc;

UNREFERENCED_PARAMETER(lpdwInfo1); UNREFERENCED_PARAMETER(lpdwInfo2); UNREFERENCED_PARAMETER(lpdwInfo3); UNREFERENCED_PARAMETER(lpdwInfo4);

DEBUGMSG( ZONE_USB_INIT, (TEXT(">DeviceNotify\n")));

if ( !VALID_CONTEXT( pUsbFps ) )

DEBUGMSG( ZONE_ERR, (TEXT("Invalid Context!\n"))); return FALSE;

switch( dwCode )

case USB_CLOSE_DEVICE: DEBUGMSG( ZONE_USB_INIT, (TEXT("USB_CLOSE_DEVICE\n"))); if ( pUsbFps­>Flags.Open )

Page 23: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 23 of 40

// When an app has an open handle to this device and then // the user unplugs the USB cable the driver will unload // here. However, we don't want to unload the driver until // the device has been closed and it's safe to clean up. // So... set the driver state to reject all I/O and wait // for the driver to signal that it has closed... EnterCriticalSection( &pUsbFps­>Lock ); pUsbFps­>Flags.Open = FALSE; pUsbFps­>Flags.UnloadPending = TRUE; LeaveCriticalSection( &pUsbFps­>Lock );

DEBUGMSG(ZONE_USB_INIT, (TEXT("Waiting for CloseEvent...\n")));

dwWaitReturn = WaitForSingleObject(pUsbFps­>hCloseEvent, INFINITE);

switch( dwWaitReturn )

case WAIT_OBJECT_0: DEBUGMSG(ZONE_USB_INIT,

(TEXT("...CloseEvent signalled\n"))); break;

case WAIT_FAILED: DEBUGMSG(ZONE_ERR,

(TEXT("CloseEvent error:%d\n"), GetLastError()));

break;

default: DEBUGMSG(ZONE_ERR,

(TEXT("Unhandled WaitReason:%d\n"), dwWaitReturn ));

break;

// give it a chance to finish up... Sleep(1000);

DEBUGMSG( ZONE_USB_INIT, (TEXT("DeactivateDevice\n")));

// unloads the driver for the Finger Print device, and delete // the active key of the device from the registry bRc = DeactivateDevice( pUsbFps­>hStreamDevice );

if ( !bRc )

DEBUGMSG(ZONE_ERR, (TEXT("DeactivateDevice error: %d\n"), GetLastError()));

// release the memory used by this device driver... RemoveDeviceContext( pUsbFps );

return TRUE;

default: DEBUGMSG( ZONE_ERR, (TEXT("Unhandled code:%d\n"), dwCode)); break;

DEBUGMSG( ZONE_USB_INIT, (TEXT("<DeviceNotify\n")));

return FALSE;

/** public functions **/

Page 24: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 24 of 40

/*++ ===============================================================================

Function USBInstallDriver

This function installs the USB client driver for the AES Finger Print Sensor.

Parameters szDriverLibFile [in] Driver DLL name. ("AesUsb.dll")

Return Value

TRUE indicates success. FALSE indicates failure.

Remarks The USB driver module calls this function when an unrecognized device is attached to the USB and the user enters the client driver DLL name. It registers a unique client identifier string with the USB driver module and sets up any client driver settings.

=============================================================================== ­­*/

BOOL USBInstallDriver(LPCWSTR szDriverLibFile)

// registry keys that need to be setup: const WCHAR wsUsbDeviceID[] = CLASS_NAME_SZ;

// register a unique string to identify this USB client driver: // creates HKLM\Drivers\USB\ClientDrivers\FPS_Class key... if ( !RegisterClientDriverID( wsUsbDeviceID ) )

return FALSE;

// registry keys that need to be setup: WCHAR wsSubClassRegKey[] = CLIENT_REGKEY_SZ;

// registry values that need to be written: REG_VALUE_DESCR usbKeyValues[] =

(TEXT("Dll")), REG_SZ, 0, (PBYTE)(szDriverLibFile), (TEXT("Prefix")), REG_SZ, 0, (PBYTE)(DEVICE_PREFIX), NULL, 0, 0, NULL

;

// Add information needed to load client driver... GetSetKeyValues( wsSubClassRegKey,

&usbKeyValues[0], SET, TRUE );

// Settings that cause this driver to be loaded in the future: USB_DRIVER_SETTINGS usbDriverSettings =

sizeof(USB_DRIVER_SETTINGS), // dwCount; USB_FPS_DEVICE_VENDOR_ID, // dwVendorId; USB_NO_INFO, // dwProductId; USB_NO_INFO, // dwReleaseNumber; USB_NO_INFO, // dwDeviceClass; USB_NO_INFO, // dwDeviceSubClass; USB_NO_INFO, // dwDeviceProtocol; USB_NO_INFO, // dwInterfaceClass; USB_NO_INFO, // dwInterfaceSubClass; USB_NO_INFO // dwInterfaceProtocol;

;

// Establish the USB driver load settings for this driver. Once registered, // this device driver will load automatically without prompting the user // for the driver name. The RegisterClientSettings function creates reg

Page 25: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 25 of 40

// key hklm\drivers\usb\loadclients\2303\default\default\FPS_CLASS // and adds an entry for the name of this driver DLL... if ( !RegisterClientSettings( szDriverLibFile,

wsUsbDeviceID, NULL, &usbDriverSettings ) )

return FALSE; else

return TRUE;

/*++ ===============================================================================

Function USBDeviceAttach

This function is a universal serial bus (USB) device attach routine for the AES Finger Print Sensor.

Parameters hDevice [in] Handle to a USB device.

lpUsbFuncs [in] Pointer to a USB device interface function table.

lpUsbInterface [in] Pointer to the USB_INTERFACE structure that contains interface information if a client is loaded as an interface driver. If a client is not loaded for a specific interface, this parameter is NULL, and the client can get interface information through the FindInterface function.

szUniqueDriverId [in] Client driver identifier string.

fAcceptControl [out] Indicator if the driver accepts control of the device or if the driver should continue to try to load client drivers. Set to TRUE if the driver accepts control of the device, or FALSE if USB driver should continue to try to load client drivers.

lpUsbDriverSettings [in] Pointer to the USB_DRIVER_SETTINGS structure that indicates how the driver is loaded.

dwUnused [in] Reserved for use with future versions of USB driver.

Return Value

Errors

=============================================================================== ­­*/

BOOL USBDeviceAttach(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs, LPCUSB_INTERFACE lpUsbInterface, LPCWSTR szUniqueDriverId, LPBOOL fAcceptControl, LPCUSB_DRIVER_SETTINGS lpUsbDriverSettings, DWORD dwUnused)

BOOL bRc = TRUE; PUSBFPS_CONTEXT pUsbFps = NULL; WCHAR wsSubClassRegKey[] = CLIENT_REGKEY_SZ;

UNREFERENCED_PARAMETER(lpUsbInterface); UNREFERENCED_PARAMETER(szUniqueDriverId); UNREFERENCED_PARAMETER(lpUsbDriverSettings);

Page 26: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 26 of 40

UNREFERENCED_PARAMETER(dwUnused);

DEBUGMSG(ZONE_USB_INIT, (TEXT(">USBDeviceAttach(0x%x, %s)\n"), hDevice, szUniqueDriverId));

// Initially, say that we don't control this USB peripheral... *fAcceptControl = FALSE;

do

// we think we found a device & interface we control, // so create our device context pUsbFps = (PUSBFPS_CONTEXT) LocalAlloc(LPTR, sizeof(USBFPS_CONTEXT)); if ( !pUsbFps )

// not enough memory, don't bother to install the driver... DEBUGMSG(ZONE_ERR,

(TEXT("LocalAlloc error:%d\n"), GetLastError())); bRc = FALSE; break;

// add our own unique signature to the context so that we can validate // the context when it is passed to us in other functions... pUsbFps­>ulSignature = USB_FPS_SIG;

InitializeCriticalSection( &pUsbFps­>Lock );

// keep track of the parameters passed into this function... pUsbFps­>hUsbDevice = hDevice; pUsbFps­>lpUsbFuncs = lpUsbFuncs;

// initially, this driver is not opened: pUsbFps­>Flags.Open = FALSE; pUsbFps­>Flags.UnloadPending = FALSE;

// create an event that may be used to gracefully exit the driver // if the cord is yanked while we're talking to the device... pUsbFps­>hCloseEvent = CreateEvent(NULL,

AUTO_RESET_EVENT, FALSE, NULL);

if ( !pUsbFps­>hCloseEvent )

DEBUGMSG(ZONE_ERR, (TEXT("CreateEvent error:%d\n"), GetLastError()));

bRc = FALSE; break;

// set up the USB interface pipes... bRc = SetUsbInterface( pUsbFps ); if ( !bRc )

DEBUGMSG( ZONE_ERR, (TEXT("SetUsbInterface failed!\n"))); break;

// start the stream driver interface: // // ActivateDevice() loads a device driver and reads the registry key // specified in wsSubClassRegKey to get the DLL name, device prefix, // etc. Next, it adds the device to the active device list in the // HKEY_LOCAL_MACHINE\Drivers\Active registry key, and stores the // relevant values there. If you do not specify a device index in // the registry subkey specified in wsSubClassRegKey, then // ActivateDevice assigns a free index. Then ActivateDevice() // loads the device driver DLL in the process space of the Device // Manager. Then the Device Manager sends a device notification // message for the new device, i.e. this causes AES_Init() to run...

Page 27: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 27 of 40

pUsbFps­>hStreamDevice = ActivateDevice( wsSubClassRegKey, (DWORD) pUsbFps );

// the return value can be used to deactivate the device when needed... if ( pUsbFps­>hStreamDevice )

// registers a callback for device notifications, // i.e. USB_CLOSE_DEVICE... bRc = lpUsbFuncs­>lpRegisterNotificationRoutine(hDevice,

DeviceNotify, pUsbFps );

if ( !bRc )

DEBUGMSG(ZONE_ERR, (TEXT("RegisterNotificationRoutine error:%d\n"), GetLastError()));

break;

else

// the streams interface failed to init, no use starting... DEBUGMSG(ZONE_ERR,

(TEXT("ActivateDevice error:%d\n"), GetLastError()));

bRc = FALSE; break;

while(0);

// were errors detected? if ( !bRc )

// If not our device, or error, then clean up RemoveDeviceContext( pUsbFps );

else

// we are ready to control this device! *fAcceptControl = TRUE;

DEBUGMSG(ZONE_USB_INIT, (TEXT("<USBDeviceAttach:%d\n"), *fAcceptControl));

return bRc;

/*++ ===============================================================================

Function DllEntry

Receives the process and thread attach messages.

Parameters

Return Value

Errors

=============================================================================== ­­*/

BOOL DllEntry(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved)

UNREFERENCED_PARAMETER(hDllHandle); UNREFERENCED_PARAMETER(lpreserved);

Page 28: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 28 of 40

switch (dwReason)

case DLL_PROCESS_ATTACH: DEBUGREGISTER((HINSTANCE) hDllHandle); DisableThreadLibraryCalls((HMODULE) hDllHandle); break;

case DLL_PROCESS_DETACH: break;

default: break;

return TRUE;

Page 29: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 29 of 40

AesIo.cpp /* /=======================================================================\

* | THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF | * | ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO | * | THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND/OR FITNESS FOR A | * | PARTICULAR PURPOSE. | * \=======================================================================/ */

/****************************************************************************

File: AesIo.cpp

Description: This file provides the stream interface functions for the USB Finger Print Sensor Client Driver.

Author: Joe Tykowski

*****************************************************************************/

/** include files **/ #include "AesUsb.h"

/** local definitions **/

/** default settings **/

/** external functions **/

/** external data **/

/** internal functions **/

/** public data **/

/** private data **/

/** private functions **/

/*++ ===============================================================================

Function ReadUsb

This function receives characters from the USB device.

Parameters pUsbFps [in] Pointer to a handle returned by the AES_Open function.

pBuffer [in] Pointer to valid memory.

dwBufferLength [in] Specifies the size, in bytes, of pTargetBuffer.

dwTimeout [in] Amount of time in milliseconds to wait for data from a USB device. INFINITE waits "forever."

Return Values The number of bytes read indicates success. A value of ­1 indicates failure.

Remarks AES_Read sets the buffer and buffer length requested by the caller. It writes the number of bytes transacted and returns the status of the call.

This function is exported to users through the ReadFile function.

This function must obey time­out values set for the serial port. ===============================================================================

Page 30: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 30 of 40

­­*/

DWORD ReadUsb(PUSBFPS_CONTEXT pUsbFps, PUCHAR pBuffer, DWORD dwBufferLength, DWORD dwTimeout)

DWORD dwBytesTransferred = (DWORD) ­1; DWORD dwErr = ERROR_SUCCESS; DWORD dwUsbErr = USB_NO_ERROR; DWORD dwFlags = USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK;

// is the device currently available? if ( ACCEPT_IO( pUsbFps ) )

// is there a buffer for transfer? if ( pBuffer && dwBufferLength )

// try to read from the device... dwErr = IssueBulkTransfer(pUsbFps­>lpUsbFuncs,

pUsbFps­>BulkIn.hPipe, DefaultTransferComplete, pUsbFps­>BulkIn.hEvent, dwFlags, pBuffer, 0, dwBufferLength, &dwBytesTransferred, dwTimeout, &dwUsbErr );

// was an error detected? if ((ERROR_SUCCESS != dwErr) || (USB_NO_ERROR != dwUsbErr))

// the device was probably unplugged... if (ERROR_GEN_FAILURE == dwErr)

// return failure... return((DWORD) ­1);

else

DEBUGMSG( ZONE_ERR,(TEXT("ReadUsb: ERROR_INVALID_PARAMETER\n"))); SetLastError(ERROR_INVALID_PARAMETER );

else

DEBUGMSG( ZONE_ERR,(TEXT("ReadUsb: ERROR_INVALID_HANDLE\n"))); SetLastError(ERROR_INVALID_HANDLE);

return dwBytesTransferred;

/*++ ===============================================================================

Function WriteUsb

This function enables the internal state machine to transmit bytes via the USB bulk out pipe.

Parameters pUsbFps [in] Handle returned by the AES_Open function.

pBuffer [in] Pointer to the bytes to be written.

Page 31: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 31 of 40

dwBufferLength [in] Specifies the number of bytes to be written.

Return Values The number of bytes written indicates success. A value of ­1 indicates failure.

=============================================================================== ­­*/

DWORD WriteUsb(PUSBFPS_CONTEXT pUsbFps, PUCHAR pBuffer, DWORD dwBufferLength)

DWORD dwTimeout; DWORD dwBytesTransferred ; DWORD dwTotalTransferred = 0 ; DWORD dwErr = ERROR_SUCCESS; DWORD dwUsbErr = USB_NO_ERROR;

DEBUGMSG(ZONE_AES_WRITE, (TEXT(">WriteUsb(0x%x, %d)\n"),pBuffer, dwBufferLength));

// is the device currently available? if ( ACCEPT_IO( pUsbFps ) )

while (pBuffer && dwBufferLength)

dwTimeout = dwBufferLength * pUsbFps­>Timeouts.WriteTotalTimeoutMultiplier + pUsbFps­>Timeouts.WriteTotalTimeoutConstant;

if (!dwTimeout)

dwTimeout = INFINITE;

DEBUGMSG(ZONE_AES_WRITE, (TEXT("WriteUsb timeout due in %d msec\n"), dwTimeout));

EnterCriticalSection( &pUsbFps­>Lock );

dwBytesTransferred = 0 ; dwErr = IssueBulkTransfer(pUsbFps­>lpUsbFuncs,

pUsbFps­>BulkOut.hPipe, DefaultTransferComplete, pUsbFps­>BulkOut.hEvent, (USB_OUT_TRANSFER), pBuffer, 0, dwBufferLength, &dwBytesTransferred, dwTimeout, &dwUsbErr );

if ((ERROR_SUCCESS != dwErr) || (USB_NO_ERROR != dwUsbErr))

DEBUGMSG( ZONE_ERR, (TEXT("IssueBulkTransfer (Write) error:%d, 0x%x\n"), dwErr, dwUsbErr));

LeaveCriticalSection( &pUsbFps­>Lock ); break;

else

LeaveCriticalSection( &pUsbFps­>Lock ); pBuffer += dwBufferLength; // protect us from unusually long transfers... dwBufferLength = 0; dwTotalTransferred += dwBufferLength;

else

DEBUGMSG( ZONE_ERR,(TEXT("WriteUsb: ERROR_INVALID_HANDLE\n")));

Page 32: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 32 of 40

dwTotalTransferred = (DWORD)­1; SetLastError(ERROR_INVALID_HANDLE);

DEBUGMSG(ZONE_AES_WRITE,(TEXT("<WriteUsb:%d\n"), dwTotalTransferred ));

return dwTotalTransferred;

/** public functions **/

/*++ ===============================================================================

Function AES_Init

This function is called by Device Manager to initialize the streams interface in response to ActivateDevice. We passed ActivateDevice a pointer to our device context, but must read it out of the registry as "ClientInfo".

Parameters Context [in] Port identifier. This value points to a string

containing the registry key within HKEY_LOCAL_MACHINE\Drivers\Active\xx.

lpvBusContext [in] Not used.

Return Values Returns a pointer to the context used as a device handle into the entry points AES_Open, AES_Close, AES_Read, AES_Write, etc.

Remarks This function is called at device load time in order to perform any initialization. Typically, this function does as little as possible. This function postpones memory allocation and device power until an application calls the driver's AES_Open function. =============================================================================== ­­*/

DWORD AES_Init(LPCTSTR lpszContext, LPCVOID lpvBusContext)

PUSBFPS_CONTEXT pUsbFps = NULL;

// registry value that need to be read: REG_VALUE_DESCR usbKeyValues[] =

(TEXT("ClientInfo")), REG_DWORD, sizeof(DWORD), (PBYTE)(&pUsbFps), NULL, 0, 0, NULL

;

UNREFERENCED_PARAMETER(lpvBusContext);

DEBUGMSG(ZONE_AES_INIT, (TEXT(">AES_Init(%p)\n"), lpszContext));

// retrieve the pointer to the device context from the registry... GetSetKeyValues( lpszContext,

&usbKeyValues[0], GET, FALSE );

DEBUGMSG(ZONE_AES_INIT, (TEXT("<AES_Init:0x%x\n"), pUsbFps ));

return((DWORD) pUsbFps);

/*++ ===============================================================================

Page 33: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 33 of 40

Function AES_Deinit

This function de­initializes the stream interface.

Parameters pUsbFps [in] Pointer to the device context information

Return Values None.

Remarks This function is called by the DeregisterDevice function, which is exposed to applications. =============================================================================== ­­*/

BOOL AES_Deinit(PUSBFPS_CONTEXT pUsbFps)

DEBUGMSG(ZONE_AES_INIT, (TEXT(">AES_Deinit\n")));

DEBUGMSG(ZONE_AES_INIT, (TEXT("<AES_Deinit\n")));

return TRUE;

/*++ ===============================================================================

Function AES_Close

This function closes the stream driver. It is called after an application calls the CloseHandle function.

Parameters pUsbFps [in] Context pointer returned from the AES_Open function.

Return Values TRUE indicates success. FALSE indicates failure.

Remarks The Device Manager calls this routine to close the device. =============================================================================== ­­*/

BOOL AES_Close(PUSBFPS_CONTEXT pUsbFps)

DEBUGMSG(ZONE_AES_INIT,(TEXT("AES_Close(0x%x)\n"),pUsbFps));

if ( VALID_CONTEXT( pUsbFps ) )

EnterCriticalSection(&pUsbFps­>Lock);

// say the file is closed... pUsbFps­>Flags.Open = FALSE;

// halt any pending transfers... SetEvent(pUsbFps­>BulkOut.hEvent);

LeaveCriticalSection(&pUsbFps­>Lock);

// Note: any waiters are run as soon as we signal this event... return SetEvent( pUsbFps­>hCloseEvent );

else

DEBUGMSG(ZONE_ERR, (TEXT("AES_Close: ERROR_INVALID_HANDLE\n"))); SetLastError(ERROR_INVALID_HANDLE); return FALSE;

Page 34: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 34 of 40

/*++ ===============================================================================

Function AES_Open

This function initializes the stream interface for the USB driver.

Parameters pUsbFps [in] Pointer to a handle returned by the AES_Init function.

AccessCode [in] Specifies the access level requested by the caller, such as shared read or read/write.

ShareMode [in] Specifies the sharing mode that the calling application passed to the CreateFile function.

Return Values Returns a handle representing the USB Finger Print Sensor context. If the device cannot be opened, this function returns NULL.

Remarks The user calls this function to open the device. The HANDLE returned must be used by the application in all subsequent calls to the driver. This function starts the thread that handles the serial events. This function is exported to applications through CreateFile. =============================================================================== ­­*/

PUSBFPS_CONTEXT AES_Open(PUSBFPS_CONTEXT pUsbFps, DWORD AccessCode, DWORD ShareMode)

BOOL bRc = TRUE; // validity flag

UNREFERENCED_PARAMETER(ShareMode); UNREFERENCED_PARAMETER(AccessCode);

DEBUGMSG(ZONE_AES_INIT, (TEXT(">AES_Open(0x%x, 0x%x, 0x%x)\n"), pUsbFps, AccessCode, ShareMode));

if ( VALID_CONTEXT( pUsbFps ) )

EnterCriticalSection(&pUsbFps­>Lock);

if (!pUsbFps­>Flags.Open && !pUsbFps­>Flags.UnloadPending )

// say the file is opened... pUsbFps­>Flags.Open = TRUE;

// set close event as non­signaled... ResetEvent( pUsbFps­>hCloseEvent );

else

DEBUGMSG( ZONE_ERR,(TEXT("AES_Open: ERROR_ACCESS_DENIED\n"))); SetLastError(ERROR_ACCESS_DENIED); bRc = FALSE;

LeaveCriticalSection(&pUsbFps­>Lock); else

DEBUGMSG(ZONE_ERR, (TEXT("AES_Open: ERROR_FILE_NOT_FOUND\n"))); SetLastError(ERROR_FILE_NOT_FOUND);

Page 35: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 35 of 40

bRc = FALSE;

DEBUGMSG(ZONE_AES_INIT, (TEXT("<AES_Open:%d\n"), bRc ));

return (bRc ? pUsbFps : NULL);

/*++ ===============================================================================

Function AES_Read

This function enables an application to receive characters from the USB device.

Parameters pUsbFps [in] Pointer to a handle returned by the AES_Open function.

pBuffer [in] Pointer to valid memory.

dwBufferLength [in] Specifies the size, in bytes, of pTargetBuffer.

Return Values The number of bytes read indicates success. A value of ­1 indicates failure.

Remarks AES_Read sets the buffer and buffer length requested by the caller. It writes the number of bytes transacted and returns the status of the call.

This function is exported to users through the ReadFile function.

This function must obey time­out values set for the serial port. =============================================================================== ­­*/

DWORD AES_Read(PUSBFPS_CONTEXT pUsbFps, PUCHAR pBuffer, DWORD dwBufferLength)

DWORD dwTimeout; DWORD dwBytesTransferred = 0; DWORD dwErr = ERROR_SUCCESS; DWORD dwUsbErr = USB_NO_ERROR;

DEBUGMSG(ZONE_AES_READ, (TEXT(">AES_Read(0x%x, %d)\n"),pBuffer, dwBufferLength));

// is there data to send? if (pBuffer && dwBufferLength)

// Determine the timeout value to use to wait for data...

// If an application sets ReadIntervalTimeout and // ReadTotalTimeoutMultiplier to MAXDWORD and sets // ReadTotalTimeoutConstant to a value greater than zero and // less than MAXDWORD, one of the following occurs when the // ReadFile function is called: // // If there are any characters in the input buffer, ReadFile // returns immediately with the characters in the buffer. // // If there are no characters in the input buffer, ReadFile // waits until a character arrives and then returns immediately. // // If no characters arrive within the time specified by // ReadTotalTimeoutConstant, ReadFile times out. if ((pUsbFps­>Timeouts.ReadIntervalTimeout == MAXDWORD) &&

(pUsbFps­>Timeouts.ReadTotalTimeoutMultiplier == MAXDWORD) && (pUsbFps­>Timeouts.ReadTotalTimeoutConstant != MAXDWORD))

// set the timeout to the constant value... dwTimeout = pUsbFps­>Timeouts.ReadTotalTimeoutConstant;

Page 36: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 36 of 40

else

// ReadIntervalTimeout ­ Specifies the maximum acceptable time, // in milliseconds, to elapse between the arrival of two // characters on the communication line. During a ReadFile // operation, the time period begins when the first character // is received. If the interval between the arrival of any two // characters exceeds this amount, the ReadFile operation is // completed and any buffered data is returned. A value of zero // indicates that interval time­outs are not used. // // This function ignores the inter­character ReadIntervalTimeout // value, it just adds it to the total timeout value.

// ReadTotalTimeoutMultiplier ­ Specifies the multiplier, in // milliseconds, used to calculate the total time­out period // for read operations. For each read operation, this value // is multiplied by the requested number of bytes to be read.

// ReadTotalTimeoutConstant ­ Specifies the constant, in // milliseconds, used to calculate the total time­out period // for read operations. For each read operation, this value // is added to the product of the ReadTotalTimeoutMultiplier // member and the requested number of bytes. A value of zero // for both the ReadTotalTimeoutMultiplier and // ReadTotalTimeoutConstant members indicates that total // time­outs are not used for read operations. dwTimeout = pUsbFps­>Timeouts.ReadIntervalTimeout +

pUsbFps­>Timeouts.ReadTotalTimeoutConstant + (pUsbFps­>Timeouts.ReadTotalTimeoutMultiplier * dwBufferLength);

// read from the USB device... dwBytesTransferred = ReadUsb(pUsbFps,

pBuffer, dwBufferLength, dwTimeout);

else

DEBUGMSG( ZONE_ERR,(TEXT("AES_Read: ERROR_INVALID_PARAMETER\n"))); dwBytesTransferred = ­1; SetLastError(ERROR_INVALID_PARAMETER );

DEBUGMSG(ZONE_AES_READ,(TEXT("<AES_Read:%d\n"), dwBytesTransferred));

return dwBytesTransferred;

/*++ ===============================================================================

Function AES_Write

This function enables an application to transmit bytes via the USB bulk out pipe.

Parameters pUsbFps [in] Handle returned by the AES_Open function.

pBuffer [in] Pointer to the bytes to be written.

dwBufferLength [in] Specifies the number of bytes to be written.

Return Values The number of bytes written indicates success. A value of ­1 indicates failure.

Remarks

Page 37: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 37 of 40

This function is exported to users through the WriteFile function. This function must obey flow­control and time­out values. =============================================================================== ­­*/

DWORD AES_Write(PUSBFPS_CONTEXT pUsbFps, PUCHAR pBuffer, DWORD dwBufferLength)

DWORD dwTotalTransferred ;

DEBUGMSG(ZONE_AES_WRITE, (TEXT(">AES_Write(0x%x, %d)\n"),pBuffer, dwBufferLength));

dwTotalTransferred = WriteUsb(pUsbFps, pBuffer, dwBufferLength);

DEBUGMSG(ZONE_AES_WRITE,(TEXT("<AES_Write:%d\n"), dwTotalTransferred));

return dwTotalTransferred;

/*++ ===============================================================================

Function AES_IOControl

This function implements the USB port's I/O control routine.

Parameters pUsbFps [in] Specifies a value returned from a call to the

AES_Open function.

dwCode [in] Specifies an I/O control code to be performed.

pInBuf [in] Pointer to data input to the device.

dwLenIn [in] Specifies the number of bytes being passed in.

pOutBuf [out] Pointer to data output from the driver.

dwLenOut [out] Specifies the maximum number of bytes to receive from the driver.

pdwActualOut [out] Pointer to the actual number of bytes received from the driver.

Return Values TRUE indicates success. FALSE indicates failure.

Remarks This function is exported by a device driver through the DeviceIoControl() function. =============================================================================== ­­*/

BOOL AES_IOControl( PUSBFPS_CONTEXT pUsbFps, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)

BOOL bRc = TRUE;

DEBUGMSG(ZONE_AES_IOCTL, (TEXT(">AES_IOControl(0x%x, 0x%x, %d, 0x%x)\n"), dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut ));

// is the device currently available? if ( ACCEPT_IO( pUsbFps ) )

Page 38: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 38 of 40

switch ( dwCode )

case IOCTL_SERIAL_SET_TIMEOUTS: case IOCTL_PARALLEL_SET_TIMEOUTS:

DEBUGMSG(ZONE_AES_IOCTL, (TEXT("IOCTL_PARALLEL_SET_TIMEOUTS\n")));

if ((NULL != pBufIn) && (dwLenIn >= sizeof(COMMTIMEOUTS)))

EnterCriticalSection(&pUsbFps­>Lock);

__try

// copy in the new timeouts... memcpy((char *) &pUsbFps­>Timeouts,

pBufIn, sizeof(COMMTIMEOUTS));

// IOCTL completes without error... __except(EXCEPTION_EXECUTE_HANDLER)

DEBUGMSG(ZONE_AES_IOCTL, (_T(" exception in ioctl\r\n")));

LeaveCriticalSection(&pUsbFps­>Lock); else

DEBUGMSG(ZONE_ERR, (TEXT("AES_IOControl:ERROR_INVALID_PARAMETER\n")));

SetLastError(ERROR_INVALID_PARAMETER); bRc = FALSE;

break;

case IOCTL_SERIAL_GET_TIMEOUTS: case IOCTL_PARALLEL_GET_TIMEOUTS:

DEBUGMSG(ZONE_AES_IOCTL, (TEXT("IOCTL_PARALLEL_GET_TIMEOUTS\n")));

if ((NULL != pBufOut) && (dwLenOut >= sizeof(COMMTIMEOUTS)))

EnterCriticalSection(&pUsbFps­>Lock);

__try

// copy out the current timeouts... memcpy((char *) pBufOut,

&pUsbFps­>Timeouts, sizeof(COMMTIMEOUTS));

// Return the size *pdwActualOut = sizeof(COMMTIMEOUTS);

// IOCTL completes without error... __except(EXCEPTION_EXECUTE_HANDLER)

DEBUGMSG(ZONE_AES_IOCTL, (_T(" exception in ioctl\r\n")));

LeaveCriticalSection(&pUsbFps­>Lock); else

DEBUGMSG(ZONE_ERR,

Page 39: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 39 of 40

(TEXT("AES_IOControl:ERROR_INVALID_PARAMETER\n"))); SetLastError(ERROR_INVALID_PARAMETER); *pdwActualOut = 0; bRc = FALSE;

break;

default: DEBUGMSG(ZONE_ERR,

(TEXT("AES_IOControl(0x%x) : ERROR_NOT_SUPPORTED\n"), dwCode));

SetLastError(ERROR_NOT_SUPPORTED); bRc = FALSE; break;

else

DEBUGMSG( ZONE_ERR,(TEXT("AES_IOControl: ERROR_INVALID_HANDLE\n"))); SetLastError(ERROR_INVALID_HANDLE); bRc = FALSE;

DEBUGMSG(ZONE_AES_IOCTL,(TEXT("<AES_IOControl:%d\n"), bRc));

return bRc;

Page 40: First Usb Driver Wince

Writing Your First WinCE USB Device Driver

ETP­301Paper.doc November 17, 2004 Page 40 of 40

AesUsb.reg ; ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ; ; ** Registry values for the AUTHENTEC USB Finger Print Sensor Class Driver ** ; ; ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ; 2303 == 0x08FF (Device Vendor ID) ; ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ; @CESYSGEN IF CE_MODULES_COMMCTRL ; @CESYSGEN IF CE_MODULES_USBD IF BSP_AUTHENTEC_USB

[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\2303\Default\Default\FPS_Class] "Prefix"="AES" "Dll"="AESUSB.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\FPS_Class] "Prefix"="AES" "Dll"="AESUSB.DLL"

ENDIF BSP_AUTHENTEC_USB ; @CESYSGEN ENDIF ; @CESYSGEN ENDIF