====本文系本站原创,欢迎转载! 转载请注明出处:http://blog.csdn.net/yyplc====
本文基于内核版本linux-2.6.30.4分析.
Linux中i2c子系统框图如下:
•客户驱动
即I2C设备驱动,每个客户驱动可以为一个或多个I2C设备服务,并向用户空间提供相应的访问接口函数。
客户驱动需要向总线注册并通过i2c-core提供的接口与I2C设备通信。与客户程序相关的数据结构主要有struct i2c_driver和struct i2c_client
添加客户驱动:static inline int i2c_add_driver(struct i2c_driver *driver)
删除客户驱动:extern void i2c_del_driver(struct i2c_driver *);
•I2C-core核心
I2c-core核心是I2C总线的核心,承上启下,其实现与平台无关。I2C总线的初始化、注册和适配器添加和注销等相关工作均在I2C核心代码中完成,
除此之外,还向上层客户驱动提供相应的API接口函数,使得客户驱动独立于适配器驱动而工作。
• 适配器驱动
负责对I2C控制器的驱动实现,一个总线对应一个适配器。I2C总线上对I2C slave设备的具体
操作是在适配器驱动中完成的。适配器驱动作为platform_driver而注册,在probe()到驱动设
备后,向总线声明并被添加:i2c_add_numbered_adapter(&i2c->adap);
I2c-dev驱动是系统自带的一个通用客户驱动,它不是针对某一个I2C设备(即没有自己驱动设备id-table),它可以使得用户空间的程序通过i2c-tools访问总线上的i2c/SMBUS设备。
I2C子系统主要数据结构
- •struct i2c_msg;
-
- •struct i2c_algorithm;
-
- •struct i2c_adapter;
-
- •struct i2c_client;
-
- •struct i2c_driver;
-
- •struct i2c_board_info;
-
以上数据结构在include/linux/i2c.h中定义,下面我们结合源码分析一下这些数据结构
I2C算法结构:
- struct i2c_algorithm {
-
-
-
-
-
-
-
-
-
-
-
-
-
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
-
- int num);
-
- int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
-
- unsigned short flags, char read_write,
-
- u8 command, int size, union i2c_smbus_data *data);
-
-
-
-
-
- u32 (*functionality) (struct i2c_adapter *);
-
- };
例如:
s3c2440 i2c 适配器驱动是这样实现这个算法结构体:
- static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
-
- .master_xfer = s3c24xx_i2c_xfer,
-
- .functionality = s3c24xx_i2c_func,
-
- };
s3c24xx_i2c_xfer 是适配器的对I2C设备的读写操作函数
s3c24xx_i2c_func函数决定了该适配器向i2c-core核心支持哪些API函数
I2c_adapter结构标识一个物理I2C总线(适配器),总线通过
算法结构访问到适配器。
- struct i2c_adapter {
-
- struct module *owner;
-
- unsigned int id;
-
- unsigned int class;
-
- const struct i2c_algorithm *algo;
-
- void *algo_data;
-
-
-
-
-
- int (*client_register)(struct i2c_client *) __deprecated;
-
- int (*client_unregister)(struct i2c_client *) __deprecated;
-
-
-
-
-
- u8 level;
-
- struct mutex bus_lock;
-
- struct mutex clist_lock;
-
-
-
- int timeout;
-
- int retries;
-
- struct device dev;
-
-
-
- int nr;
-
- struct list_head clients;
-
- char name[48];
-
- struct completion dev_released;
-
- };
I2c_msg是I2C消息,I2C通信是以i2c_msg为单位的。
- struct i2c_msg {
-
- __u16 addr;
-
- __u16 flags;
-
- #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
-
- #define I2C_M_RD 0x0001 /* read data, from slave to master */
-
- #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 /* length will be first received byte */
-
- __u16 len;
-
- __u8 *buf;
-
- };
I2c_client描述了一个i2c设备。
- struct i2c_client {
-
- unsigned short flags;
-
- unsigned short addr;
-
-
-
-
-
- char name[I2C_NAME_SIZE];
-
- struct i2c_adapter *adapter;
-
- struct i2c_driver *driver;
-
- struct device dev;
-
- int irq;
-
- struct list_head list;
-
- struct list_head detected;
-
- struct completion released;
-
- };
I2c_driver描述了I2C设备的驱动。
- struct i2c_driver {
-
- int id;
-
- unsigned int class;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int (*attach_adapter)(struct i2c_adapter *);
-
- int (*detach_adapter)(struct i2c_adapter *);
-
-
-
-
-
-
-
-
-
-
-
-
-
- int (*detach_client)(struct i2c_client *) __deprecated;
-
- 。。。
-
- }
I2C核心提供的API函数
- • extern int i2c_master_send(struct i2c_client *client, const char *buf,
-
- • int count);
-
- • extern int i2c_master_recv(struct i2c_client *client, char *buf, int count);
-
-
-
- <span style="color:#ff6666;">• extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
-
- • int num);
- </span>
- • extern s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
-
- • unsigned short flags, char read_write, u8 command,
-
- • int size, union i2c_smbus_data *data);
-
- • extern s32 i2c_smbus_read_byte(struct i2c_client *client);
-
- • extern s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);
-
- • extern s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
-
- • extern s32 i2c_smbus_write_byte_data(struct i2c_client *client,
-
- • u8 command, u8 value);
-
- • extern s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
-
- • extern s32 i2c_smbus_write_word_data(struct i2c_client *client,
-
- • u8 command, u16 value);
其中: i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 是实现其他函数的基础,换句话说,其它函数均是通过调用该函数来实现的
编写客户驱动的方法
在内核中有两种方式的i2c客户驱动的编写方法,一种叫legacy传统方式,另一种是newstyle方式. 前
一种legacy是一种旧式的方法,在2.6内核以后的标准驱动模型编写中逐渐被newstyle方式取代。本文编程实例是基于newstyle方式的来实现at24c02的驱
动。
客户驱动程序开发的一般步骤
(1)注册板载i2c设备信息
(2)定义i2c驱动设备id
(3)定义i2c_driver结构并完成其相应函数
(4)模块初始化时添加/撤销时删除i2c_driver
(5)/dev entry 访问方法 /sysfs访问方法
客户设备驱动开发实例
内核为linux-2.6.30.4
基于arm9 S3c2440平台
开发一个at24c02 eeprom的客户驱动
开发一个应用程序访问I2C设备
-----------------------------------------------------------------
根据开发客户驱动程序步骤实现对i2c设备at24c02的读写操作。
(分析at24c02 datasheet)
(1)注册板载信息
在mach-smdk2440.c文件中静态声明一个I2C设备
- static struct i2c_board_info i2c_devices[] __initdata = {
-
- {I2C_BOARD_INFO("24c02", 0x50), },
-
- {}
-
- };
在smdk2440_machine_init()函数中,向总线注册I2C设备信息:
- i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
(2)定义i2c驱动设备id:
- static struct i2c_device_id foo_idtable[] = {
-
- { “24c01", 0x51 },
-
- {“20402”,0x50 },
-
- {}
-
- };
(3)定义i2c_driver结构并完成其相应函数:
- static struct i2c_driver my_i2c_driver = {
-
- .driver = {
-
- .name = "i2c_demo",
-
- .owner = THIS_MODULE,
-
- },
-
- .probe = my_i2c_probe,
-
- .remove = my_i2c_remove,
-
- .id_table = my_ids,
-
- };
(4)模块初始化时添加/撤销时删除i2c_driver
- static int __init my_i2c_client(void)
-
- {
-
- return i2c_add_driver(&my_i2c_driver);
-
- }
-
- static void __exit my_i2c_exit(void)
-
- {
-
- i2c_del_driver(&my_i2c_driver);
-
- }
(5)使用/dev entry 访问方法
注册字符设备
- register_chrdev(I2C_MAJOR,DEVICE_NAME,&i2c_fops);
-
- 创建类class_create(THIS_MODULE, DEVICE_NAME);
-
- 在/dev下创建设备节点
-
- device_create(my_dev_class, &client->dev,MKDEV(I2C_MAJOR, 0), NULL, DEVICE_NAME);
"实现代码附件下载",有需要请留言,邮件发~
谁能告诉我csdn为什么不可以上传附件?
代码测试结果:
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/list.h>
- #include <linux/i2c.h>
- #include <linux/i2c-dev.h>
- #include <linux/smp_lock.h>
- #include <linux/jiffies.h>
- #include <asm/uaccess.h>
- #include <linux/delay.h>
-
-
- #define DEBUG 1
- #ifdef DEBUG
- #define dbg(x...) printk(x)
- #else
- #define dbg(x...) (void)(0)
- #endif
-
- #define I2C_MAJOR 89
- #define DEVICE_NAME "at24c02"
- static struct class *my_dev_class;
- static struct i2c_client *my_client;
- static struct i2c_driver my_i2c_driver;
-
-
- static struct i2c_device_id my_ids[] = {
- {"24c01",0x50},
- {"24c02",0x50},
- {"24c08",0x50},
- {}
- };
-
- MODULE_DEVICE_TABLE(i2c,my_ids);
-
- static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- int res;
- struct device *dev;
-
- dbg("probe:name = %s,flag =%d,addr = %d,adapter = %d,driver = %s\n",client->name,
- client->flags,client->addr,client->adapter->nr,client->driver->driver.name );
-
- dev = device_create(my_dev_class, &client->dev,
- MKDEV(I2C_MAJOR, 0), NULL,
- DEVICE_NAME);
- if (IS_ERR(dev))
- {
- dbg("device create error\n");
- goto out;
- }
- my_client = client;
-
- return 0;
- out:
- return -1;
- }
- static int my_i2c_remove(struct i2c_client *client)
- {
-
- dbg("remove\n");
- return 0;
- }
-
- static ssize_t at24c02_read(struct file *fd, char *buf, ssize_t count, loff_t *offset)
- {
- char *tmp;
- int ret;
- char data_byte;
- char reg_addr = 0,i;
- struct i2c_client *client = (struct i2c_client*) fd->private_data;
- struct i2c_msg msgs[2];
-
- dbg("read:count = %d,offset = %ld\n",count,*offset);
- tmp = kmalloc(count,GFP_KERNEL);
-
- if (!tmp)
- {
- dbg("malloc error in read function\n");
- goto out;
- }
-
- reg_addr = *offset;
- msgs[0].addr = client->addr;
- msgs[0].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC) ;
- msgs[0].len = 1;
- msgs[0].buf = (char *)®_addr;
-
- msgs[1].addr= client->addr;
- msgs[1].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC);
- msgs[1].flags |= I2C_M_RD;
- msgs[1].len = count;
- msgs[1].buf = (char*)tmp;
-
- ret = i2c_transfer(client->adapter,&msgs,2);
- if (ret != 2)
- goto out;
- if (copy_to_user(buf, tmp, count))
- goto out;
-
- kfree(tmp);
- return count;
- out:
- kfree(tmp);
- return -1;
-
- }
-
-
- static int at24c02_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
- {
- dbg("ioctl code ...\n");
- return 0;
- }
-
- static ssize_t at24c02_write(struct file *fd, char *buf, ssize_t count, loff_t *offset)
- {
- int ret,i;
- char *tmp;
- int errflg;
- struct i2c_msg msg;
- struct i2c_client *client = (struct i2c_client*) fd->private_data;
- char tmp_data[2];
-
- dbg("write:count = %d,offset = %ld\n",count,*offset);
- tmp = kmalloc(count, GFP_KERNEL);
- if (!tmp)
- goto out;
- if (copy_from_user(tmp, buf, count))
- goto out;
- msg.addr = client->addr;
- msg.flags = client->flags & (I2C_M_TEN | I2C_CLIENT_PEC);
- for (i = 0; i < count; i++) {
- msg.len = 2;
- tmp_data[0] = *offset + i;
- tmp_data[1] = tmp[i];
- msg.buf = tmp_data;
- ret = i2c_transfer(client->adapter,&msg,1);
- if (ret != 1)
- goto out;
- msleep(1);
- }
- kfree(tmp);
-
- return ((ret == 1) ? count:ret);
- out:
- kfree(tmp);
- return -1;
-
- }
- static int at24c02_open(struct inode *inode, struct file *fd)
- {
-
- fd->private_data =(void*)my_client;
- return 0;
-
- }
-
- static int at24c02_release(struct inode *inode, struct file *fd)
- {
- dbg("release\n");
- fd->private_data = NULL;
-
- return 0;
-
- }
-
- static const struct file_operations i2c_fops = {
- .owner = THIS_MODULE,
- .open = at24c02_open,
- .read = at24c02_read,
- .write = at24c02_write,
- .unlocked_ioctl = at24c02_ioctl,
- .release = at24c02_release,
- };
-
- static struct i2c_driver my_i2c_driver = {
- .driver = {
- .name = "i2c_demo",
- .owner = THIS_MODULE,
- },
- .probe = my_i2c_probe,
- .remove = my_i2c_remove,
- .id_table = my_ids,
- };
-
- static int __init my_i2c_init(void)
- {
- int res;
-
-
- res = register_chrdev(I2C_MAJOR,DEVICE_NAME,&i2c_fops);
- if (res)
- {
- dbg("register_chrdev error\n");
- return -1;
- }
- my_dev_class = class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(my_dev_class))
- {
- dbg("create class error\n");
- unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
- return -1;
- }
- return i2c_add_driver(&my_i2c_driver);
- }
-
- static void __exit my_i2c_exit(void)
- {
- unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
- class_destroy(my_dev_class);
- i2c_del_driver(&my_i2c_driver);
-
- }
-
- MODULE_AUTHOR("itspy<[email protected]>");
- MODULE_DESCRIPTION("i2c client driver demo");
- MODULE_LICENSE("GPL");
- module_init(my_i2c_init);
- module_exit(my_i2c_exit);