本文转载自:
V4L2驱动框架
主设备号: 81次设备号: 0-63 64-67 192-223 224-255/dev/videoX 应用层————————————char驱动————————————V4L2————————————具体的驱动————————————硬件应用层的操作都需要有底层V4L2驱动的支持。内核中有一些非常完善的例子。比如:linux-2.6.26内核目录drivers/media/video/vivi.c中的驱动代码实例。1、V4L2驱动注册、注销函数static int __init videodev_init(void) //注册256个视频设备{ 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;}static void __exit videodev_exit(void){ dev_t dev = MKDEV(VIDEO_MAJOR, 0); class_unregister(&video_class); unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);}module_init(videodev_init)module_exit(videodev_exit)2、函数实现2.1Video核心层(drivers/media/video/videodev.c)提供了注册函数供具体的V4L2驱动调用:int video_register_device(struct video_device *vfd, int type, int nr) —video_device: 要构建的核心数据结构 —Type: 表示设备类型,此设备号的基地址受此变量的影响 —Nr: 如果end-base>nr>0 :次设备号=base(基准值,受type影响)+nr;否则:系统自动分配合适的次设备号函数内部调用static int __video_register_device(struct video_device *vdev, int type, int nr, int warn_if_nr_in_use)具体驱动只需要构建video_device结构,然后调用注册函数既可。2.2Video核心层(drivers/media/video/videodev.c)提供了注销函数void video_unregister_device(struct video_device *vdev)核心定义设备数组,其中VIDEO_NUM_DEVICES 为 256 是最大设备数static struct video_device *video_device[VIDEO_NUM_DEVICES];2.3V4L2提供了统一的应用层接口static const struct file_operations v4l2_fops = { .owner = THIS_MODULE, .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, .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,};v4l2_read定义如下static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off){ struct video_device *vdev = video_devdata(filp); int ret = -ENODEV; if (!vdev->fops->read) return -EINVAL; if (vdev->lock && mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); if (vdev->lock) mutex_unlock(vdev->lock); return ret;}函数内部调用的 vdev->fops->read 由具体的V4L2驱动实现驱动开发定义最重要的数据结构体struct video_device,其中,重要的是const struct v4l2_file_operations *fops; //帧缓冲操作编写帧缓冲驱动的主要工作就是编写fops各个成员函数编写具体驱动方法步骤:1、构建具体驱动的struct video_device;3、构建具体驱动的struct fops,并定义相关的操作函数;4、定义具体驱动的XXX_probe。
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; /* 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_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; /* serialization lock */ struct mutex *lock;};================================================================================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); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct file *); int (*release) (struct file *);};================================================================================struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name;/* initial name of the device */ struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power;#ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */#endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit ses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata;#ifdef CONFIG_OF struct device_node *of_node;#endif dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev);};================================================================================struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count;};================================================================================struct v4l2_device { struct device *dev; /* 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 list_head subdevs; /* used to keep track of the registered subdevs */ spinlock_t lock; /* lock this struct; can be used by the driver as well if this struct is embedded into a larger struct. */ char name[V4L2_DEVICE_NAME_SIZE]; /* unique device name, by default the driver name + bus ID */ void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg); /* notify callback called by some sub-devices. */ struct v4l2_ctrl_handler *ctrl_handler; /* The control handler. May be NULL. */ struct mutex ioctl_lock; /* BKL replacement mutex. Temporary solution only. */};================================================================================struct v4l2_ioctl_ops { /* ioctl callbacks */ /* VIDIOC_QUERYCAP handler */ int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap); /* Priority handling */ int (*vidioc_g_priority) (struct file *file, void *fh, enum v4l2_priority *p); int (*vidioc_s_priority) (struct file *file, void *fh, enum v4l2_priority p); /* VIDIOC_ENUM_FMT handlers */ int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh, struct v4l2_fmtdesc *f); /* VIDIOC_G_FMT handlers */ int (*vidioc_g_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_vid_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_vbi_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_vbi_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); /* VIDIOC_S_FMT handlers */ int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_vid_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_vbi_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_vbi_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_s_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); /* VIDIOC_TRY_FMT handlers */ int (*vidioc_try_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_vid_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_vbi_cap) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_vbi_out) (struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh, struct v4l2_format *f); int (*vidioc_try_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); /* Buffer handlers */ int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b); int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_overlay) (struct file *file, void *fh, unsigned int i); int (*vidioc_g_fbuf) (struct file *file, void *fh, struct v4l2_framebuffer *a); int (*vidioc_s_fbuf) (struct file *file, void *fh, struct v4l2_framebuffer *a); /* Stream on/off */ int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i); int (*vidioc_streamoff) (struct file *file, void *fh, enum v4l2_buf_type i); /* Standard handling ENUMSTD is handled by videodev.c */ int (*vidioc_g_std) (struct file *file, void *fh, v4l2_std_id *norm); int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm); int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a); /* Input handling */ int (*vidioc_enum_input) (struct file *file, void *fh, struct v4l2_input *inp); int (*vidioc_g_input) (struct file *file, void *fh, unsigned int *i); int (*vidioc_s_input) (struct file *file, void *fh, unsigned int i); /* Output handling */ int (*vidioc_enum_output) (struct file *file, void *fh, struct v4l2_output *a); int (*vidioc_g_output) (struct file *file, void *fh, unsigned int *i); int (*vidioc_s_output) (struct file *file, void *fh, unsigned int i); /* Control handling */ int (*vidioc_queryctrl) (struct file *file, void *fh, struct v4l2_queryctrl *a); int (*vidioc_g_ctrl) (struct file *file, void *fh, struct v4l2_control *a); int (*vidioc_s_ctrl) (struct file *file, void *fh, struct v4l2_control *a); int (*vidioc_g_ext_ctrls) (struct file *file, void *fh, struct v4l2_ext_controls *a); int (*vidioc_s_ext_ctrls) (struct file *file, void *fh, struct v4l2_ext_controls *a); int (*vidioc_try_ext_ctrls) (struct file *file, void *fh, struct v4l2_ext_controls *a); int (*vidioc_querymenu) (struct file *file, void *fh, struct v4l2_querymenu *a); /* Audio ioctls */ int (*vidioc_enumaudio) (struct file *file, void *fh, struct v4l2_audio *a); int (*vidioc_g_audio) (struct file *file, void *fh, struct v4l2_audio *a); int (*vidioc_s_audio) (struct file *file, void *fh, struct v4l2_audio *a); /* Audio out ioctls */ int (*vidioc_enumaudout) (struct file *file, void *fh, struct v4l2_audioout *a); int (*vidioc_g_audout) (struct file *file, void *fh, struct v4l2_audioout *a); int (*vidioc_s_audout) (struct file *file, void *fh, struct v4l2_audioout *a); int (*vidioc_g_modulator) (struct file *file, void *fh, struct v4l2_modulator *a); int (*vidioc_s_modulator) (struct file *file, void *fh, struct v4l2_modulator *a); /* Crop ioctls */ int (*vidioc_cropcap) (struct file *file, void *fh, struct v4l2_cropcap *a); int (*vidioc_g_crop) (struct file *file, void *fh, struct v4l2_crop *a); int (*vidioc_s_crop) (struct file *file, void *fh, struct v4l2_crop *a); /* Compression ioctls */ int (*vidioc_g_jpegcomp) (struct file *file, void *fh, struct v4l2_jpegcompression *a); int (*vidioc_s_jpegcomp) (struct file *file, void *fh, struct v4l2_jpegcompression *a); int (*vidioc_g_enc_index) (struct file *file, void *fh, struct v4l2_enc_idx *a); int (*vidioc_encoder_cmd) (struct file *file, void *fh, struct v4l2_encoder_cmd *a); int (*vidioc_try_encoder_cmd) (struct file *file, void *fh, struct v4l2_encoder_cmd *a); /* Stream type-dependent parameter ioctls */ int (*vidioc_g_parm) (struct file *file, void *fh, struct v4l2_streamparm *a); int (*vidioc_s_parm) (struct file *file, void *fh, struct v4l2_streamparm *a); /* Tuner ioctls */ int (*vidioc_g_tuner) (struct file *file, void *fh, struct v4l2_tuner *a); int (*vidioc_s_tuner) (struct file *file, void *fh, struct v4l2_tuner *a); int (*vidioc_g_frequency) (struct file *file, void *fh, struct v4l2_frequency *a); int (*vidioc_s_frequency) (struct file *file, void *fh, struct v4l2_frequency *a); /* Sliced VBI cap */ int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh, struct v4l2_sliced_vbi_cap *a); /* Log status ioctl */ int (*vidioc_log_status) (struct file *file, void *fh); int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh, struct v4l2_hw_freq_seek *a); /* Debugging ioctls */#ifdef CONFIG_VIDEO_ADV_DEBUG int (*vidioc_g_register) (struct file *file, void *fh, struct v4l2_dbg_register *reg); int (*vidioc_s_register) (struct file *file, void *fh, struct v4l2_dbg_register *reg);#endif int (*vidioc_g_chip_ident) (struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip); int (*vidioc_enum_framesizes) (struct file *file, void *fh, struct v4l2_frmsizeenum *fsize); int (*vidioc_enum_frameintervals) (struct file *file, void *fh, struct v4l2_frmivalenum *fival); /* DV Timings IOCTLs */ int (*vidioc_enum_dv_presets) (struct file *file, void *fh, struct v4l2_dv_enum_preset *preset); int (*vidioc_s_dv_preset) (struct file *file, void *fh, struct v4l2_dv_preset *preset); int (*vidioc_g_dv_preset) (struct file *file, void *fh, struct v4l2_dv_preset *preset); int (*vidioc_query_dv_preset) (struct file *file, void *fh, struct v4l2_dv_preset *qpreset); int (*vidioc_s_dv_timings) (struct file *file, void *fh, struct v4l2_dv_timings *timings); int (*vidioc_g_dv_timings) (struct file *file, void *fh, struct v4l2_dv_timings *timings); int (*vidioc_subscribe_event) (struct v4l2_fh *fh, struct v4l2_event_subscription *sub); int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh, struct v4l2_event_subscription *sub); /* For other private ioctls */ long (*vidioc_default) (struct file *file, void *fh, int cmd, void *arg);};================================================================================================================================================================================================================================================static struct ov7670_format_struct { enum v4l2_mbus_pixelcode mbus_code; enum v4l2_colorspace colorspace; struct regval_list *regs; int cmatrix[CMATRIX_LEN]; int bpp;} ov7670_format_struct ov7670_formats[] = { /* "YUYV 4:2:2" */ .desc = "YUYV 4:2:2", .pixelformat = V4L2_PIX_FMT_YUYV, .regs = ov7670_fmt_yuv422, .cmatrix = { 128, -128, 0, -34, -94, 128 }, .bpp = 2, /* "UYVY 4:2:2" */ /* "RGB 444" */ /* "RGB 565" */ /* "Raw RGB Bayer" */}#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)struct v4l2_fmtdesc { __u32 index; /* Format number */ enum v4l2_buf_type type; /* buffer type */ __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ __u32 reserved[4];}; //每一个subdev驱动程序实例应该创建这个结构,无论是独立或在一个更大的结构之中。struct v4l2_subdev { struct list_head list; struct module *owner; u32 flags; struct v4l2_device *v4l2_dev; const struct v4l2_subdev_ops *ops; /* name must be unique */ char name[V4L2_SUBDEV_NAME_SIZE]; /* can be used to group similar subdevs, value is driver-specific */ u32 grp_id; /* pointer to private data */ void *priv;}; struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; enum v4l2_field field; __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; enum v4l2_colorspace colorspace; __u32 priv; /* private data, depends on pixelformat */}; struct v4l2_format { enum v4l2_buf_type type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ __u8 raw_data[200]; /* user-defined */ } fmt;};static struct ov7670_win_size { int width; int height; unsigned char com7_bit; int hstart; /* Start/stop values for the camera. Note */ int hstop; /* that they do not always make complete */ int vstart; /* sense to humans, but evidently the sensor */ int vstop; /* will do the right thing... */ struct regval_list *regs; /* Regs to tweak */ /* h/vref stuff */} ov7670_win_sizes[] = { /* VGA */ /* CIF */ /* QVGA */ /* QCIF */}#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes))