前面的博客已經分析了USB攝像頭驅動程序的框架,我們知道了USB攝像頭驅動程序的重點在於1)描述符的分析;2)屬性的控制(通過VC來設置);3)格式的選擇(通過VS來設置);4)數據的獲得(通過VS的URB來獲得)。後面的博客就會從這4個方面進行深入的分析,本篇博客首先來看一下UVC驅動程序的描述符分析。
每一個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 \n", cnt++);
return 0;
}
static void myuvc_disconnect(struct usb_interface *intf)
{
static int cnt = 0;
printk("myuvc_probe : cnt \n", 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]);
#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 \n", cnt++);
/* 打印設備描述符 */
printk("Device Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdUSB %2x.%02x\n"
" bDeviceClass %5u \n"
" bDeviceSubClass %5u \n"
" bDeviceProtocol %5u \n"
" bMaxPacketSize0 %5u\n"
" idVendor 0x%04x \n"
" idProduct 0x%04x \n"
" bcdDevice %2x.%02x\n"
" iManufacturer %5u\n"
" iProduct %5u\n"
" iSerial %5u\n"
" bNumConfigurations %5u\n",
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 \n", 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");
View Code
3. 打印配置描述符
#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 \n", cnt++);
/* 打印設備描述符 */
printk("Device Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdUSB %2x.%02x\n"
" bDeviceClass %5u \n"
" bDeviceSubClass %5u \n"
" bDeviceProtocol %5u \n"
" bMaxPacketSize0 %5u\n"
" idVendor 0x%04x \n"
" idProduct 0x%04x \n"
" bcdDevice %2x.%02x\n"
" iManufacturer %5u\n"
" iProduct %5u\n"
" iSerial %5u\n"
" bNumConfigurations %5u\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" wTotalLength %5u\n"
" bNumInterfaces %5u\n"
" bConfigurationValue %5u\n"
" iConfiguration %5u\n"
" bmAttributes 0x%02x\n",
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 \n", 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");
View Code
4.打印配置描述符中的第一個接口描述符
#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\n", cnt++);
/* 打印設備描述符 */
printk("Device Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdUSB %2x.%02x\n"
" bDeviceClass %5u \n"
" bDeviceSubClass %5u \n"
" bDeviceProtocol %5u \n"
" bMaxPacketSize0 %5u\n"
" idVendor 0x%04x \n"
" idProduct 0x%04x \n"
" bcdDevice %2x.%02x\n"
" iManufacturer %5u\n"
" iProduct %5u\n"
" iSerial %5u\n"
" bNumConfigurations %5u\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" wTotalLength %5u\n"
" bNumInterfaces %5u\n"
" bConfigurationValue %5u\n"
" iConfiguration %5u\n"
" bmAttributes 0x%02x\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bFirstInterface %5u\n"
" bInterfaceCount %5u\n"
" bFunctionClass %5u\n"
" bFunctionSubClass %5u\n"
" bFunctionProtocol %5u\n"
" iFunction %5u\n",
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\n", 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");
View Code
5.打印接口描述符
在probe函數中,參數就有接口這一項,usb設備中有多個接口,如果這個接口能被driver所支持,即吻合id_table的話,該接口就會作為一個參數傳給probe函數。
在一個接口中可能有多個設置,用struct usb_host_interface *altsetting來表示;當前使用哪個設置呢?使用struct usb_host_interface *cur_altsetting; /* the currently來表示。
#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\n", cnt++);
/* 打印設備描述符 */
printk("Device Descriptor:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bcdUSB %2x.%02x\n"
" bDeviceClass %5u \n"
" bDeviceSubClass %5u \n"
" bDeviceProtocol %5u \n"
" bMaxPacketSize0 %5u\n"
" idVendor 0x%04x \n"
" idProduct 0x%04x \n"
" bcdDevice %2x.%02x\n"
" iManufacturer %5u\n"
" iProduct %5u\n"
" iSerial %5u\n"
" bNumConfigurations %5u\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" wTotalLength %5u\n"
" bNumInterfaces %5u\n"
" bConfigurationValue %5u\n"
" iConfiguration %5u\n"
" bmAttributes 0x%02x\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bFirstInterface %5u\n"
" bInterfaceCount %5u\n"
" bFunctionClass %5u\n"
" bFunctionSubClass %5u\n"
" bFunctionProtocol %5u\n"
" iFunction %5u\n",
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:\n"
" bLength %5u\n"
" bDescriptorType %5u\n"
" bInterfaceNumber %5u\n"
" bAlternateSetting %5u\n"
" bNumEndpoints %5u\n"
" bInterfaceClass %5u\n"
" bInterfaceSubClass %5u\n"
" bInterfaceProtocol %5u\n"
" iInterface %5u\n",
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\n", 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");
View Code
6. 打印UVC規範定義的描述符
UVC規範中定義的描述符,有輸入描述符、處理單元描述符、選擇單元描述符、輸出單元描述符等,這些描述符存在某個buffer中,即當前設置裏面有個buffer。
打印結果如下所示:
VideoControl Interface的自定義描述符:
VideoStreaming Interface的自定義描述符:
下篇博客將對buffer中打印的這些信息進行詳細的介紹。