本文主要是介绍linux下的cmos摄像头驱动设计2-应用程序的调用与驱动程序的关系,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
上一篇写了,摄像头驱动的注册过程,这次写写应用程序的调用与驱动程序的关系,遵循V4L2架构的应用程序主要由几个ioctl组成,
其实也比较简单,有时候驱动写的不标准,应用程序按标准的操作操作就不行,出不来图像,这时需要跟踪驱动程序,看看哪个地方出错了,
首先,要打开设备
1.fd = open(dev_name, O_RDWR /* required */| O_NONBLOCK, 0);
dev_name为video0 或者video1或者video几,首先要打开设备,打开这个设备后,就可以获得一个文件操作符fd,这时就可以通过它来执行视频设备的操作函数,
从而达到控制设备的目的。
2.ioctl(fd, VIDIOC_QUERYCAP, &cap)
查询设备节点具有的功能,执行视频节点的ioctl函数,最终会调用到内核中的
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)这个函数
/* --- 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;}
其中可以看到,核心的调用函数为
ret = ops->vidioc_querycap(file, fh, cap);
这个函数指针调用最终会通过
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {.vidioc_querycap = fimc_vidioc_querycap_capture,.vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane,.vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane,.vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,.vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane,.vidioc_s_fmt_type_private = fimc_s_fmt_vid_private,.vidioc_reqbufs = fimc_cap_reqbufs,.vidioc_querybuf = fimc_cap_querybuf,.vidioc_qbuf = fimc_cap_qbuf,.vidioc_dqbuf = fimc_cap_dqbuf,.vidioc_streamon = fimc_cap_streamon,.vidioc_streamoff = fimc_cap_streamoff,.vidioc_queryctrl = fimc_vidioc_queryctrl,.vidioc_g_ctrl = fimc_vidioc_g_ctrl,.vidioc_s_ctrl = fimc_cap_s_ctrl,.vidioc_g_crop = fimc_cap_g_crop,.vidioc_s_crop = fimc_cap_s_crop,.vidioc_cropcap = fimc_cap_cropcap,.vidioc_enum_input = fimc_cap_enum_input,.vidioc_s_input = fimc_cap_s_input,.vidioc_g_input = fimc_cap_g_input,
};
调用到
static int fimc_vidioc_querycap_capture(struct file *file, void *priv,struct v4l2_capability *cap)
{struct fimc_ctx *ctx = file->private_data;struct fimc_dev *fimc = ctx->fimc_dev;strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);cap->bus_info[0] = 0;cap->version = KERNEL_VERSION(1, 0, 0);cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |V4L2_CAP_VIDEO_CAPTURE_MPLANE;return 0;
}
3,接下来的操作一般都是
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {printf("************** %s, line = %d\n", __FUNCTION__, __LINE__);fprintf(stderr, "%s is no video capture device\n", dev_name);return false;}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {printf("************** %s, line = %d\n", __FUNCTION__, __LINE__);fprintf(stderr, "%s does not support streaming i/o\n", dev_name);return false;}
根据上面驱动程序中的代码,说明,只有驱动程序注册正确,执行这些操作就不会报no video capture device 和does not support streaming 的错误。
4.接下来的操作一般是
v4l2_input input;memset(&input, 0, sizeof(struct v4l2_input));input.index = 0;int rtn = ioctl(fd, VIDIOC_S_INPUT, &input);
设置输入,这个操作有时候是很重要的,没它不行,为什么重要,请看源码。
和第二句应用程序的分析一样,最终会跳到
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)这个函数
case VIDIOC_S_INPUT:{unsigned int *i = arg;if (!ops->vidioc_s_input)break;dbgarg(cmd, "value=%d\n", *i);ret = ops->vidioc_s_input(file, fh, *i);break;}
然后根据操作函数集,会最终执行
int fimc_s_input(struct file *file, void *fh, unsigned int i)
{
<span style="white-space:pre"> </span>struct fimc_global *fimc = get_fimc_dev();
<span style="white-space:pre"> </span>struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
<span style="white-space:pre"> </span>struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
<span style="white-space:pre"> </span>int ret = 0;<span style="white-space:pre"> </span>fimc_dbg("%s: index %d\n", __func__, i);<span style="white-space:pre"> </span>if (i < 0 || i >= FIMC_MAXCAMS) {
<span style="white-space:pre"> </span>fimc_err("%s: invalid input index\n", __func__);
<span style="white-space:pre"> </span>return -EINVAL;
<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>if (!fimc->camera_isvalid[i])
<span style="white-space:pre"> </span>return -EINVAL;<span style="white-space:pre"> </span>if (fimc->camera[i]->sd && fimc_cam_use) {
<span style="white-space:pre"> </span>fimc_err("%s: Camera already in use.\n", __func__);
<span style="white-space:pre"> </span>return -EBUSY;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>mutex_lock(&ctrl->v4l2_lock);<span style="white-space:pre"> </span>/* If ctrl->cam is not NULL, there is one subdev already registered.
<span style="white-space:pre"> </span> * We need to unregister that subdev first. */
<span style="white-space:pre"> </span>if (i != fimc->active_camera) {
<span style="white-space:pre"> </span>fimc_info1("\n\nfimc_s_input activating subdev\n");
<span style="white-space:pre"> </span>if (ctrl->cam && (ctrl->cam->sd || ctrl->flite_sd))
<span style="white-space:pre"> </span>fimc_release_subdev(ctrl);
<span style="white-space:pre"> </span>else if (ctrl->is.sd)
<span style="white-space:pre"> </span>fimc_is_release_subdev(ctrl);
<span style="white-space:pre"> </span>ctrl->cam = fimc->camera[i];<span style="white-space:pre"> </span>if ((ctrl->cam->id != CAMERA_WB) && (ctrl->cam->id !=
<span style="white-space:pre"> </span>CAMERA_WB_B) && (!ctrl->cam->use_isp) && fimc_cam_use) {
<span style="white-space:pre"> </span>ret = fimc_configure_subdev(ctrl);
<span style="white-space:pre"> </span>if (ret < 0) {
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>fimc_err("%s: Could not register camera" \
<span style="white-space:pre"> </span>" sensor with V4L2.\n", __func__);
<span style="white-space:pre"> </span>return -ENODEV;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>fimc->active_camera = i;
<span style="white-space:pre"> </span>fimc_info2("fimc_s_input activated subdev = %d\n", i);
<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>if (!fimc_cam_use) {
<span style="white-space:pre"> </span>if (i == fimc->active_camera) {
<span style="white-space:pre"> </span>ctrl->cam = fimc->camera[i];
<span style="white-space:pre"> </span>fimc_info2("fimc_s_input activating subdev FIMC%d\n",
<span style="white-space:pre"> </span>ctrl->id);
<span style="white-space:pre"> </span>} else {
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -EINVAL;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>if (ctrl->cam->use_isp) {
<span style="white-space:pre"> </span> /* fimc-lite attatch */
<span style="white-space:pre"> </span> ret = fimc_subdev_attatch(ctrl);
<span style="white-space:pre"> </span> if (ret) {
<span style="white-space:pre"> </span> fimc_err("subdev_attatch failed\n");
<span style="white-space:pre"> </span> mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span> return -ENODEV;
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span> /* fimc-is attatch */
<span style="white-space:pre"> </span> ctrl->is.sd = fimc_is_get_subdev(i);
<span style="white-space:pre"> </span> if (IS_ERR_OR_NULL(ctrl->is.sd)) {
<span style="white-space:pre"> </span>fimc_err("fimc-is subdev_attatch failed\n");
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -ENODEV;
<span style="white-space:pre"> </span> }<span style="white-space:pre"> </span> ctrl->is.fmt.width = ctrl->cam->width;
<span style="white-space:pre"> </span> ctrl->is.fmt.height = ctrl->cam->height;
<span style="white-space:pre"> </span> ctrl->is.frame_count = 0;
<span style="white-space:pre"> </span> if (fimc_cam_use) {
<span style="white-space:pre"> </span>ret = fimc_is_init_cam(ctrl);
<span style="white-space:pre"> </span>if (ret < 0) {
<span style="white-space:pre"> </span>fimc_dbg("FIMC-IS init clock failed");
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -ENODEV;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ret = v4l2_subdev_call(ctrl->is.sd, core, s_power, 1);
<span style="white-space:pre"> </span>if (ret < 0) {
<span style="white-space:pre"> </span>fimc_dbg("FIMC-IS init failed");
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -ENODEV;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ret = v4l2_subdev_call(ctrl->is.sd, core, load_fw);
<span style="white-space:pre"> </span>if (ret < 0) {
<span style="white-space:pre"> </span>fimc_dbg("FIMC-IS init failed");
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -ENODEV;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ret = v4l2_subdev_call(ctrl->is.sd, core, init, ctrl->cam->sensor_index);
<span style="white-space:pre"> </span>if (ret < 0) {
<span style="white-space:pre"> </span>fimc_dbg("FIMC-IS init failed");
<span style="white-space:pre"> </span>mutex_unlock(&ctrl->v4l2_lock);
<span style="white-space:pre"> </span>return -ENODEV;
}}
}mutex_unlock(&ctrl->v4l2_lock);return 0;
}
这个函数中,最关键的是
ret = fimc_configure_subdev(ctrl);这句代码,我们知道cmos摄像头驱动也属于一个I2C驱动,因为在对摄像头芯片初始化时,设置参数时,要通过I2C
总线来操作,linux内核驱动为I2C总线驱动设计了一种框架,做到了设备与驱动的分离,这个类似于platform平台驱动原理,不懂的可以先去学一下那部分,前面我们驱动分析时,一直没有提到对于摄像头驱动I2C设备的注册,摄像头senor部分的初始化的触发,就在于这个对于的I2C设备的注册,只有系统中注册了摄像头senor对应的i2c设备,I2C设备与senor部分对应的driver就会进行匹配,然后执行对于的probe函数,probe函数中会进行摄像头senor部分的初始化。
在这里fimc_configure_subdev(ctrl)的作用主要就是就是注册一个i2c设备,当然它也干其他的了,还注册了一个V4L2子设备(sub-dev),并且把他们关联了起来,这样,以后可以通过I2C设备访问V4L2_SUB设备,也可以通过V4L2_SUB设备控制i2c设备,具体的代码我就不分析了,自己看源码吧。
5.接下来一般就是设置视频采集的格式了
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = width;fmt.fmt.pix.height = height;fmt.fmt.pix.sizeimage = (fmt.fmt.pix.width*fmt.fmt.pix.height*12)/8;fmt.fmt.pix.field = V4L2_FIELD_NONE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
ioctl(fd, VIDIOC_S_FMT, &fmt)
设置视频采集处理的格式、宽、高,通过IOCTL转到驱动程序,从而控制硬件,达到设置视频输出的目标
明天再写吧,睡觉了
这篇关于linux下的cmos摄像头驱动设计2-应用程序的调用与驱动程序的关系的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!