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

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

2013-10-21 12:18 399 查看
代码中,我添加了很多注释,应该不难理解,有错误大家可以指出来,我再改正

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