NXP

2. master和slave的匹配过程

2019-07-12 13:41发布

(一)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 "); 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 "); 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->typev4l2_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设备和第一个没有设置masterslave设备。 然后将这个slave设备的u.slave->master设置成找到的master设备m,然后调用mu.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 "); pr_debug(" slave.name = %s ", slave->name); pr_debug(" master.name = %s ", slave->u.slave->master->name); if (slave == NULL) { pr_err("ERROR: v4l2 capture: slave parameter not valid. "); return -1; } if (sdata->csi != cam->csi) { pr_debug("%s: csi doesn't match ", __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. "); 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 "); 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 "); 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 "); 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 "); 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 "); 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 "); return retval; } retval = of_property_read_u32(dev->of_node, "csi_id", &(ov5640_data.csi)); if (retval) { dev_err(dev, "csi_id invalid "); 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 "); 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 "); 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 "); 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设备和第一个没有设置masterslave设备,然后将这个slave设备的u.slave->master设置成找到的master设备m,然后调用mu.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_sizeipu_csi_set_window_pos等操作将这些值写到了寄存器中。
关于这个流程,我画了一个思维导图来辅助理解: