Linux camera驱动(6) - ioctl详解

1. 用户空间进行 ioctl 操作

        V4L2支持两种方式来采集数据,具体哪两种请回看《Linux camera驱动(1) - 概述》,ioctl 编是摄像头驱动中通过mmap方式采集数据的最关键的一个接口。

static const struct file_operations v4l2_fops = {
	.unlocked_ioctl = v4l2_ioctl,
     ······
};

static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    vdev->fops->ioctl(filp, cmd, arg);  //调用master的ioctl()
}
static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
	/*通过video_usercopy函数间接调用mxc_v4l_do_ioctl函数*/
     return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
}

static long mxc_v4l_do_ioctl(struct file *file, unsigned int ioctlnr, void *arg)
{
	/*我们根据一般摄像头应用程序的执行调用过程的顺序来分析*/
	switch (ioctlnr) {

	/*1.它的目的很简单,只是简单问问,你是谁?你能干什么?基本就是驱动来填充应用程序传入的v4l2_capability结构体cap,设置其中的一些参数*/
	/*然后应用程序就可以使用这些参数了,包括设置名字、capabilities属性等等*/
	case VIDIOC_QUERYCAP: {
		struct v4l2_capability *cap = arg;
		cap->version = KERNEL_VERSION(0, 1, 11);
		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
				    V4L2_CAP_VIDEO_OVERLAY |
				    V4L2_CAP_STREAMING |
				    V4L2_CAP_READWRITE;
		cap->card[0] = '\0';
		cap->bus_info[0] = '\0';
		break;
	}

	/*6.获取第5步中设置的一些信息,后面有用*/
	case VIDIOC_G_FMT: {
		struct v4l2_format *gf = arg;
		pr_debug("   case VIDIOC_G_FMT\n");
		retval = mxc_v4l2_g_fmt(cam, gf);
		break;
	}

	/*5.调用mxc_v4l2_s_fmt()函数*/
	case VIDIOC_S_FMT: {
		struct v4l2_format *sf = arg;
		retval = mxc_v4l2_s_fmt(cam, sf);
		break;
	}
 
	/*7.分配内存*/
	case VIDIOC_REQBUFS: {
		struct v4l2_requestbuffers *req = arg;
		/*判断申请的buffer数目是否超过最大的数目*/
		if (req->count > FRAME_NUM) {
			pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: not enough buffers\n");
			req->count = FRAME_NUM;
		}
            ······
		mxc_streamoff(cam);	/*-->*/
		if (req->memory & V4L2_MEMORY_MMAP) {
			mxc_free_frame_buf(cam); /*释放掉内存,主要是dma_free_coherent函数*/
			retval = mxc_allocate_frame_buf(cam, req->count); /*重新分配内存*/
		}
		break;
	}

	/*9.调用mxc_v4l2_buffer_status*/
	case VIDIOC_QUERYBUF: {
		struct v4l2_buffer *buf = arg;
		int index = buf->index;

		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
			pr_err("ERROR: v4l2 capture: VIDIOC_QUERYBUFS: wrong buffer type\n");
			retval = -EINVAL;
			break;
		}

		if (buf->memory & V4L2_MEMORY_MMAP) {
			memset(buf, 0, sizeof(buf));
			buf->index = index;
		}

		if (buf->memory & V4L2_MEMORY_MMAP)
			retval = mxc_v4l2_buffer_status(cam, buf); /*-->*/
		break;
	}

	/*10.根据cam->frame[index].buffer.flags的不同选择不同的操作,理论上走到这一步,flags应该是V4L2_BUF_FLAG_MAPPED*/
	case VIDIOC_QBUF: {
		struct v4l2_buffer *buf = arg;
		int index = buf->index;
		pr_debug("   case VIDIOC_QBUF\n");
		frameerr=ipu_get_csi_frame_error(cam->ipu);
		camerastatus = vidioc_int_g_lock_status(cam->sensor);
		spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
		if ((cam->frame[index].buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
			cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED;		/*给这个flags添加一个V4L2_BUF_FLAG_QUEUED属性*/
			list_add_tail(&cam->frame[index].queue, &cam->ready_q);		/*然后将这个buffer添加到cam->ready_q*/
		} else if 
                  ······
		} else if (cam->frame[index].buffer.flags & V4L2_BUF_FLAG_DONE) {
			pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: overwrite done buffer.\n");
			cam->frame[index].buffer.flags &= ~V4L2_BUF_FLAG_DONE;	/*清零*/
			cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED;	/*重新设置*/
			retval = -EINVAL;
		}
		break;
	}

	/*12.当开始数据传输以后,当有数据填满一个buffer,就可以将这个buffer出队了,应用程序会调用到VIDIOC_DQBUF 	这个ioctl函数*/
	case VIDIOC_DQBUF: {
		struct v4l2_buffer *buf = arg;
		pr_debug("   case VIDIOC_DQBUF\n");
		if ((cam->enc_counter == 0) && (file->f_flags & O_NONBLOCK)) {
			retval = -EAGAIN;
			break;
		}
		retval = mxc_v4l_dqueue(cam, buf);  /*-->*/
		break;
	}

	/*11.从第10步QBUF以后就可以开始传输数据了*/
	case VIDIOC_STREAMON: {
		pr_debug("   case VIDIOC_STREAMON\n");
		retval = mxc_streamon(cam);	/-->/
		break;
	}

	/*13.先等待idmac结束,然后先关掉csi,然关闭idmac,再关掉CSI--MEM channel*/
	case VIDIOC_STREAMOFF: {
		pr_debug("   case VIDIOC_STREAMOFF\n");
		retval = mxc_streamoff(cam);
		break;
	}

	/*3.判断传入的v4l2_cropcap *cap中type是否为V4L2_BUF_TYPE_VIDEO_CAPTURE或者V4L2_BUF_TYPE_VIDEO_OVERLAY*/
	/*应用程序中设置成了V4L2_BUF_TYPE_VIDEO_CAPTURE??我不记得*/
	case VIDIOC_CROPCAP: {
		struct v4l2_cropcap *cap = arg;
		pr_debug("   case VIDIOC_CROPCAP\n");
		if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
			retval = -EINVAL;
			break;
		}
		/*cam->crop_bounds和cam->crop_defrect两个变量是在mxc_v4l_open函数中进行设置的*/
		cap->bounds = cam->crop_bounds;
		cap->defrect = cam->crop_defrect;
		break;
	}

	/*4.跳转到mxc_v4l2_s_param中去执行*/
	case VIDIOC_S_PARM:  {
		struct v4l2_streamparm *parm = arg;
		pr_debug("   case VIDIOC_S_PARM\n");
		if (cam->sensor)
			retval = mxc_v4l2_s_param(cam, parm);
		break;
	}

	/*2.根据应用程序中传过来的args参数来判断,MXC_V4L2_CAPTURE_NUM_INPUTS==2*/
	case VIDIOC_S_INPUT: {
		int *index = arg;
		pr_debug("   case VIDIOC_S_INPUT\n");

		/*在init_camera_struct函数中将cam->current_input初始化为0了,如果要设置的input和当前input相同的话,就直接退出*/
		if (*index == cam->current_input)
			break;

		/*因为mxc_capture_inputs[0].status==0,所以后面的函数不会执行*/
		if ((mxc_capture_inputs[cam->current_input].status & V4L2_IN_ST_NO_POWER) == 0) {
			retval = mxc_streamoff(cam);
			if (retval)
				break;
			mxc_capture_inputs[cam->current_input].status |=
							V4L2_IN_ST_NO_POWER;
		}

		/*根据mxc_capture_inputs[*index].name来选择执行哪个函数,在open函数中有执行过相同步骤,不理解??*/
		if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) {
#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
			retval = csi_enc_select(cam);
			if (retval)
				break;
#endif
		} else if (strcmp(mxc_capture_inputs[*index].name,
				  "CSI IC MEM") == 0) {
#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
			retval = prp_enc_select(cam);
			if (retval)
				break;
#endif
		}

		/*将mxc_capture_inputs[*index].status标志位清零,然后将cam->current_input指向当前的mxc_capture_inputs[*index]*/
		mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;
		cam->current_input = *index;
		break;
	}

  case VIDIOC_ENUM_FMT: {	//获取从设备的pixelformat		
		struct v4l2_fmtdesc *f = arg;
		if (cam->sensor)
			retval = vidioc_int_enum_fmt_cap(cam->sensor, f);
		else {
			pr_err("ERROR: v4l2 capture: slave not found!\n");
			retval = -ENODEV;
		}
		break;
	}
	
	case VIDIOC_DBG_G_CHIP_IDENT: {	//获取解码芯片的身份,即名字和类型
		struct v4l2_dbg_chip_ident *p = arg;
		p->ident = V4L2_IDENT_NONE;
		p->revision = 0;
		if (cam->sensor)
			retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p);
		else {
			pr_err("ERROR: v4l2 capture: slave not found!\n");
			retval = -ENODEV;
		}
		break;
	}
	case VIDIOC_TRY_FMT:
	case VIDIOC_G_TUNER:
	case VIDIOC_S_TUNER:
	case VIDIOC_G_FREQUENCY:
	case VIDIOC_S_FREQUENCY:
   ······
	default:
		pr_debug("   case default or not supported\n");
		retval = -EINVAL;
		break;
	}

	if (ioctlnr != VIDIOC_DQBUF)
		up(&cam->busy_lock);
	return retval;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值