Linux驱动编程--基于I2C子系统的I2C驱动

代码中,我添加了很多注释,应该不难理解,有错误大家可以指出来,我再改正

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/slab.h>

#include <linux/cdev.h>

#include <linux/i2c.h>

#include <linux/fs.h>



#include <asm/uaccess.h>



#define I2C_MAJOR        365        //主设备号

#define I2C_MINOR        0        //从设备号

#define I2C_COUNT        1        //设备数量



MODULE_LICENSE("Dual BSD/GPL");



/* 函数声明 */

int s5pc100_i2c_probe(struct i2c_client *, const struct i2c_device_id *);

int s5pc100_i2c_remove(struct i2c_client *);

int s5pc100_i2c_open(struct inode *, struct file *);

int s5pc100_i2c_release(struct inode *, struct file *);

ssize_t s5pc100_i2c_read(struct file *, char __user *, size_t, loff_t *);



/* 定义设备结构体 */

typedef struct s5pc100_i2c {

    struct i2c_client client;

    struct cdev cdev;

}s5pc100_i2c;



/* 使用设备结构体 */

s5pc100_i2c *i2c;



/* 设备信息struct i2c_device_id结构体,保存i2c设备的设备信息,由于可能

 * 存在多个i2c设备所以将它定义为一个结构体数组,方便添加新增设备

 struct i2c_device_id {

    char name[I2C_NAME_SIZE];    设备的名字,这是一个宏定义2.6.35中默认是20个字符

    ulong driver_data;            设备的私有数据,可以自己随便填写

 }

 */

struct i2c_device_id i2c_id[] = {

    {

        "lm75", 0

    },

};



/* 构建i2c子系统的设备驱动结构体i2c_driver

 * .driver.name 表示驱动程序的名字,这个名字可以由自己任意取

 * .id_table 是一个struct i2c_device_id的结构体,保存着i2c的设备信息

 struct i2c_device_id {

    char name[I2C_NAME_SIZE];    设备的名字,这是一个宏定义2.6.35中默认是20个字符

    ulong driver_data;            设备的私有数据,可以自己随便填写

 }

 * .probe 表示匹配成功后要执行的函数

 * .remove 表示移除设备的时候要执行的函数 */ 

struct i2c_driver i2c_driver = {

    .driver.name = "lm75",

    .id_table = i2c_id,

    .probe = s5pc100_i2c_probe,

    .remove = s5pc100_i2c_remove,

};



/* 方法绑定结构体 */

struct file_operations i2c_fops = {

    .owner         = THIS_MODULE,

    .open         = s5pc100_i2c_open,

    .release     = s5pc100_i2c_release,

    .read         = s5pc100_i2c_read, 

};



int s5pc100_i2c_open(struct inode * inode, struct file *filp)

{

    printk("open\n");

    return 0;

}



int s5pc100_i2c_release(struct inode * inode, struct file *filp)

{

    printk("close\n");

    return 0;

}



/* 读取设备数据,涉及到struct i2c_msg消息结构体的实现和使用 */

ssize_t s5pc100_i2c_read(struct file *filp, char __user *buf, size_t count, loff_t *loff)

{

    int ret = 0;

    //定义读写缓冲区

    char txbuf[1] = {0};

    char rxbuf[2] = {0};    



    /* i2c设备通讯是通过系统定义的消息结构体实现的,这样可以简化驱动人员的工作,实现驱动的良好移植性.

       struct i2c_msg {

               __u16 addr;   从机地址,从struct i2c_client中获取

             __u16 flags;    标志位,使用下面的宏,其中写数据可以直接传递0进去

            #define I2C_M_TEN       0x0010        代表i2c地址为10位地址数据

            #define I2C_M_RD        0x0001      读数据,从从机到主机

            #define I2C_M_NOSTART       0x4000  if I2C_FUNC_PROTOCOL_MANGLING 

            #define I2C_M_REV_DIR_ADDR  0x2000  if I2C_FUNC_PROTOCOL_MANGLING

            #define I2C_M_IGNORE_NAK    0x1000  if I2C_FUNC_PROTOCOL_MANGLING

            #define I2C_M_NO_RD_ACK     0x0800  if I2C_FUNC_PROTOCOL_MANGLING

            #define I2C_M_RECV_LEN      0x0400  首先受到的字节表示长度

            __u16 len;      消息长度

            __u8 *buf;      指向消息数据的指针

        }; */

    struct i2c_msg msg[2] = {

        { i2c->client.addr, 0, 1, txbuf },

        { i2c->client.addr, I2C_M_RD, 2, rxbuf},

    };



    if (sizeof(rxbuf) != count)

        count = sizeof(rxbuf);



    ret = i2c_transfer(i2c->client.adapter, msg, ARRAY_SIZE(msg));

    if (ret < 0) {

        printk("failure:i2c_transfer\n");

        return -EFAULT;

    }



    if (copy_to_user(buf, rxbuf, count))

        return -EFAULT;



    return count;

}



/* 匹配函数,设备成功配对并取得设备信息struct i2c_client结构体后执行的函数 */

int s5pc100_i2c_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)

{

    int ret = 0;

    dev_t devno = MKDEV(I2C_MAJOR, I2C_MINOR);                //获取设备号



    ret = register_chrdev_region(devno, I2C_COUNT, "i2c");    //注册设备

    if (ret < 0) {

        printk("failure:register_chrdev_region\n");

        return ret;

    }



    /* 申请空间

     * 使用函数void *kmalloc(size_t size, gfp_t flags)

       size 代表申请的内存大小

       flags 表示该函数的操作类型,使用系统提供的宏实现,用到的主要有下面这些:

               GFP_ATOMIC 能申请到内存则申请,不能申请到内存则立即返回错误码

               GFP_KERNEL 能申请则申请,不能申请则睡眠,直到申请到为止

        */

    i2c = (s5pc100_i2c *)kmalloc(sizeof(*i2c), GFP_KERNEL);

    if (NULL == i2c) {

        printk("failure:i2c.kmalloc\n");

        return -ENOMEM;

    }

    /* 设备初始化,将cdev和file_operations绑定,将设备信息和实现方法进行绑定 */

    cdev_init(&i2c->cdev, &i2c_fops);

    i2c->cdev.owner = THIS_MODULE;

    /* 添加设备 */

    ret = cdev_add(&i2c->cdev, devno, I2C_COUNT);

    if (ret < 0) {

        ret = -EFAULT;

        printk("failure:cdev_add\n");

        goto err1;

    }

    /* 导出client,获取系统提供的具体的i2c的设备信息,这样就实现了设备和驱动的关联*/

    i2c->client = *client;



    return 0;



    err1:

        kfree(i2c);

        unregister_chrdev_region(devno, I2C_COUNT);



    return ret;

}



/* remove处理函数 */

int s5pc100_i2c_remove(struct i2c_client *client)

{

    /* 释放需要释放的数据 */

    int ret = 0;

    dev_t devno = MKDEV(I2C_MAJOR, I2C_MINOR);



    cdev_del(&i2c->cdev);

    kfree(i2c);

    unregister_chrdev_region(devno, I2C_COUNT);



    return ret;

}



/* 加载函数 */

static int __init s5pc100_i2c_init(void)

{

    /* 注册i2c的设备驱动

     * 使用函数int i2c_add_driver(struct i2c_driver *i2c_driver)

     * 成功返回0,失败返回错误码 */

    return i2c_add_driver(&i2c_driver);

}



/* 卸载函数 */

static void __exit s5pc100_i2c_cleanup(void)

{

    /* 删除i2c的设备驱动

     * 使用函数void i2c_del_driver(struct i2c_driver *i2c_driver) */

    i2c_del_driver(&i2c_driver);

}



module_init(s5pc100_i2c_init);

module_exit(s5pc100_i2c_cleanup);

你可能感兴趣的:(linux)