USB攝像頭驅動之描述符分析(上)


前面的博客已經分析了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 \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中打印的這些信息進行詳細的介紹。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM