USB转串口驱动代码分析

简介: <span style="font-family: 'Microsoft YaHei'; font-size: 20px; line-height: 30px;"></span><br><br><h4 style="margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; li


1、USB插入时,创建设备

[plain]  view plain copy
  1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;  

步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

(1) IoCreateDevice系统API的原理为:

[plain]  view plain copy
  1. NTKERNELAPI  
  2. NTSTATUS  
  3. IoCreateDevice(  
  4.     IN PDRIVER_OBJECT DriverObject,  
  5.     IN ULONG DeviceExtensionSize,  
  6.     IN PUNICODE_STRING DeviceName OPTIONAL,  
  7.     IN DEVICE_TYPE DeviceType,  
  8.     IN ULONG DeviceCharacteristics,  
  9.     IN BOOLEAN Reserved,  
  10.     OUT PDEVICE_OBJECT *DeviceObject  
  11.     );  


在之前真实的USB驱动中我们是这样创建的:

[cpp]  view plain copy
  1. ntStatus = IoCreateDevice(  
  2.                     DriverObject,                   // our driver object  
  3.                     sizeof(DEVICE_EXTENSION),       // extension size for us  
  4.                     NULL,                           // name for this device  
  5.                     FILE_DEVICE_UNKNOWN,  
  6.                     FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics  
  7.                     FALSE,                          // Not exclusive  
  8.                     &deviceObject);                 // Our device object  


就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。

由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型

[cpp]  view plain copy
  1. ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),  
  2.                            &deviceObjName, FILE_DEVICE_SERIAL_PORT,  
  3.                            FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);  

(2)为自定义的扩展设备中的设备名字段指定设备名

[cpp]  view plain copy
  1. // deviceExtension->DeviceName为UNICODE_STRING类型  
  2. RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));  
  3. deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);  
  4. // Buffer重新分配  
  5. deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));  
  6. RtlZeroMemory(deviceExtension->DeviceName.Buffer,  
  7.         deviceObjName.Length+sizeof(WCHAR));  
  8. RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);  


(3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。

[cpp]  view plain copy
  1.  // this event is triggered when there is no pending io of any kind and device is removed  
  2. KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);  
  3.   
  4. // this event is triggered when self-requested power irps complete  
  5. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);  
  6.   
  7. // this event is triggered when there is no pending io  (pending io count == 1 )  
  8. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);  
  9.   
  10. // spinlock used to protect inc/dec iocount logic  
  11. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);  
  12.       
  13.     deviceExtension->BaudRate = 19200;  
  14. /* Set line control */  
  15. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;  
  16. deviceExtension->SerialLineControl.Parity = NO_PARITY;  
  17. deviceExtension->SerialLineControl.WordLength = 8;  
  18.   
  19. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;  
  20. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;  
  21.   
  22. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;  
  23. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;  
  24. deviceExtension->HandFlow.XoffLimit    = 300;  
  25. deviceExtension->HandFlow.XonLimit     = 100;  
  26. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);  
  27. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);  
  28. KeInitializeSpinLock(&deviceExtension->InputBufferLock);  
  29. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);  
  30. InitializeListHead(&deviceExtension->ReadQueue);  
  31. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);  
  32. InitializeListHead(&deviceExtension->WriteQueue);  
  33. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);  
  34. InitializeListHead(&deviceExtension->PurgeQueue);  
  35. KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);  


 

步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE

 设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,

在resume时不接收一个IRP_MN_START_DEVICE消息。

[cpp]  view plain copy
  1. // we support direct io for read/write  
  2.        //  
  3.        deviceObject->Flags |= DO_DIRECT_IO;  
  4.   
  5.   
  6.        //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE  
  7.        //during suspend and also not get an IRP_MN_START_DEVICE during resume.  
  8.        //This is neccesary because during the start device call,  
  9.        // the GetDescriptors() call  will be failed by the USB stack.  
  10.        deviceObject->Flags |= DO_POWER_PAGABLE;  

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

[cpp]  view plain copy
  1. deviceExtension->TopOfStackDeviceObject =  
  2.             IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);  


 

[cpp]  view plain copy
  1. status = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,  
  2.                                       NULL, &PDevExt->DeviceClassSymbolicName);  

步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别

 

 (1)建立IRP来产生一个发往FDO的内部查询请求;

[cpp]  view plain copy
  1. irp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);  


(2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,

eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities

[cpp]  view plain copy
  1. nextStack = IoGetNextIrpStackLocation(irp);  
  2. nextStack->MajorFunction= IRP_MJ_PNP;  
  3. nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;  


在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:

[cpp]  view plain copy
  1. #define IoGetNextIrpStackLocation( Irp ) (\  
  2.     (Irp)->Tail.Overlay.CurrentStackLocation - 1 )  

从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1


 

 (3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)

(4)把IRP发送下去,并等待完成;

 

[cpp]  view plain copy
  1. ntStatus = IoCallDriver(LowerDeviceObject,  
  2.                            irp);  
  3.   
  4.    USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));  
  5.   
  6.    if (ntStatus == STATUS_PENDING) {  
  7.       // wait for irp to complete  
  8.   
  9.       KeWaitForSingleObject(  
  10.            &event,  
  11.            Suspended,  
  12.            KernelMode,  
  13.            FALSE,  
  14.            NULL);  

当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。


步五、获得USB的版本信息;

直接调用系统API:

[cpp]  view plain copy
  1. USBD_GetUSBDIVersion(&versionInformation);  

 

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

[cpp]  view plain copy
  1. DriverObject->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 plain copy
  1. if ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {  
  2.       ntStatus = STATUS_DELETE_PENDING;  
  3.   
  4. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));  
  5.       goto done;  
  6.   }  

 

在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):

1) 设备已经被移除了,
2) 从来没有被启动过,,
3) 已经停止了,
4) 有一个移除的请求还没处理,
5) 有一个停止的请求还没处理。

[cpp]  view plain copy
  1. //flag set when processing IRP_MN_REMOVE_DEVICE  
  2.     if ( !deviceExtension->DeviceRemoved &&  
  3.          // device must be started( enabled )  
  4.          deviceExtension->DeviceStarted &&  
  5.          // flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE  
  6.          !deviceExtension->RemoveDeviceRequested &&  
  7.          // flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE  
  8.          !deviceExtension->StopDeviceRequested ){  
  9.             fCan = TRUE;  
  10.     }  

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


(1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;

(2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;

[cpp]  view plain copy
  1. irp = IoAllocateIrp(stackSize, FALSE);  
  2.         if(irp == NULL)   
  3.         {  
  4.                 return STATUS_INSUFFICIENT_RESOURCES;  
  5.         }  
  6.         urb = USB2COM_ExAllocatePool(NonPagedPool,   
  7.                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));  
  8.         if(urb == NULL)  
  9.         {  
  10.             IoFreeIrp(irp);  
  11.         return STATUS_INSUFFICIENT_RESOURCES;  
  12.         }  
  13.         deviceExtension->ReadIntUrbs[i].Irp = irp;  
  14.         deviceExtension->ReadIntUrbs[i].Urb = urb;  
  15.         deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;  
  16.         deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;  
  17.         InitIntUrb(urb,  
  18.                 PipeInfo->PipeHandle,  
  19.                 deviceExtension->ReadIntUrbs[i].TransferBuffer,  
  20.                 sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),  
  21.                 TRUE);  

 

InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:

[cpp]  view plain copy
  1. VOID  
  2. InitIntUrb(  
  3.     IN PURB urb,  
  4.     IN USBD_PIPE_HANDLE  PipeHandle,  
  5.     IN PUCHAR TransferBuffer,  
  6.     IN ULONG length,  
  7.     IN BOOLEAN Read  
  8.     )  
  9. {  
  10.     USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);  
  11.     if (urb) {  
  12.         RtlZeroMemory(urb, siz);  
  13.   
  14.         urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;  
  15.         urb->UrbBulkOrInterruptTransfer.Hdr.Function =  
  16.                     URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;  
  17.         urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;  
  18.         urb->UrbBulkOrInterruptTransfer.TransferFlags =  
  19.             Read ? USBD_TRANSFER_DIRECTION_IN : 0;  
  20.         // short packet is not treated as an error.  
  21.         urb->UrbBulkOrInterruptTransfer.TransferFlags |=   
  22.             USBD_SHORT_TRANSFER_OK;              
  23.                   
  24.         //  
  25.         // not using linked urb's  
  26.         //  
  27.         urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;  
  28.         urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;  
  29.         urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;  
  30.         urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;  
  31.     }  
  32. }  


(3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;

(4)完成例程ReadIntUrbComplete的处理;

A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;

B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);

[cpp]  view plain copy
  1. KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
  2.             PushCircularBufferEntry(  
  3.                 &deviceExtension->InputBuffer,  
  4.                 &pIntUrbs->TransferBuffer[1],  
  5.                 pIntUrbs->TransferBuffer[0]);  
  6.             KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  

PushCircularBufferEntry为自己封装的函数:

把data内存中的len个数据拷到pBuffer中

[cpp]  view plain copy
  1. NTSTATUS  
  2. PushCircularBufferEntry(  
  3.     IN PCIRCULAR_BUFFER pBuffer,  
  4.     IN PUCHAR data,  
  5.     IN ULONG len)  
  6. {  
  7.     ULONG NextPosition;  
  8.     DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);  
  9.     ASSERT(pBuffer);  
  10.     ASSERT(pBuffer->Length);  
  11.   
  12.     if ((data == NULL) || (len == 0))  
  13.         return STATUS_INVALID_PARAMETER;  
  14.     do{  
  15.         NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;  
  16.         if (NextPosition == pBuffer->ReadPosition)  
  17.             return STATUS_BUFFER_TOO_SMALL;  
  18.         pBuffer->Buffer[pBuffer->WritePosition] = *data++;  
  19.         pBuffer->DataLen++;  
  20.         pBuffer->WritePosition = NextPosition;  
  21.     }while(--len);  
  22.       
  23.     return STATUS_SUCCESS;  
  24. }  

C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。

C、1   把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。

注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁

[cpp]  view plain copy
  1. void  
  2. SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )  
  3. {  
  4.     PIO_STACK_LOCATION irpStack;  
  5.     PDEVICE_EXTENSION deviceExtension;  
  6.       
  7.     DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);  
  8.     ASSERT(pIrp);  
  9.     irpStack = IoGetCurrentIrpStackLocation(pIrp);  
  10.     deviceExtension = irpStack->DeviceObject->DeviceExtension;  
  11.     deviceExtension->CurrentWaitIrp = NULL;  
  12.     /* 
  13.     *All Cancel routines must follow these guidelines: 
  14.     * 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock 
  15.     * 2. ... 
  16.     */  
  17.     IoReleaseCancelSpinLock(pIrp->CancelIrql);  
  18.     pIrp->IoStatus.Status = STATUS_CANCELLED;  
  19.     pIrp->IoStatus.Information = 0;  
  20.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);  
  21.     DbgPrint("SerialCancelCurrentWait Exit\n");  
  22. }  

C、2    如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。

[cpp]  view plain copy
  1. deviceExtension->CurrentWaitIrp = NULL;  
  2. deviceExtension->HistoryMask &= ~events;  
  3. IoReleaseCancelSpinLock(OldIrql);  
  4. pIrp->IoStatus.Information = sizeof(ULONG);  
  5. pIrp->IoStatus.Status = ntStatus;  
  6. *((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;  
  7. IoCompleteRequest (pIrp,IO_NO_INCREMENT);  

D、完成当前的读IRP;

D、1   如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。

[cpp]  view plain copy
  1. if(deviceExtension->CurrentReadIrp)  
  2. {  
  3.     ULONG       haveLen;  
  4.     BOOLEAN     returnWhatsPresent = FALSE;  
  5.       
  6.     if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&  
  7.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&  
  8.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)  
  9.     )  
  10.     {  
  11.         returnWhatsPresent = TRUE;  
  12.     }  
  13.     ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );  
  14.     ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);  
  15.     KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
  16.     haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);  
  17.     if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))  
  18.     {  
  19.         ioLength = (ioLength < haveLen) ? ioLength : haveLen;  
  20.         DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);  
  21.         ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);  
  22.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
  23.         deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;  
  24.         deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;   
  25.         IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);  
  26.         deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);  
  27.     }  
  28.     else  
  29.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
  30. }  

以上代码中MmGetSystemAddressForMdlSafe

