3.1 mxc_v4l2_capture.c分析---probe函数分析

mxc_v4l2_capture.c函数提供了应用程序中的一些接口函数,所以从分析它开始:

(一)看一个驱动程序从它的入口函数开始:

[cpp]  view plain  copy
  1. module_init(camera_init);  
  2.   
  3. static __init int camera_init(void)   
  4. {   
  5.     u8 err = 0;   
  6.   
  7.     pr_debug("In MVC:camera_init\n");   
  8.   
  9.     /* Register the device driver structure. */   
  10.     err = platform_driver_register(&mxc_v4l2_driver);   
  11.     if (err != 0) {   
  12.         pr_err("ERROR: v4l2 capture:camera_init: "   
  13.             "platform_driver_register failed.\n");   
  14.         return err;   
  15.     }   
  16.   
  17.     return err;   
  18. }  

它里面就一个重要的函数,platform_driver_register(&mxc_v4l2_driver);通过调用这个函数,将mxc_v4l2_driver这个驱动注册到platform总线上面,然后当总线中有对应的设备的时候,就会调用这个mxc_v4l2_driver结构体里面的的probe函数mxc_v4l2_probemxc_v4l2_driver结构体如下所示:

[cpp]  view plain  copy
  1. static struct platform_driver mxc_v4l2_driver = {   
  2.     .driver = {   
  3.            .name = "mxc_v4l2_capture",   
  4.            .owner = THIS_MODULE,   
  5.            .of_match_table = mxc_v4l2_dt_ids,   
  6.            },   
  7.     .id_table = imx_v4l2_devtype,   
  8.     .probe = mxc_v4l2_probe,   
  9.     .remove = mxc_v4l2_remove,   
  10.     .suspend = mxc_v4l2_suspend,   
  11.     .resume = mxc_v4l2_resume,   
  12.     .shutdown = NULL,   
  13. };  


(二) mxc_v4l2_probe函数

[cpp]  view plain  copy
  1. static int mxc_v4l2_probe(struct platform_device *pdev)   
  2. {   
  3.     /* Create cam and initialize it. */   
  4.     cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL);   
  5.     if (cam == NULL) {   
  6.         pr_err("ERROR: v4l2 capture: failed to register camera\n");   
  7.         return -1;   
  8.     }   
  9.   
  10.     init_camera_struct(cam, pdev);   
  11.     pdev->dev.release = camera_platform_release;   
  12.   
  13.     /* Set up the v4l2 device and register it*/   
  14.     cam->self->priv = cam;   
  15.     v4l2_int_device_register(cam->self);   
  16.    
  17.     /* register v4l video device */   
  18.     if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)   
  19.         < 0) {   
  20.         kfree(cam);   
  21.         cam = NULL;   
  22.         pr_err("ERROR: v4l2 capture: video_register_device failed\n");   
  23.         return -1;   
  24.     }   
  25.     pr_debug("   Video device registered: %s #%d\n",   
  26.          cam->video_dev->name, cam->video_dev->minor);   
  27.   
  28.     if (device_create_file(&cam->video_dev->dev,   
  29.             &dev_attr_fsl_v4l2_capture_property))   
  30.         dev_err(&pdev->dev, "Error on creating sysfs file"   
  31.             " for capture\n");   
  32.   
  33.     if (device_create_file(&cam->video_dev->dev,   
  34.             &dev_attr_fsl_v4l2_overlay_property))   
  35.         dev_err(&pdev->dev, "Error on creating sysfs file"   
  36.             " for overlay\n");   
  37.   
  38.     if (device_create_file(&cam->video_dev->dev,   
  39.             &dev_attr_fsl_csi_property))   
  40.         dev_err(&pdev->dev, "Error on creating sysfs file"   
  41.             " for csi number\n");   
  42.   
  43.     return 0;   
  44. }  

这个函数首先为cam_data*cam分配内存,然后就调用init_camera_struct(cam,pdev);函数来对cam结构体进行初始化。


2.1 init_camera_struct函数

[cpp]  view plain  copy
  1. static int init_camera_struct(cam_data *cam, struct platform_device *pdev)   
  2. {   
  3.     const struct of_device_id *of_id =   
  4.             of_match_device(mxc_v4l2_dt_ids, &pdev->dev);   
  5.     struct device_node *np = pdev->dev.of_node;   
  6.     int ipu_id, csi_id, mclk_source;   
  7.     int ret = 0;   
  8.     struct v4l2_device *v4l2_dev;   
  9.   
  10.     pr_debug("In MVC: init_camera_struct\n");   
  11.   
  12.     ret = of_property_read_u32(np, "ipu_id", &ipu_id);   
  13.     if (ret) {   
  14.         dev_err(&pdev->dev, "ipu_id missing or invalid\n");   
  15.         return ret;   
  16.     }   
  17.   
  18.     ret = of_property_read_u32(np, "csi_id", &csi_id);   
  19.     if (ret) {   
  20.         dev_err(&pdev->dev, "csi_id missing or invalid\n");   
  21.         return ret;   
  22.     }   
  23.   
  24.     ret = of_property_read_u32(np, "mclk_source", &mclk_source);   
  25.     if (ret) {   
  26.         dev_err(&pdev->dev, "sensor mclk missing or invalid\n");   
  27.         return ret;   
  28.     }   
  29.   
  30.     /* Default everything to 0 */   
  31.     memset(cam, 0, sizeof(cam_data));   
  32.   
  33.     /* get devtype to distinguish if the cpu is imx5 or imx6  
  34.      * IMX5_V4L2 specify the cpu is imx5  
  35.      * IMX6_V4L2 specify the cpu is imx6q or imx6sdl  
  36.      */   
  37.     if (of_id)   
  38.         pdev->id_entry = of_id->data;   
  39.     cam->devtype = pdev->id_entry->driver_data;   
  40.   
  41.     cam->ipu = ipu_get_soc(ipu_id);   
  42.     if (cam->ipu == NULL) {   
  43.         pr_err("ERROR: v4l2 capture: failed to get ipu\n");   
  44.         return -EINVAL;   
  45.     } else if (cam->ipu == ERR_PTR(-ENODEV)) {   
  46.         pr_err("ERROR: v4l2 capture: get invalid ipu\n");   
  47.         return -ENODEV;   
  48.     }   
  49.   
  50.     init_MUTEX(&cam->param_lock);   
  51.     init_MUTEX(&cam->busy_lock);   
  52.   
  53.     cam->video_dev = video_device_alloc();   
  54.     if (cam->video_dev == NULL)   
  55.         return -ENODEV;   
  56.   
  57.     *(cam->video_dev) = mxc_v4l_template;   
  58.   
  59.     video_set_drvdata(cam->video_dev, cam);   
  60.     dev_set_drvdata(&pdev->dev, (void *)cam);   
  61.     cam->video_dev->minor = -1;   
  62.   
  63.     v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);   
  64.     if (!v4l2_dev) {   
  65.         dev_err(&pdev->dev, "failed to allocate v4l2_dev structure\n");   
  66.         video_device_release(cam->video_dev);   
  67.         return -ENOMEM;   
  68.     }   
  69.   
  70.     if (v4l2_device_register(&pdev->dev, v4l2_dev) < 0) {   
  71.         dev_err(&pdev->dev, "register v4l2 device failed\n");   
  72.         video_device_release(cam->video_dev);   
  73.         kfree(v4l2_dev);   
  74.         return -ENODEV;   
  75.     }   
  76.     cam->video_dev->v4l2_dev = v4l2_dev;   
  77.   
  78.     init_waitqueue_head(&cam->enc_queue);   
  79.     init_waitqueue_head(&cam->still_queue);   
  80.   
  81.     /* setup cropping */   
  82.     cam->crop_bounds.left = 0;   
  83.     cam->crop_bounds.width = 640;   
  84.     cam->crop_bounds.top = 0;   
  85.     cam->crop_bounds.height = 480;   
  86.     cam->crop_current = cam->crop_defrect = cam->crop_bounds;   
  87.     ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,   
  88.                 cam->crop_current.height, cam->csi);   
  89.     ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,   
  90.                 cam->crop_current.top, cam->csi);   
  91.     cam->streamparm.parm.capture.capturemode = 0;   
  92.   
  93.     cam->standard.index = 0;   
  94.     cam->standard.id = V4L2_STD_UNKNOWN;   
  95.     cam->standard.frameperiod.denominator = 30;   
  96.     cam->standard.frameperiod.numerator = 1;   
  97.     cam->standard.framelines = 480;   
  98.     cam->standard_autodetect = true;   
  99.     cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  100.     cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;   
  101.     cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;   
  102.     cam->overlay_on = false;   
  103.     cam->capture_on = false;   
  104.     cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;   
  105.   
  106.     cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;   
  107.     cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;   
  108.     cam->v2f.fmt.pix.width = 288;   
  109.     cam->v2f.fmt.pix.height = 352;   
  110.     cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;   
  111.     cam->win.w.width = 160;   
  112.     cam->win.w.height = 160;   
  113.     cam->win.w.left = 0;   
  114.     cam->win.w.top = 0;   
  115.   
  116.     cam->ipu_id = ipu_id;   
  117.     cam->csi = csi_id;   
  118.     cam->mclk_source = mclk_source;   
  119.     cam->mclk_on[cam->mclk_source] = false;   
  120.   
  121.     cam->enc_callback = camera_callback;   
  122.     init_waitqueue_head(&cam->power_queue);   
  123.     spin_lock_init(&cam->queue_int_lock);   
  124.     spin_lock_init(&cam->dqueue_int_lock);   
  125.   
  126.     cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);   
  127.     cam->self->module = THIS_MODULE;   
  128.     sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);   
  129.     cam->self->type = v4l2_int_type_master;   
  130.     cam->self->u.master = &mxc_v4l2_master;   
  131.   
  132.     return 0;   
  133. }  

2.1.1 首先通过of_match_device函数根据pdev->dev里面的of_node项,从mxc_v4l2_dt_ids数组里面找出最匹配的一项of_device_id,然后将这个值赋给of_id。在这里of_id= {

.compatible= "fsl,imx6q-v4l2-capture",

.data= &imx_v4l2_devtype[IMX6_V4L2],

},


2.1.2 然后通过三个of_property_read_u32函数,来分别读取pdev->dev.of_node里面的"ipu_id""csi_id""mclk_source"的值,并将他们分别保存在ipu_idcsi_id以及mclk_source中。之后调用memsetcam_data*cam中的其他变量设置为0.

然后通过pdev->id_entry= of_id->data;pdev中的id_entry项设置为of_id->data。在这里就是pdev->id_entry= &imx_v4l2_devtype[IMX6_V4L2].

然后就是cam->devtype=pdev->id_entry->driver_data;通过上一步,刚把pdev里面的id_entry赋值,所以这一步,就是将cam->devtype的值设置为IMX6_V4L2


2.1.3 之后就是cam->ipu= ipu_get_soc(ipu_id); ipu_get_soc这个函数在ipu_common.c中定义,如下所示:

[cpp]  view plain  copy
  1. struct ipu_soc *ipu_get_soc(int id)   
  2. {   
  3.     if (id >= MXC_IPU_MAX_NUM)   
  4.         return ERR_PTR(-ENODEV);   
  5.     else if (!ipu_array[id].online)   
  6.         return ERR_PTR(-ENODEV);   
  7.     else   
  8.         return &(ipu_array[id]);   
  9. }   
  10. EXPORT_SYMBOL_GPL(ipu_get_soc);  

其中这个ipu_array数组可以搜索看到,是在前面定义的一个全局数组,如下:

[html]  view plain  copy
  1. static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM];  
  2. #define MXC_IPU_MAX_NUM     2  

目前为止,最多有2ipuipu_get_soc函数从这个ipu_array数组中根据ipu_id取出相应的ipu。然后对ipu_id进行判断,如果它大于ipu的最大数目,就直接返回。然后判断ipu_array[id].online这一项,我们可以在ipu_soc结构体里面看到这一项,他是一个bool类型的变量,判断系统是否正在占用这一个ipu,如果系统正在占用这一个ipu的话,就只能返回了,如果以上两点都满足的话,就返回找到的这个ipu_soc结构体的地址。可以看这个cam_data结构体,它里面有一项成员是void*ipu,所以这个ipu_get_soc返回的是(structipu_soc *)类型。在本例中就是返回&(ipu_array[id])

在这个ipu_soc函数中,它会返回成功的地址或者-ENODEV出错提示,在init_camera_struct函数中,它对这个函数的返回值进行了判断,如下所示:

[cpp]  view plain  copy
  1. cam->ipu = ipu_get_soc(ipu_id);   
  2. if (cam->ipu == NULL) {   
  3.     pr_err("ERROR: v4l2 capture: failed to get ipu\n");   
  4.     return -EINVAL;   
  5. else if (cam->ipu == ERR_PTR(-ENODEV)) {   
  6.     pr_err("ERROR: v4l2 capture: get invalid ipu\n");   
  7.     return -ENODEV;   
  8. }  


2.1.4 之后就是初始化一些互斥锁,为cam结构体里面的video_dev分配内存,然后通过*(cam->video_dev)= mxc_v4l_template;video_device结构体指向了mxc_v4l_templatemxc_v4l_template如下所示:

[cpp]  view plain  copy
  1. static struct video_device mxc_v4l_template = {   
  2.     .name = "Mxc Camera",   
  3.     .fops = &mxc_v4l_fops,   
  4.     .release = video_device_release,   
  5. };  
[cpp]  view plain  copy
  1. static struct v4l2_file_operations mxc_v4l_fops = {   
  2.     .owner = THIS_MODULE,   
  3.     .open = mxc_v4l_open,   
  4.     .release = mxc_v4l_close,   
  5.     .read = mxc_v4l_read,   
  6.     .ioctl = mxc_v4l_ioctl,   
  7.     .mmap = mxc_mmap,   
  8.     .poll = mxc_poll,   
  9. };  

通过这一步,就为用户空间提供了接口函数,当用户空间调用open,read, poll等函数的时候,就会调用到mxc_v4l_open,mxc_v4l_read, mxc_v4l_poll等函数。这些函数都在mxc_v4l2_capture.c里面提供了,后面再分析。


2.1.5 然后通过

[cpp]  view plain  copy
  1. video_set_drvdata(cam->video_dev, cam);   
  2. dev_set_drvdata(&pdev->dev, (void *)cam);  

这两个函数,分别将cam结构体设置为cam->video_dev&pdev->dev里面的私有数据。即将cam->video_dev->dev->p->driver_data指向cam和将&pdev->dev->p->driver_data指向cam

这两个函数就不具体分析了。


2.1.6 然后将cam->video_dev->minor设置为-1;structvideo_device结构体中有这样的注释:

/*'minor' is set to -1 if the registration failed */

因为我们现在在init_camera_struct函数中,初始化的时候也把这个值设置成初始值。


2.1.7 之后就是为v4l2_dev结构体分配内存并清零,调用了v4l2_device_register(&pdev->dev,v4l2_dev)函数,这个函数将v4l2_dev这个结构体里面的一些成员函数进行了初始化,然后将pdev->dev->p指向v4l2_dev,然后通过cam->video_dev->v4l2_dev= v4l2_dev;v4l2_dev添加到cam->video_dev结构体里面。


2.1.8 通过

[html]  view plain  copy
  1. init_waitqueue_head(&cam->enc_queue);   
  2. init_waitqueue_head(&cam->still_queue);  

这两条语句,初始化cam结构体里面的这两个队列头,这两个队列在用到的时候再分析。


2.1.9 之后就是设置cam_data结构体里面的一些其他参数的初始值:

[cpp]  view plain  copy
  1.        cam->crop_bounds.left = 0;   
  2. cam->crop_bounds.width = 640;   
  3. cam->crop_bounds.top = 0;   
  4. cam->crop_bounds.height = 480;  

首先设置的是crop_bounds的值,也就是图像裁剪的大小和位置,这个结构体是v4l2_rect类型的,

[cpp]  view plain  copy
  1. struct v4l2_rect {   
  2.     __s32   left;   
  3.     __s32   top;   
  4.     __u32   width;   
  5.     __u32   height;   
  6. };  

为什么这个结构体采用这四个值呢?可以想象一下,如果想要裁剪显示屏幕上任意一块区域,只需要知道这一块区域的左上角坐标,同时知道区域的宽度和高度,这块区域其他3个点的坐标位置就可以通过这几个值计算出来。

然后通过cam->crop_current= cam->crop_defrect =cam->crop_bounds;这条语句将cam_data结构体里面其他两个相似的成员crop_currentcrop_defrect赋值。将这三个值都初始化为左上角坐标为(00),区域大小都为640* 480

之后通过ipu_csi_set_window_size(cam->ipu,cam->crop_current.width,

cam->crop_current.height,cam->csi);

函数来设置显示窗口的大小范围,这个ipu_csi_set_window_size函数在ipu_capture.c中定义,先简略分析一下,核心就是下面这个函数:

ipu_csi_write(ipu,csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);

--->writel(value, ipu->csi_reg[csi] + offset);

其中value= (width - 1) | (height - 1) << 16, offset = CSI_ACT_FRM_SIZE,大致意思是根据cam->ipucam->csi(这两个变量之前刚设定好)将(width- 1) | (height - 1) <<16的值写到csi寄存器里面,这个csi寄存器的初始地址在通过ipu_get_soc函数获取ipu的时候已经获取到了,CSI_ACT_FRM_SIZE就是关于csi寄存器初始地址的偏移值,它在drivers/mxc/ipu3/ipu_regs.h中定义。

之后就是ipu_csi_set_window_pos(cam->ipu,cam->crop_current.left,

cam->crop_current.top,cam->csi);

函数,这个函数同样在ipu_capture.c中定义,它会根据ipucsi的值,设置底层的寄存器的值。


即上面设置了crop_bounds4个初始值,然后就调用ipu_csi_set_window_sizeipu_csi_set_window_pos将这四个值写到底层的寄存器中去。


2.1.10 下面就是继续设置cam_data结构体里面成员初始值,分别设置了streamparmstandardstandard_autodetectoverlay_oncapture_onv2fwin的值。这些就不分析了,后面用到的话再说。

然后继续设置其他值:

[cpp]  view plain  copy
  1. cam->ipu_id = ipu_id;   
  2. cam->csi = csi_id;   
  3. cam->mclk_source = mclk_source;   
  4. cam->mclk_on[cam->mclk_source] = false;  

比较重要的是下面这个:

cam->enc_callback=camera_callback;设置cam_data结构体中的enc_callback回调函数,这个函数在本文件中定义,它是一个中断处理函数,在视频采集过程中,如果一个buffer填充满的话,会产生一个中断信号,中断处理函数中最终会调用到这个函数来处理中断。


2.1.11 下面就是设置cam_data中的self结构体,self结构体为structv4l2_int_device类型的,

[cpp]  view plain  copy
  1. cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);   
  2. cam->self->module = THIS_MODULE;   
  3. sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);   
  4. cam->self->type = v4l2_int_type_master;   
  5. cam->self->u.master = &mxc_v4l2_master;  

首先分配内存,将module指向THIS_MODULE,然后将名字根据csi号设置为mxc_v4l2_cap%d的类型,分别设置selftypeu.master类型。将这个cam->self结构体作为一个master设备注册到int_list链表中。


至此,这个cam_data结构体初始化完毕了,这个结构体toobig了~~~


2.2 下面我们返回probe函数中继续分析:

pdev->dev.release= camera_platform_release;

device结构体中指定release函数,但是这个函数中什么都没有做,如果不添加这个函数的话就会报错,所以就添加一个空函数进去。


2.3 下面是

cam->self->priv= cam;

v4l2_int_device_register(cam->self);

cam_data结构体中self变量是v4l2_int_device类型的,首先将cam保存为v4l2_int_deviceself的私有数据,然后在操作cam->self结构体的时候,在很多函数中,行参只是v4l2_int_device类型的self,相要获得更外层的cam_data结构体的话,就可以从self->priv中获取。然后调用v4l2_int_device_register函数来注册v4l2_int_device

这个函数在v4l2-int-device.c中定义,如下所示:

[cpp]  view plain  copy
  1. int v4l2_int_device_register(struct v4l2_int_device *d)   
  2. {   
  3.     if (d->type == v4l2_int_type_slave)   
  4.         sort(d->u.slave->ioctls, d->u.slave->num_ioctls, <span style="font-family:Courier 10 Pitch;"> </span>/* 按照序号存储,加快访问速度。*/   
  5.              sizeof(struct v4l2_int_ioctl_desc),   
  6.              &ioctl_sort_cmp, NULL);   
  7.     mutex_lock(&mutex);   
  8.     list_add(&d->head, &int_list); //无论是slave还是master都会添加到int_list中   
  9.     v4l2_int_device_try_attach_all(); //都会做匹配动作   
  10.     mutex_unlock(&mutex);   
  11.   
  12.     return 0;   
  13. }   
  14. EXPORT_SYMBOL_GPL(v4l2_int_device_register);  

在前一节中已经具体分析这个v4l2设备中masterslave的注册过程了,在这里就不再具体分析。


2.4下面是这个probe函数的核心函数:video_register_device它在v4l2-dev.h中定义,如下所示:

[cpp]  view plain  copy
  1. static inline int __must_check video_register_device_no_warn(   
  2.         struct video_device *vdev, int type, int nr)   
  3. {   
  4.     return __video_register_device(vdev, type, nr, 0, vdev->fops->owner);   
  5. }  

__video_register_devicev4l2-dev.c中定义:(就直接在代码中注释了)

[cpp]  view plain  copy
  1. int __video_register_device(struct video_device *vdev, int type, int nr,   
  2.         int warn_if_nr_in_use, struct module *owner)   
  3. {   
  4.     int i = 0;   
  5.     int ret;   
  6.     int minor_offset = 0;   
  7.     int minor_cnt = VIDEO_NUM_DEVICES;   
  8.     const char *name_base;   
  9.   
  10.     /* A minor value of -1 marks this video device as never  
  11.        having been registered */   
  12.     vdev->minor = -1;   
  13.   
  14.     /* the release callback MUST be present */   
  15.     if (WARN_ON(!vdev->release))   
  16.         return -EINVAL;   

/*如果没有提供这个release函数的话,就直接返回错误,那么我们在哪初始化了它呢?在上面的init_camera_struct(cam,pdev);函数中,通过下面这个语句*(cam->video_dev)= mxc_v4l_template;来为它指定release函数了,在mxc_v4l_template结构体中

.release= video_device_release*/

caseVFL_TYPE_GRABBER:

[cpp]  view plain  copy
  1. /* the v4l2_dev pointer MUST be present */   
  2.     if (WARN_ON(!vdev->v4l2_dev))   
  3.         return -EINVAL;  

/*这个v4l2_dev指针也是必须要提供的,它同样在init_camera_struct函数中提供了*/

[html]  view plain  copy
  1. /* v4l2_fh support */   
  2. spin_lock_init(&vdev->fh_lock);   
  3. INIT_LIST_HEAD(&vdev->fh_list);   
  4.   
  5. /* Part 1: check device type */   
  6. switch (type) {   
  7. case VFL_TYPE_GRABBER:   
  8.     name_base = "video";   
  9.     break;   
  10. case VFL_TYPE_VBI:   
  11.     name_base = "vbi";   
  12.     break;   
  13. case VFL_TYPE_RADIO:   
  14.     name_base = "radio";   
  15.     break;   
  16. case VFL_TYPE_SUBDEV:   
  17.     name_base = "v4l-subdev";   
  18.     break;   
  19. default:   
  20.     printk(KERN_ERR "%s called with unknown type: %d\n",   
  21.            __func__, type);   
  22.     return -EINVAL;   
  23. }   

/*根据传进来的type参数,确定设备在/dev目录下看到的名字*/

[cpp]  view plain  copy
  1. vdev->vfl_type = type;   
  2. vdev->cdev = NULL;   
  3. if (vdev->v4l2_dev) {   
  4.     if (vdev->v4l2_dev->dev)   
  5.         vdev->parent = vdev->v4l2_dev->dev;   
  6.     if (vdev->ctrl_handler == NULL)   
  7.         vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;   
  8.     /* If the prio state pointer is NULL, then use the v4l2_device  
  9.        prio state. */   
  10.     if (vdev->prio == NULL)   
  11.         vdev->prio = &vdev->v4l2_dev->prio;   
  12. }   

/*进行vdev中父设备和ctrl处理函数的初始化。*/

[cpp]  view plain  copy
  1.     /* Part 2: find a free minor, device node number and device index. */   
  2. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES   
  3.     /* Keep the ranges for the first four types for historical  
  4.      * reasons.  
  5.      * Newer devices (not yet in place) should use the range  
  6.      * of 128-191 and just pick the first free minor there  
  7.      * (new style). */   
  8.     switch (type) {   
  9.     case VFL_TYPE_GRABBER:   
  10.         minor_offset = 0;   
  11.         minor_cnt = 64;   
  12.         break;   
  13.     case VFL_TYPE_RADIO:   
  14.         minor_offset = 64;   
  15.         minor_cnt = 64;   
  16.         break;   
  17.     case VFL_TYPE_VBI:   
  18.         minor_offset = 224;   
  19.         minor_cnt = 32;   
  20.         break;   
  21.     default:   
  22.         minor_offset = 128;   
  23.         minor_cnt = 64;   
  24.         break;   
  25.     }   
  26. #endif   
  27.   
  28.     /* Pick a device node number */   
  29.     mutex_lock(&videodev_lock);   
  30.     nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);   
  31.     if (nr == minor_cnt)   
  32.         nr = devnode_find(vdev, 0, minor_cnt);   
  33.     if (nr == minor_cnt) {   
  34.         printk(KERN_ERR "could not get a free device node number\n");   
  35.         mutex_unlock(&videodev_lock);   
  36.         return -ENFILE;   
  37.     }   
  38. #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES   
  39.     /* 1-on-1 mapping of device node number to minor number */   
  40.     i = nr;   
  41. #else   
  42.     /* The device node number and minor numbers are independent, so  
  43.        we just find the first free minor number. */   
  44.     for (i = 0; i < VIDEO_NUM_DEVICES; i++)   
  45.         if (video_device[i] == NULL)   
  46.             break;   
  47.     if (i == VIDEO_NUM_DEVICES) {   
  48.         mutex_unlock(&videodev_lock);   
  49.         printk(KERN_ERR "could not get a free minor\n");   
  50.         return -ENFILE;   
  51.     }   
  52. #endif   
  53.     vdev->minor = i + minor_offset;   
  54.     vdev->num = nr;   
  55.     devnode_set(vdev);   
  56.   
  57.     /* Should not happen since we thought this minor was free */   
  58.     WARN_ON(video_device[vdev->minor] != NULL);   
  59.     vdev->index = get_index(vdev);   
  60.     video_device[vdev->minor] = vdev;  
  61.     mutex_unlock(&videodev_lock);   
  62.   
  63.     if (vdev->ioctl_ops)   
  64.         determine_valid_ioctls(vdev);  

/*上面的part2就是确定设备的次设备号*/

[cpp]  view plain  copy
  1. /* Part 3: Initialize the character device */   
  2. vdev->cdev = cdev_alloc();   
  3. if (vdev->cdev == NULL) {   
  4.     ret = -ENOMEM;   
  5.     goto cleanup;   
  6. }   

/*在这进行设备的注册,用cdev_alloc函数,从这我们就可以看出来,它是一个普通的字符设备驱动,然后设置它的一些参数。怎么就是字符设备驱动了???这个通过v4l2框架中可以看出来。*/

[cpp]  view plain  copy
  1. vdev->cdev->ops = &v4l2_fops;   

/*cdev结构体里面的ops指向了v4l2_fops这个结构体,这个v4l2_fops结构体也是在v4l2-dev.c这个文件中。它是一个file_operations操作函数集,

[html]  view plain  copy
  1. static const struct file_operations v4l2_fops = {   
  2.     .owner = THIS_MODULE,   
  3.     .read = v4l2_read,   
  4.     .write = v4l2_write,   
  5.     .open = v4l2_open,   
  6.     .get_unmapped_area = v4l2_get_unmapped_area,   
  7.     .mmap = v4l2_mmap,   
  8.     .unlocked_ioctl = v4l2_ioctl,   
  9. #ifdef CONFIG_COMPAT   
  10.     .compat_ioctl = v4l2_compat_ioctl32,   
  11. #endif   
  12.     .release = v4l2_release,   
  13.     .poll = v4l2_poll,   
  14.     .llseek = no_llseek,   
  15. };  

仔细分析上面那些子函数的话,就可以发现他们最终还是调用的是cam->video_dev所提供的函数,即

mxc_v4l_template结构体里面的mxc_v4l_fops

*/

[cpp]  view plain  copy
  1.     vdev->cdev->owner = owner;   
  2.     ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);   
  3.     if (ret < 0) {   
  4.         printk(KERN_ERR "%s: cdev_add failed\n", __func__);   
  5.         kfree(vdev->cdev);   
  6.         vdev->cdev = NULL;   
  7.         goto cleanup;   
  8.     }   
  9.   
  10.   
  11.     /* Part 4: register the device with sysfs */   
  12.     vdev->dev.class = &video_class;   
  13.     vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);   
  14.     if (vdev->parent)   
  15.         vdev->dev.parent = vdev->parent;   
  16.     dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);   
  17.     ret = device_register(&vdev->dev);   
  18.     if (ret < 0) {   
  19.         printk(KERN_ERR "%s: device_register failed\n", __func__);   
  20.         goto cleanup;   
  21.     }   
  22.     /* Register the release callback that will be called when the last  
  23.        reference to the device goes away. */   
  24.     vdev->dev.release = v4l2_device_release;   
  25.   
  26.     if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)   
  27.         printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,   
  28.             name_base, nr, video_device_node_name(vdev));   
  29.   
  30.     /* Increase v4l2_device refcount */   
  31.     if (vdev->v4l2_dev)   
  32.         v4l2_device_get(vdev->v4l2_dev);   
  33. /* 在sysfs中创建类,在类下创建设备结点 */  
  34.   
  35. #if defined(CONFIG_MEDIA_CONTROLLER)   
  36.     /* Part 5: Register the entity. */   
  37.     if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&   
  38.         vdev->vfl_type != VFL_TYPE_SUBDEV) {   
  39.         vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;   
  40.         vdev->entity.name = vdev->name;   
  41.         vdev->entity.info.v4l.major = VIDEO_MAJOR;   
  42.         vdev->entity.info.v4l.minor = vdev->minor;   
  43.         ret = media_device_register_entity(vdev->v4l2_dev->mdev,   
  44.             &vdev->entity);   
  45.         if (ret < 0)   
  46.             printk(KERN_WARNING   
  47.                    "%s: media_device_register_entity failed\n",   
  48.                    __func__);   
  49.     }   
  50. #endif   

/*创建实体entity,这一步并不是必须的,需要配置了CONFIG_MEDIA_CONTROLLER选项后才会执行这一步,在这一步里面有一个media_entity实体结构体,在后面再分析它。*/

[cpp]  view plain  copy
  1. /* Part 6: Activate this minor. The char device can now be used. */   
  2. set_bit(V4L2_FL_REGISTERED, &vdev->flags);   

/*设置标志位*/

[cpp]  view plain  copy
  1. mutex_lock(&videodev_lock);   
  2. video_device[vdev->minor] = vdev;   

/*将设置好的video_device结构体vdev按照次设备号保存到video_device数组中。这个数组是在前面staticstruct video_device *video_device[VIDEO_NUM_DEVICES];定义的。*/

[cpp]  view plain  copy
  1.     mutex_unlock(&videodev_lock);   
  2.   
  3.     return 0;   
  4.   
  5. cleanup:   
  6.     mutex_lock(&videodev_lock);   
  7.     if (vdev->cdev)   
  8.         cdev_del(vdev->cdev);   
  9.     devnode_clear(vdev);   
  10.     mutex_unlock(&videodev_lock);   
  11.     /* Mark this video device as never having been registered. */   
  12.     vdev->minor = -1;   
  13.     return ret;   
  14. }   
  15. EXPORT_SYMBOL(__video_register_device);  


2.5 下面通过3次调用device_create_file函数,来分别为dev_attr_fsl_v4l2_capture_propertydev_attr_fsl_v4l2_overlay_propertydev_attr_fsl_csi_property/sys/class/下创建对应的属性文件。


至此,mxc_v4l2_probe函数就分析完毕了。

猜你喜欢

转载自blog.csdn.net/dragon101788/article/details/80660822
3.1
今日推荐