参考书籍<<Windows驱动开发技术详解>>

1、该示例介绍如何进行USB驱动的开发。

它全面地支持了即插即用的处理,

也很全面地支持了电源管理,同时很好地支持了USB设备的bulk读写.

如果从头开发 USB 驱动,往往很难达到USB驱动的稳定性,所以建议在此驱动修改基础上进行USB驱动开发。

2、功能驱动与物理总线驱动

程序员不需要了解USB如何将请求化成数据包等细节,只需要指定何种管道,发送何种数据即可。

当功能驱动想向某个管道发出读写请求时,首先构造请求发给USB总线驱动。这种请求是标准的USB请求,称为URB(USB Request Block)。

它被USB总线驱动所解释,进而转化成请求发给HOST驱动或者USB HUB驱动。

3、实现过程

3、1 构造USB请求包

USB驱动与USB设备通信时,如在控制管道中获取设备描述符、配置描述符、端点描述符、或在Bulk管道中获取大量数据,

都是通过创建USB请求包(URB)来完成的。URB中填充需要对USB的请求,然后将URB作为IRP一个参数传递给底层的USB总线驱动。

3、1、1  在DriverEntry中给了对应IRP_MJ_PNP的回调函数

DriverObject->MajorFunction[IRP_MJ_PNP]            = BulkUsb_DispatchPnP;

3、1、2   BulkUsb_DispatchPnP的实现

BulkUsb_DispatchPnP为即插即用分发函数.

在 case IRP_MN_START_DEVICE时有处理函数ntStatus = HandleStartDevice(DeviceObject, Irp);

[cpp] view plaincopy
  1. NTSTATUS
  2. BulkUsb_DispatchPnP(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP           Irp
  5. )
  6. /*++
  7. Routine Description:
  8. 即插即用分发函数.
  9. Most of these requests the driver will completely ignore.
  10. In all cases it must pass on the IRP to the lower driver.
  11. Arguments:
  12. DeviceObject - pointer to a device object.
  13. Irp - 指向一个I/O请求包
  14. Return Value:
  15. NT status value
  16. --*/
  17. {
  18. PIO_STACK_LOCATION irpStack;
  19. PDEVICE_EXTENSION  deviceExtension;
  20. KEVENT             startDeviceEvent;
  21. NTSTATUS           ntStatus;
  22. //
  23. // 初始化变量
  24. //
  25. irpStack = IoGetCurrentIrpStackLocation(Irp);
  26. deviceExtension = DeviceObject->DeviceExtension;
  27. //
  28. // 如查它被移除,那么Irp失败
  29. if(Removed == deviceExtension->DeviceState)
  30. {
  31. ntStatus = STATUS_DELETE_PENDING;
  32. Irp->IoStatus.Status = ntStatus;
  33. Irp->IoStatus.Information = 0;
  34. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  35. return ntStatus;
  36. }
  37. BulkUsb_DbgPrint(3, ("///\n"));
  38. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::"));
  39. // 提升I/O计数.
  40. BulkUsb_IoIncrement(deviceExtension);
  41. if(irpStack->MinorFunction == IRP_MN_START_DEVICE)
  42. {
  43. ASSERT(deviceExtension->IdleReqPend == 0);
  44. }
  45. else
  46. {
  47. if(deviceExtension->SSEnable) {
  48. CancelSelectSuspend(deviceExtension);
  49. }
  50. }
  51. BulkUsb_DbgPrint(2, (PnPMinorFunctionString(irpStack->MinorFunction)));
  52. switch(irpStack->MinorFunction)
  53. {
  54. case IRP_MN_START_DEVICE:
  55. ntStatus = HandleStartDevice(DeviceObject, Irp);
  56. break;
  57. case IRP_MN_QUERY_STOP_DEVICE:
  58. //
  59. // if we cannot stop the device, we fail the query stop irp
  60. //
  61. ntStatus = CanStopDevice(DeviceObject, Irp);
  62. if(NT_SUCCESS(ntStatus)) {
  63. ntStatus = HandleQueryStopDevice(DeviceObject, Irp);
  64. return ntStatus;
  65. }
  66. break;
  67. case IRP_MN_CANCEL_STOP_DEVICE:
  68. ntStatus = HandleCancelStopDevice(DeviceObject, Irp);
  69. break;
  70. case IRP_MN_STOP_DEVICE:
  71. ntStatus = HandleStopDevice(DeviceObject, Irp);
  72. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::IRP_MN_STOP_DEVICE::"));
  73. BulkUsb_IoDecrement(deviceExtension);
  74. return ntStatus;
  75. case IRP_MN_QUERY_REMOVE_DEVICE:
  76. //
  77. // if we cannot remove the device, we fail the query remove irp
  78. //
  79. ntStatus = HandleQueryRemoveDevice(DeviceObject, Irp);
  80. return ntStatus;
  81. case IRP_MN_CANCEL_REMOVE_DEVICE:
  82. ntStatus = HandleCancelRemoveDevice(DeviceObject, Irp);
  83. break;
  84. case IRP_MN_SURPRISE_REMOVAL:
  85. ntStatus = HandleSurpriseRemoval(DeviceObject, Irp);
  86. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::IRP_MN_SURPRISE_REMOVAL::"));
  87. BulkUsb_IoDecrement(deviceExtension);
  88. return ntStatus;
  89. case IRP_MN_REMOVE_DEVICE:
  90. ntStatus = HandleRemoveDevice(DeviceObject, Irp);
  91. return ntStatus;
  92. case IRP_MN_QUERY_CAPABILITIES:
  93. ntStatus = HandleQueryCapabilities(DeviceObject, Irp);
  94. break;
  95. default:
  96. IoSkipCurrentIrpStackLocation(Irp);
  97. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
  98. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::default::"));
  99. BulkUsb_IoDecrement(deviceExtension);
  100. return ntStatus;
  101. } // switch
  102. //
  103. // complete request
  104. //
  105. Irp->IoStatus.Status = ntStatus;
  106. Irp->IoStatus.Information = 0;
  107. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  108. //
  109. // decrement count
  110. //
  111. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::"));
  112. BulkUsb_IoDecrement(deviceExtension);
  113. return ntStatus;
  114. }

(1)一开始判断当前设备的状态,如果为Removed那么IRP失败,直接返回。

这里讲一下设备的状态的维护方法:

(1、1)在设备扩展中留出两字段,分别存当前状态和之前状态。

[cpp] view plaincopy
  1. // current state of device
  2. DEVSTATE DeviceState;
  3. // state prior to removal query
  4. DEVSTATE PrevDevState;

(1、2)在初始化时

(1、3)在改变时

通过以下宏来改变状态

[cpp] view plaincopy
  1. #define SET_NEW_PNP_STATE(_Data_, _state_) \
  2. (_Data_)->PrevDevState =  (_Data_)->DeviceState;\
  3. (_Data_)->DeviceState = (_state_);

需更新状态信息的有以下几个地方:

A、BulkUsb_DispatchPnP里case IRP_MN_START_DEVICE时,在选择设备描述符和选择端点完成后设置为Working;

[cpp] view plaincopy
  1. //
  2. // 读设备描述符,配置描述符并选择接口描述符
  3. //
  4. ntStatus = ReadandSelectDescriptors(DeviceObject);
  5. if(!NT_SUCCESS(ntStatus)) {
  6. BulkUsb_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
  7. return ntStatus;
  8. }
  9. //
  10. // 为系统组件去打开设备句柄而使能象征链
  11. ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName,
  12. TRUE);

B、BulkUsb_DispatchPnP里case IRP_MN_QUERY_STOP_DEVICE时,设置为PendingStop;

C、BulkUsb_DispatchPnP里case IRP_MN_STOP_DEVICE时,在取消定时器这样DPCs就不再激活后等待两个事件激活后设置为PendingStop;

[cpp] view plaincopy
  1. KeCancelTimer(&deviceExtension->Timer);
  2. //
  3. // 在设备停止后,它能被强拨了.
  4. // 我们设置它为0,这样我们在强拨或移除Irps时不再偿试去取消定时器 .
  5. // 当我们再获得设备请求时,此标志会再被初始化
  6. //
  7. deviceExtension->SSEnable = 0;
  8. //
  9. // make sure that if a DPC was fired before we called cancel timer,
  10. // then the DPC and work-time have run to their completion
  11. //
  12. KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent,
  13. Executive,
  14. KernelMode,
  15. FALSE,
  16. NULL);
  17. //
  18. // make sure that the selective suspend request has been completed.
  19. //
  20. KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
  21. Executive,
  22. KernelMode,
  23. FALSE,
  24. NULL);

D、BulkUsb_DispatchPnP里case IRP_MN_QUERY_REMOVE_DEVICE时,设置为PendingRemove;

E、BulkUsb_DispatchPnP里case IRP_MN_SURPRISE_REMOVAL时,设置为SurpriseRemoved;

对IRP_MN_SURPRISE_REMOVAL的处理与对IRP_MN_STOP_DEVICE的处理是很相似的,

只不过前者情况下认为所有请求失败,处理了队列里的所有请求,且设置设备的Interface状态为失败,中止了所有Pipe的使用:

[cpp] view plaincopy
  1. ProcessQueuedRequests(deviceExtension);
  2. ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName,
  3. FALSE);
  4. if(!NT_SUCCESS(ntStatus)) {
  5. BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState::disable:failed\n"));
  6. }
  7. BulkUsb_AbortPipes(DeviceObject);

而后者只是反配置了设备:

[cpp] view plaincopy
  1. ReleaseMemory(DeviceObject);
  2. ntStatus = DeconfigureDevice(DeviceObject);

F、BulkUsb_DispatchPnP里case IRP_MN_REMOVE_DEVICE时,设置为Removed;
在这个情况下做以下事情:反注册USB插入时创建的设备对象,把此USB的FDO从设备栈中移除;
如果之前的设备状态不是SurpriseRemoved,那么要做IRP_MN_SURPRISE_REMOVAL、IRP_MN_STOP_DEVICE两个case时做的所有事情。

3、1、3   HandleStartDevice的实现

它调用ReadandSelectDescriptors来读取设备描述符

[cpp] view plaincopy
  1. NTSTATUS
  2. HandleStartDevice(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP              Irp
  5. )
  6. /*++
  7. Routine Description:
  8. IRP_MN_START_DEVICE对应的处理函数
  9. Arguments:
  10. DeviceObject - pointer to a device object.
  11. Irp - I/O request packet
  12. Return Value:
  13. NT status value
  14. --*/
  15. {
  16. KIRQL             oldIrql;
  17. KEVENT            startDeviceEvent;
  18. NTSTATUS          ntStatus;
  19. PDEVICE_EXTENSION deviceExtension;
  20. LARGE_INTEGER     dueTime;
  21. BulkUsb_DbgPrint(3, ("HandleStartDevice - begins\n"));
  22. //
  23. // 初始化变量
  24. //
  25. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  26. deviceExtension->UsbConfigurationDescriptor = NULL;
  27. deviceExtension->UsbInterface = NULL;
  28. deviceExtension->PipeContext = NULL;
  29. //
  30. // 我们不能触摸设备 (给它发送任何非pnp 的 irps) until a
  31. // start device has been passed down to the lower drivers.
  32. // first pass the Irp down
  33. //
  34. KeInitializeEvent(&startDeviceEvent, NotificationEvent, FALSE);
  35. IoCopyCurrentIrpStackLocationToNext(Irp);
  36. IoSetCompletionRoutine(Irp,
  37. (PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
  38. (PVOID)&startDeviceEvent,
  39. TRUE,
  40. TRUE,
  41. TRUE);
  42. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
  43. if(ntStatus == STATUS_PENDING) {
  44. KeWaitForSingleObject(&startDeviceEvent,
  45. Executive,
  46. KernelMode,
  47. FALSE,
  48. NULL);
  49. ntStatus = Irp->IoStatus.Status;
  50. }
  51. if(!NT_SUCCESS(ntStatus)) {
  52. BulkUsb_DbgPrint(1, ("Lower drivers failed this Irp\n"));
  53. return ntStatus;
  54. }
  55. //
  56. // 读设备描述符,配置描述符并选择接口描述符
  57. //
  58. ntStatus = ReadandSelectDescriptors(DeviceObject);
  59. if(!NT_SUCCESS(ntStatus)) {
  60. BulkUsb_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
  61. return ntStatus;
  62. }
  63. //
  64. // 为系统组件去打开设备句柄而使能象征链
  65. ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName,
  66. TRUE);
  67. if(!NT_SUCCESS(ntStatus)) {
  68. BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState:enable:failed\n"));
  69. return ntStatus;
  70. }
  71. KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
  72. SET_NEW_PNP_STATE(deviceExtension, Working);
  73. deviceExtension->QueueState = AllowRequests;
  74. KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
  75. //
  76. // 初始化等待唤醒的标志到false  false.
  77. // and issue a wait wake.
  78. deviceExtension->FlagWWOutstanding = 0;
  79. deviceExtension->FlagWWCancel = 0;
  80. deviceExtension->WaitWakeIrp = NULL;
  81. if(deviceExtension->WaitWakeEnable) {
  82. IssueWaitWake(deviceExtension);
  83. }
  84. ProcessQueuedRequests(deviceExtension);
  85. if(WinXpOrBetter == deviceExtension->WdmVersion)
  86. {
  87. deviceExtension->SSEnable = deviceExtension->SSRegistryEnable;
  88. //
  89. // set timer.for selective suspend requests
  90. //
  91. if(deviceExtension->SSEnable) {
  92. dueTime.QuadPart = -10000 * IDLE_INTERVAL;               // 5000 ms
  93. KeSetTimerEx(&deviceExtension->Timer,
  94. dueTime,
  95. IDLE_INTERVAL,                              // 5000 ms
  96. &deviceExtension->DeferredProcCall);
  97. deviceExtension->FreeIdleIrpCount = 0;
  98. }
  99. }
  100. BulkUsb_DbgPrint(3, ("HandleStartDevice - ends\n"));
  101. return ntStatus;
  102. }

(1) IoCopyCurrentIrpStackLocationToNext介绍:

在下发IRP到底层驱动处理前,本层驱动必须负责设置下层IO堆栈的内容。这样下一层驱动调用IoGetCurrentIrpStackLocation()时能得到相应的数据。
设置下层IO堆栈的内容,一般用两个函数来实现:
IoCopyCurrentIrpStackLocationToNext( Irp )
此函数一般用在本驱动设置了完成例程时调用,把本层IO _STACK_LOCATION 中的参数copy到下层,但与完成例程相关的参数信息例外。因为本驱动设置的完成例程只对本层驱动有效。
IoSkipCurrentIrpStackLocationToNext(Irp)
此函数的作用是:直接把本层驱动IO堆栈的内容设置为下层驱动IO堆栈指针的指向。因两层驱动IO堆栈的内容完全一致,省却copy过程。
(2)根据以上知识点,我们开始分析HandleStartDevice函数的实现过程:
(2、1)把本层IO _STACK_LOCATION 中的参数copy到下层,设置完成例程,等待完成。
[cpp] view plaincopy
  1. KeInitializeEvent(&startDeviceEvent, NotificationEvent, FALSE);
  2. IoCopyCurrentIrpStackLocationToNext(Irp);
  3. IoSetCompletionRoutine(Irp,
  4. (PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
  5. (PVOID)&startDeviceEvent,
  6. TRUE,
  7. TRUE,
  8. TRUE);
  9. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
  10. if(ntStatus == STATUS_PENDING) {
  11. KeWaitForSingleObject(&startDeviceEvent,
  12. Executive,
  13. KernelMode,
  14. FALSE,
  15. NULL);
  16. ntStatus = Irp->IoStatus.Status;
  17. }

(2、2)读设备描述符,配置描述符并选择接口描述符(重点,参见3、1、4 );
(2、3)开启定时器(5S间隔);

[cpp] view plaincopy
  1. if(DeviceExtension->SSEnable) {
  2. BulkUsb_DbgPrint(3, ("Set the timer to fire DPCs\n"));
  3. dueTime.QuadPart = -10000 * IDLE_INTERVAL;               // 5000 ms
  4. KeSetTimerEx(&DeviceExtension->Timer,
  5. dueTime,
  6. IDLE_INTERVAL,                              // 5000 ms
  7. &DeviceExtension->DeferredProcCall);
  8. BulkUsb_DbgPrint(3, ("IdleNotificationRequestCompete - ends\n"));
  9. }

这里补充定时器的使用:

本实例中用到的定时器是为了与DCP协调使用,这体现在初始化时,如下面的 A、

A、初始化定时器

在系统扩展中的变量DriverObject->DriverExtension->AddDevice指定的处理函数 BulkUsb_AddDevice中初始化定时器:

[cpp] view plaincopy
  1. //
  2. // initialize DPC
  3. //
  4. KeInitializeDpc(&deviceExtension->DeferredProcCall,
  5. DpcRoutine,
  6. deviceObject);
  7. //
  8. // initialize the timer.
  9. // the DPC and the timer in conjunction,
  10. // monitor the state of the device to
  11. // selectively suspend the device.
  12. //
  13. KeInitializeTimerEx(&deviceExtension->Timer,
  14. NotificationTimer);

B、启动定时器;

它发生在两个地方:HandleStartDevice 和 SubmitIdleRequestIrp 函数设备的完成例程中。

C、关闭定时器;

它发生在三个地方:HandleStopDevice、HandleSurpriseRemoval、HandleRemoveDevice三个地方。

定时器每5S后触发的事件的目的是:

检查设备的idle状态并为设备提交一个idle请求。

步一:检查是设备是否是suspend状态。判断方法如下:

[cpp] view plaincopy
  1. if((DeviceExtension->OpenHandleCount == 0) &&
  2. (DeviceExtension->OutStandingIO == 1)) {
  3. return TRUE;
  4. }
  5. else {
  6. return FALSE;
  7. }

OpenHandleCount 、OutStandingIO都是LONG类型,它们通过InterlockedIncrement、InterlockedDecrement来自增1与自减1;

我们可以发现在进入每个派遣函数时,几乎都有对OutStandingIO加1,然后结束时减1,而刚开始时是1。根据条件判断,当在派遣函数中时此时是不能置为设备SUSPEND状态的。

OpenHandleCount作为打开设备的计数,在AddDevice时是0,在Create后是加了1,这样就是设备还没有Create时是可Idle的状态。

以上讲到的是是USB开发中用到的同步机制,它的思想与COM的计数思想是类似的。

步二:首先调用IoAllocateWorkItem函数为work item分配内存,然后调用IoQueueWorkItem函数将任务放置到工作线程队列中;

IO_WORKITEM 结构体是一个不透明的结构体,它描述了一个系统工作线程的工作Item。驱动可以通过IoAllocateWorkItem分配一个工作ITEM,或者驱动分配它自己的Buffer,然后调用IoInitializeWorkItem来初始化这个Buffer作为一个工作ITEM。

IoQueueWorkItem把一个回调函数绑定在一个工作ITEM中,并把它插入的队列中,等待系统工作线程来处理。
 步三:处理Idle请求任务IdleRequestWorkerRoutine。

IdleRequestWorkerRoutine调用SubmitIdleRequestIrp提交Idle请求的IRP,然后将事件NoDpcWorkItemPendingEvent激活。
步三、1     SubmitIdleRequestIrp 过程分析

SubmitIdleRequestIrp用一个分配的回调函数例程(PUSB_IDLE_CALLBACK_INFO)和一个IDLE通知的完成例程(IdleNotificationRequestComplete)建立一个IDLE请求IRP

[cpp] view plaincopy
  1. idleCallbackInfo = ExAllocatePool(NonPagedPool,
  2. sizeof(struct _USB_IDLE_CALLBACK_INFO));
  3. if(idleCallbackInfo) {
  4. idleCallbackInfo->IdleCallback = IdleNotificationCallback;
  5. idleCallbackInfo->IdleContext = (PVOID)DeviceExtension;
  6. ASSERT(DeviceExtension->IdleCallbackInfo == NULL);
  7. DeviceExtension->IdleCallbackInfo = idleCallbackInfo;
[cpp] view plaincopy
  1. irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize,
  2. FALSE);
  3. nextStack = IoGetNextIrpStackLocation(irp);
  4. nextStack->MajorFunction =
  5. IRP_MJ_INTERNAL_DEVICE_CONTROL;
  6. nextStack->Parameters.DeviceIoControl.IoControlCode =
  7. IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
  8. nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
  9. idleCallbackInfo;
  10. nextStack->Parameters.DeviceIoControl.InputBufferLength =
  11. sizeof(struct _USB_IDLE_CALLBACK_INFO);
  12. IoSetCompletionRoutine(irp,
  13. IdleNotificationRequestComplete,
  14. DeviceExtension,
  15. TRUE,
  16. TRUE,
  17. TRUE);
  18. DeviceExtension->PendingIdleIrp = irp;

并把IRP传到栈下面:

[cpp] view plaincopy
  1. ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp);

步三、2     IDLE通知的完成例程分析

判断IDLE请求的IRP的执行结果,如果有错误发生,那么通过PoRequestPowerIrp告诉设备栈的顶层驱动电源状态为PowerDeviceD0。

[cpp] view plaincopy
  1. powerState.DeviceState = PowerDeviceD0;
  2. ntStatus = PoRequestPowerIrp(
  3. DeviceExtension->PhysicalDeviceObject,
  4. IRP_MN_SET_POWER,
  5. powerState,
  6. (PREQUEST_POWER_COMPLETE) PoIrpAsyncCompletionFunc,
  7. DeviceExtension,
  8. NULL);

PoRequestPowerIrp 分析一个电源IRP并把它发送到设备栈的顶层驱动中。

、、、、、、

、、、、、、

、、、、、、

3、1、4   ReadandSelectDescriptors的实现

A、首先分配一个控制描述符请求的URB内存,然后再分析一个设备描述符内存PUSB_DEVICE_DESCRIPTOR

B、 然后通过UsbBuildGetDescriptorRequest填充URB后得到

[cpp] view plaincopy
  1. #define UsbBuildGetDescriptorRequest(urb, \
  2. length, \
  3. descriptorType, \
  4. descriptorIndex, \
  5. languageId, \
  6. transferBuffer, \
  7. transferBufferMDL, \
  8. transferBufferLength, \
  9. link) { \
  10. (urb)->UrbHeader.Function =  URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE; \
  11. (urb)->UrbHeader.Length = (length); \
  12. (urb)->UrbControlDescriptorRequest.TransferBufferLength = (transferBufferLength); \
  13. (urb)->UrbControlDescriptorRequest.TransferBufferMDL = (transferBufferMDL); \
  14. (urb)->UrbControlDescriptorRequest.TransferBuffer = (transferBuffer); \
  15. (urb)->UrbControlDescriptorRequest.DescriptorType = (descriptorType); \
  16. (urb)->UrbControlDescriptorRequest.Index = (descriptorIndex); \
  17. (urb)->UrbControlDescriptorRequest.LanguageId = (languageId); \
  18. (urb)->UrbControlDescriptorRequest.UrbLink = (link); }

C、请求设备描述符的URB, 将此URB请求自己封装的CallUSBD通过发送出去。

CallUSBD用于同步地提交一个URB到栈,步骤如下:

C、1   创建一个I/O控制码的IRP;

C、2   通过IoGetNextIrpStackLocation得到irp的下一层设备栈,并把urb值给nextStack->Parameters.Others.Argument1;

C、3  用IoCallDriver将irp发送到底层总线驱动上;

C、4 由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断,当异步完成IRP时,用事件等待总线驱动完成这个IRP。(参见3、2 )

[cpp] view plaincopy
  1. NTSTATUS
  2. ReadandSelectDescriptors(
  3. IN PDEVICE_OBJECT DeviceObject
  4. )
  5. /*++
  6. Routine Description:
  7. 配置USB设备.
  8. 我们先获得设备描述符,配置描述符然后选择配置描述符
  9. Arguments:
  10. DeviceObject - pointer to a device object
  11. Return Value:
  12. NTSTATUS - NT status value.
  13. --*/
  14. {
  15. PURB                   urb;
  16. ULONG                  siz;
  17. NTSTATUS               ntStatus;
  18. PUSB_DEVICE_DESCRIPTOR deviceDescriptor;
  19. //
  20. // initialize variables
  21. //
  22. urb = NULL;
  23. deviceDescriptor = NULL;
  24. //
  25. // 1.读设备描述符
  26. urb = ExAllocatePool(NonPagedPool,
  27. sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
  28. if(urb)
  29. {
  30. siz = sizeof(USB_DEVICE_DESCRIPTOR);
  31. deviceDescriptor = ExAllocatePool(NonPagedPool, siz);
  32. if(deviceDescriptor)
  33. {
  34. // 构造请求
  35. UsbBuildGetDescriptorRequest(
  36. urb,
  37. (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
  38. USB_DEVICE_DESCRIPTOR_TYPE,
  39. 0,
  40. 0,
  41. deviceDescriptor,
  42. NULL,
  43. siz,
  44. NULL);
  45. // 发送请求
  46. ntStatus = CallUSBD(DeviceObject, urb);
  47. if(NT_SUCCESS(ntStatus))
  48. {
  49. ASSERT(deviceDescriptor->bNumConfigurations);
  50. ntStatus = ConfigureDevice(DeviceObject);
  51. }
  52. ExFreePool(urb);
  53. ExFreePool(deviceDescriptor);
  54. }
  55. else
  56. {
  57. BulkUsb_DbgPrint(1, ("Failed to allocate memory for deviceDescriptor"));
  58. ExFreePool(urb);
  59. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  60. }
  61. }
  62. else
  63. {
  64. BulkUsb_DbgPrint(1, ("Failed to allocate memory for urb"));
  65. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  66. }
  67. return ntStatus;
  68. }

D、请求配置描述符

读取的过程是:

第一步:读取配置描述符的大小;

分配的配置描述符内存大小为 sizeof(USB_CONFIGURATION_DESCRIPTOR),也是把内存绑定到URB,URB再绑到IRP,然后通过IoCallDriver提交请求;

(实现与上步C类似)

第二步:根据第一步得到的大小,重新分析PUSB_CONFIGURATION_DESCRIPTOR内存, 与第一步一样的方法提交。

由于希望得到的配置描述符的大小变化了,所以得提交的请求返回的内存就是真正的配置描述符数据。

E、保存上步D得到的配置描述符指针到extension中,根据指针内容判断是否支持远程唤醒;

F、根据返回的配置描述符数据选择其中一个Interfaces。(重点)

[cpp] view plaincopy
  1. NTSTATUS
  2. SelectInterfaces(
  3. IN PDEVICE_OBJECT                DeviceObject,
  4. IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
  5. )

F、1  从ConfigurationDescriptor描述符数据中通过USBD_ParseConfigurationDescriptorEx取出一个一个的PUSB_INTERFACE_DESCRIPTOR,组成链表;

USBD_ParseConfigurationDescriptorEx为DDK提供的API, 用于解析从设备返回的标准的USB配置描述符,以得到一个指定的接口,可改变的设备类子类或协议代码。

[cpp] view plaincopy
  1. DECLSPEC_IMPORT
  2. PUSB_INTERFACE_DESCRIPTOR
  3. USBD_ParseConfigurationDescriptorEx(
  4. IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
  5. IN PVOID StartPosition,
  6. IN LONG InterfaceNumber,
  7. IN LONG AlternateSetting,
  8. IN LONG InterfaceClass,
  9. IN LONG InterfaceSubClass,
  10. IN LONG InterfaceProtocol
  11. );

ConfigurationDescriptor - 指向从设备返回的USB配置描述符, (包含所有的接口和端点描述符);
StartPosition - 指向将要解析的配置描述符的开始位置;

InterfaceNumber - 要查找的接口号,(-1)表满足所有;
AlternateSetting - 要查找的可改变的设备号,(-1)表满足所有;

InterfaceClass - 要查找的类,(-1)表满足所有;
InterfaceSubClass - 要查找的子类,(-1)表满足所有;
InterfaceProtocol - 要查找的协议,(-1)表满足所有;

[cpp] view plaincopy
  1. tmp = interfaceList =
  2. ExAllocatePool(
  3. NonPagedPool,
  4. sizeof(USBD_INTERFACE_LIST_ENTRY) * (numberOfInterfaces + 1));
  5. if(!tmp) {
  6. BulkUsb_DbgPrint(1, ("Failed to allocate mem for interfaceList\n"));
  7. return STATUS_INSUFFICIENT_RESOURCES;
  8. }
  9. while(interfaceNumber < numberOfInterfaces) {
  10. interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
  11. ConfigurationDescriptor,
  12. ConfigurationDescriptor,
  13. interfaceindex,
  14. 0, -1, -1, -1);
  15. if(interfaceDescriptor) {
  16. interfaceList->InterfaceDescriptor = interfaceDescriptor;
  17. interfaceList->Interface = NULL;
  18. interfaceList++;
  19. interfaceNumber++;
  20. }
  21. interfaceindex++;
  22. }

F、2   通过DDK提供的USBD_CreateConfigurationRequestEx函数,

USBD_CreateConfigurationRequestEx函数基于传进来的接口列表,来分配并初始化一个有足够大小的URB来配置设备, 接口列表是一个连续的 USBD_INTERFACE_LIST_ENTRIES数组,每一个指定接口描述符的指针被包含在请求中,列表通过指向NULL的列表入口来终止。

[cpp] view plaincopy
  1. DECLSPEC_IMPORT
  2. PURB
  3. USBD_CreateConfigurationRequestEx(
  4. IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
  5. IN PUSBD_INTERFACE_LIST_ENTRY InterfaceList
  6. );

F、3  把E、2返回的URB请求发送出去;把Interface信息urb->UrbSelectConfiguration.Interface保存在extension中;

F、4  初始化BULKUSB_PIPE_CONTEXT  PipeContext

3、2  然后调用CallUSBD将此请求发送出去

URB包要和一个IRP相关联起来。这就需要IoBuildDeviceIoControlRequest创建一个I/O控制码的IRP,

然后将URB作为IRP的参数,用IoCallDriver将URB发送到底层总线驱动上。

由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断当异步完成IRP时( if(ntStatus == STATUS_PENDING)),用事件等待总线驱动完成这个IRP。

[cpp] view plaincopy
  1. NTSTATUS
  2. CallUSBD(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PURB           Urb
  5. )
  6. /*++
  7. Routine Description:
  8. 同步地提交一个URB到栈
  9. Arguments:
  10. DeviceObject - pointer to device object
  11. Urb - USB request block
  12. Return Value:
  13. --*/
  14. {
  15. PIRP               irp;
  16. KEVENT             event;
  17. NTSTATUS           ntStatus;
  18. IO_STATUS_BLOCK    ioStatus;
  19. PIO_STACK_LOCATION nextStack;
  20. PDEVICE_EXTENSION  deviceExtension;
  21. //
  22. // initialize the variables
  23. //
  24. irp = NULL;
  25. deviceExtension = DeviceObject->DeviceExtension;
  26. KeInitializeEvent(&event, NotificationEvent, FALSE);
  27. // 创建一个I/O控制码的IRP
  28. irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,
  29. deviceExtension->TopOfStackDeviceObject,
  30. NULL,
  31. 0,
  32. NULL,
  33. 0,
  34. TRUE,
  35. &event,
  36. &ioStatus);
  37. if(!irp)
  38. {
  39. BulkUsb_DbgPrint(1, ("IoBuildDeviceIoControlRequest failed\n"));
  40. return STATUS_INSUFFICIENT_RESOURCES;
  41. }
  42. nextStack = IoGetNextIrpStackLocation(irp);
  43. ASSERT(nextStack != NULL);
  44. nextStack->Parameters.Others.Argument1 = Urb;
  45. BulkUsb_DbgPrint(3, ("CallUSBD::"));
  46. BulkUsb_IoIncrement(deviceExtension);
  47. // 用IoCallDriver将URB发送到底层总线驱动上
  48. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
  49. // 由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断当异步完成
  50. //  IRP时,用事件等待总线驱动完成这个IRP。
  51. if(ntStatus == STATUS_PENDING)
  52. {
  53. KeWaitForSingleObject(&event,
  54. Executive,
  55. KernelMode,
  56. FALSE,
  57. NULL);
  58. ntStatus = ioStatus.Status;
  59. }
  60. BulkUsb_DbgPrint(3, ("CallUSBD::"));
  61. BulkUsb_IoDecrement(deviceExtension);
  62. return ntStatus;
  63. }
3、3  USB设备初始化

在AddDevice全程中,创建功能设备对象,然后将该对象挂载在总线设备对象之上,从而形成设备栈。

另外为设备创建一个设备链接,便于应用程序可以找到这个设备。

[cpp] view plaincopy
  1. NTSTATUS
  2. BulkUsb_AddDevice(
  3. IN PDRIVER_OBJECT DriverObject,
  4. IN PDEVICE_OBJECT PhysicalDeviceObject
  5. )
  6. /*++
  7. Description:
  8. Arguments:
  9. DriverObject - Store the pointer to the object representing us.
  10. PhysicalDeviceObject - Pointer to the device object created by the
  11. undelying bus driver.
  12. Return:
  13. STATUS_SUCCESS - if successful
  14. STATUS_UNSUCCESSFUL - otherwise
  15. --*/
  16. {
  17. NTSTATUS          ntStatus;
  18. PDEVICE_OBJECT    deviceObject;
  19. PDEVICE_EXTENSION deviceExtension;
  20. POWER_STATE       state;
  21. KIRQL             oldIrql;
  22. BulkUsb_DbgPrint(3, ("BulkUsb_AddDevice - begins\n"));
  23. deviceObject = NULL;
  24. ntStatus = IoCreateDevice(
  25. DriverObject,                   // our driver object
  26. sizeof(DEVICE_EXTENSION),       // extension size for us
  27. NULL,                           // name for this device
  28. FILE_DEVICE_UNKNOWN,
  29. FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics
  30. FALSE,                          // Not exclusive
  31. &deviceObject);                 // Our device object
  32. if(!NT_SUCCESS(ntStatus))
  33. {
  34. //
  35. // returning failure here prevents the entire stack from functioning,
  36. // but most likely the rest of the stack will not be able to create
  37. // device objects either, so it is still OK.
  38. //
  39. BulkUsb_DbgPrint(1, ("Failed to create device object\n"));
  40. return ntStatus;
  41. }
  42. //
  43. // 初始化设备扩展
  44. deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
  45. deviceExtension->FunctionalDeviceObject = deviceObject;
  46. deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
  47. deviceObject->Flags |= DO_DIRECT_IO;
  48. //
  49. // 初始化设备栈并设置设备状态
  50. KeInitializeSpinLock(&deviceExtension->DevStateLock);
  51. INITIALIZE_PNP_STATE(deviceExtension);
  52. //
  53. // 初始化 OpenHandleCount
  54. //
  55. deviceExtension->OpenHandleCount = 0;
  56. //
  57. // 初始化可选的中止变量
  58. KeInitializeSpinLock(&deviceExtension->IdleReqStateLock);
  59. deviceExtension->IdleReqPend = 0;
  60. deviceExtension->PendingIdleIrp = NULL;
  61. //
  62. // Hold住请求一直到设备开始
  63. deviceExtension->QueueState = HoldRequests;
  64. //
  65. // 初始化队列和队列 spin 锁
  66. InitializeListHead(&deviceExtension->NewRequestsQueue);
  67. KeInitializeSpinLock(&deviceExtension->QueueLock);
  68. //
  69. // 初始化可移除的事件到没信号
  70. KeInitializeEvent(&deviceExtension->RemoveEvent,
  71. SynchronizationEvent,
  72. FALSE);
  73. //
  74. // 初始化停止事件到有信号.
  75. // This event is signaled when the OutstandingIO becomes 1
  76. //
  77. KeInitializeEvent(&deviceExtension->StopEvent,
  78. SynchronizationEvent,
  79. TRUE);
  80. //
  81. // OutstandingIo count biased to 1.
  82. // Transition to 0 during remove device means IO is finished.
  83. // Transition to 1 means the device can be stopped
  84. //
  85. deviceExtension->OutStandingIO = 1;
  86. KeInitializeSpinLock(&deviceExtension->IOCountLock);
  87. //
  88. // Delegating to WMILIB
  89. //
  90. ntStatus = BulkUsb_WmiRegistration(deviceExtension);
  91. if(!NT_SUCCESS(ntStatus)) {
  92. BulkUsb_DbgPrint(1, ("BulkUsb_WmiRegistration failed with %X\n", ntStatus));
  93. IoDeleteDevice(deviceObject);
  94. return ntStatus;
  95. }
  96. //
  97. // 设置标示为 underlying PDO
  98. //
  99. if(PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) {
  100. deviceObject->Flags |= DO_POWER_PAGABLE;
  101. }
  102. //
  103. // Typically, the function driver for a device is its
  104. // power policy owner, although for some devices another
  105. // driver or system component may assume this role.
  106. // Set the initial power state of the device, if known, by calling
  107. // PoSetPowerState.
  108. //
  109. deviceExtension->DevPower = PowerDeviceD0;
  110. deviceExtension->SysPower = PowerSystemWorking;
  111. state.DeviceState = PowerDeviceD0;
  112. PoSetPowerState(deviceObject, DevicePowerState, state);
  113. //
  114. // 把我们的驱动接入到设备栈
  115. // IoAttachDeviceToDeviceStack返回值是接入链的顶层对象
  116. //  This is where all the IRPs should be routed.
  117. //
  118. deviceExtension->TopOfStackDeviceObject =
  119. IoAttachDeviceToDeviceStack(deviceObject,
  120. PhysicalDeviceObject);
  121. if(NULL == deviceExtension->TopOfStackDeviceObject) {
  122. BulkUsb_WmiDeRegistration(deviceExtension);
  123. IoDeleteDevice(deviceObject);
  124. return STATUS_NO_SUCH_DEVICE;
  125. }
  126. //
  127. // 注册设备接口
  128. ntStatus = IoRegisterDeviceInterface(deviceExtension->PhysicalDeviceObject,
  129. &GUID_CLASS_I82930_BULK,
  130. NULL,
  131. &deviceExtension->InterfaceName);
  132. if(!NT_SUCCESS(ntStatus))
  133. {
  134. BulkUsb_WmiDeRegistration(deviceExtension);
  135. IoDetachDevice(deviceExtension->TopOfStackDeviceObject);
  136. IoDeleteDevice(deviceObject);
  137. return ntStatus;
  138. }
  139. if(IoIsWdmVersionAvailable(1, 0x20))
  140. {
  141. deviceExtension->WdmVersion = WinXpOrBetter;
  142. }
  143. else if(IoIsWdmVersionAvailable(1, 0x10))
  144. {
  145. deviceExtension->WdmVersion = Win2kOrBetter;
  146. }
  147. else if(IoIsWdmVersionAvailable(1, 0x5))
  148. {
  149. deviceExtension->WdmVersion = WinMeOrBetter;
  150. }
  151. else if(IoIsWdmVersionAvailable(1, 0x0)) {
  152. deviceExtension->WdmVersion = Win98OrBetter;
  153. }
  154. deviceExtension->SSRegistryEnable = 0;
  155. deviceExtension->SSEnable = 0;
  156. //
  157. // WinXP only
  158. // check the registry flag -
  159. // whether the device should selectively
  160. // suspend when idle
  161. //
  162. if(WinXpOrBetter == deviceExtension->WdmVersion)
  163. {
  164. BulkUsb_GetRegistryDword(BULKUSB_REGISTRY_PARAMETERS_PATH,
  165. L"BulkUsbEnable",
  166. &deviceExtension->SSRegistryEnable);
  167. if(deviceExtension->SSRegistryEnable) {
  168. //
  169. // initialize DPC
  170. //
  171. KeInitializeDpc(&deviceExtension->DeferredProcCall,
  172. DpcRoutine,
  173. deviceObject);
  174. //
  175. // initialize the timer.
  176. // the DPC and the timer in conjunction,
  177. // monitor the state of the device to
  178. // selectively suspend the device.
  179. //
  180. KeInitializeTimerEx(&deviceExtension->Timer,
  181. NotificationTimer);
  182. //
  183. // Initialize the NoDpcWorkItemPendingEvent to signaled state.
  184. // This event is cleared when a Dpc is fired and signaled
  185. // on completion of the work-item.
  186. //
  187. KeInitializeEvent(&deviceExtension->NoDpcWorkItemPendingEvent,
  188. NotificationEvent,
  189. TRUE);
  190. //
  191. // Initialize the NoIdleReqPendEvent to ensure that the idle request
  192. // is indeed complete before we unload the drivers.
  193. //
  194. KeInitializeEvent(&deviceExtension->NoIdleReqPendEvent,
  195. NotificationEvent,
  196. TRUE);
  197. }
  198. }
  199. //
  200. // Clear the DO_DEVICE_INITIALIZING flag.
  201. // Note: Do not clear this flag until the driver has set the
  202. // device power state and the power DO flags.
  203. //
  204. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  205. BulkUsb_DbgPrint(3, ("BulkUsb_AddDevice - ends\n"));
  206. return ntStatus;
  207. }
3、4 USB设备的插拔

插拔设备会设计3个即插即用IRP,包括IRP_MN_START_DEVICE、

IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL.

3、4、1

IRP_MN_START_DEVICE 是当驱动争取加载并运行时,操作系统的即插即用管理器会将

这个IRP发给设备驱动。因此当获得这个IRP后,USB驱动需要获得USB设备类别描述符,并通过这些

描述符,从中获取有用信息,记录在设备扩展中。

处理函数就是上面讲到的 HandleStartDevice 函数

3、4、2

IRP_MN_STOP_DEVICE 是设备关闭前,即插即用管理器发的IRP。USB驱动获得这个IRP时,

应该尽快结束当前执行的IRP, 并将其逐个取消掉。别外在设备扩展中还应该有表示当前状态的变量,

当IRP_MN_STOP_DEVICE来临时,将当前状态记录成停止状态。

它的处理函数是:

[cpp] view plaincopy
  1. NTSTATUS
  2. HandleStopDevice(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP           Irp
  5. )
  6. /*++
  7. Routine Description:
  8. This routine services Irp of minor type IRP_MN_STOP_DEVICE
  9. Arguments:
  10. DeviceObject - pointer to device object
  11. Irp - I/O request packet sent by the pnp manager.
  12. Return Value:
  13. NT status value
  14. --*/
  15. {
  16. KIRQL             oldIrql;
  17. NTSTATUS          ntStatus;
  18. PDEVICE_EXTENSION deviceExtension;
  19. BulkUsb_DbgPrint(3, ("HandleStopDevice - begins\n"));
  20. //
  21. // initialize variables
  22. //
  23. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  24. if(WinXpOrBetter == deviceExtension->WdmVersion) {
  25. if(deviceExtension->SSEnable) {
  26. //
  27. // 取消定时器这样DPCs就不再激活.
  28. // Thus, we are making judicious usage of our resources.
  29. // 我们不再需要DPCs,因为设备已经停止.
  30. // The timers are re-initialized while handling the start
  31. // device irp.
  32. //
  33. KeCancelTimer(&deviceExtension->Timer);
  34. //
  35. // 在设备停止后,它能被强拨了.
  36. // 我们设置它为0,这样我们在强拨或移除Irps时不再偿试去取消定时器 .
  37. // 当我们再获得设备请求时,此标志会再被初始化
  38. //
  39. deviceExtension->SSEnable = 0;
  40. //
  41. // make sure that if a DPC was fired before we called cancel timer,
  42. // then the DPC and work-time have run to their completion
  43. //
  44. KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent,
  45. Executive,
  46. KernelMode,
  47. FALSE,
  48. NULL);
  49. //
  50. // make sure that the selective suspend request has been completed.
  51. //
  52. KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
  53. Executive,
  54. KernelMode,
  55. FALSE,
  56. NULL);
  57. }
  58. }
  59. //
  60. // after the stop Irp is sent to the lower driver object,
  61. // the driver must not send any more Irps down that touch
  62. // the device until another Start has occurred.
  63. //
  64. if(deviceExtension->WaitWakeEnable) {
  65. CancelWaitWake(deviceExtension);
  66. }
  67. KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
  68. // 将当前状态记录成停止状态
  69. SET_NEW_PNP_STATE(deviceExtension, Stopped);
  70. KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
  71. //
  72. // This is the right place to actually give up all the resources used
  73. // This might include calls to IoDisconnectInterrupt, MmUnmapIoSpace,
  74. // etc.
  75. //
  76. ReleaseMemory(DeviceObject);
  77. ntStatus = DeconfigureDevice(DeviceObject);
  78. Irp->IoStatus.Status = ntStatus;
  79. Irp->IoStatus.Information = 0;
  80. IoSkipCurrentIrpStackLocation(Irp);
  81. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
  82. BulkUsb_DbgPrint(3, ("HandleStopDevice - ends\n"));
  83. return ntStatus;
  84. }

我们看到它在取消定时器后做了等待,这样能确保我们取消定时器时万一还在定时器的完成例程中时能等待例程完成,

以免把设备停止了,可是DPC还在访问设备。所以在DPC例程中,首先做的事就是把自己的事件置为无信号。

[cpp] view plaincopy
  1. KeClearEvent(&deviceExtension->NoDpcWorkItemPendingEvent);

3、4、3  IRP_MN_SURPRISE_REMOVAL

IRP_MN_EJECT 是设备被正常弹出,而IRP_MN_SURPRISE_REMOVAL则是设备非自然弹出,

有可能是掉电或强行拨出。在这种IRP到来时,应该强迫所有未完成的读写IRP结束并取消。

[cpp] view plaincopy
  1. NTSTATUS
  2. HandleSurpriseRemoval(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP           Irp
  5. )
  6. /*++
  7. Routine Description:
  8. This routine services Irp of minor type IRP_MN_SURPRISE_REMOVAL
  9. Arguments:
  10. DeviceObject - pointer to device object
  11. Irp - I/O request packet sent by the pnp manager.
  12. Return Value:
  13. NT status value
  14. --*/
  15. {
  16. KIRQL             oldIrql;
  17. NTSTATUS          ntStatus;
  18. PDEVICE_EXTENSION deviceExtension;
  19. BulkUsb_DbgPrint(3, ("HandleSurpriseRemoval - begins\n"));
  20. //
  21. // initialize variables
  22. //
  23. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  24. //
  25. // 1. fail pending requests
  26. // 2. return device and memory resources
  27. // 3. disable interfaces
  28. //
  29. if(deviceExtension->WaitWakeEnable) {
  30. CancelWaitWake(deviceExtension);
  31. }
  32. if(WinXpOrBetter == deviceExtension->WdmVersion) {
  33. if(deviceExtension->SSEnable) {
  34. //
  35. // Cancel the timer so that the DPCs are no longer fired.
  36. // we do not need DPCs because the device has been surprise
  37. // removed
  38. //
  39. KeCancelTimer(&deviceExtension->Timer);
  40. deviceExtension->SSEnable = 0;
  41. //
  42. // make sure that if a DPC was fired before we called cancel timer,
  43. // then the DPC and work-time have run to their completion
  44. //
  45. KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent,
  46. Executive,
  47. KernelMode,
  48. FALSE,
  49. NULL);
  50. //
  51. // make sure that the selective suspend request has been completed.
  52. //
  53. KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
  54. Executive,
  55. KernelMode,
  56. FALSE,
  57. NULL);
  58. }
  59. }
  60. KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);
  61. deviceExtension->QueueState = FailRequests;
  62. SET_NEW_PNP_STATE(deviceExtension, SurpriseRemoved);
  63. KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);
  64. ProcessQueuedRequests(deviceExtension);
  65. ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName,
  66. FALSE);
  67. if(!NT_SUCCESS(ntStatus)) {
  68. BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState::disable:failed\n"));
  69. }
  70. BulkUsb_AbortPipes(DeviceObject);
  71. Irp->IoStatus.Status = STATUS_SUCCESS;
  72. Irp->IoStatus.Information = 0;
  73. IoSkipCurrentIrpStackLocation(Irp);
  74. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
  75. BulkUsb_DbgPrint(3, ("HandleSurpriseRemoval - ends\n"));
  76. return ntStatus;
  77. }

3、4 USB设备的读写

USB设备接口主要是为了传送数据,80%的传输是通过Bulk管道。在BulkUSB驱动中,Bulk管道的读取是在IRP_MJ_READ和IRP_MJ_WRITE的函数中。

IRP_MJ_READ和IRP_MJ_WRITE的派遣函数我们设置为同一个BulkUsb_DispatchReadWrite:

[cpp] view plaincopy
  1. NTSTATUS
  2. BulkUsb_DispatchReadWrite(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP           Irp
  5. )
  6. /*++
  7. Routine Description:
  8. Dispatch routine for read and write.
  9. This routine creates a BULKUSB_RW_CONTEXT for a read/write.
  10. This read/write is performed in stages of BULKUSB_MAX_TRANSFER_SIZE.
  11. once a stage of transfer is complete, then the irp is circulated again,
  12. until the requested length of tranfer is performed.
  13. Arguments:
  14. DeviceObject - pointer to device object
  15. Irp - I/O request packet
  16. Return Value:
  17. NT status value
  18. --*/
  19. {
  20. PMDL                   mdl;
  21. PURB                   urb;
  22. ULONG                  totalLength;
  23. ULONG                  stageLength;
  24. ULONG                  urbFlags;
  25. BOOLEAN                read;
  26. NTSTATUS               ntStatus;
  27. ULONG_PTR              virtualAddress;
  28. PFILE_OBJECT           fileObject;
  29. PDEVICE_EXTENSION      deviceExtension;
  30. PIO_STACK_LOCATION     irpStack;
  31. PIO_STACK_LOCATION     nextStack;
  32. PBULKUSB_RW_CONTEXT    rwContext;
  33. PUSBD_PIPE_INFORMATION pipeInformation;
  34. //
  35. // 初始化变量
  36. urb = NULL;
  37. mdl = NULL;
  38. rwContext = NULL;
  39. totalLength = 0;
  40. irpStack = IoGetCurrentIrpStackLocation(Irp);
  41. fileObject = irpStack->FileObject;
  42. read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
  43. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  44. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - begins\n"));
  45. if(deviceExtension->DeviceState != Working) {
  46. BulkUsb_DbgPrint(1, ("Invalid device state\n"));
  47. ntStatus = STATUS_INVALID_DEVICE_STATE;
  48. goto BulkUsb_DispatchReadWrite_Exit;
  49. }
  50. //
  51. // It is true that the client driver cancelled the selective suspend
  52. // request in the dispatch routine for create Irps.
  53. // But there is no guarantee that it has indeed completed.
  54. // so wait on the NoIdleReqPendEvent and proceed only if this event
  55. // is signalled.
  56. //
  57. BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
  58. //
  59. // make sure that the selective suspend request has been completed.
  60. //
  61. if(deviceExtension->SSEnable)
  62. {
  63. KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
  64. Executive,
  65. KernelMode,
  66. FALSE,
  67. NULL);
  68. }
  69. if(fileObject && fileObject->FsContext)
  70. {
  71. pipeInformation = fileObject->FsContext;
  72. if(UsbdPipeTypeBulk != pipeInformation->PipeType) {
  73. BulkUsb_DbgPrint(1, ("Usbd pipe type is not bulk\n"));
  74. ntStatus = STATUS_INVALID_HANDLE;
  75. goto BulkUsb_DispatchReadWrite_Exit;
  76. }
  77. }
  78. else
  79. {
  80. BulkUsb_DbgPrint(1, ("Invalid handle\n"));
  81. ntStatus = STATUS_INVALID_HANDLE;
  82. goto BulkUsb_DispatchReadWrite_Exit;
  83. }
  84. // 设置完成例程的参数
  85. rwContext = (PBULKUSB_RW_CONTEXT)
  86. ExAllocatePool(NonPagedPool,
  87. sizeof(BULKUSB_RW_CONTEXT));
  88. if(rwContext == NULL)
  89. {
  90. BulkUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));
  91. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  92. goto BulkUsb_DispatchReadWrite_Exit;
  93. }
  94. if(Irp->MdlAddress)
  95. {
  96. totalLength = MmGetMdlByteCount(Irp->MdlAddress);
  97. }
  98. if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE)
  99. {
  100. BulkUsb_DbgPrint(1, ("Transfer length > circular buffer\n"));
  101. ntStatus = STATUS_INVALID_PARAMETER;
  102. ExFreePool(rwContext);
  103. goto BulkUsb_DispatchReadWrite_Exit;
  104. }
  105. if(totalLength == 0)
  106. {
  107. BulkUsb_DbgPrint(1, ("Transfer data length = 0\n"));
  108. ntStatus = STATUS_SUCCESS;
  109. ExFreePool(rwContext);
  110. goto BulkUsb_DispatchReadWrite_Exit;
  111. }
  112. // 设置URB标志
  113. urbFlags = USBD_SHORT_TRANSFER_OK;
  114. virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);
  115. // 判断是读还是写
  116. if(read)
  117. {
  118. urbFlags |= USBD_TRANSFER_DIRECTION_IN;
  119. BulkUsb_DbgPrint(3, ("Read operation\n"));
  120. }
  121. else
  122. {
  123. urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
  124. BulkUsb_DbgPrint(3, ("Write operation\n"));
  125. }
  126. //
  127. // the transfer request is for totalLength.
  128. // we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
  129. // in each stage.
  130. //
  131. if(totalLength > BULKUSB_MAX_TRANSFER_SIZE)
  132. {
  133. stageLength = BULKUSB_MAX_TRANSFER_SIZE;
  134. }
  135. else
  136. {
  137. stageLength = totalLength;
  138. }
  139. // 建立MDL
  140. mdl = IoAllocateMdl((PVOID) virtualAddress,
  141. totalLength,
  142. FALSE,
  143. FALSE,
  144. NULL);
  145. if(mdl == NULL)
  146. {
  147. BulkUsb_DbgPrint(1, ("Failed to alloc mem for mdl\n"));
  148. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  149. ExFreePool(rwContext);
  150. goto BulkUsb_DispatchReadWrite_Exit;
  151. }
  152. //
  153. // 将新MDL进行映射
  154. // map the portion of user-buffer described by an mdl to another mdl
  155. //
  156. IoBuildPartialMdl(Irp->MdlAddress,
  157. mdl,
  158. (PVOID) virtualAddress,
  159. stageLength);
  160. // 申请URB数据结构
  161. urb = ExAllocatePool(NonPagedPool,
  162. sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
  163. if(urb == NULL)
  164. {
  165. BulkUsb_DbgPrint(1, ("Failed to alloc mem for urb\n"));
  166. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  167. ExFreePool(rwContext);
  168. IoFreeMdl(mdl);
  169. goto BulkUsb_DispatchReadWrite_Exit;
  170. }
  171. // 建立Bulk管道的URB
  172. UsbBuildInterruptOrBulkTransferRequest(
  173. urb,
  174. sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
  175. pipeInformation->PipeHandle,
  176. NULL,
  177. mdl,
  178. stageLength,
  179. urbFlags,
  180. NULL);
  181. //
  182. // 设置完成例程参数  BULKUSB_RW_CONTEXT
  183. //
  184. rwContext->Urb             = urb;
  185. rwContext->Mdl             = mdl;
  186. rwContext->Length          = totalLength - stageLength;
  187. rwContext->Numxfer         = 0;
  188. rwContext->VirtualAddress  = virtualAddress + stageLength;
  189. rwContext->DeviceExtension = deviceExtension;
  190. //
  191. // use the original read/write irp as an internal device control irp
  192. // 设置设备堆栈
  193. nextStack = IoGetNextIrpStackLocation(Irp);
  194. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  195. nextStack->Parameters.Others.Argument1 = (PVOID) urb;
  196. nextStack->Parameters.DeviceIoControl.IoControlCode =
  197. IOCTL_INTERNAL_USB_SUBMIT_URB;
  198. // 设置完成例程式
  199. IoSetCompletionRoutine(Irp,
  200. (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
  201. rwContext,
  202. TRUE,
  203. TRUE,
  204. TRUE);
  205. //
  206. // since we return STATUS_PENDING call IoMarkIrpPending.
  207. // This is the boiler plate code.
  208. // This may cause extra overhead of an APC for the Irp completion
  209. // but this is the correct thing to do.
  210. //
  211. // 将当前IRP阻塞
  212. IoMarkIrpPending(Irp);
  213. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite::"));
  214. BulkUsb_IoIncrement(deviceExtension);
  215. // 将IRP转发到底层USB总线驱动
  216. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
  217. Irp);
  218. if(!NT_SUCCESS(ntStatus)) {
  219. BulkUsb_DbgPrint(1, ("IoCallDriver fails with status %X\n", ntStatus));
  220. //
  221. // if the device was yanked out, then the pipeInformation
  222. // field is invalid.
  223. // similarly if the request was cancelled, then we need not
  224. // invoked reset pipe/device.
  225. //
  226. if((ntStatus != STATUS_CANCELLED) &&
  227. (ntStatus != STATUS_DEVICE_NOT_CONNECTED)) {
  228. ntStatus = BulkUsb_ResetPipe(DeviceObject,
  229. pipeInformation);
  230. if(!NT_SUCCESS(ntStatus)) {
  231. BulkUsb_DbgPrint(1, ("BulkUsb_ResetPipe failed\n"));
  232. ntStatus = BulkUsb_ResetDevice(DeviceObject);
  233. }
  234. }
  235. else {
  236. BulkUsb_DbgPrint(3, ("ntStatus is STATUS_CANCELLED or "
  237. "STATUS_DEVICE_NOT_CONNECTED\n"));
  238. }
  239. }
  240. //
  241. // we return STATUS_PENDING and not the status returned by the lower layer.
  242. //
  243. return STATUS_PENDING;
  244. BulkUsb_DispatchReadWrite_Exit:
  245. Irp->IoStatus.Status = ntStatus;
  246. Irp->IoStatus.Information = 0;
  247. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  248. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - ends\n"));
  249. return ntStatus;
  250. }

(1)如果设备状态不是Working,直接返回,因为此时设备已经拔掉了;

(2)如果应用程序要读的Pipe不是Bulk(Pipe信息从设备栈中的FileObject对象中获得),那么也直接返回;

(3)调用MmGetMdlByteCount从Irp中的MdlAddress得到Byte数量(这个数量就是应用层想读或写的数量)及virtualAddress,然后根据它们建立MDL,

并调用IoBuildPartialMdl将用户的Buffer将映射到新建的虚地址中;

如果Byte数量过大或为0,那么直接返回。

[cpp] view plaincopy
  1. // 建立MDL
  2. mdl = IoAllocateMdl((PVOID) virtualAddress,
  3. totalLength,
  4. FALSE,
  5. FALSE,
  6. NULL);
[cpp] view plaincopy
  1. IoBuildPartialMdl(Irp->MdlAddress,
  2. mdl,
  3. (PVOID) virtualAddress,
  4. stageLength);

(4)根据MDL建立Bulk管道的URB;

[cpp] view plaincopy
  1. // 建立Bulk管道的URB
  2. UsbBuildInterruptOrBulkTransferRequest(
  3. urb,
  4. sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
  5. pipeInformation->PipeHandle,
  6. NULL,
  7. mdl,
  8. stageLength,
  9. urbFlags,
  10. NULL);

(5)把上面得到的URB组合成一个新的IRP(操作码为IOCTL_INTERNAL_USB_SUBMIT_URB),设置完成例程(如3、5所讲)传递给总线管理器;

[cpp] view plaincopy
  1. nextStack = IoGetNextIrpStackLocation(Irp);
  2. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  3. nextStack->Parameters.Others.Argument1 = (PVOID) urb;
  4. nextStack->Parameters.DeviceIoControl.IoControlCode =
  5. IOCTL_INTERNAL_USB_SUBMIT_URB;
  6. // 设置完成例程式
  7. IoSetCompletionRoutine(Irp,
  8. (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
  9. rwContext,
  10. TRUE,
  11. TRUE,
  12. TRUE);
[cpp] view plaincopy
  1. // 将当前IRP阻塞
  2. IoMarkIrpPending(Irp);
  3. BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite::"));
  4. BulkUsb_IoIncrement(deviceExtension);
  5. // 将IRP转发到底层USB总线驱动
  6. ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
  7. Irp);

3、5  读写完成的例子程是

[cpp] view plaincopy
  1. NTSTATUS
  2. BulkUsb_ReadWriteCompletion(
  3. IN PDEVICE_OBJECT DeviceObject,
  4. IN PIRP           Irp,
  5. IN PVOID          Context
  6. )
  7. /*++
  8. Routine Description:
  9. This is the completion routine for reads/writes
  10. If the irp completes with success, we check if we
  11. need to recirculate this irp for another stage of
  12. transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
  13. if the irp completes in error, free all memory allocs and
  14. return the status.
  15. Arguments:
  16. DeviceObject - pointer to device object
  17. Irp - I/O request packet
  18. Context - context passed to the completion routine.
  19. Return Value:
  20. NT status value
  21. --*/
  22. {
  23. ULONG               stageLength;
  24. NTSTATUS            ntStatus;
  25. PIO_STACK_LOCATION  nextStack;
  26. PBULKUSB_RW_CONTEXT rwContext;
  27. //
  28. // initialize variables
  29. //
  30. rwContext = (PBULKUSB_RW_CONTEXT) Context;
  31. ntStatus = Irp->IoStatus.Status;
  32. UNREFERENCED_PARAMETER(DeviceObject);
  33. BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - begins\n"));
  34. //
  35. // successfully performed a stageLength of transfer.
  36. // check if we need to recirculate the irp.
  37. //
  38. if(NT_SUCCESS(ntStatus)) {
  39. if(rwContext) {
  40. rwContext->Numxfer +=
  41. rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
  42. if(rwContext->Length) {
  43. //
  44. // another stage transfer
  45. //
  46. BulkUsb_DbgPrint(3, ("Another stage transfer...\n"));
  47. if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {
  48. stageLength = BULKUSB_MAX_TRANSFER_SIZE;
  49. }
  50. else {
  51. stageLength = rwContext->Length;
  52. }
  53. IoBuildPartialMdl(Irp->MdlAddress,
  54. rwContext->Mdl,
  55. (PVOID) rwContext->VirtualAddress,
  56. stageLength);
  57. //
  58. // reinitialize the urb
  59. //
  60. rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength
  61. = stageLength;
  62. rwContext->VirtualAddress += stageLength;
  63. rwContext->Length -= stageLength;
  64. nextStack = IoGetNextIrpStackLocation(Irp);
  65. nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  66. nextStack->Parameters.Others.Argument1 = rwContext->Urb;
  67. nextStack->Parameters.DeviceIoControl.IoControlCode =
  68. IOCTL_INTERNAL_USB_SUBMIT_URB;
  69. IoSetCompletionRoutine(Irp,
  70. BulkUsb_ReadWriteCompletion,
  71. rwContext,
  72. TRUE,
  73. TRUE,
  74. TRUE);
  75. IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,
  76. Irp);
  77. return STATUS_MORE_PROCESSING_REQUIRED;
  78. }
  79. else {
  80. //
  81. // this is the last transfer
  82. //
  83. Irp->IoStatus.Information = rwContext->Numxfer;
  84. }
  85. }
  86. }
  87. else {
  88. BulkUsb_DbgPrint(1, ("ReadWriteCompletion - failed with status = %X\n", ntStatus));
  89. }
  90. if(rwContext) {
  91. //
  92. // dump rwContext
  93. //
  94. BulkUsb_DbgPrint(3, ("rwContext->Urb             = %X\n",
  95. rwContext->Urb));
  96. BulkUsb_DbgPrint(3, ("rwContext->Mdl             = %X\n",
  97. rwContext->Mdl));
  98. BulkUsb_DbgPrint(3, ("rwContext->Length          = %d\n",
  99. rwContext->Length));
  100. BulkUsb_DbgPrint(3, ("rwContext->Numxfer         = %d\n",
  101. rwContext->Numxfer));
  102. BulkUsb_DbgPrint(3, ("rwContext->VirtualAddress  = %X\n",
  103. rwContext->VirtualAddress));
  104. BulkUsb_DbgPrint(3, ("rwContext->DeviceExtension = %X\n",
  105. rwContext->DeviceExtension));
  106. BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion::"));
  107. BulkUsb_IoDecrement(rwContext->DeviceExtension);
  108. ExFreePool(rwContext->Urb);
  109. IoFreeMdl(rwContext->Mdl);
  110. ExFreePool(rwContext);
  111. }
  112. BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - ends\n"));
  113. return ntStatus;
  114. }

4、编译过程中出来以下报错。

[plain] view plaincopy
  1. 1>LINK : error LNK2001: unresolved external symbol _mainCRTStartup
  2. 1>rwbulk.obj : error LNK2019: unresolved external symbol _printf referenced in function _OpenOneDevice@12
  3. 1>rwbulk.obj : error LNK2019: unresolved external symbol _free referenced in function _OpenOneDevice@12
  4. 1>rwbulk.obj : error LNK2019: unresolved external symbol _malloc referenced in function _OpenOneDevice@12
  5. 1>rwbulk.obj : error LNK2019: unresolved external symbol _calloc referenced in function _OpenUsbDevice@8
  6. 1>rwbulk.obj : error LNK2019: unresolved external symbol _realloc referenced in function _OpenUsbDevice@8
  7. 1>rwbulk.obj : error LNK2019: unresolved external symbol _atoi referenced in function _parse@8
  8. 1>rwbulk.obj : error LNK2019: unresolved external symbol __assert referenced in function _main

解决方法:在sources中添加USE_MSVCRT=1项.

USB WDM驱动开发实例 bulkusb相关推荐

  1. Linux USB 驱动开发实例(七)—— 基于USB 总线的无线网卡浅析

    回顾一下USB的相关知识 USB(Universal Serial Bus)总线又叫通用串行外部总线, 它是20世纪90年代发展起来的.USB接口现在得到了广泛的应用和普及,现在的PC机中都带有大量的 ...

  2. WinCE平台USB摄像头驱动开发

    (转载)http://tech.e800.com.cn/articles/2009/116/1257487620781_1.html 由于良好的性能.低廉的价格和灵活方便的特性,USB 摄像头正被广泛 ...

  3. 浅谈WinCE平台USB摄像头驱动开发流程

    转自http://tech.e800.com.cn/articles/2009/116/1257487620781_1.html 由于良好的性能.低廉的价格和灵活方便的特性,USB 摄像头正被广泛的集 ...

  4. Linux USB 驱动开发实例 (三)—— 基于USB总线的无线网卡浅析

    回顾一下USB的相关知识   USB(Universal Serial Bus)总线又叫通用串行外部总线,它是20世纪90年代发展起来的.USB接口现在得到了广泛的应用和普及,现在的PC机中都带有大量 ...

  5. Linux USB 驱动开发实例(二)—— USB 鼠标驱动注解及测试

    参考2.6.14版本中的driver/usb/input/usbmouse.c.鼠标驱动可分为几个部分:驱动加载部分.probe部分.open部分.urb回调函数处理部分. 一.驱动加载部分 [cpp ...

  6. USB鼠标驱动开发流程

    USB驱动开发,针对某一个USB设备的某个功能(接口)构建的驱动程序.USB驱动并不直接和USB设备进行数据交互,而是通过USB总线驱动程序(USB Core和USB HCD)来操作USB设备的.一般 ...

  7. Android USB转串口通信开发实例详解

    好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平板通过usb转接后与好几个外设进行通信,一直忙到最近,才慢慢闲下来,趁着这个周末不忙,记录下usb转串口通信开发的基 ...

  8. linux usb can驱动开发,linux驱动编写之十 USB初探

    通用串行总线(USB)是主机和外设设备之间的一种连接 USB的关键点在于搞懂那些连成线,连成片的各个数据结构,其实linux系统总的说来,不就是一个个的数据结构加上一些方法函数所组成的庞大的整体吗?w ...

  9. USB设备驱动开发之扩展(利用USB虚拟总线驱动模拟USB摄像头)

    fanxiushu 2016-10-08 转载或引用,请注明原始作者 做这个事情写这篇文章之前,压根没朝模拟USB摄像头这方面去想过. 直到CSDN上一位朋友提出问题,才想到还有这么一个玩意. 因此花 ...

最新文章

  1. ubuntu下载安装MaskRCNN-benchmark
  2. 自定义Linq的Distinct
  3. U盘安装win8教程(资源下载地址、远程桌面连接方法等)
  4. LeetCode Reverse Vowels of a String(字符串中元音字符反转)
  5. 第七章 前端开发——前端工程化(NPM、脚手架、前端环境搭建)
  6. hdu3037 Lucas定理
  7. Java黑皮书课后题第5章:*5.1(统计正数和负数的个数然后计算这些数的平均值)编写程序,读入未指定个数的整数,判断读入的正数有多少个、负数有多少个,然后计算输入值的总和和平均值(不记0,浮点表示)
  8. OSChina 周日乱弹 —— 我叫张一条
  9. 建立时间、保持时间与亚稳态
  10. linux部署tomcat项目404_一个tomcat下部署多个项目或一个服务器部署多个tomcat
  11. 中国教育与软件企业的共同误区
  12. 长春java培训老师
  13. 求出现重现次数最多的字母,如有多个反复的则都求出来
  14. iOS开发之单元测试
  15. 【Flutter】Dart中的var、final 和 const基本使用
  16. iptables小结
  17. 【iOS安全】iOS应用安全开发总结
  18. 高德地图纠偏 php,驾车轨迹纠偏-轨迹纠偏-示例中心-JS API 示例 | 高德地图API
  19. 2022广东最新初级消防员模拟考试试题及答案
  20. 康耐视VisionPro

热门文章

  1. 对联盟链的零星想法,欢迎指正交流
  2. 如何修复Android手机上无响应的触摸屏
  3. React上拉加载(React-PullLoad)
  4. 查看进程占用的句柄数
  5. C sharp(#)中的float,double, Single,Double关键字
  6. DLL 注入的三种方法详解
  7. 社区服务开启“云”智慧社区时代,CDN高防能否成为服务器的源动力呢?
  8. 数据结构更新中...
  9. docker启动容器
  10. python和易语言哪个好学_易语言好用还是python语言好用?