您的位置:首页 > 运维架构 > Linux

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

2013-03-20 11:38 357 查看
原创作品,转载时请务必以超链接形式标明文章原始出处: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为例来说明:

static struct i2c_board_info __initdata h4_i2c_board_info[] = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq		= OMAP_GPIO_IRQ(125),
},
{	/* EEPROM on mainboard */
I2C_BOARD_INFO("24c01", 0x52),
.platform_data	= &m24c01,
},
{	/* EEPROM on cpu card */
I2C_BOARD_INFO("24c01", 0x57),
.platform_data	= &m24c01,
},
};

static void __init omap_h4_init(void)
{
(...)/* 指定1号总线 */
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(...)
}

        上面的代码在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 网络设备驱动为例:

static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
(...)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

(...)
}

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

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

以pnx4008 OHCI 驱动代码来说明:

static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
{
(...)
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;

(...)
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(...)
}

      上面的代码通过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)的四种方法(三、四)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息