struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
as is the case with e.g. ISA devices. */struct device *dev; //设备模型dev#if defined(CONFIG_MEDIA_CONTROLLER)struct media_device *mdev; //媒体设备,指向一个媒体控制器的指针#endif/* used to keep track of the registered subdevs */struct list_head subdevs; //保存所有子设备的链表/* lock this struct; can be used by the driver as well if this
struct is embedded into a larger struct. */
spinlock_t lock; //锁/* unique device name, by default the driver name + bus ID */char name[V4L2_DEVICE_NAME_SIZE]; //独一无二的名字,默认是驱动名+总线ID/* notify callback called by some sub-devices. */void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg); //通知链回调函数,被子设备调用/* The control handler. May be NULL. */struct v4l2_ctrl_handler *ctrl_handler; //控制句柄/* Device's priority state */struct v4l2_prio_state prio; //设备的优先级状态,一般有后台,交互,记录三种优先级,依次变高/* BKL replacement mutex. Temporary solution only. */struct mutex ioctl_lock; //ioctl操作的互斥量/* Keep track of the references to this struct. */struct kref ref; //引用计数/* Release function that is called when the ref count goes to 0. */void (*release)(struct v4l2_device *v4l2_dev); //设备释放函数
};
struct v4l2_device在v4l2子系统中处理核心位置,链表subdevs保存所有v4l2子设备,v4l2_device通常会被嵌入另一个特定的结构体,根据不同cpu厂商做特定修改来来确定。在s5p6818中,v4l2_device被嵌入到nxp_v4l2中
int register_nxp_capture(struct nxp_capture *me)
{
int ret;
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSIbool csi_enabled = me->interface_type == NXP_CAPTURE_INF_CSI;
#endif#ifdef CONFIG_NXP_CAPTURE_MIPI_CSIif (csi_enabled) {
ret = nxp_csi_register(&me->csi); /* csi注册 */if (ret < 0) {
pr_err("%s: failed to nxp_csi_register()
", __func__);
return ret;
}
}
#endif
ret = nxp_vin_clipper_register(&me->vin_clipper); /* clipper 注册 */if (ret < 0) {
pr_err("%s: failed to nxp_vin_clipper_register()
", __func__);
goto error_vin;
}
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
ret = nxp_decimator_register(&me->decimator); /* decimator 注册 */if (ret < 0) {
pr_err("%s: failed to nxp_decimator_register()
", __func__);
goto error_decimator;
}
#endifif (NULL == _register_sensor(me, me->platdata->sensor)) {
pr_err("%s: can't register sensor subdev
", __func__);
goto error_sensor;
}
/* capture中断注册 */
ret = request_irq(me->irq, &_irq_handler, IRQF_SHARED, "nxp-capture", me);
if (ret){
pr_err("%s: failed to request_irq()
", __func__);
goto error_irq;
}
return0;
error_irq:
_unregister_sensor(me);
error_sensor:
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
nxp_decimator_unregister(&me->decimator);
error_decimator:
#endif
nxp_vin_clipper_unregister(&me->vin_clipper);
error_vin:
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSIif (csi_enabled)
nxp_csi_unregister(&me->csi);
#endifreturn ret;
}
clipper注册
int nxp_vin_clipper_register(struct nxp_vin_clipper *me)
{
int ret;
struct nxp_capture *parent= nxp_vin_to_parent(me);
vmsg("%s
", __func__);
ret = v4l2_device_register_subdev(parent->get_v4l2_device(parent),
&me->subdev); /* 将clipper->subdev添加到顶层v4l2_device设备的子设备链表中 */if (ret <0) {
pr_err("%s: failed to v4l2_device_register_subdev()
", __func__);
return ret;
}
ret = register_nxp_video(me->video); /* 注册一个video设备,生成/dev/video*结点 */if (ret <0) {
pr_err("%s: failed to register_nxp_video()
", __func__);
v4l2_device_unregister_subdev(&me->subdev);
}
return ret;
}
register_nxp_video函数实际上调用的是__video_register_device函数
/* 注册videos设备,生成/dev/video结点(camera为例) */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;
constchar *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */if (WARN_ON(!vdev->release))
return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video"; //camerabreak;
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
",
__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;
}
/* 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
");
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
");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset; //次设备号
vdev->num = nr; //设备结点序号,如video1, nr = 1
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 */
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
", __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);
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_register failed
", __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
", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#if defined(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
",
__func__);
}
#endif/* Part 6: Activate this minor. 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);
return0;
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;
}
decimator的注册跟clipper注册是一样的,将subdev添加到v4l2_device的子设备链表中,调用register_nxp_video注册一个video设备,生成/dev/video*结点。