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转到驱动程序,从而控制硬件,达到设置视频输出的目标

明天再写吧,睡觉了






  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值