主要是参考http://blog.csdn.net/cjok376240497/article/details/6972305,对I2C子系统讲解的很透彻的一篇文章,值得一读。

1 硬件特性
1.1 概述

I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA)。由于I2C总线仅需要两根线,因此在电路板上占用的空间更少,带来的问题是带宽较窄。I2C在标准模式下传输速率最高100Kb/s,在快速模式下最高可达400kb/s。属于半双工。在嵌入式系统中,I2C应用非常广泛,大多数微控制器中集成了I2C总线,一般用于和RTC,EEPROM,智能电池电路,传感器,LCD以及其他类似设备之间的通信。I2C总线时钟都是由I2C主控器提供。
1.2 I2C总线传输时序


1.3 I2C总线的信号状态
1、  空闲状态:SDA和SCL都是高电平;
2、  开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;
3、  结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;
4、  数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;
5、  ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
读寄存器的标准流程为:
1.    Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
2.    Slave发送ACK
3.    Master发送reg addr(8bit),等待ACK
4.    Slave发送ACK
5.    Master发起START
6.    Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
7.    Slave发送ACK
8.    Slave发送data(8bit),即寄存器里的值
9.    Master发送ACK
10.  第8步和第9步可以重复多次,即顺序读多个寄存器
10bit地址
10bit的寻址扩展可能寻址的数目.有7bit地址和10bit地址的设备可以连接到相同的I2C总线上,而且7bit寻址和10bit寻址都可以用在所有的总线速度模式下.不过,10bit寻址用的不多.
10bit的从机地址由开始条件(S)或重复开始条件(Sr)后的两个字节组成.第一个字节的前7位是1111 0XX,XX是10bit地址的最高有效位的前两位.第一个字节的第8bit是读写位,决定传输方向.
尽管1111 XXX有8种可能的组合,然后只有1111 0XX这四种可以用于10bit寻址.剩下的1111 1XX这四种是为将来I2C扩展用的.

1.4 从设备地址


从datasheet发现,有三个IO口确定I2C从设备地址后三位,I2C总线从设备使用7位地址,最后一个为读写控制位。下图是TVP5158的原理图,我们可以计算出它的地址,在读取SII9135A的时候,手册上写得是0X60、0X68,这是8位,前7位有效,所以真实的I2C地址为0x30、0x34,第八位代表读写。


1.5 I2C读写方式

下面I2C写操作的步骤:


多字节写的时序

下面是I2C读操作的步骤:


多字节读的时序


具体可参考datasheet

2  I2C子系统
2.1 LinuxI2C子系统架构

在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。


2.2 三大组成部分
1、I2C核心(i2c-core)
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
2、I2C总线驱动(I2Cadapter/Algo driver)
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力。
I2C总线驱动由i2c_adapter和i2c_algorithm来描述
3、I2C客户驱动程序(I2Cclient driver)
I2C客户驱动是对I2C从设备的软件实现,一个具体的I2C客户驱动包括两个部分:一部分是i2c_driver,用于将设备挂接于i2c总线;另一部分是设备本身的驱动。
I2C客户驱动程序由i2c_driver和i2c_client来描述
2.3 所有的I2C驱动代码位于drivers/i2c目录下
I2c-core.c    实现I2C核心的功能
I2c-dev.c     通用的从设备驱动
Chips       特定的I2C设备驱动
Busses      I2C适配器的驱动
Algos       实现了一些I2C总线适配器的algorithm
2.4 I2C驱动编写的两种方法
从上面的图我们可以看到两种编写驱动方法,一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。
2.5 重要的数据结构
每次分析子系统免不了分析它的数据结构,OK我们先来分析一下。
I2c_adapter结构体代表I2C总线控制器

struct i2c_adapter {struct module *owner;unsigned int class;       /*classes to allow probing for */const struct i2c_algorithm*algo; /* 总线上数据传输的算法*/void *algo_data;              /* algorithm 数据 */int timeout;            /* injiffies */int retries;             /* 重试次数 */struct device dev;      /* the adapter device */int nr;char name[48];                 /* 适配器名字 */struct completion dev_released;   /* 用于同步 */
};

I2c_algorithm对应一套通信方法

struct i2c_algorithm {int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, charread_write,u8 command, int size, unioni2c_smbus_data *data);u32 (*functionality) (structi2c_adapter *);
};

Functionality 函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等。
smbus_xfer   函数SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
Master_xfer  函数实现总线上数据传输,与具体的适配器有关
Master_xfer  函数实现模板:

static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{......for (i = 0; i < num; i++) {i2c_adapter_xxx_start();         /*产生起始位*/if (msgs[i]->flags & I2C_M_RD) {    /*读取*/i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);  /*发送从设备地址*/i2c_adapter_xxx_wait_ack();   /*获得从设备的ACK*/
i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);  /*读取len长度的数据到buf中*/} else {i2c_adapter_xxx_setaddr(msg->addr << 1);i2c_adapter_xxx_wait_ack();i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);}}i2c_adapter_xxx_stop(); /*产生停止位*/
}

上面调用的函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。在内核源码中,针对不同的I2C适配器都有master_xfer的实现,风格与模板不尽相同,但是可以用该模板作为参考来看源代码,受益匪浅。
I2c_driver代表I2C从设备驱动

struct i2c_driver {unsignedint class;int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/int (*probe)(struct i2c_client*, const struct i2c_device_id *);int (*remove)(struct i2c_client*);int(*suspend)(struct i2c_client *, pm_message_t mesg);int(*resume)(struct i2c_client *);void(*alert)(struct i2c_client *, unsigned int data);int(*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id*id_table;  /* 该驱动所支持的设备ID表 *//*Device detection callback for automatic device creation */int(*detect)(struct i2c_client *, struct i2c_board_info *);constunsigned short *address_list;structlist_head clients;
};

在新内核中,attach_adapter和detach_adapter已经被probe和remove取代
Id_table用于i2c_driver和i2c_client的匹配
I2c_client代表I2C从设备

struct i2c_client {
unsigned short flags;                 /*I2C_CLIENT_TEN:使用10位从地址,I2C_CLIENT_PEC:使用SMBus包错误检测*/unsignedshort addr;                 /* chipaddress - NOTE: 7bit    */charname[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* 依附的i2c_adapter   */struct i2c_driver *driver;         /* 依附的i2c_driver*/structdevice dev;             /* the devicestructure             */intirq;                         /* irq issuedby device               */structlist_head detected;
};

2.6 核心层提供的接口函数
1、  增加/删除I2C适配器
int i2c_add_adapter(struct i2c_adapter *adapter)->static int i2c_register_adapter(struct i2c_adapter *adap)
int i2c_del_adapter(struct i2c_adapter *adap)

static int i2c_register_adapter(struct i2c_adapter *adap)
{int res = 0;/* Can't register until after driver model init */if (unlikely(WARN_ON(!i2c_bus_type.p))) {res = -EAGAIN;goto out_list;}/* Sanity checks */if (unlikely(adap->name[0] == '\0')) {pr_err("i2c-core: Attempt to register an adapter with ""no name!\n");return -EINVAL;}if (unlikely(!adap->algo)) {pr_err("i2c-core: Attempt to register adapter '%s' with ""no algo!\n", adap->name);return -EINVAL;}rt_mutex_init(&adap->bus_lock);mutex_init(&adap->userspace_clients_lock);INIT_LIST_HEAD(&adap->userspace_clients);/* Set default timeout to 1 second if not already set */if (adap->timeout == 0)adap->timeout = HZ;dev_set_name(&adap->dev, "i2c-%d", adap->nr);adap->dev.bus = &i2c_bus_type;adap->dev.type = &i2c_adapter_type;res = device_register(&adap->dev);if (res)goto out_list;dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);#ifdef CONFIG_I2C_COMPATres = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,adap->dev.parent);if (res)dev_warn(&adap->dev,"Failed to create compatibility class link\n");
#endif/* create pre-declared device nodes */if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);/* Notify drivers */mutex_lock(&core_lock);bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);mutex_unlock(&core_lock);return 0;out_list:mutex_lock(&core_lock);idr_remove(&i2c_adapter_idr, adap->nr);mutex_unlock(&core_lock);return res;
}

Device_register(&adap->dev)  向I2C总线注册一个adapter设备
i2c_scan_static_board_info(adap)   注册所有已知的i2c_client
2、  增加/删除I2C从设备驱动  
int i2c_add_driver(struct i2c_driver *driver)->int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
void i2c_del_driver(struct i2c_driver *driver)

/** An i2c_driver is used with one or more i2c_client (device) nodes to access* i2c slave chips, on a bus instance associated with some i2c_adapter.*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{int res;/* Can't register until after driver model init */if (unlikely(WARN_ON(!i2c_bus_type.p)))return -EAGAIN;/* add the driver to the list of i2c drivers in the driver core */driver->driver.owner = owner;driver->driver.bus = &i2c_bus_type;/* When registration returns, the driver core* will have called probe() for all matching-but-unbound devices.*/res = driver_register(&driver->driver);if (res)return res;pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);INIT_LIST_HEAD(&driver->clients);/* Walk the adapters that are already present */mutex_lock(&core_lock);bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);mutex_unlock(&core_lock);return 0;
}
EXPORT_SYMBOL(i2c_register_driver);

