From fdbede5dcf9c853de7a8f751838394585a71dbfe Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Mon, 15 Jul 2013 10:29:37 -0400 Subject: [PATCH] eepromread: simple program to view eeprom contents This program uses the i2c /dev interface to read the contents of EEPROMs and display it to the user in HEX and ASCII. It also has a mode that can display data in label:value pairs. That mode is used for board detection in the rc script to start the right i2c drivers for the board. Change-Id: I0bf5b13ffab5a89533c762d6881a145cf7f14914 --- commands/Makefile | 5 + commands/eepromread/Makefile | 5 + commands/eepromread/board_info.c | 87 ++++++++++++ commands/eepromread/eepromread.1 | 46 +++++++ commands/eepromread/eepromread.c | 205 +++++++++++++++++++++++++++++ commands/eepromread/eepromread.h | 8 ++ distrib/sets/lists/minix/md.evbarm | 2 + etc/usr/rc | 24 ++++ 8 files changed, 382 insertions(+) create mode 100644 commands/eepromread/Makefile create mode 100644 commands/eepromread/board_info.c create mode 100644 commands/eepromread/eepromread.1 create mode 100644 commands/eepromread/eepromread.c create mode 100644 commands/eepromread/eepromread.h diff --git a/commands/Makefile b/commands/Makefile index aa9c0d42a..3cae84cb1 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -38,4 +38,9 @@ SUBDIR+= atnormalize dosread fdisk loadfont \ autopart part partition playwave \ recwave repartition screendump .endif + +.if ${MACHINE_ARCH} == "earm" +SUBDIR+= eepromread +.endif + .include diff --git a/commands/eepromread/Makefile b/commands/eepromread/Makefile new file mode 100644 index 000000000..66c0b43cc --- /dev/null +++ b/commands/eepromread/Makefile @@ -0,0 +1,5 @@ +PROG= eepromread +SRCS= eepromread.c eepromread.h board_info.c +MAN= eepromread.1 + +.include diff --git a/commands/eepromread/board_info.c b/commands/eepromread/board_info.c new file mode 100644 index 000000000..000086837 --- /dev/null +++ b/commands/eepromread/board_info.c @@ -0,0 +1,87 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "eepromread.h" + +/* + * Attempt to read the board info from an eeprom on this board. + * Currently only supports the BeagleBone and BeagleBone Black. + * In the future, this could be expanded to support cape EEPROMs. + */ + +static int board_info_beaglebone(int fd, i2c_addr_t address); + +/* Memory Layout of the BeagleBone and BeagleBone Black EEPROM */ +typedef struct beaglebone_info +{ + uint8_t magic_number[4]; /* Should be 0xaa 0x55 0x33 0xee */ + char board_name[8]; /* Warning: strings not NULL terminated */ + char version[4]; + char serial_number[12]; + char config[32]; /* All 0x00 on BeagleBone White */ + char mac_addrs[3][6]; /* Not set on BeagleBone White */ +} beaglebone_info_t; + +static int +board_info_beaglebone(int fd, i2c_addr_t address) +{ + int r; + int i, j; + char s[33]; + beaglebone_info_t boneinfo; + + r = eeprom_read(fd, address, 0x0000, &boneinfo, + sizeof(beaglebone_info_t)); + if (r == -1) { + fprintf(stderr, "Failed to read BeagleBone info r=%d\n", r); + return -1; + } + + fprintf(stdout, "%-16s: 0x%x%x%x%x\n", "MAGIC_NUMBER", + boneinfo.magic_number[0], boneinfo.magic_number[1], + boneinfo.magic_number[2], boneinfo.magic_number[3]); + + memset(s, '\0', 33); + memcpy(s, boneinfo.board_name, 8); + fprintf(stdout, "%-16s: %s\n", "BOARD_NAME", s); + + memset(s, '\0', 33); + memcpy(s, boneinfo.version, 4); + fprintf(stdout, "%-16s: %s\n", "VERSION", s); + + memcpy(s, boneinfo.serial_number, 12); + fprintf(stdout, "%-16s: %s\n", "SERIAL_NUMBER", s); + + return 0; +} + +int +board_info(int fd, i2c_addr_t address) +{ + int r; + uint8_t magic_number[4]; + + r = eeprom_read(fd, address, 0x0000, &magic_number, 4); + if (r == -1) { + printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN"); + return 0; + } + + if (magic_number[0] == 0xaa && magic_number[1] == 0x55 && + magic_number[2] == 0x33 && magic_number[3] == 0xee) { + board_info_beaglebone(fd, address); + } else { + printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN"); + } + + return 0; +} diff --git a/commands/eepromread/eepromread.1 b/commands/eepromread/eepromread.1 new file mode 100644 index 000000000..a3bbfc91d --- /dev/null +++ b/commands/eepromread/eepromread.1 @@ -0,0 +1,46 @@ +.TH EEPROMREAD 1 +.SH NAME +eepromread \- read data from an EEPROM +.SH SYNOPSIS +\fBeepromread\fR [\fB\-i\fR] [\fB\-f\fR \fIdev\fR] [\fB\-a\fR \fIslave_addr\fR] +.br +.de FL +.TP +\\fB\\$1\\fR +\\$2 +.. +.de EX +.TP 20 +\\fB\\$1\\fR +# \\$2 +.. +.SH OPTIONS +.TP 5 +.B \-i +# interpret the data on the EEPROM and display it as a set of fields. +.TP 5 +.B \-f +# Use \fIdevice\fR instead of \fI/dev/i2c-1\fR. +.TP 5 +.B \-a +# Use \fIslave_address\fR instead of \fI0x50\fR. +.SH EXAMPLES +.TP 20 +.B eepromread -i +# display the contents of the EEPROM as a list of label:value pairs. +.TP 20 +.B eepromread +# display the first 256 bytes of the EEPROM in HEX and ASCII. +.TP 20 +.B eepromread -f /dev/i2c-3 -a 0x54 +# display the first 256 bytes of the EEPROM on I2C bus 3, slave address 0x54. +.SH DESCRIPTION +.PP +\fIeepromread\fR is a simple tool for viewing the contents of an EEPROM. +For EEPROM data that is in a specific format that this program knows how to +detect, \fIeepromread\fR can properly format each of the fields and display +the information via the \fI-i\fR command line option. +.SH NOTES +If the \fIcat24c256\fR driver has claimed the EEPROM device that this +program is attempting to read from, then this program will fail. Once +a driver claims an I2C device, the driver has exclusive access. diff --git a/commands/eepromread/eepromread.c b/commands/eepromread/eepromread.c new file mode 100644 index 000000000..1d9e84f58 --- /dev/null +++ b/commands/eepromread/eepromread.c @@ -0,0 +1,205 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "eepromread.h" + +static int __eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, + void *buf, size_t buflen); +static int eeprom_dump(int fd, i2c_addr_t addr); + +#define DEFAULT_I2C_DEVICE "/dev/i2c-1" +#define DEFAULT_I2C_ADDRESS 0x50 + +/* + * The /dev interface only supports 32 byte reads/writes and the EEPROM is + * larger, so to read the whole EEPROM, the task is broken down into 32 byte + * chunks in eeprom_read(). __eeprom_read32() does the actual ioctl() to do + * the read. + * + * A future enhancement might be to add support for the /dev/eeprom interface + * and if one way fails, fall back to the other. /dev/eeprom can fail if the + * eeprom driver isn't running and /dev/i2c can fail if the eeprom driver + * claimed the eeprom device. + */ + +static int +__eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, + size_t buflen) +{ + int r; + minix_i2c_ioctl_exec_t ioctl_exec; + + if (buflen > I2C_EXEC_MAX_BUFLEN || buf == NULL + || ((memaddr + buflen) < memaddr)) { + errno = EINVAL; + return -1; + } + + memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t)); + + ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP; + ioctl_exec.iie_addr = addr; + + /* set the address to read from */ + ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff); + ioctl_exec.iie_cmd[1] = (memaddr & 0xff); + ioctl_exec.iie_cmdlen = 2; + + ioctl_exec.iie_buflen = buflen; + + r = ioctl(fd, MINIX_I2C_IOCTL_EXEC, &ioctl_exec); + if (r == -1) { + return -1; + } + + /* call was good, copy results to caller's buffer */ + memcpy(buf, ioctl_exec.iie_buf, buflen); + + return 0; +} + +int +eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, + size_t buflen) +{ + int r; + uint16_t i; + + if (buf == NULL || ((memaddr + buflen) < memaddr)) { + errno = EINVAL; + return -1; + } + + for (i = 0; i < buflen; i += 32) { + + r = __eeprom_read32(fd, addr, memaddr + i, buf + i, + ((buflen - i) < 32) ? (buflen - i) : 32); + if (r == -1) { + return -1; + } + } + + return 0; +} + +/* + * Read 256 bytes and print it to the screen in HEX and ASCII. + */ +static int +eeprom_dump(int fd, i2c_addr_t addr) +{ + int i, j, r; + uint8_t buf[256]; + + memset(buf, '\0', 256); + + r = eeprom_read(fd, addr, 0x0000, buf, 256); + if (r == -1) { + return r; + } + + /* print table header */ + for (i = 0; i < 2; i++) { + printf(" "); + for (j = 0x0; j <= 0xf; j++) { + if (i == 0) { + printf(" "); + } + printf("%x", j); + } + } + printf("\n"); + + /* print table data */ + for (i = 0x00; i < 0xff; i += 0x10) { + + /* row label */ + printf("%02x:", i); + + /* row data (in hex) */ + for (j = 0x0; j <= 0xf; j++) { + printf(" %02x", buf[i + j]); + } + + printf(" "); + + /* row data (in ASCII) */ + for (j = 0x0; j <= 0xf; j++) { + if (isprint(buf[i + j])) { + printf("%c", buf[i + j]); + } else { + printf("."); + } + } + + printf("\n"); + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int r, fd; + int ch, iflag = 0; + char *device = DEFAULT_I2C_DEVICE; + i2c_addr_t address = DEFAULT_I2C_ADDRESS; + + setprogname(*argv); + + while ((ch = getopt(argc, argv, "a:f:i")) != -1) { + switch (ch) { + case 'a': + address = strtol(optarg, NULL, 0x10); + break; + case 'f': + device = optarg; + break; + case 'i': + iflag = 1; + break; + default: + break; + } + } + + fd = open(device, O_RDWR); + if (fd == -1) { + fprintf(stderr, "open(): %s\n", strerror(errno)); + return 1; + } + + if (iflag == 1) { + r = board_info(fd, address); + if (r == -1) { + fprintf(stderr, "board_info(): %s\n", strerror(errno)); + return 1; + } + } else { + r = eeprom_dump(fd, address); + if (r == -1) { + fprintf(stderr, "eeprom_dump(): %s\n", strerror(errno)); + return 1; + } + } + + r = close(fd); + if (r == -1) { + fprintf(stderr, "close(): %s\n", strerror(errno)); + return 1; + } + + return 0; +} + diff --git a/commands/eepromread/eepromread.h b/commands/eepromread/eepromread.h new file mode 100644 index 000000000..ee04677b5 --- /dev/null +++ b/commands/eepromread/eepromread.h @@ -0,0 +1,8 @@ +#ifndef __EEPROMREAD_H +#define __EEPROMREAD_H + +int eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, + size_t buflen); +int board_info(int fd, i2c_addr_t address); + +#endif /* __EEPROMREAD_H */ diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index 5d9778808..e671d2f73 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -10,6 +10,7 @@ ./multiboot/mod10_vm minix-sys ./multiboot/mod11_pfs minix-sys ./multiboot/mod12_init minix-sys +./usr/bin/eepromread minix-sys ./usr/include/arm minix-sys ./usr/include/arm/aeabi.h minix-sys ./usr/include/arm/ansi.h minix-sys @@ -101,6 +102,7 @@ ./usr/lib/libi2cdriver_pic.a minix-sys ./usr/lib/libpadconf.a minix-sys ./usr/lib/libpadconf_pic.a minix-sys +./usr/man/man1/eepromread.1 minix-sys ./usr/mdec minix-sys ./usr/sbin/cat24c256 minix-sys ./usr/sbin/fb minix-sys diff --git a/etc/usr/rc b/etc/usr/rc index 21c2cd99c..ab8595c8f 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -194,6 +194,30 @@ start) -args instance=${bus} done echo . + + BOARD_NAME=`eepromread -i | sed -n 's/^BOARD_NAME : \(.*\)$/\1/p'` + case "${BOARD_NAME}" in + A335BONE) + echo "Detected BeagleBone" + echo -n "Starting i2c device drivers: " + test -e /dev/eepromb1s50 || (cd /dev && MAKEDEV eepromb1s50) + up cat24c256 -dev /dev/eepromb1s50 \ + -label cat24c256.1.50 -args 'bus=1 address=0x50' + ;; + A335BNLT) + echo "Detected BeagleBone Black" + echo -n "Starting i2c device drivers: " + test -e /dev/eepromb1s50 || (cd /dev && MAKEDEV eepromb1s50) + up cat24c256 -dev /dev/eepromb1s50 \ + -label cat24c256.1.50 -args 'bus=1 address=0x50' + ;; + UNKNOWN) + echo "Unable to detect board -- assuming BeagleBoard-xM" + echo -n "Starting i2c device drivers: " + ;; + esac + + echo . fi if [ "$net" ]