V4L2系列之video_register_device函数分析

感觉写完了其实也没怎么理解这个函数,但是还是这里保存一下,希望大家予以拍砖指导:


下面分析一下video_register_device这个函数:位于include/media/v4l2-dev.h头文件中:

参考文献:http://blog.sina.com.cn/s/blog_602f87700101a52s.html

static inline int__must_check video_register_device(struct video_device *vdev,

              int type, int nr)

{

       return __video_register_device(vdev, type, nr, 1,vdev->fops->owner);

}

video_register_device函数:

其中:第一个参数struct video_device *vdev是想注册的video device的结构体;

第二个参数是视频设备类型,在此头文件(v4l2-dev.h中),定义了几种视频设备类型:

#defineVFL_TYPE_GRABBER    0                   //图像采集设备

#defineVFL_TYPE_VBI              1                   //从视频消隐的时间段获得信息的设备

#defineVFL_TYPE_RADIO         2                   //无线电设备

#defineVFL_TYPE_SUBDEV              3            //视频设备(不确定

#defineVFL_TYPE_MAX            4                   //最大值

第三个参数是注册的设备节点号;

 

video_register_device函数主要是注册一个V4L2device,指定类型,然后指定device node number。函数只是简单的调用了__video_register_device函数。

 

按照参考文档的说明:这个__video_register_device函数主要做的工作有:1、初始化一些值;2、创建字符模式驱动;3、注册字符设备等。

下面我来分析这个函数,这个函数的定义在v4l2-dev.c中,声明在v4l2-dev.h中:

int__video_register_device(struct video_device *vdev, int type, int nr,

              int warn_if_nr_in_use, struct module *owner)

前三个参数与video_register_device中相同,第四个参数不知道,第五个参数表面指向当前的模块;

 

int i =0;

intret;

intminor_offset = 0;

intminor_cnt = VIDEO_NUM_DEVICES;

constchar *name_base;

声明了一些变量;其中minor_cnt的赋值VIDEO_NUM_DEVICES仍定义在v4l2-dev.c中:

#defineVIDEO_NUM_DEVICES  256

声明最大的设备数量为256个?

 

/* A minor value of -1marks this video device as never

          having beenregistered */

vdev->minor = -1;

-1表明这个video device从未被注册过(其实是指之前注册失败)。

 

/* the release callbackMUST be present */

       if (WARN_ON(!vdev->release))

              return -EINVAL;

声明release回调函数。

 

/* v4l2_fh support */

       spin_lock_init(&vdev->fh_lock);

       INIT_LIST_HEAD(&vdev->fh_list);

调用自旋锁和初始化链表。

 

下面开始进入各个部分的工作:

第一部分:

/* Part 1: check devicetype */

       switch (type) {

       case VFL_TYPE_GRABBER:

              name_base = "video";

              break;

       case VFL_TYPE_VBI:

              name_base = "vbi";

              break;

       case VFL_TYPE_RADIO:

              name_base = "radio";

              break;

       case VFL_TYPE_SUBDEV:

              name_base = "v4l-subdev";

              break;

       default:

              printk(KERN_ERR "%s called with unknown type:%d\n",

                     __func__, type);

              return -EINVAL;

       }

根据各个类型设备的不同,取不同的设备类型名称。

 

vdev->vfl_type= type;

vdev->cdev= NULL;

if(vdev->v4l2_dev) {

       if (vdev->v4l2_dev->dev)

              vdev->parent = vdev->v4l2_dev->dev;

       if (vdev->ctrl_handler == NULL)

              vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

       /* If the prio state pointer is NULL, then use the v4l2_device prio state. */

       if (vdev->prio == NULL)

              vdev->prio = &vdev->v4l2_dev->prio;

}

这里是填充struct video_device的一些字段。

 

第二部分:

注册device node number等。

/* Part 2: find a freeminor, device node number and device index. */

#ifdefCONFIG_VIDEO_FIXED_MINOR_RANGES

       /* Keep the ranges for the first four types for historical

        * reasons.

        * Newer devices (notyet in place) should use the range

        * of 128-191 and justpick the first free minor there

        * (new style). */

       switch (type) {

       case VFL_TYPE_GRABBER:

              minor_offset = 0;

              minor_cnt = 64;

              break;

       case VFL_TYPE_RADIO:

              minor_offset = 64;

              minor_cnt = 64;

              break;

       case VFL_TYPE_VBI:

              minor_offset = 224;

              minor_cnt = 32;

              break;

       default:

              minor_offset = 128;

              minor_cnt = 64;

              break;

       }

#endif

定义minor_offset和minor_cnt。

 

       /* Pick a device node number */

       mutex_lock(&videodev_lock);

       nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);

       if (nr == minor_cnt)

              nr = devnode_find(vdev, 0, minor_cnt);

       if (nr == minor_cnt) {

              printk(KERN_ERR "could not get a free device nodenumber\n");

              mutex_unlock(&videodev_lock);

              return -ENFILE;

       }

注册device node number。

函数devnode_find:发现节点号。

 

#ifdefCONFIG_VIDEO_FIXED_MINOR_RANGES

       /* 1-on-1 mapping of device node number to minor number */

       i = nr;

#else

       /* The device node number and minor numbers are independent,so

          we just find thefirst free minor number. */

       for (i = 0; i < VIDEO_NUM_DEVICES;i++)

              if (video_device[i] == NULL)

                     break;

       if (i == VIDEO_NUM_DEVICES) {

              mutex_unlock(&videodev_lock);

              printk(KERN_ERR "could not get a freeminor\n");

              return -ENFILE;

       }

#endif

 

vdev->minor = i + minor_offset;

       vdev->num = nr;

       devnode_set(vdev);

 

       /* Should not happen since we thought this minor was free */

       WARN_ON(video_device[vdev->minor] != NULL);

       vdev->index = get_index(vdev);

       mutex_unlock(&videodev_lock);

       /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is

          clear and warn if itwasn't. */

       if (vdev->lock == NULL)

              WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags));

 

       if(vdev->ioctl_ops)

              determine_valid_ioctls(vdev);

我都搞晕了,不知道第二部分在搞什么。

 

 

第三部分:初始化字符设备

/* Part 3: Initialize thecharacter device */

       vdev->cdev = cdev_alloc();

       if (vdev->cdev == NULL) {

              ret = -ENOMEM;

              goto cleanup;

       }

       vdev->cdev->ops = &v4l2_fops;

       vdev->cdev->owner = owner;

       ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR,vdev->minor), 1);

       if (ret < 0) {

              printk(KERN_ERR "%s: cdev_add failed\n",__func__);

              kfree(vdev->cdev);

              vdev->cdev = NULL;

              goto cleanup;

       }

调用cdev_add注册字符设备。这个看的比较简单。

 

第四部分:

注册device

/* Part 4: register thedevice with sysfs */

       vdev->dev.class = &video_class;

       vdev->dev.devt= MKDEV(VIDEO_MAJOR, vdev->minor);

       if (vdev->parent)

              vdev->dev.parent =vdev->parent;

       dev_set_name(&vdev->dev, "%s%d", name_base,vdev->num);

       ret = device_register(&vdev->dev);

       if (ret < 0) {

              printk(KERN_ERR "%s: device_registerfailed\n", __func__);

              goto cleanup;

       }

在sysfs中注册device。

 

       /* Register the release callback that will be called when thelast

          reference to thedevice goes away. */

       vdev->dev.release = v4l2_device_release;

注册回调函数release。

 

       if (nr != -1 && nr != vdev->num &&warn_if_nr_in_use)

              printk(KERN_WARNING "%s: requested %s%d, got%s\n", __func__,

                     name_base, nr, video_device_node_name(vdev));

 

       /* Increase v4l2_devicerefcount */

       if(vdev->v4l2_dev)

              v4l2_device_get(vdev->v4l2_dev);

 

第五部分:

在定义了CONFIG_MEDIA_CONTROLLER后才有第五部分,注册entity(实体?)

#ifdefined(CONFIG_MEDIA_CONTROLLER)

       /* Part 5: Register the entity. */

       if (vdev->v4l2_dev&& vdev->v4l2_dev->mdev&&

           vdev->vfl_type !=VFL_TYPE_SUBDEV) {

              vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;

              vdev->entity.name = vdev->name;

              vdev->entity.info.v4l.major = VIDEO_MAJOR;

              vdev->entity.info.v4l.minor = vdev->minor;

              ret = media_device_register_entity(vdev->v4l2_dev->mdev,

                     &vdev->entity);

              if (ret < 0)

                     printk(KERN_WARNING

                           "%s: media_device_register_entity failed\n",

                           __func__);

       }

#endif

 

第六部分:

/* Part 6: Activate thisminor. The char device can now be used. */

       set_bit(V4L2_FL_REGISTERED, &vdev->flags);

       mutex_lock(&videodev_lock);

       video_device[vdev->minor] = vdev;

       mutex_unlock(&videodev_lock);

解锁,激活这一字符设备。

 

最后:

cleanup:

       mutex_lock(&videodev_lock);

       if (vdev->cdev)

              cdev_del(vdev->cdev);

       devnode_clear(vdev);

       mutex_unlock(&videodev_lock);

       /* Mark this video device as never having been registered. */

       vdev->minor = -1;

       return ret;

相关的一些清理操作。

 

总之呢,video_register_device的作用就是注册一个video device,并完成相关设备初始化的一些工作。

你可能感兴趣的:(linux设备驱动,V4L2)