本文转载:http://blog.csdn.net/airk000/article/details/21345457
在Linux驱动中I2C系统主要包括以下成员:
I2C adapter 即I2C适配器 I2C driver 某个I2C可以由设备驱动driver理解。 I2C client 某个I2C设备的设备声明可以device理解。
I2C adapter
是CPU集成或外接的I2C控制各种适配器I2C从设备上看,其驱动需要完成对适配器的完整描述,最重要的工作是完成i2c_algorithm结构体。这个结构体包含了此I2C具体实现控制器的数据传输,并向外界报告该设备支持的功能类型。i2c_algorithm结构如下:
struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); u32 (*functionality) (struct i2c_adapter *); };
如果一个I2C不支持适配器I2C通道,然后就会master_xfer成员设为NULL。若支持适配器SMBUS协议需要实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议将通过I2C模拟通道。master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,问这个I2C什么功能支持主控器?
在内核的drivers/i2c/i2c-stub.c实现了一个i2c adapter实现的例子更为复杂SMBUS。
SMBus 与 I2C的区别
通常,I2C和SMBus它是兼容的,但仍然有一些微妙的区别。
时钟速度对比:
I2C | SMBus | |
---|---|---|
最小 | 无 | 10kHz |
最大 | 100kHZ(标准)400kHz(快速模式)2MHz(高速模式) | 100kHz |
超时 | 无 | 35ms |
它们在电气特性上也有所不同,SMBus电压范围较低。
I2C driver
具体的I2C大多数常见的硬件设备,如相机、传感器、触摸屏和背光控制器,都是通过I2C协议与主机传输和控制数据。结构如下:
struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared or is about to be * removed. You should avoid using this, it will be removed in a * near future. */ int (*attach_adapter)(struct i2c_adapter *) __deprecated; //旧接口函数绑定设备 int (*detach_adapter)(struct i2c_adapter *) __deprecated; ///旧接口函数与设备解绑 /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); ///与相应设备绑定的通用接口函数 int (*remove)(struct i2c_client *); ///现行与相应设备解绑的接口函数 /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); ///关闭设备 int (*suspend)(struct i2c_client *, pm_message_t mesg); //挂设备与电源管理有关 int (*resume)(struct i2c_client *); //从悬挂状态恢复 /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; //I2C设备驱动模型 const struct i2c_device_id *id_table; //匹配设备列表 /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) ///一般在编写驱动过程中,对象往往是driver可通过类型to_i2c_driver找到父亲的类型i2c_driver
就像普通设备可以驱动多个设备一样,一个I2C driver也可对应多个I2C client。
重力传感器AXLL34X例如,实现了I2C驱动为:
static const struct i2c_device_id adxl34x_id[] = { { "adxl34x", 0 }, //匹配i2c client名为adxl34x的设备 { } }; MODULE_DEVICE_TABLE(i2c, adxl34x_id); static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, //指定设备驱动的电源管理接口,包括suspend、resume }, .probe = adxl34x_i2c_probe, ///组装设备匹配时的匹配动作 .remove = adxl34x_i2c_remove, ///组装设备移除接口 .id_table = adxl34x_id, ///制定匹配设备列表 }; module_i2c_driver(adxl34x_driver);
这里要解释一下module_i2c_driver宏定义(i2c.h):
#define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, \ i2c_del_driver) #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)
module_driver():
#define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__diver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:
static int __int adxl34x_driver_init(void)
{
return i2c_register_driver(&adxl34x_driver);
}
module_init(adxl34x_driver_init);
static void __exit adxl34x_driver_exit(void)
{
return i2c_del_driver(&adxl34x_driver);
}
module_exit(adxl34x_driver_exit);
这一句宏就解决了模块module安装卸载的复杂代码。这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。
I2C client
即I2C设备。I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义:
struct i2c_client {
unsigned short flags; //I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错
unsigned short addr; //设备从地址,7bit。这里说一下为什么是7位,因为最后以为0表示写,1表示读,通过对这个7bit地址移位处理即可。addr<<1 & 0x0即写,addr<<1 | 0x01即读。
char name[I2C_NAME_SIZE]; //从设备名称
struct i2c_adapter *adapter; //此从设备依附于哪个adapter上
struct i2c_driver *driver; // 此设备对应的I2C驱动指针
struct device dev; // 设备模型
int irq; // 设备使用的中断号
struct list_head detected; //用于链表操作
};
#define to_i2c_client(d) container_of(d, struct i2c_client, dev) //通常使用device设备模型进行操作,可以通过to_i2c_client找到对应client指针
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //设备名,最长20个字符,最终安装到client的name上
unsigned short flags; //最终安装到client.flags
unsigned short addr; //设备从地址slave address,最终安装到client.addr上
void *platform_data; //设备数据,最终存储到i2c_client.dev.platform_data上
struct dev_archdata *archdata;
struct device_node *of_node; //OpenFirmware设备节点指针
struct acpi_dev_node acpi_node;
int irq; //设备采用的中断号,最终存储到i2c_client.irq上
};
//可以看到,i2c_board_info基本是与i2c_client对应的。
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
//通过这个宏定义可以方便的定义I2C设备的名称和从地址(别忘了是7bit的)
下面还是以adxl34x为例:
static struct i2c_board_info i2c0_devices[] = {
{
I2C_BOARD_INFO("ak4648", 0x12),
},
{
I2C_BOARD_INFO("r2025sd", 0x32),
},
{
I2C_BOARD_INFO("ak8975", 0x0c),
.irq = intcs_evt2irq(0x3380), /* IRQ28 */
},
{
I2C_BOARD_INFO("adxl34x", 0x1d),
.irq = intcs_evt2irq(0x3340), /* IRQ26 */
},
};
...
i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
这样ADXL34X的i2c设备就被注册到了系统中,当名字与i2c_driver中的id_table中的成员匹配时就能够出发probe匹配函数了。