首先呢, linux的i2c主要有三层:i2c核心层 i2c总线层 i2c设备层
i2c核心是内核为我们提供的, 它主要的作用就是提供了
由于它是内核提供的,那么我们要做的的驱动呢, 分为两块, 一个就是i2c总线的驱动 另一个就是i2c设备的驱动,
i2c总线的驱动主要是对硬件体系结构的适配器的实现, 说白了, 就是传输的那些具体的传输的开始信号, 停止信号, ack等的设置(当然还有其他的....)
而, 今天分析的并不分析i2c总线的驱动, 我们的总线驱动可以利用现成的那就是i2c-s3c2410.c这个函数.
今天分析的主要是i2c设备驱动,
i2c设备驱动主要有两个结构体:
i2c_client 和
i2c_driver
i2c_driver是一套驱动方法,用于辅助作用的数据结构,不对应任何物理实体。
i2c_client对应真实的物理设备,每个I2C设备都需要一个i2c_client来描述。
在2.6内核已经没有了以前的legacy model和standard drivermodel的驱动之分, 具体新方法查看:
http://blog.csdn.net/yuanlulu/article/details/6557901
今天分析的是使用 注册 i2c_board_info 让 i2c核心帮我们创建i2c_client , 这样的话在i2c_driver注册的时候就会调用 其指定的probe函数.
首先我们先写个最简单的i2c设备驱动, 主要是能够让i2c 核心帮我们匹配到设备:
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "zhutoubenben"
#define DRIVER_DESC "i i c iic_at24c08..."
#define I2C_DEVICE "24c08"
static int i2c_at24c08_probe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id){
printk("i2c_at24c08_probe , probe the device ..\n");
return 0;
}
static int i2c_at24c08_remove(struct i2c_client *i2c_client){
printk("i2c_at24c08_remove , remove the device ..\n");
return 0;
}
struct i2c_device_id at24c08_id[] = {
{I2C_DEVICE,0},{} // 这里会和i2c_client的名字匹配,一样的话就调用probe
};
MODULE_DEVICE_TABLE(i2c, at24c08_id);
static struct i2c_driver i2c_at24c08_driver = {
.driver = {
.name = I2C_DEVICE
},
.probe = i2c_at24c08_probe,
.remove = i2c_at24c08_remove,
.id_table = at24c08_id,
};
static int __init i2c_at24c08_init(void){
int result;
result = i2c_add_driver(&i2c_at24c08_driver); // 注册i2c_driver
if (result)
printk("i2c_add_driver failed. Error number %d", result);
return result;
}
static void __exit i2c_at24c08_exit(void){
i2c_del_driver(&i2c_at24c08_driver);
}
module_init(i2c_at24c08_init);
module_exit(i2c_at24c08_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
接下来我们得在mach-mini2440.c下添加:
static struct i2c_board_info __initdata mini2440_i2c_board_info[] = {
{
I2C_BOARD_INFO("24c08", 0x50),
},
};
在 mini2440_machine_init()函数下添加:
i2c_register_board_info(0, mini2440_i2c_board_info,
ARRAY_SIZE(mini2440_i2c_board_info));
// 那么接下来我们先分析系统启动的时候 我们的i2c_client怎么来的呢?
// 接下来这一整段主要是系统启动的时候 , 当s3c2410-i2c平台设备和驱动都加载时,调用driver中的probe
// 而其中会分析到i2c_client的分配
s3c24xx_i2c_probe
i2c_add_numbered_adapter
i2c_register_adapter
device_register //
i2c_scan_static_board_info
list_for_each_entry
if (devinfo->busnum == adapter->nr \
&& !i2c_new_device(adapter, &devinfo->board_info))//分配i2c_client, 并指定adapter
bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);// 此时i2cbus还没有driver,不调用
i2c_add_driver
i2c_register_driver
driver_register // 注册驱动, 接下来就是通用的sysfs下的驱动添加到bus下的匹配方法
bus_add_driver
driver_attach
__driver_attach
driver_probe_device
really_probe
//到此的
really_probe{
...
if (dev->bus->probe) {
ret = dev->bus->probe(dev);//调用bus的probe
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
...
}
/*
if (dev->bus->probe)判断bus是否有probe方法,
而我们的i2c bus是有probe方法的, 即i2c_device_probe
最后调用driver->probe(client, i2c_match_id(driver->id_table, client));//此处的client是上面分析的创建的那个
也就是我们自己的probe函数了
*/
i2c驱动的数据结构图:
接下来具体读写的主要在: http://blog.csdn.net/zhutoubenben/article/details/8007432
参考文献:
http://blog.csdn.net/sadamoo/article/details/7968011
http://blog.csdn.net/yuanlulu/article/details/6557901
http://blog.csdn.net/yuanlulu/article/details/6559252
内核源码at24.c