uvc摄像头驱动从0编写

一:将ucv摄像头驱动编译生成uvc模块,我这边采用的是海思3536提供的linux系统时linux-3.10

模块生成参考一下地址

http://www.coin163.com/it/1305375058925494518:

其中模块生成是:

Device Drivers -> Multimedia support -> Video capture adapters -> V4L USB Devices -> USB Video Class (UVC),这个选项就是在make menuconfig的时候对应的Kconfig中的内容,在编译内核是采用该命令生成uvc 内核模块,还有其他的配置选项,主要文件(driver/media/v4l2-core  driver/media/usb/uvc)(如果是默认的,需要配置多个选项)然后通过config去配置make menuconfig

注意:刚开始老不能生成.ko文件,最后查找资料发现在make uIamge之后生成然后采用make modules生成模块,才能生成.ko文件。

主要是需要添加ucv摄像头的id_table。

内核中的驱动对应的文件夹是:\linux-3.10.y\drivers\media\usb\uvc在该文件夹下就是用来生成uvc模块的程序。

linux-3.10.y\drivers\media\v4l2-core文件夹程序是基础的uvc程序,用来进行itocl以及内存的分配函数。最好用两个文件夹参考程序。

二:查看摄像头参数(操作平台:虚拟机中查看usb设备参数)

lsusb:用来查看摄像头设备。
lsusb -s 003(Bus):004(device) -v(查看usb设备的详细参数)

https://www.cnblogs.com/tureno/articles/6888794.html  用来分析一个usb的描述符参数

参考程序:dev/gadget、uvc_v412

我们先找一个切入点:v4l2_file_operations结构体

以上是准备工作。下面才是我们正常的uvc摄像头流程分析。

找到ioctal的设置,

1:一些基本结构体:获取设备支持的操作模式struct v4l2_capability 与 VIDIOC_QUERYCA

struct v4l2_capability {
    __u8    driver[16];     /* i.e. "bttv" */
    __u8    card[32];       /* i.e. "Hauppauge WinTV" */
    __u8    bus_info[32];   /* "PCI:" + pci_name(pci_dev) */
    __u32   version;        /* should use KERNEL_VERSION() */
    __u32    capabilities;   /* Device capabilities */
    __u32    reserved[4];
};

2:通过结构 v4l2_format 初始化捕获视频的格式

struct v4l2_format {
    __u32     type;
    union {
        struct v4l2_pix_format        pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
        struct v4l2_pix_format_mplane    pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
        struct v4l2_window        win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
        struct v4l2_vbi_format        vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
        struct v4l2_sliced_vbi_format    sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
        __u8    raw_data[200];                   /* user-defined */
    } fmt;
};

3:VIDIOC_REQBUFS 命令通过结构 v4l2_requestbuffers 请求驱动申请一片连续的内存用于缓存视频信息:

struct v4l2_requestbuffers {
    __u32            count;
    __u32            type;        /* enum v4l2_buf_type */
    __u32            memory;        /* enum v4l2_memory */
    __u32            reserved[2];
};

4:

struct uvc_video
{
    struct usb_ep *ep;

    /* Frame parameters */
    u8 bpp;
    u32 fcc;
    unsigned int width;
    unsigned int height;
    unsigned int imagesize;

    /* Requests */
    unsigned int req_size;
    struct usb_request *req[UVC_NUM_REQUESTS];
    __u8 *req_buffer[UVC_NUM_REQUESTS];
    struct list_head req_free;
    spinlock_t req_lock;

    void (*encode) (struct usb_request *req, struct uvc_video *video,
            struct uvc_buffer *buf);

    /* Context data used by the completion handler */
    __u32 payload_size;
    __u32 max_payload_size;

    struct uvc_video_queue queue;
    unsigned int fid;
};

struct uvc_device //uvc的设备描述,用来配置摄像头的接口参数,用来配置基本的usb的配置参数以及terminal和unit

{
    struct usb_device *udev; //usb的设备指针
    struct usb_interface *intf; //usb的接口指针
    unsigned long warnings;
    __u32 quirks;
    int intfnum;  //usb的接口数
    char name[32]; //设备名

