您的位置:首页 > 编程语言

USB转串口驱动代码分析

2015-06-10 18:16 507 查看

1、USB插入时,创建设备

[plain] view plaincopyDriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;

步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)
(1) IoCreateDevice系统API的原理为:[plain] view plaincopyNTKERNELAPI
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Reserved,
OUT PDEVICE_OBJECT *DeviceObject
);

在之前真实的USB驱动中我们是这样创建的:[cpp] view plaincopyntStatus = IoCreateDevice(
DriverObject, // our driver object
sizeof(DEVICE_EXTENSION), // extension size for us
NULL, // name for this device
FILE_DEVICE_UNKNOWN,
FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics
FALSE, // Not exclusive
&deviceObject); // Our device object

就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型。[cpp] view plaincopyntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
&deviceObjName, FILE_DEVICE_SERIAL_PORT,
FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);
(2)为自定义的扩展设备中的设备名字段指定设备名[cpp] view plaincopy// deviceExtension->DeviceName为UNICODE_STRING类型
RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));
deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);
// Buffer重新分配
deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));
RtlZeroMemory(deviceExtension->DeviceName.Buffer,
deviceObjName.Length+sizeof(WCHAR));
RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);

(3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。[cpp] view plaincopy // this event is triggered when there is no pending io of any kind and device is removed
KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);

// this event is triggered when self-requested power irps complete
KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);

// this event is triggered when there is no pending io (pending io count == 1 )
KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);

// spinlock used to protect inc/dec iocount logic
KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);

deviceExtension->BaudRate = 19200;
/* Set line control */
deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
deviceExtension->SerialLineControl.Parity = NO_PARITY;
deviceExtension->SerialLineControl.WordLength = 8;

deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;
deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;

deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
deviceExtension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
deviceExtension->HandFlow.XoffLimit = 300;
deviceExtension->HandFlow.XonLimit = 100;
InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);
InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);
KeInitializeSpinLock(&deviceExtension->InputBufferLock);
KeInitializeSpinLock(&deviceExtension->OutputBufferLock);
InitializeListHead(&deviceExtension->ReadQueue);
KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);
InitializeListHead(&deviceExtension->WriteQueue);
KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);
InitializeListHead(&deviceExtension->PurgeQueue);
KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);

步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE 设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,在resume时不接收一个IRP_MN_START_DEVICE消息。[cpp] view plaincopy// we support direct io for read/write
//
deviceObject->Flags |= DO_DIRECT_IO;


//Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE
//during suspend and also not get an IRP_MN_START_DEVICE during resume.
//This is neccesary because during the start device call,
// the GetDescriptors() call will be failed by the USB stack.
deviceObject->Flags |= DO_POWER_PAGABLE;

步三、附加FDO到物理设备对象上,并创建SymbolLink,并通过IoRegisterDeviceInterface来设备绑定让设备可见。


[cpp] view plaincopydeviceExtension->TopOfStackDeviceObject =
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);

[cpp] view plaincopystatus = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,
NULL, &PDevExt->DeviceClassSymbolicName);

步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别
(1)建立IRP来产生一个发往FDO的内部查询请求;
[cpp] view plaincopyirp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);

(2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities[cpp] view plaincopynextStack = IoGetNextIrpStackLocation(irp);
nextStack->MajorFunction= IRP_MJ_PNP;
nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;

在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:[cpp] view plaincopy#define IoGetNextIrpStackLocation( Irp ) (\
(Irp)->Tail.Overlay.CurrentStackLocation - 1 )
从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1。
(3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)(4)把IRP发送下去,并等待完成; [cpp] view plaincopyntStatus = IoCallDriver(LowerDeviceObject,
irp);

USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));

if (ntStatus == STATUS_PENDING) {
// wait for irp to complete

KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。
步五、获得USB的版本信息;
直接调用系统API:[cpp] view plaincopyUSBD_GetUSBDIVersion(&versionInformation);

2、处理系统PNP和电源管理请求的派遣函数

[cpp] view plaincopyDriverObject->MajorFunction[IRP_MJ_PNP] = USB2COM_ProcessPnPIrp;
在USB2COM_ProcessPnPIrp里case了以下几个消息:IRP_MN_START_DEVICE 、IRP_MN_QUERY_STOP_DEVICE、IRP_MN_CANCEL_STOP_DEVICE、IRP_MN_STOP_DEVICE、IRP_MN_QUERY_REMOVE_DEVICE、IRP_MN_CANCEL_REMOVE_DEVICE、IRP_MN_SURPRISE_REMOVAL、IRP_MN_REMOVE_DEVICE
文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中讲到的同类派遣函数,它只case 了以下:IRP_MN_START_DEVICE、IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL所以一比较,觉得USB2COM考虑得更全面。(1)IRP_MN_START_DEVICE与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;(2)IRP_MN_QUERY_STOP_DEVICE与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;(3)IRP_MN_CANCEL_STOP_DEVICE (4)IRP_MN_STOP_DEVICE、(5)IRP_MN_QUERY_REMOVE_DEVICE、(6)IRP_MN_CANCEL_REMOVE_DEVICE、(7)IRP_MN_SURPRISE_REMOVAL、(8)IRP_MN_REMOVE_DEVICE

3、当应用程序CreateFile时,调用USB2COM_Create

DriverObject->MajorFunction[IRP_MJ_CREATE] = USB2COM_Create;步一、判断是否能接收一个新的IO请求如果不能接收一个新的IO请求,那么直接返回。[cpp] view plaincopyif ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {
ntStatus = STATUS_DELETE_PENDING;

USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));
goto done;
}
在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):1) 设备已经被移除了,
2) 从来没有被启动过,,
3) 已经停止了,
4) 有一个移除的请求还没处理,
5) 有一个停止的请求还没处理。[cpp] view plaincopy//flag set when processing IRP_MN_REMOVE_DEVICE
if ( !deviceExtension->DeviceRemoved &&
// device must be started( enabled )
deviceExtension->DeviceStarted &&
// flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE
!deviceExtension->RemoveDeviceRequested &&
// flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE
!deviceExtension->StopDeviceRequested ){
fCan = TRUE;
}

步二:开始从interface中的Pipe[0]中读(说明Interface的获取是在之前就完成的,是在USB2COM_ProcessPnPIrp
里case IRP_MN_START_DEVICE:完成的)[cpp] view plaincopyStartReadIntUrb(
DeviceObject,
&interface->Pipes[0]
);

(1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;(2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;[cpp] view plaincopyirp = IoAllocateIrp(stackSize, FALSE);
if(irp == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
urb = USB2COM_ExAllocatePool(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
if(urb == NULL)
{
IoFreeIrp(irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
deviceExtension->ReadIntUrbs[i].Irp = irp;
deviceExtension->ReadIntUrbs[i].Urb = urb;
deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;
deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;
InitIntUrb(urb,
PipeInfo->PipeHandle,
deviceExtension->ReadIntUrbs[i].TransferBuffer,
sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),
TRUE);
InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:[cpp] view plaincopyVOID
InitIntUrb(
IN PURB urb,
IN USBD_PIPE_HANDLE PipeHandle,
IN PUCHAR TransferBuffer,
IN ULONG length,
IN BOOLEAN Read
)
{
USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
if (urb) {
RtlZeroMemory(urb, siz);

urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;
urb->UrbBulkOrInterruptTransfer.Hdr.Function =
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
urb->UrbBulkOrInterruptTransfer.TransferFlags =
Read ? USBD_TRANSFER_DIRECTION_IN : 0;
// short packet is not treated as an error.
urb->UrbBulkOrInterruptTransfer.TransferFlags |=
USBD_SHORT_TRANSFER_OK;

//
// not using linked urb's
//
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;
}
}

(3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;(4)完成例程ReadIntUrbComplete的处理;A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);[cpp] view plaincopyKeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);
PushCircularBufferEntry(
&deviceExtension->InputBuffer,
&pIntUrbs->TransferBuffer[1],
pIntUrbs->TransferBuffer[0]);
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
PushCircularBufferEntry为自己封装的函数:把data内存中的len个数据拷到pBuffer中[cpp] view plaincopyNTSTATUS
PushCircularBufferEntry(
IN PCIRCULAR_BUFFER pBuffer,
IN PUCHAR data,
IN ULONG len)
{
ULONG NextPosition;
DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);
ASSERT(pBuffer);
ASSERT(pBuffer->Length);

if ((data == NULL) || (len == 0))
return STATUS_INVALID_PARAMETER;
do{
NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;
if (NextPosition == pBuffer->ReadPosition)
return STATUS_BUFFER_TOO_SMALL;
pBuffer->Buffer[pBuffer->WritePosition] = *data++;
pBuffer->DataLen++;
pBuffer->WritePosition = NextPosition;
}while(--len);

return STATUS_SUCCESS;
}
C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。C、1 把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁。[cpp] view plaincopyvoid
SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )
{
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION deviceExtension;

DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);
ASSERT(pIrp);
irpStack = IoGetCurrentIrpStackLocation(pIrp);
deviceExtension = irpStack->DeviceObject->DeviceExtension;
deviceExtension->CurrentWaitIrp = NULL;
/*
*All Cancel routines must follow these guidelines:
* 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock
* 2. ...
*/
IoReleaseCancelSpinLock(pIrp->CancelIrql);
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
DbgPrint("SerialCancelCurrentWait Exit\n");
}
C、2 如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。[cpp] view plaincopydeviceExtension->CurrentWaitIrp = NULL;
deviceExtension->HistoryMask &= ~events;
IoReleaseCancelSpinLock(OldIrql);
pIrp->IoStatus.Information = sizeof(ULONG);
pIrp->IoStatus.Status = ntStatus;
*((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;
IoCompleteRequest (pIrp,IO_NO_INCREMENT);
D、完成当前的读IRP;D、1 如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。[cpp] view plaincopyif(deviceExtension->CurrentReadIrp)
{
ULONG haveLen;
BOOLEAN returnWhatsPresent = FALSE;

if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&
( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&
( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)
)
{
returnWhatsPresent = TRUE;
}
ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );
ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);
KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);
haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);
if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))
{
ioLength = (ioLength < haveLen) ? ioLength : haveLen;
DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);
ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;
deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;
IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);
deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);
}
else
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
}
以上代码中MmGetSystemAddressForMdlSafe[cpp] view plaincopy// 函数说明:
// 此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射
// 参数:
// MemoryDescriptorList - 指向MDL的指针
// Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了
// 返回值:
// 返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.
// 与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.
// 版本说明:
// 此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.
#define MmGetSystemAddressForMdlSafe(MDL, PRIORITY) \
(((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \
MDL_SOURCE_IS_NONPAGED_POOL)) ? \
((MDL)->MappedSystemVa) : \
(MmMapLockedPagesSpecifyCache((MDL), \
KernelMode, \
MmCached, \
NULL, \
FALSE, \
(PRIORITY))))

第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:[cpp] view plaincopy// 定义一个指向这个I/O请求的内存描述符(MDL)的指针,
// 此域仅在I/O是“direct I/O"时被用
PMDL MdlAddress;

D、2 判断是否能接收一个新的IO请求如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。
D、3 循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?结束条件:直至设备不能接收一个新的IO请求或未连接、取消。所以处理当前读IRP是一直进行的。步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)[cpp] view plaincopyPrepareWriteIntUrb(
IN PDEVICE_OBJECT DeviceObject,
IN PUSBD_PIPE_INFORMATION PipeInfo
)
A、分配写的Irb 与 Urb空间。B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)应用程序的名字从当前IRP中的fileObject得到。[cpp] view plaincopyourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );

步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
[cpp] view plaincopyfor (i=0; i<interface->NumberOfPipes; i++) {

PipeInfo = &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION PipeInfo;

if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {

//
// found a match
//
USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));
fileObject->FsContext = PipeInfo;
ourPipeInfo->fPipeOpened = TRUE; // set flag for opened
ntStatus = STATUS_SUCCESS;

deviceExtension->OpenPipeCount++;

// try to power up device if its not in D0
actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );
break;
}
}

1、USB插入时,创建设备

[plain] view plaincopyDriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;

步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)
(1) IoCreateDevice系统API的原理为:[plain] view plaincopyNTKERNELAPI
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Reserved,
OUT PDEVICE_OBJECT *DeviceObject
);

在之前真实的USB驱动中我们是这样创建的:[cpp] view plaincopyntStatus = IoCreateDevice(
DriverObject, // our driver object
sizeof(DEVICE_EXTENSION), // extension size for us
NULL, // name for this device
FILE_DEVICE_UNKNOWN,
FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics
FALSE, // Not exclusive
&deviceObject); // Our device object

就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型。[cpp] view plaincopyntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
&deviceObjName, FILE_DEVICE_SERIAL_PORT,
FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);
(2)为自定义的扩展设备中的设备名字段指定设备名[cpp] view plaincopy// deviceExtension->DeviceName为UNICODE_STRING类型
RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));
deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);
// Buffer重新分配
deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));
RtlZeroMemory(deviceExtension->DeviceName.Buffer,
deviceObjName.Length+sizeof(WCHAR));
RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);

(3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。[cpp] view plaincopy // this event is triggered when there is no pending io of any kind and device is removed
KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);

// this event is triggered when self-requested power irps complete
KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);

// this event is triggered when there is no pending io (pending io count == 1 )
KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);

// spinlock used to protect inc/dec iocount logic
KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);

deviceExtension->BaudRate = 19200;
/* Set line control */
deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
deviceExtension->SerialLineControl.Parity = NO_PARITY;
deviceExtension->SerialLineControl.WordLength = 8;

deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;
deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;

deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
deviceExtension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
deviceExtension->HandFlow.XoffLimit = 300;
deviceExtension->HandFlow.XonLimit = 100;
InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);
InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);
KeInitializeSpinLock(&deviceExtension->InputBufferLock);
KeInitializeSpinLock(&deviceExtension->OutputBufferLock);
InitializeListHead(&deviceExtension->ReadQueue);
KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);
InitializeListHead(&deviceExtension->WriteQueue);
KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);
InitializeListHead(&deviceExtension->PurgeQueue);
KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);

步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE 设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,在resume时不接收一个IRP_MN_START_DEVICE消息。[cpp] view plaincopy// we support direct io for read/write
//
deviceObject->Flags |= DO_DIRECT_IO;


//Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE
//during suspend and also not get an IRP_MN_START_DEVICE during resume.
//This is neccesary because during the start device call,
// the GetDescriptors() call will be failed by the USB stack.
deviceObject->Flags |= DO_POWER_PAGABLE;

步三、附加FDO到物理设备对象上,并创建SymbolLink,并通过IoRegisterDeviceInterface来设备绑定让设备可见。


[cpp] view plaincopydeviceExtension->TopOfStackDeviceObject =
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);

[cpp] view plaincopystatus = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,
NULL, &PDevExt->DeviceClassSymbolicName);

步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别
(1)建立IRP来产生一个发往FDO的内部查询请求;
[cpp] view plaincopyirp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);

(2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities[cpp] view plaincopynextStack = IoGetNextIrpStackLocation(irp);
nextStack->MajorFunction= IRP_MJ_PNP;
nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;

在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:[cpp] view plaincopy#define IoGetNextIrpStackLocation( Irp ) (\
(Irp)->Tail.Overlay.CurrentStackLocation - 1 )
从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1。
(3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)(4)把IRP发送下去,并等待完成; [cpp] view plaincopyntStatus = IoCallDriver(LowerDeviceObject,
irp);

USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));

