前面的博客已经分析了USB摄像头驱动程序的框架,我们知道了USB摄像头驱动程序的重点在于1)描述符的分析;2)属性的控制(通过VC来设置);3)格式的选择(通过VS来设置);4)数据的获得(通过VS的URB来获得)。后面的博客就会从这4个方面进行深入的分析,本篇博客首先来看一下UVC驱动程序的描述符分析。
回顾之前的内容https://www.cnblogs.com/-glb/p/11568992.html,每一个USB设备都有一个设备描述符,设备描述符中有配置描述符,配置描述符中有接口描述符,接口描述符中有端点描述符。
在进行分析之前,首先来看一张USB摄像头的描述符的布局
白色的描述符是每个usb设备都要支持的,灰色的部分是UVC规范自己定义的。本篇博客将围绕这张图进行讲解,将图中的描述符打印出来,来个直观的感受。
1.史上最简单的USB摄像头驱动程序
Myuvc.c
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/version.h>
#include <asm/unaligned.h>
#include <media/v4l2-common.h>
static int myuvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static int cnt = 0;
printk("myuvc_probe : cnt
", cnt++);
return 0;
}
static void myuvc_disconnect(struct usb_interface *intf)
{
static int cnt = 0;
printk("myuvc_probe : cnt
", cnt++);
}
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
};
/*1.分配一个usb driver结构体*/
static struct usb_driver myuvc_driver = {
.name = "myuvc",
.probe = myuvc_probe,
.disconnect = myuvc_disconnect,
.id_table = myuvc_ids,
};
/*2.设置*/
static int myuvc_init(void)
{
/*3.注册*/
usb_register(&myuvc_driver);
}
static void myuvc_exit(void)
{
usb_deregister(&myuvc_driver);
}
module_init(myuvc_init);
module_exit(myuvc_exit);
MODULE_LICENSE("GPL v2");
1.1分析USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0)
USB_INTERFACE_INFO是一个宏,首先看一下该宏是如何定义的:
#define USB_INTERFACE_INFO(cl, sc, pr)
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO,
.bInterfaceClass = (cl),
.bInterfaceSubClass = (sc),
.bInterfaceProtocol = (pr)
第一个参数cl:表示Video interface class
第二个参数sc:表示Video interface subclass
第三个参数pr:表示Video interface protocol
#define USB_CLASS_VIDEO 0x0e
任意一个USB摄像头,它都有一个视频控制接口,在这个地方为什么不把视频流接口也放上去呢?
因为只要硬件上有视频控制接口,就可以根据视频控制接口找到从属于此视频控制接口的视频流接口。
上面这个驱动程序没有实际的意义,只是当我们插上USB摄像头的时候,会将USB摄像头的接口个数进行打印。在程序中,我们只有一个视频控制接口,所有cnt的值为1.
注意:在进行试验的时候,首先将ubuntu中自带的USB摄像头驱动程序去掉,然后安装上我们自己编写的USB摄像头驱动程序。
2. 打印设备描述符
lsusb
Bus 001 Device 007: ID 1e4e: 0102
Bus 001 Device 007: ID 1d6b: 0002 Linux Foundation 2.0 root hub
Bus 001 Device 007: ID 1e4e: 0001 Linux Foundation 1.1 root hub
lsusb.c:
main
dumpdev
dump_device
dump_config
for (i = 0 ; i < config->bNumInterfaces ; i++)
dump_interface(dev, &config->interface[i]);
for (i = 0; i < interface->num_altsetting; i++)
dump_altsetting(dev, &interface->altsetting[i]);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/version.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> static int myuvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { static int cnt = 0; struct usb_device *udev = interface_to_usbdev(intf); struct usb_device_descriptor *descriptor = &dev->descriptor; printk("myuvc_probe : cnt ", cnt++); /* 打印设备描述符 */ printk("Device Descriptor: " " bLength %5u " " bDescriptorType %5u " " bcdUSB %2x.%02x " " bDeviceClass %5u " " bDeviceSubClass %5u " " bDeviceProtocol %5u " " bMaxPacketSize0 %5u " " idVendor 0x%04x " " idProduct 0x%04x " " bcdDevice %2x.%02x " " iManufacturer %5u " " iProduct %5u " " iSerial %5u " " bNumConfigurations %5u ", descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff, descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol, descriptor->bMaxPacketSize0, descriptor->idVendor, descriptor->idProduct, descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, descriptor->iProduct, descriptor->iSerialNumber, descriptor->bNumConfigurations); return 0; } static void myuvc_disconnect(struct usb_interface *intf) { static int cnt = 0; printk("myuvc_probe : cnt ", cnt++); } static struct usb_device_id myuvc_ids[] = { /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} }; /*1.分配一个usb driver结构体*/ static struct usb_driver myuvc_driver = { .name = "myuvc", .probe = myuvc_probe, .disconnect = myuvc_disconnect, .id_table = myuvc_ids, }; /*2.设置*/ static int myuvc_init(void) { /*3.注册*/ usb_register(&myuvc_driver); } static void myuvc_exit(void) { usb_deregister(&myuvc_driver); } module_init(myuvc_init); module_exit(myuvc_exit); MODULE_LICENSE("GPL v2");
3. 打印配置描述符
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/version.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> static int myuvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { static int cnt = 0; struct usb_device *udev = interface_to_usbdev(intf); struct usb_device_descriptor *descriptor = &dev->descriptor; struct usb_host_config *hostconfig; struct usb_config_descriptor *config; int i = 0; printk("myuvc_probe : cnt ", cnt++); /* 打印设备描述符 */ printk("Device Descriptor: " " bLength %5u " " bDescriptorType %5u " " bcdUSB %2x.%02x " " bDeviceClass %5u " " bDeviceSubClass %5u " " bDeviceProtocol %5u " " bMaxPacketSize0 %5u " " idVendor 0x%04x " " idProduct 0x%04x " " bcdDevice %2x.%02x " " iManufacturer %5u " " iProduct %5u " " iSerial %5u " " bNumConfigurations %5u ", descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff, descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol, descriptor->bMaxPacketSize0, descriptor->idVendor, descriptor->idProduct, descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, descriptor->iProduct, descriptor->iSerialNumber, descriptor->bNumConfigurations); for (i = 0; i < descriptor->bNumConfigurations; i++) { hostconfig = &dev->config[i]; config = &hostconfig->desc; printk(" Configuration Descriptor %d: " " bLength %5u " " bDescriptorType %5u " " wTotalLength %5u " " bNumInterfaces %5u " " bConfigurationValue %5u " " iConfiguration %5u " " bmAttributes 0x%02x ", i, config->bLength, config->bDescriptorType, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bmAttributes); } return 0; } static void myuvc_disconnect(struct usb_interface *intf) { static int cnt = 0; printk("myuvc_probe : cnt ", cnt++); } static struct usb_device_id myuvc_ids[] = { /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} }; /*1.分配一个usb driver结构体*/ static struct usb_driver myuvc_driver = { .name = "myuvc", .probe = myuvc_probe, .disconnect = myuvc_disconnect, .id_table = myuvc_ids, }; /*2.设置*/ static int myuvc_init(void) { /*3.注册*/ usb_register(&myuvc_driver); } static void myuvc_exit(void) { usb_deregister(&myuvc_driver); } module_init(myuvc_init); module_exit(myuvc_exit); MODULE_LICENSE("GPL v2");
4.打印配置描述符中的第一个接口描述符
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> #include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> static int myuvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { static int cnt = 0; struct usb_device *dev = interface_to_usbdev(intf); struct usb_device_descriptor *descriptor = &dev->descriptor; struct usb_host_config *hostconfig; struct usb_config_descriptor *config; struct usb_interface_assoc_descriptor *assoc_desc; int i; printk("myuvc_probe : cnt = %d ", cnt++); /* 打印设备描述符 */ printk("Device Descriptor: " " bLength %5u " " bDescriptorType %5u " " bcdUSB %2x.%02x " " bDeviceClass %5u " " bDeviceSubClass %5u " " bDeviceProtocol %5u " " bMaxPacketSize0 %5u " " idVendor 0x%04x " " idProduct 0x%04x " " bcdDevice %2x.%02x " " iManufacturer %5u " " iProduct %5u " " iSerial %5u " " bNumConfigurations %5u ", descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff, descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol, descriptor->bMaxPacketSize0, descriptor->idVendor, descriptor->idProduct, descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, descriptor->iProduct, descriptor->iSerialNumber, descriptor->bNumConfigurations); for (i = 0; i < descriptor->bNumConfigurations; i++) { hostconfig = &dev->config[i]; config = &hostconfig->desc; printk(" Configuration Descriptor %d: " " bLength %5u " " bDescriptorType %5u " " wTotalLength %5u " " bNumInterfaces %5u " " bConfigurationValue %5u " " iConfiguration %5u " " bmAttributes 0x%02x ", i, config->bLength, config->bDescriptorType, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bmAttributes); assoc_desc = hostconfig->intf_assoc[0]; printk(" Interface Association: " " bLength %5u " " bDescriptorType %5u " " bFirstInterface %5u " " bInterfaceCount %5u " " bFunctionClass %5u " " bFunctionSubClass %5u " " bFunctionProtocol %5u " " iFunction %5u ", assoc_desc->bLength, assoc_desc->bDescriptorType, assoc_desc->bFirstInterface, assoc_desc->bInterfaceCount, assoc_desc->bFunctionClass, assoc_desc->bFunctionSubClass, assoc_desc->bFunctionProtocol, assoc_desc->iFunction); } return 0; } static void myuvc_disconnect(struct usb_interface *intf) { static int cnt = 0; printk("myuvc_disconnect : cnt = %d ", cnt++); } static struct usb_device_id myuvc_ids[] = { /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /* VideoControl Interface */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /* VideoStreaming Interface */ {} }; /* 1. 分配usb_driver */ /* 2. 设置 */ static struct usb_driver myuvc_driver = { .name = "myuvc", .probe = myuvc_probe, .disconnect = myuvc_disconnect, .id_table = myuvc_ids, }; static int myuvc_init(void) { /* 3. 注册 */ usb_register(&myuvc_driver); return 0; } static void myuvc_exit(void) { usb_deregister(&myuvc_driver); } module_init(myuvc_init); module_exit(myuvc_exit); MODULE_LICENSE("GPL");
5.打印接口描述符
在probe函数中,参数就有接口这一项,usb设备中有多个接口,如果这个接口能被driver所支持,即吻合id_table的话,该接口就会作为一个参数传给probe函数。
在一个接口中可能有多个设置,用struct usb_host_interface *altsetting来表示;当前使用哪个设置呢?使用struct usb_host_interface *cur_altsetting; /* the currently来表示。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> #include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> static int myuvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { static int cnt = 0; struct usb_device *dev = interface_to_usbdev(intf); struct usb_device_descriptor *descriptor = &dev->descriptor; struct usb_host_config *hostconfig; struct usb_config_descriptor *config; struct usb_interface_assoc_descriptor *assoc_desc; struct usb_interface_descriptor *interface; int i, j; printk("myuvc_probe : cnt = %d ", cnt++); /* 打印设备描述符 */ printk("Device Descriptor: " " bLength %5u " " bDescriptorType %5u " " bcdUSB %2x.%02x " " bDeviceClass %5u " " bDeviceSubClass %5u " " bDeviceProtocol %5u " " bMaxPacketSize0 %5u " " idVendor 0x%04x " " idProduct 0x%04x " " bcdDevice %2x.%02x " " iManufacturer %5u " " iProduct %5u " " iSerial %5u " " bNumConfigurations %5u ", descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff, descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol, descriptor->bMaxPacketSize0, descriptor->idVendor, descriptor->idProduct, descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, descriptor->iProduct, descriptor->iSerialNumber, descriptor->bNumConfigurations); for (i = 0; i < descriptor->bNumConfigurations; i++) { hostconfig = &dev->config[i]; config = &hostconfig->desc; printk(" Configuration Descriptor %d: " " bLength %5u " " bDescriptorType %5u " " wTotalLength %5u " " bNumInterfaces %5u " " bConfigurationValue %5u " " iConfiguration %5u " " bmAttributes 0x%02x ", i, config->bLength, config->bDescriptorType, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bmAttributes); assoc_desc = hostconfig->intf_assoc[0]; printk(" Interface Association: " " bLength %5u " " bDescriptorType %5u " " bFirstInterface %5u " " bInterfaceCount %5u " " bFunctionClass %5u " " bFunctionSubClass %5u " " bFunctionProtocol %5u " " iFunction %5u ", assoc_desc->bLength, assoc_desc->bDescriptorType, assoc_desc->bFirstInterface, assoc_desc->bInterfaceCount, assoc_desc->bFunctionClass, assoc_desc->bFunctionSubClass, assoc_desc->bFunctionProtocol, assoc_desc->iFunction); for (j = 0; j < intf->num_altsetting; j++) { interface = &intf->altsetting[j].desc; printk(" Interface Descriptor altsetting %d: " " bLength %5u " " bDescriptorType %5u " " bInterfaceNumber %5u " " bAlternateSetting %5u " " bNumEndpoints %5u " " bInterfaceClass %5u " " bInterfaceSubClass %5u " " bInterfaceProtocol %5u " " iInterface %5u ", j, interface->bLength, interface->bDescriptorType, interface->bInterfaceNumber, interface->bAlternateSetting, interface->bNumEndpoints, interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol, interface->iInterface); } } return 0; } static void myuvc_disconnect(struct usb_interface *intf) { static int cnt = 0; printk("myuvc_disconnect : cnt = %d ", cnt++); } static struct usb_device_id myuvc_ids[] = { /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /* VideoControl Interface */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /* VideoStreaming Interface */ {} }; /* 1. 分配usb_driver */ /* 2. 设置 */ static struct usb_driver myuvc_driver = { .name = "myuvc", .probe = myuvc_probe, .disconnect = myuvc_disconnect, .id_table = myuvc_ids, }; static int myuvc_init(void) { /* 3. 注册 */ usb_register(&myuvc_driver); return 0; } static void myuvc_exit(void) { usb_deregister(&myuvc_driver); } module_init(myuvc_init); module_exit(myuvc_exit); MODULE_LICENSE("GPL");
6. 打印UVC规范定义的描述符
UVC规范中定义的描述符,有输入描述符、处理单元描述符、选择单元描述符、输出单元描述符等,这些描述符存在某个buffer中,即当前设置里面有个buffer。
打印结果如下所示:
VideoControl Interface的自定义描述符:
VideoStreaming Interface的自定义描述符:
下篇博客将对buffer中打印的这些信息进行详细的介绍。