I2C从驱动到应用(中篇)



Linux中对I2C的支持很是全面,既提供了内核态的访问方式,也提供了用户态的访问方法。
linux

Linux中对I2C的支持能够分为两个层面,一个是adapteralgorithm,对应的是i2c控制器;再一个是driverclient.Linux内核提供了丰富的接口来实添加i2c设备驱动。要添加一个i2c设备驱动,须要几个固定的步骤。首先,须要往i2c设备列表里添加一组设备ID foo_idtable数据结构

static struct i2c_device_id foo_idtable[] = { ide

{ "foo", my_id_for_foo },函数

{ "bar", my_id_for_bar },spa

{ } code

};orm


MODULE_DEVICE_TABLE(i2c, foo_idtable);接口

而后填充i2c driver的数据结构:ip

static struct i2c_driver foo_driver = { rem

.driver = {

.name   = "foo",

.pm = &foo_pm_ops,  /* optional */

},


.id_table   = foo_idtable,

.probe      = foo_probe,

.remove     = foo_remove,

/* if device autodetection is needed: */

.class      = I2C_CLASS_SOMETHING,

.detect     = foo_detect,

.address_list  = normal_i2c,


.shutdown   = foo_shutdown, /* optional */

.command    = foo_command,  /* optional, deprecated */

}


剩下的就是逐一初始化foo_probe()foo_remove()foo_detect()等函数。一旦准备好了相应的client结构,就能够实现foo_read_valule()foo_write_value()函数,固然这二者都是基于底层公共的i2c/smbus读写函数:i2c_smbus_read/write_byte_data/word()去实现。


若是确实有i2c设备挂载在某个i2c总线上,能够经过填充i2c_board_info数据结构来构造这个设备的实例,而后调用i2c_new_device()来探测实际接入的i2c设备。固然也可能事先没法确认系统上有什么类型的i2c设备,这个时候须要定义一个call back函数,放在probe()后面,让它去确认是否有指定类型的i2c设备挂在系统上。使用完设备后,能够调用i2c_unregister_device()来注销以前注册的设备。固然执行这些全部操做的前提是设备对应的驱动已经被初始化好而且加载到内核,能够参考下面的代码来实现初始化和退出操做:

static int __init foo_init(void)

{  

return i2c_add_driver(&foo_driver);

}

module_init(foo_init);


static void __exit foo_cleanup(void)

{  

i2c_del_driver(&foo_driver);

}

module_exit(foo_cleanup);


The module_i2c_driver() macro can be used to reduce above code.


module_i2c_driver(foo_driver);


若是驱动中,须要向设备发送或者从设备接受数据,能够调用:

int i2c_master_send(struct i2c_client *client, const char *buf, int count);

int i2c_master_recv(struct i2c_client *client, char *buf, int count);

这两组函数来实现,更多的函数在linux/i2c.h中有说明。



固然linux内核中也提供了设备做为从设备的驱动,这中状况下的系统层次图以下所示:

e.g. sysfs      I2C slave events        I/O registers

+-----------+  v    +---------+     v     +--------+  v  +------------+

| Userspace +........+ Backend +-----------+ Driver +-----+ Controller |

+-----------+       +---------+           +--------+     +------------+

| |

----------------------------------------------------------------+-- I2C

--------------------------------------------------------------+---- Bus


不一样于PCI/USB设备,I2C没有提供硬件上自动枚举的能力,所以在初始化i2c设备以前须要显式地指定设备的地址。内核提供了多个显式初始化一个i2c设备的方法:

1、经过bus number声明一个i2c设备:

这种状况尤为适用于i2c做为系统总线的嵌入式系统,以omp2 h4为例,用户须要注册i2c board info:

Example (from omap2 h4):


static struct i2c_board_info h4_i2c_board_info[] __initdata = {

{  

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)

{

(...)

i2c_register_board_info(1, h4_i2c_board_info,

ARRAY_SIZE(h4_i2c_board_info));

(...)

}


2、经过设备树申明一个i2c设备:

这种方式须要把心建的设备节点挂在master conrollor对应的设备树上,也就是说它必须是master controller的子节点,以下面的例子所示:

i2c1: i2c@400a0000 {

/* ... master properties skipped ... */

clock-frequency = <100000>;


flash@50 {

compatible = "atmel,24c256";

reg = <0x50>;

};


pca9532: gpio@60 {

compatible = "nxp,pca9532";

gpio-controller;

#gpio-cells = <2>;

reg = <0x60>;

};

};


3、经过ACPI申明一个I2C设备:

ACPI表里可以申明I2C设备:Documentation/acpi/enumeration.txt


4、显式地实例化i2c设备:

这也是经过填充i2c_board_info数据结构,而后调用i2c_new_device()实现的,以下面的代码所示:

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);


(...)

}

上面的代码在i2c bus上实例化了一个i2c设备。


5、对一些特殊的设备进行自动探测

有的系统上并无提供足够多的关于i2c设备和拓扑信息,这个时候就须要依赖i2c-core去探测设备,这就要求:


  • i2c设备驱动必须实现detect()函数;


  • 只有挂载了这种设备的i2c总线可以且容许被探测。


6、利用/sysfs经过用户态初始化i2c设备

好比咱们须要把位于i2c地址0x50eerom添加到设备当中去,能够直接操做sysfs实现:

echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

相关文章
相关标签/搜索