我是靠谱客的博主 虚幻小甜瓜,最近开发中收集的这篇文章主要介绍2. master和slave的匹配过程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

(一)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 failedn"); 
		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 failedn"); 
		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_attachn"); 
	pr_debug("   slave.name = %sn", slave->name); 
	pr_debug("   master.name = %sn", 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 matchn", __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 failedn"); 
		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 availablen"); 
		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 availablen"); 
		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 failedn"); 
		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 invalidn"); 
		return retval; 
	} 

	retval = of_property_read_u32(dev->of_node, "mclk_source", 
					(u32 *) &(ov5640_data.mclk_source)); 
	if (retval) { 
		dev_err(dev, "mclk_source invalidn"); 
		return retval; 
	} 

	retval = of_property_read_u32(dev->of_node, "csi_id", 
					&(ov5640_data.csi)); 
	if (retval) { 
		dev_err(dev, "csi_id invalidn"); 
		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 foundn"); 
		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 foundn"); 
		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 foundn"); 
	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等操作将这些值写到了寄存器中。


关于这个流程,我画了一个思维导图来辅助理解:












最后

以上就是虚幻小甜瓜为你收集整理的2. master和slave的匹配过程的全部内容,希望文章能够帮你解决2. master和slave的匹配过程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部