您的位置:首页 > 运维架构 > Linux

soc camera子系统之注册video device设备

2014-04-22 16:28 267 查看
该函数是用于soc camera 子系统向v4l2子系统注册video_device设备,这个设备是v4l2子系统的核心设备。先展开代码.

首先展开video_dev_create函数,代码如下:

static int video_dev_create(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct video_device *vdev = video_device_alloc(); //为video device申请内存。

if (!vdev)
return -ENOMEM;

strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));

vdev->parent = &icd->dev; //通过这个pointer,可以由vdev来获取到icd
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;//该函数集类似于file_opration,其实就是用来作为v4l2 子系统的默认的file_opration函数集中的回调函数,下面会有对应的分析。
static struct v4l2_file_operations soc_camera_fops = {
.owner = THIS_MODULE,
.open = soc_camera_open, //作为v4l2_open函数的回调函数
.release = soc_camera_close, //作为v4l2_release函数的回调函数
.unlocked_ioctl = video_ioctl2,//作为v4l2_ioctl函数的回调函数。
.read = soc_camera_read,//作为v4l2_read函数的回调函数
.mmap = soc_camera_mmap,//作为v4l2_mmcp函数的回调函数。
.poll = soc_camera_poll,//作为v4l2_poll函数的回调函数
};
vdev->ioctl_ops = &soc_camera_ioctl_ops; //该函数集是soc camera.c中实现的核心函数集,作为videp_ioctl2函数的回调函数集,根据用户空间传入的不同的ioctl命令。
vdev->release = video_device_release;
vdev->tvnorms = V4L2_STD_UNKNOWN;

icd->vdev = vdev; //让icd的成员指针指向已经初始化的video device设备。

return 0;
}


好了,我们现在着重分析soc_camera_video_start函数
static int soc_camera_video_start(struct soc_camera_device *icd)
{
const struct device_type *type = icd->vdev->dev.type;
int ret;

if (!icd->dev.parent)
return -ENODEV;

if (!icd->ops ||
!icd->ops->query_bus_param ||
!icd->ops->set_bus_param)
return -EINVAL;

ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);核心行为,向v4l2子系统注册video device设备,其实v4l2子系统向上实质是抽象成了一个字符设备。下面就会看到。type指定为VFL_TYPE_GRABBRER类型,对应video设备。
if (ret < 0) {
dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
return ret;
}

/* Restore device type, possibly set by the subdevice driver */
icd->vdev->dev.type = type;

return 0;
}


我们来着重分析一下video_register_device函数。该函数体内就是直接调用函数__video_register_device,所以我们真正要着重分析的函数是__video_register_device。这是一个很长的函数,我们需要慢慢分析,展开代码如下:
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES; //做多可以注册的设备数目256
const char *name_base;

/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
......

/* Part 1: check device type */
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; //type类型为VFL_TYPE_GRABBER
vdev->cdev = NULL;/
......

/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick 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

/* 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 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; //如果定义了改宏,那么video device的次设备号由nr和minor_offset共同决定。
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
为新注册的video device在数组video_device中寻找一个位置。
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
......
#endif
vdev->minor = i + minor_offset; //计算出设备的次设备号
如果定义CONFIG_VIDEO_FIXED_MINOR_RANGES
vdev->minor = i + minor_offset.
否则
vdev->minor = vdeio_device数组中第一个空闲的位置(从低地址开始。)

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);

/* Part 3: Initialize the character device */、
//以下是初始化一个char dev结构,然后注册字符设备。
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops; //v4l2子系统实现的默认的file opration操作函数集,每个成员函数内会调用对应的v4l2_file_oprations操作函数集。
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;
}
.......
return ret;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息