camera驱动
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了camera驱动相关的知识,希望对你有一定的参考价值。
1. 用户空间进行 ioctl 操作
V4L2支持两种方式来采集数据,具体哪两种请回看《camera驱动(一)》,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;
}
以上是关于camera驱动的主要内容,如果未能解决你的问题,请参考以下文章
android摄像头(camera)之 v4l2的c测试代码