本文共 6575 字,大约阅读时间需要 21 分钟。
V4L2支持两种方式来采集数据,具体哪两种请回看,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;}
转载地址:http://keuen.baihongyu.com/