Linux之IIC驱动
一、IIC接口下的24C02 驱动分析
1、I2C通信介绍
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,是一个多主机的半双工通信方式
每个挂接在总线上的器件都有个唯一的地址
位速在标准模式下可达 100kbit/s,在快速模式下可达400kbit/s,在高速模式下可待3.4Mbit/s。
2、I2C总线系统结构,如下所示
其中SCL时钟线的频率由主机提供,且从机不能主动来引起数据传输,必须等待主机先发信号才行
两个或多个主机同时发起数据传输时,可以通过冲突检测和仲裁来防止数据被破坏。
3、I2C时序介绍
1)空闲状态
当总线上的SDA和SCL两条信号线同时处于高电平,便是空闲状态,如上面的硬件图所示,当我们不传输数据时, SDA和SCL被上拉电阻拉高,即进入空闲状态
2)起始信号
当SCL为高期间,SDA由高到低的跳变;便是总线的启动信号,只能由主机发起,且在空闲状态下才能启动该信号,如下图所示:
3)停止信号
当SCL为高期间,SDA由低到高的跳变;便是总线的停止信号,表示数据已传输完成,如下图所示:
4)传输数据格式
当发了起始信号后,就开始传输数据,传输的数据格式如下图所示:
当SCL为高电平时,便会获取SDA数据值,其中SDA数据必须是稳定的(若SDA不稳定就会变成起始/停止信号)
当SCL为低电平时,便是SDA的电平变化状态
若主从机在传输数据期间,需要完成其它功能(例如一个中断),可以主动拉低SCL,使I2C进入等待状态,直到处理结束再释放SCL,数据传输会继续
5)应答信号ACK
I2C总线上的数据都是以8位数据(字节)进行的,当发送了8个数据后,发送方会在第9个时钟脉冲期间释放SDA数据,当接收方接收该字节成功,便会输出一个ACK应答信号,当SDA为高电平,表示为非应答信号NACK,当SDA为低电平,表示为有效应答信号ACK
当主机为接收方 时,收到最后一个字节后,主机可以不发送ACK,直接发送停止信号来结束传输。
当从机为接收方 时,没有发送ACK,则表示从机可能在忙其它事、或者不匹配地址信号和不支持多主机发送,主机可以发送停止信号再次发送起始信号启动新的传输
并非每传输8位数据之后,都会有ACK信号,有以下3种例外。
当从机不能响应从机地址时(例如它正忙于其他事而无法响应I2C总线的操作,或者这个地址没有对应的从机),在第9个SCL周期内SDA线没有被拉低,即没有ACK信号。
这时,主机发出一个P信号终止传输或者重新发出一个S信号开始新的传输。
如果从机接收器在传输过程中不能接收更多的数据时,它也不会发出ACK信号。这样,主机就可以意识到这点,从而发出一个P信号终止传输或者重新发出一个S信号开始新的传输。
主机接收器在接收到最后一个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,以允许主机发出P信号结束传输。
6)完整的数据传输
如下图所示, 发送起始信号后,便发送一个8位的设备地址,其中第8位是对设备的读写标志,后面紧跟着的就是数据了,直到发送停止信号终止
当我们第一次是读操作,然后想换成写操作时,可以再次发送一个起始信号,然后发送读的设备地址,不需要停止信号便能实现不同的地址转换
4、AT24C02介绍
AT24C02是通过I2C实现通讯的,是一个存储芯片,能够存储2Kb(256KB*8)数据
4.1、原理图
其中A2~A0,是这个24C02设备的硬件地址,接GND表示硬件地址都为0
4.2、AT24C02的数据格式
如下所示:
4.3、AT24C02的设备地址
打开AT24C02数据手册,它的设备地址如下图所示:
其中A2~A1表示硬件地址,P2~P0表示page页地址
bit[0]地址:表示读/写状态,1:读,0:写
所有I2C器件都是这样,最低位表示方向位
4.3.1、为什么需要page页地址
因为I2C的数据位是8位,而AT24CXX的读写地址值最大可以为2048(2^11^),超过了I2C的数据位
而page页地址就是用来解决这个问题的
比如AT24C16:
当发送:0XA2(设备地址P[2:0]=0x01) ,0x00(读地址)时:
表示要读的真正地址=0x01(页地址)*256+0(读地址)=0x100,转化为二进制= 1 0000 0000
当发送:0XA0(设备地址), 0x00(读地址)时:
表示要读的真正地址=0x00*256+0xFF=0xFF,转化为二进制= 0 1111 1111
4.3.2、对于AT24C02
芯片的容量小于等于2^8(256)字节,那么读写地址就用8bit来表示,所以设备地址里没有P2~P0
读操作时,发送的设备地址等于0XA1
写操作时,发送的设备地址等于0XA0
5、AT24C02时序图介绍
5.1、写时序介绍
当随机写一个字节时,只需要先发送一个起始信号,然后跟上0XA0设备地址,以及要写的起始地址值,后面便是要写入地址的data,如果需要连续写数据,只需要连续写入data,地址会自动加1,直到发送停止信号结束
5.2、读时序介绍
当随机读一个字节时,先发送第一个起始信号,然后写入0XA0设备地址和要读的地址值,
接着发送第二个起始信号,然后写入0XA1设备地址,接着就是要读的data,如果需要连续读数据,只需要连续读出data,地址会自动加1,直到发送停止信号结束
二、Linux下IIC驱动分析
1、I2C体系结构分析
1.1、driver/i2c目录
进入linux内核的driver/i2c目录下,其中重要的文件介绍如下:
1)algos文件夹(algorithms)
里面保存I2C的通信方面的算法
2)busses文件夹
里面保存I2C总线驱动相关的文件,比如i2c-omap.c、 i2c-versatile.c、 i2c-s3c2410.c等。
3) chips文件夹
里面保存I2C设备驱动相关的文件,比如m41t00.c就是RTC实时钟
4) i2c-core.c
这个文件实现了I2C核心的功能(I2C总线的初始化、注册和适配器添加和注销等相关工作)以及/proc/bus/i2c*接口。
5) i2c-dev.c
提供了通用的read() 、write()和ioctl()等接口,实现了I2C适配器设备文件的功能,其中I2C设备的主设备号都为89, 次设备号为0~255。
应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器, 并控制I2C设备的工作方式
显然,它和前几次驱动类似, I2C也分为总线驱动和设备驱动,总线就是协议相关的,它知道如何收发数据,但不知道数据含义,设备驱动却知道数据含义
1.2、I2C驱动架构
如上图所示,每一条I2C对应一个adapter适配器(可以理解为插槽,就是利用之前的dev-bus-drv模型改的,属于之前的dev设备模型),在kernel中,adapter适配器是通过struct adapter结构体定义,主要是通过i2c core层将i2c设备与i2c adapter关联起来。
在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和 i2c_add_numbered_adapter() 。由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号 。这个总线号和PCI中的总线号不同。它和硬件无关 ,只是软件上便于区分而已。
对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。
2、分析I2C总线驱动i2c-s3c2410.c
参考drivers/i2c/busses/i2c-s3c2410.c
先进入init入口函数,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static struct platform_driver s3c2440_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .resume = s3c24xx_i2c_resume, .driver = { .owner = THIS_MODULE, .name = "s3c2440-i2c" , }, }; static int __init i2c_adap_s3c_init (void ) { int ret; ret = platform_driver_register(&s3c2410_i2c_driver); if (ret == 0 ) { ret = platform_driver_register(&s3c2440_i2c_driver); if (ret) platform_driver_unregister(&s3c2410_i2c_driver); } return ret; }
在init函数中,注册了一个 “s3c2440-i2c”的platform_driver平台驱动,当内核中有同名的平台设备时会调用platform_driver结构体的.probe成员函数,我们来看看probe函数做了些什么
3、s3c24xx_i2c_probe()分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 struct i2c_adapter adap ;static int s3c24xx_i2c_probe (struct platform_device *pdev) { struct s3c24xx_i2c *i2c = &s3c24xx_i2c; ... ... i2c->clk = clk_get(&pdev->dev, "i2c" ); clk_enable(i2c->clk); ... .... res = platform_get_resource(pdev, IORESOURCE_MEM, 0 ); i2c->regs = ioremap(res->start, (res->end-res->start)+1 ); ... .... i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; ret = s3c24xx_i2c_init(i2c); if (ret != 0 ) goto err_iomap; ... ... ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c); ... ... ret = i2c_add_adapter(&i2c->adap); ... ... }
其中i2c_adapter结构体是放在s3c24xx_i2c->adap下,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct s3c24xx_i2c { spinlock_t lock; wait_queue_head_t wait; struct i2c_msg *msg ; unsigned int msg_num; unsigned int msg_idx; unsigned int msg_ptr; unsigned int tx_setup; enum s3c24xx_i2c_state state ; void __iomem *regs; struct clk *clk ; struct device *dev ; struct resource *irq ; struct resource *ioarea ; struct i2c_adapter adap ; };
4、i2c_add_adapter()函数分析
该函数是注册i2c_adapter适配器结构体,这里我们来分析下他是如何进行注册的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int i2c_add_adapter (struct i2c_adapter *adapter) { int id, res = 0 ; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0 ) return -ENOMEM; mutex_lock(&core_lists); res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id); mutex_unlock(&core_lists); if (res < 0 ) { if (res == -EAGAIN) goto retry; return res; } adapter->nr = id; return i2c_register_adapter(adapter); }
接着分析**i2c_register_adapter()**函数,其代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static int i2c_register_adapter (struct i2c_adapter *adap) { struct list_head *item ; struct i2c_driver *driver ; list_add_tail(&adap->list , &adapters); ... ... if (adap->dev.parent == NULL ) { adap->dev.parent = &platform_bus; pr_debug("I2C adapter driver [%s] forgot to specify " "physical device\n" , adap->name); } sprintf (adap->dev.bus_id, "i2c-%d" , adap->nr); adap->dev.release = &i2c_adapter_dev_release; adap->dev.class = &i2c_adapter_class; res = device_register(&adap->dev); ... ... list_for_each(item,&drivers) { driver = list_entry(item, struct i2c_driver, list ); if (driver->attach_adapter) driver->attach_adapter(adap); } }
在**i2c_register_adapter()**函数里主要执行以下几步:
其中, i2c_driver结构体会在后面讲述到
而i2c_adapter适配器结构体 的成员结构,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct i2c_adapter { struct module *owner ; unsigned int id; unsigned int class ; const struct i2c_algorithm *algo ; void *algo_data; struct rt_mutex bus_lock ; int timeout; int retries; struct device dev ; int nr; char name[48 ]; struct completion dev_released ; struct list_head userspace_clients ; };
i2c_adapter表示物理上的一个i2C设备(适配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct i2c_adapter adap)成员中
5、s3c24xx_i2c的结构体成员分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; static struct s3c24xx_i2c s3c24xx_i2c = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock), .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), .tx_setup = 50 , .adap = { .name = "s3c2410-i2c" , .owner = THIS_MODULE, .algo = &s3c24xx_i2c_algorithm, .retries = 2 , .class = I2C_CLASS_HWMON, }, };
显然这里是直接设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe ()函数中没有分配i2c_adapter适配器结构体,
其中, i2c_adapter结构体的名称等于"s3c2410-i2c",它的通信方式等于s3c24xx_i2c_algorithm,重试次数等于2
如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是个I2C设备,而没有通信方式
s3c24xx_i2c_algorithm中的关键函数**master_xfer()**就是用于产生i2c通信所需要的start、stop、ack等信号
比如,在s3c24xx_i2c_algorithm中的关键函数master_xfer()里,调用了:
s3c24xx_i2c_xfer -> s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()
来启动传输message信息, 其中s3c24xx_i2c_message_start()函数代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static void s3c24xx_i2c_message_start (struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f ) << 1 ; ... ... stat = 0 ; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { stat |= S3C2410_IICSTAT_MASTER_RX; addr |= 1 ; } else stat |= S3C2410_IICSTAT_MASTER_TX; s3c24xx_i2c_enable_ack(i2c); iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n" , stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx\n" , iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); }
通过上面的代码和注释,发现主要是写入IIC从设备地址,然后发送起始信号+IIC从设备地址值,并回应ACK
显然IIC总线驱动i2c-s3c2410.c中主要是设置适配器adapter。里面帮我们做好了IIC通信的架构,就是不知道发什么内容
6、分析I2C设备驱动eeprom.c
进入driver/i2c/chips中,看看eeprom设备驱动是如何写的
参考: driver/i2c/chips/eeprom.c
首先来看它的init入口函数:
1 2 3 4 5 6 7 8 9 static int __init eeprom_init (void ) { return i2c_add_driver(&eeprom_driver); } static void __exit eeprom_exit (void ) { i2c_del_driver(&eeprom_driver); }
其中struct i2c_driver eeprom_driver的成员如下:
1 2 3 4 5 6 7 8 static struct i2c_driver eeprom_driver = { .driver = { .name = "eeprom" , }, .id = I2C_DRIVERID_EEPROM, .attach_adapter = eeprom_attach_adapter, .detach_client = eeprom_detach_client, };
如下所示, eeprom_driver结构体的ID成员在i2c-id.h中,里面还定义了大部分常用I2C设备驱动的设备ID(事实证明不写也行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 ...... #define I2C_DRIVERID_LM78 1002 #define I2C_DRIVERID_LM75 1003 #define I2C_DRIVERID_GL518 1004 #define I2C_DRIVERID_EEPROM 1005 #define I2C_DRIVERID_W83781D 1006 #define I2C_DRIVERID_LM80 1007 #define I2C_DRIVERID_ADM1021 1008 #define I2C_DRIVERID_ADM9240 1009 #define I2C_DRIVERID_LTC1710 1010 #define I2C_DRIVERID_BT869 1013 #define I2C_DRIVERID_MAXILIFE 1014 #define I2C_DRIVERID_MATORB 1015 #define I2C_DRIVERID_GL520 1016 #define I2C_DRIVERID_THMC50 1017 #define I2C_DRIVERID_ADM1025 1020 #define I2C_DRIVERID_LM87 1021 #define I2C_DRIVERID_PCF8574 1022 #define I2C_DRIVERID_MTP008 1023 #define I2C_DRIVERID_DS1621 1024 #define I2C_DRIVERID_ADM1024 1025 #define I2C_DRIVERID_CH700X 1027 #define I2C_DRIVERID_FSCPOS 1028 #define I2C_DRIVERID_FSCSCY 1029 #define I2C_DRIVERID_PCF8591 1030 #define I2C_DRIVERID_LM92 1033 #define I2C_DRIVERID_SMARTBATT 1035 #define I2C_DRIVERID_BMCSENSORS 1036 #define I2C_DRIVERID_FS451 1037 #define I2C_DRIVERID_LM85 1039 #define I2C_DRIVERID_LM83 1040 #define I2C_DRIVERID_LM90 1042 #define I2C_DRIVERID_ASB100 1043 #define I2C_DRIVERID_FSCHER 1046 #define I2C_DRIVERID_W83L785TS 1047 #define I2C_DRIVERID_OV7670 1048 ......
显然,在init函数中通过i2c_add_driver()注册i2c_driver结构体,然后通过i2c_driver ->attach_adapter来匹配内核中的各个总线驱动的适配器,发送这个设备地址,若有ACK响应,表示匹配成功
7、i2c_add_driver()函数分析
接下来,我们进入i2c_add_driver()来看看是不是像刚刚分析的那样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int i2c_add_driver (struct module *owner, struct i2c_driver *driver) { driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; res = driver_register(&driver->driver); ... ... if (driver->attach_adapter) { struct i2c_adapter *adapter ; list_for_each_entry(adapter, &adapters, list ) { driver->attach_adapter(adapter); } } ... ... return 0 ; }
在i2c_add_driver()函数里主要执行以下几步:
所以i2c_adapter适配器和i2c_driver设备驱动注册框架如下所示:
这里调用了i2c_driver->attach_adapter(adapter),我们看看里面是不是通过发送IIC设备地址,等待ACK响应来匹配的
8、i2c_driver ->attach_adapter()函数分析
这里以struct i2c_driver eeprom_driver 为例
1 2 3 4 5 6 7 8 9 static struct i2c_driver eeprom_driver = { .driver = { .name = "eeprom" , }, .id = I2C_DRIVERID_EEPROM, .attach_adapter = eeprom_attach_adapter, .detach_client = eeprom_detach_client, };
所以这里的i2c_driver ->attach_adapter()函数即为eeprom_attach_adapter()
如下所示,eeprom_attach_adapter()调用了i2c_probe(adapter, &addr_data, eeprom_detect)函数
1 2 3 4 static int eeprom_attach_adapter (struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, eeprom_detect); }
上述代码中i2c_probe的参数如下:
第1个参数是i2c_adapter适配器;
第2个参数addr_data变量,里面存放了IIC设备地址的信息;
第3个参数eeprom_detect就是具体的设备探测回调函数i2c_probe()函数,会通过adapter适配器发送IIC设备地址addr_data,如果收到ACK信号,就调用eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备,而i2c_driver对应的是设备驱动,也就是说,只有当适配器支持这个设备驱动,才会注册i2c_client从设备,后面会讲这个回调函数如何注册i2c_client
而在i2c_driver ->detach_client()中,则注销i2c_client结构体
其中addr_data变量是struct i2c_client_address_data结构体 ,它的成员如下所示:
1 2 3 4 5 6 struct i2c_client_address_data { unsigned short *normal_i2c; unsigned short *probe; unsigned short *ignore; unsigned short **forces; };
当上面结构体的数组成员以I2C_CLIENT_END 结尾,则表示地址已结束,比如AT24C02设备
为例,看这个结构体如何定义的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define AT24C02_ADDR (0xA0>>1) static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { AT24C02_ADDR, I2C_CLIENT_END };static unsigned short force_addr[] = {ANY_I2C_BUS, AT24C02_ADDR ,2 C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL };static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr, .probe = ignore, .ignore = ignore, . forces = forces, };
一般而言,都不会设置.forces成员,这里只是打个比方
8.1、i2c_probe()函数分析
1 2 3 4 5 int i2c_probe (struct i2c_adapter *adapter,struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int , int )) { ... ... err = i2c_probe_address(adapter,forces[kind][i + 1 ],kind, found_proc); }
里面调用了i2c_probe_address()函数,从名称上来看,显然它就是用来发送起始信号+设备地址,来探测IIC设备地址用的
8.2、i2c_probe_address()函数分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static int i2c_probe_address (struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int , int )) { if (addr < 0x03 || addr > 0x77 ) { dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n" ,addr); return -EINVAL; } if (i2c_check_addr(adapter, addr)) return 0 ; if (kind < 0 ) { if (i2c_smbus_xfer(adapter, addr, 0 , 0 , 0 ,I2C_SMBUS_QUICK, NULL ) < 0 ) return 0 ; ... ... }
8.3、i2c_smbus_xfer()函数分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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) { s32 res; flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) { mutex_lock(&adapter->bus_lock); res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data); mutex_unlock(&adapter->bus_lock); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data); return res; }
看了上面代码后,显然我们的s3c2410-i2c适配器没有algo->smbus_xfer函数,而是使用i2c_smbus_xfer_emulated()函数,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; static struct s3c24xx_i2c s3c24xx_i2c = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock), .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), .tx_setup = 50 , .adap = { .name = "s3c2410-i2c" , .owner = THIS_MODULE, .algo = &s3c24xx_i2c_algorithm, .retries = 2 , .class = I2C_CLASS_HWMON, }, };
通常适配器都是不支持的,都使用默认的i2c_smbus_xfer_emulated()函数
8.4、i2c_smbus_xfer_emulated()函数分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 static s32 i2c_smbus_xfer_emulated (struct i2c_adapter * adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size, union i2c_smbus_data * data) { unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3 ]; unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2 ]; int num = read_write == I2C_SMBUS_READ?2 :1 ; struct i2c_msg msg [2] = { { addr, flags, 1 , msgbuf0 }, { addr, flags | I2C_M_RD, 0 , msgbuf1 }}; msgbuf0[0 ] = command; ... ... if (i2c_transfer(adapter, msg, num) < 0 ) return -1 ; if (read_write == I2C_SMBUS_READ) switch (size) { ... ... case I2C_SMBUS_BYTE_DATA: if (read_write == I2C_SMBUS_READ) msg[1 ].len = 1 ; else { msg[0 ].len = 2 ; msgbuf0[1 ] = data->byte; } break ; ... ... } ... ... if (i2c_transfer(adapter, msg, num) < 0 ) return -1 ; ... ... }
其中i2c_msg结构体的结构,如下所示:
1 2 3 4 5 6 struct i2c_msg { __u16 addr; __u16 flags; __u16 len; __u8 *buf; };
上面代码中之所以读操作需要两个i2c_msg,写操作需要一个i2c_msg,是因为读IIC设备是两个流程,在上一节已经分析到了,具体如下所示:
只要发送一个S起始信号则就是一个i2c_msg,如下图所示:
而在i2c_transfer()函数中,最终又是调用了之前分析的i2c_adapter->algo->master_xfer()发送函数,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int i2c_transfer (struct i2c_adapter * adap, struct i2c_msg *msgs, int num) { int ret; if (adap->algo->master_xfer) { #ifdef DEBUG for (ret = 0 ; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d%s\n" , ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W' , msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "" ); } #endif mutex_lock_nested(&adap->bus_lock, adap->level); ret = adap->algo->master_xfer(adap,msgs,num); mutex_unlock(&adap->bus_lock); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n" ); return -ENOSYS; } }
其中i2c_transfer()的参数adap
表示通过哪个适配器传输出去,msgs
表示I2C消息,num
表示msgs的数目
内核每发送一个Msg都会先发出S开始信号和设备地址,直到所有Msg传输完毕,最后发出P停止信号。
当i2c_transfer()返回值为正数,表示已经传输正数个数据,当返回负数,说明I2C传输出错
8.5、总结
综上,在i2c_driver->attach_adapter(adapter)函数里主要执行以下几步:
调用i2c_probe (adap适配器, i2c_client_address_data设备地址结构体, 回调函数);
将要发的设备地址结构体打包成i2c_msg
然后执行**i2c_transfer()**来调用i2c_adapter->algo->master_xfer ()将i2c_msg发出去
若收到ACK回应(即发现有I2C设备),便进入回调函数,注册i2c_client 从设备,使该设备与适配器联系在一起
所以适配器和iic设备驱动最终注册框架图 如下所示:
9、注册i2c_client从设备
接下来便来分析回调函数如何注册i2c_client从设备,先来看看i2c_client结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 detected ; };
还是以driver/i2c/chips/eeprom.c为例,如下所示:
1 2 3 4 static int eeprom_attach_adapter (struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, eeprom_detect); }
这里的回调函数是eeprom_detect()函数接下来便分析该函数
9.1、回调函数eeprom_detect()分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static int eeprom_detect (struct i2c_adapter *adapter, int address, int kind) { struct i2c_client *new_client ; new_client =kzalloc(sizeof (struct i2c_client), GFP_KERNEL); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &eeprom_driver; new_client->flags = 0 ; strlcpy(new_client->name, "eeprom" , I2C_NAME_SIZE); if ((err = i2c_attach_client(new_client))) goto exit_kfree; ... ... exit_kfree: kfree(new_client); exit : return err; }
当注册了i2c_client从设备后,便可以使用i2c_transfer()来实现与设备传输数据了
10、24C02驱动及测试程序的编写
参考driver/i2c/chips/eeprom.c驱动,可以得出驱动代码步骤如下:
定义file_operations 结构体 ,设置字符设备的读写函数(实现对24C02的读写操作)
//构造i2c_msg结构体,使用i2c_transfer()来实现与设备传输数据
定义i2c_client_address_data 结构体,里面保存24C02的设备地址
定义一个i2c_driver 驱动结构体
3.1 设置i2c_driver-> attach_adapter // i2c_add_driver函数的最后会调用attach_adapter函数,attach_adapter函数最后会对adapters链表中的每个adapter(可理解为设备dev(这里的adapter相当于芯片内的硬件i2c部分,而芯片外接的i2c从机可理解为i2c_client
))调用该函数与相应的驱动进行连接,里面直接调用函数i2c_probe(adap适配器, i2c_client_address_data设备地址结构体, 回调函数fn);
即可
attach_adapter函数(实际上是i2c_probe)中的连接过程:
发出start信号(调用adapter->algo->master_xfer算法函数)
发出设备地址
若从设备会用ACK信号则调用回调函数fn
3.2 设置i2c_driver-> detach_client //卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理,主要负责卸载i2c_client、字符设备
写回调函数fn
,里面注册i2c_client ,字符设备( 字符设备用来实现读写24C02里的数据)
4.1 分配并设置i2c_client(adress(高七位,不算读写位)、adapter、driver、name)
4.2 使用i2c_attach_client()将i2c_client与适配器adapter进行连接
此步骤后i2c_driver->detach_client函数才能在卸载时被正常调用
上述两步可通过以下函数实现
1 2 3 struct i2c_client *i2c_new_device (struct i2c_adapter *adap, struct i2c_board_info const *info)
或:
1 2 3 4 struct i2c_client *i2c_new_probed_device (struct i2c_adapter *adap,struct i2c_board_info *info,unsigned short const *addr_list)
4.3 注册字符设备
写init入口函数,exit出口函数
init:使用**i2c_add_driver()注册i2c_driver驱动结构体
exit:使用 i2c_del_driver()**卸载i2c_driver驱动结构体
i2c数据传输:
6.1 构造i2c消息i2c_msg
结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 struct i2c_msg { __u16 addr; __u16 flags; #define I2C_M_TEN 0x10 #define I2C_M_RD 0x01 #define I2C_M_NOSTART 0x4000 #define I2C_M_REV_DIR_ADDR 0x2000 #define I2C_M_IGNORE_NAK 0x1000 #define I2C_M_NO_RD_ACK 0x0800 #define I2C_M_RECV_LEN 0x0400 __u16 len; __u8 *buf; };
6.2 调用函数i2c_transfer()
函数传输消息
1 int i2c_transfer (struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
adap:适配器(dev),一般i2c_client->adapter
msgs:传输消息
num:消息条数
返回值:发送成功条数
i2c从机设备i2c_client的4种构建方法(3.4.2内核)
1. 通过总线号声明
定义一个i2c_board_info结构体,用于描述硬件名字、地址addr及相应其他数据,该结构体会在系统初始化时被i2c_register_board_info
调用并生成对应的i2c_client
1 2 3 static struct i2c_board_info at24cxx_info = { I2C_BOARD_INFO("at24c08" ,0x50 ), }
i2c_register_board_info
函数最后还是通过调用i2c_new_device
生成及注册i2c_client
限制:不适合动态加载
2. 直接创建设备
直接使用i2c_new_device
或i2c_new_probed_device
函数,具体使用方法见上文
可以通过以下方法取得适配器:
1 2 3 4 5 6 static struct i2c_client *at24cxx_client ;... struct i2c_adapter *i2c_adap ;i2c_adap = i2c_get_adapter(0 ); at24cxx_client = i2c_new_device(i2c_adap,&at24cxx_info); i2c_put_adapter(i2c_adap);
3. 从用户空间创建设备
向new_device
写入数据,能够自己创建设备:
1 2 3 4 $ echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-0/new_device $ echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
向delete_device
写入数据,能够自己删除设备
最终仍是调用i2c_new_device
4. 探测某些设备的I2c总线
例子:drivers/hwmon/lm90.c
使用较少,类似于2.6内核中的方式
具体驱动代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/fs.h> #include <asm/uaccess.h> static struct i2c_client *at24c02_client ; static struct class *at24c02_class ; static unsigned int at24c02_major;static ssize_t at24c02_read (struct file *file, char __user *buf, size_t size, loff_t * offset) { struct i2c_msg msg [2]; u8 addr; u8 data; int ret; if (size!=1 ) return -EINVAL; copy_from_user(&addr,buf,1 ); msg[0 ].addr=at24c02_client->addr; msg[0 ].flags=0 ; msg[0 ].len =1 ; msg[0 ].buf =&addr; msg[1 ].addr=at24c02_client->addr; msg[1 ].flags=I2C_M_RD; msg[1 ].len =1 ; msg[1 ].buf =&data; ret=i2c_transfer(at24c02_client->adapter, msg, 2 ); if (ret==2 ) { copy_to_user(buf,&data,1 ); return 0 ; } else return -EAGAIN; } static ssize_t at24c02_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) { struct i2c_msg msg [1]; u8 val[2 ]; int ret; if (size!=2 ) return -EINVAL; copy_from_user(val,buf,2 ); msg[0 ].addr=at24c02_client->addr; msg[0 ].flags=0 ; msg[0 ].len =2 ; msg[0 ].buf =val; ret=i2c_transfer(at24c02_client->adapter, msg, 1 ); if (ret==1 ) { return 0 ; } else return -EAGAIN; } static struct file_operations at24c02_fops = { .owner = THIS_MODULE, .read = at24c02_read, .write = at24c02_write, }; static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = {0X50 , I2C_CLIENT_END };static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60 , I2C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL };static struct i2c_client_address_data at24c02_addr = { .normal_i2c=normal_addr, .probe=ignore, .ignore=ignore, }; static int at24c02_attach_adapter (struct i2c_adapter *adapter) ;static int at24c02_detach_client (struct i2c_client *client) ;static int at24c02_detect (struct i2c_adapter *adap, int addr, int kind) ;static struct i2c_driver at24c02_driver = { .driver = { .name = "at24c02" , }, .attach_adapter = at24c02_attach_adapter, .detach_client = at24c02_detach_client, }; static int at24c02_attach_adapter (struct i2c_adapter *adapter) { return i2c_probe(adapter,&at24c02_addr, at24c02_detect); } static int at24c02_detach_client (struct i2c_client *client) { printk("at24c02_detach_client\n" ); i2c_detach_client(at24c02_client) ; kfree(at24c02_client); class_device_destroy(at24c02_class,MKDEV(at24c02_major, 0 )); class_destroy(at24c02_class); return 0 ; } static int at24c02_detect (struct i2c_adapter *adap, int addr, int kind) { printk("at24c02_detect\n" ); at24c02_client= kzalloc(sizeof (struct i2c_client), GFP_KERNEL); at24c02_client->addr = addr; at24c02_client->adapter = adap; at24c02_client->driver = &at24c02_driver; at24c02_client->flags = 0 ; strlcpy(at24c02_client->name, "at24c02" , I2C_NAME_SIZE); i2c_attach_client(at24c02_client); at24c02_major= register_chrdev(0 , "at24c02" , &at24c02_fops); at24c02_class=class_create(THIS_MODULE, "at24c02" ); class_device_create(at24c02_class,0 , MKDEV(at24c02_major, 0 ),0 ,"at24c02" ); return 0 ; } static int at24c02_init (void ) { i2c_add_driver(&at24c02_driver); return 0 ; } static void at24c02_exit (void ) { i2c_del_driver(&at24c02_driver); } module_init(at24c02_init); module_exit(at24c02_exit); MODULE_LICENSE("GPL" );
测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> void print_usage (char *file) { printf ("%s r addr\n" , file); printf ("%s w addr val\n" , file); } int main (int argc, char **argv) { int fd; unsigned char buf[2 ]; if ((argc != 3 ) && (argc != 4 )) { print_usage(argv[0 ]); return -1 ; } fd = open("/dev/at24cxx" , O_RDWR); if (fd < 0 ) { printf ("can't open /dev/at24cxx\n" ); return -1 ; } if (strcmp (argv[1 ], "r" ) == 0 ) { buf[0 ] = strtoul(argv[2 ], NULL , 0 ); read(fd, buf, 1 ); printf ("data: %c, %d, 0x%2x\n" , buf[0 ], buf[0 ], buf[0 ]); } else if (strcmp (argv[1 ], "w" ) == 0 ) { buf[0 ] = strtoul(argv[2 ], NULL , 0 ); buf[1 ] = strtoul(argv[3 ], NULL , 0 ); write(fd, buf, 2 ); } else { print_usage(argv[0 ]); return -1 ; } return 0 ; }
扩展:
strtoul
函数(将字符串转换成无符号长整型数)
头文件
定义函数
1 >unsigned long strtoul (const char *nptr,char **endptr,int base) ;
函数说明
strtoul()会将参数nptr字符串根据参数base来转换成无符号的长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式,如base值为10则采用10进制,若base值为16则采用16进制数等。当base值为0时会根据情况选择用哪种进制:如果第一个字符是’0’,就判断第二字符如果是‘x’则用16进制,否则用8进制;第一个字符不是‘0’,则用10进制。一开始strtoul()会扫描参数nptr字符串,跳过前面的空格字符串,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束时(‘’)结束转换,并将结果返回。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
返回值
返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。
附加说明
ERANGE指定的转换字符串超出合法范围。
范例
将十六进制 0xFF,转换成 10进制,得到 255
1 2 3 4 5 6 7 8 9 10 >#include <stdio.h> >#include <stdlib.h> >int main () >{ int a; char pNum[]="0xFF" ; a=strtoul(pNum,0 ,0 ); printf ("%ul\n" ,a); return 0 ; >}
输出:
11、测试运行
如下所示:
1 2 3 4 5 6 7 # ./19th_test R 0x10 Read address 0x10 data is 0xff # ./19th_test W 0x10 0x10 Write address 0x10 data is 0x10 # ./19th_test R 0x10 Read address 0x10 data is 0x10 #