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

Linux驱动子系统之I2C(3)

2014-01-09 10:14 357 查看


3 i2c-dev

3.1 概述

之前在介绍I2C子系统时,提到过使用i2c-dev.c文件在应用程序中实现我们的I2C从设备驱动。不过,它实现的是一个虚拟,临时的i2c_client,随着设备文件的打开而产生,并随着设备文件的关闭而撤销。I2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主题是”i2c_driver成员函数+字符设备驱动”。

3.2 i2c-dev.c源码分析

初始化模块

[cpp] view
plaincopy

static int __init i2c_dev_init(void)

{

res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

i2c_dev_class= class_create(THIS_MODULE, "i2c-dev");

/*Keep track of adapters which will be added or removed later */

res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);

/*绑定已经存在的适配器 */

i2c_for_each_dev(NULL,i2cdev_attach_adapter);

}

I2c-dev初始化函数主要做了注册名为”i2c”的字符设备文件和”i2c-dev”的类

i2cdev_read和i2cdev_write

I2c-dev.c中实现的i2cdev_read和i2cdev_write函数不具有太强的通用性,只适合下面这种单开始信号情况:



而不适合多开始信号的情况:



所以我们经常会使用i2cdev_ioctl函数的I2C_RDWR,在分析i2cdev_ioctl函数之前,我们需要了解一个结构体:

[cpp] view
plaincopy

/* This is the structure as used in theI2C_RDWR ioctl call */

struct i2c_rdwr_ioctl_data {

structi2c_msg __user *msgs; /* pointersto i2c_msgs */

__u32nmsgs; /* number ofi2c_msgs */

};

Msgs 表示单个开始信号传递的数据;

Nmsgs 表示有多少个msgs,比如上图,单开始信号时,nmsgs等于1;多开始信号时,nmsgs等于2

[cpp] view
plaincopy

struct i2c_msg {

__u16addr; /* slave address */

__u16flags; /* 默认为写入 */

#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 */

__u16len; /* msg length */

__u8*buf; /* pointer to msgdata */

};

使用i2cdev_ioctl函数的I2C_RDWR指令会调用到i2cdev_ioctl_rdrw函数:

[cpp] view
plaincopy

static noinline inti2cdev_ioctl_rdrw(struct i2c_client *client,

unsignedlong arg)

{

structi2c_rdwr_ioctl_data rdwr_arg;

structi2c_msg *rdwr_pa;

u8__user **data_ptrs;

inti, res;

if(copy_from_user(&rdwr_arg,

(struct i2c_rdwr_ioctl_data __user *)arg,

sizeof(rdwr_arg)))

return-EFAULT;

if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

return-EINVAL;

rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);

if(copy_from_user(rdwr_pa, rdwr_arg.msgs,

rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {

kfree(rdwr_pa);

return-EFAULT;

}

res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);

while(i-- > 0) {

if(res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {

if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf,

rdwr_pa[i].len))

res= -EFAULT;

}

kfree(rdwr_pa[i].buf);

}

}

咋一看,还挺复杂,其实主要做了一件事情:把用户空间传递过来的i2c_rdwr_ioctl_data数据进行错误检查,然后调用i2c_transfer函数与适配器进行通信,如果是接收数据,代码会将访问到的数据传回i2c_rdwr_ioctl_data的buf中。I2c_transfer最终会调用到I2C适配器具体实现的master_xfer函数来与硬件进行通信。

3.3 eeprom实例

预备知识

使用的mini2440开发板,eeprom的地址为0x50,实验完成一个数据的读写,先看下读写时序

AT24C08任意地址字节写的时序:



AT24C08任意地址字节写的时序:



下面的代码可以按照上面的两个图来阅读:

[cpp] view
plaincopy

#include <stdio.h>

#include <linux/types.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <errno.h>

#include <assert.h>

#include <string.h>

#include <linux/i2c.h>

#include <linux/i2c-dev.h>

int main()

{

intfd, ret;

unsignedchar rdwr_addr = 0x42; /* e2prom 读写地址 */

unsignedchar device_addr = 0x50; /* e2prom 设备地址 */

unsignedchar data = 0x12; /* 向e2prom写的数据 */

structi2c_rdwr_ioctl_data e2prom_data;

fd= open("/dev/i2c/0", O_RDWR);

if(fd < 0) {

perror("openerror");

exit(1);

}

e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \

sizeof(structi2c_msg));

if(e2prom_data.msgs == NULL) {

perror("mallocerror");

exit(1);

}

ioctl(fd,I2C_TIMEOUT, 1); /* 设置超时 */

ioctl(fd,I2C_RETRIES, 2); /* 设置重试次数 */

/*向e2prom的rdwr_addr地址写入数据data*/

e2prom_data.nmsgs= 1;

e2prom_data.msgs[0].len= 2;

e2prom_data.msgs[0].addr= device_addr;

e2prom_data.msgs[0].flags= 0; /* write */

e2prom_data.msgs[0].buf= (unsigned char *)malloc(2);

e2prom_data.msgs[0].buf[0]= rdwr_addr; /* write address */

e2prom_data.msgs[0].buf[1]= data; /* write data */

ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);

if(ret < 0) {

perror("writedata error");

exit(1);

}

printf("writedata: %d to address: %#x\n", data, rdwr_addr);

data= 0; /* be zero*/

/*从e2prom的rdwr_addr地址读取数据存入buf*/

e2prom_data.nmsgs= 2;

e2prom_data.msgs[0].len= 1;

e2prom_data.msgs[0].addr= device_addr;

// e2prom_data.msgs[0].flags= 0; /* write */

e2prom_data.msgs[0].buf= &rdwr_addr;

e2prom_data.msgs[1].len= 1;

e2prom_data.msgs[1].addr= device_addr;

e2prom_data.msgs[1].flags= 1; /* read */

e2prom_data.msgs[1].buf= &data;

ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);

if(ret < 0) {

perror("readerror");

exit(1);

}

printf("read data: %d from address: %#x\n", data,rdwr_addr);

free(e2prom_data.msgs);

close(fd);

return0;

}

在mini2440开发板上已经实验成功。

原文见:http://blog.csdn.net/paul_liao/article/details/6982883
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: