USBCCGP 获取USB设备描述符
USB设备的描述符是通过函数USBCCGP_GetDescriptors
来获取的。
descriptor.c
/* Get descriptors */
Status = USBCCGP_GetDescriptors(DeviceObject);
if (!NT_SUCCESS(Status))
{
/* Failed to start lower device */
DPRINT1("FDO_StartDevice failed to get descriptors with %x\n", Status);
return Status;
}
USB描述符主要是获取设备描述符和配置描述符。
两种设备描述符分别存储于FDO扩展结构体的DeviceDescriptor和ConfigurationDescriptor指向的内存中。
typedef struct
{
...
PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; // usb device descriptor
PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; // usb configuration descriptor
...
}FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION;
获取设备描述符
设备描述符是标准的数据结构,且数据大小已知,可一次性获取。数据结构定义如下:
//
// USB 1.1: 9.6.1 Device, Table 9-7. Standard Device Descriptor
// USB 2.0: 9.6.1 Device, Table 9-8. Standard Device Descriptor
// USB 3.0: 9.6.1 Device, Table 9-8. Standard Device Descriptor
//
typedef struct _USB_DEVICE_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
USHORT bcdUSB;
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize0;
USHORT idVendor;
USHORT idProduct;
USHORT bcdDevice;
UCHAR iManufacturer;
UCHAR iProduct;
UCHAR iSerialNumber;
UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
描述符的获取通过一个通用函数USBCCGP_GetDescriptors
来实现,而获取设备描述符的代码如下:
Status = USBCCGP_GetDescriptor(DeviceExtension->NextDeviceObject, USB_DEVICE_DESCRIPTOR_TYPE, sizeof(USB_DEVICE_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->DeviceDescriptor);
if (!NT_SUCCESS(Status))
{
//
// failed to get device descriptor
//
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
可以看到,获取设备描述符需要提定数据类型:
#define USB_DEVICE_DESCRIPTOR_TYPE 0x01
获取配置描述符
配置描述符里面不光有配置描述符,还包括可能多个接口描述符,端点描述符,在UVC设备中,甚至还包括接口关联描述符等其它接口描述符。
配置描述符的总大小因硬件设备的不同而不同,但在配置描述符的头有一个成员指定了配置描述符的总大小。所在在获取配配描述符就需要通过二次来获取,第一次只获取配置描述符的基本大小,从基本数据结构中解析出总大小后,再次获取全部的描述符。
//
// 2.0 now get basic configuration descriptor
//
Status = BulkUsbGetDescriptor(DeviceExtension->TopOfStackDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, sizeof(USB_CONFIGURATION_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
if (!NT_SUCCESS(Status))
{
ExFreePool(DeviceExtension->DeviceDescriptor);
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
DescriptorLength = DeviceExtension->ConfigurationDescriptor->wTotalLength;
//
// release basic descriptor
//
ExFreePool(DeviceExtension->ConfigurationDescriptor);
DeviceExtension->ConfigurationDescriptor = NULL;
//
// 2.1 allocate full descriptor
//
Status = BulkUsbGetDescriptor(DeviceExtension->TopOfStackDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, DescriptorLength, 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
if (!NT_SUCCESS(Status))
{
ExFreePool(DeviceExtension->DeviceDescriptor);
DeviceExtension->DeviceDescriptor = NULL;
return Status;
}
BulkUsbGetDescriptor的实现
从BulkUsbGetDescriptor的实现可以看出,其还是按照USB的规范,创建URB,然后再下发到PDO中,由PDO实现数据的组织返回。
NTSTATUS BulkUsbGetDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR DescriptorType,
IN ULONG DescriptorLength,
IN UCHAR DescriptorIndex,
IN LANGID LanguageId,
OUT PVOID* OutDescriptor)
{
PURB Urb;
NTSTATUS Status;
PVOID Descriptor;
ASSERT(DeviceObject);
ASSERT(OutDescriptor);
ASSERT(DescriptorLength);
Descriptor = ExAllocatePool(NonPagedPool, DescriptorLength);
if (!Descriptor)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(URB));
if (!Urb)
{
ExFreePool(Descriptor);
return STATUS_INSUFFICIENT_RESOURCES;
}
UsbBuildGetDescriptorRequest(Urb,
sizeof(Urb->UrbControlDescriptorRequest),
DescriptorType,
DescriptorIndex,
LanguageId,
Descriptor,
NULL,
DescriptorLength,
NULL);
// Status = CallUSBD(DeviceObject, Urb);
Status = USBCCGP_SyncUrbRequest(DeviceObject, Urb);
ExFreePool(Urb);
if (NT_SUCCESS(Status))
{
*OutDescriptor = Descriptor;
}
return Status;
}
IPR和URB的关系
IRP是WINDOWS定义的设备之间通讯的请求包,而IO_STACK_LOCATION是各层设备之间的数据栈,而URB一般用于上下层设备之间的传输,故在WINDOWS中,其作为IO_STACK_LOCATION的一个指针存在。
这点在USBCCGP_SyncUrbRequest可以得到很明显的体现。
NTSTATUS
USBCCGP_SyncUrbRequest(
IN PDEVICE_OBJECT DeviceObject,
OUT PURB UrbRequest)
{
PIRP Irp;
PIO_STACK_LOCATION IoStack;
KEVENT Event;
NTSTATUS Status;
/* Allocate irp */
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp)
{
/* No memory */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Get next stack location */
IoStack = IoGetNextIrpStackLocation(Irp);
/* Initialize stack location */
IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoStack->Parameters.Others.Argument1 = (PVOID)UrbRequest;
IoStack->Parameters.DeviceIoControl.InputBufferLength = UrbRequest->UrbHeader.Length;
Irp->IoStatus.Status = STATUS_SUCCESS;
/* Setup completion routine */
IoSetCompletionRoutine(Irp,
USBSTOR_SyncForwardIrpCompletionRoutine,
&Event,
TRUE,
TRUE,
TRUE);
/* Call driver */
Status = IoCallDriver(DeviceObject, Irp);
/* Check if request is pending */
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
/* Update status */
Status = Irp->IoStatus.Status;
}
/* Free irp */
IoFreeIrp(Irp);
/* Done */
return Status;
}
而函数数据返回的成功,也是通过完成函数来实现的,这点和上一节中USBCCGP_SyncForwardIrp
的实现大同小异,请大家注意类比,只是这里多了一个创建URB,并设备URB指针的过程。