[cpp]  view plain copy
  1. // 函数说明:  
  2. //     此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射  
  3. // 参数:  
  4. //     MemoryDescriptorList - 指向MDL的指针  
  5. //     Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了  
  6. // 返回值:  
  7. //     返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.  
  8. //     与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.  
  9. // 版本说明:  
  10. //     此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.  
  11. #define MmGetSystemAddressForMdlSafe(MDL, PRIORITY)                    \  
  12.      (((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |                    \  
  13.                         MDL_SOURCE_IS_NONPAGED_POOL)) ?                \  
  14.                              ((MDL)->MappedSystemVa) :                 \  
  15.                              (MmMapLockedPagesSpecifyCache((MDL),      \  
  16.                                                            KernelMode, \  
  17.                                                            MmCached,   \  
  18.                                                            NULL,       \  
  19.                                                            FALSE,      \  
  20.                                                            (PRIORITY))))  


 

第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:

[cpp]  view plain copy
  1. // 定义一个指向这个I/O请求的内存描述符(MDL)的指针,  
  2. // 此域仅在I/O是“direct I/O"时被用  
  3. PMDL MdlAddress;  


D、2   判断是否能接收一个新的IO请求

如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。


D、3   循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?

结束条件:直至设备不能接收一个新的IO请求或未连接、取消。

所以处理当前读IRP是一直进行的。

步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)
[cpp]  view plain copy
  1. PrepareWriteIntUrb(  
  2.     IN PDEVICE_OBJECT DeviceObject,  
  3.     IN PUSBD_PIPE_INFORMATION PipeInfo  
  4.     )  

A、分配写的Irb 与 Urb空间。

B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。

步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)

应用程序的名字从当前IRP中的fileObject得到。

[cpp]  view plain copy
  1. ourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );  

步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
[cpp]  view plain copy
  1. for (i=0; i<interface->NumberOfPipes; i++) {  
  2.   
  3.         PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;  
  4.   
  5.         if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {  
  6.   
  7.             //  
  8.             // found a match  
  9.             //  
  10.             USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));  
  11.             fileObject->FsContext = PipeInfo;  
  12.             ourPipeInfo->fPipeOpened = TRUE; // set flag for opened  
  13.             ntStatus = STATUS_SUCCESS;  
  14.   
  15.             deviceExtension->OpenPipeCount++;  
  16.   
  17.             // try to power up device if its not in D0  
  18.             actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );  
  19.             break;  
  20.         }  
  21.     }  


 

1、USB插入时,创建设备

[plain]  view plain copy
  1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;  

步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

(1) IoCreateDevice系统API的原理为:

[plain]  view plain copy
  1. NTKERNELAPI  
  2. NTSTATUS  
  3. IoCreateDevice(  
  4.     IN PDRIVER_OBJECT DriverObject,  
  5.     IN ULONG DeviceExtensionSize,  
  6.     IN PUNICODE_STRING DeviceName OPTIONAL,  
  7.     IN DEVICE_TYPE DeviceType,  
  8.     IN ULONG DeviceCharacteristics,  
  9.     IN BOOLEAN Reserved,  
  10.     OUT PDEVICE_OBJECT *DeviceObject  
  11.     );  


在之前真实的USB驱动中我们是这样创建的:

[cpp]  view plain copy
  1. ntStatus = IoCreateDevice(  
  2.                     DriverObject,                   // our driver object  
  3.                     sizeof(DEVICE_EXTENSION),       // extension size for us  
  4.                     NULL,                           // name for this device  
  5.                     FILE_DEVICE_UNKNOWN,  
  6.                     FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics  
  7.                     FALSE,                          // Not exclusive  
  8.                     &deviceObject);                 // Our device object  


就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。

由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型

[cpp]  view plain copy
  1. ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),  
  2.                            &deviceObjName, FILE_DEVICE_SERIAL_PORT,  
  3.                            FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);  

(2)为自定义的扩展设备中的设备名字段指定设备名

[cpp]  view plain copy
  1. // deviceExtension->DeviceName为UNICODE_STRING类型  
  2. RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));  
  3. deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);  
  4. // Buffer重新分配  
  5. deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));  
  6. RtlZeroMemory(deviceExtension->DeviceName.Buffer,  
  7.         deviceObjName.Length+sizeof(WCHAR));  
  8. RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);  


(3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。

[cpp]  view plain copy
  1.  // this event is triggered when there is no pending io of any kind and device is removed  
  2. KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);  
  3.   
  4. // this event is triggered when self-requested power irps complete  
  5. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);  
  6.   
  7. // this event is triggered when there is no pending io  (pending io count == 1 )  
  8. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);  
  9.   
  10. // spinlock used to protect inc/dec iocount logic  
  11. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);  
  12.       
  13.     deviceExtension->BaudRate = 19200;  
  14. /* Set line control */  
  15. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;  
  16. deviceExtension->SerialLineControl.Parity = NO_PARITY;  
  17. deviceExtension->SerialLineControl.WordLength = 8;  
  18.   
  19. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;  
  20. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;  
  21.   
  22. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;  
  23. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;  
  24. deviceExtension->HandFlow.XoffLimit    = 300;  
  25. deviceExtension->HandFlow.XonLimit     = 100;  
  26. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);  
  27. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);  
  28. KeInitializeSpinLock(&deviceExtension->InputBufferLock);  
  29. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);  
  30. InitializeListHead(&deviceExtension->ReadQueue);  
  31. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);  
  32. InitializeListHead(&deviceExtension->WriteQueue);  
  33. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);  
  34. InitializeListHead(&deviceExtension->PurgeQueue);  
  35. KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);  


 

步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE

 设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,

在resume时不接收一个IRP_MN_START_DEVICE消息。

[cpp]  view plain copy
  1. // we support direct io for read/write  
  2.        //  
  3.        deviceObject->Flags |= DO_DIRECT_IO;  
  4.   
  5.   
  6.        //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE  
  7.        //during suspend and also not get an IRP_MN_START_DEVICE during resume.  
  8.        //This is neccesary because during the start device call,  
  9.        // the GetDescriptors() call  will be failed by the USB stack.  
  10.        deviceObject->Flags |= DO_POWER_PAGABLE;  

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

[cpp]  view plain copy
  1. deviceExtension->TopOfStackDeviceObject =  
  2.             IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);  


 

[cpp]  view plain copy
  1. status = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,  
  2.                                       NULL, &PDevExt->DeviceClassSymbolicName);  

步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别

 

 (1)建立IRP来产生一个发往FDO的内部查询请求;

[cpp]  view plain copy
  1. irp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);  


(2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,

eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities

[cpp]  view plain copy
  1. nextStack = IoGetNextIrpStackLocation(irp);  
  2. nextStack->MajorFunction= IRP_MJ_PNP;  
  3. nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;  


在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:

[cpp]  view plain copy
  1. #define IoGetNextIrpStackLocation( Irp ) (\  
  2.     (Irp)->Tail.Overlay.CurrentStackLocation - 1 )  

从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1


 

 (3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)

(4)把IRP发送下去,并等待完成;

 

[cpp]  view plain copy
  1. ntStatus = IoCallDriver(LowerDeviceObject,  
  2.                            irp);  
  3.   
  4.    USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));  
  5.   
  6.    if (ntStatus == STATUS_PENDING) {  
  7.       // wait for irp to complete  
  8.   
  9.       KeWaitForSingleObject(  
  10.            &event,  
  11.            Suspended,  
  12.            KernelMode,  
  13.            FALSE,  
  14.            NULL);  

当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。


步五、获得USB的版本信息;

直接调用系统API:

[cpp]  view plain copy
  1. USBD_GetUSBDIVersion(&versionInformation);  

 

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

[cpp]  view plain copy
  1. DriverObject->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 plain copy
  1. if ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {  
  2.       ntStatus = STATUS_DELETE_PENDING;  
  3.   
  4. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));  
  5.       goto done;  
  6.   }  

 

在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):

1) 设备已经被移除了,
2) 从来没有被启动过,,
3) 已经停止了,
4) 有一个移除的请求还没处理,
5) 有一个停止的请求还没处理。

[cpp]  view plain copy
  1. //flag set when processing IRP_MN_REMOVE_DEVICE  
  2.     if ( !deviceExtension->DeviceRemoved &&  
  3.          // device must be started( enabled )  
  4.          deviceExtension->DeviceStarted &&  
  5.          // flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE  
  6.          !deviceExtension->RemoveDeviceRequested &&  
  7.          // flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE  
  8.          !deviceExtension->StopDeviceRequested ){  
  9.             fCan = TRUE;  
  10.     }  

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


(1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;

(2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;

[cpp]  view plain copy
  1. irp = IoAllocateIrp(stackSize, FALSE);  
  2.         if(irp == NULL)   
  3.         {  
  4.                 return STATUS_INSUFFICIENT_RESOURCES;  
  5.         }  
  6.         urb = USB2COM_ExAllocatePool(NonPagedPool,   
  7.                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));  
  8.         if(urb == NULL)  
  9.         {  
  10.             IoFreeIrp(irp);  
  11.         return STATUS_INSUFFICIENT_RESOURCES;  
  12.         }  
  13.         deviceExtension->ReadIntUrbs[i].Irp = irp;  
  14.         deviceExtension->ReadIntUrbs[i].Urb = urb;  
  15.         deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;  
  16.         deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;  
  17.         InitIntUrb(urb,  
  18.                 PipeInfo->PipeHandle,  
  19.                 deviceExtension->ReadIntUrbs[i].TransferBuffer,  
  20.                 sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),  
  21.                 TRUE);  

 

InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:

[cpp]  view plain copy
  1. VOID  
  2. InitIntUrb(  
  3.     IN PURB urb,  
  4.     IN USBD_PIPE_HANDLE  PipeHandle,  
  5.     IN PUCHAR TransferBuffer,  
  6.     IN ULONG length,  
  7.     IN BOOLEAN Read  
  8.     )  
  9. {  
  10.     USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);  
  11.     if (urb) {  
  12.         RtlZeroMemory(urb, siz);  
  13.   
  14.         urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;  
  15.         urb->UrbBulkOrInterruptTransfer.Hdr.Function =  
  16.                     URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;  
  17.         urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;  
  18.         urb->UrbBulkOrInterruptTransfer.TransferFlags =  
  19.             Read ? USBD_TRANSFER_DIRECTION_IN : 0;  
  20.         // short packet is not treated as an error.  
  21.         urb->UrbBulkOrInterruptTransfer.TransferFlags |=   
  22.             USBD_SHORT_TRANSFER_OK;              
  23.                   
  24.         //  
  25.         // not using linked urb's  
  26.         //  
  27.         urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;  
  28.         urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;  
  29.         urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;  
  30.         urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;  
  31.     }  
  32. }  


(3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;

(4)完成例程ReadIntUrbComplete的处理;

A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;

B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);

[cpp]  view plain copy
  1. KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
  2.             PushCircularBufferEntry(  
  3.                 &deviceExtension->InputBuffer,  
  4.                 &pIntUrbs->TransferBuffer[1],  
  5.                 pIntUrbs->TransferBuffer[0]);  
  6.             KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  

PushCircularBufferEntry为自己封装的函数:

把data内存中的len个数据拷到pBuffer中

[cpp]  view plain copy
  1. NTSTATUS  
  2. PushCircularBufferEntry(  
  3.     IN PCIRCULAR_BUFFER pBuffer,  
  4.     IN PUCHAR data,  
  5.     IN ULONG len)  
  6. {  
  7.     ULONG NextPosition;  
  8.     DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);  
  9.     ASSERT(pBuffer);  
  10.     ASSERT(pBuffer->Length);  
  11.   
  12.     if ((data == NULL) || (len == 0))  
  13.         return STATUS_INVALID_PARAMETER;  
  14.     do{  
  15.         NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;  
  16.         if (NextPosition == pBuffer->ReadPosition)  
  17.             return STATUS_BUFFER_TOO_SMALL;  
  18.         pBuffer->Buffer[pBuffer->WritePosition] = *data++;  
  19.         pBuffer->DataLen++;  
  20.         pBuffer->WritePosition = NextPosition;  
  21.     }while(--len);  
  22.       
  23.     return STATUS_SUCCESS;  
  24. }  

C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。

C、1   把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。

注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁

[cpp]  view plain copy
  1. void  
  2. SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )  
  3. {  
  4.     PIO_STACK_LOCATION irpStack;  
  5.     PDEVICE_EXTENSION deviceExtension;  
  6.       
  7.     DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);  
  8.     ASSERT(pIrp);  
  9.     irpStack = IoGetCurrentIrpStackLocation(pIrp);  
  10.     deviceExtension = irpStack->DeviceObject->DeviceExtension;  
  11.     deviceExtension->CurrentWaitIrp = NULL;  
  12.     /* 
  13.     *All Cancel routines must follow these guidelines: 
  14.     * 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock 
  15.     * 2. ... 
  16.     */  
  17.     IoReleaseCancelSpinLock(pIrp->CancelIrql);  
  18.     pIrp->IoStatus.Status = STATUS_CANCELLED;  
  19.     pIrp->IoStatus.Information = 0;  
  20.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);  
  21.     DbgPrint("SerialCancelCurrentWait Exit\n");  
  22. }  

C、2    如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。

[cpp]  view plain copy
  1. deviceExtension->CurrentWaitIrp = NULL;  
  2. deviceExtension->HistoryMask &= ~events;  
  3. IoReleaseCancelSpinLock(OldIrql);  
  4. pIrp->IoStatus.Information = sizeof(ULONG);  
  5. pIrp->IoStatus.Status = ntStatus;  
  6. *((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;  
  7. IoCompleteRequest (pIrp,IO_NO_INCREMENT);  

D、完成当前的读IRP;

D、1   如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。

[cpp]  view plain copy
  1. if(deviceExtension->CurrentReadIrp)  
  2. {  
  3.     ULONG       haveLen;  
  4.     BOOLEAN     returnWhatsPresent = FALSE;  
  5.       
  6.     if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&  
  7.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&  
  8.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)  
  9.     )  
  10.     {  
  11.         returnWhatsPresent = TRUE;  
  12.     }  
  13.     ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );  
  14.     ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);  
  15.     KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
  16.     haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);  
  17.     if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))  
  18.     {  
  19.         ioLength = (ioLength < haveLen) ? ioLength : haveLen;  
  20.         DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);  
  21.         ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);  
  22.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
  23.         deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;  
  24.         deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;   
  25.         IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);  
  26.         deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);  
  27.     }  
  28.     else  
  29.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
  30. }  

以上代码中MmGetSystemAddressForMdlSafe

[cpp]  view plain copy
  1. // 函数说明:  
  2. //     此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射  
  3. // 参数:  
  4. //     MemoryDescriptorList - 指向MDL的指针  
  5. //     Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了  
  6. // 返回值:  
  7. //     返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.  
  8. //     与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.  
  9. // 版本说明:  
  10. //     此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.  
  11. #define MmGetSystemAddressForMdlSafe(MDL, PRIORITY)                    \  
  12.      (((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |                    \  
  13.                         MDL_SOURCE_IS_NONPAGED_POOL)) ?                \  
  14.                              ((MDL)->MappedSystemVa) :                 \  
  15.                              (MmMapLockedPagesSpecifyCache((MDL),      \  
  16.                                                            KernelMode, \  
  17.                                                            MmCached,   \  
  18.                                                            NULL,       \  
  19.                                                            FALSE,      \  
  20.                                                            (PRIORITY))))  


 

第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:

[cpp]  view plain copy
  1. // 定义一个指向这个I/O请求的内存描述符(MDL)的指针,  
  2. // 此域仅在I/O是“direct I/O"时被用  
  3. PMDL MdlAddress;  


D、2   判断是否能接收一个新的IO请求

如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。


D、3   循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?

结束条件:直至设备不能接收一个新的IO请求或未连接、取消。

所以处理当前读IRP是一直进行的。

步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)
[cpp]  view plain copy
  1. PrepareWriteIntUrb(  
  2.     IN PDEVICE_OBJECT DeviceObject,  
  3.     IN PUSBD_PIPE_INFORMATION PipeInfo  
  4.     )  

A、分配写的Irb 与 Urb空间。

B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。

步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)

应用程序的名字从当前IRP中的fileObject得到。

[cpp]  view plain copy
  1. ourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );  

步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
[cpp]  view plain copy
  1. for (i=0; i<interface->NumberOfPipes; i++) {  
  2.   
  3.         PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;  
  4.   
  5.         if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {  
  6.   
  7.             //  
  8.             // found a match  
  9.             //  
  10.             USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));  
  11.             fileObject->FsContext = PipeInfo;  
  12.             ourPipeInfo->fPipeOpened = TRUE; // set flag for opened  
  13.             ntStatus = STATUS_SUCCESS;  
  14.   
  15.             deviceExtension->OpenPipeCount++;  
  16.   
  17.             // try to power up device if its not in D0  
  18.             actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );  
  19.             break;  
  20.         }  
  21.     }  


 

相关文章
|
1月前
嵌入式开发板串口驱动框架
嵌入式开发板串口驱动框架
13 0
|
11月前
|
XML 测试技术 网络安全
开发工具:USB转IIC/I2C/SPI/UART适配器模块可编程开发板
总的思路是通过USB或者UART接口发送一些协议字符串,由模块转换成上面几种接口的硬件时序电信号,实现与这几种接口芯片、设备的快速测试。 首先声明一下,大家都是搞硬件开发的,这几种接口当然是很简单的事,但有些时候对于一个新的设备或者芯片的测试,有个现成的工具当然更顺手,节省时间,也更可靠嘛。
|
11月前
|
XML 测试技术 网络安全
开发调试工具:可编程USB转IIC/I2C/SPI/UART适配器模块开发板
发个方便测试I2C、SPI、1Wire接口的工具模块 总的思路是通过USB或者UART接口发送一些协议字符串,由模块转换成上面几种接口的硬件时序电信号,实现与这几种接口芯片、设备的快速测试。
|
11月前
|
缓存 Linux 芯片
Linux驱动分析之Uart驱动
之前对Uart驱动的整体架构做了介绍,现在来分析具体的驱动程序。我们以NXP 的 IMX6来进行分析。
|
12月前
|
XML 测试技术 网络安全
开发调试工具:可编程 USB 转串口适配器开发板
首先声明一下,大家都是搞硬件开发的,这几种接口当然是很简单的事,但有些时候对于一个新的设备或者芯片的测试,有个现成的工具当然更顺手,节省时间,也更可靠嘛。
|
Ubuntu 开发工具 虚拟化
UART子系统(十六)编写虚拟UART驱动程序_调试
UART子系统(十六)编写虚拟UART驱动程序_调试
229 0
UART子系统(十六)编写虚拟UART驱动程序_调试
可编程USB 转串口适配器开发板的S2S功能
USB2S 支持基于 STC 单片机的二次开发,若有需要,可参照原理图和单片机型号手册自行开发具有特殊功能的固件程序。
可编程USB 转串口适配器开发板的S2S功能
|
传感器 测试技术 芯片
可编程 USB 转串口适配器开发板连接计算机使用S2STool工具
S2STool 是为 S2S 固件开发的测试工具,运行于 Windows 平台,内置串口调试助手和 S2S 参数配置工具,支持动态解析自定义的芯片驱动,用户可自行编写、修改、增加任何芯片的驱动程序, 方便对芯片进行测试和评估。
可编程 USB 转串口适配器开发板连接计算机使用S2STool工具
可编程 USB 转串口适配器开发板常见问题及注意事项
当使用导线连接外部设备或芯片时,导线不可过长,一般控制在 20CM 以内,IIC、SPI、UART 等数字接口数据线驱动能力有限,过长的导线会导致通讯波形迟缓。当导线确实无法缩短时,可通过降低通讯速率的方法来解决、缓解通讯异常问题。
可编程 USB 转串口适配器开发板常见问题及注意事项
|
XML 传感器 测试技术
开发调试工具:USB转IIC/I2C/SPI/UART适配器模块可编程开发板
发个方便测试I2C、SPI、1Wire接口的工具模块 总的思路是通过USB或者UART接口发送一些协议字符串,由模块转换成上面几种接口的硬件时序电信号,实现与这几种接口芯片、设备的快速测试。 首先声明一下,大家都是搞硬件开发的,这几种接口当然是很简单的事,但有些时候对于一个新的设备或者芯片的测试,有个现成的工具当然更顺手,节省时间,也更可靠嘛。
开发调试工具:USB转IIC/I2C/SPI/UART适配器模块可编程开发板