一、 注册一个video_device设备
它代表系统/dev/videox设备节点的实际的物理设备。
下边一内核版本2.6.32种成熟的omap2432处理器摄像头控制器模块驱动为例分析:
下边的代码在driver/media/video/omap24xxcam.c中
1、Video device的操作函数集
static struct v4l2_file_operations omap24xxcam_fops = {
.ioctl = video_ioctl2,
.poll = omap24xxcam_poll,
.mmap = omap24xxcam_mmap,
.open = omap24xxcam_open,
.release = omap24xxcam_release,
};
2、Video device控制操作函数集
static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
.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_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
};
3、注册函数:omap24xxcam_device_register
static int omap24xxcam_device_register(struct v4l2_int_device *s)
{
struct omap24xxcam_device *cam = s->u.slave->master->priv;
struct video_device *vfd;//定义struct video_device结构体
int rval;
。。。。。。
/* initialize the video_device struct */
vfd = cam->vfd = video_device_alloc();
if (!vfd) {
dev_err(cam->dev, "could not allocate video device struct\n");
rval = -ENOMEM;
goto err;
}
vfd->release = video_device_release;
vfd->parent = cam->dev;
strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
//该video device设备V4L2操作函数集,在下边定义,这个通过最顶级字符设备的响应操作函数调用
vfd->fops = &omap24xxcam_fops;
vfd->minor = -1;
//该video device设备控制操作,和vfd->fops中的ioctl可能重复,下边进一步分析。
vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
omap24xxcam_hwinit(cam);
rval = omap24xxcam_sensor_init(cam);
if (rval)
goto err;
//注册video device,VFL_TYPE_GRABBER表示注册的是视频处理设备,video_nr=-1表示自动分配从设备号
if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
dev_err(cam->dev, "could not register V4L device\n");
vfd->minor = -1;
rval = -EBUSY;
goto err;
}
。。。。。。。
err:
omap24xxcam_device_unregister(s);
return rval;
}
二、 调用过程
应用程序的read、write、ioctl等操作函数传到内核层
以ioctl为例作说明:
1、最顶层的调用
在driver/media/video/v4l2-dev.c中:
static int v4l2_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
//根据次设备号从video_device[VIDEO_NUM_DEVICES]数组中得到当前操作的video device
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);
}
2、自己实现的操作函数集
根据上边注册时填充的fops,vdev->fops->ioctl(filp, cmd, arg)调用下边函数:
(在driver/media/video/omap24xxcam.c)
static struct v4l2_file_operations omap24xxcam_fops = {
.ioctl = video_ioctl2,
。。。。。
}
3、中间层处理调用
Video_ioctl2是定义在driver/media/video/v4l2-ioctl.c中的函数:
long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
long err = -EINVAL;
int is_ext_ctrl;
size_t ctrls_size = 0;
void __user *user_ptr = NULL;
控制命令处理。。。。。。。。。。
/* Handles IOCTL */
err = __video_do_ioctl(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
if (is_ext_ctrl) {
struct v4l2_ext_controls *p = parg;
p->controls = (void *)user_ptr;
if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))
err = -EFAULT;
goto out_ext_ctrl;
}
if (err < 0)
goto out;
错误处理。。。。。。。。。
}
4、根据命令调用相应的处理函数
下边就是根据传入的控制参数,调用相应的已经注册的v4l2_ioctl_ops操作处理
static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
//得到当前操作的video device
struct video_device *vfd = video_devdata(file);
//v4l2 控制操作函数集,在video device注册时赋值。
const struct v4l2_ioctl_ops *ops = vfd->omap24xxcam_ioctl_fops;
void *fh = file->private_data;
long ret = -EINVAL;
参数错误处理。。。。。
#ifdef CONFIG_VIDEO_V4L1_COMPAT
/***********************************************************
Handles calls to the obsoleted V4L1 API
Due to the nature of VIDIOCGMBUF, each driver that supports
V4L1 should implement its own handler for this ioctl.
***********************************************************/
/* --- streaming capture ------------------------------------- */
if (cmd == VIDIOCGMBUF) {
struct video_mbuf *p = arg;
if (!ops->vidiocgmbuf)
return ret;
ret = ops->vidiocgmbuf(file, fh, p);
if (!ret)
dbgarg(cmd, "size=%d, frames=%d, offsets=0x%08lx\n",
p->size, p->frames,
(unsigned long)p->offsets);
return ret;
}
/********************************************************
All other V4L1 calls are handled by v4l1_compat module.
Those calls will be translated into V4L2 calls, and
__video_do_ioctl will be called again, with one or more
V4L2 ioctls.
********************************************************/
if (_IOC_TYPE(cmd) == 'v' && _IOC_NR(cmd) < BASE_VIDIOCPRIVATE)
return v4l_compat_translate_ioctl(file, cmd, arg,
__video_do_ioctl);
#endif
switch (cmd) {
/* --- capabilities ------------------------------------------ */
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
if (!ops->vidioc_querycap)
break;
ret = ops->vidioc_querycap(file, fh, cap);
if (!ret)
dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
"version=0x%08x, "
"capabilities=0x%08x\n",
cap->driver, cap->card, cap->bus_info,
cap->version,
cap->capabilities);
break;
}
}