linux下I2C驱动的开发

前言:抽时间看了一下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

#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);



 

你可能感兴趣的:(linux驱动及系统)