Browse Source

Oops, forgot to create the trunk level.

git-svn-id: http://lm-sensors.org/svn/i2c-tools/trunk@4481 7894878c-1315-0410-8ee3-d5d059ff63e0
tags/v3.0.0
Jean Delvare 18 years ago
commit
c922a78bb4
  1. 259
      tools/i2cbusses.c
  2. 29
      tools/i2cbusses.h
  3. 88
      tools/i2cdetect.8
  4. 347
      tools/i2cdetect.c
  5. 80
      tools/i2cdump.8
  6. 426
      tools/i2cdump.c
  7. 68
      tools/i2cget.8
  8. 263
      tools/i2cget.c
  9. 77
      tools/i2cset.8
  10. 286
      tools/i2cset.c
  11. 47
      tools/util.c
  12. 15
      tools/util.h

259
tools/i2cbusses.c

@ -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;
}

29
tools/i2cbusses.h

@ -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

88
tools/i2cdetect.8

@ -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.

347
tools/i2cdetect.c

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

80
tools/i2cdump.8

@ -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.

426
tools/i2cdump.c

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

68
tools/i2cget.8

@ -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.

263
tools/i2cget.c

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

77
tools/i2cset.8

@ -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.

286
tools/i2cset.c

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

47
tools/util.c

@ -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;
}

15
tools/util.h

@ -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 */
Loading…
Cancel
Save