zoukankan      html  css  js  c++  java
  • 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 
    ", 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]);
    #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");
    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 
    ", 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");
    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
    ", 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");
    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
    ", 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");
    View Code

    6. 打印UVC规范定义的描述符

    UVC规范中定义的描述符,有输入描述符、处理单元描述符、选择单元描述符、输出单元描述符等,这些描述符存在某个buffer中,即当前设置里面有个buffer。

    打印结果如下所示:

    VideoControl Interface的自定义描述符:

     VideoStreaming Interface的自定义描述符:

     下篇博客将对buffer中打印的这些信息进行详细的介绍。

  • 相关阅读:
    畅通工程(hdu1232)并查集
    qsort函数的用法
    二叉搜索树(hdu3791)
    Binary Tree Traversals(HDU1710)二叉树的简单应用
    Safe Or Unsafe(hdu2527)哈弗曼VS优先队列
    山东省第四届acm解题报告(部分)
    Points on Cycle (hdu1700,几何)
    A计划 hdu2102(bfs一般题)
    杀人游戏(hdu2211)插入法
    hdu1518 Square(dfs)
  • 原文地址:https://www.cnblogs.com/-glb/p/13700258.html
Copyright © 2011-2022 走看看