概述
(一)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->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_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设备和第一个没有设置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等操作将这些值写到了寄存器中。
关于这个流程,我画了一个思维导图来辅助理解:
最后
以上就是虚幻小甜瓜为你收集整理的2. master和slave的匹配过程的全部内容,希望文章能够帮你解决2. master和slave的匹配过程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复