您的位置:首页 > 运维架构 > Linux

linux下I2C驱动的开发

2013-12-21 20:34 387 查看
前言:抽时间看了一下I2C适配器驱动等源码,写点自己的理解

日期:2013-12-21 王超群

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<linux/clk.h>

#include<linux/module.h>

#include<linux/kernel.h>

#include<linux/init.h>

#include<linux/fs.h>

#include<linux/cdev.h>

#include<asm/uaccess.h>

#include<linux/device.h>

#include<linux/wait.h>

#include<linux/interrupt.h>

#include<asm/io.h>

//#include<mach/regs-gpio.h>

#include<linux/i2c.h>

#include<linux/slab.h>

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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: