整个v4l2的框架分为三层:
在应用层,我们可以在 /dev 目录发现 video0 类似的设备节点,上层的摄像头程序打开设备节点进行数据捕获,显示视频画面。设备节点的名字很统一,video0 video1 video2…这些设备节点在是核心层注册。
核心层 v4l2-dev.c,承上启下,对于每一个硬件相关层注册进来的设备,设置一个统一的接口 v4l2_fops ,既然是统一的接口必然不是具体的视频设备的操作函数,应用层调用 v4l2_fops 中的函数最终将调用到硬件相关层的 video_device 的 fops 。
硬件相关层,与具体的视频硬件打交道,分配、设置、注册 video_device 结构体。
static int __init videodev_init(void)
{
/* 申请设备号,留给 video 设备使用 */
dev_t dev = MKDEV(VIDEO_MAJOR, );
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
/* 创建 video 类 */
ret = class_register(&video_class);
return ;
}
struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops;
/\* sysfs \*/
struct device dev; /\* v4l device \*/
struct cdev \*cdev; /\* character device \*/
/\* Set either parent or v4l2\_dev if your driver uses v4l2\_device \*/
struct device \*parent; /\* device parent \*/
struct v4l2\_device \*v4l2\_dev; /\* v4l2\_device parent \*/
/\* Control handler associated with this device node. May be NULL. \*/
struct v4l2\_ctrl\_handler \*ctrl\_handler;
/\* Priority state. If NULL, then v4l2\_dev->prio will be used. \*/
struct v4l2\_prio\_state \*prio;
/\* device info \*/
char name\[\];
int vfl\_type;
/\* 'minor' is set to -1 if the registration failed \*/
int minor;
u16 num;
/\* use bitops to set/clear/test flags \*/
unsigned long flags;
/\* attribute to differentiate multiple indices on one physical device \*/
int index;
/\* V4L2 file handles \*/
spinlock\_t fh\_lock; /\* Lock for all v4l2\_fhs \*/
struct list\_head fh\_list; /\* List of struct v4l2\_fh \*/
int debug; /\* Activates debug level\*/
/\* Video standard vars \*/
v4l2\_std\_id tvnorms; /\* Supported tv norms \*/
v4l2\_std\_id current\_norm; /\* Current tvnorm \*/
/\* callbacks \*/
void (\*release)(struct video\_device \*vdev);
/\* ioctl callbacks \*/
const struct v4l2\_ioctl\_ops \*ioctl\_ops;
DECLARE\_BITMAP(valid\_ioctls, BASE\_VIDIOC\_PRIVATE);
/\* serialization lock \*/
DECLARE\_BITMAP(disable\_locking, BASE\_VIDIOC\_PRIVATE);
struct mutex \*lock;
};
struct v4l2_device {
struct device *dev;
/* used to keep track of the registered subdevs */
struct list_head subdevs;
spinlock_t lock;
char name[V4L2_DEVICE_NAME_SIZE];
void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
struct v4l2_ctrl_handler *ctrl_handler;
struct v4l2_prio_state prio;
struct mutex ioctl_lock;
struct kref ref;
void (*release)(struct v4l2_device *v4l2_dev);
};
static inline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev, type, nr, , vdev->fops->owner);
}
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = ;
int ret;
int minor_offset = ;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/\* A minor value of -1 marks this video device as never having been registered \*/
vdev->minor = -;
/\* 视频设备的设备节点一般为 video0 ..video 就是由此而来 \*/
switch (type) {
case VFL\_TYPE\_GRABBER:
name\_base = "video";
break;
case VFL\_TYPE\_VBI:
name\_base = "vbi";
break;
...
}
vdev->vfl\_type = type;
vdev->cdev = NULL;
/\* Part 2: find a free minor, device node number and device index. \*/
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = ;
minor_cnt = ;
break;
…
#endif
/\* 寻找一个空的项,这个好像并不重要 \*/
mutex\_lock(&videodev\_lock);
nr = devnode\_find(vdev, nr == - ? : nr, minor\_cnt);
if (nr == minor\_cnt)
nr = devnode\_find(vdev, , 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
/* 在全局 video_deivce 数组中寻找一个空的项,下标+minor_offset作为设备的次设备号 */
for (i = ; 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
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
if (vdev->ioctl\_ops)
determine\_valid\_ioctls(vdev);
/\* 注册字符设备 \*/
vdev->cdev = cdev\_alloc();
vdev->cdev->ops = &v4l2\_fops;
vdev->cdev->owner = owner;
ret = cdev\_add(vdev->cdev, MKDEV(VIDEO\_MAJOR, vdev->minor), );
/\* 得把device注册进内核,mdev才能自动创建设备节点,/dev 目录下的video0 等就是来自这里 \*/
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);
vdev->dev.release = v4l2\_device\_release;
/\* 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);
return ;
}
EXPORT_SYMBOL(__video_register_device);
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->open) {
if (video\_is\_registered(vdev))
ret = vdev->fops->open(filp);
}
}
static ssize_t v4l2_read(struct file *filp, char __user *buf,
size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->read)
return -EINVAL;
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
}
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->mmap)
return ret;
ret = vdev->fops->mmap(filp, vm);
}
static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->unlocked_ioctl) {
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
} else if (vdev->fops->ioctl) {
if (video_is_registered(vdev))
ret = vdev->fops->ioctl(filp, cmd, arg);
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章