向I2C总线注册一个i2c_driver
3、  i2c传输,发送和接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num)  
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)  
int i2c_master_recv(const struct i2c_client *client, char*buf, int count)

/*** i2c_transfer - execute a single or combined I2C message* @adap: Handle to I2C bus* @msgs: One or more messages to execute before STOP is issued to*    terminate the operation; each message begins with a START.* @num: Number of messages to be executed.** Returns negative errno, else the number of messages executed.** Note that there is no requirement that each message be sent to* the same slave address, although that is the most common model.*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{unsigned long orig_jiffies;int ret, try;/* REVISIT the fault reporting model here is weak:**  - When we get an error after receiving N bytes from a slave,*    there is no way to report "N".**  - When we get a NAK after transmitting N bytes to a slave,*    there is no way to report "N" ... or to let the master*    continue executing the rest of this combined message, if*    that's the appropriate response.**  - When for example "num" is two and we successfully complete*    the first message but get an error part way through the*    second, it's unclear whether that should be reported as*    one (discarding status on the second message) or errno*    (discarding status on the first one).*/if (adap->algo->master_xfer) {
#ifdef DEBUGfor (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) ? "+" : "");}
#endifif (in_atomic() || irqs_disabled()) {ret = i2c_trylock_adapter(adap);if (!ret)/* I2C activity is ongoing. */return -EAGAIN;} else {i2c_lock_adapter(adap);}/* Retry automatically on arbitration loss */orig_jiffies = jiffies;for (ret = 0, try = 0; try <= adap->retries; try++) {ret = adap->algo->master_xfer(adap, msgs, num);if (ret != -EAGAIN)break;if (time_after(jiffies, orig_jiffies + adap->timeout))break;}i2c_unlock_adapter(adap);return ret;} else {dev_dbg(&adap->dev, "I2C level transfers not supported\n");return -EOPNOTSUPP;}
}
EXPORT_SYMBOL(i2c_transfer);

最终会调用到适配器实现的master_xfer函数来完成数据传输工作

2.6 I2C子系统初始化

struct bus_type i2c_bus_type = {.name        = "i2c",.match        = i2c_device_match,.probe        = i2c_device_probe,.remove        = i2c_device_remove,.shutdown    = i2c_device_shutdown,.pm        = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
//
static int __init i2c_init(void)
{int retval;retval = bus_register(&i2c_bus_type);if (retval)return retval;
#ifdef CONFIG_I2C_COMPATi2c_adapter_compat_class = class_compat_register("i2c-adapter");if (!i2c_adapter_compat_class) {retval = -ENOMEM;goto bus_err;}
#endifretval = i2c_add_driver(&dummy_driver);if (retval)goto class_err;return 0;class_err:
#ifdef CONFIG_I2C_COMPATclass_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endifbus_unregister(&i2c_bus_type);return retval;
}static void __exit i2c_exit(void)
{i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPATclass_compat_unregister(i2c_adapter_compat_class);
#endifbus_unregister(&i2c_bus_type);
}/* We must initialize early, because some subsystems register i2c drivers* in subsys_initcall() code, but are linked (and initialized) before i2c.*/
postcore_initcall(i2c_init);
module_exit(i2c_exit);

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源码分析
初始化模块

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函数之前,我们需要了解一个结构体:

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

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函数:

static noinline int i2cdev_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 用户空间驱动模板

#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>#define OSA_SOK      0  ///< Status : OK
#define OSA_EFAIL   -1  ///< Status : Generic error#define I2C_DEFAULT_INST_ID  (2)
#define I2C_TRANSFER_SIZE_MAX   (254)#ifndef TRUE
#define TRUE              1
#endif#ifndef FALSE
#define FALSE             0
#endiftypedef unsigned short    Bool;
typedef unsigned long long Uint64;      ///< Unsigned 64-bit integer
typedef unsigned int Uint32;            ///< Unsigned 32-bit integer
typedef unsigned short Uint16;          ///< Unsigned 16-bit integer
typedef unsigned char Uint8;            ///< Unsigned  8-bit integer

#define OSA_ERROR(...) \do \{ \fprintf(stderr, " ERROR  (%s|%s|%d): ", __FILE__, __func__, __LINE__); \fprintf(stderr, __VA_ARGS__); \} \while(0);typedef struct {int fd;} OSA_I2cHndl;#define OSA_I2C_DEBUGstatic char xtod(char c) {if (c>='0' && c<='9') return c-'0';if (c>='A' && c<='F') return c-'A'+10;if (c>='a' && c<='f') return c-'a'+10;return c=0;        // not Hex digit
}static int HextoDec(char *hex, int l)
{if (*hex==0) return(l);return HextoDec(hex+1, l*16+xtod(*hex)); // hex+1?
}int xstrtoi(char *hex)      // hex string to integer
{return HextoDec(hex,0);
}int OSA_i2cOpen(OSA_I2cHndl *hndl, Uint8 instId)
{char deviceName[20];int status = 0;sprintf(deviceName, "/dev/i2c-%d", instId);printf("dev name = %s \n", deviceName);hndl->fd = open(deviceName, O_RDWR);if(hndl->fd<0)return OSA_EFAIL;return status;
}int OSA_i2cRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *reg, Uint8 *value, Uint32 count)
{int i, j;int status = 0;struct i2c_msg * msgs = NULL;struct i2c_rdwr_ioctl_data data;msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count * 2);if(msgs==NULL){printf(" I2C (0x%02x): Malloc ERROR during Read !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);return OSA_EFAIL;}for (i = 0, j = 0; i < count * 2; i+=2, j++){msgs[i].addr  = devAddr;msgs[i].flags = 0;msgs[i].len   = 1;msgs[i].buf   = &reg[j];msgs[i+1].addr  = devAddr;msgs[i+1].flags = I2C_M_RD /* | I2C_M_REV_DIR_ADDR */;msgs[i+1].len   = 1;msgs[i+1].buf   = &value[j];}data.msgs = msgs;data.nmsgs = count * 2;status = ioctl(hndl->fd, I2C_RDWR, &data);if(status < 0){status = OSA_EFAIL;
#ifdef OSA_I2C_DEBUGprintf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
#endif}elsestatus = OSA_SOK;free(msgs);return status;
}int OSA_i2cWrite8(OSA_I2cHndl *hndl, Uint16 devAddr,  Uint8 *reg, Uint8 *value, Uint32 count)
{int i,j;unsigned char * bufAddr;int status = 0;struct i2c_msg * msgs = NULL;struct i2c_rdwr_ioctl_data data;msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count);if(msgs==NULL){printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);return OSA_EFAIL;}bufAddr = (unsigned char *) malloc(sizeof(unsigned char) * count * 2);if(bufAddr == NULL){free(msgs);printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);return OSA_EFAIL;}for (i = 0, j = 0; i < count; i++, j+=2){bufAddr[j] = reg[i];bufAddr[j + 1] = value[i];msgs[i].addr  = devAddr;msgs[i].flags = 0;msgs[i].len   = 2;msgs[i].buf   = &bufAddr[j];}data.msgs = msgs;data.nmsgs = count;status = ioctl(hndl->fd, I2C_RDWR, &data);if(status < 0){status = OSA_EFAIL;
#ifdef OSA_I2C_DEBUGprintf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
#endif}elsestatus = OSA_SOK;free(msgs);free(bufAddr);return status;
}int OSA_i2cRawWrite8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
{int status = 0;struct i2c_msg msgs[1];struct i2c_rdwr_ioctl_data data;msgs[0].addr  = devAddr;msgs[0].flags = 0;msgs[0].len   = count;msgs[0].buf   = value;data.msgs = msgs;data.nmsgs = 1;status = ioctl(hndl->fd, I2C_RDWR, &data);if(status < 0){status = OSA_EFAIL;
#ifdef OSA_I2C_DEBUGprintf(" I2C (0x%02x): Raw Write ERROR !!! (count = %d)\n", devAddr, count);
#endif}elsestatus = OSA_SOK;return status;
}int OSA_i2cRawRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
{int status = 0;struct i2c_msg msgs[1];struct i2c_rdwr_ioctl_data data;msgs[0].addr  = devAddr;msgs[0].flags = I2C_M_RD;msgs[0].len   = count;msgs[0].buf   = value;data.msgs = msgs;data.nmsgs = 1;status = ioctl(hndl->fd, I2C_RDWR, &data);if(status < 0){status = OSA_EFAIL;
#ifdef OSA_I2C_DEBUGprintf(" I2C (0x%02x): Raw Read ERROR !!! count = %d)\n", devAddr, count);
#endif}elsestatus = OSA_SOK;return status;
}int OSA_i2cClose(OSA_I2cHndl *hndl)
{return close(hndl->fd);
}int OSA_i2cTestShowUsage(char *str)
{printf(" \n");printf(" I2C Test Utility, \r\n");printf(" Usage: %s -r|-w <devAddrInHex> <regAddrInHex> <regValueInHex or numRegsToReadInDec> \r\n", str);printf(" \n");return 0;
}int main(int argc, char **argv)
{OSA_I2cHndl i2cHndl;Uint8 devAddr, numRegs;Bool doRead;int status, i;static Uint8 regAddr[I2C_TRANSFER_SIZE_MAX], regValue8[I2C_TRANSFER_SIZE_MAX];if(argc<3) {OSA_i2cTestShowUsage(argv[0]);return -1;}if(strcmp(argv[1], "-r")==0)doRead=TRUE;elseif(strcmp(argv[1], "-w")==0)doRead=FALSE;else {OSA_i2cTestShowUsage(argv[0]);return -1;}devAddr = 0;numRegs = 4;regValue8[0] = 0;regAddr[0] = 0;if(argc>2)devAddr = xstrtoi(argv[2]);if(argc>3)regAddr[0] = xstrtoi(argv[3]);if(argc>4) {if(doRead){numRegs = atoi(argv[4]);if(numRegs>I2C_TRANSFER_SIZE_MAX)numRegs = I2C_TRANSFER_SIZE_MAX;}else {regValue8[0] = xstrtoi(argv[4]);}}if(devAddr==0) {printf(" I2C: Invalid device address\n");OSA_i2cTestShowUsage(argv[0]);return -1;}status = OSA_i2cOpen(&i2cHndl, I2C_DEFAULT_INST_ID);if(status != OSA_SOK) {OSA_ERROR("OSA_i2cOpen( instId = %d )\n", I2C_DEFAULT_INST_ID);return status;}if(status==OSA_SOK){if(doRead) {for(i=0; i<numRegs; i++)regValue8[i] = 0;for(i=1; i<numRegs; i++){regAddr[i] = regAddr[0]+i;}status = OSA_i2cRead8(&i2cHndl, devAddr, regAddr, regValue8, numRegs);if(status==OSA_SOK) {for(i=0; i<numRegs; i++) {printf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[i], regValue8[i] );}} else {printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, regAddr[0], numRegs);}} else {status = OSA_i2cWrite8(&i2cHndl, devAddr, regAddr, regValue8, 1);if(status==OSA_SOK) {status = OSA_i2cRead8(&i2cHndl, devAddr, regAddr, regValue8, 1);}if(status==OSA_SOK) {printf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[0], regValue8[0] );} else {printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], value = 0x%02x\n", devAddr, regAddr[0], regValue8[0]);}}OSA_i2cClose(&i2cHndl);}return 0;
}

Linux驱动子系统之I2C(一)相关推荐

  1. Linux驱动开发—内核I2C驱动详解

    Linux驱动开发--内核I2C驱动 I2C驱动文件结构 I2C数据传输过程 i2c_transfer i2c_msg I2C通讯常用的接口函数(老版本) 快速读写接口函数:(连续读写) 常用的读操作 ...

  2. linux驱动系列学习之i2c子系统(四)

    一.i2c子系统简介 1. i2c总线 i2c总线因为只用SCL.SDA两根线就实现了设备之间的数据互传,极大的简化PCB布线,因此,2c总线在EEPROM.小型LCD等设备中应用极光.i2c的相关时 ...

  3. 【linux驱动开发】i2c驱动框架之温湿度传感器htu21d

    文章目录 一.linux内核i2c驱动框架 1.1 i2c适配器 1.2 i2c设备驱动 1.3 i2c设备驱动匹配过程 二.温湿度传感器htu21d 三.htu21d设备驱动编写 3.1 修改设备树 ...

  4. 嵌入式Linux驱动笔记(十一)------i2c设备之mpu6050驱动

    ###你好!这里是风筝的博客, ###欢迎和我一起交流. 上一节讲了i2c框架: 嵌入式Linux驱动笔记(十)------通俗易懂式了解i2c框架 这次就来写一写真正的i2c设备驱动: mpu605 ...

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

    代码中,我添加了很多注释,应该不难理解,有错误大家可以指出来,我再改正 #include <linux/kernel.h> #include <linux/module.h> ...

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

    [概述] 之前在介绍I2C子系统时,提到过使用i2c-dev.c文件在应用程序中实现我们的I2C从设备驱动.不过,它实现的是一个虚拟,临时的i2c_client,随着设备文件的打开而产生,并随着设备文 ...

  7. 【linux iic子系统】i2c设备的添加方法(四)

    文章目录 前言 一.静态注册 二.动态注册 三.用户空间注册 四.i2c驱动扫描注册 前言 I2C设备的4种添加方法: 1)静态注册 2)动态注册 3)用户空间注册 4)i2c驱动扫描注册 一.静态注 ...

  8. 嵌入式Linux驱动笔记--转自风筝丶

    为了阅读学习方便,将系列博客的网址进行粘贴,感谢原博客的分享. 嵌入式Linux驱动笔记(一)------第一个LED驱动程序 嵌入式Linux驱动笔记(二)------定时器 嵌入式Linux驱动笔 ...

  9. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

