(一)master的注册过程
1. 首先来看看master的注册过程,在mxc_v4l2_capture.c文件中,从module_init(camera_init)函数开始,在camera_init函数中通过
err= platform_driver_register(&mxc_v4l2_driver)
来将mxc_v4l2_driver这个驱动注册到platform平台上面,如果有匹配的设备的话,就会调用到mxc_v4l2_driver里面的probe函数。
(下面的分析都是从流程上面来分析,去掉了函数中一些与分析无关的代码,关于函数的详细分析,看《mxc_v4l2_capture.c分析》那一节)
2. 下面来看看这个probe函数
- static int mxc_v4l2_probe(struct platform_device *pdev)
- {
- /* Create cam and initialize it. */
- cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
- init_camera_struct(cam, pdev); //初始化cam_data结构体,分析见2.1
- pdev->dev.release = camera_platform_release;
- /* Set up the v4l2 device and register it*/
- cam->self->priv = cam; //将cam_data结构体里面的self里面的priv指针指向自己,在下面的 v4l2_int_device_register 函数中就会用到。
- v4l2_int_device_register(cam->self); //分析见2.2
- /* register v4l video device */
- if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) { //注册video设备
- kfree(cam);
- cam = NULL;
- pr_err("ERROR: v4l2 capture: video_register_device failed\n");
- return -1;
- }
- }
在这个函数中,首先是为cam_data结构体分配了内存,然后就调用init_camera_struct函数来初始化这个cam_data结构体。
2.1 我们来看看这个init_camera_struct函数里面都做了什么:
- static int init_camera_struct(cam_data *cam, struct platform_device *pdev)
- {
- const struct of_device_id *of_id =
- of_match_device(mxc_v4l2_dt_ids, &pdev->dev);
- struct device_node *np = pdev->dev.of_node;
- int ipu_id, csi_id, mclk_source;
- int ret = 0;
- struct v4l2_device *v4l2_dev;
- /* Default everything to 0 */
- memset(cam, 0, sizeof(cam_data));
- init_MUTEX(&cam->param_lock);
- init_MUTEX(&cam->busy_lock);
- cam->video_dev = video_device_alloc(); //分配一个video_device结构体
- *(cam->video_dev) = mxc_v4l_template; //设置ops操作
- video_set_drvdata(cam->video_dev, cam); //将cam设置为cam->video_dev的私有数据
- dev_set_drvdata(&pdev->dev, (void *)cam);
- cam->video_dev->minor = -1;
- v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
- if (v4l2_device_register(&pdev->dev, v4l2_dev) < 0) {
- dev_err(&pdev->dev, "register v4l2 device failed\n");
- video_device_release(cam->video_dev);
- kfree(v4l2_dev);
- return -ENODEV;
- }
- cam->video_dev->v4l2_dev = v4l2_dev;
- init_waitqueue_head(&cam->enc_queue);
- init_waitqueue_head(&cam->still_queue);
- /* setup cropping */
- cam->crop_bounds.left = 0;
- cam->crop_bounds.width = 640;
- cam->crop_bounds.top = 0;
- cam->crop_bounds.height = 480;
- cam->crop_current = cam->crop_defrect = cam->crop_bounds;
- 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);
- cam->streamparm.parm.capture.capturemode = 0;
- cam->standard.index = 0;
- cam->standard.id = V4L2_STD_UNKNOWN;
- cam->standard.frameperiod.denominator = 30;
- cam->standard.frameperiod.numerator = 1;
- cam->standard.framelines = 480;
- cam->standard_autodetect = true;
- cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
- cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- cam->overlay_on = false;
- cam->capture_on = false;
- cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
- cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
- cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
- cam->v2f.fmt.pix.width = 288;
- cam->v2f.fmt.pix.height = 352;
- cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
- cam->win.w.width = 160;
- cam->win.w.height = 160;
- cam->win.w.left = 0;
- cam->win.w.top = 0;
- cam->ipu_id = ipu_id;
- cam->csi = csi_id;
- cam->mclk_source = mclk_source;
- cam->mclk_on[cam->mclk_source] = false;
- cam->enc_callback = camera_callback; //设置回调函数,这个函数很重要
- init_waitqueue_head(&cam->power_queue);
- spin_lock_init(&cam->queue_int_lock);
- spin_lock_init(&cam->dqueue_int_lock);
- cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);
- cam->self->module = THIS_MODULE;
- sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);
- cam->self->type = v4l2_int_type_master;
- cam->self->u.master = &mxc_v4l2_master;
- return 0;
- }
在这个函数中,对cam_data结构体里面的一些变量进行了初始化,设置了一些默认值。同时注意这个函数的最后几步操作,它为cam->self分配了内存,然后设置cam->self->type为v4l2_int_type_master,指定了cam->self->u.master为&mxc_v4l2_master。这几步有什么作用呢?这个在2.2中分析。
2.2 v4l2_int_device_register函数分析
- int v4l2_int_device_register(struct v4l2_int_device *d)
- {
- if (d->type == v4l2_int_type_slave)
- sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
- sizeof(struct v4l2_int_ioctl_desc),
- &ioctl_sort_cmp, NULL);
- mutex_lock(&mutex);
- list_add(&d->head, &int_list);
- v4l2_int_device_try_attach_all();
- mutex_unlock(&mutex);
- return 0;
- }
- EXPORT_SYMBOL_GPL(v4l2_int_device_register);
在这个函数中,首先通过list_add将这个cam->self结构体添加到int_list链表中,最重要的操作就是v4l2_int_device_try_attach_all()函数,
- void v4l2_int_device_try_attach_all(void)
- {
- struct v4l2_int_device *m, *s;
- list_for_each_entry(m, &int_list, head) {
- if (m->type != v4l2_int_type_master)
- continue;
- list_for_each_entry(s, &int_list, head) {
- if (s->type != v4l2_int_type_slave)
- continue;
- /* Slave is connected? */
- if (s->u.slave->master)
- continue;
- /* Slave wants to attach to master? */
- if (s->u.slave->attach_to[0] != 0
- && strncmp(m->name, s->u.slave->attach_to,
- V4L2NAMESIZE))
- continue;
- if (!try_module_get(m->module))
- continue;
- s->u.slave->master = m;
- if (m->u.master->attach(s)) {
- s->u.slave->master = NULL;
- module_put(m->module);
- continue;
- }
- }
- }
- }
- EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
这个函数首先从int_list链表中取出type类型为v4l2_int_type_master的结构体保存在m中,然后再取出type类型为v4l2_int_type_slave的结构体保存在s中。然后判断s->u.slave->master是否存在,如果存在的话就跳过继续寻找。这个变量就代表了这个slave设备已经设置了它所对应的master。那么这个变量是在哪设置的呢?继续看这个函数,就在后面设置了:s->u.slave->master= m; 。
这两个list_for_each_entry的最终结果就是找到master设备和第一个没有设置master的slave设备。
然后将这个slave设备的u.slave->master设置成找到的master设备m,然后调用m的u.master->atach(s)函数,这个函数就是在init_camera_struct函数最后设置的:cam->self->u.master= &mxc_v4l2_master;
- static struct v4l2_int_master mxc_v4l2_master = {
- .attach = mxc_v4l2_master_attach,
- .detach = mxc_v4l2_master_detach,
- };
所以最终会调用到mxc_v4l2_master_attach函数,同时这个函数的行参是这个slave设备s。
- static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
- {
- cam_data *cam = slave->u.slave->master->priv;
- struct v4l2_format cam_fmt;
- int i;
- struct sensor_data *sdata = slave->priv;
- pr_debug("In MVC: mxc_v4l2_master_attach\n");
- pr_debug(" slave.name = %s\n", slave->name);
- pr_debug(" master.name = %s\n", slave->u.slave->master->name);
- if (slave == NULL) {
- pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
- return -1;
- }
- if (sdata->csi != cam->csi) {
- pr_debug("%s: csi doesn't match\n", __func__);
- return -1;
- }
- cam->sensor = slave;
- if (cam->sensor_index < MXC_SENSOR_NUM) {
- cam->all_sensors[cam->sensor_index] = slave;
- cam->sensor_index++;
- } else {
- pr_err("ERROR: v4l2 capture: slave number exceeds "
- "the maximum.\n");
- return -1;
- }
- for (i = 0; i < cam->sensor_index; i++) {
- vidioc_int_dev_exit(cam->all_sensors[i]);
- vidioc_int_s_power(cam->all_sensors[i], 0);
- }
- cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
- /* Used to detect TV in (type 1) vs. camera (type 0)*/
- cam->device_type = cam_fmt.fmt.pix.priv;
- /* Set the input size to the ipu for this device */
- 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;
- /* This also is the max crop size for this device. */
- cam->crop_defrect.top = cam->crop_defrect.left = 0;
- cam->crop_defrect.width = cam_fmt.fmt.pix.width;
- cam->crop_defrect.height = cam_fmt.fmt.pix.height;
- /* At this point, this is also the current image size. */
- cam->crop_current.top = cam->crop_current.left = 0;
- cam->crop_current.width = cam_fmt.fmt.pix.width;
- cam->crop_current.height = cam_fmt.fmt.pix.height;
- return 0;
- }
在这个函数中设置了cam->sensor= slave;然后又设置了crop的一些参数。而这些crop参数都是根据slave结构体来设置的。在init_camera_struct函数中只是给了这些crop参数一些初始默认值,在这个函数中才是根据真正从slave设备中获取到的参数来填充crop参数。
(二)slave的注册过程
3. 以上是master的注册过程,再来看看slave的注册过程。以ov5640.c为例:
3.1 module_i2c_driver(ov5640_i2c_driver);
- static struct i2c_driver ov5640_i2c_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ov564x",
- },
- .probe = ov5640_probe,
- .remove = ov5640_remove,
- .id_table = ov5640_id,
- };
之后就会调用到ov5640_probe函数。
3.2 ov5640_probe函数
- static int ov5640_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct pinctrl *pinctrl;
- struct device *dev = &client->dev;
- int retval;
- u8 chip_id_high, chip_id_low;
- /* ov5640 pinctrl */
- pinctrl = devm_pinctrl_get_select_default(dev);
- if (IS_ERR(pinctrl)) {
- dev_err(dev, "setup pinctrl failed\n");
- return PTR_ERR(pinctrl);
- }
- /* request power down pin */
- pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
- if (!gpio_is_valid(pwn_gpio)) {
- dev_err(dev, "no sensor pwdn pin available\n");
- return -ENODEV;
- }
- retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_pwdn");
- if (retval < 0)
- return retval;
- /* request reset pin */
- rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
- if (!gpio_is_valid(rst_gpio)) {
- dev_err(dev, "no sensor reset pin available\n");
- return -EINVAL;
- }
- retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_reset");
- if (retval < 0)
- return retval;
- /* Set initial values for the sensor struct. */
- memset(&ov5640_data, 0, sizeof(ov5640_data));
- ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
- if (IS_ERR(ov5640_data.sensor_clk)) {
- dev_err(dev, "get mclk failed\n");
- return PTR_ERR(ov5640_data.sensor_clk);
- }
- retval = of_property_read_u32(dev->of_node, "mclk",
- &ov5640_data.mclk);
- if (retval) {
- dev_err(dev, "mclk frequency is invalid\n");
- return retval;
- }
- retval = of_property_read_u32(dev->of_node, "mclk_source",
- (u32 *) &(ov5640_data.mclk_source));
- if (retval) {
- dev_err(dev, "mclk_source invalid\n");
- return retval;
- }
- retval = of_property_read_u32(dev->of_node, "csi_id",
- &(ov5640_data.csi));
- if (retval) {
- dev_err(dev, "csi_id invalid\n");
- return retval;
- }
- clk_prepare_enable(ov5640_data.sensor_clk);
- ov5640_data.io_init = ov5640_reset;
- ov5640_data.i2c_client = client;
- ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- ov5640_data.pix.width = 640;
- ov5640_data.pix.height = 480;
- ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
- V4L2_CAP_TIMEPERFRAME;
- ov5640_data.streamcap.capturemode = 0;
- ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
- ov5640_data.streamcap.timeperframe.numerator = 1;
- ov5640_regulator_enable(&client->dev);
- ov5640_reset();
- ov5640_power_down(0);
- retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
- if (retval < 0 || chip_id_high != 0x56) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 is not found\n");
- return -ENODEV;
- }
- retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
- if (retval < 0 || chip_id_low != 0x40) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 is not found\n");
- return -ENODEV;
- }
- ov5640_power_down(1);
- clk_disable_unprepare(ov5640_data.sensor_clk);
- ov5640_int_device.priv = &ov5640_data;
- retval = v4l2_int_device_register(&ov5640_int_device);
- pr_info("camera ov5640 is found\n");
- return retval;
- }
这个函数主要是设置ov5640_data结构体的一些值,通过几个of函数来获取到的信息填充到这个结构体中。然后最后调用到了v4l2_int_device_register这个函数。这个函数同样在上面的mxc_v4l2_probe中调用了。不同的是注册的ov5640_int_device结构体,在mxc_v4l2_probe中注册的是cam->self结构体,这两个结构体都是v4l2_int_device类型的:
- static struct v4l2_int_device ov5640_int_device = {
- .module = THIS_MODULE,
- .name = "ov564x",
- .type = v4l2_int_type_slave,
- .u = {
- .slave = &ov5640_slave,
- },
- };
可以看到,这个ov5640_int_device结构体中设置的type类型是v4l2_int_type_slave,同时u设置的是slave。再次对比init_camera_struct函数中master是怎么设置的:
- cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);
- cam->self->module = THIS_MODULE;
- sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);
- cam->self->type = v4l2_int_type_master;
- cam->self->u.master = &mxc_v4l2_master;
然后调用v4l2_int_device_register函数,在里面通过list_add将这个设备添加到int_list链表中,所以这个链表中的设备既包括master设备,同时也包括slave设备。在这里,每添加进去一个设备,这个int_list链表中都会增加一个slave设备,同时master设备就是cam->self结构体。从这个链表中取出设备的话,需要根据这个type类型来区分。
之后在v4l2_int_device_register函数继续调用v4l2_int_device_try_attach_all()函数,这个函数在上面已经分析很清楚了,会从int_list链表找到master设备和第一个没有设置master的slave设备,然后将这个slave设备的u.slave->master设置成找到的master设备m,然后调用m的u.master->attach(s)函数,完成匹配过程。
这个v4l2_int_device_try_attach_all()函数在master设备或者slave设备注册进链表的时候,都会调用到,都会互相去匹配。
4.当在应用程序执行open的时候,再次通过vidioc_int_g_fmt_cap函数获取了cam->sensor的信息,此时经过上面那些步骤,cam->sensor已经指向了找到的slave设备。重新设置crop的一些值,在这里通过ipu_csi_set_window_size和ipu_csi_set_window_pos等操作将这些值写到了寄存器中。
关于这个流程,我画了一个思维导图来辅助理解:
![](https://i-blog.csdnimg.cn/blog_migrate/08c580b56f7cfad7825e852094b3f2d4.png)