设备树内容
ov2640: camera@0x30 { compatible = "ovti,ov2640"; reg = <0x30>; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_csi1 &csi_pwn_rst>; resetb = <&gpio1 2 GPIO_ACTIVE_LOW>; pwdn = <&gpio1 4 GPIO_ACTIVE_HIGH>; clocks = <&clks IMX6UL_CLK_CSI>; clock-names = "xvclk"; port { camera_ep: endpoint { remote-endpoint = <&csi_ep>; bus-width = <8>; }; }; };
module_i2c_driver宏分析
// 注册i2c驱动程序
module_i2c_driver(ov2640_i2c_driver);
module_i2c_driver(ov2640_i2c_driver),可以分析出以下信息:
module_i2c_driver是一个宏,它可能是在编程中定义的一个宏,用于简化I2C驱动模块的注册。
ov2640_i2c_driver是一个I2C驱动的结构体或变量名。它可能定义了有关OV2640摄像头的I2C通信设置和功能的信息。
宏module_i2c_driver可能在内部执行一些操作,以便将ov2640_i2c_driver的I2C驱动注册到系统中。这通常涉及使用相关的函数和数据结构将驱动程序添加到I2C驱动程序列表中,并与I2C总线进行关联。
总的来说,module_i2c_driver(ov2640_i2c_driver)宏的作用是将ov2640_i2c_driver所定义的I2C驱动注册到系统中,以便系统能够正确识别和使用与OV2640摄像头相关的I2C通信功能。
module_i2c_driver原型
#define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, \ i2c_del_driver)
根据您提供的代码宏定义,module_i2c_driver宏用于简化I2C驱动模块的注册和注销过程,并使用了module_driver宏。
该宏定义的详细分析如下:
module_i2c_driver是宏的名称。
__i2c_driver是一个传入的参数,表示要注册的I2C驱动程序。宏的具体实现包括以下步骤:
使用module_driver宏,传入__i2c_driver作为驱动参数,以及i2c_add_driver和i2c_del_driver作为注册和注销函数。
i2c_add_driver是用于将I2C驱动程序添加到系统中的函数。
i2c_del_driver是用于从系统中注销I2C驱动程序的函数。
通过使用module_i2c_driver宏,可以将注册和注销I2C驱动程序的过程简化为调用module_driver宏,并传入适当的参数。这样可以减少手动编写注册和注销函数的工作量,并提高代码的可读性和可维护性。
总结而言,module_i2c_driver宏的作用是将指定的I2C驱动程序注册到系统中,并在加载模块时调用注册函数,以及在卸载模块时调用注销函数。
module_i2c_driver的原型为module_driver,定义如下
#define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ __unregister(&(__driver) , ##__VA_ARGS__); \ } \ module_exit(__driver##_exit);
module_driver宏用于简化驱动模块的注册和注销过程,并提供了模块的初始化和退出函数。
该宏定义的详细分析如下:
module_driver是宏的名称。
__driver是一个传入的参数,表示要注册的驱动程序。
__register是一个传入的参数,表示用于注册驱动的函数。
__unregister是一个传入的参数,表示用于注销驱动的函数。
…表示可变参数,用于传递额外的参数给注册和注销函数。
宏的具体实现包括以下步骤:
定义一个静态的初始化函数__driver##_init,该函数在模块初始化时被调用。
在__driver##_init函数中,调用__register函数来注册驱动程序,传递驱动程序结构体和额外的参数。
使用module_init宏将__driver##_init函数指定为模块的初始化函数,确保在加载模块时会调用该函数进行初始化。
定义一个静态的退出函数__driver##_exit,该函数在模块注销时被调用。
在__driver##_exit函数中,调用__unregister函数来注销驱动程序,传递驱动程序结构体和额外的参数。
使用module_exit宏将__driver##_exit函数指定为模块的退出函数,确保在卸载模块时会调用该函数进行注销。
通过使用module_driver宏,可以简化驱动模块的注册和注销过程,减少了手动编写初始化和退出函数的工作量,提高了代码的可读性和可维护性。
ov2640_i2c_driver
ov2640的i2c驱动程序
// 定义ov2640的i2c设备ID static const struct i2c_device_id ov2640_id[] = { { "ov2640", 0 }, // 设备名为ov2640,ID为0 { } // 结束符 }; MODULE_DEVICE_TABLE(i2c, ov2640_id); // 将ov2640_id注册到i2c设备表中,以便内核能够自动加载驱动程序 // 定义设备树匹配表 static const struct of_device_id ov2640_of_match[] = { {.compatible = "ovti,ov2640", }, // 匹配ovti,ov2640 {}, // 结束符 }; MODULE_DEVICE_TABLE(of, ov2640_of_match); // 将ov2640_of_match注册到设备树匹配表中,以便内核能够自动加载驱动程序 // 定义ov2640的i2c驱动程序 static struct i2c_driver ov2640_i2c_driver = { .driver = { .name = "ov2640", // 驱动程序名为ov2640 .of_match_table = of_match_ptr(ov2640_of_match), // 设置设备树匹配表 }, .probe = ov2640_probe, // 设置探测函数 .remove = ov2640_remove, // 设置反初始化函数 .id_table = ov2640_id, // 设置i2c设备ID };
ov2640_probe
这个函数是用于初始化并探测OV2640摄像头的驱动程序。下面是对该函数的概括总结:
检查所使用的I2C适配器是否支持SMBUS功能,如果不支持则返回错误码。
分配内存并初始化ov2640_priv结构体。
获取摄像头的时钟,如果获取失败则返回延迟探测错误码。
检查是否存在soc_camera_subdev_desc结构体或设备树节点,如果都不存在则返回错误码。
如果不存在soc_camera_subdev_desc结构体,从设备树中获取OV2640的GPIO引脚并进行初始化。
初始化v4l2子设备,使用ov2640_subdev_ops作为操作函数。
初始化v4l2控制器,创建VFLIP和HFLIP控制器。
将控制器绑定到子设备的控制器处理器中。
如果控制器存在错误,则返回错误码。
进行视频探测,初始化摄像头的视频功能。
如果视频探测失败,则释放控制器并释放时钟,并返回错误码。
注册v4l2异步子设备。
如果注册失败,则释放探测的视频,并返回错误码。
打印OV2640已成功探测的消息。
返回0表示探测成功。
如果探测失败,将会在相应的错误标签处释放控制器和时钟,并返回相应的错误码。
static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov2640_priv *priv; struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); // 获取soc_camera_subdev_desc struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); // 获取i2c_adapter int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { // 如果i2c_adapter不支持SMBUS dev_err(&adapter->dev, "OV2640: I2C-Adapter doesn't support SMBUS\n"); // 打印错误信息 return -EIO; // 返回-EIO } priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); // 分配内存 if (!priv) { // 如果分配失败 dev_err(&adapter->dev, "Failed to allocate memory for private data!\n"); // 打印错误信息 return -ENOMEM; // 返回-ENOMEM } priv->clk = v4l2_clk_get(&client->dev, "xvclk"); // 获取时钟 if (IS_ERR(priv->clk)) // 如果获取失败 return -EPROBE_DEFER; // 返回-EPROBE_DEFER if (!ssdd && !client->dev.of_node) { // 如果soc_camera_subdev_desc不存在且设备树节点不存在 dev_err(&client->dev, "Missing platform_data for driver\n"); // 打印错误信息 ret = -EINVAL; // 返回-EINVAL goto err_clk; // 跳转到err_clk } //if (!ssdd) { ret = ov2640_probe_dt(client, priv); // 从设备树中获取ov2640的GPIO引脚并进行初始化 if (ret) // 如果初始化失败 goto err_clk; // 跳转到err_clk //} // 初始化v4l2子设备 v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); // 初始化v4l2控制器 v4l2_ctrl_handler_init(&priv->hdl, 2); // 添加vflip控制器 v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); // 添加hflip控制器 v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); // 设置子设备的控制器 priv->subdev.ctrl_handler = &priv->hdl; // 如果控制器有错误,则返回错误 if (priv->hdl.error) { ret = priv->hdl.error; goto err_clk; } // 进行视频探测 ret = ov2640_video_probe(client); // 如果探测失败,则跳转到err_videoprobe if (ret < 0) goto err_videoprobe; // 注册v4l2异步子设备 ret = v4l2_async_register_subdev(&priv->subdev); // 如果注册失败,则跳转到err_videoprobe if (ret < 0) goto err_videoprobe; // 打印信息 dev_info(&adapter->dev, "OV2640 Probed\n"); // 返回0 return 0; // 如果探测失败,则释放控制器并释放时钟 err_videoprobe: v4l2_ctrl_handler_free(&priv->hdl); err_clk: v4l2_clk_put(priv->clk); // 返回错误码 return ret; }
ov2640_remove
这个函数是用于从i2c_client中获取ov2640_priv结构体,并进行反初始化操作。下面是对该函数的概括总结:
从i2c_client中获取ov2640_priv结构体。
取消v4l2异步子设备的注册。
释放时钟资源。
取消v4l2子设备的注册。
释放控制器资源。
返回0表示反初始化成功。
该函数主要用于释放与OV2640摄像头驱动程序相关的资源,包括时钟、控制器和子设备的注册。
// 从i2c_client中获取ov2640_priv,并进行反初始化
static int ov2640_remove(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); // 获取ov2640_priv v4l2_async_unregister_subdev(&priv->subdev); // 取消v4l2异步子设备的注册 v4l2_clk_put(priv->clk); // 释放时钟 v4l2_device_unregister_subdev(&priv->subdev); // 取消v4l2子设备的注册 v4l2_ctrl_handler_free(&priv->hdl); // 释放控制器 return 0; // 返回0 }
如果文章对您有帮助,点赞👍支持,感谢🤝