主设备号: 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.1
Video核心层(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.2
Video核心层(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.3
V4L2提供了统一的应用层接口
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))