diff options
Diffstat (limited to 'tools/i2cdetect.c')
-rw-r--r-- | tools/i2cdetect.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/tools/i2cdetect.c b/tools/i2cdetect.c new file mode 100644 index 0000000..4750db5 --- /dev/null +++ b/tools/i2cdetect.c @@ -0,0 +1,357 @@ +/* + 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <linux/i2c-dev.h> +#include "i2cbusses.h" +#include "../version.h" + +#define MODE_AUTO 0 +#define MODE_QUICK 1 +#define MODE_READ 2 +#define MODE_FUNC 3 + +static void help(void) +{ + fprintf(stderr, + "Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]\n" + " i2cdetect -F I2CBUS\n" + " i2cdetect -l\n" + " I2CBUS is an integer or an I2C bus name\n" + " If provided, FIRST and LAST limit the probing range.\n"); +} + +static int scan_i2c_bus(int file, int mode, int first, 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_PEC, + .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" }, + { .value = 0, .name = "" } +}; + +static 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"); + } +} + +/* + * Print the installed i2c busses. The format is those of Linux 2.4's + * /proc/bus/i2c for historical compatibility reasons. + */ +static void print_i2c_busses(void) +{ + struct i2c_adap *adapters; + int count; + + adapters = gather_i2c_busses(); + if (adapters == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return; + } + + for (count = 0; adapters[count].name; count++) { + printf("i2c-%d\t%-10s\t%-32s\t%s\n", + adapters[count].nr, adapters[count].funcs, + adapters[count].name, adapters[count].algo); + } + + free_adapters(adapters); +} + +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, "Error: Unsupported option " + "\"%s\"!\n", argv[1+flags]); + help(); + exit(1); + } + flags++; + } + + if (version) { + fprintf(stderr, "i2cdetect version %s\n", VERSION); + exit(0); + } + + if (list) { + print_i2c_busses(); + exit(0); + } + + if (argc < flags + 2) { + fprintf(stderr, "Error: No i2c-bus specified!\n"); + help(); + exit(1); + } + i2cbus = lookup_i2c_bus(argv[flags+1]); + if (i2cbus < 0) { + 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); +} |