下面分析mxc_v4l_ioctl函数:
- static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- pr_debug("In MVC:mxc_v4l_ioctl\n");
- return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
- }
它通过调用video_usercopy函数,最终就调用到这个mxc_v4l_do_ioctl函数,这个mxc_v4l_do_ioctl函数内部包含一个switch语句,根据传入不同的ioctl宏来选择执行不同的语句。下面就按不同的宏来分析。
我们根据一般摄像头应用程序的执行调用过程来顺序分析。
1.VIDIOC_QUERYCAP宏
- case VIDIOC_QUERYCAP: {
- struct v4l2_capability *cap = arg;
- pr_debug(" case VIDIOC_QUERYCAP\n");
- strcpy(cap->driver, "mxc_v4l2");
- cap->version = KERNEL_VERSION(0, 1, 11);
- cap-> = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OVERLAY |
- V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- cap->card[0] = '\0';
- cap->bus_info[0] = '\0';
- break;
- }
它的目的很简单,只是简单问问“你是谁?你能干什么?”基本就是驱动来填充应用程序传入的v4l2_capability结构体cap,设置其中的一些参数,然后应用程序就可以使用这些参数了。包括设置名字为”mxc_v4l2”,capabilities属性包括V4L2_CAP_VIDEO_CAPTURE,
V4L2_CAP_VIDEO_OVERLAY,V4L2_CAP_STREAMING,V4L2_CAP_READWRITE等等。
2.VIDIOC_S_INPUT宏
- case VIDIOC_S_INPUT: {
- int *index = arg;
- pr_debug(" case VIDIOC_S_INPUT\n");
- if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
- retval = -EINVAL;
- break;
- }
/*根据从从应用程序中传过来的arg参数来判断。MXC_V4L2_CAPTURE_NUM_INPUTS==2。*/
- if (*index == cam->current_input)
- break;
/*在init_camera_struct函数中将cam->current_input初始化为0了。如果要设置的input与当前input相同的话,就直接退出即可。*/
- 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[]这个数组如下所示:
- static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {
- {
- .index = 0,
- .name = "CSI IC MEM",
- .type = V4L2_INPUT_TYPE_CAMERA,
- .audioset = 0,
- .tuner = 0,
- .std = V4L2_STD_UNKNOWN,
- .status = 0,
- },
- {
- .index = 1,
- .name = "CSI MEM",
- .type = V4L2_INPUT_TYPE_CAMERA,
- .audioset = 0,
- .tuner = 0,
- .std = V4L2_STD_UNKNOWN,
- .status = V4L2_IN_ST_NO_POWER,
- },
- };
可以看出mxc_capture_inputs[0].status= 0,所以下面的语句不会执行。
*/
- 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].name来选择执行哪一个函数,之前分析过,这两个函数都是去填充cam_data结构体中的几个函数指针。*/
- mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;
- cam->current_input = *index;
/*将mxc_capture_inputs[*index].status位清0,然后将cam->current_input指向当前的mxc_capture_inputs[*index]。*/
- break;
3. VIDIOC_CROPCAP宏
- 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;
- }
/*判断传入的v4l2_cropcap*cap中type是否是V4L2_BUF_TYPE_VIDEO_CAPTURE或V4L2_BUF_TYPE_VIDEO_OVERLAY,应用程序中设置成了V4L2_BUF_TYPE_VIDEO_CAPTURE。*/
- cap->bounds = cam->crop_bounds;
- cap->defrect = cam->crop_defrect;
/*将传入的v4l2_cropcap*cap中的bounds和defrect设置成cam->crop_bounds和cam->crop_defrect,这两个变量是在mxc_v4l_open函数中进行设置的。*/
- break;
4. VIDIOC_S_PARM 宏
- 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);
- else {
- pr_err("ERROR: v4l2 capture: slave not found!\n");
- retval = -ENODEV;
- }
- break;
- }
这个函数跳转到mxc_v4l2_s_param中去执行了。
- static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
- {
- struct v4l2_ifparm ifparm;
- struct v4l2_format cam_fmt;
- struct v4l2_streamparm currentparm;
- ipu_csi_signal_cfg_t csi_param;
- u32 current_fps, parm_fps;
- int err = 0;
- pr_debug("In mxc_v4l2_s_param\n");
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");
- return -EINVAL;
- }
/*判断传入的参数parm->type是否为V4L2_BUF_TYPE_VIDEO_CAPTURE,所以肯定需要在应用程序中设置这一项。*/
- /* Stop the viewfinder */
- if (cam->overlay_on == true)
- stop_preview(cam);
/*这一项在init_camera_struct函数初始化的时候设置为了false。*/
- currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- /* First check that this device can support the changes requested. */
- err = vidioc_int_g_parm(cam->sensor, ¤tparm);
- if (err) {
- pr_err("%s: vidioc_int_g_parm returned an error %d\n",
- __func__, err);
- goto exit;
- }
/*vidioc_int_g_parm 函数,以ov5640.c为例,它最终会调用到里面的ioctl_g_parm函数。
- static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
- {
- struct sensor_data *sensor = s->priv;
- struct v4l2_captureparm *cparm = &a->parm.capture;
- int ret = 0;
- switch (a->type) {
- /* This is the only case currently handled. */
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memset(a, 0, sizeof(*a));
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cparm->capability = sensor->streamcap.capability;
- cparm->timeperframe = sensor->streamcap.timeperframe;
- cparm->capturemode = sensor->streamcap.capturemode;
- ret = 0;
- break;
- /* These are all the possible cases. */
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- ret = -EINVAL;
- break;
- default:
- pr_debug(" type is unknown - %d\n", a->type);
- ret = -EINVAL;
- break;
- }
- return ret;
- }
看这个函数,它最终的目的是根据cam->sensor->priv里面的值来设置currentparm->parm.capture结构体,而这个currentparm就是获取到的当前sensor的parm参数。
*/
- current_fps = currentparm.parm.capture.timeperframe.denominator
- / currentparm.parm.capture.timeperframe.numerator;
- parm_fps = parm->parm.capture.timeperframe.denominator
- / parm->parm.capture.timeperframe.numerator;
/*设置current_fps和parm_fps这两个值,current_fps是当前每秒传输帧数,parm_fps是参数中设置的每秒传输帧数。*/
- pr_debug(" Current capabilities are %x\n",
- currentparm.parm.capture.capability);
- pr_debug(" Current capturemode is %d change to %d\n",
- currentparm.parm.capture.capturemode,
- parm->parm.capture.capturemode);
- pr_debug(" Current framerate is %d change to %d\n",
- current_fps, parm_fps);
- /* This will change any camera settings needed. */
- err = vidioc_int_s_parm(cam->sensor, parm);
- if (err) {
- pr_err("%s: vidioc_int_s_parm returned an error %d\n",
- __func__, err);
- goto exit;
- }
/* 这个vidioc_int_s_parm函数最终会调用到ov5640.c中的ioctl_s_parm函数。它如下所示:
- static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
- {
- struct sensor_data *sensor = s->priv;
- struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
- u32 tgt_fps; /* target frames per secound */
- enum ov5640_frame_rate frame_rate;
- int ret = 0;
- /* Make sure power on */
- ov5640_power_down(0);
- switch (a->type) {
- /* This is the only case currently handled. */
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- /* Check that the new frame rate is allowed. */
- if ((timeperframe->numerator == 0) ||
- (timeperframe->denominator == 0)) {
- timeperframe->denominator = DEFAULT_FPS;
- timeperframe->numerator = 1;
- }
- tgt_fps = timeperframe->denominator /
- timeperframe->numerator;
- if (tgt_fps > MAX_FPS) {
- timeperframe->denominator = MAX_FPS;
- timeperframe->numerator = 1;
- } else if (tgt_fps < MIN_FPS) {
- timeperframe->denominator = MIN_FPS;
- timeperframe->numerator = 1;
- }
- /* Actual frame rate we use */
- tgt_fps = timeperframe->denominator /
- timeperframe->numerator;
- if (tgt_fps == 15)
- frame_rate = ov5640_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5640_30_fps;
- else {
- pr_err(" The camera frame rate is not supported!\n");
- return -EINVAL;
- }
- ret = ov5640_change_mode(frame_rate,
- a->parm.capture.capturemode);
- if (ret < 0)
- return ret;
- sensor->streamcap.timeperframe = *timeperframe;
- sensor->streamcap.capturemode = a->parm.capture.capturemode;
- break;
- /* These are all the possible cases. */
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- pr_debug(" type is not " \
- "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
- a->type);
- ret = -EINVAL;
- break;
- default:
- pr_debug(" type is unknown - %d\n", a->type);
- ret = -EINVAL;
- break;
- }
- return ret;
- }
这个函数首先判断应用程序中传进来的timeperframe->numerator和timeperframe->denominator两个参数,然后计算出tgt_fps。最后通过ov5640_change_mode函数来设置ov5640摄像头里面的寄存器的值,因为应用程序想要设置的是帧率等信息,这些信息会通过V4L2框架设置到摄像头里面,因为真正采集数据的是摄像头。
*/
/*综上来看,mxc_v4l2_s_param通过vidioc_int_g_parm和vidioc_int_s_parm两个函数,来获取sensor的parm设置,然后修改它。*/
- /* If resolution changed, need to re-program the CSI */
- /* Get new values. */
- vidioc_int_g_ifparm(cam->sensor, &ifparm);
/*通过上面的函数,已经将摄像头里面的信息修改了,但是应用程序还不知道,在这里调用vidioc_int_g_ifparm函数来获取ifparm的信息,然后告诉应用程序。如果分辨率改变的话需要重新修改CSI参数。调用vidioc_int_g_ifparm来获取sensor的ifparm参数。*/
- csi_param.data_width = 0;
- csi_param.clk_mode = 0;
- csi_param.ext_vsync = 0;
- csi_param.Vsync_pol = 0;
- csi_param.Hsync_pol = 0;
- csi_param.pixclk_pol = 0;
- csi_param.data_pol = 0;
- csi_param.sens_clksrc = 0;
- csi_param.pack_tight = 0;
- csi_param.force_eof = 0;
- csi_param.data_en_pol = 0;
- csi_param.data_fmt = 0;
- csi_param.csi = cam->csi;
- csi_param.mclk = 0;
- pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);
- if (ifparm.u.bt656.clock_curr == 0)
- csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
- else
- csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
- csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
- if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {
- csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
- } else if (ifparm.u.bt656.mode
- == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {
- csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
- } else {
- csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
- }
- csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
- csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
- csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;
- /* 设置csi_param的一些参数。 */
- /* if the capturemode changed, the size bounds will have changed. */
- cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
- pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n",
- cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
- /* 通过vidioc_int_g_fmt_cap函数调用到ov5640.c中的ioctl_g_fmt_cap函数,如下:
- static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
- {
- struct sensor_data *sensor = s->priv;
- f->fmt.pix = sensor->pix;
- return 0;
- }
- 它获取了根据cam->sensor->priv来获取cam_fmt参数,保存在cam_fmt->fmt.pix中。
- */
- csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
- cam->crop_bounds.top = cam->crop_bounds.left = 0;
- cam->crop_bounds.width = cam_fmt.fmt.pix.width;
- cam->crop_bounds.height = cam_fmt.fmt.pix.height;
- /* 根据获得的cam_fmt参数继续设置csi_param的参数和cam->crop_bounds参数。 */
- /*
- * Set the default current cropped resolution to be the same with
- * the cropping boundary(except for tvin module).
- */
- if (cam->device_type != 1) {
- cam->crop_current.width = cam->crop_bounds.width;
- cam->crop_current.height = cam->crop_bounds.height;
- }
- /* This essentially loses the data at the left and bottom of the image
- * giving a digital zoom image, if crop_current is less than the full
- * size of the image. */
- ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,
- cam->crop_current.height, cam->csi);
- ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,
- cam->crop_current.top,
- cam->csi);
- ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,
- cam->crop_bounds.height,
- cam_fmt.fmt.pix.pixelformat, csi_param);
- exit:
- if (cam->overlay_on == true)
- start_preview(cam);
- return err;
- }
/*最后调用ipu_csi_set_window_size和ipu_csi_set_window_pos重新设置窗口的大小和位置,这两个函数都在mxc_v4l_open函数中分析了,在这就不再分析。然后继续调用ipu_csi_init_interface函数来设置底层的寄存器的值。*/
5.VIDIOC_S_FMT宏
- case VIDIOC_S_FMT: {
- struct v4l2_format *sf = arg;
- pr_debug(" case VIDIOC_S_FMT\n");
- retval = mxc_v4l2_s_fmt(cam, sf);
- break;
- }
它跳转到mxc_v4l2_s_fmt函数中去执行:
- static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
- {
- int retval = 0;
- int size = 0;
- int bytesperline = 0;
- int *width, *height;
- pr_debug("In MVC: mxc_v4l2_s_fmt\n");
- switch (f->type) { //应用程序传进来的是V4L2_BUF_TYPE_VIDEO_CAPTUR。
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
- if (!valid_mode(f->fmt.pix.pixelformat)) {
- pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format "
- "not supported\n");
- return -EINVAL;
- }
- /*
- * Force the capture window resolution to be crop bounds
- * for CSI MEM input mode.
- */
- if (strcmp(mxc_capture_inputs[cam->current_input].name,
- "CSI MEM") == 0) {
- f->fmt.pix.width = cam->crop_current.width;
- f->fmt.pix.height = cam->crop_current.height;
- }
- /* 设置 fmt.pix.width和 fmt.pix.height, cam->crop_current.width和 cam->crop_current.height这两个参数是在上一个ioctl(VIDIOC_S_PARM)调用中赋值的。 */
- if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
- height = &f->fmt.pix.width;
- width = &f->fmt.pix.height;
- } else {
- width = &f->fmt.pix.width;
- height = &f->fmt.pix.height;
- }
- /* 根据 cam->rotation参数决定是否反转图像的宽度和高度。 */
- /* stride line limitation */
- *width -= *width % 8;
- *height -= *height % 8;
- /* 宽度和高度都像8取整,这是对宽度和高度进行一些微调。 */
- if (*width == 0 || *height == 0) {
- pr_err("ERROR: v4l2 capture: width or height"
- " too small.\n");
- return -EINVAL;
- }
- if ((cam->crop_current.width / *width > 8) ||
- ((cam->crop_current.width / *width == 8) &&
- (cam->crop_current.width % *width))) {
- *width = cam->crop_current.width / 8;
- if (*width % 8)
- *width += 8 - *width % 8;
- pr_err("ERROR: v4l2 capture: width exceeds limit "
- "resize to %d.\n",
- *width);
- }
- if ((cam->crop_current.height / *height > 8) ||
- ((cam->crop_current.height / *height == 8) &&
- (cam->crop_current.height % *height))) {
- *height = cam->crop_current.height / 8;
- if (*height % 8)
- *height += 8 - *height % 8;
- pr_err("ERROR: v4l2 capture: height exceeds limit "
- "resize to %d.\n",
- *height);
- }
- /* 上面这两个判断应该是在反转的情况下调整宽度和高度的大小。 */
- switch (f->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB565:
- size = f->fmt.pix.width * f->fmt.pix.height * 2;
- bytesperline = f->fmt.pix.width * 2;
- break;
- case V4L2_PIX_FMT_BGR24:
- size = f->fmt.pix.width * f->fmt.pix.height * 3;
- bytesperline = f->fmt.pix.width * 3;
- break;
- case V4L2_PIX_FMT_RGB24:
- size = f->fmt.pix.width * f->fmt.pix.height * 3;
- bytesperline = f->fmt.pix.width * 3;
- break;
- case V4L2_PIX_FMT_BGR32:
- size = f->fmt.pix.width * f->fmt.pix.height * 4;
- bytesperline = f->fmt.pix.width * 4;
- break;
- case V4L2_PIX_FMT_RGB32:
- size = f->fmt.pix.width * f->fmt.pix.height * 4;
- bytesperline = f->fmt.pix.width * 4;
- break;
- case V4L2_PIX_FMT_YUV422P:
- size = f->fmt.pix.width * f->fmt.pix.height * 2;
- bytesperline = f->fmt.pix.width;
- break;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- size = f->fmt.pix.width * f->fmt.pix.height * 2;
- bytesperline = f->fmt.pix.width * 2;
- break;
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
- bytesperline = f->fmt.pix.width;
- break;
- case V4L2_PIX_FMT_NV12:
- size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
- bytesperline = f->fmt.pix.width;
- break;
- default:
- break;
- }
- /* 根据fmt.pix.pixelformat的格式来计算size和bytesperline的大小。这个size就是指图像的大小,bytesperline就是指一行数据所占用的字节数。 */
- if (f->fmt.pix.bytesperline < bytesperline)
- f->fmt.pix.bytesperline = bytesperline;
- else
- bytesperline = f->fmt.pix.bytesperline;
- if (f->fmt.pix.sizeimage < size)
- f->fmt.pix.sizeimage = size;
- else
- size = f->fmt.pix.sizeimage;
- cam->v2f.fmt.pix = f->fmt.pix;
- if (cam->v2f.fmt.pix.priv != 0) {
- if (copy_from_user(&cam->offset,
- (void *)cam->v2f.fmt.pix.priv,
- sizeof(cam->offset))) {
- retval = -EFAULT;
- break;
- }
- }
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
- retval = verify_preview(cam, &f->fmt.win);
- cam->win = f->fmt.win;
- break;
- default:
- retval = -EINVAL;
- }
- pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
- __func__,
- cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
- pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
- __func__,
- cam->crop_bounds.width, cam->crop_bounds.height);
- pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
- __func__,
- cam->crop_defrect.width, cam->crop_defrect.height);
- pr_debug("End of %s: crop_current widthxheight %d x %d\n",
- __func__,
- cam->crop_current.width, cam->crop_current.height);
- return retval;
- }
注意上面我标红的代码:cam->v2f.fmt.pix=f->fmt.pix;在这个函数中,对于应用程序中传入的参数,或者修改的参数,到此为止,都已经设置完毕了。然后在这个函数中,对cam->v2f结构体进行赋值,这个结构体里面的cam->v2f.fmt.pix里面包含width,height,pixelformat,bytesperline,sizeimage等等信息,这些信息在后面申请内存大小等函数中都会使用到。
6.VIDIOC_G_FMT宏
- case VIDIOC_G_FMT: {
- struct v4l2_format *gf = arg;
- pr_debug(" case VIDIOC_G_FMT\n");
- retval = mxc_v4l2_g_fmt(cam, gf);
- break;
- }
- static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
- {
- int retval = 0;
- pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type);
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
- f->fmt.pix = cam->v2f.fmt.pix;
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
- f->fmt.win = cam->win;
- break;
- default:
- pr_debug(" type is invalid\n");
- retval = -EINVAL;
- }
- pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
- __func__,
- cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
- pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
- __func__,
- cam->crop_bounds.width, cam->crop_bounds.height);
- pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
- __func__,
- cam->crop_defrect.width, cam->crop_defrect.height);
- pr_debug("End of %s: crop_current widthxheight %d x %d\n",
- __func__,
- cam->crop_current.width, cam->crop_current.height);
- return retval;
- }
这个宏没什么意思,在上一个宏VIDIOC_S_FMT中,对于设置的那些信息,最终定下来了,应用程序需要调用这个VIDIOC_G_FMT宏来获取v4l2_formatfmt的信息等,这后面需要使用到这些信息。
7.VIDIOC_REQBUFS宏
- case VIDIOC_REQBUFS: {
- struct v4l2_requestbuffers *req = arg;
- pr_debug(" case VIDIOC_REQBUFS\n");
- if (req->count > FRAME_NUM) {
- pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
- "not enough buffers\n");
- req->count = FRAME_NUM;
- }
- /* 判断申请的buffer数目是否超过最大的数目。 */
- if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
- pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
- "wrong buffer type\n");
- retval = -EINVAL;
- break;
- }
- mxc_streamoff(cam);
/*这个函数同样也在这个文件中:
- static int mxc_streamoff(cam_data *cam)
- {
- int err = 0;
- pr_debug("In MVC:mxc_streamoff\n");
- if (cam->capture_on == false)
- return 0;
- /* For both CSI--MEM and CSI--IC--MEM
- * 1. wait for idmac eof
- * 2. disable csi first
- * 3. disable idmac
- * 4. disable smfc (CSI--MEM channel)
- */
- if (mxc_capture_inputs[cam->current_input].name != NULL) {
- if (cam->enc_disable_csi) {
- err = cam->enc_disable_csi(cam);
- if (err != 0)
- return err;
- }
- if (cam->enc_disable) {
- err = cam->enc_disable(cam);
- if (err != 0)
- return err;
- }
- }
- mxc_free_frames(cam);
- mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;
- cam->capture_on = false;
- return err;
- }
它通过调用cam->enc_disable_csi(cam)和cam->enc_disable(cam)这两个函数来关闭csi(这两个函数都在ipu_csi_enc.c中),然后调用mxc_free_frames(cam);函数来释放掉framebuffer的状态,主要是通过
cam->frame[i].buffer.flags= V4L2_BUF_FLAG_MAPPED;来修改flags参数。
*/
- if (req->memory & V4L2_MEMORY_MMAP) {
- mxc_free_frame_buf(cam); //释放掉内存,主要是dma_free_coherent函数。
- retval = mxc_allocate_frame_buf(cam, req->count);
- }
- break;
/*最后调用mxc_allocate_frame_buf函数来重新分配内存。函数如下:
- static int mxc_allocate_frame_buf(cam_data *cam, int count)
- {
- int i;
- pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",
- cam->v2f.fmt.pix.sizeimage);
- for (i = 0; i < count; i++) {
- cam->frame[i].vaddress =
- dma_alloc_coherent(0,
- PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
- &cam->frame[i].paddress,
- GFP_DMA | GFP_KERNEL);
- if (cam->frame[i].vaddress == 0) {
- pr_err("ERROR: v4l2 capture: "
- "mxc_allocate_frame_buf failed.\n");
- mxc_free_frame_buf(cam);
- return -ENOBUFS;
- }
- cam->frame[i].buffer.index = i;
- cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
- cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cam->frame[i].buffer.length =
- PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
- cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
- cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
- cam->frame[i].index = i;
- }
- return 0;
- }
这个函数并不是太难理解,根据应用程序传进来的想要申请的buffer数目,调用dma_alloc_coherent来分配内存,然后设置这些内存的参数等,注意此时的cam->frame[i].buffer.flags的类型是V4L2_BUF_FLAG_MAPPED。
*/
8.mmap函数:
- static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
- {
- struct video_device *dev = video_devdata(file);
- unsigned long size;
- int res = 0;
- cam_data *cam = video_get_drvdata(dev);
- pr_debug("In MVC:mxc_mmap\n");
- pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
- vma->vm_pgoff, vma->vm_start, vma->vm_end);
- /* make this _really_ smp-safe */
- if (down_interruptible(&cam->busy_lock))
- return -EINTR;
- size = vma->vm_end - vma->vm_start;
- vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
- if (remap_pfn_range(vma, vma->vm_start,
- vma->vm_pgoff, size, vma->vm_page_prot)) {
- pr_err("ERROR: v4l2 capture: mxc_mmap: "
- "remap_pfn_range failed\n");
- res = -ENOBUFS;
- goto mxc_mmap_exit;
- }
- vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
- mxc_mmap_exit:
- up(&cam->busy_lock);
- return res;
- }
9.VIDIOC_QUERYBUF宏
- case VIDIOC_QUERYBUF: {
- struct v4l2_buffer *buf = arg;
- int index = buf->index;
- pr_debug(" case VIDIOC_QUERYBUF\n");
- 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;
- }
- down(&cam->param_lock);
- if (buf->memory & V4L2_MEMORY_USERPTR) {
- mxc_v4l2_release_bufs(cam);
- retval = mxc_v4l2_prepare_bufs(cam, buf);
- }
- if (buf->memory & V4L2_MEMORY_MMAP)
- retval = mxc_v4l2_buffer_status(cam, buf);
- up(&cam->param_lock);
- break;
- }
这个函数先进行了一些判断,然后最终调用的是mxc_v4l2_buffer_status这个函数,如下:
- static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
- {
- pr_debug("In MVC:mxc_v4l2_buffer_status\n");
- if (buf->index < 0 || buf->index >= FRAME_NUM) {
- pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers "
- "not allocated\n");
- return -EINVAL;
- }
- memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
- return 0;
- }
这个函数也是进行了一些判断,然后调用memcpy函数,将上面VIDIOC_REQBUFS宏中通过mxc_allocate_frame_buf函数分配的内存拷贝给buf,返回给应用程序即可。
10.VIDIOC_QBUF宏
- case VIDIOC_QBUF: {
- struct v4l2_buffer *buf = arg;
- int index = buf->index;
- pr_debug(" case VIDIOC_QBUF\n");
- 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;
- list_add_tail(&cam->frame[index].queue,
- &cam->ready_q);
- } else if (cam->frame[index].buffer.
- flags & V4L2_BUF_FLAG_QUEUED) {
- pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
- "buffer already queued\n");
- retval = -EINVAL;
- } 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;
- }
- buf->flags = cam->frame[index].buffer.flags;
- spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
- break;
- }
这个函数主要就是根据cam->frame[index].buffer.flag的不同来选择进行不同的操作,理论上来讲,走到这一步的cam->frame[index].buffer.flag应该是V4L2_BUF_FLAG_MAPPED,那么这个VIDIOC_QBUF需要将它的flags添加一个V4L2_BUF_FLAG_QUEUED属性,然后将这个buffer添加到cam->ready_q队列中。
如果cam->frame[index].buffer.flag为V4L2_BUF_FLAG_QUEUED的话,就直接返回EINVAL。
如果cam->frame[index].buffer.flag为V4L2_BUF_FLAG_DONE的话,就先将flags的V4L2_BUF_FLAG_DONE位清零,然后重新设置为V4L2_BUF_FLAG_QUEUED,然后返回EINVAL。
11.VIDIOC_STREAMON宏
qbuf以后就可以开始传输数据了~
- case VIDIOC_STREAMON: {
- pr_debug(" case VIDIOC_STREAMON\n");
- retval = mxc_streamon(cam);
- break;
- }
- static int mxc_streamon(cam_data *cam)
- {
- struct mxc_v4l_frame *frame;
- unsigned long lock_flags;
- int err = 0;
- pr_debug("In MVC:mxc_streamon\n");
- if (NULL == cam) {
- pr_err("ERROR! cam parameter is NULL\n");
- return -1;
- }
- if (cam->capture_on) {
- pr_err("ERROR: v4l2 capture: Capture stream has been turned "
- " on\n");
- return -1;
- }
- if (list_empty(&cam->ready_q)) {
- pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been "
- "queued yet\n");
- return -EINVAL;
- }
- if (cam->enc_update_eba &&
- cam->ready_q.prev == cam->ready_q.next) {
- pr_err("ERROR: v4l2 capture: mxc_streamon buffer need "
- "ping pong at least two buffers\n");
- return -EINVAL;
- }
- /* 至少保证有2个buffer才能够传输数据。 */
- cam->capture_pid = current->pid;
- if (cam->overlay_on == true)
- stop_preview(cam);
- /* 如果打开了overlay的话,就先关闭preview. */
- if (cam->enc_enable) {
- err = cam->enc_enable(cam);
- if (err != 0)
- return err;
- }
- /* 通过调用cam->enc_enable函数使能译码任务。在mxc_v4l_open函数中,通过csi_enc_select函数或者prp_enc_select函数来为cam_data结构体里面的这几个函数指针
- * 赋值了。具体的操作会跳转到ipu_prp_enc.c或者ipu_csi_enc.c文件中去执行,具体后面再分析。 */
- spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
- cam->ping_pong_csi = 0;
- cam->local_buf_num = 0;
- if (cam->enc_update_eba) {
- frame =
- list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
- list_del(cam->ready_q.next);
- list_add_tail(&frame->queue, &cam->working_q);
- frame->ipu_buf_num = cam->ping_pong_csi;
- err = cam->enc_update_eba(cam, frame->buffer.m.offset);
- frame =
- list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
- list_del(cam->ready_q.next);
- list_add_tail(&frame->queue, &cam->working_q);
- frame->ipu_buf_num = cam->ping_pong_csi;
- err |= cam->enc_update_eba(cam, frame->buffer.m.offset);
- spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
- } else {
- spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
- return -EINVAL;
- }
- /* 对这两个buffer进行操作,首先根据 cam->ready_q.next找到包含它的frame,然后删除 cam->ready_q.next队列中的最后一项,将frame添加到 cam->working_q队列中,
- * 然后调用 cam->enc_update_eba函数来更新buffer的地址。在这里有两个参数ping_pong_csi和local_buf_num,对于这个buffer更新的过程,在后面的《应用程序和驱动程序
- * 中buffer的传输流程》文件中详细分析。个人感觉,这个buffer地址的更新过程是一个重点。 */
- if (cam->overlay_on == true)
- start_preview(cam);
- /* 如果打开了overlay的话,继续开启preview。 */
- if (cam->enc_enable_csi) {
- err = cam->enc_enable_csi(cam);
- if (err != 0)
- return err;
- }
- /* 调用cam->enc_enable_csi函数使能csi。 */
- cam->capture_on = true;
- return err;
- }
12.VIDIOC_DQBUF宏
当开始数据传输以后,当有数据填充满一个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;
- }
- static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
- {
- int retval = 0;
- struct mxc_v4l_frame *frame;
- unsigned long lock_flags;
- pr_debug("In MVC:mxc_v4l_dqueue\n");
- if (!wait_event_interruptible_timeout(cam->enc_queue,
- cam->enc_counter != 0,
- 10 * HZ)) {
- pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
- "enc_counter %x\n",
- cam->enc_counter);
- return -ETIME;
- } else if (signal_pending(current)) {
- pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
- "interrupt received\n");
- return -ERESTARTSYS;
- }
- /* 等待 cam->enc_queue这个队列唤醒,当有填充满的buffer的时候,就会产生一个中断,然后在中断处理函数camera_callback函数中,唤醒这个队列。 */
- if (down_interruptible(&cam->busy_lock))
- return -EBUSY;
- spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);
- cam->enc_counter--;
- /* 每次执行一次这个函数,enc_queue队列中的buffer数目就会减少1,这个引用计数就减少1. */
- frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
- list_del(cam->done_q.next);
- /* 首先从 cam->done_q队列中取出 queue所对应的frame,然后将cam->done_q队列的最后一项删除。 */
- if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
- frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
- } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
- pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
- "Buffer not filled.\n");
- frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
- retval = -EINVAL;
- } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
- pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
- "Buffer not queued.\n");
- retval = -EINVAL;
- }
- /* 然后根据buffer的flags参数来选择执行的步骤,(但是在这有点疑问, V4L2_BUF_FLAG_DONE不知的在什么时候设置的,这的意思是buffer到这的时候,flags应该
- * 是 V4L2_BUF_FLAG_DONE,但是不知的是在哪设置的。对于这个问题,ipu采用的方法是,申请一个中断,当填充满一个buffer的时候,就会触发中断,在中断处理
- * 函数camera_callback中处理这些事情。这个流程在后面的buf流程中会详细讲解,这里先标记一下,毕竟在讲ioctl函数~)如果flags参数是 V4L2_BUF_FLAG_DONE的
- * 话,就将它清零,如果是 V4L2_BUF_FLAG_QUEUED的话,就说明buffer没有被填充,如果是 V4L2_BUF_FLAG_MAPPED的话,说明buffer没有被qbuf。 */
- cam->frame[frame->index].buffer.field = cam->device_type ?
- V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
- buf->bytesused = cam->v2f.fmt.pix.sizeimage;
- buf->index = frame->index;
- buf->flags = frame->buffer.flags;
- buf->m = cam->frame[frame->index].buffer.m;
- buf->timestamp = cam->frame[frame->index].buffer.timestamp;
- buf->field = cam->frame[frame->index].buffer.field;
- spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);
- up(&cam->busy_lock);
- return retval;
- }
- /* 将bytesused, index等等信息记录在buf里面,这个buf能够返回给应用程序。重要的是buf->m这个结构体,里面包含应用程序所需要的buffer的物理地址,应用程序
- 去那里取摄像头采集到的数据。 */
13.VIDIOC_STREAMOFF宏
- case VIDIOC_STREAMOFF: {
- pr_debug(" case VIDIOC_STREAMOFF\n");
- retval = mxc_streamoff(cam);
- break;
- }
- static int mxc_streamoff(cam_data *cam)
- {
- int err = 0;
- pr_debug("In MVC:mxc_streamoff\n");
- if (cam->capture_on == false)
- return 0;
- /* For both CSI--MEM and CSI--IC--MEM
- * 1. wait for idmac eof
- * 2. disable csi first
- * 3. disable idmac
- * 4. disable smfc (CSI--MEM channel)
- */
- if (mxc_capture_inputs[cam->current_input].name != NULL) {
- if (cam->enc_disable_csi) {
- err = cam->enc_disable_csi(cam);
- if (err != 0)
- return err;
- }
- if (cam->enc_disable) {
- err = cam->enc_disable(cam);
- if (err != 0)
- return err;
- }
- }
- mxc_free_frames(cam);
- mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;
- cam->capture_on = false;
- return err;
- }
看注释写的很清楚,先等待idmac结束,然后先关掉csi,然关闭idmac,再关掉CSI--MEMchannel,关闭csi通过enc_disable_csi函数来实现的,其他3项任务是通过enc_disable函数里面调用的csi_enc_disabling_tasks函数来完成的。最后调用mxc_free_frames函数来清除掉frame的状态参数。