LinuxI2C子系统之一实例化IC2设备(Client)的四种方法(一、二)

1680阅读 0评论2016-05-10 bandiyanlei
分类:LINUX

原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb666/article/details/8668670,作者:gqb666

    最近的项目工作中需要调试维护I2C控制的视频采集设备驱动代码,于是花了些时间来研究I2C驱动框架,发现其复杂度不亚于ALSA-ASOC驱动框架(仅个人感觉,勿怪)!并打算将自己的研究心得记录下来,一是自己想看时可以随时找到,二来是帮助后来者少走弯路。第一篇I2C子系统从译文开始:

原文件位置:linux源码目录\Documentation\i2c\instantiating-devices

                       ===========================================

                                                    怎么实例化I2C设备

                      ===========================================

   与PCI与USB总线不同,I2C总线不能在硬件层就将挂在其上的设备枚举出来。相反地,软件必须清楚哪个设备挂在哪个I2C总线上及各个设备的地址ID。所以,内核驱动代码必须显式地“手动”实例化I2C设备。针对不同设备操作的复杂度及项目及其他需要,目前有四种实例化I2C设备的方法:

第一:根据I2C总线号来实例化设备(静态)

   这种方法适用于用I2C作为系统总线的嵌入式设备。在这类系统中,I2C总线会事先确定一个总线号,正是这个原因,就可以提前定义(原文是pre-declare字面意思是声明,但个人感觉是定义,此处值得商榷,还请读者多提宝贵意见,下同)挂在确定了总线号的I2C总线设备了。对I2C总线设备的定义是通过结构体i2c_board_info来完成的,该结构体实例后来会被i2c_register_board_info()函数调用并挂到一个专门管理i2c_board_info结构的链表。

用omap2 h4为例来说明:

[cpp]
  1. static struct i2c_board_info __initdata h4_i2c_board_info[] = { 
  2.     { 
  3.         I2C_BOARD_INFO("isp1301_omap", 0x2d), 
  4.         .irq        = OMAP_GPIO_IRQ(125), 
  5.     }, 
  6.     {   /* EEPROM on mainboard */ 
  7.         I2C_BOARD_INFO("24c01", 0x52), 
  8.         .platform_data  = &m24c01, 
  9.     }, 
  10.     {   /* EEPROM on cpu card */ 
  11.         I2C_BOARD_INFO("24c01", 0x57), 
  12.         .platform_data  = &m24c01, 
  13.     }, 
  14. }; 
  15.  
  16. static void __init omap_h4_init(void
  17.     (...)/* 指定1号总线 */ 
  18.     i2c_register_board_info(1, h4_i2c_board_info, 
  19.             ARRAY_SIZE(h4_i2c_board_info)); 
  20.     (...) 


        上面的代码在1号I2C总线上定义了3个设备,结构中包含了它们的名字(I2C设备总线驱动框架i2c_match_id()函数就是根据这个名字与i2c_driver结构中id_table成员中的名字进行匹配来完成设备与驱动程序的挂接)、设备地址以及驱动程序中要用到的私有数据。当平台驱动要注册I2C总线时,其下面的相应设备也就会被自动地实例化。


    同时,已经被实例化的设备也会在I2C总线被系统释放时被释放或者说被析构,这一般发生在系统关闭时。

第二:显式地实例化设备(动态)

    这种方法适用于I2C总线作为一个大型设备的内部通信通道的情况,一个典型的例子是TV适配器,内部包含调频器、音频解码器、视频解码器,它们往往通过I2C总线同TV适配器主芯片相连。在定义这些子设备的时候,事先不能确定I2C总线号,因此前文所述的方法将不再适用。此时,就应该使用显式地实例化设备这种方法了,这种方法通过填充结构i2c_board_info并调用i2c_new_device函数来实例化设备。

以sfe4001 网络设备驱动为例:

[cpp]
  1. static struct i2c_board_info sfe4001_hwmon_info = { 
  2.     I2C_BOARD_INFO("max6647", 0x4e), 
  3. }; 
  4.  
  5. int sfe4001_init(struct efx_nic *efx) 
  6.     (...) 
  7.     efx->board_info.hwmon_client = 
  8.         i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info); 
  9.  
  10.     (...) 


   上面的代码为sfe4001网络适配器上的I2C总线实例化了一个I2C设备。


   当然这种实现方法还要注意几点差别:(1)当不确定某一I2C总线上是否挂载了I2C设备时(在一个较便宜的板子上某个可选的功能设备,但确定无法单独告知用户)(2)某个I2C设备的设备地址发生了变化(制造商在没有提前通知的情况下改变了它)。当这两种情况发生时,必须用i2c_new_probed_device()函数来代替i2c_new_device()来实例化设备。

以pnx4008 OHCI 驱动代码来说明:

[cpp]
  1. static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; 
  2.  
  3. static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) 
  4.     (...) 
  5.     struct i2c_adapter *i2c_adap; 
  6.     struct i2c_board_info i2c_info; 
  7.  
  8.     (...) 
  9.     i2c_adap = i2c_get_adapter(2); 
  10.     memset(&i2c_info, 0, sizeof(struct i2c_board_info)); 
  11.     strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); 
  12.     isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, 
  13.                            normal_i2c, NULL); 
  14.     i2c_put_adapter(i2c_adap); 
  15.     (...) 


     上面的代码通过i2c_new_probed_device()函数为OHCI适配器上的I2C总线实例化了至少1个I2C设备。首先测试设备地址0X2C,如果没反应则再尝试设备地址0x2D,如果还没反应地话,就会放弃,匹配设备驱动失败。

   实例化I2C设备的驱动程序同样负责在Cleanup时析构设备。析构是通过i2c_new_device()或者i2c_new_probed_device()函数中i2c_unregister_device()来完成的。

另两种方法见后续博文。

  下转:LinuxI2C子系统之一实例化IC2设备(Client)的四种方法(三、四)
上一篇:没有了
下一篇:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)