/drivers/media/platform/vivi.c
module_init(vivi_init);
module_exit(vivi_exit);
static int __init vivi_init(void)
ret = vivi_create_instance(i);
struct vivi_dev { // 这个结构体还是非常的重要的
struct list_head vivi_devlist;
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
struct video_device vdev;
/* controls */
struct v4l2_ctrl *brightness;
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *saturation;
struct v4l2_ctrl *hue;
struct {
/* autogain/gain cluster */
struct v4l2_ctrl *autogain;
struct v4l2_ctrl *gain;
};
struct v4l2_ctrl *volume;
struct v4l2_ctrl *alpha;
struct v4l2_ctrl *button;
struct v4l2_ctrl *boolean;
struct v4l2_ctrl *int32;
struct v4l2_ctrl *int64;
struct v4l2_ctrl *menu;
struct v4l2_ctrl *string;
struct v4l2_ctrl *bitmask;
struct v4l2_ctrl *int_menu;
spinlock_t slock;
struct mutex mutex;
struct vivi_dmaqueue vidq;
/* Several counters */
unsigned ms;
unsigned long jiffies;
unsigned button_pressed;
int mv_count; /* Controls bars movement */
/* Input Number */
int input;
/* video capture */
const struct vivi_fmt *fmt;
struct v4l2_fract timeperframe;
unsigned int width, height;
struct vb2_queue vb_vidq;
unsigned int field_count;
u8 bars[9][3];
u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
unsigned int pixelsize;
u8 alpha_component;
u32 textfg, textbg;
};
static int __init vivi_create_instance(int inst):
struct vivi_dev *dev;
struct video_device *vfd;
struct v4l2_ctrl_handler *hdl;
struct vb2_queue *q;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VIVI_MODULE_NAME, inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
// 下面就是填充这个 struct vivi_dev *dev; 结构体
dev->fmt = &formats[0]; // 格式
static const struct vivi_fmt formats[] = {
{
.name = "4:2:2, packed, YUYV",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
.is_yuv = true,
},
dev->timeperframe = tpf_default; //
tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */
dev->width = 640;
dev->height = 480;
dev->pixelsize = dev->fmt->depth / 8;
hdl = &dev->ctrl_handler; // 统一指针
v4l2_ctrl_handler_init(hdl, 11); // 初始化v4l2_ctrl_handler
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 127);
dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_GAIN, 0, 255, 1, 100);
dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
。。。。
dev->v4l2_dev.ctrl_handler = hdl;
/* initialize locks */
spin_lock_init(&dev->slock);
/* initialize queue */ //初始化 缓冲队列
q = &dev->vb_vidq;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
// 初始化 struct video_device *vfd;
vfd = &dev->vdev;
*vfd = vivi_template; //重点
static const struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release_empty,
};
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vb2_fop_mmap,
};
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.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_enum_framesizes = vidioc_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev; //重点
vfd->queue = q;// 重点
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
vfd->lock = &dev->mutex;
video_set_drvdata(vfd, dev);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); //重点
int __video_register_device(struct video_device *vdev, int type, int nr,...
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &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,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
vdev->dev.release = v4l2_device_release;
video_device[vdev->minor] = vdev; // 为了后面方便获得vdev
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(vfd));
return 0;
以上就初始化完成了摄像头的 字符设备。下来就是看应用层如何调用底层的
app: open("/dev/video0",....)
字符设备是哪里注册的,去哪里找fops
vdev->cdev->ops = &v4l2_fops;
v4l2_fops.open = v4l2_open,
static int v4l2_open(struct inode *inode, struct file *filp)
vdev = video_devdata(filp);
return video_device[iminor(file_inode(file))]; //全局变量 在注册字符设备的时候
static inline struct inode *file_inode(struct file *f)
{
return f->f_inode;
}
video_get(vdev);
if (vdev->fops->open) {
if (video_is_registered(vdev))
ret = vdev->fops->open(filp); // 最终调用的是video_device结构体的open函数
else
ret = -ENODEV;
}
在vivi初始化的时候
*vfd = vivi_template;
static const struct video_device vivi_template = {
.fops = &vivi_fops,
static const struct v4l2_file_operations vivi_fops = {
.open = v4l2_fh_open,
int v4l2_fh_open(struct file *filp)
v4l2_fh_init(fh, vdev);
v4l2_fh_add(fh);
主要是就是初始化了 struct v4l2_fh *fh
struct v4l2_fh {
struct list_head list;
struct video_device *vdev;
struct v4l2_ctrl_handler *ctrl_handler;
enum v4l2_priority prio;
/* Events */
wait_queue_head_t wait;
struct list_head subscribed; /* Subscribed events */
struct list_head available; /* Dequeueable event */
unsigned int navailable;
u32 sequence;
};
app: read
也是找字符设备注册的fops
v4l2_fops.read = v4l2_read,
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->read(filp, buf, sz, off);
也是最终调用video_device 中的read
vivi_template.fops = &vivi_fops,
.read = vb2_fop_read,
ssize_t vb2_fop_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
if (vb2_queue_is_busy(vdev, file))
goto exit;
err = vb2_read(vdev->queue, buf, count, ppos,
file->f_flags & O_NONBLOCK);
// 读的就是 缓冲区的中内容
size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
}
EXPORT_SYMBOL_GPL(vb2_read);
size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
}
EXPORT_SYMBOL_GPL(vb2_write);
app: ioctl ...
v4l2_fops.unlocked_ioctl = v4l2_ioctl,
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
vivi_template.fops = &vivi_fops
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
long video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(file, cmd, arg, __video_do_ioctl);
/* Handles IOCTL */
err = func(file, cmd, parg);
}
//看函数之后前 先看一个结构体
struct v4l2_ioctl_info {
unsigned int ioctl;
u32 flags;
const char * const name;
union {
u32 offset;
int (*func)(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *p);
} u;
void (*debug)(const void *arg, bool write_only);
};
static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) :
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; //重点
bool write_only = false;
struct v4l2_ioctl_info default_info;
const struct v4l2_ioctl_info *info;
void *fh = file->private_data; //重点
struct v4l2_fh *vfh = NULL;
int use_fh_prio = 0;
int debug = vfd->debug;
long ret = -ENOTTY;
if (v4l2_is_known_ioctl(cmd)) { //return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; //最下面有这个全局变量
info = &v4l2_ioctls[_IOC_NR(cmd)]; // info保存了这个地址
// 如不在这个全局变量里面的话
} else {
default_info.ioctl = cmd;
default_info.flags = 0;
default_info.debug = v4l_print_default;
info = &default_info;
}
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
if (info->flags & INFO_FL_STD) { // 如果是IOCTL_INFO_STD开头的
typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
const void *p = vfd->ioctl_ops; // p取ioctl_ops的基地址
const vidioc_op *vidioc = p + info->u.offset; // + 在(v4l2_ioctl_ops)中函数的偏移量,就得到了这个函数的地址
ret = (*vidioc)(file, fh, arg); // 重点
} else if (info->flags & INFO_FL_FUNC) {
ret = info->u.func(ops, file, fh, arg);
。。。。
} else {
ret = ops->vidioc_default(file, fh,
use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
cmd, arg);
}
===============================================================================
#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
[_IOC_NR(_ioctl)] = { \
.ioctl = _ioctl, \
.flags = _flags | INFO_FL_STD, \
.name = #_ioctl, \
.u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
.debug = _debug, \
}
#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
[_IOC_NR(_ioctl)] = { \
.ioctl = _ioctl, \
.flags = _flags | INFO_FL_FUNC, \
.name = #_ioctl, \
.u.func = _func, \
.debug = _debug, \
}
static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
到这里基本就完成了 应用层到底层的调用关系。