    enum uvc_device_state state; //uvc的设备状态
    atomic_t users;
    atomic_t nmappings;
#ifdef CONFIG_MEDIA_CONTROLLER
    struct media_device mdev;
#endif
    struct v4l2_device vdev; //uvc的子设备v4l2设备(一个父设备分为好几个子设备)
    __u16 uvc_version;
    __u32 clock_frequency; //时钟频率

    struct list_head entities; //uvc实体链表头(挂着uvc设备的terminal和unit)
    struct list_head chains; //uvc视频链表头
    struct list_head streams; //uvc的视频流链表头 (结构体对应的设备接口)
    atomic_t nstreams; //视频流个数
    struct usb_host_endpoint *int_ep;//usb_host_endpoint的对象
    struct urb *int_urb; //中断urb
    __u8 *status; //uvc的设备状态
    struct input_dev *input; //输入设备
    char input_phys[64]; //输入设备节点路径
}

三:uvc_probe函数:

3.1:当插入摄像头后会根据:通过usb_register注册usb摄像头struct uvc_driver uvc_driver程序中的id_table来判断该驱动是否支持不同类型的usb 设备,,如果没有该变量,则usb中的探测回调函数则不会被调用,(有兴趣的朋友可以仔细分析该探测函数)。

当UVC摄像头被正常调用后,通过.probe = uvc_probe,调用uvc的处理函数:传递两个参数(usb接口参数,USB的id_table参数)。static struct usb_device_id uvc_ids[]中有一个id_table:    { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },可以配置所有的摄像头参数

struct usb_device *udev = interface_to_usbdev(intf);(这个时候已经得到usb的设备的基本参数了,我们要做的就是分析usb的参数的信息),

注意一个比较重要的结构体:usb_interface,一个usb_interface代表一个功能,,每个usb驱动控制器控制一个usb_interface,所有的驱动控制器都是由usb_interface和usb_core惊醒通信,usb_interface就是我们的入口参数。usb_device通常具备一个或多个usb_host_config;一个usb_host_config通常具备一个或多个usb_interface。(usb_device是爷爷,usb_host_config是爸爸,usb_interface是孩子),我们可以自己打印查看这些usb的配置的 过程描述符

3.2:摄像头的接口参数配置

if (uvc_parse_control(dev) < 0)用来解析usb的参数;(这里注意数据的排列顺序不一样:简称的大头小头)。

https://blog.csdn.net/eternity9255/article/details/53522966 用来介绍unit和terminal的参数

unit可以理解为构建UVC设备功能的各功能单元,多个unit按照一定的规则链接后就是一个完整的uvc功能设备,unit有一个或者多个入口,一个出口。

Processing Unit代表对采集单元的数据进行处理的单元,负责对采集到的图像进行调整,当host端要求设定这些Processing Unit负责范围内的功能特性时,SPEC定义要求PU单元负责相应Host端对图像特性的调整。

Terminal指整个UVC功能的入口和出口,Input Terminal可以理解为整个UVC功能的数据源头,它仅有一个outPin,可以连接到其他Unit的Input Pin作为该Unit的输入源。Output Terminal则可以理解为当图像数据流程UVC设备内部的整个处理流程后的输出点,它仅有一个Input Pin, 整个UVC处理流程上的最后一个Unit会将它的Output Pin与Output Terminal的Input Pin连接在一起。

我们可以看出该USB Camera大致有三个逻辑功能意义上的Entity:CT(代表硬件上的图像数据采集源,Sensor),PU(代表Sensor中可对采集所得图像数据进行加工的功能单元),OT(代表实际中USB模块的的ISO In端点),并无SU单元来控制图像数据采集源的切换和选择(只有一个输入源),此外也没有对采集所得的数据进行个性化处理的功能,故也没有EU单元的存在(无EU描述符)。

通过usb的host得到接口配置参数,通过该参数填充所需的配置格式,配置

https://blog.csdn.net/u010783226/article/details/93298993  用来分析各种接口配置描述符,联合接口描述符,配置描述符的功能。

接口描述符的参数:bInterfaceNumber    用来描述接口号,根据接口号,配置不同的接口,eg:vc控制接口。vc流接口

uvc摄像头有四个接口,接口号0:控制接口,接口1:流接口,用来设置接口格式,帧大小等参数。(H264,MJPEG,颜色域)

if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)解析标准的uvc设备。是uvc的特殊的一连串的接口描述符,是用来描述设备功能的描述符,比如unit和terminal的描述,通过:bDescriptorSubtype参数来描述不同的控制接口功能描述符。

uvc_parse_standard_control:用来解析控制接口描述符,一个设备需要一个联合接口描述符来描述视频接口集合,对于每个设备功能需要一个视频控制接口或者多个视频流接口,标准的视频接口联合描述符。

端点号:0x80(输入)端点号:0x81(输出),用来控制vc头,输入terminal,输出terminal,unit,扩展unit参数功能。

UVC_VC_HEADER:控制接口描述头,uvc_parse_format:根据不同的参数解析不同的格式

UVC_VC_INPUT_TERMINAL,输入terminal,UVC_VC_OUTPUT_TERMINAL:输出list_add_tail(&term->list, &dev->entities);根据不同的操作terminal对uvc不同的操作

terminal,UVC_VC_SELECTOR_UNIT:选择unit  UVC_VC_PROCESSING_UNIT:扩展unit

根据buffer[2]参数的不同解析不同的uvc功能:uvc_parse_streaming(dev, intf);解析uvc的流接口,添加数据流的头

struct usb_host_interface *alts = &intf->altsetting[0];用来得到流接口描述符。

videostreaming:VS Interface Descriptors:主要报货视频流接口input header output headerformat frame描述符组成,每个视频流接口都有一个单独的input或者output描述符,,得到摄像头为捕捉设备

streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

根据输入头描述符(INPUT_HEAD 1的到format的个数为2(配置为2个,配置bDescriptorSubtype 为4(得到帧格式数为5(类型,格式通道号1),9(个数))13(颜色帧格式配置符),6(MJPEG format的格式,得到2个format格式类型 7,个数(11)格式通道号2)))

端点号:0x82(输入)

对支持的每个format都有一个单独的format描述符,对于每一个format描述符有多个frame描述符,format和frame描述符仅定义在接口的第0号设置中

    streaming->header.bNumFormats = p; //得到的格式种类号
    streaming->header.bEndpointAddress = buffer[6];//得到的端点地址

uvc_parse_streaming(struct uvc_device *dev,    struct usb_interface *intf):用来解析视频流接口参数

list_add_tail(&streaming->list, &dev->streams);;最终将参数添加到该数据链表中

(其中主要就是一些参数的填充,根据不同的参数,执行不同的流程,一些uvc定义好的死规定,有兴趣有兴趣的还可以打印参数)。

最终解析完流参数,填充到:struct uvc_streaming *streaming(该结构体中)

以上为uvc接口解析的主要函数。

if (media_device_register(&dev->mdev) < 0)media设备的注册注册一个流媒体设备,用于运行时设备的控制,运行时设备能达到控制节点的效果

if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)注册一个v4l2结构体。

if (uvc_ctrl_init_device(dev) < 0)初始化一个控制结构体

if (uvc_register_chains(dev) < 0):注册uvc视频流

if ((ret = uvc_status_init(dev)) < 0):初始化中断urb

uvc_register_chains->ret = uvc_register_terms(dev, chain);->ret = uvc_register_video(dev, stream);主要功能呢注册uvc_stream流

ret = uvc_video_init(stream);接口初始化:

vdev = video_device_alloc();    //分配一个video_device结构体,1:队列初始化,2:设置和得到摄像头的参数uvc_get_video_ctrl->__uvc_query_ctrl通过该函数来设置数据图像格式等摄像头的参数。

vdev->fops = &uvc_fops; //操作函数

  ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);    //注册video_device结构体

通过填充的video_device结构体中的fops函数来操作摄像头

摄像头主要是填充该操作函数:

const struct v4l2_file_operations uvc_fops = {
    .owner        = THIS_MODULE,
    .open        = uvc_v4l2_open,
    .release    = uvc_v4l2_release,
    .unlocked_ioctl    = uvc_v4l2_ioctl,
    .read        = uvc_v4l2_read,
    .mmap        = uvc_v4l2_mmap,
    .poll        = uvc_v4l2_poll,

};

你可能感兴趣的:(个人随笔)