if (ntStatus == STATUS_PENDING) {
// wait for irp to complete

KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。
步五、获得USB的版本信息;
直接调用系统API:[cpp] view plaincopyUSBD_GetUSBDIVersion(&versionInformation);

2、处理系统PNP和电源管理请求的派遣函数

[cpp] view plaincopyDriverObject->MajorFunction[IRP_MJ_PNP] = USB2COM_ProcessPnPIrp;
在USB2COM_ProcessPnPIrp里case了以下几个消息:IRP_MN_START_DEVICE 、IRP_MN_QUERY_STOP_DEVICE、IRP_MN_CANCEL_STOP_DEVICE、IRP_MN_STOP_DEVICE、IRP_MN_QUERY_REMOVE_DEVICE、IRP_MN_CANCEL_REMOVE_DEVICE、IRP_MN_SURPRISE_REMOVAL、IRP_MN_REMOVE_DEVICE
文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中讲到的同类派遣函数,它只case 了以下:IRP_MN_START_DEVICE、IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL所以一比较,觉得USB2COM考虑得更全面。(1)IRP_MN_START_DEVICE与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;(2)IRP_MN_QUERY_STOP_DEVICE与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;(3)IRP_MN_CANCEL_STOP_DEVICE (4)IRP_MN_STOP_DEVICE、(5)IRP_MN_QUERY_REMOVE_DEVICE、(6)IRP_MN_CANCEL_REMOVE_DEVICE、(7)IRP_MN_SURPRISE_REMOVAL、(8)IRP_MN_REMOVE_DEVICE

3、当应用程序CreateFile时,调用USB2COM_Create

DriverObject->MajorFunction[IRP_MJ_CREATE] = USB2COM_Create;步一、判断是否能接收一个新的IO请求如果不能接收一个新的IO请求,那么直接返回。[cpp] view plaincopyif ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {
ntStatus = STATUS_DELETE_PENDING;

USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));
goto done;
}
在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):1) 设备已经被移除了,
2) 从来没有被启动过,,
3) 已经停止了,
4) 有一个移除的请求还没处理,
5) 有一个停止的请求还没处理。[cpp] view plaincopy//flag set when processing IRP_MN_REMOVE_DEVICE
if ( !deviceExtension->DeviceRemoved &&
// device must be started( enabled )
deviceExtension->DeviceStarted &&
// flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE
!deviceExtension->RemoveDeviceRequested &&
// flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE
!deviceExtension->StopDeviceRequested ){
fCan = TRUE;
}

步二:开始从interface中的Pipe[0]中读(说明Interface的获取是在之前就完成的,是在USB2COM_ProcessPnPIrp
里case IRP_MN_START_DEVICE:完成的)[cpp] view plaincopyStartReadIntUrb(
DeviceObject,
&interface->Pipes[0]
);

