转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
在看这篇文章时可参考我的另一篇文章:
<< Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>
1、 INF 文件基础
所有的USB设备在设备描述符中都有一个厂商ID(VID)和产品ID(PID)来向Windows 报告。Windows 使用VID和PID来寻找匹配的设备驱动程序。INF文件就是连接VID和PID和指定的驱动程序的连接。
2、 注册表
INF文件是用来当设备首次连接到系统上时,把设备和它的驱动程序联系在一起。这个信息然后存储在注册表中以便于后来的设备能快速而不需要用户干预地完成连接。大多数的USB信息存储在系统注册表在
\HKEY_LOCAL_MACHINE\Enum\USB的下面。
系统注册表可以用工具regedit.exe来查看和修改,此工具在操作系统中。但修改注册表时要小心。
3. 用户态和GPD的接口
所有的用户态是通过I/O控制调用来访问EZ_USB GPD的。一个用户态程序首先通过调用一个Win32 函数CreateFile( )来获得设备驱动程序的句柄。用户态应用然后用Win32 函数DeviceIoControl( )通过CreateFile( )函数返回的句柄,来提交I/O控制代码和相关的输入输出缓冲区到驱动程序。
4、符号连接
EZ_USB GPD 可以和多个EZ_USB 设备通讯。对于每一个连接到主机上的EZ_USB设备,驱动程序以ezusb-I 的形式创建一个符号连接,此处I 是一个从0开始的事例。如果有3个EZ_USB 设备连到主机上,那就存在3个符号连接:ezusb-0, ezusb-1, ezusb-2。符号连接的名字在调用CreateFile( )时使用来得到设备驱动的句柄。CreateFile( )真正做的是为设备驱动程序创建的设备对象得到一个句柄。下面的例子展示了得到 EZ_USB 设备ezusb-0 的句柄:
HANDLE DeviceHandle;
DeviceHandle = CreateFile("\\\\.\\ezusb-0",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
5、 设备I/O控制
有两种方法:
5、1
用户态应用使用win32 函数DeviceIoControl( )来向设备驱动程序发送请求。下面的代码示例显示了向EZ_USB GPD 提交请求来读取设备的USB设备描述符。它使用上一个例子中返回的DeviceHandle 句柄。
void
rw_dev( HANDLE hDEV )
/*++
Routine Description:
Called to do formatted ascii dump to console of USB
configuration, interface, and endpoint descriptors
(Cmdline "rwbulk -u" )
Arguments:
handle to device
Return Value:
none
--*/
{
UINT success;
int siz, nBytes;
char buf[256];
PUSB_CONFIGURATION_DESCRIPTOR cd;
PUSB_INTERFACE_DESCRIPTOR id;
PUSB_ENDPOINT_DESCRIPTOR ed;
siz = sizeof(buf);
if (hDEV == INVALID_HANDLE_VALUE) {
NOISY(("DEV not open"));
return;
}
success = DeviceIoControl(hDEV,
IOCTL_BULKUSB_GET_CONFIG_DESCRIPTOR,
buf,
siz,
buf,
siz,
&nBytes,
NULL);
NOISY(("request complete, success = %d nBytes = %d\n", success, nBytes));
if (success) {
ULONG i;
UINT j, n;
char *pch;
pch = buf;
n = 0;
cd = (PUSB_CONFIGURATION_DESCRIPTOR) pch;
print_USB_CONFIGURATION_DESCRIPTOR( cd );
pch += cd->bLength;
do {
id = (PUSB_INTERFACE_DESCRIPTOR) pch;
print_USB_INTERFACE_DESCRIPTOR(id, n++);
pch += id->bLength;
for (j=0; j<id->bNumEndpoints; j++) {
ed = (PUSB_ENDPOINT_DESCRIPTOR) pch;
print_USB_ENDPOINT_DESCRIPTOR(ed,j);
pch += ed->bLength;
}
i = (ULONG)(pch - buf);
} while (i<cd->wTotalLength);
}
return;
}
对应此功能,驱动程序端:(SYS)应该这样设计:
#define BULKUSB_IOCTL_INDEX 0x0000
#define IOCTL_BULKUSB_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN, \
BULKUSB_IOCTL_INDEX, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
在DriverEntry中有
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = BulkUsb_DispatchDevCtrl;
用户模式的 DeviceIoControl()将会调用到它。
它的实现是:
NTSTATUS
BulkUsb_DispatchDevCtrl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Dispatch routine for IRP_MJ_DEVICE_CONTROL
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Return Value:
NT status value
--*/
{
ULONG code;
PVOID ioBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG info;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
//
// initialize variables
//
info = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
code = irpStack->Parameters.DeviceIoControl.IoControlCode;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
if(deviceExtension->DeviceState != Working) {
BulkUsb_DbgPrint(1, ("Invalid device state\n"));
Irp->IoStatus.Status = ntStatus = STATUS_INVALID_DEVICE_STATE;
Irp->IoStatus.Information = info;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
BulkUsb_DbgPrint(3, ("BulkUsb_DispatchDevCtrl::"));
BulkUsb_IoIncrement(deviceExtension);
//
// It is true that the client driver cancelled the selective suspend
// request in the dispatch routine for create.
// But there is no guarantee that it has indeed been completed.
// so wait on the NoIdleReqPendEvent and proceed only if this event
// is signalled.
//
BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
//
// make sure that the selective suspend request has been completed.
//
if(deviceExtension->SSEnable) {
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
switch(code) {
case IOCTL_BULKUSB_RESET_PIPE:
{
PFILE_OBJECT fileObject;
PUSBD_PIPE_INFORMATION pipe;
pipe = NULL;
fileObject = NULL;
//
// FileObject is the address of the kernel file object to
// which the IRP is directed. Drivers use the FileObject
// to correlate IRPs in a queue.
//
fileObject = irpStack->FileObject;
if(fileObject == NULL) {
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
pipe = (PUSBD_PIPE_INFORMATION) fileObject->FsContext;
if(pipe == NULL) {
ntStatus = STATUS_INVALID_PARAMETER;
}
else {
ntStatus = BulkUsb_ResetPipe(DeviceObject, pipe);
}
break;
}
case IOCTL_BULKUSB_GET_CONFIG_DESCRIPTOR:
{
ULONG length;
if(deviceExtension->UsbConfigurationDescriptor) {
length = deviceExtension->UsbConfigurationDescriptor->wTotalLength;
if(outputBufferLength >= length) {
RtlCopyMemory(ioBuffer,
deviceExtension->UsbConfigurationDescriptor,
length);
info = length;
ntStatus = STATUS_SUCCESS;
}
else {
ntStatus = STATUS_BUFFER_TOO_SMALL;
}
}
else {
ntStatus = STATUS_UNSUCCESSFUL;
}
break;
}
case IOCTL_BULKUSB_RESET_DEVICE:
ntStatus = BulkUsb_ResetDevice(DeviceObject);
break;
default :
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = info;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
BulkUsb_DbgPrint(3, ("BulkUsb_DispatchDevCtrl::"));
BulkUsb_IoDecrement(deviceExtension);
return ntStatus;
}
5、2 也可以直接通过WriteFile,ReadFile来读写USB设备。
int _cdecl main(int argc,char *argv[])
{
char *pinBuf = NULL, *poutBuf = NULL;
int nBytesRead, nBytesWrite, nBytes;
ULONG i, j;
int ok;
UINT success;
HANDLE hRead = INVALID_HANDLE_VALUE, hWrite = INVALID_HANDLE_VALUE;
char buf[1024];
clock_t start, finish;
ULONG totalBytes = 0L;
double seconds;
ULONG fail = 0L;
parse(argc, argv );
// dump USB configuation and pipe info
if( fDumpUsbConfig ) {
dumpUsbConfig();
}
// doing a read, write, or both test
if ((fRead) || (fWrite)) {
if (fRead) {
//
// open the output file
//
if ( fDumpReadData ) { // round size to sizeof ULONG for readable dumping
while( ReadLen % sizeof( ULONG ) )
ReadLen++;
}
hRead = open_file( inPipe);
pinBuf = malloc(ReadLen);
}
if (fWrite) {
if ( fDumpReadData ) { // round size to sizeof ULONG for readable dumping
while( WriteLen % sizeof( ULONG ) )
WriteLen++;
}
hWrite = open_file( outPipe);
poutBuf = malloc(WriteLen);
}
for (i=0; i<IterationCount; i++) {
if (fWrite && poutBuf && hWrite != INVALID_HANDLE_VALUE) {
PULONG pOut = (PULONG) poutBuf;
ULONG numLongs = WriteLen / sizeof( ULONG );
//
// put some data in the output buffer
//
for (j=0; j<numLongs; j++) {
*(pOut+j) = j;
}
//
// send the write
//
WriteFile(hWrite,
poutBuf,
WriteLen,
&nBytesWrite,
NULL);
printf("<%s> W (%04.4d) : request %06.6d bytes -- %06.6d bytes written\n",
outPipe, i, WriteLen, nBytesWrite);
assert(nBytesWrite == WriteLen);
}
if (fRead && pinBuf) {
success = ReadFile(hRead,
pinBuf,
ReadLen,
&nBytesRead,
NULL);
printf("<%s> R (%04.4d) : request %06.6d bytes -- %06.6d bytes read\n",
inPipe, i, ReadLen, nBytesRead);
if (fWrite) {
//
// validate the input buffer against what
// we sent to the 82930 (loopback test)
//
ok = compare_buffs(pinBuf, poutBuf, nBytesRead);
if( fDumpReadData ) {
printf("Dumping read buffer\n");
dump( pinBuf, nBytesRead );
printf("Dumping write buffer\n");
dump( poutBuf, nBytesRead );
}
assert(ok);
if(ok != 1)
fail++;
assert(ReadLen == WriteLen);
assert(nBytesRead == ReadLen);
assert(nBytesWrite == WriteLen);
}
}
}
if (pinBuf) {
free(pinBuf);
}
if (poutBuf) {
free(poutBuf);
}
// close devices if needed
if(hRead != INVALID_HANDLE_VALUE)
CloseHandle(hRead);
if(hWrite != INVALID_HANDLE_VALUE)
CloseHandle(hWrite);
}
return 0;
}
对应此功能,驱动程序端:(SYS)应该这样设计:
在DriverEntry 中有
DriverObject->MajorFunction[IRP_MJ_READ] =
DriverObject->MajorFunction[IRP_MJ_WRITE] = BulkUsb_DispatchReadWrite;
用户模式的ReadFile() WriteFile()将会调用到它们。
NTSTATUS
BulkUsb_DispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Dispatch routine for read and write.
This routine creates a BULKUSB_RW_CONTEXT for a read/write.
This read/write is performed in stages of BULKUSB_MAX_TRANSFER_SIZE.
once a stage of transfer is complete, then the irp is circulated again,
until the requested length of tranfer is performed.
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Return Value:
NT status value
--*/
{
PMDL mdl;
PURB urb;
ULONG totalLength;
ULONG stageLength;
ULONG urbFlags;
BOOLEAN read;
NTSTATUS ntStatus;
ULONG_PTR virtualAddress;
PFILE_OBJECT fileObject;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;
PUSBD_PIPE_INFORMATION pipeInformation;
//
// 初始化变量
urb = NULL;
mdl = NULL;
rwContext = NULL;
totalLength = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - begins\n"));
if(deviceExtension->DeviceState != Working) {
BulkUsb_DbgPrint(1, ("Invalid device state\n"));
ntStatus = STATUS_INVALID_DEVICE_STATE;
goto BulkUsb_DispatchReadWrite_Exit;
}
//
// It is true that the client driver cancelled the selective suspend
// request in the dispatch routine for create Irps.
// But there is no guarantee that it has indeed completed.
// so wait on the NoIdleReqPendEvent and proceed only if this event
// is signalled.
//
BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
//
// make sure that the selective suspend request has been completed.
//
if(deviceExtension->SSEnable)
{
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
if(fileObject && fileObject->FsContext)
{
pipeInformation = fileObject->FsContext;
if(UsbdPipeTypeBulk != pipeInformation->PipeType) {
BulkUsb_DbgPrint(1, ("Usbd pipe type is not bulk\n"));
ntStatus = STATUS_INVALID_HANDLE;
goto BulkUsb_DispatchReadWrite_Exit;
}
}
else
{
BulkUsb_DbgPrint(1, ("Invalid handle\n"));
ntStatus = STATUS_INVALID_HANDLE;
goto BulkUsb_DispatchReadWrite_Exit;
}
// 设置完成例程的参数
rwContext = (PBULKUSB_RW_CONTEXT)
ExAllocatePool(NonPagedPool,
sizeof(BULKUSB_RW_CONTEXT));
if(rwContext == NULL)
{
BulkUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto BulkUsb_DispatchReadWrite_Exit;
}
if(Irp->MdlAddress)
{
totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}
if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE)
{
BulkUsb_DbgPrint(1, ("Transfer length > circular buffer\n"));
ntStatus = STATUS_INVALID_PARAMETER;
ExFreePool(rwContext);
goto BulkUsb_DispatchReadWrite_Exit;
}
if(totalLength == 0)
{
BulkUsb_DbgPrint(1, ("Transfer data length = 0\n"));
ntStatus = STATUS_SUCCESS;
ExFreePool(rwContext);
goto BulkUsb_DispatchReadWrite_Exit;
}
// 设置URB标志
urbFlags = USBD_SHORT_TRANSFER_OK;
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);
// 判断是读还是写
if(read)
{
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
BulkUsb_DbgPrint(3, ("Read operation\n"));
}
else
{
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
BulkUsb_DbgPrint(3, ("Write operation\n"));
}
//
// the transfer request is for totalLength.
// we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
// in each stage.
//
if(totalLength > BULKUSB_MAX_TRANSFER_SIZE)
{
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else
{
stageLength = totalLength;
}
// 建立MDL
mdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);
if(mdl == NULL)
{
BulkUsb_DbgPrint(1, ("Failed to alloc mem for mdl\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(rwContext);
goto BulkUsb_DispatchReadWrite_Exit;
}
//
// 将新MDL进行映射
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(Irp->MdlAddress,
mdl,
(PVOID) virtualAddress,
stageLength);
// 申请URB数据结构
urb = ExAllocatePool(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
if(urb == NULL)
{
BulkUsb_DbgPrint(1, ("Failed to alloc mem for urb\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(rwContext);
IoFreeMdl(mdl);
goto BulkUsb_DispatchReadWrite_Exit;
}
// 建立Bulk管道的URB
UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
pipeInformation->PipeHandle,
NULL,
mdl,
stageLength,
urbFlags,
NULL);
//
// 设置完成例程参数 BULKUSB_RW_CONTEXT
//
rwContext->Urb = urb;
rwContext->Mdl = mdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
rwContext->DeviceExtension = deviceExtension;
//
// use the original read/write irp as an internal device control irp
// 设置设备堆栈
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
// 设置完成例程式
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);
//
// since we return STATUS_PENDING call IoMarkIrpPending.
// This is the boiler plate code.
// This may cause extra overhead of an APC for the Irp completion
// but this is the correct thing to do.
//
// 将当前IRP阻塞
IoMarkIrpPending(Irp);
BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite::"));
BulkUsb_IoIncrement(deviceExtension);
// 将IRP转发到底层USB总线驱动
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
Irp);
if(!NT_SUCCESS(ntStatus)) {
BulkUsb_DbgPrint(1, ("IoCallDriver fails with status %X\n", ntStatus));
//
// if the device was yanked out, then the pipeInformation
// field is invalid.
// similarly if the request was cancelled, then we need not
// invoked reset pipe/device.
//
if((ntStatus != STATUS_CANCELLED) &&
(ntStatus != STATUS_DEVICE_NOT_CONNECTED)) {
ntStatus = BulkUsb_ResetPipe(DeviceObject,
pipeInformation);
if(!NT_SUCCESS(ntStatus)) {
BulkUsb_DbgPrint(1, ("BulkUsb_ResetPipe failed\n"));
ntStatus = BulkUsb_ResetDevice(DeviceObject);
}
}
else {
BulkUsb_DbgPrint(3, ("ntStatus is STATUS_CANCELLED or "
"STATUS_DEVICE_NOT_CONNECTED\n"));
}
}
//
// we return STATUS_PENDING and not the status returned by the lower layer.
//
return STATUS_PENDING;
BulkUsb_DispatchReadWrite_Exit:
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - ends\n"));
return ntStatus;
}