首页 > 代码库 > soc camera子系统之注冊video device设备

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

该函数是用于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; }



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