3.6 mxc_v4l_ioctl函数分析

下面分析mxc_v4l_ioctl函数:

[cpp]  view plain  copy
  1. static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,   
  2.              unsigned long arg)   
  3. {   
  4.     pr_debug("In MVC:mxc_v4l_ioctl\n");   
  5.     return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);   
  6. }  

它通过调用video_usercopy函数,最终就调用到这个mxc_v4l_do_ioctl函数,这个mxc_v4l_do_ioctl函数内部包含一个switch语句,根据传入不同的ioctl宏来选择执行不同的语句。下面就按不同的宏来分析。


我们根据一般摄像头应用程序的执行调用过程来顺序分析。

1.VIDIOC_QUERYCAP

[cpp]  view plain  copy
  1. case VIDIOC_QUERYCAP: {   
  2.         struct v4l2_capability *cap = arg;   
  3.         pr_debug("   case VIDIOC_QUERYCAP\n");   
  4.         strcpy(cap->driver, "mxc_v4l2");   
  5.         cap->version = KERNEL_VERSION(0, 1, 11);   
  6.         cap-> = V4L2_CAP_VIDEO_CAPTURE |   
  7.                     V4L2_CAP_VIDEO_OVERLAY |   
  8.                     V4L2_CAP_STREAMING |   
  9.                     V4L2_CAP_READWRITE;   
  10.         cap->card[0] = '\0';   
  11.         cap->bus_info[0] = '\0';   
  12.         break;   
  13.     }  

它的目的很简单,只是简单问问“你是谁?你能干什么?”基本就是驱动来填充应用程序传入的v4l2_capability结构体cap,设置其中的一些参数,然后应用程序就可以使用这些参数了。包括设置名字为”mxc_v4l2”capabilities属性包括V4L2_CAP_VIDEO_CAPTURE

V4L2_CAP_VIDEO_OVERLAYV4L2_CAP_STREAMINGV4L2_CAP_READWRITE等等。


2.VIDIOC_S_INPUT

[cpp]  view plain  copy
  1. case VIDIOC_S_INPUT: {   
  2.         int *index = arg;   
  3.         pr_debug("   case VIDIOC_S_INPUT\n");   
  4.         if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {   
  5.             retval = -EINVAL;   
  6.             break;   
  7.         }   

/*根据从从应用程序中传过来的arg参数来判断。MXC_V4L2_CAPTURE_NUM_INPUTS==2*/

[cpp]  view plain  copy
  1. if (*index == cam->current_input)   
  2.     break;   

/*init_camera_struct函数中将cam->current_input初始化为0了。如果要设置的input与当前input相同的话,就直接退出即可。*/

[cpp]  view plain  copy
  1. if ((mxc_capture_inputs[cam->current_input].status &   
  2.     V4L2_IN_ST_NO_POWER) == 0) {   
  3.     retval = mxc_streamoff(cam);   
  4.     if (retval)   
  5.         break;   
  6.     mxc_capture_inputs[cam->current_input].status |=   
  7.                     V4L2_IN_ST_NO_POWER;   
  8. }   

/* mxc_capture_inputs[]这个数组如下所示:

[cpp]  view plain  copy
  1. static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {   
  2.     {   
  3.      .index = 0,   
  4.      .name = "CSI IC MEM",   
  5.      .type = V4L2_INPUT_TYPE_CAMERA,   
  6.      .audioset = 0,   
  7.      .tuner = 0,   
  8.      .std = V4L2_STD_UNKNOWN,   
  9.      .status = 0,   
  10.      },   
  11.     {   
  12.      .index = 1,   
  13.      .name = "CSI MEM",   
  14.      .type = V4L2_INPUT_TYPE_CAMERA,   
  15.      .audioset = 0,   
  16.      .tuner = 0,   
  17.      .std = V4L2_STD_UNKNOWN,   
  18.      .status = V4L2_IN_ST_NO_POWER,   
  19.      },   
  20. };  

可以看出mxc_capture_inputs[0].status= 0,所以下面的语句不会执行。

*/

[cpp]  view plain  copy
  1.         if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) {   
  2. #if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)   
  3.             retval = csi_enc_select(cam);   
  4.             if (retval)   
  5.                 break;   
  6. #endif   
  7.         } else if (strcmp(mxc_capture_inputs[*index].name,   
  8.                   "CSI IC MEM") == 0) {   
  9. #if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)   
  10.             retval = prp_enc_select(cam);   
  11.             if (retval)   
  12.                 break;   
  13. #endif   
  14.         }   

/*会根据mxc_capture_inputs[*index].name来选择执行哪一个函数,之前分析过,这两个函数都是去填充cam_data结构体中的几个函数指针。*/

[cpp]  view plain  copy
  1. mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;   
  2. cam->current_input = *index;   

/*mxc_capture_inputs[*index].status位清0,然后将cam->current_input指向当前的mxc_capture_inputs[*index]*/

[html]  view plain  copy
  1. break;   

3. VIDIOC_CROPCAP

[cpp]  view plain  copy
  1. case VIDIOC_CROPCAP: {   
  2.         struct v4l2_cropcap *cap = arg;   
  3.         pr_debug("   case VIDIOC_CROPCAP\n");   
  4.         if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&   
  5.             cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {   
  6.             retval = -EINVAL;   
  7.             break;   
  8.         }   

/*判断传入的v4l2_cropcap*captype是否是V4L2_BUF_TYPE_VIDEO_CAPTUREV4L2_BUF_TYPE_VIDEO_OVERLAY,应用程序中设置成了V4L2_BUF_TYPE_VIDEO_CAPTURE*/

[cpp]  view plain  copy
  1. cap->bounds = cam->crop_bounds;   
  2. cap->defrect = cam->crop_defrect;   

/*将传入的v4l2_cropcap*cap中的boundsdefrect设置成cam->crop_boundscam->crop_defrect,这两个变量是在mxc_v4l_open函数中进行设置的。*/

[cpp]  view plain  copy
  1. break;   


4. VIDIOC_S_PARM 

[cpp]  view plain  copy
  1. case VIDIOC_S_PARM:  {   
  2.         struct v4l2_streamparm *parm = arg;   
  3.         pr_debug("   case VIDIOC_S_PARM\n");   
  4.         if (cam->sensor)   
  5.             retval = mxc_v4l2_s_param(cam, parm);   
  6.         else {   
  7.             pr_err("ERROR: v4l2 capture: slave not found!\n");   
  8.             retval = -ENODEV;   
  9.         }   
  10.         break;   
  11.     }  

这个函数跳转到mxc_v4l2_s_param中去执行了。

[cpp]  view plain  copy
  1. static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)   
  2. {   
  3.     struct v4l2_ifparm ifparm;   
  4.     struct v4l2_format cam_fmt;   
  5.     struct v4l2_streamparm currentparm;   
  6.     ipu_csi_signal_cfg_t csi_param;   
  7.     u32 current_fps, parm_fps;   
  8.     int err = 0;   
  9.   
  10.     pr_debug("In mxc_v4l2_s_param\n");   
  11.   
  12.     if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {   
  13.         pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");   
  14.         return -EINVAL;   
  15.     }   

/*判断传入的参数parm->type是否为V4L2_BUF_TYPE_VIDEO_CAPTURE,所以肯定需要在应用程序中设置这一项。*/

[cpp]  view plain  copy
  1. /* Stop the viewfinder */   
  2. if (cam->overlay_on == true)   
  3.     stop_preview(cam);   

/*这一项在init_camera_struct函数初始化的时候设置为了false*/

[cpp]  view plain  copy
  1. currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  2.   
  3. /* First check that this device can support the changes requested. */   
  4. err = vidioc_int_g_parm(cam->sensor, ¤tparm);   
  5. if (err) {   
  6.     pr_err("%s: vidioc_int_g_parm returned an error %d\n",   
  7.         __func__, err);   
  8.     goto exit;   
  9. }   

/*vidioc_int_g_parm 函数,以ov5640.c为例,它最终会调用到里面的ioctl_g_parm函数。

[cpp]  view plain  copy
  1. static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)   
  2. {   
  3.     struct sensor_data *sensor = s->priv;   
  4.     struct v4l2_captureparm *cparm = &a->parm.capture;   
  5.     int ret = 0;   
  6.   
  7.     switch (a->type) {   
  8.     /* This is the only case currently handled. */   
  9.     case V4L2_BUF_TYPE_VIDEO_CAPTURE:   
  10.         memset(a, 0, sizeof(*a));   
  11.         a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  12.         cparm->capability = sensor->streamcap.capability;   
  13.         cparm->timeperframe = sensor->streamcap.timeperframe;   
  14.         cparm->capturemode = sensor->streamcap.capturemode;   
  15.         ret = 0;   
  16.         break;   
  17.   
  18.     /* These are all the possible cases. */   
  19.     case V4L2_BUF_TYPE_VIDEO_OUTPUT:   
  20.     case V4L2_BUF_TYPE_VIDEO_OVERLAY:   
  21.     case V4L2_BUF_TYPE_VBI_CAPTURE:   
  22.     case V4L2_BUF_TYPE_VBI_OUTPUT:   
  23.     case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:   
  24.     case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:   
  25.         ret = -EINVAL;   
  26.         break;   
  27.   
  28.     default:   
  29.         pr_debug("   type is unknown - %d\n", a->type);   
  30.         ret = -EINVAL;   
  31.         break;   
  32.     }   
  33.   
  34.     return ret;   
  35. }  

看这个函数,它最终的目的是根据cam->sensor->priv里面的值来设置currentparm->parm.capture结构体,而这个currentparm就是获取到的当前sensorparm参数。

*/

[cpp]  view plain  copy
  1. current_fps = currentparm.parm.capture.timeperframe.denominator   
  2.         / currentparm.parm.capture.timeperframe.numerator;   
  3. parm_fps = parm->parm.capture.timeperframe.denominator   
  4.         / parm->parm.capture.timeperframe.numerator;   

/*设置current_fpsparm_fps这两个值,current_fps是当前每秒传输帧数,parm_fps是参数中设置的每秒传输帧数。*/

[cpp]  view plain  copy
  1. pr_debug("   Current capabilities are %x\n",   
  2.         currentparm.parm.capture.capability);   
  3. pr_debug("   Current capturemode is %d  change to %d\n",   
  4.         currentparm.parm.capture.capturemode,   
  5.         parm->parm.capture.capturemode);   
  6. pr_debug("   Current framerate is %d  change to %d\n",   
  7.         current_fps, parm_fps);   
  8.   
  9. /* This will change any camera settings needed. */   
  10. err = vidioc_int_s_parm(cam->sensor, parm);   
  11. if (err) {   
  12.     pr_err("%s: vidioc_int_s_parm returned an error %d\n",   
  13.         __func__, err);   
  14.     goto exit;   
  15. }   

/* 这个vidioc_int_s_parm函数最终会调用到ov5640.c中的ioctl_s_parm函数。它如下所示:

[cpp]  view plain  copy
  1. static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)   
  2. {   
  3.     struct sensor_data *sensor = s->priv;   
  4.     struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;   
  5.     u32 tgt_fps;    /* target frames per secound */   
  6.     enum ov5640_frame_rate frame_rate;   
  7.     int ret = 0;   
  8.   
  9.     /* Make sure power on */   
  10.     ov5640_power_down(0);   
  11.   
  12.     switch (a->type) {   
  13.     /* This is the only case currently handled. */   
  14.     case V4L2_BUF_TYPE_VIDEO_CAPTURE:   
  15.         /* Check that the new frame rate is allowed. */   
  16.         if ((timeperframe->numerator == 0) ||   
  17.             (timeperframe->denominator == 0)) {   
  18.             timeperframe->denominator = DEFAULT_FPS;   
  19.             timeperframe->numerator = 1;   
  20.         }   
  21.   
  22.         tgt_fps = timeperframe->denominator /   
  23.               timeperframe->numerator;   
  24.   
  25.         if (tgt_fps > MAX_FPS) {   
  26.             timeperframe->denominator = MAX_FPS;   
  27.             timeperframe->numerator = 1;   
  28.         } else if (tgt_fps < MIN_FPS) {   
  29.             timeperframe->denominator = MIN_FPS;   
  30.             timeperframe->numerator = 1;   
  31.         }   
  32.   
  33.         /* Actual frame rate we use */   
  34.         tgt_fps = timeperframe->denominator /   
  35.               timeperframe->numerator;   
  36.   
  37.         if (tgt_fps == 15)   
  38.             frame_rate = ov5640_15_fps;   
  39.         else if (tgt_fps == 30)   
  40.             frame_rate = ov5640_30_fps;   
  41.         else {   
  42.             pr_err(" The camera frame rate is not supported!\n");   
  43.             return -EINVAL;   
  44.         }   
  45.   
  46.         ret = ov5640_change_mode(frame_rate,   
  47.                 a->parm.capture.capturemode);   
  48.         if (ret < 0)   
  49.             return ret;   
  50.   
  51.         sensor->streamcap.timeperframe = *timeperframe;   
  52.         sensor->streamcap.capturemode = a->parm.capture.capturemode;   
  53.   
  54.         break;   
  55.   
  56.     /* These are all the possible cases. */   
  57.     case V4L2_BUF_TYPE_VIDEO_OUTPUT:   
  58.     case V4L2_BUF_TYPE_VIDEO_OVERLAY:   
  59.     case V4L2_BUF_TYPE_VBI_CAPTURE:   
  60.     case V4L2_BUF_TYPE_VBI_OUTPUT:   
  61.     case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:   
  62.     case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:   
  63.         pr_debug("   type is not " \   
  64.             "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",   
  65.             a->type);   
  66.         ret = -EINVAL;   
  67.         break;   
  68.   
  69.     default:   
  70.         pr_debug("   type is unknown - %d\n", a->type);   
  71.         ret = -EINVAL;   
  72.         break;   
  73.     }   
  74.   
  75.     return ret;   
  76. }  

这个函数首先判断应用程序中传进来的timeperframe->numeratortimeperframe->denominator两个参数,然后计算出tgt_fps。最后通过ov5640_change_mode函数来设置ov5640摄像头里面的寄存器的值,因为应用程序想要设置的是帧率等信息,这些信息会通过V4L2框架设置到摄像头里面,因为真正采集数据的是摄像头。

*/

/*综上来看,mxc_v4l2_s_param通过vidioc_int_g_parmvidioc_int_s_parm两个函数,来获取sensorparm设置,然后修改它。*/

[cpp]  view plain  copy
  1. /* If resolution changed, need to re-program the CSI */   
  2. /* Get new values. */   
  3. vidioc_int_g_ifparm(cam->sensor, &ifparm);   

/*通过上面的函数,已经将摄像头里面的信息修改了,但是应用程序还不知道,在这里调用vidioc_int_g_ifparm函数来获取ifparm的信息,然后告诉应用程序。如果分辨率改变的话需要重新修改CSI参数。调用vidioc_int_g_ifparm来获取sensorifparm参数。*/

[cpp]  view plain  copy
  1.     csi_param.data_width = 0;   
  2.     csi_param.clk_mode = 0;   
  3.     csi_param.ext_vsync = 0;   
  4.     csi_param.Vsync_pol = 0;   
  5.     csi_param.Hsync_pol = 0;   
  6.     csi_param.pixclk_pol = 0;   
  7.     csi_param.data_pol = 0;   
  8.     csi_param.sens_clksrc = 0;   
  9.     csi_param.pack_tight = 0;   
  10.     csi_param.force_eof = 0;   
  11.     csi_param.data_en_pol = 0;   
  12.     csi_param.data_fmt = 0;   
  13.     csi_param.csi = cam->csi;   
  14.     csi_param.mclk = 0;   
  15.   
  16.     pr_debug("   clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);   
  17.     if (ifparm.u.bt656.clock_curr == 0)   
  18.         csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;   
  19.     else   
  20.         csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;   
  21.    
  22.     csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;   
  23.   
  24.     if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {   
  25.         csi_param.data_width = IPU_CSI_DATA_WIDTH_8;   
  26.     } else if (ifparm.u.bt656.mode   
  27.                 == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {   
  28.         csi_param.data_width = IPU_CSI_DATA_WIDTH_10;   
  29.     } else {   
  30.         csi_param.data_width = IPU_CSI_DATA_WIDTH_8;   
  31.     }   
  32.   
  33.     csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;   
  34.     csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;   
  35.     csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;   
  36. /* 设置csi_param的一些参数。 */  
  37.     /* if the capturemode changed, the size bounds will have changed. */   
  38.     cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  39.     vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);   
  40.     pr_debug("   g_fmt_cap returns widthxheight of input as %d x %d\n",   
  41.             cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);   
  42. /* 通过vidioc_int_g_fmt_cap函数调用到ov5640.c中的ioctl_g_fmt_cap函数,如下: 
  43. static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)  
  44.  
  45.     struct sensor_data *sensor = s->priv;  
  46.  
  47.     f->fmt.pix = sensor->pix;  
  48.  
  49.     return 0;  
  50. } 
  51. 它获取了根据cam->sensor->priv来获取cam_fmt参数,保存在cam_fmt->fmt.pix中。 
  52.  */  
  53.     csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;   
  54.   
  55.     cam->crop_bounds.top = cam->crop_bounds.left = 0;   
  56.     cam->crop_bounds.width = cam_fmt.fmt.pix.width;   
  57.     cam->crop_bounds.height = cam_fmt.fmt.pix.height;   
  58. /* 根据获得的cam_fmt参数继续设置csi_param的参数和cam->crop_bounds参数。 */  
  59.     /*  
  60.      * Set the default current cropped resolution to be the same with  
  61.      * the cropping boundary(except for tvin module).  
  62.      */   
  63.     if (cam->device_type != 1) {   
  64.         cam->crop_current.width = cam->crop_bounds.width;   
  65.         cam->crop_current.height = cam->crop_bounds.height;   
  66.     }   
  67.   
  68.     /* This essentially loses the data at the left and bottom of the image  
  69.      * giving a digital zoom image, if crop_current is less than the full  
  70.      * size of the image. */   
  71.     ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,   
  72.                 cam->crop_current.height, cam->csi);   
  73.     ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,   
  74.                    cam->crop_current.top,   
  75.                    cam->csi);   
  76.     ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,   
  77.                    cam->crop_bounds.height,   
  78.                    cam_fmt.fmt.pix.pixelformat, csi_param);   
  79.   
  80.    
  81. exit:   
  82.     if (cam->overlay_on == true)   
  83.         start_preview(cam);   
  84.   
  85.     return err;   
  86. }  

/*最后调用ipu_csi_set_window_sizeipu_csi_set_window_pos重新设置窗口的大小和位置,这两个函数都在mxc_v4l_open函数中分析了,在这就不再分析。然后继续调用ipu_csi_init_interface函数来设置底层的寄存器的值。*/


5.VIDIOC_S_FMT

[cpp]  view plain  copy
  1. case VIDIOC_S_FMT: {   
  2.         struct v4l2_format *sf = arg;   
  3.         pr_debug("   case VIDIOC_S_FMT\n");   
  4.         retval = mxc_v4l2_s_fmt(cam, sf);   
  5.         break;   
  6.     }  

它跳转到mxc_v4l2_s_fmt函数中去执行:

[cpp]  view plain  copy
  1. static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)   
  2. {   
  3.     int retval = 0;   
  4.     int size = 0;   
  5.     int bytesperline = 0;   
  6.     int *width, *height;   
  7.   
  8.     pr_debug("In MVC: mxc_v4l2_s_fmt\n");   
  9.   
  10.     switch (f->type) {  //应用程序传进来的是V4L2_BUF_TYPE_VIDEO_CAPTUR。  
  11.     case V4L2_BUF_TYPE_VIDEO_CAPTURE:   
  12.         pr_debug("   type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");   
  13.         if (!valid_mode(f->fmt.pix.pixelformat)) {   
  14.             pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format "   
  15.                    "not supported\n");   
  16.             return -EINVAL;   
  17.         }   
  18.   
  19.         /*  
  20.          * Force the capture window resolution to be crop bounds  
  21.          * for CSI MEM input mode.  
  22.          */   
  23.         if (strcmp(mxc_capture_inputs[cam->current_input].name,   
  24.                "CSI MEM") == 0) {   
  25.             f->fmt.pix.width = cam->crop_current.width;   
  26.             f->fmt.pix.height = cam->crop_current.height;   
  27.         }   
  28. /* 设置 fmt.pix.width和 fmt.pix.height, cam->crop_current.width和 cam->crop_current.height这两个参数是在上一个ioctl(VIDIOC_S_PARM)调用中赋值的。 */  
  29.         if (cam->rotation >= IPU_ROTATE_90_RIGHT) {   
  30.             height = &f->fmt.pix.width;   
  31.             width = &f->fmt.pix.height;   
  32.         } else {   
  33.             width = &f->fmt.pix.width;   
  34.             height = &f->fmt.pix.height;   
  35.         }   
  36. /* 根据 cam->rotation参数决定是否反转图像的宽度和高度。 */  
  37.         /* stride line limitation */   
  38.         *width -= *width % 8;   
  39.         *height -= *height % 8;   
  40. /* 宽度和高度都像8取整,这是对宽度和高度进行一些微调。 */  
  41.         if (*width == 0 || *height == 0) {   
  42.             pr_err("ERROR: v4l2 capture: width or height"   
  43.                 " too small.\n");   
  44.             return -EINVAL;   
  45.         }   
  46.   
  47.         if ((cam->crop_current.width / *width > 8) ||   
  48.             ((cam->crop_current.width / *width == 8) &&   
  49.              (cam->crop_current.width % *width))) {   
  50.             *width = cam->crop_current.width / 8;   
  51.             if (*width % 8)   
  52.                 *width += 8 - *width % 8;   
  53.             pr_err("ERROR: v4l2 capture: width exceeds limit "   
  54.                 "resize to %d.\n",   
  55.                    *width);   
  56.         }   
  57.   
  58.         if ((cam->crop_current.height / *height > 8) ||   
  59.             ((cam->crop_current.height / *height == 8) &&   
  60.              (cam->crop_current.height % *height))) {   
  61.             *height = cam->crop_current.height / 8;   
  62.             if (*height % 8)   
  63.                 *height += 8 - *height % 8;   
  64.             pr_err("ERROR: v4l2 capture: height exceeds limit "   
  65.                    "resize to %d.\n",   
  66.                    *height);   
  67.         }   
  68. /* 上面这两个判断应该是在反转的情况下调整宽度和高度的大小。 */  
  69.         switch (f->fmt.pix.pixelformat) {   
  70.         case V4L2_PIX_FMT_RGB565:   
  71.             size = f->fmt.pix.width * f->fmt.pix.height * 2;   
  72.             bytesperline = f->fmt.pix.width * 2;   
  73.             break;   
  74.         case V4L2_PIX_FMT_BGR24:   
  75.             size = f->fmt.pix.width * f->fmt.pix.height * 3;   
  76.             bytesperline = f->fmt.pix.width * 3;   
  77.             break;   
  78.         case V4L2_PIX_FMT_RGB24:   
  79.             size = f->fmt.pix.width * f->fmt.pix.height * 3;   
  80.             bytesperline = f->fmt.pix.width * 3;   
  81.             break;   
  82.         case V4L2_PIX_FMT_BGR32:   
  83.             size = f->fmt.pix.width * f->fmt.pix.height * 4;   
  84.             bytesperline = f->fmt.pix.width * 4;   
  85.             break;   
  86.         case V4L2_PIX_FMT_RGB32:   
  87.             size = f->fmt.pix.width * f->fmt.pix.height * 4;   
  88.             bytesperline = f->fmt.pix.width * 4;   
  89.             break;   
  90.         case V4L2_PIX_FMT_YUV422P:   
  91.             size = f->fmt.pix.width * f->fmt.pix.height * 2;   
  92.             bytesperline = f->fmt.pix.width;   
  93.             break;   
  94.         case V4L2_PIX_FMT_UYVY:   
  95.         case V4L2_PIX_FMT_YUYV:   
  96.             size = f->fmt.pix.width * f->fmt.pix.height * 2;   
  97.             bytesperline = f->fmt.pix.width * 2;   
  98.             break;   
  99.         case V4L2_PIX_FMT_YUV420:   
  100.         case V4L2_PIX_FMT_YVU420:   
  101.             size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;   
  102.             bytesperline = f->fmt.pix.width;   
  103.             break;   
  104.         case V4L2_PIX_FMT_NV12:   
  105.             size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;   
  106.             bytesperline = f->fmt.pix.width;   
  107.             break;   
  108.         default:   
  109.             break;   
  110.         }   
  111. /* 根据fmt.pix.pixelformat的格式来计算size和bytesperline的大小。这个size就是指图像的大小,bytesperline就是指一行数据所占用的字节数。 */  
  112.         if (f->fmt.pix.bytesperline < bytesperline)   
  113.             f->fmt.pix.bytesperline = bytesperline;   
  114.         else   
  115.             bytesperline = f->fmt.pix.bytesperline;   
  116.   
  117.         if (f->fmt.pix.sizeimage < size)   
  118.             f->fmt.pix.sizeimage = size;   
  119.         else   
  120.             size = f->fmt.pix.sizeimage;   
  121.   
  122.         cam->v2f.fmt.pix = f->fmt.pix;   
  123.   
  124.         if (cam->v2f.fmt.pix.priv != 0) {   
  125.             if (copy_from_user(&cam->offset,   
  126.                        (void *)cam->v2f.fmt.pix.priv,   
  127.                        sizeof(cam->offset))) {   
  128.                 retval = -EFAULT;   
  129.                 break;   
  130.             }   
  131.         }   
  132.         break;   
  133.     case V4L2_BUF_TYPE_VIDEO_OVERLAY:   
  134.         pr_debug("   type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");   
  135.         retval = verify_preview(cam, &f->fmt.win);   
  136.         cam->win = f->fmt.win;   
  137.         break;   
  138.     default:   
  139.         retval = -EINVAL;   
  140.     }   
  141.   
  142.     pr_debug("End of %s: v2f pix widthxheight %d x %d\n",   
  143.          __func__,   
  144.          cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);   
  145.     pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",   
  146.          __func__,   
  147.          cam->crop_bounds.width, cam->crop_bounds.height);   
  148.     pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",   
  149.          __func__,   
  150.          cam->crop_defrect.width, cam->crop_defrect.height);   
  151.     pr_debug("End of %s: crop_current widthxheight %d x %d\n",   
  152.          __func__,   
  153.          cam->crop_current.width, cam->crop_current.height);   
  154.   
  155.     return retval;   
  156. }  

注意上面我标红的代码:cam->v2f.fmt.pix=f->fmt.pix;在这个函数中,对于应用程序中传入的参数,或者修改的参数,到此为止,都已经设置完毕了。然后在这个函数中,对cam->v2f结构体进行赋值,这个结构体里面的cam->v2f.fmt.pix里面包含widthheightpixelformatbytesperlinesizeimage等等信息,这些信息在后面申请内存大小等函数中都会使用到。


6.VIDIOC_G_FMT

[cpp]  view plain  copy
  1. case VIDIOC_G_FMT: {   
  2.     struct v4l2_format *gf = arg;   
  3.     pr_debug("   case VIDIOC_G_FMT\n");   
  4.     retval = mxc_v4l2_g_fmt(cam, gf);   
  5.     break;   
  6. }  
[cpp]  view plain  copy
  1. static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)   
  2. {   
  3.     int retval = 0;   
  4.   
  5.     pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type);   
  6.   
  7.     switch (f->type) {   
  8.     case V4L2_BUF_TYPE_VIDEO_CAPTURE:   
  9.         pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");   
  10.         f->fmt.pix = cam->v2f.fmt.pix;   
  11.         break;   
  12.     case V4L2_BUF_TYPE_VIDEO_OVERLAY:   
  13.         pr_debug("   type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");   
  14.         f->fmt.win = cam->win;   
  15.         break;   
  16.     default:   
  17.         pr_debug("   type is invalid\n");   
  18.         retval = -EINVAL;   
  19.     }   
  20.   
  21.     pr_debug("End of %s: v2f pix widthxheight %d x %d\n",   
  22.          __func__,   
  23.          cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);   
  24.     pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",   
  25.          __func__,   
  26.          cam->crop_bounds.width, cam->crop_bounds.height);   
  27.     pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",   
  28.          __func__,   
  29.          cam->crop_defrect.width, cam->crop_defrect.height);   
  30.     pr_debug("End of %s: crop_current widthxheight %d x %d\n",   
  31.          __func__,   
  32.          cam->crop_current.width, cam->crop_current.height);   
  33.   
  34.     return retval;   
  35. }  

这个宏没什么意思,在上一个宏VIDIOC_S_FMT中,对于设置的那些信息,最终定下来了,应用程序需要调用这个VIDIOC_G_FMT宏来获取v4l2_formatfmt的信息等,这后面需要使用到这些信息。


7.VIDIOC_REQBUFS

[cpp]  view plain  copy
  1. case VIDIOC_REQBUFS: {   
  2.         struct v4l2_requestbuffers *req = arg;   
  3.         pr_debug("   case VIDIOC_REQBUFS\n");   
  4.   
  5.         if (req->count > FRAME_NUM) {   
  6.             pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "   
  7.                    "not enough buffers\n");   
  8.             req->count = FRAME_NUM;   
  9.         }   
  10. /* 判断申请的buffer数目是否超过最大的数目。 */  
  11.         if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {   
  12.             pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "   
  13.                    "wrong buffer type\n");   
  14.             retval = -EINVAL;   
  15.             break;   
  16.         }   
  17.   
  18.         mxc_streamoff(cam);   

/*这个函数同样也在这个文件中:

[cpp]  view plain  copy
  1. static int mxc_streamoff(cam_data *cam)   
  2. {   
  3.     int err = 0;   
  4.   
  5.     pr_debug("In MVC:mxc_streamoff\n");   
  6.   
  7.     if (cam->capture_on == false)   
  8.         return 0;   
  9.   
  10.     /* For both CSI--MEM and CSI--IC--MEM  
  11.      * 1. wait for idmac eof  
  12.      * 2. disable csi first  
  13.      * 3. disable idmac  
  14.      * 4. disable smfc (CSI--MEM channel)  
  15.      */   
  16.     if (mxc_capture_inputs[cam->current_input].name != NULL) {   
  17.         if (cam->enc_disable_csi) {   
  18.             err = cam->enc_disable_csi(cam);   
  19.             if (err != 0)   
  20.                 return err;   
  21.         }   
  22.         if (cam->enc_disable) {   
  23.             err = cam->enc_disable(cam);   
  24.             if (err != 0)   
  25.                 return err;   
  26.         }   
  27.     }   
  28.   
  29.     mxc_free_frames(cam);   
  30.     mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;   
  31.     cam->capture_on = false;   
  32.     return err;   
  33. }  

它通过调用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参数。

*/

[cpp]  view plain  copy
  1. if (req->memory & V4L2_MEMORY_MMAP) {   
  2.     mxc_free_frame_buf(cam); //释放掉内存,主要是dma_free_coherent函数。  
  3.     retval = mxc_allocate_frame_buf(cam, req->count);   
  4. }   
  5. break;   

/*最后调用mxc_allocate_frame_buf函数来重新分配内存。函数如下:

[cpp]  view plain  copy
  1. static int mxc_allocate_frame_buf(cam_data *cam, int count)   
  2. {   
  3.     int i;   
  4.   
  5.     pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",   
  6.         cam->v2f.fmt.pix.sizeimage);   
  7.   
  8.     for (i = 0; i < count; i++) {   
  9.         cam->frame[i].vaddress =   
  10.             dma_alloc_coherent(0,   
  11.                        PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),   
  12.                        &cam->frame[i].paddress,   
  13.                        GFP_DMA | GFP_KERNEL);   
  14.         if (cam->frame[i].vaddress == 0) {   
  15.             pr_err("ERROR: v4l2 capture: "   
  16.                 "mxc_allocate_frame_buf failed.\n");   
  17.             mxc_free_frame_buf(cam);   
  18.             return -ENOBUFS;   
  19.         }   
  20.         cam->frame[i].buffer.index = i;   
  21.         cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;   
  22.         cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  23.         cam->frame[i].buffer.length =   
  24.             PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);   
  25.         cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;   
  26.         cam->frame[i].buffer.m.offset = cam->frame[i].paddress;   
  27.         cam->frame[i].index = i;   
  28.     }   
  29.   
  30.     return 0;   
  31. }  

这个函数并不是太难理解,根据应用程序传进来的想要申请的buffer数目,调用dma_alloc_coherent来分配内存,然后设置这些内存的参数等,注意此时的cam->frame[i].buffer.flags的类型是V4L2_BUF_FLAG_MAPPED

*/


8.mmap函数:

[cpp]  view plain  copy
  1. static int mxc_mmap(struct file *file, struct vm_area_struct *vma)   
  2. {   
  3.     struct video_device *dev = video_devdata(file);   
  4.     unsigned long size;   
  5.     int res = 0;   
  6.     cam_data *cam = video_get_drvdata(dev);   
  7.   
  8.     pr_debug("In MVC:mxc_mmap\n");   
  9.     pr_debug("   pgoff=0x%lx, start=0x%lx, end=0x%lx\n",   
  10.          vma->vm_pgoff, vma->vm_start, vma->vm_end);   
  11.   
  12.     /* make this _really_ smp-safe */   
  13.     if (down_interruptible(&cam->busy_lock))   
  14.         return -EINTR;   
  15.   
  16.     size = vma->vm_end - vma->vm_start;   
  17.     vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);   
  18.   
  19.     if (remap_pfn_range(vma, vma->vm_start,   
  20.                 vma->vm_pgoff, size, vma->vm_page_prot)) {   
  21.         pr_err("ERROR: v4l2 capture: mxc_mmap: "   
  22.             "remap_pfn_range failed\n");   
  23.         res = -ENOBUFS;   
  24.         goto mxc_mmap_exit;   
  25.     }   
  26.   
  27.     vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */   
  28.   
  29. mxc_mmap_exit:   
  30.     up(&cam->busy_lock);   
  31.     return res;   
  32. }  


9.VIDIOC_QUERYBUF

[cpp]  view plain  copy
  1. case VIDIOC_QUERYBUF: {   
  2.         struct v4l2_buffer *buf = arg;   
  3.         int index = buf->index;   
  4.         pr_debug("   case VIDIOC_QUERYBUF\n");   
  5.   
  6.         if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {   
  7.             pr_err("ERROR: v4l2 capture: "   
  8.                    "VIDIOC_QUERYBUFS: "   
  9.                    "wrong buffer type\n");   
  10.             retval = -EINVAL;   
  11.             break;   
  12.         }   
  13.   
  14.         if (buf->memory & V4L2_MEMORY_MMAP) {   
  15.             memset(buf, 0, sizeof(buf));   
  16.             buf->index = index;   
  17.         }   
  18.   
  19.         down(&cam->param_lock);   
  20.         if (buf->memory & V4L2_MEMORY_USERPTR) {   
  21.             mxc_v4l2_release_bufs(cam);   
  22.             retval = mxc_v4l2_prepare_bufs(cam, buf);   
  23.         }   
  24.   
  25.         if (buf->memory & V4L2_MEMORY_MMAP)   
  26.             retval = mxc_v4l2_buffer_status(cam, buf);   
  27.         up(&cam->param_lock);   
  28.         break;   
  29.     }  

这个函数先进行了一些判断,然后最终调用的是mxc_v4l2_buffer_status这个函数,如下:

[cpp]  view plain  copy
  1. static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)   
  2. {   
  3.     pr_debug("In MVC:mxc_v4l2_buffer_status\n");   
  4.   
  5.     if (buf->index < 0 || buf->index >= FRAME_NUM) {   
  6.         pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers "   
  7.                "not allocated\n");   
  8.         return -EINVAL;   
  9.     }   
  10.   
  11.     memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));   
  12.     return 0;   
  13. }  

这个函数也是进行了一些判断,然后调用memcpy函数,将上面VIDIOC_REQBUFS宏中通过mxc_allocate_frame_buf函数分配的内存拷贝给buf,返回给应用程序即可。


10.VIDIOC_QBUF

[cpp]  view plain  copy
  1. case VIDIOC_QBUF: {   
  2.         struct v4l2_buffer *buf = arg;   
  3.         int index = buf->index;   
  4.         pr_debug("   case VIDIOC_QBUF\n");   
  5.   
  6.         spin_lock_irqsave(&cam->queue_int_lock, lock_flags);   
  7.         if ((cam->frame[index].buffer.flags & 0x7) ==   
  8.             V4L2_BUF_FLAG_MAPPED) {   
  9.             cam->frame[index].buffer.flags |=   
  10.                 V4L2_BUF_FLAG_QUEUED;   
  11.             list_add_tail(&cam->frame[index].queue,   
  12.                       &cam->ready_q);   
  13.         } else if (cam->frame[index].buffer.   
  14.                flags & V4L2_BUF_FLAG_QUEUED) {   
  15.             pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "   
  16.                    "buffer already queued\n");   
  17.             retval = -EINVAL;   
  18.         } else if (cam->frame[index].buffer.   
  19.                flags & V4L2_BUF_FLAG_DONE) {   
  20.             pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "   
  21.                    "overwrite done buffer.\n");   
  22.             cam->frame[index].buffer.flags &=   
  23.                 ~V4L2_BUF_FLAG_DONE;   
  24.             cam->frame[index].buffer.flags |=   
  25.                 V4L2_BUF_FLAG_QUEUED;   
  26.             retval = -EINVAL;   
  27.         }   
  28.   
  29.         buf->flags = cam->frame[index].buffer.flags;   
  30.         spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);   
  31.         break;   
  32.     }  

这个函数主要就是根据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.flagV4L2_BUF_FLAG_QUEUED的话,就直接返回EINVAL

如果cam->frame[index].buffer.flagV4L2_BUF_FLAG_DONE的话,就先将flagsV4L2_BUF_FLAG_DONE位清零,然后重新设置为V4L2_BUF_FLAG_QUEUED,然后返回EINVAL


11.VIDIOC_STREAMON

qbuf以后就可以开始传输数据了~

[cpp]  view plain  copy
  1. case VIDIOC_STREAMON: {   
  2.         pr_debug("   case VIDIOC_STREAMON\n");   
  3.         retval = mxc_streamon(cam);   
  4.         break;   
  5.     }  

[cpp]  view plain  copy
  1. static int mxc_streamon(cam_data *cam)   
  2. {   
  3.     struct mxc_v4l_frame *frame;   
  4.     unsigned long lock_flags;   
  5.     int err = 0;   
  6.   
  7.     pr_debug("In MVC:mxc_streamon\n");   
  8.   
  9.     if (NULL == cam) {   
  10.         pr_err("ERROR! cam parameter is NULL\n");   
  11.         return -1;   
  12.     }   
  13.   
  14.     if (cam->capture_on) {   
  15.         pr_err("ERROR: v4l2 capture: Capture stream has been turned "   
  16.                " on\n");   
  17.         return -1;   
  18.     }   
  19.   
  20.     if (list_empty(&cam->ready_q)) {   
  21.         pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been "   
  22.             "queued yet\n");   
  23.         return -EINVAL;   
  24.     }   
  25.     if (cam->enc_update_eba &&   
  26.         cam->ready_q.prev == cam->ready_q.next) {   
  27.         pr_err("ERROR: v4l2 capture: mxc_streamon buffer need "   
  28.                "ping pong at least two buffers\n");   
  29.         return -EINVAL;   
  30.     }   
  31. /* 至少保证有2个buffer才能够传输数据。 */  
  32.     cam->capture_pid = current->pid;   
  33.   
  34.     if (cam->overlay_on == true)   
  35.         stop_preview(cam);   
  36. /* 如果打开了overlay的话,就先关闭preview. */  
  37.     if (cam->enc_enable) {   
  38.         err = cam->enc_enable(cam);   
  39.         if (err != 0)   
  40.             return err;   
  41.     }   
  42. /* 通过调用cam->enc_enable函数使能译码任务。在mxc_v4l_open函数中,通过csi_enc_select函数或者prp_enc_select函数来为cam_data结构体里面的这几个函数指针 
  43.  * 赋值了。具体的操作会跳转到ipu_prp_enc.c或者ipu_csi_enc.c文件中去执行,具体后面再分析。 */  
  44.     spin_lock_irqsave(&cam->queue_int_lock, lock_flags);   
  45.     cam->ping_pong_csi = 0;   
  46.     cam->local_buf_num = 0;   
  47.     if (cam->enc_update_eba) {   
  48.         frame =   
  49.             list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);   
  50.         list_del(cam->ready_q.next);   
  51.         list_add_tail(&frame->queue, &cam->working_q);   
  52.         frame->ipu_buf_num = cam->ping_pong_csi;   
  53.         err = cam->enc_update_eba(cam, frame->buffer.m.offset);   
  54.   
  55.         frame =   
  56.             list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);   
  57.         list_del(cam->ready_q.next);   
  58.         list_add_tail(&frame->queue, &cam->working_q);   
  59.         frame->ipu_buf_num = cam->ping_pong_csi;   
  60.         err |= cam->enc_update_eba(cam, frame->buffer.m.offset);   
  61.         spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);   
  62.     } else {   
  63.         spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);   
  64.         return -EINVAL;   
  65.     }   
  66. /* 对这两个buffer进行操作,首先根据 cam->ready_q.next找到包含它的frame,然后删除 cam->ready_q.next队列中的最后一项,将frame添加到 cam->working_q队列中, 
  67.  * 然后调用 cam->enc_update_eba函数来更新buffer的地址。在这里有两个参数ping_pong_csi和local_buf_num,对于这个buffer更新的过程,在后面的《应用程序和驱动程序 
  68.  * 中buffer的传输流程》文件中详细分析。个人感觉,这个buffer地址的更新过程是一个重点。 */  
  69.     if (cam->overlay_on == true)   
  70.         start_preview(cam);   
  71. /* 如果打开了overlay的话,继续开启preview。 */  
  72.     if (cam->enc_enable_csi) {   
  73.         err = cam->enc_enable_csi(cam);   
  74.         if (err != 0)   
  75.             return err;   
  76.     }   
  77. /* 调用cam->enc_enable_csi函数使能csi。 */  
  78.     cam->capture_on = true;   
  79.   
  80.     return err;   
  81. }  

12.VIDIOC_DQBUF

当开始数据传输以后,当有数据填充满一个buffer,就可以将这个buffer出队了,应用程序会调用到VIDIOC_DQBUF这个ioctl函数:

[cpp]  view plain  copy
  1. case VIDIOC_DQBUF: {   
  2.         struct v4l2_buffer *buf = arg;   
  3.         pr_debug("   case VIDIOC_DQBUF\n");   
  4.   
  5.         if ((cam->enc_counter == 0) &&   
  6.             (file->f_flags & O_NONBLOCK)) {   
  7.             retval = -EAGAIN;   
  8.             break;   
  9.         }   
  10.   
  11.         retval = mxc_v4l_dqueue(cam, buf);   
  12.         break;   
  13.     }  
[html]  view plain  copy
  1. static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)   
  2. {   
  3.     int retval = 0;   
  4.     struct mxc_v4l_frame *frame;   
  5.     unsigned long lock_flags;   
  6.   
  7.     pr_debug("In MVC:mxc_v4l_dqueue\n");   
  8.   
  9.     if (!wait_event_interruptible_timeout(cam->enc_queue,   
  10.                           cam->enc_counter != 0,   
  11.                           10 * HZ)) {   
  12.         pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "   
  13.             "enc_counter %x\n",   
  14.                cam->enc_counter);   
  15.         return -ETIME;   
  16.     } else if (signal_pending(current)) {   
  17.         pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "   
  18.             "interrupt received\n");   
  19.         return -ERESTARTSYS;   
  20.     }   
  21. /* 等待 cam->enc_queue这个队列唤醒,当有填充满的buffer的时候,就会产生一个中断,然后在中断处理函数camera_callback函数中,唤醒这个队列。 */  
  22.   
  23.     if (down_interruptible(&cam->busy_lock))   
  24.         return -EBUSY;   
  25.   
  26.     spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);   
  27.     cam->enc_counter--;   
  28. /* 每次执行一次这个函数,enc_queue队列中的buffer数目就会减少1,这个引用计数就减少1. */  
  29.     frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);   
  30.     list_del(cam->done_q.next);   
  31. /* 首先从 cam->done_q队列中取出 queue所对应的frame,然后将cam->done_q队列的最后一项删除。 */  
  32.     if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {   
  33.         frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;   
  34.     } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {   
  35.         pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "   
  36.             "Buffer not filled.\n");   
  37.         frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;   
  38.         retval = -EINVAL;   
  39.     } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {   
  40.         pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "   
  41.             "Buffer not queued.\n");   
  42.         retval = -EINVAL;   
  43.     }   
  44. /* 然后根据buffer的flags参数来选择执行的步骤,(但是在这有点疑问, V4L2_BUF_FLAG_DONE不知的在什么时候设置的,这的意思是buffer到这的时候,flags应该  
  45.  * 是 V4L2_BUF_FLAG_DONE,但是不知的是在哪设置的。对于这个问题,ipu采用的方法是,申请一个中断,当填充满一个buffer的时候,就会触发中断,在中断处理  
  46.  * 函数camera_callback中处理这些事情。这个流程在后面的buf流程中会详细讲解,这里先标记一下,毕竟在讲ioctl函数~)如果flags参数是 V4L2_BUF_FLAG_DONE的  
  47.  * 话,就将它清零,如果是 V4L2_BUF_FLAG_QUEUED的话,就说明buffer没有被填充,如果是 V4L2_BUF_FLAG_MAPPED的话,说明buffer没有被qbuf。 */  
  48.     cam->frame[frame->index].buffer.field = cam->device_type ?   
  49.                 V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;   
  50.   
  51.     buf->bytesused = cam->v2f.fmt.pix.sizeimage;   
  52.     buf->index = frame->index;   
  53.     buf->flags = frame->buffer.flags;   
  54.     buf->m = cam->frame[frame->index].buffer.m;   
  55.     buf->timestamp = cam->frame[frame->index].buffer.timestamp;   
  56.     buf->field = cam->frame[frame->index].buffer.field;   
  57.     spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);   
  58.   
  59.     up(&cam->busy_lock);   
  60.     return retval;   
  61. }  
  62. /* 将bytesused, index等等信息记录在buf里面,这个buf能够返回给应用程序。重要的是buf->m这个结构体,里面包含应用程序所需要的buffer的物理地址,应用程序  
  63. 去那里取摄像头采集到的数据。 */  

13.VIDIOC_STREAMOFF

[cpp]  view plain  copy
  1. case VIDIOC_STREAMOFF: {   
  2.     pr_debug("   case VIDIOC_STREAMOFF\n");   
  3.     retval = mxc_streamoff(cam);   
  4.     break;   
  5. }  
[cpp]  view plain  copy
  1. static int mxc_streamoff(cam_data *cam)   
  2. {   
  3.     int err = 0;   
  4.   
  5.     pr_debug("In MVC:mxc_streamoff\n");   
  6.   
  7.     if (cam->capture_on == false)   
  8.         return 0;   
  9.   
  10.     /* For both CSI--MEM and CSI--IC--MEM  
  11.      * 1. wait for idmac eof  
  12.      * 2. disable csi first  
  13.      * 3. disable idmac  
  14.      * 4. disable smfc (CSI--MEM channel)  
  15.      */   
  16.     if (mxc_capture_inputs[cam->current_input].name != NULL) {   
  17.         if (cam->enc_disable_csi) {   
  18.             err = cam->enc_disable_csi(cam);   
  19.             if (err != 0)   
  20.                 return err;   
  21.         }   
  22.         if (cam->enc_disable) {   
  23.             err = cam->enc_disable(cam);   
  24.             if (err != 0)   
  25.                 return err;   
  26.         }   
  27.     }   
  28.   
  29.     mxc_free_frames(cam);   
  30.     mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;   
  31.     cam->capture_on = false;   
  32.     return err;   
  33. }  

看注释写的很清楚,先等待idmac结束,然后先关掉csi,然关闭idmac,再关掉CSI--MEMchannel,关闭csi通过enc_disable_csi函数来实现的,其他3项任务是通过enc_disable函数里面调用的csi_enc_disabling_tasks函数来完成的。最后调用mxc_free_frames函数来清除掉frame的状态参数。


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yanbixing123/article/details/52291745

猜你喜欢

转载自blog.csdn.net/dragon101788/article/details/80660897
3.6