前言:抽时间看了一下I2C适配器驱动等源码,写点自己的理解
LinuxI2c驱动中包含3个部分
1:I2C核心----就是些通用的API
2:I2C总线驱动,在目录drivers/busses/下,主要内容在i2c-s3c2410.c
3:I2C设备驱动
从I2C适配器注册开始进入I2C讲解
---------------------------------------------------------
I2C适配器进行platform平台注册
---------------------------------------------------------
I2C适配器驱动主要在drivers/busses/下,找到目录下的i2c-s3c2410.c文件,第1020行是对I2C适配器进行platform平台注册:
platform_driver_register(&s3c24xx_i2c_driver);
其流程与简单字符设备驱动进行platform平台注册流程一样,所以的要求s3c24xx_i2c_driver中的driver.name与I2C适配器资源描述中的name要要一致,I2C适配器资源描述在文件在arch\arm\plat-samsung(因为I2C适配器为公司级别的,非具体芯片级别)中dev-i2c0.c第40行,其在arch\arm\mach-s5pc100中的mach-smdkc100.c的第328行加入了资源链表中进行注册。
当I2C适配器驱动和I2C适配器匹配后,会调用I2C适配器驱动中的probe函数,此函数在drivers/busses/i2c-s3c2410.c中的第790行
static ints3c24xx_i2c_probe(struct platform_device *pdev)
第一阶段结束
---------------------------------------------------------
I2C适配器驱动中的probe函数的执行
---------------------------------------------------------
I2C适配器驱动中的probe函数主要完成如下几个工作(宋宝华驱动详解第二版351页)
1):创建一个I2C适配器(实际创建的是一个含有I2C适配器的私有数据struct s3c2xx_i2ci2c,其中含有I2C适配器);
2):第一次填充i2c中的元素;
3):获取platform平台注册时传入的资源,然后将资源中的中断申请、内存申请映射等,初始化控制器,第二次填充i2c中的元素
4):注册这个I2C适配器
i2c_add_numbered_adapter(&i2c->adap);
这个步骤中会注册I2C-client (将I2C-client的资源加入链表,此资源事先已经写好,在文件arch/arm/mach-s5pc100/smdk100.c中的第249行,然后将I2C-client成员赋值,资源也赋给I2C-client的成员,所以I2C-driver中的probe函数参数为:
static int lm75_probe(struct i2c_client*client, const struct i2c_device_id *id)
)
备注:一般platform平台注册中prber函数的参数platform_device *pdev,其实设备的资源在此之前已经赋值给了pdev
当I2C-driver运用platform平台注册时会匹配I2C-client中的driver.name字段,匹配完成会执行I2C-driver中的probe函数,进行字符设备的注册
---------------------------------------------------------
I2C-driver中的probe函数的执行
---------------------------------------------------------
主要进行字符设备的注册,和一般的注册没什么区别
---------------------------------------------------------
一个简单的I2C-driver
---------------------------------------------------------
/*
最后会发现此驱动只不过是用platform平台进行了字符设备的注册,注册好后,完善字符驱动的oper操作函数,其中的read作用是:组织一次mesg,发送并接受返回的值,传给用户 空间即结束,但之前分析的各种注册是需要的
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
MODULE_LICENSE("Dual BSD/GPL");
#defineLM75_REG_CONF 0x01
staticconst u8 LM75_REG_TEMP[3] = {
0x00,
0x03,
0x02,
};
structlm75_data
{
u16 temp[3];
};
staticint lm75_major = 250;
staticint lm75_minor = 0;
staticint number_of_devices = 1;
staticdev_t devno = 0;
staticstruct cdev cdev;
staticstruct i2c_client *new_client;
structlm75_data *data;
static int lm75_read_value(struct i2c_client *client){
struct i2c_msg msgs[2];
int status;
char buf1[2];
char buf2[2];
msgs[0].len = 1;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].buf = buf1;
msgs[0].buf[0] = LM75_REG_TEMP[0];
msgs[1].len = 2;
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].buf = buf2;
status =i2c_transfer(client->adapter,msgs, 2); //通过哪个适配器发送?在client->adapter.nr保存的是此适配器的编号,i2c_transfer会调用适配器中的i2c_algorithm中得master_xfer,此接口会把mesg发送出去(一个mesg中有多个信息会通过中断推进发送)
if(status < 0)
return status;
return (buf2[0] << 8) | buf2[1];
}
static ssize_t lm75_read(struct file *file, char__user *buff, size_t count, loff_t *offset){
int status;
status = lm75_read_value(new_client);
if(status < 0)
return status;
printk("status = %x\n", status);
if(copy_to_user(buff, (char *)&status,sizeof(status)))
return -EFAULT;
return 0;
}
static int lm75_open(struct inode *inode, struct file*file){
return 0;
}
static int lm75_release(struct inode *inode, structfile *file){
return 0;
}
staticstruct file_operations lm75_fops = {
.owner =THIS_MODULE,
.read = lm75_read,
.open = lm75_open,
.release = lm75_release,
};
static int lm75_probe(struct i2c_client *client, conststruct i2c_device_id *id){
int ret = 0;
new_client = client;//将client保存下来,其他函数要用
printk(KERN_ERR"%s %s %d\n",__FILE__, __func__, __LINE__);
if(!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) //看看此适配器的功能
return -EIO;
//----------------下面的可加可不加,client中可以添加私有数据---
data = kzalloc(sizeof(struct lm75_data),GFP_KERNEL);
if(!data){
return -ENOMEM;
}
i2c_set_clientdata(client, data);
//-------------------------------------------------------
devno = MKDEV(lm75_major, lm75_minor);
ret = register_chrdev_region(devno,number_of_devices, "lm75");
if(ret){
printk("failed to register devicenumber\n");
goto err_register_chrdev_region;
}
cdev_init(&cdev, &lm75_fops);
ret = cdev_add(&cdev, devno,number_of_devices);
if(ret){
printk("failed to adddevice\n");
goto err_cdev_add;
}
return 0;
err_cdev_add:
unregister_chrdev_region(devno,number_of_devices);
err_register_chrdev_region:
kfree(data);
return ret;
}
static int lm75_remove(struct i2c_client *client){
cdev_del(&cdev);
unregister_chrdev_region(devno,number_of_devices);
return 0;
}
enumlm75_type {
lm75,
lm75a,
};
staticconst struct i2c_device_id lm75_ids[] = {
{ "lm75", lm75, },
{ "lm75a",lm75a, },
{ /* LISTEND */ }
};
staticstruct i2c_driver lm75_driver = {
.driver ={
.name = "lm75",
},
.probe = lm75_probe,
.remove = lm75_remove,
.id_table = lm75_ids,
};
static int __init s5pc100_lm75_init(void){
return i2c_add_driver(&lm75_driver);
}
static void __exit s5pc100_lm75_exit(void){
i2c_del_driver(&lm75_driver);
}
module_init(s5pc100_lm75_init);
module_exit(s5pc100_lm75_exit);