(1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;(2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;[cpp] view plaincopyirp = IoAllocateIrp(stackSize, FALSE);
if(irp == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
urb = USB2COM_ExAllocatePool(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
if(urb == NULL)
{
IoFreeIrp(irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
deviceExtension->ReadIntUrbs[i].Irp = irp;
deviceExtension->ReadIntUrbs[i].Urb = urb;
deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;
deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;
InitIntUrb(urb,
PipeInfo->PipeHandle,
deviceExtension->ReadIntUrbs[i].TransferBuffer,
sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),
TRUE);
InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:[cpp] view plaincopyVOID
InitIntUrb(
IN PURB urb,
IN USBD_PIPE_HANDLE PipeHandle,
IN PUCHAR TransferBuffer,
IN ULONG length,
IN BOOLEAN Read
)
{
USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
if (urb) {
RtlZeroMemory(urb, siz);

urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;
urb->UrbBulkOrInterruptTransfer.Hdr.Function =
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
urb->UrbBulkOrInterruptTransfer.TransferFlags =
Read ? USBD_TRANSFER_DIRECTION_IN : 0;
// short packet is not treated as an error.
urb->UrbBulkOrInterruptTransfer.TransferFlags |=
USBD_SHORT_TRANSFER_OK;

//
// not using linked urb's
//
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;
}
}

(3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;(4)完成例程ReadIntUrbComplete的处理;A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);[cpp] view plaincopyKeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);
PushCircularBufferEntry(
&deviceExtension->InputBuffer,
&pIntUrbs->TransferBuffer[1],
pIntUrbs->TransferBuffer[0]);
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
PushCircularBufferEntry为自己封装的函数:把data内存中的len个数据拷到pBuffer中[cpp] view plaincopyNTSTATUS
PushCircularBufferEntry(
IN PCIRCULAR_BUFFER pBuffer,
IN PUCHAR data,
IN ULONG len)
{
ULONG NextPosition;
DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);
ASSERT(pBuffer);
ASSERT(pBuffer->Length);

if ((data == NULL) || (len == 0))
return STATUS_INVALID_PARAMETER;
do{
NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;
if (NextPosition == pBuffer->ReadPosition)
return STATUS_BUFFER_TOO_SMALL;
pBuffer->Buffer[pBuffer->WritePosition] = *data++;
pBuffer->DataLen++;
pBuffer->WritePosition = NextPosition;
}while(--len);

return STATUS_SUCCESS;
}
C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。C、1 把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁。[cpp] view plaincopyvoid
SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )
{
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION deviceExtension;

DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);
ASSERT(pIrp);
irpStack = IoGetCurrentIrpStackLocation(pIrp);
deviceExtension = irpStack->DeviceObject->DeviceExtension;
deviceExtension->CurrentWaitIrp = NULL;
/*
*All Cancel routines must follow these guidelines:
* 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock
* 2. ...
*/
IoReleaseCancelSpinLock(pIrp->CancelIrql);
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
DbgPrint("SerialCancelCurrentWait Exit\n");
}
C、2 如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。[cpp] view plaincopydeviceExtension->CurrentWaitIrp = NULL;
deviceExtension->HistoryMask &= ~events;
IoReleaseCancelSpinLock(OldIrql);
pIrp->IoStatus.Information = sizeof(ULONG);
pIrp->IoStatus.Status = ntStatus;
*((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;
IoCompleteRequest (pIrp,IO_NO_INCREMENT);
D、完成当前的读IRP;D、1 如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。[cpp] view plaincopyif(deviceExtension->CurrentReadIrp)
{
ULONG haveLen;
BOOLEAN returnWhatsPresent = FALSE;

if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&
( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&
( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)
)
{
returnWhatsPresent = TRUE;
}
ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );
ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);
KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);
haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);
if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))
{
ioLength = (ioLength < haveLen) ? ioLength : haveLen;
DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);
ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;
deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;
IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);
deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);
}
else
KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);
}
以上代码中MmGetSystemAddressForMdlSafe[cpp] view plaincopy// 函数说明:
// 此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射
// 参数:
// MemoryDescriptorList - 指向MDL的指针
// Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了
// 返回值:
// 返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.
// 与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.
// 版本说明:
// 此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.
#define MmGetSystemAddressForMdlSafe(MDL, PRIORITY) \
(((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \
MDL_SOURCE_IS_NONPAGED_POOL)) ? \
((MDL)->MappedSystemVa) : \
(MmMapLockedPagesSpecifyCache((MDL), \
KernelMode, \
MmCached, \
NULL, \
FALSE, \
(PRIORITY))))

第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:[cpp] view plaincopy// 定义一个指向这个I/O请求的内存描述符(MDL)的指针,
// 此域仅在I/O是“direct I/O"时被用
PMDL MdlAddress;

D、2 判断是否能接收一个新的IO请求如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。
D、3 循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?结束条件:直至设备不能接收一个新的IO请求或未连接、取消。所以处理当前读IRP是一直进行的。步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)[cpp] view plaincopyPrepareWriteIntUrb(
IN PDEVICE_OBJECT DeviceObject,
IN PUSBD_PIPE_INFORMATION PipeInfo
)
A、分配写的Irb 与 Urb空间。B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)应用程序的名字从当前IRP中的fileObject得到。[cpp] view plaincopyourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );

步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
[cpp] view plaincopyfor (i=0; i<interface->NumberOfPipes; i++) {

PipeInfo = &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION PipeInfo;

if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {

//
// found a match
//
USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));
fileObject->FsContext = PipeInfo;
ourPipeInfo->fPipeOpened = TRUE; // set flag for opened
ntStatus = STATUS_SUCCESS;

deviceExtension->OpenPipeCount++;

// try to power up device if its not in D0
actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );
break;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: