昨天,我们看到了USB的每帧传输的最大长度,在USB的规格里面将到,HighSpeed是480Mbit/s FullSpeed 12Mbit/S,定义1毫秒传输Frame的数据,125微秒传送一MicorFrame,所以我们很好理解下面的代码:
//
// For high-speed isochronous endpoints, the bInterval value is used
// as the exponent for a 2^(bInterval-1) value expressed in
// microframes; e.g., a bInterval of 4 means a period of 8 (2^(4-1))
// microframes. The bInterval value must be from 1 to 16. NOTE: The
// USBPORT.SYS driver only supports high-speed isochronous bInterval
// values of {1, 2, 3, 4}.
//
switch (pipeInfo.Interval) {
case 1:
//
// Transfer period is every microframe (8 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8;
break;
case 2:
//
// Transfer period is every 2 microframes (4 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4;
break;
case 3:
//
// Transfer period is every 4 microframes (2 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2;
break;
case 4:
//
// Transfer period is every 8 microframes (1 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe;
break;
}
UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n",
pipeInfo.MaximumPacketSize,
pipeInfo.Interval));
UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n",
pipeContext->TransferSizePerFrame,
pipeContext->TransferSizePerMicroframe));
return STATUS_SUCCESS;
}
昨天我们省略了#if (NTDDI_VERSION >= NTDDI_WIN8)这分支,我们今天来看一下,一看这个,好像是专为WIN8准备的,USB3.0准备的,不清楚,是猜测的。我们继续往下看:
//
// We only use pipe context for super speed isoch and bulk speed bulk endpoints.
//
if ((WdfUsbPipeTypeIsochronous == pipeInfo.PipeType)) {
status = InitializePipeContextForSuperSpeedIsochPipe(DeviceContext,
WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface),
Pipe);
} else if (WdfUsbPipeTypeBulk == pipeInfo.PipeType) {
status = InitializePipeContextForSuperSpeedBulkPipe(DeviceContext,
WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface),
Pipe);
}
这里,通过PIPE的类型,我们用InitializePipeContextForSuperSpeedIsochPipe和InitializePipeContextForSuperSpeedBulkPipe来进行初始化
NTSTATUS
InitializePipeContextForSuperSpeedIsochPipe(
_In_ PDEVICE_CONTEXT DeviceContext,
_In_ UCHAR InterfaceNumber,
_In_ WDFUSBPIPE Pipe
)
/*++
Routine Description
This function validates all the isoch related fields in the endpoint descriptor
to make sure it's in comformance with the spec and Microsoft core stack
implementation and intializes the pipe context.
The TransferSizePerMicroframe and TransferSizePerFrame values will be
used in the I/O path to do read and write transfers.
Return Value:
NT status value
-*/
{
WDF_USB_PIPE_INFORMATION pipeInfo;
PPIPE_CONTEXT pipeContext;
UCHAR endpointAddress;
PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor;
PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor;
USHORT wMaxPacketSize;
UCHAR bMaxBurst;
UCHAR bMult;
USHORT wBytesPerInterval;
PAGED_CODE();
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
//
// We use the pipe context only for isoch endpoints.
//
if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) {
return STATUS_SUCCESS;
}
pipeContext = GetPipeContext(Pipe);
endpointAddress = pipeInfo.EndpointAddress;
pEndpointDescriptor = GetEndpointDescriptorForEndpointAddress(
DeviceContext,
InterfaceNumber,
endpointAddress,
&pEndpointCompanionDescriptor);
if (pEndpointDescriptor == NULL || pEndpointCompanionDescriptor == NULL ){
UsbSamp_DbgPrint(1, ("pEndpointDescriptor or pEndpointCompanionDescriptor is invalid (NULL)\n"));
return STATUS_INVALID_PARAMETER;
}
//
// For SuperSpeed isoch endpoint, it uses wBytesPerInterval from its
// endpoint companion descriptor. If bMaxBurst field in its endpoint
// companion descriptor is greater than zero, wMaxPacketSize must be
// 1024. If the value in the bMaxBurst field is set to zero then
// wMaxPacketSize can have any value from 0 to 1024.
//
wBytesPerInterval = pEndpointCompanionDescriptor->wBytesPerInterval;
wMaxPacketSize = pEndpointDescriptor->wMaxPacketSize;
bMaxBurst = pEndpointCompanionDescriptor->bMaxBurst;
bMult = pEndpointCompanionDescriptor->bmAttributes.Isochronous.Mult;
if (wBytesPerInterval > (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1))){
UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints's wBytesPerInterval value (%d) is greater than wMaxPacketSize * (bMaxBurst+1) * (Mult +1) (%d) \n",
wBytesPerInterval, (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1))));
return STATUS_INVALID_PARAMETER;
}
if (bMaxBurst > 0){
if (wMaxPacketSize != USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){
UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value of %d bytes when bMaxpBurst is %d \n",
USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst));
return STATUS_INVALID_PARAMETER;
}
} else {
if (wMaxPacketSize > USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){
UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value no more than %d bytes when bMaxpBurst is %d \n",
USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst));
return STATUS_INVALID_PARAMETER;
}
}
//
// This sample demos how to use wBytesPerInterval from its Endpoint
// Companion Descriptor. Actaully, for Superspeed isochronous endpoints,
// MaximumPacketSize in WDF_USB_PIPE_INFORMATION and USBD_PIPE_INFORMATION
// is returned with the value of wBytesPerInterval in the endpoint
// companion descriptor. This is different than the true MaxPacketSize of
// the endpoint descriptor.
//
NT_ASSERT(pipeInfo.MaximumPacketSize == wBytesPerInterval);
pipeContext->TransferSizePerMicroframe = wBytesPerInterval;
//
// Microsoft USB 3.0 stack only supports bInterval value of 1, 2, 3 and 4
// (or polling period of 1, 2, 4 and 8).
// For super-speed isochronous endpoints, the bInterval value is used as
// the exponent for a 2^(bInterval-1) value expressed in microframes;
// e.g., a bInterval of 4 means a period of 8 (2^(4-1)) microframes.
//
if (pipeInfo.Interval == 0 || pipeInfo.Interval > 4) {
UsbSamp_DbgPrint(1, ("bInterval value in pipeInfo is invalid (0 or > 4)\n"));
return STATUS_INVALID_PARAMETER;
}
switch (pipeInfo.Interval) {
case 1:
//
// Transfer period is every microframe (8 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8;
break;
case 2:
//
// Transfer period is every 2 microframes (4 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4;
break;
case 3:
//
// Transfer period is every 4 microframes (2 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2;
break;
case 4:
//
// Transfer period is every 8 microframes (1 times a frame).
//
pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe;
break;
}
UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n",
pipeInfo.MaximumPacketSize,
pipeInfo.Interval));
UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n",
pipeContext->TransferSizePerFrame,
pipeContext->TransferSizePerMicroframe));
return STATUS_SUCCESS;
}
这个函数就是前半部分跟我们的HighSpeed和FullSpeed不同,后面基本一样,这里主要调用了GetEndpointDescriptorForEndpointAddress这个函数,我们来看下这个函数:
PUSB_ENDPOINT_DESCRIPTOR
GetEndpointDescriptorForEndpointAddress(
_In_ PDEVICE_CONTEXT DeviceContext,
_In_ UCHAR InterfaceNumber,
_In_ UCHAR EndpointAddress,
_Out_ PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR *ppEndpointCompanionDescriptor
)
/*++
Routine Description:
The helper routine gets the Endpoint Descriptor matched with EndpointAddress and return
its Endpoint Companion Descriptor if it has.
Arguments:
DeviceContext - pointer to the device context which includes configuration descriptor
InterfaceNumber - InterfaceNumber of selected interface
EndpointAddress - EndpointAddress of the Pipe
ppEndpointCompanionDescriptor - pointer to the Endpoint Companioin Descroptor pointer
Return Value:
Pointer to Endpoint Descriptor
--*/
{
PUSB_COMMON_DESCRIPTOR pCommonDescriptorHeader = NULL;
PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor = NULL;
PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor = NULL;
PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor = NULL;
PUCHAR startingPosition;
ULONG index;
BOOLEAN found = FALSE;
PAGED_CODE();
pConfigurationDescriptor = DeviceContext->UsbConfigurationDescriptor;
*ppEndpointCompanionDescriptor = NULL;
//
// Parse the ConfigurationDescriptor (including all Interface and
// Endpoint Descriptors) and locate a Interface Descriptor which
// matches the InterfaceNumber, AlternateSetting, InterfaceClass,
// InterfaceSubClass, and InterfaceProtocol parameters.
//
pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
pConfigurationDescriptor,
pConfigurationDescriptor,
InterfaceNumber,
DeviceContext->SelectedAlternateSetting,
-1, // InterfaceClass, don't care
-1, // InterfaceSubClass, don't care
-1 // InterfaceProtocol, don't care
);
if (pInterfaceDescriptor == NULL ) {
UsbSamp_DbgPrint(1, ("USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.\n"));
goto End;
}
startingPosition = (PUCHAR) pInterfaceDescriptor;
for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++) {
pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor,
pConfigurationDescriptor->wTotalLength,
startingPosition,
USB_ENDPOINT_DESCRIPTOR_TYPE);
if (pCommonDescriptorHeader == NULL) {
UsbSamp_DbgPrint(1, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Descriptor unexpectedly\n"));
goto End;
}
pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader;
//
// Search an Endpoint Descriptor that matches the EndpointAddress
//
if (pEndpointDescriptor->bEndpointAddress == EndpointAddress){
found = TRUE;
break;
}
//
// Skip the current Endpoint Descriptor and search for the next.
//
startingPosition = (PUCHAR)pCommonDescriptorHeader + pCommonDescriptorHeader->bLength;
}
if (found) {
//
// Locate the SuperSpeed Endpoint Companion Descriptor associated with the endpoint descriptor
//
pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor,
pConfigurationDescriptor->wTotalLength,
pEndpointDescriptor,
USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE);
if (pCommonDescriptorHeader != NULL) {
*ppEndpointCompanionDescriptor =
(PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader;
} else {
UsbSamp_DbgPrint(3, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly\n"));
}
}
End:
return pEndpointDescriptor;
}
这个函数返回一个端点的描述符,首先调用USBD_ParseConfigurationDescriptorEx函数,分析得到选择的USB接口描述符,从接口描述符的个数中,分析配置描述符,然后得到端点描述符。通过,之前的PIPE中的端点地址和端点描述符中的地址进行比对,如果一样返回。
typedef struct _USB_COMMON_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
} USB_COMMON_DESCRIPTOR, *PUSB_COMMON_DESCRIPTOR;
typedef struct _USB_CONFIGURATION_DESCRIPTOR {
UCHAR bLength ;
UCHAR bDescriptorType ;
USHORT wTotalLength ;
UCHAR bNumInterfaces ;
UCHAR bConfigurationValue;
UCHAR iConfiguration ;
UCHAR bmAttributes ;
UCHAR MaxPower ;
} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR ;
typedef struct _USB_ENDPOINT_DESCRIPTOR {
UCHAR bLength ;
UCHAR bDescriptorType ;
UCHAR bEndpointAddress ;
UCHAR bmAttributes ;
USHORT wMaxPacketSize ;
UCHAR bInterval ;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR ;
我们再来看InitializePipeContextForSuperSpeedBulkPipe这个函数:
NTSTATUS
InitializePipeContextForSuperSpeedBulkPipe(
_In_ PDEVICE_CONTEXT DeviceContext,
_In_ UCHAR InterfaceNumber,
_In_ WDFUSBPIPE Pipe
)
/*++
Routine Description:
This routine initialize pipe's streams context.
Arguments:
DeviceContext - pointer to device context
InterfaceNumber - InterfaceNumber of selected interface
Pipe - Bullk Pipe
Return Value:
NTSTATUS
--*/
{
WDF_USB_PIPE_INFORMATION pipeInfo;
PPIPE_CONTEXT pipeContext;
PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor;
PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor;
UCHAR endpointAddress;
ULONG maxStreams;
ULONG supportedStreams;
PUSBSAMP_STREAM_INFO pStreamInfo;
NTSTATUS status;
PURB pUrb = NULL;
ULONG i;
PAGED_CODE();
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
pipeContext = GetPipeContext(Pipe);
pStreamInfo = &pipeContext->StreamInfo;
pStreamInfo->NumberOfStreams = 0;
pStreamInfo->StreamList = NULL;
pipeContext->StreamConfigured = FALSE;
//
// Validate that the endpoint/pipe is of type BULK.
// Streams are only allowed on a SS BULK endpoint.
// Also ensure that the bus actually supports streams
// before proceeding
//
if (pipeInfo.PipeType != WdfUsbPipeTypeBulk ||
DeviceContext->NumberOfStreamsSupportedByController == 0) {
status = STATUS_SUCCESS;
goto End;
}
endpointAddress = pipeInfo.EndpointAddress;
pEndpointDescriptor = GetEndpointDescriptorForEndpointAddress(
DeviceContext,
InterfaceNumber,
endpointAddress,
&pEndpointCompanionDescriptor);
if (pEndpointDescriptor != NULL &&
pEndpointCompanionDescriptor != NULL) {
maxStreams = pEndpointCompanionDescriptor->bmAttributes.Bulk.MaxStreams;
if (maxStreams == 0) {
supportedStreams = 0;
} else {
supportedStreams = 1 << maxStreams;
}
} else {
UsbSamp_DbgPrint(1, ("Endpoint Descriptor or Endpoint Companion Descriptor is NULL.\n"));
status = STATUS_INVALID_PARAMETER;
goto End;
}
if (supportedStreams == 0) {
status = STATUS_SUCCESS;
goto End;
}
if (supportedStreams > DeviceContext->NumberOfStreamsSupportedByController) {
supportedStreams = DeviceContext->NumberOfStreamsSupportedByController;
}
pStreamInfo->NumberOfStreams = supportedStreams;
pStreamInfo->StreamList = ExAllocatePoolWithTag(
NonPagedPool,
supportedStreams * sizeof(USBD_STREAM_INFORMATION),
POOL_TAG);
if (pStreamInfo->StreamList == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto End;
}
for(i = 0; i < supportedStreams; i++)
{
pStreamInfo->StreamList[i].StreamID = i + 1;
}
status = USBD_UrbAllocate(DeviceContext->UsbdHandle, &pUrb);
if (!NT_SUCCESS(status)){
pUrb = NULL;
status = STATUS_INSUFFICIENT_RESOURCES;
goto End;
}
pUrb->UrbOpenStaticStreams.Hdr.Length = sizeof(struct _URB_OPEN_STATIC_STREAMS);
pUrb->UrbOpenStaticStreams.Hdr.Function = URB_FUNCTION_OPEN_STATIC_STREAMS;
pUrb->UrbOpenStaticStreams.PipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(Pipe);
pUrb->UrbOpenStaticStreams.NumberOfStreams = pStreamInfo->NumberOfStreams;
pUrb->UrbOpenStaticStreams.StreamInfoSize = sizeof(USBD_STREAM_INFORMATION);
pUrb->UrbOpenStaticStreams.Streams = pStreamInfo->StreamList;
// Send the URB down the stack
status = WdfUsbTargetPipeSendUrbSynchronously(
Pipe,
NULL,
NULL,
pUrb
);
if (NT_SUCCESS(status)) {
pipeContext->StreamConfigured = TRUE;
}
End:
if (!NT_SUCCESS(status)) {
pipeContext->StreamConfigured = FALSE;
pStreamInfo->NumberOfStreams = 0;
if (pStreamInfo->StreamList != NULL) {
ExFreePool(pStreamInfo->StreamList);
pStreamInfo->StreamList = NULL;
}
}
if(pUrb != NULL) {
USBD_UrbFree(DeviceContext->UsbdHandle, pUrb);
}
return status;
}
前面的代码跟之前的同步传输一样,也是获得端点描述符,只是后面需要目标的管道发送一个URB进行配置,主要是包括stream方面的。我们在这里先看下URB的结构后续还要进行讲解
typedef struct _URB {
union {
struct _URB_HEADER UrbHeader;
struct _URB_SELECT_INTERFACE UrbSelectInterface;
struct _URB_SELECT_CONFIGURATION UrbSelectConfiguration;
struct _URB_PIPE_REQUEST UrbPipeRequest;
struct _URB_FRAME_LENGTH_CONTROL UrbFrameLengthControl;
struct _URB_GET_FRAME_LENGTH UrbGetFrameLength;
struct _URB_SET_FRAME_LENGTH UrbSetFrameLength;
struct _URB_GET_CURRENT_FRAME_NUMBER UrbGetCurrentFrameNumber;
struct _URB_CONTROL_TRANSFER UrbControlTransfer;
struct _URB_BULK_OR_INTERRUPT_TRANSFER UrbBulkOrInterruptTransfer;
struct _URB_ISOCH_TRANSFER UrbIsochronousTransfer;
struct _URB_CONTROL_DESCRIPTOR_REQUEST UrbControlDescriptorRequest;
struct _URB_CONTROL_GET_STATUS_REQUEST UrbControlGetStatusRequest;
struct _URB_CONTROL_FEATURE_REQUEST UrbControlFeatureRequest;
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST UrbControlVendorClassRequest;
struct _URB_CONTROL_GET_INTERFACE_REQUEST UrbControlGetInterfaceRequest;
struct _URB_CONTROL_GET_CONFIGURATION_REQUEST UrbControlGetConfigurationRequest;
}
} URB, *PURB ;
struct _URB_BULK_OR_INTERRUPT_TRANSFER {
struct _URB_HEADER Hdr;
USBD_PIPE_HANDLE PipeHandle;
ULONG TransferFlags;
ULONG TransferBufferLength;
PVOID TransferBuffer;
PMDL TransferBufferMDL;
struct _URB *UrbLink;
.
};
这里发送的配置。后续需要了解下WIN8对USB驱动这边的修改。至此,USB设备的配置过程结束了,后续就要进行数据的真正的传输等操作。今天有点感冒,所以今天就到这里,后续继续。