论程序员的心态,保持一个平常心,对自己的事业来说,无比的的重要,这个不光光对程序员这个职业,对其他职业也是,冲动,浮躁是魔鬼,本来这个社会就已经很浮躁,自己身处之中,如果还能保持一个平常心,就显得更加的难能可贵了。我只能说,平常心,以及自己这种心态,如果能够保持,就是自己成熟的最最明显的标志。不要去羡慕别人,应该更多的给与鼓励。自己现在的心态保持的非常好,但是要做到游刃有余,可能还需要一些时间的磨练。加油,自己。
前面对Windows的USB驱动开发,分别介绍了一下,USB的几种描述符,还有几种传输模式,USB涉及的知识非常多,驱动堆栈的层次跟SCSI,以及文件系统的驱动堆栈的复杂程度有一拼,所以,这个Windows USB驱动的文章会一直下去,做最大的努力,让其涉及到USB所有的方方面面。今天,我们先粗略的看一下WDK中intel的一个USB设备驱动的例子,目前是关注这个流程,详细的数据结构的解释,后续会加上。
这个例子,基本上也是在微软最新的WDF框架驱动模型中开发的。所以前面的部分跟一般的WDF驱动开发的模型一致。
先看下DriverEntry函数:
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: DriverEntry initializes the driver and is the first routine called by the system after the driver is loaded. Parameters Description: DriverObject - represents the instance of the function driver that is loaded into memory. DriverEntry must initialize members of DriverObject before it returns to the caller. DriverObject is allocated by the system before the driver is loaded, and it is released by the system after the system unloads the function driver from memory. RegistryPath - represents the driver specific path in the Registry. The function driver can use the path to store driver related data between reboots. The path does not store hardware instance specific data. Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise. --*/ { WDF_DRIVER_CONFIG config; NTSTATUS status; UsbSamp_DbgPrint(3, ("UsbSamp Driver Sample - Driver Framework Edition.\n")); UsbSamp_DbgPrint(3, ("Built %s %s\n", __DATE__, __TIME__)); // // Initiialize driver config to control the attributes that // are global to the driver. Note that framework by default // provides a driver unload routine. If you create any resources // in the DriverEntry and want to be cleaned in driver unload, // you can override that by manually setting the EvtDriverUnload in the // config structure. In general xxx_CONFIG_INIT macros are provided to // initialize most commonly used members. // WDF_DRIVER_CONFIG_INIT( &config, UsbSamp_EvtDeviceAdd ); // // Create a framework driver object to represent our driver. // status = WdfDriverCreate( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes &config, // Driver Config Info WDF_NO_HANDLE // hDriver ); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfDriverCreate failed with status 0x%x\n", status)); } return status; }
这里没有什么好讲的,创建驱动对象。注册了创建设备对象的函数UsbSamp_EvtDeviceAdd
下面我们再来看一下UsbSamp_EvtDeviceAdd这个函数:
NTSTATUS UsbSamp_EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: EvtDeviceAdd is called by the framework in response to AddDevice call from the PnP manager. We create and initialize a device object to represent a new instance of the device. All the software resources should be allocated in this callback. Arguments: Driver - Handle to a framework driver object created in DriverEntry DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. Return Value: NTSTATUS --*/ { WDF_FILEOBJECT_CONFIG fileConfig; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_OBJECT_ATTRIBUTES fdoAttributes; WDF_OBJECT_ATTRIBUTES fileObjectAttributes; WDF_OBJECT_ATTRIBUTES requestAttributes; WDF_OBJECT_ATTRIBUTES queueAttributes; NTSTATUS status; WDFDEVICE device; WDF_DEVICE_PNP_CAPABILITIES pnpCaps; WDF_IO_QUEUE_CONFIG ioQueueConfig; PDEVICE_CONTEXT pDevContext; WDFQUEUE queue; ULONG maximumTransferSize; UNREFERENCED_PARAMETER(Driver); UsbSamp_DbgPrint (3, ("UsbSamp_EvtDeviceAdd routine\n")); PAGED_CODE(); // // Initialize the pnpPowerCallbacks structure. Callback events for PNP // and Power are specified here. If you don't supply any callbacks, // the Framework will take appropriate default actions based on whether // DeviceInit is initialized to be an FDO, a PDO or a filter device // object. // WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); pnpPowerCallbacks.EvtDevicePrepareHardware = UsbSamp_EvtDevicePrepareHardware; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); // // Initialize the request attributes to specify the context size and type // for every request created by framework for this device. // WDF_OBJECT_ATTRIBUTES_INIT(&requestAttributes); WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&requestAttributes, REQUEST_CONTEXT); WdfDeviceInitSetRequestAttributes(DeviceInit, &requestAttributes); // // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the // framework whether you are interested in handle Create, Close and // Cleanup requests that gets genereate when an application or another // kernel component opens an handle to the device. If you don't register // the framework default behaviour would be complete these requests // with STATUS_SUCCESS. A driver might be interested in registering these // events if it wants to do security validation and also wants to maintain // per handle (fileobject) context. // WDF_FILEOBJECT_CONFIG_INIT( &fileConfig, UsbSamp_EvtDeviceFileCreate, WDF_NO_EVENT_CALLBACK, WDF_NO_EVENT_CALLBACK ); // // Specify a context for FileObject. If you register FILE_EVENT callbacks, // the framework by default creates a framework FILEOBJECT corresponding // to the WDM fileobject. If you want to track any per handle context, // use the context for FileObject. Driver that typically use FsContext // field should instead use Framework FileObject context. // WDF_OBJECT_ATTRIBUTES_INIT(&fileObjectAttributes); WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fileObjectAttributes, FILE_CONTEXT); WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, &fileObjectAttributes); #if !defined(BUFFERED_READ_WRITE) // // I/O type is Buffered by default. We want to do direct I/O for Reads // and Writes so set it explicitly. Please note that this sample // can do isoch transfer only if the io type is directio. // WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); #endif // // Now specify the size of device extension where we track per device // context.DeviceInit is completely initialized. So call the framework // to create the device and attach it to the lower stack. // WDF_OBJECT_ATTRIBUTES_INIT(&fdoAttributes); WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fdoAttributes, DEVICE_CONTEXT); status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfDeviceCreate failed with Status code %!STATUS!\n", status)); return status; } // // Get the DeviceObject context by using accessor function specified in // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT. // pDevContext = GetDeviceContext(device); // //Get MaximumTransferSize from registry // maximumTransferSize=0; status = UsbSamp_ReadFdoRegistryKeyValue(Driver, L"MaximumTransferSize", &maximumTransferSize); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("UsbSamp_ReadFdoRegistryKeyValue failed with Status code %!STATUS!\n", status)); return status; } if(maximumTransferSize){ pDevContext->MaximumTransferSize = maximumTransferSize; } else { pDevContext->MaximumTransferSize=DEFAULT_REGISTRY_TRANSFER_SIZE; } // // Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so // that you don't get the popup in usermode (on Win2K) when you surprise // remove the device. // WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); pnpCaps.SurpriseRemovalOK = WdfTrue; WdfDeviceSetPnpCapabilities(device, &pnpCaps); // // Register I/O callbacks to tell the framework that you are interested // in handling WdfRequestTypeRead, WdfRequestTypeWrite, and IRP_MJ_DEVICE_CONTROL requests. // WdfIoQueueDispatchParallel means that we are capable of handling // all the I/O request simultaneously and we are responsible for protecting // data that could be accessed by these callbacks simultaneously. // This queue will be, by default, automanaged by the framework with // respect to PNP and Power events. That is, framework will take care // of queuing, failing, dispatching incoming requests based on the current // pnp/power state of the device. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchParallel); ioQueueConfig.EvtIoRead = UsbSamp_EvtIoRead; ioQueueConfig.EvtIoWrite = UsbSamp_EvtIoWrite; ioQueueConfig.EvtIoDeviceControl = UsbSamp_EvtIoDeviceControl; ioQueueConfig.EvtIoStop = UsbSampEvtIoStop; ioQueueConfig.EvtIoResume = UsbSampEvtIoResume; status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);// pointer to default queue if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for Default Queue %!STATUS!\n", status)); return status; } WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchParallel); WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes); queueAttributes.SynchronizationScope=WdfSynchronizationScopeQueue; ioQueueConfig.EvtIoRead = UsbSamp_EvtIsochRead; status = WdfIoQueueCreate(device, &ioQueueConfig, &queueAttributes, &pDevContext->IsochReadQ);// pointer to IsochRead queue if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for IsochRead Queue %!STATUS!\n", status)); return status; } WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchParallel); WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes); queueAttributes.SynchronizationScope=WdfSynchronizationScopeQueue; ioQueueConfig.EvtIoWrite = UsbSamp_EvtIsochWrite; status = WdfIoQueueCreate(device, &ioQueueConfig, &queueAttributes, &pDevContext->IsochWriteQ);// pointer to IsochWrite queue if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for IsochWrite Queue %!STATUS!\n", status)); return status; } // // Register a device interface so that app can find our device and talk to it. // status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &GUID_CLASS_USBSAMP_USB, NULL);// Reference String if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status)); return status; } UsbSamp_DbgPrint(3, ("EvtDriverDeviceAdd - ends\n")); return status; }
这个创建设备对象的函数,跟一般的WDF创建设备对象的函数基本一致:
设置了一个资源分配的函数 UsbSamp_EvtDevicePrepareHardware,在这个函数里取得资源。
然后,设置了文件对象创建的回调函数UsbSamp_EvtDeviceFileCreate,我们知道文件对象是在这个设备堆栈的最上层。当上层通过接口打开这个设备的时候,框架会调用这个回调函数。后面有继续设置了IO空间的访问方式,这里有一个宏来区分两种不同的处理,如果不是Buffered形式就是,Directed形式。WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); 然后调用WdfDeviceCreate创建了设备对象。我们在上一节提到过,在描述符里面有一个域,指定传输的最大包的字节数,这里根据不同设备属性可以更改,有些设备没写到FW里面,而是直接在驱动的INF中配置到注册表中。所以这里,从注册表中驱动这个值。然后设置了PNP的属性,设备支持随时拔除。下面创建了一个队列对象,来存储过来的读,写,停止,恢复,Control请求。然后创建了一个同步读和一个同步写的队列对象,来存储这两种请求,因为这个是摄像头设备,所以支持同步传输。下面创建了一个设备接口,可以供上层或驱动调用。
下面来看以下,UsbSamp_EvtDevicePrepareHardware这个函数:
NTSTATUS UsbSamp_EvtDevicePrepareHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourceList, IN WDFCMRESLIST ResourceListTranslated ) /*++ Routine Description: In this callback, the driver does whatever is necessary to make the hardware ready to use. In the case of a USB device, this involves reading and selecting descriptors. //TODO: Arguments: Device - handle to a device Return Value: NT status value --*/ { NTSTATUS status; PDEVICE_CONTEXT pDeviceContext; WDF_USB_DEVICE_INFORMATION info; UNREFERENCED_PARAMETER(ResourceList); UNREFERENCED_PARAMETER(ResourceListTranslated); UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - begins\n")); PAGED_CODE(); pDeviceContext = GetDeviceContext(Device); // // Read the device descriptor, configuration descriptor // and select the interface descriptors // status = ReadAndSelectDescriptors(Device); if (!NT_SUCCESS(status)) { UsbSamp_DbgPrint(1, ("ReadandSelectDescriptors failed\n")); return status; } WDF_USB_DEVICE_INFORMATION_INIT(&info); // // Retrieve USBD version information, port driver capabilites and device // capabilites such as speed, power, etc. // status = WdfUsbTargetDeviceRetrieveInformation(pDeviceContext->WdfUsbTargetDevice, &info); if (NT_SUCCESS(status)) { pDeviceContext->IsDeviceHighSpeed = (info.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? TRUE : FALSE; UsbSamp_DbgPrint(3, ("DeviceIsHighSpeed: %s\n", pDeviceContext->IsDeviceHighSpeed ? "TRUE" : "FALSE")); } else { pDeviceContext->IsDeviceHighSpeed = FALSE; } UsbSamp_DbgPrint(3, ("IsDeviceSelfPowered: %s\n", (info.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE")); pDeviceContext->WaitWakeEnable = info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE; UsbSamp_DbgPrint(3, ("IsDeviceRemoteWakeable: %s\n", (info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE) ? "TRUE" : "FALSE")); // // Enable wait-wake and idle timeout if the device supports it // if(pDeviceContext->WaitWakeEnable){ status = UsbSampSetPowerPolicy(Device); if (!NT_SUCCESS (status)) { UsbSamp_DbgPrint(3, ("UsbSampSetPowerPolicy failed\n")); return status; } } UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - ends\n")); return status; }
从这个函数开始,真正涉及到了USB方面,我们一步一步来看:
首先调用了ReadAndSelectDescriptors这个函数。
NTSTATUS
ReadAndSelectDescriptors(
IN WDFDEVICE Device
)
/*++
Routine Description:
This routine configures the USB device.
In this routines we get the device descriptor,
the configuration descriptor and select the
configuration.
Arguments:
Device - Handle to a framework device
Return Value:
NTSTATUS - NT status value.
--*/
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
PAGED_CODE();
//
// initialize variables
//
pDeviceContext = GetDeviceContext(Device);
//
// Create a USB device handle so that we can communicate with the
// underlying USB stack. The WDFUSBDEVICE handle is used to query,
// configure, and manage all aspects of the USB device.
// These aspects include device properties, bus properties,
// and I/O creation and synchronization. We only create device the first
// the PrepareHardware is called. If the device is restarted by pnp manager
// for resource rebalance, we will use the same device handle but then select
// the interfaces again because the USB stack could reconfigure the device on
// restart.
//
if (pDeviceContext->WdfUsbTargetDevice == NULL) {
status = WdfUsbTargetDeviceCreate(Device,
WDF_NO_OBJECT_ATTRIBUTES,
&pDeviceContext->WdfUsbTargetDevice);
if (!NT_SUCCESS(status)) {
return status;
}
}
WdfUsbTargetDeviceGetDeviceDescriptor(pDeviceContext->WdfUsbTargetDevice,
&pDeviceContext->UsbDeviceDescriptor);
ASSERT(pDeviceContext->UsbDeviceDescriptor.bNumConfigurations);
status = ConfigureDevice(Device);
return status;
}
这个函数首先创建了一个和USB设备堆栈进行连接的目标设备对象,为什么要在这么早的情况下,创建这个目标设备对象的原因,可以看一下 英文的说明,因为USB的设备经常需要更换资源,重启设备。用这个接口可以使用WdfUsbTargetDeviceGetDeviceDescriptor来得到设备描述符,使用WdfUsbTargetDeviceRetrieveConfigDescriptor来得到控制描述符,一般,我们会调用两次WdfUsbTargetDeviceRetrieveConfigDescriptor,第一次,驱动这个控制描述符的大小, status = WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice,NULL,&size);大小得到后,可以根据这个大小,来分配内存,然后再调用WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice,configurationDescriptor,&size);真正得到控制描述符。然后根据设备,来选择控制描述符。
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams);
status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->WdfUsbTargetDevice,
WDF_NO_OBJECT_ATTRIBUTES,
&configParams);
这里有一个接口,使用的是WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE这个宏,然后得到接口的数量WdfUsbTargetDeviceGetNumInterfaces(pDeviceContext->WdfUsbTargetDevice)。我们再回到UsbSamp_EvtDevicePrepareHardware这个函数,下面调用了WdfUsbTargetDeviceRetrieveInformation(pDeviceContext->WdfUsbTargetDevice,&info); 看一下这个info的数据结构:
typedef struct _WDF_USB_DEVICE_INFORMATION {
ULONG Size;
USBD_VERSION_INFORMATION UsbdVersionInformation;
ULONG HcdPortCapabilities;
ULONG Traits;
} WDF_USB_DEVICE_INFORMATION, *PWDF_USB_DEVICE_INFORMATION;
这个基本没有什么,驱动USB的BCD格式的版本号。Traits是设备支持的模式,
typedef enum _WDF_USB_DEVICE_TRAITS {
WDF_USB_DEVICE_TRAIT_SELF_POWERED = 0x00000001,
WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE = 0x00000002,
WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED = 0x00000004,
} WDF_USB_DEVICE_TRAITS;
如果设备支持WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE,设置一下设备空闲处理,以及一些S0状态下的设备唤醒。
下面我们可以只用看一下UsbSamp_EvtDeviceFileCreate这个函数,就是在应用程序或者另外的模块打开这个USB设备时候,这个回调函数会被调用,这个函数主要做一些必须是在有访问请求时,才可配置的资源。
VOID UsbSamp_EvtDeviceFileCreate( IN WDFDEVICE Device, IN WDFREQUEST Request, IN WDFFILEOBJECT FileObject ) /*++ Routine Description: The framework calls a driver's EvtDeviceFileCreate callback when the framework receives an IRP_MJ_CREATE request. The system sends this request when a user application opens the device to perform an I/O operation, such as reading or writing a file. This callback is called synchronously, in the context of the thread that created the IRP_MJ_CREATE request. Arguments: Device - Handle to a framework device object. FileObject - Pointer to fileobject that represents the open handle. CreateParams - copy of the create IO_STACK_LOCATION Return Value: NT status code --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PUNICODE_STRING fileName; PFILE_CONTEXT pFileContext; PDEVICE_CONTEXT pDevContext; WDFUSBPIPE pipe; UsbSamp_DbgPrint(3, ("EvtDeviceFileCreate - begins\n")); PAGED_CODE(); // // initialize variables // pDevContext = GetDeviceContext(Device); pFileContext = GetFileContext(FileObject); fileName = WdfFileObjectGetFileName(FileObject); if (0 == fileName->Length) { // // opening a device as opposed to pipe. // status = STATUS_SUCCESS; } else { pipe = GetPipeFromName(pDevContext, fileName); if (pipe != NULL) { // // found a match // pFileContext->Pipe = pipe; WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe); status = STATUS_SUCCESS; } else { status = STATUS_INVALID_DEVICE_REQUEST; } } WdfRequestComplete(Request, status); UsbSamp_DbgPrint(3, ("EvtDeviceFileCreate - ends\n")); return; }
这个函数,基本没有做什么事情,当上层发送一个打开设备的请求时,框架会创建一个文件对象,并调用这个函数,这里主要调用了GetPipeFromName,这个函数:
WDFUSBPIPE
GetPipeFromName(
IN PDEVICE_CONTEXT DeviceContext,
IN PUNICODE_STRING FileName
)
/*++
Routine Description:
This routine will pass the string pipe name and
fetch the pipe handle.
Arguments:
DeviceContext - pointer to Device Context
FileName - string pipe name
Return Value:
The device extension maintains a pipe context for
the pipes on 82930 board.
--*/
{
LONG ix;
ULONG uval;
ULONG nameLength;
ULONG umultiplier;
WDFUSBPIPE pipe = NULL;
PAGED_CODE();
//
// typedef WCHAR *PWSTR;
//
nameLength = (FileName->Length / sizeof(WCHAR));
UsbSamp_DbgPrint(3, ("UsbSamp_PipeWithName - begins\n"));
if(nameLength != 0) {
UsbSamp_DbgPrint(3, ("Filename = %wZ nameLength = %d\n", FileName, nameLength));
//
// Parse the pipe#
//
ix = nameLength - 1;
// if last char isn't digit, decrement it.
while((ix > -1) &&
((FileName->Buffer[ix] < (WCHAR) '0') ||
(FileName->Buffer[ix] > (WCHAR) '9'))) {
ix--;
}
if (ix > -1) {
uval = 0;
umultiplier = 1;
// traversing least to most significant digits.
while((ix > -1) &&
(FileName->Buffer[ix] >= (WCHAR) '0') &&
(FileName->Buffer[ix] <= (WCHAR) '9')) {
uval += (umultiplier *
(ULONG) (FileName->Buffer[ix] - (WCHAR) '0'));
ix--;
umultiplier *= 10;
}
pipe = WdfUsbInterfaceGetConfiguredPipe(
DeviceContext->UsbInterface,
(UCHAR)uval, //PipeIndex,
NULL
);
}
}
UsbSamp_DbgPrint(3, ("UsbSamp_PipeWithName - ends\n"));
return pipe;
}
看代码的意思是从文件对象中驱动文件名,通过这个文件名驱动管道号,也就是PIPE号,DeviceContext->UsbInterface是在前面赋的值 pDeviceContext->UsbInterface =
configParams.Types.SingleInterface.ConfiguredUsbInterface;我们再回到UsbSamp_EvtDeviceFileCreate这个函数,它下面调用了WdfUsbTargetPipeSetNoMaximumPacketSizeCheck检查USB管道能够传输的最大包的长度。到现在,基本的配置工作都已经完成了,现在就是开始进行读写,停止,恢复,同步读,同步写,IOCTL等请求的处理了,这里还涉及到一些USB相关的结构,后续继续分析。