[概述]

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

[i2c-dev.c源码分析]

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

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

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

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

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

structi2c_rdwr_ioctl_data {

structi2c_msg __user *msgs;/* pointersto i2c_msgs */

__u32nmsgs;/* number ofi2c_msgs */

};

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

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

structi2c_msg {

__u16addr;/* slave address                         */

__u16flags;/* 默认为写入 */

#define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */

#define I2C_M_RD           0x0001     /* readdata, 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函数:

staticnoinlineinti2cdev_ioctl_rdrw(structi2c_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,

(structi2c_rdwr_ioctl_data __user *)arg,

sizeof(rdwr_arg)))

return-EFAULT;

/*Put an arbitrary limit on the number of messages that can

* be sent at once */

if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

return-EINVAL;

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

if(!rdwr_pa)

return-ENOMEM;

if(copy_from_user(rdwr_pa, rdwr_arg.msgs,

rdwr_arg.nmsgs *sizeof(structi2c_msg))) {

kfree(rdwr_pa);

return-EFAULT;

}

data_ptrs= kmalloc(rdwr_arg.nmsgs *sizeof(u8 __user *), GFP_KERNEL);

if(data_ptrs == NULL) {

kfree(rdwr_pa);

return-ENOMEM;

}

res= 0;

for(i = 0; i

/*Limit the size of the message to a sane amount;

* and don't let length change either. */

if((rdwr_pa[i].len > 8192) ||

(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {

res= -EINVAL;

break;

}

data_ptrs[i]= (u8 __user *)rdwr_pa[i].buf;

rdwr_pa[i].buf= memdup_user(data_ptrs[i], rdwr_pa[i].len);

if(IS_ERR(rdwr_pa[i].buf)) {

res= PTR_ERR(rdwr_pa[i].buf);

break;

}

}

if(res

intj;

for(j = 0; j

kfree(rdwr_pa[j].buf);

kfree(data_ptrs);

kfree(rdwr_pa);

returnres;

}

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);

}

kfree(data_ptrs);

kfree(rdwr_pa);

returnres;

}

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

[eeprom实例]

预备知识

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

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

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

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

intmain()

{

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

perror("openerror");

exit(1);

}

e2prom_data.msgs= (structi2c_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= (unsignedchar*)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, (unsignedlong)&e2prom_data);

if(ret

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, (unsignedlong)&e2prom_data);

if(ret

perror("readerror");

exit(1);

}

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

free(e2prom_data.msgs);

close(fd);

return0;

}

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

