1.video_device的初始化
static struct video_device viu_template = {
.name = "FSL viu",
.fops = &viu_fops,
.minor = -1,
.ioctl_ops = &viu_ioctl_ops,
.release = video_device_release,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
};
一般我们使用上述方式,定义一个video_device实现,里面核心是fops和ioctl_ops 这两个接口的实现。
使用video_device_alloc()函数分配一个video_device内存空间,然后指向上述的结构体实现,如下所示:
struct video_device *vdev;
vdev = video_device_alloc();
*vdev = viu_template;
2.注册video_device
注册video_device的函数接口为video_register_device
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函数说明
/**
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
* @warn_if_nr_in_use: warn if the desired device node number
* was already in use and another number was chosen instead.
* @owner: module that owns the video device node
*
* The registration code assigns minor numbers and device node numbers
* based on the requested type and registers the new device node with
* the kernel.
*
* This function assumes that struct video_device was zeroed when it
* was allocated and does not contain any stale date.
*
* An error is returned if no free minor or device node number could be
* found, or if the registration of the device node failed.
*
* Zero is returned on success.
*
* Valid types are
*
* %VFL_TYPE_GRABBER - A frame grabber
*
* %VFL_TYPE_VBI - Vertical blank data (undecoded)
*
* %VFL_TYPE_RADIO - A radio card
*
* %VFL_TYPE_SUBDEV - A subdevice
*
* %VFL_TYPE_SDR - Software Defined Radio
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
函数说明如下:
/**
- __video_register_device - register video4linux devices
vdev: 这个参数是我们想注册的video device结构体
type: 注册的社备类型
nr: 选择的设备节点号0代表选择 /dev/video0,1代码选择 /dev/video 1,-1代表第一个 (0 == /dev/video0, 1 == /dev/video1, … -1 == first free)
warn_if_nr_in_use: 警告如果我们期望的设备号已经被占用,将会选择另 外一个设备号
owner: 模块的拥有者
0返回成功
*合法的类型有如下几种:
*
- %VFL_TYPE_GRABBER - A frame grabber
- %VFL_TYPE_VBI - Vertical blank data (undecoded)
- %VFL_TYPE_RADIO - A radio card
- %VFL_TYPE_SUBDEV - A subdevice
- %VFL_TYPE_SDR - Software Defined Radio
*/
接下来看看其函数代码实现
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
...
//如果release和v4l2_dev这两个成员是null将返回失败,也就是说这两个成员都必须赋值
if (WARN_ON(!vdev->release))
return -EINVAL;
/* the v4l2_dev pointer MUST be present */
if (WARN_ON(!vdev->v4l2_dev))
return -EINVAL;
//下面就是选择一个最小的没有使用的下标,作为次设备号
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 node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_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 the first 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 free minor\n");
return -ENFILE;
}
#endif
//赋值次设备号,为下标+偏移,不同的设备类型在数据的不同范围时硕
下标0-63 为VFL_TYPE_GRABBER
下标64-127 为VFL_TYPE_RADIO
下标128-223 为其它类型
下标224-255 为VFL_TYPE_VBI
video_device数组大小为256
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
//由于次设备号是没有使用的下标+偏移,由于有偏移所以这个下标可以已经被使用
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
//使用数组存储这个video_device结构体
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
if (vdev->ioctl_ops)
determine_valid_ioctls(vdev);
//初始化字符设备
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;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_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_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_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_device refcount */
v4l2_device_get(vdev->v4l2_dev);
/* Part 5: Register the entity. */
ret = video_register_media_controller(vdev, type);
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
video_device[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
这个注册函数为v4l2的核心函数大体有以下作用:
1.进行合法性检查
2.分配一个未使用的最小下标来保存video_device
3.注册字符设备,以产生节点被上层访问
4.设备注册
…
大体的调用过程如下:
v4l2_fops结构体里面的实现基本都是中转,把调用从节点转动自己定义的video_device的v4l2_file_operations结构体中。也就相当对上层提供了统一接口,所有的video_device都是通过这个中转到具体实现。