v4l2 核心分析 (待续)

来源:互联网 发布:linux权限管理命令 编辑:程序博客网 时间:2024/06/07 05:54

v4l2核心是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数,由v4l2-dev.c实现,主要作用申请字符主设备号、注册class和提供video device注册注销等相关函数。

涉及到的结构体:

struct v4l2_file_operations {struct module *owner;ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*ioctl) (struct file *, unsigned int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);unsigned long (*get_unmapped_area) (struct file *, unsigned long,unsigned long, unsigned long, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct file *);int (*release) (struct file *);};
/* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to *the common handler */struct video_device{#if defined(CONFIG_MEDIA_CONTROLLER)struct media_entity entity;#endif/* 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[32];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_tfh_lock; /* Lock for all v4l2_fhs */struct list_headfh_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;/* serialization lock */struct mutex *lock;};
video_device结构体中的v4l2-iotcl结构体在v4l2-ioctl.h中定义。

涉及到的函数:

与优先权有关的函数:

void v4l2_prio_init(struct v4l2_prio_state *global);

int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, enum v4l2_priority new);
void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local);
void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local);
enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global);
int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local);

注册相关的函数:

int __must_check __video_register_device(struct video_device *vdev, int type,int nr, int warn_if_nr_in_use, struct module *owner);

static inline int __must_check video_register_device(struct video_device *vdev,int type, int nr)

static inline int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr)
void video_unregister_device(struct video_device *vdev);
struct video_device * __must_check video_device_alloc(void);

void video_device_release(struct video_device *vdev);

void video_device_release_empty(struct video_device *vdev);

杂项函数:

static inline void *video_get_drvdata(struct video_device *vdev)

static inline void video_set_drvdata(struct video_device *vdev, void *data)

struct video_device *video_devdata(struct file *file);

static inline void *video_drvdata(struct file *file)

static inline const char *video_device_node_name(struct video_device *vdev)

static inline int video_is_registered(struct video_device *vdev)

v4l2-dev框架在初始化时会执行videodev_init,下面重点分析重点分析一下videodev_init和video_register_device函数

videodev_init:

/* *Initialise video for linux */static int __init videodev_init(void){dev_t dev = MKDEV(VIDEO_MAJOR, 0);int ret;printk(KERN_INFO "Linux video capture interface: v2.00\n");ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);if (ret < 0) {printk(KERN_WARNING "videodev: unable to get major %d\n",VIDEO_MAJOR);return ret;}ret = class_register(&video_class);if (ret < 0) {unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);printk(KERN_WARNING "video_dev: class_register failed\n");return -EIO;}return 0;}

register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME)注册了名为video4linux,主设备为81的字符设备,并且使用从0开始的256个节点。

class_register(&video_class)注册了vidoe类,方便后面生成设备节点。

videodev_init函数完成的初始化工作就这些,下面分析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 */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;const char *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";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;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\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;}#endifvdev->minor = i + minor_offset;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);/* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is   clear and warn if it wasn't. */if (vdev->lock == NULL)WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags));if (vdev->ioctl_ops)determine_valid_ioctls(vdev);/* 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\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);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\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 */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\n",       __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);return 0;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;}