在内核中 这边把iic整个流程分成了 4层
iic_dtiver at24_iic_eeprom 也就是我们的自己的驱动
i2c-core.c 核心层
i2c/busses/i2c-s3c2410.c 控制器层
平台总线驱动层,或者也是图中的设备树
硬件描述
我们假设 板子上有三个iic控制器 0 1 2
这里在控制器0 上挂载了gt24c02的eeprom 和一个触摸屏
控制器1 没有挂东西
控制器2 挂了一个触摸屏
第一步,找到iic控制器往平台总线丢
如果是老的内核,开机自启的时候会注册各种控制器的平台总线dev
新的内核就是用设备树 往平台总线上面注册pdev
相似的 这里iic控制器加入到 平台总线中
smdkv210_machine_init()
i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
i2c_board_info smdkv210_i2c_devs0[] __initdata = {
{ I2C_BOARD_INFO("at24c02a", 0x50),}, //总线下挂的iic_dev 名字用于进行匹配, 0x50表示从设备地址
{ I2C_BOARD_INFO("wm8580", 0x1b), },};
for (status = 0; len; len--, info++)//遍历数组,上面这个数组就有两个设备
struct i2c_devinfo *devinfo;
//主要代码,给每个当前适配器下面的 iic从设备构建devinfo结构体
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
devinfo->busnum = busnum;//表示总线编号--0, 1,2
devinfo->board_info = *info;//将数组中成员赋值给节点
list_add_tail(&devinfo->list,& i2c_board_list); //把devinfo结构体注册到链表i2c_board_list
i2c_register_board_info(1, smdkv210_i2c_devs1,ARRAY_SIZE(smdkv210_i2c_devs1));
.name = "samsung,s3c2410-i2c"
i2c_register_board_info(2, smdkv210_i2c_devs2, ARRAY_SIZE(smdkv210_i2c_devs2));
platform_add_device(&smdkv210_devs,ARRAY_SIZE(smdkv210_devs)); //这里注册各种板子的控制器到平台总线
smdkv210_i2c_devs1 //看来没有设备树的时候,各种控制器的dev都是在init的时候丢进,plantform中的pdev链表
smdkv210_dm9000 //就是pdev,硬件资源信息
smdkv210_fb //就是pdev,硬件资源信息
这里得到一个i2c_board_list链表 有当前各个从iic设备的信息
同时在平台总线上注册了 三个pdev 对应的是三个iic控制器
第二步,创建iic总线
进入iic核心层
drivers/i2c/i2c-core-base.c
这里很简单就是构造了iic总线,提供了一些匹配规则
让iic控制器和iic驱动进行匹配
postcore_initcall(i2c_init); //编译到内核,自动注册这个驱动
static int __init i2c_init(void)
retval = bus_register(&i2c_bus_type);//core中注册了iic总线,这个函数自动构建出一个dev链表一个drv链表
retval = i2c_add_driver(&dummy_driver); //总线中增加了驱动-dummy_driver
static struct i2c_driver dummy_driver = { //这个driver只是一个模板,教你iic驱动怎么写
.driver.name = "dummy",
.probe = dummy_probe,
.remove = dummy_remove,
}
note: 总线注册bus_register()
设备注册 device_regisiter(结构体)//结构体里有个属性表示自己是哪个总线的
驱动注册 driver_regisiter(结构体)//结构体里有个属性表示自己是哪个总线的
第三步平台总线匹配
iic 控制器层
i2c/busses/i2c-s3c2410.c
需要把自己控制器drv 丢到平台总线的pdrv 链表中
subsys_initcall(i2c_adap_s3c_init); //编译到内核,自动注册这个驱动
i2c_adap_s3c_init(void)
platform_driver_register(&s3c24xx_i2c_driver); //注册这个平台驱动
.of_match_table = of_match_ptr(s3c24xx_i2c_match),
.name = "samsung,s3c2410-i2c" //可以匹配上面的三个pdev
.probe = s3c24xx_i2c_probe,}
第四步,控制器层的probe调用,注册iic控制器到iic_dev链表中
这里匹配成功,创建了图中的iic适配器结构体
里面有相应的算法和,对这个iic控制器的操作方法 同时把这个iic_adap 要注册到 iic_core的 iic_dev链表
s3c24xx_i2c_probe(struct platform_device *pdev)
struct s3c24xx_i2c *i2c; //创建了一个iic全局变量
i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
//对这个iic的全局变量初始化 i2c->adap 适配器和算法
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm; //指定了算法
.master_xfer = s3c24xx_i2c_xfer, //算法里有每个iic_控制器的操作方法
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_DEPRECATED;
i2c->tx_setup = 50;
i2c_add_adapter(struct i2c_adapter *adapter);
因为构建了 iic_adap 所以iic_adap 也有编号
iic_adap 要注册到 core的 iic_dev链表
第五步,控制器层的probe调用,遍历适配器下面的从设备
i2c_add_adapter(struct i2c_adapter *adapter) 继续上面的函数
这个函数不仅注册iic控制器到iic_dev链表中
在里面还把 这个控制器下面的 iic从设备初始化为client结构体
并且把这个client结构体的父类dev 注册到iic_dev链表中
i2c_add_adapter(struct i2c_adapter *adapter)
i2c_register_adapter(adapter);
dev_set_name(&adap->dev, "i2c-%d", adap->nr); //设置里面dev的名字叫i2c-0
adap->dev.bus = &i2c_bus_type; //这个dev是要放入,iicbus总线的
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev); //注册这个adap到iic总线
i2c_scan_static_board_info(adap);
struct i2c_devinfo *devinfo;//构建devinfo结构体
list_for_each_entry(devinfo, &__i2c_board_list, list) //i2c_board_list链表找到 这个适配器下的iic从设备
devinfo->busnum == adapter->nr && !i2c_new_device(adapter,&devinfo->board_info)
client = kzalloc(sizeof *client, GFP_KERNEL);//创建一个client
client->adapter = adap;//链接这个client对应的适配器
client->addr = info->addr; //拿到了i2c_board_info 里面的从设备地址
i2c_dev_set_name(adap, client, info); //设置 这个dev在总线的名字 /sys/bus/i2c/devices/0-0050
client->dev.bus = &i2c_bus_type; //这个client的总线是iic总线
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),i2c_encode_flags_to_addr(client)); //名字是适配器编号-i2c设备的地址
status = device_register(&client->dev); //从设备也注册到iic总线
第六步,构建iic从设备driver,这个要自己写
把自己的写的iic_drv 放入iic总线中,用于匹配 iic_dev
从而调用drv的probe函数
struct i2c_driver at24_drv = {
.probe = at24_drv_probe,
.remove = at24_drv_remove,
.id_table = at24_id_table,//用来比对name符合匹配规则
};
module_init(at24_drv_init);
static int __init at24_drv_init(void)
return i2c_add_driver(&at24_drv);//注册到iic_drv总线中用于匹配
第七步,构建iic从设备driver,这个要自己写,里面的probe函数开始启动
自己写的驱动弄一个结构体at24_dev,记录一些信息
比如图中的字符设备驱动的结构体dev 类 主次设备号等
还有对应的client
at24_drv_probe(struct i2c_client *client, const struct i2c_device_id *id);
at24_dev = kzalloc(sizeof(struct i2c_e2prom), GFP_KERNEL); //自己写的驱动弄一个结构体at24_dev,记录一些信息
at24_dev->dev_major = register_chrdev(0,"at24_drv", &at24_fops); //创建了字符设备驱动,注册fops
at24_dev->cls = class_create(THIS_MODULE, "at24_cls"); //自己写的驱动创建了一个类
at24_dev->dev = device_create(at24_dev->cls, NULL,MKDEV(at24_dev->dev_major, 0),NULL, "at24_e2prom"); //注册字符设备驱动
//记录当前client
at24_dev->client = client;
// 硬件初始化 ---e2prom只要上电就可以功能
第八步应用程序使用read/write读取写iic数据
把发过来的数据组成数据包,进行发送给这个iic适配器
.write = at24_e2prom_drv_write //fops中的wirte函数
at24_e2prom_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
char *tmp = kzalloc(count, GFP_KERNEL);//分配结构体读取用户空间写下来的数据
ret = copy_from_user(tmp, buf, count);
ret = at24_i2c_write(at24_dev->client, tmp, count);
struct i2c_adapter *adapter = client->adapter; //拿到适配器
struct i2c_msg msg; //组一个iic数据包
msg.addr = client->addr; //对应的iic从设备地址
msg.flags = 0;
msg.len = size;
msg.buf = buf;
ret = i2c_transfer(adapter, &msg, 1);//发送数据
自己写的驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include <asm/uaccess.h>
// 全局的设备对象
struct i2c_e2prom{
int dev_major;
struct class *cls;
struct device *dev;
struct i2c_client *client;//记录当前匹配的client
};
struct i2c_e2prom *at24_dev;
//编写一个类似i2c_master_recv/i2c_master_send
int at24_i2c_read(struct i2c_client *client, char *buf, int size)
{
int ret;
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = I2C_M_RD;
msg.len = size;
msg.buf = buf;
// 参数1---适配器
//参数2--消息包
// 参数3--消息的个数
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?size:ret;
}
int at24_i2c_write(struct i2c_client *client, char *buf, int size)
{
int ret;
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = 0;
msg.len = size;
msg.buf = buf;
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?size:ret;
}
int at24_e2prom_drv_open (struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t at24_e2prom_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
printk("-----------%s-----------\n", __FUNCTION__);
int ret;
if(count < 0 || count > 256)
return -EINVAL;
char *tmp = kzalloc(count, GFP_KERNEL);
// 1, 从硬件中获取数据
ret = at24_i2c_read(at24_dev->client, tmp, count);
if(ret < 0)
{
printk("at24_i2c_read error\n");
goto err_free;
}
// 2 ,将数据给用户
ret = copy_to_user(buf, tmp, count);
if(ret > 0)
{
printk("copy_to_user error\n");
goto err_free;
}
kfree(tmp);
return count;
err_free:
kfree(tmp);
return ret;
}
ssize_t at24_e2prom_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
printk("-----------%s-----------\n", __FUNCTION__);
int ret;
if(count < 0 || count > 256)
return -EINVAL;
char *tmp = kzalloc(count, GFP_KERNEL);
// 1, 从用户空间将数据获取到
ret = copy_from_user(tmp, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
goto err_free;
}
// 2, 将数据写入硬件中去
ret = at24_i2c_write(at24_dev->client, tmp, count);
if(ret < 0)
{
printk("at24_i2c_write error\n");
goto err_free;
}
kfree(tmp);
return count;
err_free:
kfree(tmp);
return ret;
}
int at24_e2prom_drv_close(struct inode *inode, struct file *filp)
{
return 0;
}
const struct file_operations at24_fops = {
.open = at24_e2prom_drv_open,
.read = at24_e2prom_drv_read,
.write = at24_e2prom_drv_write,
.release = at24_e2prom_drv_close,
};
int at24_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("-----id->name = %s, id->driver_data = 0x%x\n",id->name, id->driver_data);
/*
// 申请设备号
// 创建设备文件
// 硬件初始化
// 实现fops
*/
at24_dev = kzalloc(sizeof(struct i2c_e2prom), GFP_KERNEL);
at24_dev->dev_major = register_chrdev(0,"at24_drv", &at24_fops);
at24_dev->cls = class_create(THIS_MODULE, "at24_cls");
at24_dev->dev = device_create(at24_dev->cls, NULL,
MKDEV(at24_dev->dev_major, 0),NULL, "at24_e2prom");
//记录当前client
at24_dev->client = client;
// 硬件初始化 ---e2prom只要上电就可以功能
// i2c系统中为从设备传输数据的方法
return 0;
}
int at24_drv_remove(struct i2c_client *client)
{
device_destroy(at24_dev->cls, MKDEV(at24_dev->dev_major, 0));
class_destroy(at24_dev->cls );
unregister_chrdev(at24_dev->dev_major, "at24_drv");
kfree(at24_dev);
return 0;
}
const struct i2c_device_id at24_id_table[] = {
{"at24c02a", 0x2222},
{"at24c04a", 0x4444},
{"at24c08a", 0x8888},
};
struct i2c_driver at24_drv = {
.probe = at24_drv_probe,
.remove = at24_drv_remove,
.driver = {
.name = "at24_e2prom_drv", //不会用于比对
// /sys/bus/i2c/drivers/at24_e2prom_drv
},
.id_table = at24_id_table,
};
static int __init at24_drv_init(void)
{
//注册一个i2c driver
return i2c_add_driver(&at24_drv);
}
static void __exit at24_drv_exit(void)
{
i2c_del_driver(&at24_drv);
}
module_init(at24_drv_init);
module_exit(at24_drv_exit);
MODULE_LICENSE("GPL");