点击(此处)折叠或打开
-
以vivi.c为例分析摄像头驱动程序思路:
-
Virtual Video driver - This code emulates a real video device with v4l2 api
-
-
入口:static int __init vivi_init(void)
-
ret = vivi_create_instance(i) //创建设备
-
struct vivi_dev *dev; //定义一个vivi_dev结构体
-
(a)定义 struct video_device *vfd //定义一个video_devce结构体
-
struct vivi_dev {
-
struct list_head vivi_devlist;
-
struct v4l2_device v4l2_dev;
-
-
spinlock_t slock;
-
struct mutex mutex;
-
-
int users;
-
-
/* various device info */
-
struct video_device *vfd;
-
-
struct vivi_dmaqueue vidq;
-
-
/* Several counters */
-
int h, m, s, ms;
-
unsigned long jiffies;
-
char timestr[13];
-
-
int mv_count; /* Controls bars movement */
-
-
/* Input Number */
-
int input;
-
-
/* Control 'registers' */
-
int qctl_regs[ARRAY_SIZE(vivi_qctrl)];
-
};
-
重要成员:
-
/* various device info */
-
struct video_device *vfd; //套接一个video_devce结构体,里面设置设备信息
-
struct video_device
-
{
-
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 */
-
-
/* 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;
-
-
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);
-
-
-
const struct v4l2_ioctl_ops *ioctl_ops;
-
};
-
重要成员:
-
const struct v4l2_file_operations *fops; //定义设备操作函数
-
const struct v4l2_ioctl_ops *ioctl_ops; //定义ioctl 回调函数,用于应用程序获取底层信息比如获取、配置摄像头所支持的格式
-
-
vivi.c中对video_device设置的过程:
-
(b)分配 vfd = video_device_alloc(); //给前面定义的video_device结构体分配内存
-
(c)设置 *vfd = vivi_template; //把之前设置好的video_device结构体赋给vfd
-
注:
-
static struct video_device vivi_template = {
-
.name = "vivi",
-
.fops = &vivi_fops,
-
.ioctl_ops = &vivi_ioctl_ops,
-
.minor = -1,
-
.release = video_device_release,
-
其中包括几个两个重要操作函数的设置:
-
1.
-
static const struct v4l2_file_operations vivi_fops = {
-
.owner = THIS_MODULE,
-
.open = vivi_open,
-
.release = vivi_close,
-
.read = vivi_read,
-
.poll = vivi_poll,
-
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
-
.mmap = vivi_mmap,
-
};
-
-
-
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 *);
-
};
-
2.
-
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
-
/* 表示它是一个摄像头设备 */
-
.vidioc_querycap = vidioc_querycap,
-
-
/* 用于选择输入源,在xawtv里面就是video source */
-
//.vidioc_enum_input = vidioc_enum_input,
-
//.vidioc_g_input = vidioc_g_input,
-
//.vidioc_s_input = vidioc_s_input,
-
-
/* 用于列举、获得TV 制式,不是必须的*/
-
//.vidioc_s_std = vidioc_s_std,
-
/* video_device里面:
-
.tvnorms = V4L2_STD_525_60, //用于IDIOC_ENUMSTD
-
.current_norm = V4L2_STD_NTSC_M,// 用于VIDIOC_G_STD
-
*/
-
-
/* 用于列举、获得、测试、设置摄像头所提供的格式 */
-
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,//也可以去掉
-
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
-
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
-
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-
-
/* 缓冲区操作: 申请/ 查询/ 放入队列/取出队列*/
-
.vidioc_reqbufs = vidioc_reqbufs,
-
.vidioc_querybuf = vidioc_querybuf,
-
.vidioc_qbuf = vidioc_qbuf,
-
.vidioc_dqbuf = vidioc_dqbuf,
-
-
/* 查询/获得/设置属性 */
-
//.vidioc_queryctrl = vidioc_queryctrl,
-
//.vidioc_g_ctrl = vidioc_g_ctrl,
-
//.vidioc_s_ctrl = vidioc_s_ctrl,
-
-
/* 启动/停止摄像头 */
-
.vidioc_streamon = vidioc_streamon,
-
.vidioc_streamoff = vidioc_streamoff,
-
};
-
-
-
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);
-
#ifdef CONFIG_VIDEO_V4L1_COMPAT
-
/* buffer type is struct vidio_mbuf * */
-
int (*vidiocgmbuf) (struct file *file, void *fh, struct video_mbuf *p);
-
#endif
-
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);
-
-
/* For other private ioctls */
-
long (*vidioc_default) (struct file *file, void *fh,
-
int cmd, void *arg);
-
};
-
(d)注册 ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
-
return video_register_device_index(vdev, type, nr, -1);
-
/*注册三部曲*/
-
vdev->cdev = cdev_alloc(); //第一步 分配字符设备内存
-
vdev->cdev->ops = &v4l2_fops; //第二部 设置将设备节点的操作函数
-
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); //将字符设备注册进内核
-
以上是虚拟视频驱动vivi.c的分析,以下下是分析vivi.c的open,read,write,ioctl过程:
-
1. open
-
app: open(/dev/video0)
-
-
2. read
-
app: read(/dev/video0)
-
-
3. write
-
app: write(/dev/video0)
-
4. ioctl
-
app: iotcl(/dev/video0)
-
-
以上系统函数调用,在进入驱动程序里都对应上面注册的v4l2_fops结构体里面的对应函数:
-
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,
-
.ioctl = v4l2_ioctl,
-
-
-
static int v4l2_open(struct inode *inode, struct file *filp)
-
{
-
struct video_device *vdev;
-
int ret = 0;
-
-
/* Check if the video device is available */
-
mutex_lock(&videodev_lock);
-
vdev = video_devdata(filp); // 根据次设备号从数组中得到video_device
-
/* return ENODEV if the video device has been removed
-
already or if it is not registered anymore. */
-
if (vdev == NULL || video_is_unregistered(vdev)) {
-
mutex_unlock(&videodev_lock);
-
return -ENODEV;
-
}
-
/* and increase the device refcount */
-
video_get(vdev);
-
mutex_unlock(&videodev_lock);
-
if (vdev->fops->open)
-
ret = vdev->fops->open(filp); //这里才显示庐山真面目,最终调用的是前面写的vivi_open函数
-
-
/* decrease the refcount in case of an error */
-
if (ret)
-
video_put(vdev);
-
return ret;
-
}
-
注:
-
struct video_device *video_devdata(struct file *file)
-
{
-
return video_device[iminor(file->f_path.dentry->d_inode)];
-
-
}
-
同理:app中的read函数最终会调用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);
-
-
if (!vdev->fops->read)
-
return -EINVAL;
-
if (video_is_unregistered(vdev))
-
return -EIO;
-
return vdev->fops->read(filp, buf, sz, off); //最终会调用v4l2_fops操作结构体中的vivi_read函数
-
}
-
注:由于采用总线设备驱动模型,一些驱动通用的函数,linux系统已经帮助实现v4l2_dev.c(例v4l2_fops),到具体open,read,write,ioctl时,即最终
-
的open,read,write,ioctl等函数还要写驱动的人实现。
-
-
static ssize_t v4l2_write(struct file *filp, const char __user *buf,
-
size_t sz, loff_t *off)
-
{
-
struct video_device *vdev = video_devdata(filp);
-
-
if (!vdev->fops->write)
-
return -EINVAL;
-
if (video_is_unregistered(vdev))
-
return -EIO;
-
return vdev->fops->write(filp, buf, sz, off);//最终会调用v4l2_fops操作结构体中的vivi_write函数
-
}
-
-
static int v4l2_ioctl(struct inode *inode, struct file *filp,
-
unsigned int cmd, unsigned long arg)
-
{
-
struct video_device *vdev = video_devdata(filp);
-
-
if (!vdev->fops->ioctl)
-
return -ENOTTY;
-
/* Allow ioctl to continue even if the device was unregistered.
-
Things like dequeueing buffers might still be useful. */
- return vdev->fops->ioctl(filp, cmd, arg); //最终会调用v4l2_fops操作结构体中的vivi_ioctl函数