主要是参考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_COMPAT res = 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 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 if (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_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter"); if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; } #endif retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif bus_unregister(&i2c_bus_type); return retval; } static void __exit i2c_exit(void) { i2c_del_driver(&dummy_driver); #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); #endif bus_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 #endif typedef 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_DEBUG static 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 = ®[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_DEBUG printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count); #endif } else status = 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_DEBUG printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count); #endif } else status = 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_DEBUG printf(" I2C (0x%02x): Raw Write ERROR !!! (count = %d)\n", devAddr, count); #endif } else status = 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_DEBUG printf(" I2C (0x%02x): Raw Read ERROR !!! count = %d)\n", devAddr, count); #endif } else status = 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; else if(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; }