最新文章

  1. SQL2005客户端连接到SQL2000服务器存在的问题
  2. 数据挖掘-数据清理过程
  3. 单元格格式_你最想要的自定义单元格格式来了!
  4. 从零开始数据科学与机器学习算法-KNN分类算法-07
  5. 我的同事Leo做的一个AR增强现实的项目
  6. 医疗:ICU(10)
  7. vc6.0mfc中单选按钮如何分组_按钮系列02-搞定按钮和选框的14个秘诀
  8. java基础—采用递归的方式来获取相关目录下的子目录的名称
  9. android-handler、looper、messageQueue、message
  10. Python 处理 PDF 的神器 -- PyMuPDF(上)
  11. 如何将pdf中的矢量图另存为图片
  12. 如何调用阿里云、百度云API接口
  13. Minecraft教育版——两个村庄的故事
  14. 消费者太穷不愿买手机?苹果的份额创新高,撕下国产手机遮羞布
  15. lampp mysql未启动,lampp1.7中mysql无法启动问题
  16. 开篇──纪念调零的百合
  17. 疫情开发,软件测试行情趋势是怎么样的?
  18. 学习笔记14--其他自动驾驶开发平台
  19. 2021长城杯pwn部分wp
  20. Linux 内核配置选项

热门文章

  1. VC++ 利用MFC的CWindowDC类实现画线功能 在桌面窗口中画线 绘制彩色线条 CPen nPenStyle nWidth crColor
  2. stop word理解及超全的停用词表
  3. 1_22_python基础学习_0518
  4. 人生海海,山山而川,不过尔尔。
  5. ffmpeg项目编译出错问题解决方案.
  6. 《Kali Linux渗透测试的艺术》—第2章2.3节安全测试方法论
  7. ThreadX移植——STM32H7+MDK-AC6平台
  8. ES6 isFinite()
  9. “pcl_isfinite”: 找不到标识符
  10. 远程主机强迫关闭了一个现有的链接