add: 04_I2C/06_编写APP直接访问EEPROM
143
IMX6ULL/doc_pic/04_I2C/06_编写APP直接访问EEPROM.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
## 编写APP直接访问EEPROM
|
||||||
|
|
||||||
|
参考资料:
|
||||||
|
|
||||||
|
* Linux驱动程序: `drivers/i2c/i2c-dev.c`
|
||||||
|
* I2C-Tools-4.2: `https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/`
|
||||||
|
* AT24cxx.pdf
|
||||||
|
|
||||||
|
本节源码:GIT仓库中
|
||||||
|
|
||||||
|
* `doc_and_source_for_drivers\IMX6ULL\source\04_I2C\01_at24c02_test`
|
||||||
|
* `doc_and_source_for_drivers\STM32MP157\source\A7\04_I2C\01_at24c02_test`
|
||||||
|
|
||||||
|
### 1. 硬件连接
|
||||||
|
|
||||||
|
* STM32MP157的I2C模块连接方法
|
||||||
|

|
||||||
|
|
||||||
|
* IMX6ULL的I2C模块连接方法
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 2. AT24C02访问方法
|
||||||
|
|
||||||
|
#### 2.1 设备地址
|
||||||
|
|
||||||
|
从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
打开I2C模块的原理图(这2个文件是一样的):
|
||||||
|
|
||||||
|
* `STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||||
|
* `IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||||
|
* 如下:
|
||||||
|

|
||||||
|
|
||||||
|
从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.2 写数据
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.3 读数据
|
||||||
|
|
||||||
|
可以读1个字节,也可以连续读出多个字节。
|
||||||
|
连续读多个字节时,芯片内部的地址会自动累加。
|
||||||
|
当地址到达存储空间最后一个地址时,会从0开始。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 3. 使用I2C-Tools的函数编程
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 4. 编译
|
||||||
|
|
||||||
|
#### 4.1 在Ubuntu设置交叉编译工具链
|
||||||
|
|
||||||
|
* STM32MP157
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export ARCH=arm
|
||||||
|
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||||
|
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
* IMX6ULL
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export ARCH=arm
|
||||||
|
export CROSS_COMPILE=arm-linux-gnueabihf-
|
||||||
|
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 4.2 使用I2C-Tools的源码
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 4.3 编译
|
||||||
|
|
||||||
|
为IMX6ULL编译时,有如下错误:
|
||||||
|

|
||||||
|
|
||||||
|
这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。
|
||||||
|
|
||||||
|
需要我们自己提供这个头文件,解决方法:
|
||||||
|
|
||||||
|
* 提供头文件:
|
||||||
|

|
||||||
|
|
||||||
|
* 修改Makefile指定头文件目录
|
||||||
|
|
||||||
|
```shell
|
||||||
|
all:
|
||||||
|
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 4.4 上机测试
|
||||||
|
|
||||||
|
**以下命令在开发板中执行。**
|
||||||
|
|
||||||
|
* 挂载NFS
|
||||||
|
|
||||||
|
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||||
|
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||||
|
```
|
||||||
|
|
||||||
|
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* 复制、执行程序
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# cp /mnt/at24c02_test /bin
|
||||||
|
[root@100ask:~]# at24c02_test 0 w www.100ask.net
|
||||||
|
[root@100ask:~]# at24c02_test 0 r
|
||||||
|
get data: www.100ask.net
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 886 KiB |
|
After Width: | Height: | Size: 876 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/038_at24c02_device_address.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/039_at24c02_sch.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/040_at24c02_byte_write.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/041_at24c02_byte_read.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
3
IMX6ULL/source/04_I2C/01_at24c02_test/Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
all:
|
||||||
|
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||||
|
|
||||||
102
IMX6ULL/source/04_I2C/01_at24c02_test/at24c02_test.c
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
#include <i2c/smbus.h>
|
||||||
|
#include "i2cbusses.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
|
||||||
|
* ./at24c02 <i2c_bus_number> r
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned char dev_addr = 0x50;
|
||||||
|
unsigned char mem_addr = 0;
|
||||||
|
unsigned char buf[32];
|
||||||
|
|
||||||
|
int file;
|
||||||
|
char filename[20];
|
||||||
|
unsigned char *str;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
struct timespec req;
|
||||||
|
|
||||||
|
if (argc != 3 && argc != 4)
|
||||||
|
{
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
|
||||||
|
printf("read eeprom: %s <i2c_bus_number> r\n", argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
|
||||||
|
if (file < 0)
|
||||||
|
{
|
||||||
|
printf("can't open %s\n", filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_slave_addr(file, dev_addr, 1))
|
||||||
|
{
|
||||||
|
printf("can't set_slave_addr\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[2][0] == 'w')
|
||||||
|
{
|
||||||
|
// write str: argv[3]
|
||||||
|
str = argv[3];
|
||||||
|
|
||||||
|
req.tv_sec = 0;
|
||||||
|
req.tv_nsec = 20000000; /* 20ms */
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
// mem_addr, *str
|
||||||
|
// mem_addr++, str++
|
||||||
|
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_write_byte_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// wait tWR(10ms)
|
||||||
|
nanosleep(&req, NULL);
|
||||||
|
|
||||||
|
mem_addr++;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_write_byte_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_read_i2c_block_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[31] = '\0';
|
||||||
|
printf("get data: %s\n", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
455
IMX6ULL/source/04_I2C/01_at24c02_test/i2cbusses.c
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
|
||||||
|
Part of user-space programs to access for I2C
|
||||||
|
devices.
|
||||||
|
Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
|
||||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||||
|
Copyright (C) 2008-2012 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* For strdup and snprintf */
|
||||||
|
#define _BSD_SOURCE 1 /* for glibc <= 2.19 */
|
||||||
|
#define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/param.h> /* for NAME_MAX */
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h> /* for strcasecmp() */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "i2cbusses.h"
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
|
||||||
|
|
||||||
|
struct adap_type {
|
||||||
|
const char *funcs;
|
||||||
|
const char* algo;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct adap_type adap_types[5] = {
|
||||||
|
{ .funcs = "dummy",
|
||||||
|
.algo = "Dummy bus", },
|
||||||
|
{ .funcs = "isa",
|
||||||
|
.algo = "ISA bus", },
|
||||||
|
{ .funcs = "i2c",
|
||||||
|
.algo = "I2C adapter", },
|
||||||
|
{ .funcs = "smbus",
|
||||||
|
.algo = "SMBus adapter", },
|
||||||
|
{ .funcs = "unknown",
|
||||||
|
.algo = "N/A", },
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum adt i2c_get_funcs(int i2cbus)
|
||||||
|
{
|
||||||
|
unsigned long funcs;
|
||||||
|
int file;
|
||||||
|
char filename[20];
|
||||||
|
enum adt ret;
|
||||||
|
|
||||||
|
file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
|
||||||
|
if (file < 0)
|
||||||
|
return adt_unknown;
|
||||||
|
|
||||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0)
|
||||||
|
ret = adt_unknown;
|
||||||
|
else if (funcs & I2C_FUNC_I2C)
|
||||||
|
ret = adt_i2c;
|
||||||
|
else if (funcs & (I2C_FUNC_SMBUS_BYTE |
|
||||||
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||||
|
I2C_FUNC_SMBUS_WORD_DATA))
|
||||||
|
ret = adt_smbus;
|
||||||
|
else
|
||||||
|
ret = adt_dummy;
|
||||||
|
|
||||||
|
close(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove trailing spaces from a string
|
||||||
|
Return the new string length including the trailing NUL */
|
||||||
|
static int rtrim(char *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
|
||||||
|
s[i] = '\0';
|
||||||
|
return i + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_adapters(struct i2c_adap *adapters)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; adapters[i].name; i++)
|
||||||
|
free(adapters[i].name);
|
||||||
|
free(adapters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We allocate space for the adapters in bunches. The last item is a
|
||||||
|
terminator, so here we start with room for 7 adapters, which should
|
||||||
|
be enough in most cases. If not, we allocate more later as needed. */
|
||||||
|
#define BUNCH 8
|
||||||
|
|
||||||
|
/* n must match the size of adapters at calling time */
|
||||||
|
static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
|
||||||
|
{
|
||||||
|
struct i2c_adap *new_adapters;
|
||||||
|
|
||||||
|
new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
|
||||||
|
if (!new_adapters) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
|
||||||
|
|
||||||
|
return new_adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i2c_adap *gather_i2c_busses(void)
|
||||||
|
{
|
||||||
|
char s[120];
|
||||||
|
struct dirent *de, *dde;
|
||||||
|
DIR *dir, *ddir;
|
||||||
|
FILE *f;
|
||||||
|
char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
|
||||||
|
int foundsysfs = 0;
|
||||||
|
int len, count = 0;
|
||||||
|
struct i2c_adap *adapters;
|
||||||
|
|
||||||
|
adapters = calloc(BUNCH, sizeof(struct i2c_adap));
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* look in /proc/bus/i2c */
|
||||||
|
if ((f = fopen("/proc/bus/i2c", "r"))) {
|
||||||
|
while (fgets(s, 120, f)) {
|
||||||
|
char *algo, *name, *type, *all;
|
||||||
|
int len_algo, len_name, len_type;
|
||||||
|
int i2cbus;
|
||||||
|
|
||||||
|
algo = strrchr(s, '\t');
|
||||||
|
*(algo++) = '\0';
|
||||||
|
len_algo = rtrim(algo);
|
||||||
|
|
||||||
|
name = strrchr(s, '\t');
|
||||||
|
*(name++) = '\0';
|
||||||
|
len_name = rtrim(name);
|
||||||
|
|
||||||
|
type = strrchr(s, '\t');
|
||||||
|
*(type++) = '\0';
|
||||||
|
len_type = rtrim(type);
|
||||||
|
|
||||||
|
sscanf(s, "i2c-%d", &i2cbus);
|
||||||
|
|
||||||
|
if ((count + 1) % BUNCH == 0) {
|
||||||
|
/* We need more space */
|
||||||
|
adapters = more_adapters(adapters, count + 1);
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
all = malloc(len_name + len_type + len_algo);
|
||||||
|
if (all == NULL) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
adapters[count].nr = i2cbus;
|
||||||
|
adapters[count].name = strcpy(all, name);
|
||||||
|
adapters[count].funcs = strcpy(all + len_name, type);
|
||||||
|
adapters[count].algo = strcpy(all + len_name + len_type,
|
||||||
|
algo);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look in sysfs */
|
||||||
|
/* First figure out where sysfs was mounted */
|
||||||
|
if ((f = fopen("/proc/mounts", "r")) == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
while (fgets(n, NAME_MAX, f)) {
|
||||||
|
sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
|
||||||
|
if (strcasecmp(fstype, "sysfs") == 0) {
|
||||||
|
foundsysfs++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
if (! foundsysfs) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bus numbers in i2c-adapter don't necessarily match those in
|
||||||
|
i2c-dev and what we really care about are the i2c-dev numbers.
|
||||||
|
Unfortunately the names are harder to get in i2c-dev */
|
||||||
|
strcat(sysfs, "/class/i2c-dev");
|
||||||
|
if(!(dir = opendir(sysfs)))
|
||||||
|
goto done;
|
||||||
|
/* go through the busses */
|
||||||
|
while ((de = readdir(dir)) != NULL) {
|
||||||
|
if (!strcmp(de->d_name, "."))
|
||||||
|
continue;
|
||||||
|
if (!strcmp(de->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* this should work for kernels 2.6.5 or higher and */
|
||||||
|
/* is preferred because is unambiguous */
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/name", sysfs, de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f = fopen(n, "r");
|
||||||
|
/* this seems to work for ISA */
|
||||||
|
if(f == NULL) {
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/device/name", sysfs,
|
||||||
|
de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f = fopen(n, "r");
|
||||||
|
}
|
||||||
|
/* non-ISA is much harder */
|
||||||
|
/* and this won't find the correct bus name if a driver
|
||||||
|
has more than one bus */
|
||||||
|
if(f == NULL) {
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/device", sysfs,
|
||||||
|
de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!(ddir = opendir(n)))
|
||||||
|
continue;
|
||||||
|
while ((dde = readdir(ddir)) != NULL) {
|
||||||
|
if (!strcmp(dde->d_name, "."))
|
||||||
|
continue;
|
||||||
|
if (!strcmp(dde->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
if ((!strncmp(dde->d_name, "i2c-", 4))) {
|
||||||
|
len = snprintf(n, NAME_MAX,
|
||||||
|
"%s/%s/device/%s/name",
|
||||||
|
sysfs, de->d_name,
|
||||||
|
dde->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: path truncated\n",
|
||||||
|
n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if((f = fopen(n, "r")))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found:
|
||||||
|
if (f != NULL) {
|
||||||
|
int i2cbus;
|
||||||
|
enum adt type;
|
||||||
|
char *px;
|
||||||
|
|
||||||
|
px = fgets(s, 120, f);
|
||||||
|
fclose(f);
|
||||||
|
if (!px) {
|
||||||
|
fprintf(stderr, "%s: read error\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((px = strchr(s, '\n')) != NULL)
|
||||||
|
*px = 0;
|
||||||
|
if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
|
||||||
|
continue;
|
||||||
|
if (!strncmp(s, "ISA ", 4)) {
|
||||||
|
type = adt_isa;
|
||||||
|
} else {
|
||||||
|
/* Attempt to probe for adapter capabilities */
|
||||||
|
type = i2c_get_funcs(i2cbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((count + 1) % BUNCH == 0) {
|
||||||
|
/* We need more space */
|
||||||
|
adapters = more_adapters(adapters, count + 1);
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
adapters[count].nr = i2cbus;
|
||||||
|
adapters[count].name = strdup(s);
|
||||||
|
if (adapters[count].name == NULL) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
adapters[count].funcs = adap_types[type].funcs;
|
||||||
|
adapters[count].algo = adap_types[type].algo;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lookup_i2c_bus_by_name(const char *bus_name)
|
||||||
|
{
|
||||||
|
struct i2c_adap *adapters;
|
||||||
|
int i, i2cbus = -1;
|
||||||
|
|
||||||
|
adapters = gather_i2c_busses();
|
||||||
|
if (adapters == NULL) {
|
||||||
|
fprintf(stderr, "Error: Out of memory!\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk the list of i2c busses, looking for the one with the
|
||||||
|
right name */
|
||||||
|
for (i = 0; adapters[i].name; i++) {
|
||||||
|
if (strcmp(adapters[i].name, bus_name) == 0) {
|
||||||
|
if (i2cbus >= 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: I2C bus name is not unique!\n");
|
||||||
|
i2cbus = -4;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
i2cbus = adapters[i].nr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2cbus == -1)
|
||||||
|
fprintf(stderr, "Error: I2C bus name doesn't match any "
|
||||||
|
"bus present!\n");
|
||||||
|
|
||||||
|
done:
|
||||||
|
free_adapters(adapters);
|
||||||
|
return i2cbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse an I2CBUS command line argument and return the corresponding
|
||||||
|
* bus number, or a negative value if the bus is invalid.
|
||||||
|
*/
|
||||||
|
int lookup_i2c_bus(const char *i2cbus_arg)
|
||||||
|
{
|
||||||
|
unsigned long i2cbus;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
i2cbus = strtoul(i2cbus_arg, &end, 0);
|
||||||
|
if (*end || !*i2cbus_arg) {
|
||||||
|
/* Not a number, maybe a name? */
|
||||||
|
return lookup_i2c_bus_by_name(i2cbus_arg);
|
||||||
|
}
|
||||||
|
if (i2cbus > 0xFFFFF) {
|
||||||
|
fprintf(stderr, "Error: I2C bus out of range!\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2cbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a CHIP-ADDRESS command line argument and return the corresponding
|
||||||
|
* chip address, or a negative value if the address is invalid.
|
||||||
|
*/
|
||||||
|
int parse_i2c_address(const char *address_arg, int all_addrs)
|
||||||
|
{
|
||||||
|
long address;
|
||||||
|
char *end;
|
||||||
|
long min_addr = 0x08;
|
||||||
|
long max_addr = 0x77;
|
||||||
|
|
||||||
|
address = strtol(address_arg, &end, 0);
|
||||||
|
if (*end || !*address_arg) {
|
||||||
|
fprintf(stderr, "Error: Chip address is not a number!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_addrs) {
|
||||||
|
min_addr = 0x00;
|
||||||
|
max_addr = 0x7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address < min_addr || address > max_addr) {
|
||||||
|
fprintf(stderr, "Error: Chip address out of range "
|
||||||
|
"(0x%02lx-0x%02lx)!\n", min_addr, max_addr);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
|
||||||
|
{
|
||||||
|
int file, len;
|
||||||
|
|
||||||
|
len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
|
||||||
|
if (len >= (int)size) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", filename);
|
||||||
|
return -EOVERFLOW;
|
||||||
|
}
|
||||||
|
file = open(filename, O_RDWR);
|
||||||
|
|
||||||
|
if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
|
||||||
|
len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
|
||||||
|
if (len >= (int)size) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", filename);
|
||||||
|
return -EOVERFLOW;
|
||||||
|
}
|
||||||
|
file = open(filename, O_RDWR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file < 0 && !quiet) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
fprintf(stderr, "Error: Could not open file "
|
||||||
|
"`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
|
||||||
|
i2cbus, i2cbus, strerror(ENOENT));
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: Could not open file "
|
||||||
|
"`%s': %s\n", filename, strerror(errno));
|
||||||
|
if (errno == EACCES)
|
||||||
|
fprintf(stderr, "Run as root?\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_slave_addr(int file, int address, int force)
|
||||||
|
{
|
||||||
|
/* With force, let the user read from/write to the registers
|
||||||
|
even when a driver is also running */
|
||||||
|
if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: Could not set address to 0x%02x: %s\n",
|
||||||
|
address, strerror(errno));
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
44
IMX6ULL/source/04_I2C/01_at24c02_test/i2cbusses.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
i2cbusses.h - Part of the i2c-tools package
|
||||||
|
|
||||||
|
Copyright (C) 2004-2010 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _I2CBUSSES_H
|
||||||
|
#define _I2CBUSSES_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct i2c_adap {
|
||||||
|
int nr;
|
||||||
|
char *name;
|
||||||
|
const char *funcs;
|
||||||
|
const char *algo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_adap *gather_i2c_busses(void);
|
||||||
|
void free_adapters(struct i2c_adap *adapters);
|
||||||
|
|
||||||
|
int lookup_i2c_bus(const char *i2cbus_arg);
|
||||||
|
int parse_i2c_address(const char *address_arg, int all_addrs);
|
||||||
|
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);
|
||||||
|
int set_slave_addr(int file, int address, int force);
|
||||||
|
|
||||||
|
#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n"
|
||||||
|
|
||||||
|
#endif
|
||||||
52
IMX6ULL/source/04_I2C/01_at24c02_test/include/i2c/smbus.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
smbus.h - SMBus level access helper functions
|
||||||
|
|
||||||
|
Copyright (C) 1995-1997 Simon G. Vogl
|
||||||
|
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||||
|
Copyright (C) 2012-2017 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published
|
||||||
|
by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_I2C_SMBUS_H
|
||||||
|
#define LIB_I2C_SMBUS_H
|
||||||
|
|
||||||
|
#define I2C_API_VERSION 0x100
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
|
||||||
|
extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||||
|
int size, union i2c_smbus_data *data);
|
||||||
|
|
||||||
|
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_byte(int file);
|
||||||
|
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
|
||||||
|
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
|
||||||
|
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
|
||||||
|
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
|
||||||
|
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||||
|
ask for less than 32 bytes, your code will only work with kernels
|
||||||
|
2.6.23 and later. */
|
||||||
|
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values);
|
||||||
|
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values);
|
||||||
|
|
||||||
|
#endif /* LIB_I2C_SMBUS_H */
|
||||||
214
IMX6ULL/source/04_I2C/01_at24c02_test/smbus.c
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
smbus.c - SMBus level access helper functions
|
||||||
|
|
||||||
|
Copyright (C) 1995-1997 Simon G. Vogl
|
||||||
|
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||||
|
Copyright (C) 2012-2013 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published
|
||||||
|
by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <i2c/smbus.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
/* Compatibility defines */
|
||||||
|
#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
|
||||||
|
#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
|
||||||
|
#endif
|
||||||
|
#ifndef I2C_FUNC_SMBUS_PEC
|
||||||
|
#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||||
|
int size, union i2c_smbus_data *data)
|
||||||
|
{
|
||||||
|
struct i2c_smbus_ioctl_data args;
|
||||||
|
__s32 err;
|
||||||
|
|
||||||
|
args.read_write = read_write;
|
||||||
|
args.command = command;
|
||||||
|
args.size = size;
|
||||||
|
args.data = data;
|
||||||
|
|
||||||
|
err = ioctl(file, I2C_SMBUS, &args);
|
||||||
|
if (err == -1)
|
||||||
|
err = -errno;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_quick(int file, __u8 value)
|
||||||
|
{
|
||||||
|
return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_byte(int file)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FF & data.byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_byte(int file, __u8 value)
|
||||||
|
{
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
|
||||||
|
I2C_SMBUS_BYTE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_byte_data(int file, __u8 command)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FF & data.byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.byte = value;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_word_data(int file, __u8 command)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FFFF & data.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.word = value;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.word = value;
|
||||||
|
if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_PROC_CALL, &data))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0x0FFFF & data.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_BLOCK_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i;
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BLOCK_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||||
|
ask for less than 32 bytes, your code will only work with kernels
|
||||||
|
2.6.23 and later. */
|
||||||
|
__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
data.block[0] = length;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
|
||||||
|
I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i;
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||
@@ -85,10 +85,10 @@ git clone https://e.coding.net/weidongshan/doc_and_source_for_drivers.git
|
|||||||
* 2021.02.25 发布"I2C系统":
|
* 2021.02.25 发布"I2C系统":
|
||||||
|
|
||||||
* 修改:03_SMBus协议,增加了I2C Block Read/Write
|
* 修改:03_SMBus协议,增加了I2C Block Read/Write
|
||||||
|
* 新增:05\_无需编写驱动直接访问设备\_I2C-Tools介绍
|
||||||
|
* 新增:06_编写APP直接访问EEPROM
|
||||||
|
|
||||||
* 新增:05\_无需编写驱动直接访问设备\_I2C-Tools介绍
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 6. 联系方式
|
## 6. 联系方式
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,41 @@ Functionality flag: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 2.11 SMBus Block Write - Block Read Process Call
|
#### 2.11 I2C Block Read
|
||||||
|
|
||||||
|
在一般的I2C协议中,也可以连续读出多个字节。
|
||||||
|
它跟`SMBus Block Read`的差别在于设备发出的第1个数据不是长度N,如下图所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
I2C-tools中的函数:i2c_smbus_read_i2c_block_data()。
|
||||||
|
|
||||||
|
先发出`Command Code`(它一般表示芯片内部的寄存器地址),再发出1个字节的`Byte Conut`(表示后续要发出的数据字节数),最后发出全部数据。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Functionality flag: I2C_FUNC_SMBUS_READ_I2C_BLOCK
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.12 I2C Block Write
|
||||||
|
|
||||||
|
在一般的I2C协议中,也可以连续发出多个字节。
|
||||||
|
它跟`SMBus Block Write`的差别在于发出的第1个数据不是长度N,如下图所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
I2C-tools中的函数:i2c_smbus_write_i2c_block_data()。
|
||||||
|
|
||||||
|
先发出`Command Code`(它一般表示芯片内部的寄存器地址),再发出1个字节的`Byte Conut`(表示后续要发出的数据字节数),最后发出全部数据。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Functionality flag: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.13 SMBus Block Write - Block Read Process Call
|
||||||
|
|
||||||
先写一块数据,再读一块数据。
|
先写一块数据,再读一块数据。
|
||||||
|
|
||||||
@@ -231,7 +265,7 @@ Functionality flag: I2C_FUNC_SMBUS_BLOCK_PROC_CALL
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 2.12 Packet Error Checking (PEC)
|
#### 2.14 Packet Error Checking (PEC)
|
||||||
|
|
||||||
PEC是一种错误校验码,如果使用PEC,那么在P信号之前,数据发送方要发送一个字节的PEC码(它是CRC-8码)。
|
PEC是一种错误校验码,如果使用PEC,那么在P信号之前,数据发送方要发送一个字节的PEC码(它是CRC-8码)。
|
||||||
|
|
||||||
|
|||||||
143
STM32MP157/doc_pic/04_I2C/06_编写APP直接访问EEPROM.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
## 编写APP直接访问EEPROM
|
||||||
|
|
||||||
|
参考资料:
|
||||||
|
|
||||||
|
* Linux驱动程序: `drivers/i2c/i2c-dev.c`
|
||||||
|
* I2C-Tools-4.2: `https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/`
|
||||||
|
* AT24cxx.pdf
|
||||||
|
|
||||||
|
本节源码:GIT仓库中
|
||||||
|
|
||||||
|
* `doc_and_source_for_drivers\IMX6ULL\source\04_I2C\01_at24c02_test`
|
||||||
|
* `doc_and_source_for_drivers\STM32MP157\source\A7\04_I2C\01_at24c02_test`
|
||||||
|
|
||||||
|
### 1. 硬件连接
|
||||||
|
|
||||||
|
* STM32MP157的I2C模块连接方法
|
||||||
|

|
||||||
|
|
||||||
|
* IMX6ULL的I2C模块连接方法
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 2. AT24C02访问方法
|
||||||
|
|
||||||
|
#### 2.1 设备地址
|
||||||
|
|
||||||
|
从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
打开I2C模块的原理图(这2个文件是一样的):
|
||||||
|
|
||||||
|
* `STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||||
|
* `IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||||
|
* 如下:
|
||||||
|

|
||||||
|
|
||||||
|
从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.2 写数据
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.3 读数据
|
||||||
|
|
||||||
|
可以读1个字节,也可以连续读出多个字节。
|
||||||
|
连续读多个字节时,芯片内部的地址会自动累加。
|
||||||
|
当地址到达存储空间最后一个地址时,会从0开始。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 3. 使用I2C-Tools的函数编程
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 4. 编译
|
||||||
|
|
||||||
|
#### 4.1 在Ubuntu设置交叉编译工具链
|
||||||
|
|
||||||
|
* STM32MP157
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export ARCH=arm
|
||||||
|
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||||
|
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
* IMX6ULL
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export ARCH=arm
|
||||||
|
export CROSS_COMPILE=arm-linux-gnueabihf-
|
||||||
|
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 4.2 使用I2C-Tools的源码
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 4.3 编译
|
||||||
|
|
||||||
|
为IMX6ULL编译时,有如下错误:
|
||||||
|

|
||||||
|
|
||||||
|
这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。
|
||||||
|
|
||||||
|
需要我们自己提供这个头文件,解决方法:
|
||||||
|
|
||||||
|
* 提供头文件:
|
||||||
|

|
||||||
|
|
||||||
|
* 修改Makefile指定头文件目录
|
||||||
|
|
||||||
|
```shell
|
||||||
|
all:
|
||||||
|
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 4.4 上机测试
|
||||||
|
|
||||||
|
**以下命令在开发板中执行。**
|
||||||
|
|
||||||
|
* 挂载NFS
|
||||||
|
|
||||||
|
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||||
|
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||||
|
```
|
||||||
|
|
||||||
|
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* 复制、执行程序
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[root@100ask:~]# cp /mnt/at24c02_test /bin
|
||||||
|
[root@100ask:~]# at24c02_test 0 w www.100ask.net
|
||||||
|
[root@100ask:~]# at24c02_test 0 r
|
||||||
|
get data: www.100ask.net
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 886 KiB |
|
After Width: | Height: | Size: 876 KiB |
|
After Width: | Height: | Size: 24 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/039_at24c02_sch.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/040_at24c02_byte_write.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/041_at24c02_byte_read.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
3
STM32MP157/source/A7/04_I2C/01_at24c02_test/Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
all:
|
||||||
|
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||||
|
|
||||||
102
STM32MP157/source/A7/04_I2C/01_at24c02_test/at24c02_test.c
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
#include <i2c/smbus.h>
|
||||||
|
#include "i2cbusses.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
|
||||||
|
* ./at24c02 <i2c_bus_number> r
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned char dev_addr = 0x50;
|
||||||
|
unsigned char mem_addr = 0;
|
||||||
|
unsigned char buf[32];
|
||||||
|
|
||||||
|
int file;
|
||||||
|
char filename[20];
|
||||||
|
unsigned char *str;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
struct timespec req;
|
||||||
|
|
||||||
|
if (argc != 3 && argc != 4)
|
||||||
|
{
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
|
||||||
|
printf("read eeprom: %s <i2c_bus_number> r\n", argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
|
||||||
|
if (file < 0)
|
||||||
|
{
|
||||||
|
printf("can't open %s\n", filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_slave_addr(file, dev_addr, 1))
|
||||||
|
{
|
||||||
|
printf("can't set_slave_addr\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[2][0] == 'w')
|
||||||
|
{
|
||||||
|
// write str: argv[3]
|
||||||
|
str = argv[3];
|
||||||
|
|
||||||
|
req.tv_sec = 0;
|
||||||
|
req.tv_nsec = 20000000; /* 20ms */
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
// mem_addr, *str
|
||||||
|
// mem_addr++, str++
|
||||||
|
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_write_byte_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// wait tWR(10ms)
|
||||||
|
nanosleep(&req, NULL);
|
||||||
|
|
||||||
|
mem_addr++;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_write_byte_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
printf("i2c_smbus_read_i2c_block_data err\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[31] = '\0';
|
||||||
|
printf("get data: %s\n", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
455
STM32MP157/source/A7/04_I2C/01_at24c02_test/i2cbusses.c
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
|
||||||
|
Part of user-space programs to access for I2C
|
||||||
|
devices.
|
||||||
|
Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
|
||||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||||
|
Copyright (C) 2008-2012 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* For strdup and snprintf */
|
||||||
|
#define _BSD_SOURCE 1 /* for glibc <= 2.19 */
|
||||||
|
#define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/param.h> /* for NAME_MAX */
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h> /* for strcasecmp() */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "i2cbusses.h"
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
|
||||||
|
|
||||||
|
struct adap_type {
|
||||||
|
const char *funcs;
|
||||||
|
const char* algo;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct adap_type adap_types[5] = {
|
||||||
|
{ .funcs = "dummy",
|
||||||
|
.algo = "Dummy bus", },
|
||||||
|
{ .funcs = "isa",
|
||||||
|
.algo = "ISA bus", },
|
||||||
|
{ .funcs = "i2c",
|
||||||
|
.algo = "I2C adapter", },
|
||||||
|
{ .funcs = "smbus",
|
||||||
|
.algo = "SMBus adapter", },
|
||||||
|
{ .funcs = "unknown",
|
||||||
|
.algo = "N/A", },
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum adt i2c_get_funcs(int i2cbus)
|
||||||
|
{
|
||||||
|
unsigned long funcs;
|
||||||
|
int file;
|
||||||
|
char filename[20];
|
||||||
|
enum adt ret;
|
||||||
|
|
||||||
|
file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
|
||||||
|
if (file < 0)
|
||||||
|
return adt_unknown;
|
||||||
|
|
||||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0)
|
||||||
|
ret = adt_unknown;
|
||||||
|
else if (funcs & I2C_FUNC_I2C)
|
||||||
|
ret = adt_i2c;
|
||||||
|
else if (funcs & (I2C_FUNC_SMBUS_BYTE |
|
||||||
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||||
|
I2C_FUNC_SMBUS_WORD_DATA))
|
||||||
|
ret = adt_smbus;
|
||||||
|
else
|
||||||
|
ret = adt_dummy;
|
||||||
|
|
||||||
|
close(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove trailing spaces from a string
|
||||||
|
Return the new string length including the trailing NUL */
|
||||||
|
static int rtrim(char *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
|
||||||
|
s[i] = '\0';
|
||||||
|
return i + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_adapters(struct i2c_adap *adapters)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; adapters[i].name; i++)
|
||||||
|
free(adapters[i].name);
|
||||||
|
free(adapters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We allocate space for the adapters in bunches. The last item is a
|
||||||
|
terminator, so here we start with room for 7 adapters, which should
|
||||||
|
be enough in most cases. If not, we allocate more later as needed. */
|
||||||
|
#define BUNCH 8
|
||||||
|
|
||||||
|
/* n must match the size of adapters at calling time */
|
||||||
|
static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
|
||||||
|
{
|
||||||
|
struct i2c_adap *new_adapters;
|
||||||
|
|
||||||
|
new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
|
||||||
|
if (!new_adapters) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
|
||||||
|
|
||||||
|
return new_adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i2c_adap *gather_i2c_busses(void)
|
||||||
|
{
|
||||||
|
char s[120];
|
||||||
|
struct dirent *de, *dde;
|
||||||
|
DIR *dir, *ddir;
|
||||||
|
FILE *f;
|
||||||
|
char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
|
||||||
|
int foundsysfs = 0;
|
||||||
|
int len, count = 0;
|
||||||
|
struct i2c_adap *adapters;
|
||||||
|
|
||||||
|
adapters = calloc(BUNCH, sizeof(struct i2c_adap));
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* look in /proc/bus/i2c */
|
||||||
|
if ((f = fopen("/proc/bus/i2c", "r"))) {
|
||||||
|
while (fgets(s, 120, f)) {
|
||||||
|
char *algo, *name, *type, *all;
|
||||||
|
int len_algo, len_name, len_type;
|
||||||
|
int i2cbus;
|
||||||
|
|
||||||
|
algo = strrchr(s, '\t');
|
||||||
|
*(algo++) = '\0';
|
||||||
|
len_algo = rtrim(algo);
|
||||||
|
|
||||||
|
name = strrchr(s, '\t');
|
||||||
|
*(name++) = '\0';
|
||||||
|
len_name = rtrim(name);
|
||||||
|
|
||||||
|
type = strrchr(s, '\t');
|
||||||
|
*(type++) = '\0';
|
||||||
|
len_type = rtrim(type);
|
||||||
|
|
||||||
|
sscanf(s, "i2c-%d", &i2cbus);
|
||||||
|
|
||||||
|
if ((count + 1) % BUNCH == 0) {
|
||||||
|
/* We need more space */
|
||||||
|
adapters = more_adapters(adapters, count + 1);
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
all = malloc(len_name + len_type + len_algo);
|
||||||
|
if (all == NULL) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
adapters[count].nr = i2cbus;
|
||||||
|
adapters[count].name = strcpy(all, name);
|
||||||
|
adapters[count].funcs = strcpy(all + len_name, type);
|
||||||
|
adapters[count].algo = strcpy(all + len_name + len_type,
|
||||||
|
algo);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look in sysfs */
|
||||||
|
/* First figure out where sysfs was mounted */
|
||||||
|
if ((f = fopen("/proc/mounts", "r")) == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
while (fgets(n, NAME_MAX, f)) {
|
||||||
|
sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
|
||||||
|
if (strcasecmp(fstype, "sysfs") == 0) {
|
||||||
|
foundsysfs++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
if (! foundsysfs) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bus numbers in i2c-adapter don't necessarily match those in
|
||||||
|
i2c-dev and what we really care about are the i2c-dev numbers.
|
||||||
|
Unfortunately the names are harder to get in i2c-dev */
|
||||||
|
strcat(sysfs, "/class/i2c-dev");
|
||||||
|
if(!(dir = opendir(sysfs)))
|
||||||
|
goto done;
|
||||||
|
/* go through the busses */
|
||||||
|
while ((de = readdir(dir)) != NULL) {
|
||||||
|
if (!strcmp(de->d_name, "."))
|
||||||
|
continue;
|
||||||
|
if (!strcmp(de->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* this should work for kernels 2.6.5 or higher and */
|
||||||
|
/* is preferred because is unambiguous */
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/name", sysfs, de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f = fopen(n, "r");
|
||||||
|
/* this seems to work for ISA */
|
||||||
|
if(f == NULL) {
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/device/name", sysfs,
|
||||||
|
de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f = fopen(n, "r");
|
||||||
|
}
|
||||||
|
/* non-ISA is much harder */
|
||||||
|
/* and this won't find the correct bus name if a driver
|
||||||
|
has more than one bus */
|
||||||
|
if(f == NULL) {
|
||||||
|
len = snprintf(n, NAME_MAX, "%s/%s/device", sysfs,
|
||||||
|
de->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!(ddir = opendir(n)))
|
||||||
|
continue;
|
||||||
|
while ((dde = readdir(ddir)) != NULL) {
|
||||||
|
if (!strcmp(dde->d_name, "."))
|
||||||
|
continue;
|
||||||
|
if (!strcmp(dde->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
if ((!strncmp(dde->d_name, "i2c-", 4))) {
|
||||||
|
len = snprintf(n, NAME_MAX,
|
||||||
|
"%s/%s/device/%s/name",
|
||||||
|
sysfs, de->d_name,
|
||||||
|
dde->d_name);
|
||||||
|
if (len >= NAME_MAX) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: path truncated\n",
|
||||||
|
n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if((f = fopen(n, "r")))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found:
|
||||||
|
if (f != NULL) {
|
||||||
|
int i2cbus;
|
||||||
|
enum adt type;
|
||||||
|
char *px;
|
||||||
|
|
||||||
|
px = fgets(s, 120, f);
|
||||||
|
fclose(f);
|
||||||
|
if (!px) {
|
||||||
|
fprintf(stderr, "%s: read error\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((px = strchr(s, '\n')) != NULL)
|
||||||
|
*px = 0;
|
||||||
|
if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
|
||||||
|
continue;
|
||||||
|
if (!strncmp(s, "ISA ", 4)) {
|
||||||
|
type = adt_isa;
|
||||||
|
} else {
|
||||||
|
/* Attempt to probe for adapter capabilities */
|
||||||
|
type = i2c_get_funcs(i2cbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((count + 1) % BUNCH == 0) {
|
||||||
|
/* We need more space */
|
||||||
|
adapters = more_adapters(adapters, count + 1);
|
||||||
|
if (!adapters)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
adapters[count].nr = i2cbus;
|
||||||
|
adapters[count].name = strdup(s);
|
||||||
|
if (adapters[count].name == NULL) {
|
||||||
|
free_adapters(adapters);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
adapters[count].funcs = adap_types[type].funcs;
|
||||||
|
adapters[count].algo = adap_types[type].algo;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lookup_i2c_bus_by_name(const char *bus_name)
|
||||||
|
{
|
||||||
|
struct i2c_adap *adapters;
|
||||||
|
int i, i2cbus = -1;
|
||||||
|
|
||||||
|
adapters = gather_i2c_busses();
|
||||||
|
if (adapters == NULL) {
|
||||||
|
fprintf(stderr, "Error: Out of memory!\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk the list of i2c busses, looking for the one with the
|
||||||
|
right name */
|
||||||
|
for (i = 0; adapters[i].name; i++) {
|
||||||
|
if (strcmp(adapters[i].name, bus_name) == 0) {
|
||||||
|
if (i2cbus >= 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: I2C bus name is not unique!\n");
|
||||||
|
i2cbus = -4;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
i2cbus = adapters[i].nr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2cbus == -1)
|
||||||
|
fprintf(stderr, "Error: I2C bus name doesn't match any "
|
||||||
|
"bus present!\n");
|
||||||
|
|
||||||
|
done:
|
||||||
|
free_adapters(adapters);
|
||||||
|
return i2cbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse an I2CBUS command line argument and return the corresponding
|
||||||
|
* bus number, or a negative value if the bus is invalid.
|
||||||
|
*/
|
||||||
|
int lookup_i2c_bus(const char *i2cbus_arg)
|
||||||
|
{
|
||||||
|
unsigned long i2cbus;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
i2cbus = strtoul(i2cbus_arg, &end, 0);
|
||||||
|
if (*end || !*i2cbus_arg) {
|
||||||
|
/* Not a number, maybe a name? */
|
||||||
|
return lookup_i2c_bus_by_name(i2cbus_arg);
|
||||||
|
}
|
||||||
|
if (i2cbus > 0xFFFFF) {
|
||||||
|
fprintf(stderr, "Error: I2C bus out of range!\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2cbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a CHIP-ADDRESS command line argument and return the corresponding
|
||||||
|
* chip address, or a negative value if the address is invalid.
|
||||||
|
*/
|
||||||
|
int parse_i2c_address(const char *address_arg, int all_addrs)
|
||||||
|
{
|
||||||
|
long address;
|
||||||
|
char *end;
|
||||||
|
long min_addr = 0x08;
|
||||||
|
long max_addr = 0x77;
|
||||||
|
|
||||||
|
address = strtol(address_arg, &end, 0);
|
||||||
|
if (*end || !*address_arg) {
|
||||||
|
fprintf(stderr, "Error: Chip address is not a number!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_addrs) {
|
||||||
|
min_addr = 0x00;
|
||||||
|
max_addr = 0x7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address < min_addr || address > max_addr) {
|
||||||
|
fprintf(stderr, "Error: Chip address out of range "
|
||||||
|
"(0x%02lx-0x%02lx)!\n", min_addr, max_addr);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
|
||||||
|
{
|
||||||
|
int file, len;
|
||||||
|
|
||||||
|
len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
|
||||||
|
if (len >= (int)size) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", filename);
|
||||||
|
return -EOVERFLOW;
|
||||||
|
}
|
||||||
|
file = open(filename, O_RDWR);
|
||||||
|
|
||||||
|
if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
|
||||||
|
len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
|
||||||
|
if (len >= (int)size) {
|
||||||
|
fprintf(stderr, "%s: path truncated\n", filename);
|
||||||
|
return -EOVERFLOW;
|
||||||
|
}
|
||||||
|
file = open(filename, O_RDWR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file < 0 && !quiet) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
fprintf(stderr, "Error: Could not open file "
|
||||||
|
"`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
|
||||||
|
i2cbus, i2cbus, strerror(ENOENT));
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: Could not open file "
|
||||||
|
"`%s': %s\n", filename, strerror(errno));
|
||||||
|
if (errno == EACCES)
|
||||||
|
fprintf(stderr, "Run as root?\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_slave_addr(int file, int address, int force)
|
||||||
|
{
|
||||||
|
/* With force, let the user read from/write to the registers
|
||||||
|
even when a driver is also running */
|
||||||
|
if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: Could not set address to 0x%02x: %s\n",
|
||||||
|
address, strerror(errno));
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
44
STM32MP157/source/A7/04_I2C/01_at24c02_test/i2cbusses.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
i2cbusses.h - Part of the i2c-tools package
|
||||||
|
|
||||||
|
Copyright (C) 2004-2010 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _I2CBUSSES_H
|
||||||
|
#define _I2CBUSSES_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct i2c_adap {
|
||||||
|
int nr;
|
||||||
|
char *name;
|
||||||
|
const char *funcs;
|
||||||
|
const char *algo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_adap *gather_i2c_busses(void);
|
||||||
|
void free_adapters(struct i2c_adap *adapters);
|
||||||
|
|
||||||
|
int lookup_i2c_bus(const char *i2cbus_arg);
|
||||||
|
int parse_i2c_address(const char *address_arg, int all_addrs);
|
||||||
|
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);
|
||||||
|
int set_slave_addr(int file, int address, int force);
|
||||||
|
|
||||||
|
#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n"
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
smbus.h - SMBus level access helper functions
|
||||||
|
|
||||||
|
Copyright (C) 1995-1997 Simon G. Vogl
|
||||||
|
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||||
|
Copyright (C) 2012-2017 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published
|
||||||
|
by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_I2C_SMBUS_H
|
||||||
|
#define LIB_I2C_SMBUS_H
|
||||||
|
|
||||||
|
#define I2C_API_VERSION 0x100
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
|
||||||
|
extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||||
|
int size, union i2c_smbus_data *data);
|
||||||
|
|
||||||
|
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_byte(int file);
|
||||||
|
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
|
||||||
|
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
|
||||||
|
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
|
||||||
|
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
|
||||||
|
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
|
||||||
|
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||||
|
ask for less than 32 bytes, your code will only work with kernels
|
||||||
|
2.6.23 and later. */
|
||||||
|
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values);
|
||||||
|
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values);
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values);
|
||||||
|
|
||||||
|
#endif /* LIB_I2C_SMBUS_H */
|
||||||
214
STM32MP157/source/A7/04_I2C/01_at24c02_test/smbus.c
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
smbus.c - SMBus level access helper functions
|
||||||
|
|
||||||
|
Copyright (C) 1995-1997 Simon G. Vogl
|
||||||
|
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||||
|
Copyright (C) 2012-2013 Jean Delvare <jdelvare@suse.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published
|
||||||
|
by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <i2c/smbus.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
/* Compatibility defines */
|
||||||
|
#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
|
||||||
|
#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
|
||||||
|
#endif
|
||||||
|
#ifndef I2C_FUNC_SMBUS_PEC
|
||||||
|
#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||||
|
int size, union i2c_smbus_data *data)
|
||||||
|
{
|
||||||
|
struct i2c_smbus_ioctl_data args;
|
||||||
|
__s32 err;
|
||||||
|
|
||||||
|
args.read_write = read_write;
|
||||||
|
args.command = command;
|
||||||
|
args.size = size;
|
||||||
|
args.data = data;
|
||||||
|
|
||||||
|
err = ioctl(file, I2C_SMBUS, &args);
|
||||||
|
if (err == -1)
|
||||||
|
err = -errno;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_quick(int file, __u8 value)
|
||||||
|
{
|
||||||
|
return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_byte(int file)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FF & data.byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_byte(int file, __u8 value)
|
||||||
|
{
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
|
||||||
|
I2C_SMBUS_BYTE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_byte_data(int file, __u8 command)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FF & data.byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.byte = value;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_read_word_data(int file, __u8 command)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0x0FFFF & data.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.word = value;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
data.word = value;
|
||||||
|
if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_PROC_CALL, &data))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0x0FFFF & data.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
I2C_SMBUS_BLOCK_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i;
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BLOCK_DATA, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||||
|
ask for less than 32 bytes, your code will only work with kernels
|
||||||
|
2.6.23 and later. */
|
||||||
|
__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
data.block[0] = length;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||||
|
length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
|
||||||
|
I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||||
|
const __u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i;
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of read bytes */
|
||||||
|
__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||||
|
__u8 *values)
|
||||||
|
{
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
for (i = 1; i <= length; i++)
|
||||||
|
data.block[i] = values[i-1];
|
||||||
|
data.block[0] = length;
|
||||||
|
|
||||||
|
err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 1; i <= data.block[0]; i++)
|
||||||
|
values[i-1] = data.block[i];
|
||||||
|
return data.block[0];
|
||||||
|
}
|
||||||