linux iopen i2c dev,i2c-dev - Linux驱动子系统之I2C_Linux编程_Linux公社-Linux系统门户网站...相关推荐

  1. 显示驱动包含在Linux内核层,驱动程序层(上) - Linux内核--网络栈实现分析_Linux编程_Linux公社-Linux系统门户网站...

    经过前面两篇博文的分析,已经对Linux的内核网络栈的结构有了一个模糊的认识,这里我们开始从底层开始详细分析Linux内核网络栈的实现.由于这是早期版本,代码的层次隔离做的还不是很好,这里说是从底层分 ...

  2. win驱动移植linux,LCD移植 - 基于Tiny210v2的Linux-3.9.6内核驱动移植_Linux编程_Linux公社-Linux系统门户网站...

    友善的tiny210v2我买的是7寸电容屏,具体型号得再查查,说是S70. 用原本的LINUX内的SMDKV210的LCD驱动能实现LINUX LOGO的输出,但是有一定的偏差. 主要参考: arm9 ...

  3. linux在开发板LCD上显,W35型LCD驱动移植 - linux-2.6.32在mini2440开发板上移植_Linux编程_Linux公社-Linux系统门户网站...

    编者注:本移植主要步骤还是按照手册来,里面讲解了一些有用的基础知识.但书册上提供了集中屏幕的方案,我们这里主要就用一种,也就是开发板自带的W35型号.液晶驱动的源程序在src/drivers/vide ...

  4. linux内核添加usb键盘驱动,配置USB外设 - linux-2.6.32在mini2440开发板上移植_Linux编程_Linux公社-Linux系统门户网站...

    linux-2.6.32在mini2440开发板上移植 配置USB外设 [日期:2013-04-08] 来源:Linux社区 作者:ssdsafsdsd [字体:大 中 小] 编者:因为LINUX内核 ...

  5. 2440 linux文件写,添加yaffs2文件系统 - Linux2.6.39在S3C2440上的移植_Linux编程_Linux公社-Linux系统门户网站...

    1.主机环境:VMare下Ubuntu10.04 ,1G内存. 2.编译编译环境:arm-linux-gcc 3.开发板:Micro2440,2M nor flash,256M nand flash. ...

  6. s5pv210 linux内核移植,简单根文件系统制作 - S5PV210 Linux3.8.3内核移植_Linux编程_Linux公社-Linux系统门户网站...

    1.这里为什么选nfs文件系统呢? 在产品开发阶段,因为nfs根文件系统并不需要编译进内核,方便调试. 2.制作根文件系统需要用到BusyBox 解压进入busybox目录: root@linuxid ...

  7. 嵌入式linux编程,嵌入式Linux学习笔记 - 嵌入式Linux基础知识和开发环境的构建_Linux编程_Linux公社-Linux系统门户网站...

    注:所有内容基于友善之臂Mini2440开发板 一.嵌入式Linux开发环境的构建 嵌入式开发一般分为三个步骤: 1.编译bootloader,烧到开发板 2.编译嵌入式Linux内核,烧到开发板 3 ...

  8. c主线程如何等待子线程结束 linux_使用互斥量进行同步 - Linux C进程与多线程入门_Linux编程_Linux公社-Linux系统门户网站...

    互斥 简单地理解就是,一个线程进入工作区后,如果有其他线程想要进入工作区,它就会进入等待状态,要等待工作区内的线程结束后才可以进入. 基本函数 (1) pthread_mutex_init函数 原型: ...

  9. 给linux内核传递数组,数组与指针 - Linux C编程实战之路_Linux编程_Linux公社-Linux系统门户网站...

    谈到C语言编程,数组和指针是很多人的心头大石,总觉得它们是重点难点,重点是没错的,但绝不是什么难点,要说C语言的难点,客观地讲应该是带参宏,而数组和指针,概念浅显易懂,操作简洁方便,根本不是很多初学者 ...

最新文章

  1. JavaScript基础教程(三)
  2. 英语语法---形容词性从句详解
  3. nzhtl1477-ただいま帰りました ( bfs )
  4. 大叔手记(12):我的一次面试经历(谈大叔如何应对面试官)
  5. GCD API 理解 (一)
  6. apache安装_kali Linux下的Apache的配置和安装:
  7. python七段数码管绘制学号_python七段数码管绘制
  8. 获取当地天气_6 点起来看天气预报?正经人谁看天气预报,原来是为了看她
  9. bundle 安装_centos6.7安装zabbix4
  10. mac sublime java_Mac环境下使用SublimeText写Java代码
  11. mycat php pdo,mycat程序写入和读取中文乱码解决
  12. c语言中的memset函数与bzero函数
  13. Java后台开发常见官方网站汇总
  14. linux会计软件,免费好用的会计软件(Manager for Mac)
  15. 联想微型计算机7360,我的电脑是联想启天M7360台式品牌机,想升级CPU,请问可以换什么CPU?...
  16. PHP架构师“精简”进阶路线规划
  17. PS打开前景色快捷键
  18. AirTest Poco定位 和启动方式
  19. K210应用2-使用GPIO控制LED状态
  20. 瑞士酒店管理大学计算机专业,瑞士DCT国际酒店管理学院生活费用详细介绍

热门文章

  1. uni-app/微信小程序:验证手机号 身份证 邮箱(正则表达式)
  2. oracle的parse是什么意思,Oracle性能测量体系(Parse Time)
  3. ‘ping‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
  4. 系统升级后找不到网络计算机,Windows10系统局域网中共享计算机找不到怎么办
  5. java 的 非短路_Java 短路运算符和非短路运算符
  6. fastadmin 批量上传不成功_shopee批量上传产品步骤
  7. layui table行点击tr_LayUI数据表格行单击事件中选中
  8. mysql 凭证_如何用mysql验证flask/python中的凭证?
  9. c语言字符串截取_笔记 | 自学Python 05:数据类型之字符串
  10. mysql group_concat