Browse Source
Oops, forgot to create the trunk level.
Oops, forgot to create the trunk level.
git-svn-id: http://lm-sensors.org/svn/i2c-tools/trunk@4481 7894878c-1315-0410-8ee3-d5d059ff63e0tags/v3.0.0
commit
c922a78bb4
12 changed files with 1985 additions and 0 deletions
-
259tools/i2cbusses.c
-
29tools/i2cbusses.h
-
88tools/i2cdetect.8
-
347tools/i2cdetect.c
-
80tools/i2cdump.8
-
426tools/i2cdump.c
-
68tools/i2cget.8
-
263tools/i2cget.c
-
77tools/i2cset.8
-
286tools/i2cset.c
-
47tools/util.c
-
15tools/util.h
@ -0,0 +1,259 @@ |
|||||
|
/* |
||||
|
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> |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
#include <sys/stat.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <unistd.h> |
||||
|
#include <limits.h> |
||||
|
#include <dirent.h> |
||||
|
#include <fcntl.h> |
||||
|
#include <errno.h> |
||||
|
#include "i2cbusses.h" |
||||
|
#include "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, 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; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
this just prints out the installed i2c busses in a consistent format, whether |
||||
|
on a 2.4 kernel using /proc or a 2.6 kernel using /sys. |
||||
|
If procfmt == 1, print out exactly /proc/bus/i2c format on stdout. |
||||
|
This allows this to be used in a program to emulate /proc/bus/i2c on a |
||||
|
sysfs system. |
||||
|
*/ |
||||
|
void print_i2c_busses(int procfmt) |
||||
|
{ |
||||
|
FILE *fptr; |
||||
|
char s[100]; |
||||
|
struct dirent *de, *dde; |
||||
|
DIR *dir, *ddir; |
||||
|
FILE *f; |
||||
|
char *border; |
||||
|
char dev[NAME_MAX], fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX]; |
||||
|
int foundsysfs = 0; |
||||
|
int count=0; |
||||
|
|
||||
|
|
||||
|
/* look in /proc/bus/i2c */ |
||||
|
if((fptr = fopen("/proc/bus/i2c", "r"))) { |
||||
|
while(fgets(s, 100, fptr)) { |
||||
|
if(count++ == 0 && !procfmt) |
||||
|
fprintf(stderr," Installed I2C busses:\n"); |
||||
|
if(procfmt) |
||||
|
printf("%s", s); |
||||
|
else |
||||
|
fprintf(stderr, " %s", s); |
||||
|
} |
||||
|
fclose(fptr); |
||||
|
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", dev, 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 */ |
||||
|
sprintf(n, "%s/%s/name", sysfs, de->d_name); |
||||
|
f = fopen(n, "r"); |
||||
|
/* this seems to work for ISA */ |
||||
|
if(f == NULL) { |
||||
|
sprintf(n, "%s/%s/device/name", sysfs, de->d_name); |
||||
|
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) { |
||||
|
sprintf(n, "%s/%s/device", sysfs, de->d_name); |
||||
|
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))) { |
||||
|
sprintf(n, "%s/%s/device/%s/name", |
||||
|
sysfs, de->d_name, dde->d_name); |
||||
|
if((f = fopen(n, "r"))) |
||||
|
goto found; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
found: |
||||
|
if (f != NULL) { |
||||
|
int i2cbus; |
||||
|
enum adt type; |
||||
|
char x[120]; |
||||
|
char *px; |
||||
|
|
||||
|
px = fgets(x, 120, f); |
||||
|
fclose(f); |
||||
|
if (!px) { |
||||
|
fprintf(stderr, "%s: read error\n", n); |
||||
|
continue; |
||||
|
} |
||||
|
if((border = index(x, '\n')) != NULL) |
||||
|
*border = 0; |
||||
|
if(count++ == 0 && !procfmt) |
||||
|
fprintf(stderr," Installed I2C busses:\n"); |
||||
|
/* match 2.4 /proc/bus/i2c format as closely as possible */ |
||||
|
if(!strncmp(x, "ISA ", 4)) { |
||||
|
type = adt_isa; |
||||
|
} else if(!sscanf(de->d_name, "i2c-%d", &i2cbus)) { |
||||
|
type = adt_dummy; |
||||
|
} else { |
||||
|
/* Attempt to probe for adapter capabilities */ |
||||
|
type = i2c_get_funcs(i2cbus); |
||||
|
} |
||||
|
|
||||
|
if (procfmt) |
||||
|
printf("%s\t%-10s\t%-32s\t%s\n", de->d_name, |
||||
|
adap_types[type].funcs, x, adap_types[type].algo); |
||||
|
else |
||||
|
fprintf(stderr, " %s\t%-10s\t%s\n", de->d_name, |
||||
|
adap_types[type].funcs, x); |
||||
|
} |
||||
|
} |
||||
|
closedir(dir); |
||||
|
|
||||
|
done: |
||||
|
if(count == 0 && !procfmt) |
||||
|
fprintf(stderr,"Error: No I2C busses found!\n" |
||||
|
"Be sure you have done 'modprobe i2c-dev'\n" |
||||
|
"and also modprobed your i2c bus drivers\n"); |
||||
|
} |
||||
|
|
||||
|
int open_i2c_dev(const int i2cbus, char *filename, const int quiet) |
||||
|
{ |
||||
|
int file; |
||||
|
|
||||
|
sprintf(filename, "/dev/i2c/%d", i2cbus); |
||||
|
file = open(filename, O_RDWR); |
||||
|
|
||||
|
if (file < 0 && errno == ENOENT) { |
||||
|
sprintf(filename, "/dev/i2c-%d", i2cbus); |
||||
|
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; |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
/* |
||||
|
i2cbusses.h - Part of the lm_sensors project |
||||
|
|
||||
|
Copyright (C) 2004 The lm_sensors group |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef _I2CBUSSES_H |
||||
|
#define _I2CBUSSES_H |
||||
|
|
||||
|
void print_i2c_busses(int procfmt); |
||||
|
|
||||
|
int open_i2c_dev(const int i2cbus, char *filename, const int quiet); |
||||
|
int set_slave_addr(int file, int address, int force); |
||||
|
|
||||
|
#endif |
@ -0,0 +1,88 @@ |
|||||
|
.TH I2CDETECT 8 "October 2006" |
||||
|
.SH NAME |
||||
|
i2cdetect \- detect I2C chips |
||||
|
|
||||
|
.SH SYNOPSIS |
||||
|
.B i2cdetect |
||||
|
.RI [ -y ] |
||||
|
.RI [ -a ] |
||||
|
.RI [ -q | -r ] |
||||
|
.I i2cbus |
||||
|
.RI [ "first last" ] |
||||
|
.br |
||||
|
.B i2cdetect |
||||
|
.I -F |
||||
|
.I i2cbus |
||||
|
.br |
||||
|
.B i2cdetect |
||||
|
.I -V |
||||
|
.br |
||||
|
.B i2cdetect |
||||
|
.I -l |
||||
|
|
||||
|
.SH DESCRIPTION |
||||
|
i2cdetect is a userspace program to scan an I2C bus for devices. It |
||||
|
outputs a table with the list of detected devices on the specified bus. |
||||
|
\fIi2cbus\fR indicates the number of the I2C bus to be scanned, and |
||||
|
should correspond to one of the busses listed by \fIi2cdetect -l\fR. |
||||
|
The optional parameters \fIfirst\fR and \fIlast\fR restrict the scanning |
||||
|
range (default: from 0x03 to 0x77). |
||||
|
.PP |
||||
|
i2cdetect can also be used to query the functionalities of an I2C bus |
||||
|
(see option \fB-F\fP.) |
||||
|
|
||||
|
.SH WARNING |
||||
|
This program can confuse your I2C bus, cause data loss and worse! |
||||
|
|
||||
|
.SH INTERPRETING THE OUTPUT |
||||
|
Each cell in the output table will contain one of the following symbols: |
||||
|
.IP \(bu "\w'\(bu'u+1n" |
||||
|
"--". The address was probed but no chip answered. |
||||
|
.IP \(bu |
||||
|
"UU". Probing was skipped, because this address is currently in use by |
||||
|
a driver. This strongly suggests that there is a chip at this address. |
||||
|
.IP \(bu |
||||
|
An address number in hexadecimal, e.g. "2d" or "4e". A chip |
||||
|
was found at this address. |
||||
|
|
||||
|
.SH OPTIONS |
||||
|
.TP |
||||
|
.B "\-y" |
||||
|
Disable interactive mode. By default, i2cdetect will wait for a confirmation |
||||
|
from the user before messing with the I2C bus. When this flag is used, it |
||||
|
will perform the operation directly. This is mainly meant to be used in |
||||
|
scripts. |
||||
|
.TP |
||||
|
.B "\-a" |
||||
|
Force scanning of non-regular addresses. Not recommended. |
||||
|
.TP |
||||
|
.B "\-q" |
||||
|
Use SMBus "quick write" commands for probing (by default, the command |
||||
|
used is the one believed to be the safest for each address). |
||||
|
Not recommended. This is known to corrupt the Atmel AT24RF08 EEPROM |
||||
|
found on many IBM Thinkpad laptops. |
||||
|
.TP |
||||
|
.B "\-r" |
||||
|
Use SMBus "read byte" commands for probing (by default, the command |
||||
|
used is the one believed to be the safest for each address). |
||||
|
Not recommended. This is known to lock SMBus on various write-only |
||||
|
chips (most notably clock chips at address 0x69). |
||||
|
.TP |
||||
|
.B "\-F" |
||||
|
Display the list of functionalities implemented by the adapter and exit. |
||||
|
.TP |
||||
|
.B "\-V" |
||||
|
Display the version and exit. |
||||
|
.TP |
||||
|
.B "\-l" |
||||
|
Output a list of installed busses. |
||||
|
|
||||
|
.SH SEE ALSO |
||||
|
i2cdump(8), sensors-detect(8) |
||||
|
|
||||
|
.SH AUTHOR |
||||
|
Frodo Looijaard, Mark D. Studebaker, and the lm_sensors group |
||||
|
http://www.lm-sensors.org/ |
||||
|
This manual page was written by Aurelien Jarno <aurel32@debian.org>, for |
||||
|
the Debian GNU/Linux system. It was then reviewed by the lm_sensors team and |
||||
|
is now part of the lm_sensors source distribution. |
@ -0,0 +1,347 @@ |
|||||
|
/* |
||||
|
i2cdetect.c - a user-space program to scan for I2C devices |
||||
|
Copyright (C) 1999-2004 Frodo Looijaard <frodol@dds.nl>, |
||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com> and |
||||
|
Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include "../dump/i2cbusses.h" |
||||
|
#include "i2c-dev.h" |
||||
|
#include "version.h" |
||||
|
|
||||
|
#define MODE_AUTO 0 |
||||
|
#define MODE_QUICK 1 |
||||
|
#define MODE_READ 2 |
||||
|
#define MODE_FUNC 3 |
||||
|
|
||||
|
void help(void) |
||||
|
{ |
||||
|
fprintf(stderr, |
||||
|
"Syntax: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]\n" |
||||
|
" i2cdetect -F I2CBUS\n" |
||||
|
" i2cdetect -l\n" |
||||
|
" i2cdetect -V\n" |
||||
|
" I2CBUS is an integer\n" |
||||
|
" With -a, probe all addresses (NOT RECOMMENDED)\n" |
||||
|
" With -q, uses only quick write commands for probing (NOT " |
||||
|
"RECOMMENDED)\n" |
||||
|
" With -r, uses only read byte commands for probing (NOT " |
||||
|
"RECOMMENDED)\n" |
||||
|
" If provided, FIRST and LAST limit the probing range.\n" |
||||
|
" With -l, lists installed busses only\n"); |
||||
|
print_i2c_busses(0); |
||||
|
} |
||||
|
|
||||
|
int scan_i2c_bus(int file, const int mode, const int first, const int last) |
||||
|
{ |
||||
|
int i, j; |
||||
|
int res; |
||||
|
|
||||
|
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); |
||||
|
|
||||
|
for (i = 0; i < 128; i += 16) { |
||||
|
printf("%02x: ", i); |
||||
|
for(j = 0; j < 16; j++) { |
||||
|
fflush(stdout); |
||||
|
|
||||
|
/* Skip unwanted addresses */ |
||||
|
if (i+j < first || i+j > last) { |
||||
|
printf(" "); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
/* Set slave address */ |
||||
|
if (ioctl(file, I2C_SLAVE, i+j) < 0) { |
||||
|
if (errno == EBUSY) { |
||||
|
printf("UU "); |
||||
|
continue; |
||||
|
} else { |
||||
|
fprintf(stderr, "Error: Could not set " |
||||
|
"address to 0x%02x: %s\n", i+j, |
||||
|
strerror(errno)); |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Probe this address */ |
||||
|
switch (mode) { |
||||
|
case MODE_QUICK: |
||||
|
/* This is known to corrupt the Atmel AT24RF08 |
||||
|
EEPROM */ |
||||
|
res = i2c_smbus_write_quick(file, |
||||
|
I2C_SMBUS_WRITE); |
||||
|
break; |
||||
|
case MODE_READ: |
||||
|
/* This is known to lock SMBus on various |
||||
|
write-only chips (mainly clock chips) */ |
||||
|
res = i2c_smbus_read_byte(file); |
||||
|
break; |
||||
|
default: |
||||
|
if ((i+j >= 0x30 && i+j <= 0x37) |
||||
|
|| (i+j >= 0x50 && i+j <= 0x5F)) |
||||
|
res = i2c_smbus_read_byte(file); |
||||
|
else |
||||
|
res = i2c_smbus_write_quick(file, |
||||
|
I2C_SMBUS_WRITE); |
||||
|
} |
||||
|
|
||||
|
if (res < 0) |
||||
|
printf("-- "); |
||||
|
else |
||||
|
printf("%02x ", i+j); |
||||
|
} |
||||
|
printf("\n"); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
struct func |
||||
|
{ |
||||
|
long value; |
||||
|
const char* name; |
||||
|
}; |
||||
|
|
||||
|
static const struct func all_func[] = { |
||||
|
{ .value = I2C_FUNC_I2C, |
||||
|
.name = "I2C" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_QUICK, |
||||
|
.name = "SMBus Quick Command" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_WRITE_BYTE, |
||||
|
.name = "SMBus Send Byte" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_READ_BYTE, |
||||
|
.name = "SMBus Receive Byte" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA, |
||||
|
.name = "SMBus Write Byte" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_READ_BYTE_DATA, |
||||
|
.name = "SMBus Read Byte" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA, |
||||
|
.name = "SMBus Write Word" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_READ_WORD_DATA, |
||||
|
.name = "SMBus Read Word" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_PROC_CALL, |
||||
|
.name = "SMBus Process Call" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, |
||||
|
.name = "SMBus Block Write" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA, |
||||
|
.name = "SMBus Block Read" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL, |
||||
|
.name = "SMBus Block Process Call" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_HWPEC_CALC, |
||||
|
.name = "SMBus PEC" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, |
||||
|
.name = "I2C Block Write" }, |
||||
|
{ .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK, |
||||
|
.name = "I2C Block Read" }, |
||||
|
{ } |
||||
|
}; |
||||
|
|
||||
|
void print_functionality(unsigned long funcs) |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
for (i = 0; all_func[i].value; i++) { |
||||
|
printf("%-32s %s\n", all_func[i].name, |
||||
|
(funcs & all_func[i].value) ? "yes" : "no"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
char *end; |
||||
|
int i2cbus, file, res; |
||||
|
char filename[20]; |
||||
|
unsigned long funcs; |
||||
|
int mode = MODE_AUTO; |
||||
|
int first = 0x03, last = 0x77; |
||||
|
int flags = 0; |
||||
|
int yes = 0, version = 0, list = 0; |
||||
|
|
||||
|
/* handle (optional) flags first */ |
||||
|
while (1+flags < argc && argv[1+flags][0] == '-') { |
||||
|
switch (argv[1+flags][1]) { |
||||
|
case 'V': version = 1; break; |
||||
|
case 'y': yes = 1; break; |
||||
|
case 'l': list = 1; break; |
||||
|
case 'F': |
||||
|
if (mode != MODE_AUTO && mode != MODE_FUNC) { |
||||
|
fprintf(stderr, "Error: Different modes " |
||||
|
"specified!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
mode = MODE_FUNC; |
||||
|
break; |
||||
|
case 'r': |
||||
|
if (mode == MODE_QUICK) { |
||||
|
fprintf(stderr, "Error: Different modes " |
||||
|
"specified!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
mode = MODE_READ; |
||||
|
break; |
||||
|
case 'q': |
||||
|
if (mode == MODE_READ) { |
||||
|
fprintf(stderr, "Error: Different modes " |
||||
|
"specified!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
mode = MODE_QUICK; |
||||
|
break; |
||||
|
case 'a': |
||||
|
first = 0x00; |
||||
|
last = 0x7F; |
||||
|
break; |
||||
|
default: |
||||
|
fprintf(stderr, "Warning: Unsupported flag " |
||||
|
"\"-%c\"!\n", argv[1+flags][1]); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
flags++; |
||||
|
} |
||||
|
|
||||
|
if (version) { |
||||
|
fprintf(stderr, "i2cdetect version %s\n", LM_VERSION); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (list) { |
||||
|
print_i2c_busses(1); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 2) { |
||||
|
fprintf(stderr, "Error: No i2c-bus specified!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
i2cbus = strtol(argv[flags+1], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: I2CBUS argument not a number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if ((i2cbus < 0) || (i2cbus > 0xff)) { |
||||
|
fprintf(stderr, "Error: I2CBUS argument out of range " |
||||
|
"(0-255)!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
/* read address range if present */ |
||||
|
if (argc == flags + 4 && mode != MODE_FUNC) { |
||||
|
int tmp; |
||||
|
|
||||
|
tmp = strtol(argv[flags+2], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: FIRST argment not a " |
||||
|
"number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (tmp < first || tmp > last) { |
||||
|
fprintf(stderr, "Error: FIRST argument out of range " |
||||
|
"(0x%02x-0x%02x)!\n", first, last); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
first = tmp; |
||||
|
|
||||
|
tmp = strtol(argv[flags+3], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: LAST argment not a " |
||||
|
"number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (tmp < first || tmp > last) { |
||||
|
fprintf(stderr, "Error: LAST argument out of range " |
||||
|
"(0x%02x-0x%02x)!\n", first, last); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
last = tmp; |
||||
|
} else if (argc != flags + 2) { |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
file = open_i2c_dev(i2cbus, filename, 0); |
||||
|
if (file < 0) { |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0) { |
||||
|
fprintf(stderr, "Error: Could not get the adapter " |
||||
|
"functionality matrix: %s\n", strerror(errno)); |
||||
|
close(file); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
/* Special case, we only list the implemented functionalities */ |
||||
|
if (mode == MODE_FUNC) { |
||||
|
close(file); |
||||
|
printf("Functionalities implemented by %s:\n", filename); |
||||
|
print_functionality(funcs); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (mode != MODE_READ && !(funcs & I2C_FUNC_SMBUS_QUICK)) { |
||||
|
fprintf(stderr, "Error: Can't use SMBus Quick Write command " |
||||
|
"on this bus (ISA bus?)\n"); |
||||
|
close(file); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (mode != MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) { |
||||
|
fprintf(stderr, "Error: Can't use SMBus Read Byte command " |
||||
|
"on this bus (ISA bus?)\n"); |
||||
|
close(file); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (!yes) { |
||||
|
char s[2]; |
||||
|
|
||||
|
fprintf(stderr, "WARNING! This program can confuse your I2C " |
||||
|
"bus, cause data loss and worse!\n"); |
||||
|
|
||||
|
fprintf(stderr, "I will probe file %s%s.\n", filename, |
||||
|
mode==MODE_QUICK?" using quick write commands": |
||||
|
mode==MODE_READ?" using read byte commands":""); |
||||
|
fprintf(stderr, "I will probe address range 0x%02x-0x%02x.\n", |
||||
|
first, last); |
||||
|
|
||||
|
fprintf(stderr, "Continue? [Y/n] "); |
||||
|
fflush(stderr); |
||||
|
if (!fgets(s, 2, stdin) |
||||
|
|| (s[0] != '\n' && s[0] != 'y' && s[0] != 'Y')) { |
||||
|
fprintf(stderr, "Aborting on user request.\n"); |
||||
|
exit(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
res = scan_i2c_bus(file, mode, first, last); |
||||
|
|
||||
|
close(file); |
||||
|
|
||||
|
exit(res?1:0); |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
.TH I2CDUMP 8 "June 2007" |
||||
|
.SH NAME |
||||
|
i2cdump \- examine I2C registers |
||||
|
|
||||
|
.SH SYNOPSIS |
||||
|
.B i2cdump |
||||
|
.RB [ -f ] |
||||
|
.RB [ -y ] |
||||
|
.I i2cbus |
||||
|
.I address |
||||
|
.RI [ mode ] |
||||
|
.RI [ "bank " [ bankreg ]] |
||||
|
.br |
||||
|
.B i2cdump |
||||
|
.B -V |
||||
|
|
||||
|
.SH DESCRIPTION |
||||
|
i2cdump is a small helper program to examine registers |
||||
|
visible through the I2C bus. |
||||
|
|
||||
|
.SH OPTIONS |
||||
|
.TP |
||||
|
.B -V |
||||
|
Display the version and exit. |
||||
|
.TP |
||||
|
.B -f |
||||
|
Force access to the device even if it is already busy. By default, i2cdump |
||||
|
will refuse to access a device which is already under the control of a |
||||
|
kernel driver. Using this flag is dangerous, it can seriously confuse the |
||||
|
kernel driver in question. It can also cause i2cdump to return invalid |
||||
|
results. So use at your own risk and only if you know what you're doing. |
||||
|
.TP |
||||
|
.B -y |
||||
|
Disable interactive mode. By default, i2cdump will wait for a confirmation |
||||
|
from the user before messing with the I2C bus. When this flag is used, it |
||||
|
will perform the operation directly. This is mainly meant to be used in |
||||
|
scripts. |
||||
|
.PP |
||||
|
At least two options must be provided to i2cdump. \fIi2cbus\fR indicates the |
||||
|
number of the I2C bus to be scanned. This number should correspond to one |
||||
|
of the busses listed by \fIi2cdetect -l\fR. \fIaddress\fR indicates the |
||||
|
address to be scanned on that bus, and is an integer between 0x00 and 0x7F. |
||||
|
.PP |
||||
|
The \fImode\fR parameter, if specified, is one of the letters \fBb\fP, \fBw\fP, |
||||
|
\fBs\fP, or \fBi\fP, corresponding to a read size of a single byte, a 16-bit |
||||
|
word, an SMBus block, an I2C block, respectively. The \fBc\fP mode is a |
||||
|
little different, it reads all bytes consecutively, and is useful for chips that |
||||
|
have an address auto-increment feature, such as EEPROMs. The \fBW\fP mode is |
||||
|
also special, it is similar to \fBw\fP except that a read command will only |
||||
|
be issued on even register addresses; this is again mainly useful for EEPROMs. |
||||
|
.PP |
||||
|
A \fBp\fP can also be appended to the \fImode\fR parameter (except for |
||||
|
\fBi\fP and \fBW\fP) to enable PEC. If the \fImode\fR parameter is omitted, |
||||
|
i2cdump defaults to byte access without PEC. |
||||
|
.PP |
||||
|
The \fIbank\fR and \fIbankreg\fR parameters are useful on the W83781D and |
||||
|
similar chips (at the time of writing, all Winbond and Asus chips). |
||||
|
\fIbank\fR is an integer between 0 and 7, and \fIbankreg\fR is an integer |
||||
|
between 0x00 and 0xFF (default value: 0x4E). The W83781D data sheet has more |
||||
|
information on bank selection. |
||||
|
|
||||
|
.SH WARNING |
||||
|
i2cdump can be dangerous if used improperly. Most notably, the \fBc\fP mode |
||||
|
starts with WRITING a byte to the chip. On most chips it will be stored in the |
||||
|
address pointer register, which is OK, but some chips with a single register |
||||
|
or no (visible) register at all will most likely see this as a real WRITE, |
||||
|
resulting in possible misbehavior or corruption. Do not use i2cdump |
||||
|
on random addresses. Anyway, it is of little use unless you have an idea of |
||||
|
what you are looking for and have some general knowledge about hardware |
||||
|
monitoring chips internals. |
||||
|
|
||||
|
.SH SEE ALSO |
||||
|
i2cset(8), i2cdetect(8), isadump(8) |
||||
|
|
||||
|
.SH AUTHOR |
||||
|
Frodo Looijaard, Mark D. Studebaker, and the lm_sensors group |
||||
|
http://www.lm-sensors.org/ |
||||
|
This manual page was originally written by David Z Maze <dmaze@debian.org> for |
||||
|
the Debian GNU/Linux system. It was then reviewed by the lm_sensors team and |
||||
|
is now part of the lm_sensors source distribution. |
@ -0,0 +1,426 @@ |
|||||
|
/* |
||||
|
i2cdump.c - a user-space program to dump I2C registers |
||||
|
Copyright (C) 2002-2003 Frodo Looijaard <frodol@dds.nl>, and |
||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com> |
||||
|
Copyright (C) 2004 The lm_sensors group |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include "util.h" |
||||
|
#include "i2cbusses.h" |
||||
|
#include "i2c-dev.h" |
||||
|
#include "version.h" |
||||
|
|
||||
|
void help(void) |
||||
|
{ |
||||
|
fprintf(stderr, "Syntax: i2cdump [-f] [-y] I2CBUS ADDRESS [MODE] " |
||||
|
"[BANK [BANKREG]]\n" |
||||
|
" i2cdump -V\n" |
||||
|
" MODE is one of:\n" |
||||
|
" b (byte, default)\n" |
||||
|
" w (word)\n" |
||||
|
" W (word on even register addresses)\n" |
||||
|
" s (SMBus block)\n" |
||||
|
" i (I2C block)\n" |
||||
|
" c (consecutive byte)\n" |
||||
|
" Append 'p' to 'b', 'w', 's' or 'c' for PEC checking\n" |
||||
|
" I2CBUS is an integer\n" |
||||
|
" ADDRESS is an integer 0x00 - 0x7f\n" |
||||
|
" BANK and BANKREG are for byte and word accesses (default " |
||||
|
"bank 0, reg 0x4e)\n" |
||||
|
" BANK is the command for smbusblock accesses (default 0)\n"); |
||||
|
print_i2c_busses(0); |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
char *end; |
||||
|
int i, j, res, i2cbus, address, size, file; |
||||
|
int bank = 0, bankreg = 0x4E, old_bank = 0; |
||||
|
char filename[20]; |
||||
|
unsigned long funcs; |
||||
|
int block[256], s_length = 0; |
||||
|
int pec = 0, even = 0; |
||||
|
int flags = 0; |
||||
|
int force = 0, yes = 0, version = 0; |
||||
|
|
||||
|
/* handle (optional) flags first */ |
||||
|
while (1+flags < argc && argv[1+flags][0] == '-') { |
||||
|
switch (argv[1+flags][1]) { |
||||
|
case 'V': version = 1; break; |
||||
|
case 'f': force = 1; break; |
||||
|
case 'y': yes = 1; break; |
||||
|
default: |
||||
|
fprintf(stderr, "Warning: Unsupported flag " |
||||
|
"\"-%c\"!\n", argv[1+flags][1]); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
flags++; |
||||
|
} |
||||
|
|
||||
|
if (version) { |
||||
|
fprintf(stderr, "i2cdump version %s\n", LM_VERSION); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 2) { |
||||
|
fprintf(stderr, "Error: No i2c-bus specified!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
i2cbus = strtol(argv[flags+1], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: First argument not a number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (i2cbus < 0 || i2cbus > 0xff) { |
||||
|
fprintf(stderr, "Error: I2CBUS argument out of range!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 3) { |
||||
|
fprintf(stderr, "Error: No address specified!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
address = strtol(argv[flags+2], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: Second argument not a number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (address < 0 || address > 0x7f) { |
||||
|
fprintf(stderr, "Error: Address out of range!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 4) { |
||||
|
fprintf(stderr, "No size specified (using byte-data access)\n"); |
||||
|
size = I2C_SMBUS_BYTE_DATA; |
||||
|
} else if (!strncmp(argv[flags+3], "b", 1)) { |
||||
|
size = I2C_SMBUS_BYTE_DATA; |
||||
|
pec = argv[flags+3][1] == 'p'; |
||||
|
} else if (!strncmp(argv[flags+3], "w", 1)) { |
||||
|
size = I2C_SMBUS_WORD_DATA; |
||||
|
pec = argv[flags+3][1] == 'p'; |
||||
|
} else if (!strncmp(argv[flags+3], "W", 1)) { |
||||
|
size = I2C_SMBUS_WORD_DATA; |
||||
|
even = 1; |
||||
|
} else if (!strncmp(argv[flags+3], "s", 1)) { |
||||
|
size = I2C_SMBUS_BLOCK_DATA; |
||||
|
pec = argv[flags+3][1] == 'p'; |
||||
|
} else if (!strncmp(argv[flags+3], "c", 1)) { |
||||
|
size = I2C_SMBUS_BYTE; |
||||
|
pec = argv[flags+3][1] == 'p'; |
||||
|
} else if (!strcmp(argv[flags+3], "i")) |
||||
|
size = I2C_SMBUS_I2C_BLOCK_DATA; |
||||
|
else { |
||||
|
fprintf(stderr, "Error: Invalid mode!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (argc > flags + 4) { |
||||
|
bank = strtol(argv[flags+4], &end, 0); |
||||
|
if (*end || size == I2C_SMBUS_I2C_BLOCK_DATA) { |
||||
|
fprintf(stderr, "Error: Invalid bank number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if ((size == I2C_SMBUS_BYTE_DATA || size == I2C_SMBUS_WORD_DATA) |
||||
|
&& (bank < 0 || bank > 15)) { |
||||
|
fprintf(stderr, "Error: bank out of range!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (size == I2C_SMBUS_BLOCK_DATA |
||||
|
&& (bank < 0 || bank > 0xff)) { |
||||
|
fprintf(stderr, "Error: block command out of range!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (argc > flags + 5) { |
||||
|
bankreg = strtol(argv[flags+5], &end, 0); |
||||
|
if (*end || size == I2C_SMBUS_BLOCK_DATA) { |
||||
|
fprintf(stderr, "Error: Invalid bank register " |
||||
|
"number!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (bankreg < 0 || bankreg > 0xff) { |
||||
|
fprintf(stderr, "Error: bank out of range " |
||||
|
"(0-0xff)!\n"); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
file = open_i2c_dev(i2cbus, filename, 0); |
||||
|
if (file < 0) { |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
/* check adapter functionality */ |
||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0) { |
||||
|
fprintf(stderr, "Error: Could not get the adapter " |
||||
|
"functionality matrix: %s\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
switch(size) { |
||||
|
case I2C_SMBUS_BYTE: |
||||
|
if (!((funcs & I2C_FUNC_SMBUS_BYTE) == I2C_FUNC_SMBUS_BYTE)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have byte capability\n", i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_BYTE_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have byte read capability\n", i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_WORD_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have word read capability\n", i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_BLOCK_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have smbus block read capability\n", |
||||
|
i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_I2C_BLOCK_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have i2c block read capability\n", |
||||
|
i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (set_slave_addr(file, address, force) < 0) |
||||
|
exit(1); |
||||
|
|
||||
|
if (pec) { |
||||
|
if (ioctl(file, I2C_PEC, 1) < 0) { |
||||
|
fprintf(stderr, "Error: Could not set PEC: %s\n", |
||||
|
strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (!(funcs & (I2C_FUNC_SMBUS_HWPEC_CALC | I2C_FUNC_I2C))) { |
||||
|
fprintf(stderr, "Warning: Adapter for i2c bus %d does " |
||||
|
"not seem to actually support PEC\n", i2cbus); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!yes) { |
||||
|
fprintf(stderr, "WARNING! This program can confuse your I2C " |
||||
|
"bus, cause data loss and worse!\n"); |
||||
|
|
||||
|
fprintf(stderr, "I will probe file %s, address 0x%x, mode " |
||||
|
"%s\n", filename, address, |
||||
|
size == I2C_SMBUS_BLOCK_DATA ? "smbus block" : |
||||
|
size == I2C_SMBUS_I2C_BLOCK_DATA ? "i2c block" : |
||||
|
size == I2C_SMBUS_BYTE ? "byte consecutive read" : |
||||
|
size == I2C_SMBUS_BYTE_DATA ? "byte" : "word"); |
||||
|
if (pec) |
||||
|
fprintf(stderr, "PEC checking enabled.\n"); |
||||
|
if (even) |
||||
|
fprintf(stderr, "Only probing even register " |
||||
|
"addresses.\n"); |
||||
|
if (bank) { |
||||
|
if (size == I2C_SMBUS_BLOCK_DATA) |
||||
|
fprintf(stderr, "Using command 0x%02x.\n", |
||||
|
bank); |
||||
|
else |
||||
|
fprintf(stderr, "Probing bank %d using bank " |
||||
|
"register 0x%02x.\n", bank, bankreg); |
||||
|
} |
||||
|
|
||||
|
fprintf(stderr, "Continue? [Y/n] "); |
||||
|
fflush(stderr); |
||||
|
if (!user_ack(1)) { |
||||
|
fprintf(stderr, "Aborting on user request.\n"); |
||||
|
exit(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* See Winbond w83781d data sheet for bank details */ |
||||
|
if (bank && size != I2C_SMBUS_BLOCK_DATA) { |
||||
|
res = i2c_smbus_read_byte_data(file, bankreg); |
||||
|
if (res >= 0) { |
||||
|
old_bank = res; |
||||
|
res = i2c_smbus_write_byte_data(file, bankreg, |
||||
|
bank | (old_bank & 0xf0)); |
||||
|
} |
||||
|
if (res < 0) { |
||||
|
fprintf(stderr, "Error: Bank switching failed\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* handle all but word data */ |
||||
|
if (size != I2C_SMBUS_WORD_DATA || even) { |
||||
|
/* do the block transaction */ |
||||
|
if (size == I2C_SMBUS_BLOCK_DATA |
||||
|
|| size == I2C_SMBUS_I2C_BLOCK_DATA) { |
||||
|
unsigned char cblock[288]; |
||||
|
|
||||
|
if (size == I2C_SMBUS_BLOCK_DATA) { |
||||
|
res = i2c_smbus_read_block_data(file, bank, |
||||
|
cblock); |
||||
|
/* Remember returned block length for a nicer |
||||
|
display later */ |
||||
|
s_length = res; |
||||
|
} else { |
||||
|
for (res = 0; res < 256; res += i) { |
||||
|
i = i2c_smbus_read_i2c_block_data(file, |
||||
|
res, 32, cblock + res); |
||||
|
if (i <= 0) |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (res <= 0) { |
||||
|
fprintf(stderr, "Error: Block read failed, " |
||||
|
"return code %d\n", res); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (res >= 256) |
||||
|
res = 256; |
||||
|
for (i = 0; i < res; i++) |
||||
|
block[i] = cblock[i]; |
||||
|
if (size != I2C_SMBUS_BLOCK_DATA) |
||||
|
for (i = res; i < 256; i++) |
||||
|
block[i] = -1; |
||||
|
} |
||||
|
|
||||
|
if (size == I2C_SMBUS_BYTE) { |
||||
|
res = i2c_smbus_write_byte(file, 0); |
||||
|
if(res != 0) { |
||||
|
fprintf(stderr, "Error: Write start address " |
||||
|
"failed, return code %d\n", res); |
||||
|
exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f" |
||||
|
" 0123456789abcdef\n"); |
||||
|
for (i = 0; i < 256; i+=16) { |
||||
|
if (size == I2C_SMBUS_BLOCK_DATA && i >= s_length) |
||||
|
break; |
||||
|
printf("%02x: ", i); |
||||
|
for (j = 0; j < 16; j++) { |
||||
|
fflush(stdout); |
||||
|
if (size == I2C_SMBUS_BYTE_DATA) { |
||||
|
block[i+j] = res = |
||||
|
i2c_smbus_read_byte_data(file, i+j); |
||||
|
} else if (size == I2C_SMBUS_WORD_DATA) { |
||||
|
res = i2c_smbus_read_word_data(file, |
||||
|
i+j); |
||||
|
if (res < 0) { |
||||
|
block[i+j] = res; |
||||
|
block[i+j+1] = res; |
||||
|
} else { |
||||
|
block[i+j] = res & 0xff; |
||||
|
block[i+j+1] = res >> 8; |
||||
|
} |
||||
|
} else if (size == I2C_SMBUS_BYTE) { |
||||
|
block[i+j] = res = |
||||
|
i2c_smbus_read_byte(file); |
||||
|
} else |
||||
|
res = block[i+j]; |
||||
|
|
||||
|
if (size == I2C_SMBUS_BLOCK_DATA |
||||
|
&& i+j >= s_length) { |
||||
|
printf(" "); |
||||
|
} else if (res < 0) { |
||||
|
printf("XX "); |
||||
|
if (size == I2C_SMBUS_WORD_DATA) |
||||
|
printf("XX "); |
||||
|
} else { |
||||
|
printf("%02x ", block[i+j]); |
||||
|
if (size == I2C_SMBUS_WORD_DATA) |
||||
|
printf("%02x ", block[i+j+1]); |
||||
|
} |
||||
|
if (size == I2C_SMBUS_WORD_DATA) |
||||
|
j++; |
||||
|
} |
||||
|
printf(" "); |
||||
|
|
||||
|
for (j = 0; j < 16; j++) { |
||||
|
if (size == I2C_SMBUS_BLOCK_DATA |
||||
|
&& i+j >= s_length) |
||||
|
break; |
||||
|
|
||||
|
res = block[i+j]; |
||||
|
if (res < 0) |
||||
|
printf("X"); |
||||
|
else |
||||
|
if ((res & 0xff) == 0x00 |
||||
|
|| (res & 0xff) == 0xff) |
||||
|
printf("."); |
||||
|
else |
||||
|
if ((res & 0xff) < 32 |
||||
|
|| (res & 0xff) >= 127) |
||||
|
printf("?"); |
||||
|
else |
||||
|
printf("%c", res & 0xff); |
||||
|
} |
||||
|
printf("\n"); |
||||
|
} |
||||
|
} else { |
||||
|
printf(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f\n"); |
||||
|
for (i = 0; i < 256; i+=8) { |
||||
|
printf("%02x: ", i); |
||||
|
for (j = 0; j < 8; j++) { |
||||
|
res = i2c_smbus_read_word_data(file, i+j); |
||||
|
if (res < 0) |
||||
|
printf("XXXX "); |
||||
|
else |
||||
|
printf("%04x ", res & 0xffff); |
||||
|
} |
||||
|
printf("\n"); |
||||
|
} |
||||
|
} |
||||
|
if (bank && size != I2C_SMBUS_BLOCK_DATA) { |
||||
|
i2c_smbus_write_byte_data(file, bankreg, old_bank); |
||||
|
} |
||||
|
exit(0); |
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
.TH I2CGET 8 "June 2007" |
||||
|
.SH "NAME" |
||||
|
i2cget \- read from I2C/SMBus chip registers |
||||
|
|
||||
|
.SH SYNOPSIS |
||||
|
.B i2cget |
||||
|
.RB [ -f ] |
||||
|
.RB [ -y ] |
||||
|
.I i2cbus |
||||
|
.I chip-address |
||||
|
.RI [ data-address " [" mode ]] |
||||
|
.br |
||||
|
.B i2cget |
||||
|
.B -V |
||||
|
|
||||
|
.SH DESCRIPTION |
||||
|
i2cget is a small helper program to read registers visible through the I2C |
||||
|
bus (or SMBus). |
||||
|
|
||||
|
.SH OPTIONS |
||||
|
.TP |
||||
|
.B -V |
||||
|
Display the version and exit. |
||||
|
.TP |
||||
|
.B -f |
||||
|
Force access to the device even if it is already busy. By default, i2cget |
||||
|
will refuse to access a device which is already under the control of a |
||||
|
kernel driver. Using this flag is dangerous, it can seriously confuse the |
||||
|
kernel driver in question. It can also cause i2cget to return an invalid |
||||
|
value. So use at your own risk and only if you know what you're doing. |
||||
|
.TP |
||||
|
.B -y |
||||
|
Disable interactive mode. By default, i2cget will wait for a confirmation |
||||
|
from the user before messing with the I2C bus. When this flag is used, it |
||||
|
will perform the operation directly. This is mainly meant to be used in |
||||
|
scripts. Use with caution. |
||||
|
.PP |
||||
|
There are two required options to i2cget. \fIi2cbus\fR indicates the number |
||||
|
of the I2C bus to be scanned. This number should correspond to one of |
||||
|
the busses listed by \fIi2cdetect -l\fR. \fIchip-address\fR specifies the |
||||
|
address of the chip on that bus, and is an integer between 0x03 and 0x77. |
||||
|
.PP |
||||
|
\fIdata-address\fR specifies the address on that chip to read from, and is |
||||
|
an integer between 0x00 and 0xFF. If omitted, the currently active register |
||||
|
will be read (if that makes sense for the considered chip). |
||||
|
.PP |
||||
|
The \fImode\fR parameter, if specified, is one of the letters \fBb\fP, |
||||
|
\fBw\fP or \fBc\fP, corresponding to a read byte data, a read word data or a |
||||
|
write byte/read byte transaction, respectively. A \fBp\fP can also be appended |
||||
|
to the \fImode\fR parameter to enable PEC. If the \fImode\fR parameter is omitted, |
||||
|
i2cget defaults to a read byte data transaction, unless \fIdata-address\fR is |
||||
|
also omitted, in which case the default (and only valid) transaction is a |
||||
|
single read byte. |
||||
|
|
||||
|
.SH WARNING |
||||
|
i2cget can be extremely dangerous if used improperly. I2C and SMBus are designed |
||||
|
in such a way that an SMBus read transaction can be seen as a write transaction by |
||||
|
certain chips. This is particularly true if setting \fImode\fR to \fBcp\fP (write byte/read |
||||
|
byte with PEC). Be extremely careful using this program. |
||||
|
|
||||
|
.SH SEE ALSO |
||||
|
i2cdump(8), i2cset(8) |
||||
|
|
||||
|
.SH AUTHOR |
||||
|
Jean Delvare |
||||
|
http://www.lm-sensors.org/ |
||||
|
This manual page was strongly inspired from those written by David Z Maze |
||||
|
for i2cset. |
@ -0,0 +1,263 @@ |
|||||
|
/* |
||||
|
i2cget.c - A user-space program to read an I2C register. |
||||
|
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
Based on i2cset.c: |
||||
|
Copyright (C) 2001-2003 Frodo Looijaard <frodol@dds.nl>, and |
||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com> |
||||
|
Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include "util.h" |
||||
|
#include "i2cbusses.h" |
||||
|
#include "i2c-dev.h" |
||||
|
#include "version.h" |
||||
|
|
||||
|
void help(void) __attribute__ ((noreturn)); |
||||
|
|
||||
|
void help(void) |
||||
|
{ |
||||
|
fprintf(stderr, "Syntax: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS " |
||||
|
"[DATA-ADDRESS [MODE]]\n" |
||||
|
" i2cget -V\n" |
||||
|
" MODE can be: 'b' (read byte data, default)\n" |
||||
|
" 'w' (read word data)\n" |
||||
|
" 'c' (write byte/read byte)\n" |
||||
|
" If DATA-ADDRESS is omitted, a single read byte command is " |
||||
|
"issued\n" |
||||
|
" Append 'p' to MODE for PEC checking\n" |
||||
|
" I2CBUS is an integer\n"); |
||||
|
print_i2c_busses(0); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
int check_funcs(int file, int i2cbus, int size, int daddress, int pec) |
||||
|
{ |
||||
|
unsigned long funcs; |
||||
|
|
||||
|
/* check adapter functionality */ |
||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0) { |
||||
|
fprintf(stderr, "Error: Could not get the adapter " |
||||
|
"functionality matrix: %s\n", strerror(errno)); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
switch (size) { |
||||
|
case I2C_SMBUS_BYTE: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have read byte capability\n", i2cbus); |
||||
|
return -1; |
||||
|
} |
||||
|
if (daddress >= 0 |
||||
|
&& !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have write byte capability\n", i2cbus); |
||||
|
return -1; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_BYTE_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have read byte data capability\n", i2cbus); |
||||
|
return -1; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_WORD_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have read word data capability\n", i2cbus); |
||||
|
return -1; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (pec |
||||
|
&& !(funcs & (I2C_FUNC_SMBUS_HWPEC_CALC | I2C_FUNC_I2C))) { |
||||
|
fprintf(stderr, "Warning: Adapter for i2c bus %d does " |
||||
|
"not seem to support PEC\n", i2cbus); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int confirm(const char *filename, int address, int size, int daddress, int pec) |
||||
|
{ |
||||
|
int dont = 0; |
||||
|
|
||||
|
fprintf(stderr, "WARNING! This program can confuse your I2C " |
||||
|
"bus, cause data loss and worse!\n"); |
||||
|
|
||||
|
/* Don't let the user break his/her EEPROMs */ |
||||
|
if (address >= 0x50 && address <= 0x57 && pec) { |
||||
|
fprintf(stderr, "STOP! EEPROMs are I2C devices, not " |
||||
|
"SMBus devices. Using PEC\non I2C devices may " |
||||
|
"result in unexpected results, such as\n" |
||||
|
"trashing the contents of EEPROMs. We can't " |
||||
|
"let you do that, sorry.\n"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (size == I2C_SMBUS_BYTE && daddress >= 0 && pec) { |
||||
|
fprintf(stderr, "WARNING! All I2C chips and some SMBus chips " |
||||
|
"will interpret a write\nbyte command with PEC as a" |
||||
|
"write byte data command, effectively writing a\n" |
||||
|
"value into a register!\n"); |
||||
|
dont++; |
||||
|
} |
||||
|
|
||||
|
fprintf(stderr, "I will read from device file %s, chip " |
||||
|
"address 0x%02x, ", filename, address); |
||||
|
if (daddress < 0) |
||||
|
fprintf(stderr, "current data\naddress"); |
||||
|
else |
||||
|
fprintf(stderr, "data address\n0x%02x", daddress); |
||||
|
fprintf(stderr, ", using %s.\n", |
||||
|
size == I2C_SMBUS_BYTE ? (daddress < 0 ? |
||||
|
"read byte" : "write byte/read byte") : |
||||
|
size == I2C_SMBUS_BYTE_DATA ? "read byte data" : |
||||
|
"read word data"); |
||||
|
if (pec) |
||||
|
fprintf(stderr, "PEC checking enabled.\n"); |
||||
|
|
||||
|
fprintf(stderr, "Continue? [%s] ", dont ? "y/N" : "Y/n"); |
||||
|
fflush(stderr); |
||||
|
if (!user_ack(!dont)) { |
||||
|
fprintf(stderr, "Aborting on user request.\n"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
char *end; |
||||
|
int res, i2cbus, address, file; |
||||
|
int size = I2C_SMBUS_BYTE_DATA; |
||||
|
int daddress; |
||||
|
char filename[20]; |
||||
|
int pec = 0; |
||||
|
int flags = 0; |
||||
|
int force = 0, yes = 0, version = 0; |
||||
|
|
||||
|
/* handle (optional) flags first */ |
||||
|
while (1+flags < argc && argv[1+flags][0] == '-') { |
||||
|
switch (argv[1+flags][1]) { |
||||
|
case 'V': version = 1; break; |
||||
|
case 'f': force = 1; break; |
||||
|
case 'y': yes = 1; break; |
||||
|
default: |
||||
|
fprintf(stderr, "Warning: Unsupported flag " |
||||
|
"\"-%c\"!\n", argv[1+flags][1]); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
flags++; |
||||
|
} |
||||
|
|
||||
|
if (version) { |
||||
|
fprintf(stderr, "i2cget version %s\n", LM_VERSION); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (argc - flags < 3) |
||||
|
help(); |
||||
|
|
||||
|
i2cbus = strtol(argv[flags+1], &end, 0); |
||||
|
if (*end || i2cbus < 0 || i2cbus > 0x3f) { |
||||
|
fprintf(stderr, "Error: I2CBUS argument invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
address = strtol(argv[flags+2], &end, 0); |
||||
|
if (*end || address < 3 || address > 0x77) { |
||||
|
fprintf(stderr, "Error: Chip address invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
if (!(flags+3 < argc)) { |
||||
|
size = I2C_SMBUS_BYTE; |
||||
|
daddress = -1; |
||||
|
} else { |
||||
|
daddress = strtol(argv[flags+3], &end, 0); |
||||
|
if (*end || daddress < 0 || daddress > 0xff) { |
||||
|
fprintf(stderr, "Error: Data address invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (flags+4 < argc) { |
||||
|
switch (argv[flags+4][0]) { |
||||
|
case 'b': size = I2C_SMBUS_BYTE_DATA; break; |
||||
|
case 'w': size = I2C_SMBUS_WORD_DATA; break; |
||||
|
case 'c': size = I2C_SMBUS_BYTE; break; |
||||
|
default: |
||||
|
fprintf(stderr, "Error: Invalid mode!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
pec = argv[flags+4][1] == 'p'; |
||||
|
} |
||||
|
|
||||
|
file = open_i2c_dev(i2cbus, filename, 0); |
||||
|
if (file < 0 |
||||
|
|| check_funcs(file, i2cbus, size, daddress, pec) |
||||
|
|| set_slave_addr(file, address, force)) |
||||
|
exit(1); |
||||
|
|
||||
|
if (!yes && !confirm(filename, address, size, daddress, pec)) |
||||
|
exit(0); |
||||
|
|
||||
|
if (pec && ioctl(file, I2C_PEC, 1) < 0) { |
||||
|
fprintf(stderr, "Error: Could not set PEC: %s\n", |
||||
|
strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
switch (size) { |
||||
|
case I2C_SMBUS_BYTE: |
||||
|
if (daddress >= 0) { |
||||
|
res = i2c_smbus_write_byte(file, daddress); |
||||
|
if (res < 0) |
||||
|
fprintf(stderr, "Warning - write failed\n"); |
||||
|
} |
||||
|
res = i2c_smbus_read_byte(file); |
||||
|
break; |
||||
|
case I2C_SMBUS_WORD_DATA: |
||||
|
res = i2c_smbus_read_word_data(file, daddress); |
||||
|
break; |
||||
|
default: /* I2C_SMBUS_BYTE_DATA */ |
||||
|
res = i2c_smbus_read_byte_data(file, daddress); |
||||
|
} |
||||
|
close(file); |
||||
|
|
||||
|
if (res < 0) { |
||||
|
fprintf(stderr, "Error: Read failed\n"); |
||||
|
exit(2); |
||||
|
} |
||||
|
|
||||
|
printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res); |
||||
|
|
||||
|
exit(0); |
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
.TH I2CSET 8 "June 2007" |
||||
|
.SH "NAME" |
||||
|
i2cset \- set I2C registers |
||||
|
|
||||
|
.SH SYNOPSIS |
||||
|
.B i2cset |
||||
|
.RB [ -f ] |
||||
|
.RB [ -y ] |
||||
|
.I i2cbus |
||||
|
.I chip-address |
||||
|
.I data-address |
||||
|
.I value |
||||
|
.RI [ mode ] |
||||
|
.RI [ mask ] |
||||
|
.br |
||||
|
.B i2cset |
||||
|
.B -V |
||||
|
|
||||
|
.SH DESCRIPTION |
||||
|
i2cset is a small helper program to set registers visible through the I2C |
||||
|
bus. |
||||
|
|
||||
|
.SH OPTIONS |
||||
|
.TP |
||||
|
.B -V |
||||
|
Display the version and exit. |
||||
|
.TP |
||||
|
.B -f |
||||
|
Force access to the device even if it is already busy. By default, i2cset |
||||
|
will refuse to access a device which is already under the control of a |
||||
|
kernel driver. Using this flag is dangerous, it can seriously confuse the |
||||
|
kernel driver in question. It can also cause i2cset to silently write to |
||||
|
the wrong register. So use at your own risk and only if you know what |
||||
|
you're doing. |
||||
|
.TP |
||||
|
.B -y |
||||
|
Disable interactive mode. By default, i2cset will wait for a confirmation |
||||
|
from the user before messing with the I2C bus. When this flag is used, it |
||||
|
will perform the operation directly. This is mainly meant to be used in |
||||
|
scripts. |
||||
|
.PP |
||||
|
There are four required options to i2cset. \fIi2cbus\fR indicates the number |
||||
|
of the I2C bus to be scanned. This number should correspond to one of |
||||
|
the busses listed by \fIi2cdetect -l\fR. \fIchip-address\fR specifies the |
||||
|
address of the chip on that bus, and is an integer between 0x00 and 0x7F. |
||||
|
\fIdata-address\fR specifies the address on that chip to write to, and is an |
||||
|
integer between 0x00 and 0xFF. \fIvalue\fR is the value to write to that |
||||
|
location on the chip. |
||||
|
.PP |
||||
|
The \fImode\fR parameter, if specified, is one of the letters \fBb\fP or |
||||
|
\fBw\fP, corresponding to a write size of a single byte or a 16-bit word, |
||||
|
respectively. A \fBp\fP can also be appended to the \fImode\fR parameter to |
||||
|
enable PEC. If the \fImode\fR parameter is omitted, i2cset defaults to byte |
||||
|
mode without PEC. The \fIvalue\fR provided must be within range for the |
||||
|
specified data type (0x00-0xFF for bytes, 0x0000-0xFFFF for words). |
||||
|
.PP |
||||
|
The \fImask\fR parameter, if specified, describes which bits of \fIvalue\fR |
||||
|
will be actually written to \fIdata-address\fR. Bits set to 1 in the mask |
||||
|
are taken from \fIvalue\fR, while bits set to 0 will be read from |
||||
|
\fIdata-address\fR and thus preserved by the operation. |
||||
|
|
||||
|
.SH WARNING |
||||
|
i2cset can be extremely dangerous if used improperly. It can confuse your |
||||
|
I2C bus, cause data loss, or have more serious side effects. Writing to |
||||
|
a serial EEPROM on a memory DIMM (chip addresses between 0x50 and 0x57) may |
||||
|
DESTROY your memory, leaving your system unbootable! Be extremely careful |
||||
|
using this program. |
||||
|
|
||||
|
.SH SEE ALSO |
||||
|
i2cdump(8), isaset(8) |
||||
|
|
||||
|
.SH AUTHOR |
||||
|
Frodo Looijaard, Mark D. Studebaker, and the lm_sensors group |
||||
|
http://www.lm-sensors.org/ |
||||
|
This manual page was originally written by David Z Maze <dmaze@debian.org> for |
||||
|
the Debian GNU/Linux system. It was then reviewed by the lm_sensors team and |
||||
|
is now part of the lm_sensors source distribution. |
@ -0,0 +1,286 @@ |
|||||
|
/* |
||||
|
i2cset.c - A user-space program to write an I2C register. |
||||
|
Copyright (C) 2001-2003 Frodo Looijaard <frodol@dds.nl>, and |
||||
|
Mark D. Studebaker <mdsxyz123@yahoo.com> |
||||
|
Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include "i2cbusses.h" |
||||
|
#include "util.h" |
||||
|
#include "i2c-dev.h" |
||||
|
#include "version.h" |
||||
|
|
||||
|
void help(void) __attribute__ ((noreturn)); |
||||
|
|
||||
|
void help(void) |
||||
|
{ |
||||
|
fprintf(stderr, "Syntax: i2cset [-f] [-y] I2CBUS CHIP-ADDRESS DATA-ADDRESS " |
||||
|
"VALUE [MODE] [MASK]\n" |
||||
|
" i2cset -V\n" |
||||
|
" MODE is 'b[yte]' or 'w[ord]' (default b)\n" |
||||
|
" Append 'p' to MODE for PEC checking\n" |
||||
|
" I2CBUS is an integer\n"); |
||||
|
print_i2c_busses(0); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
char *end; |
||||
|
int res, i2cbus, address, size, file; |
||||
|
int value, daddress, vmask = 0; |
||||
|
int e1; |
||||
|
char filename[20]; |
||||
|
unsigned long funcs; |
||||
|
int pec = 0; |
||||
|
int flags = 0; |
||||
|
int force = 0, yes = 0, version = 0; |
||||
|
|
||||
|
/* handle (optional) flags first */ |
||||
|
while (1+flags < argc && argv[1+flags][0] == '-') { |
||||
|
switch (argv[1+flags][1]) { |
||||
|
case 'V': version = 1; break; |
||||
|
case 'f': force = 1; break; |
||||
|
case 'y': yes = 1; break; |
||||
|
default: |
||||
|
fprintf(stderr, "Warning: Unsupported flag " |
||||
|
"\"-%c\"!\n", argv[1+flags][1]); |
||||
|
help(); |
||||
|
exit(1); |
||||
|
} |
||||
|
flags++; |
||||
|
} |
||||
|
|
||||
|
if (version) { |
||||
|
fprintf(stderr, "i2cset version %s\n", LM_VERSION); |
||||
|
exit(0); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 5) |
||||
|
help(); |
||||
|
|
||||
|
i2cbus = strtol(argv[flags+1], &end, 0); |
||||
|
if (*end || i2cbus < 0 || i2cbus > 0x3f) { |
||||
|
fprintf(stderr, "Error: I2CBUS argument invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
address = strtol(argv[flags+2], &end, 0); |
||||
|
if (*end || address < 0 || address > 0x7f) { |
||||
|
fprintf(stderr, "Error: Chip address invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
daddress = strtol(argv[flags+3], &end, 0); |
||||
|
if (*end || daddress < 0 || daddress > 0xff) { |
||||
|
fprintf(stderr, "Error: Data address invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
value = strtol(argv[flags+4], &end, 0); |
||||
|
if (*end) { |
||||
|
fprintf(stderr, "Error: Data value invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
if (argc < flags + 6) { |
||||
|
fprintf(stderr, "No size specified (using byte-data access)\n"); |
||||
|
size = I2C_SMBUS_BYTE_DATA; |
||||
|
} else if (argv[flags+5][0] == 'b') { |
||||
|
size = I2C_SMBUS_BYTE_DATA; |
||||
|
pec = argv[flags+5][1] == 'p'; |
||||
|
} else if (argv[flags+5][0] == 'w') { |
||||
|
size = I2C_SMBUS_WORD_DATA; |
||||
|
pec = argv[flags+5][1] == 'p'; |
||||
|
} else { |
||||
|
fprintf(stderr, "Error: Invalid mode!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
if (argc >= flags + 7) { |
||||
|
vmask = strtol(argv[flags+6], &end, 0); |
||||
|
if (*end || vmask == 0) { |
||||
|
fprintf(stderr, "Error: Data value mask invalid!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (value < 0 |
||||
|
|| (size == I2C_SMBUS_BYTE_DATA && value > 0xff) |
||||
|
|| (size == I2C_SMBUS_WORD_DATA && value > 0xffff)) { |
||||
|
fprintf(stderr, "Error: Data value out of range!\n"); |
||||
|
help(); |
||||
|
} |
||||
|
|
||||
|
file = open_i2c_dev(i2cbus, filename, 0); |
||||
|
if (file < 0) { |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
/* check adapter functionality */ |
||||
|
if (ioctl(file, I2C_FUNCS, &funcs) < 0) { |
||||
|
fprintf(stderr, "Error: Could not get the adapter " |
||||
|
"functionality matrix: %s\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
switch (size) { |
||||
|
case I2C_SMBUS_BYTE_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have byte write capability\n", i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case I2C_SMBUS_WORD_DATA: |
||||
|
if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { |
||||
|
fprintf(stderr, "Error: Adapter for i2c bus %d does " |
||||
|
"not have word write capability\n", i2cbus); |
||||
|
exit(1); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (set_slave_addr(file, address, force) < 0) |
||||
|
exit(1); |
||||
|
|
||||
|
if (!yes) { |
||||
|
int dont = 0; |
||||
|
|
||||
|
fprintf(stderr, "WARNING! This program can confuse your I2C " |
||||
|
"bus, cause data loss and worse!\n"); |
||||
|
|
||||
|
if (address >= 0x50 && address <= 0x57) { |
||||
|
fprintf(stderr, "DANGEROUS! Writing to a serial " |
||||
|
"EEPROM on a memory DIMM\nmay render your " |
||||
|
"memory USELESS and make your system " |
||||
|
"UNBOOTABLE!\n"); |
||||
|
dont = 1; |
||||
|
} |
||||
|
|
||||
|
fprintf(stderr, "I will write to device file %s, chip address " |
||||
|
"0x%02x, data address\n0x%02x, data 0x%02x%s, mode " |
||||
|
"%s.\n", filename, address, daddress, value, |
||||
|
vmask ? " (masked)" : "", |
||||
|
size == I2C_SMBUS_BYTE_DATA ? "byte" : "word"); |
||||
|
if (pec) |
||||
|
fprintf(stderr, "PEC checking enabled.\n"); |
||||
|
|
||||
|
fprintf(stderr, "Continue? [%s] ", dont ? "y/N" : "Y/n"); |
||||
|
fflush(stderr); |
||||
|
if (!user_ack(!dont)) { |
||||
|
fprintf(stderr, "Aborting on user request.\n"); |
||||
|
exit(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (vmask) { |
||||
|
int oldvalue; |
||||
|
|
||||
|
if (size == I2C_SMBUS_WORD_DATA) { |
||||
|
oldvalue = i2c_smbus_read_word_data(file, daddress); |
||||
|
} else { |
||||
|
oldvalue = i2c_smbus_read_byte_data(file, daddress); |
||||
|
} |
||||
|
|
||||
|
if (oldvalue < 0) { |
||||
|
fprintf(stderr, "Error: Failed to read old value\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
value = (value & vmask) | (oldvalue & ~vmask); |
||||
|
|
||||
|
if (!yes) { |
||||
|
fprintf(stderr, "Old value 0x%0*x, write mask " |
||||
|
"0x%0*x: Will write 0x%0*x to register " |
||||
|
"0x%02x\n", |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, oldvalue, |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, vmask, |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, value, |
||||
|
daddress); |
||||
|
|
||||
|
fprintf(stderr, "Continue? [Y/n] "); |
||||
|
fflush(stderr); |
||||
|
if (!user_ack(1)) { |
||||
|
fprintf(stderr, "Aborting on user request.\n"); |
||||
|
exit(0); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (pec) { |
||||
|
if (ioctl(file, I2C_PEC, 1) < 0) { |
||||
|
fprintf(stderr, "Error: Could not set PEC: %s\n", |
||||
|
strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
if (!(funcs & (I2C_FUNC_SMBUS_HWPEC_CALC | I2C_FUNC_I2C))) { |
||||
|
fprintf(stderr, "Warning: Adapter for i2c bus %d does " |
||||
|
"not seem to actually support PEC\n", i2cbus); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
e1 = 0; |
||||
|
if (size == I2C_SMBUS_WORD_DATA) { |
||||
|
res = i2c_smbus_write_word_data(file, daddress, value); |
||||
|
} else { |
||||
|
res = i2c_smbus_write_byte_data(file, daddress, value); |
||||
|
} |
||||
|
if (res < 0) { |
||||
|
fprintf(stderr, "Warning - write failed\n"); |
||||
|
e1++; |
||||
|
} |
||||
|
|
||||
|
if (pec) { |
||||
|
if (ioctl(file, I2C_PEC, 0) < 0) { |
||||
|
fprintf(stderr, "Error: Could not clear PEC: %s\n", |
||||
|
strerror(errno)); |
||||
|
close(file); |
||||
|
exit(e1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (size == I2C_SMBUS_WORD_DATA) { |
||||
|
res = i2c_smbus_read_word_data(file, daddress); |
||||
|
} else { |
||||
|
res = i2c_smbus_read_byte_data(file, daddress); |
||||
|
} |
||||
|
close(file); |
||||
|
|
||||
|
if (res < 0) { |
||||
|
fprintf(stderr, "Warning - readback failed\n"); |
||||
|
e1++; |
||||
|
} else |
||||
|
if (res != value) { |
||||
|
e1++; |
||||
|
fprintf(stderr, "Warning - data mismatch - wrote " |
||||
|
"0x%0*x, read back 0x%0*x\n", |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, value, |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, res); |
||||
|
} else { |
||||
|
fprintf(stderr, "Value 0x%0*x written, readback matched\n", |
||||
|
size == I2C_SMBUS_WORD_DATA ? 4 : 2, value); |
||||
|
} |
||||
|
|
||||
|
exit(e1); |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
/* |
||||
|
util.c - helper functions |
||||
|
Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
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; version 2. |
||||
|
*/ |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include "util.h" |
||||
|
|
||||
|
/* Return 1 if we should continue, 0 if we should abort */ |
||||
|
int user_ack(int def) |
||||
|
{ |
||||
|
char s[2]; |
||||
|
int ret; |
||||
|
|
||||
|
if (!fgets(s, 2, stdin)) |
||||
|
return 0; /* Nack by default */ |
||||
|
|
||||
|
switch (s[0]) { |
||||
|
case 'y': |
||||
|
case 'Y': |
||||
|
ret = 1; |
||||
|
break; |
||||
|
case 'n': |
||||
|
case 'N': |
||||
|
ret = 0; |
||||
|
break; |
||||
|
default: |
||||
|
ret = def; |
||||
|
} |
||||
|
|
||||
|
/* Flush extra characters */ |
||||
|
while (s[0] != '\n') { |
||||
|
int c = fgetc(stdin); |
||||
|
if (c == EOF) { |
||||
|
ret = 0; |
||||
|
break; |
||||
|
} |
||||
|
s[0] = c; |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
@ -0,0 +1,15 @@ |
|||||
|
/* |
||||
|
util - helper functions |
||||
|
Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> |
||||
|
|
||||
|
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; version 2. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef _UTIL_H |
||||
|
#define _UTIL_H |
||||
|
|
||||
|
extern int user_ack(int def); |
||||
|
|
||||
|
#endif /* _UTIL_H */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue