Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/i2c/i2c.c
/*- | /*- | ||||
* Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka | * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka | ||||
* Copyright (C) 2015 Emmanuel Vadot <manu@bidouilliste.com> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | struct options { | ||||
int verbose; | int verbose; | ||||
int addr_set; | int addr_set; | ||||
int binary; | int binary; | ||||
int scan; | int scan; | ||||
int skip; | int skip; | ||||
int reset; | int reset; | ||||
int mode; | int mode; | ||||
char dir; | char dir; | ||||
char rdrw; | |||||
uint32_t addr; | uint32_t addr; | ||||
uint32_t off; | uint32_t off; | ||||
}; | }; | ||||
struct skip_range { | struct skip_range { | ||||
int start; | int start; | ||||
int end; | int end; | ||||
}; | }; | ||||
__dead2 static void | __dead2 static void | ||||
usage(void) | usage(void) | ||||
{ | { | ||||
fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] " | fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] " | ||||
"[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-v]\n", | "[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-x] [-v]\n", | ||||
getprogname()); | getprogname()); | ||||
fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n", | fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n", | ||||
getprogname()); | getprogname()); | ||||
fprintf(stderr, " %s -r [-f device] -v\n", getprogname()); | fprintf(stderr, " %s -r [-f device] -v\n", getprogname()); | ||||
exit(EX_USAGE); | exit(EX_USAGE); | ||||
} | } | ||||
static struct skip_range | static struct skip_range | ||||
Show All 31 Lines | for (i = 0; i < max_index; i++) { | ||||
if (token == NULL) | if (token == NULL) | ||||
break; | break; | ||||
sk_addr[i] = strtoul(token, 0, 16); | sk_addr[i] = strtoul(token, 0, 16); | ||||
} | } | ||||
return (i); | return (i); | ||||
} | } | ||||
static int | static int | ||||
scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr) | scan_bus(struct iiccmd cmd, char rdrw, char *dev, int skip, char *skip_addr) | ||||
{ | { | ||||
struct iic_rdwr_data msg; | |||||
struct skip_range addr_range = { 0, 0 }; | struct skip_range addr_range = { 0, 0 }; | ||||
int *tokens, fd, error, i, index, j; | int *tokens, fd, error, index, i; | ||||
int len = 0, do_skip = 0, no_range = 1; | int len = 0, no_range = 1, row, col, addr, result; | ||||
char buf = 0; | |||||
fd = open(dev, O_RDWR); | fd = open(dev, O_RDWR); | ||||
if (fd == -1) { | if (fd == -1) { | ||||
fprintf(stderr, "Error opening I2C controller (%s) for " | fprintf(stderr, "Error opening I2C controller (%s) for " | ||||
"scanning: %s\n", dev, strerror(errno)); | "scanning: %s\n", dev, strerror(errno)); | ||||
return (EX_NOINPUT); | return (EX_NOINPUT); | ||||
} | } | ||||
Show All 16 Lines | if (skip) { | ||||
if (!no_range && (addr_range.start > addr_range.end)) { | if (!no_range && (addr_range.start > addr_range.end)) { | ||||
fprintf(stderr, "Skip address out of range\n"); | fprintf(stderr, "Skip address out of range\n"); | ||||
error = -1; | error = -1; | ||||
goto out; | goto out; | ||||
} | } | ||||
} | } | ||||
printf("Scanning I2C devices on %s: ", dev); | msg.nmsgs = 1; | ||||
for (i = 1; i < 127; i++) { | if ((msg.msgs = calloc(1, sizeof(struct iic_msg))) == NULL) | ||||
errx(1, "%s", strerror(errno)); | |||||
msg.msgs->buf = &buf; | |||||
msg.msgs->len = 1; | |||||
msg.msgs->flags = IIC_M_RD; | |||||
printf("Scanning I2C devices on %s:\n", dev); | |||||
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); | |||||
for (row = 1; row < 8; row++) { | |||||
printf("%02X:", row * 16); | |||||
for (col = 0; col < 16; col++) { | |||||
addr = (row * 16 + col); | |||||
result = 0; | |||||
if (skip && ( addr_range.start < addr_range.end)) { | if (skip && ( addr_range.start < addr_range.end)) { | ||||
if (i >= addr_range.start && i <= addr_range.end) | if (addr >= addr_range.start && addr <= addr_range.end) { | ||||
printf(" XX"); | |||||
continue; | continue; | ||||
} | |||||
} else if (skip && no_range) | } else if (skip && no_range) | ||||
for (j = 0; j < index; j++) { | for (i = 0; i < index; i++) { | ||||
if (tokens[j] == i) { | if (tokens[i] == (row * 16) + col) { | ||||
do_skip = 1; | printf(" XX"); | ||||
break; | continue; | ||||
} | } | ||||
} | } | ||||
if (do_skip) { | if (rdrw) { | ||||
do_skip = 0; | msg.msgs->slave = addr << 1; | ||||
continue; | if (ioctl(fd, I2CRDWR, &msg) < 0) | ||||
} | result = errno; | ||||
} else { | |||||
cmd.slave = i << 1; | cmd.slave = addr << 1; | ||||
cmd.last = 1; | cmd.last = 1; | ||||
cmd.count = 0; | cmd.count = 0; | ||||
error = ioctl(fd, I2CRSTCARD, &cmd); | if (ioctl(fd, I2CRSTCARD, &cmd) < 0) | ||||
if (error) | |||||
goto out; | goto out; | ||||
cmd.slave = i << 1; | cmd.slave = addr << 1; | ||||
cmd.last = 1; | cmd.last = 1; | ||||
error = ioctl(fd, I2CSTART, &cmd); | if (ioctl(fd, I2CSTART, &cmd) < 0) | ||||
if (!error) | result = errno; | ||||
printf("%x ", i); | cmd.slave = addr << 1; | ||||
cmd.slave = i << 1; | |||||
cmd.last = 1; | cmd.last = 1; | ||||
error = ioctl(fd, I2CSTOP, &cmd); | ioctl(fd, I2CSTOP, &cmd); | ||||
} | } | ||||
switch (result) { | |||||
case 0: | |||||
printf(" %02X", addr); | |||||
break; | |||||
case EIO: | |||||
case ENXIO: | |||||
case ENOENT: | |||||
printf(" --"); | |||||
break; | |||||
case EBUSY: | |||||
printf(" UU"); | |||||
break; | |||||
default: | |||||
printf(" "); | |||||
break; | |||||
} | |||||
} | |||||
printf("\n"); | printf("\n"); | ||||
} | |||||
printf("\n"); | |||||
if (!rdrw) | |||||
error = ioctl(fd, I2CRSTCARD, &cmd); | error = ioctl(fd, I2CRSTCARD, &cmd); | ||||
out: | out: | ||||
close(fd); | close(fd); | ||||
if (skip && no_range) | if (skip && no_range) | ||||
free(tokens); | free(tokens); | ||||
if (error) { | if (rdrw) | ||||
free(msg.msgs); | |||||
if (!rdrw && error) { | |||||
fprintf(stderr, "Error scanning I2C controller (%s): %s\n", | fprintf(stderr, "Error scanning I2C controller (%s): %s\n", | ||||
dev, strerror(errno)); | dev, strerror(errno)); | ||||
return (EX_NOINPUT); | return (EX_NOINPUT); | ||||
} else | } else | ||||
return (EX_OK); | return (EX_OK); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | |||||
err2: | err2: | ||||
if (err_msg) | if (err_msg) | ||||
fprintf(stderr, "%s", err_msg); | fprintf(stderr, "%s", err_msg); | ||||
close(fd); | close(fd); | ||||
return (1); | return (1); | ||||
} | } | ||||
static int | |||||
i2c_rdrw(char *dev, struct options i2c_opt, char *i2c_buf) | |||||
{ | |||||
struct iic_rdwr_data msgs; | |||||
int fd, error, i; | |||||
char *err_msg, ch; | |||||
if (i2c_opt.dir == 'w') { | |||||
if (i2c_opt.verbose && !i2c_opt.binary) | |||||
fprintf(stderr, "Enter %u bytes of data: ", i2c_opt.count); | |||||
for (i = 0; i < i2c_opt.count; i++) { | |||||
ch = getchar(); | |||||
if (ch == EOF) { | |||||
free(i2c_buf); | |||||
err(1, "not enough data, exiting\n"); | |||||
} | |||||
i2c_buf[i] = ch; | |||||
} | |||||
} | |||||
fd = open(dev, O_RDWR); | |||||
if (fd == -1) | |||||
err(1, "open failed"); | |||||
msgs.nmsgs = 1; | |||||
if (i2c_opt.width) { | |||||
msgs.nmsgs = 2; | |||||
msgs.msgs = calloc(2, sizeof(struct iic_msg)); | |||||
msgs.msgs[0].slave = i2c_opt.addr; | |||||
msgs.msgs[0].len = i2c_opt.width / 8; | |||||
if ((msgs.msgs[0].buf = calloc(msgs.msgs[0].len, sizeof(uint8_t))) == NULL) { | |||||
err_msg = "can't malloc\n"; | |||||
goto err; | |||||
} | |||||
if (msgs.msgs[0].len == 1) | |||||
msgs.msgs[0].buf[0] = i2c_opt.off & 0xff; | |||||
else if (msgs.msgs[0].len == 2) { | |||||
msgs.msgs[0].buf[0] = (i2c_opt.off >> 8) & 0xff; | |||||
msgs.msgs[0].buf[1] = i2c_opt.off & 0xff; | |||||
} | |||||
if (msgs.msgs[0].buf == NULL) { | |||||
err_msg = "error: offset malloc"; | |||||
goto err; | |||||
} | |||||
msgs.msgs[0].flags = IIC_M_WR; | |||||
if (i2c_opt.mode == I2C_MODE_REPEATED_START || i2c_opt.mode == I2C_MODE_NONE) | |||||
msgs.msgs[0].flags |= IIC_M_NOSTOP; | |||||
} | |||||
msgs.msgs[msgs.nmsgs - 1].slave = i2c_opt.addr; | |||||
msgs.msgs[msgs.nmsgs - 1].len = i2c_opt.count; | |||||
msgs.msgs[msgs.nmsgs - 1].buf = i2c_buf; | |||||
if (i2c_opt.dir == 'r') | |||||
msgs.msgs[msgs.nmsgs - 1].flags = IIC_M_RD; | |||||
else | |||||
msgs.msgs[msgs.nmsgs - 1].flags = IIC_M_WR; | |||||
if (i2c_opt.mode == I2C_MODE_NONE) | |||||
msgs.msgs[msgs.nmsgs - 1].flags |= IIC_M_NOSTART; | |||||
error = ioctl(fd, I2CRDWR, &msgs); | |||||
if (error == -1) { | |||||
err_msg = "error sending i2c frame\n"; | |||||
goto err; | |||||
} | |||||
close(fd); | |||||
return (0); | |||||
err: | |||||
if (err_msg) | |||||
fprintf(stderr, "%s", err_msg); | |||||
close(fd); | |||||
return (1); | |||||
} | |||||
int | int | ||||
main(int argc, char** argv) | main(int argc, char** argv) | ||||
{ | { | ||||
struct iiccmd cmd; | struct iiccmd cmd; | ||||
struct options i2c_opt; | struct options i2c_opt; | ||||
char *dev, *skip_addr, *i2c_buf; | char *dev, *skip_addr, *i2c_buf; | ||||
int error, chunk_size, i, j, ch; | int error, chunk_size, i, j, ch; | ||||
Show All 12 Lines | main(int argc, char** argv) | ||||
i2c_opt.dir = 'r'; /* direction = read */ | i2c_opt.dir = 'r'; /* direction = read */ | ||||
i2c_opt.width = 8; | i2c_opt.width = 8; | ||||
i2c_opt.count = 1; | i2c_opt.count = 1; | ||||
i2c_opt.binary = 0; /* ASCII text output */ | i2c_opt.binary = 0; /* ASCII text output */ | ||||
i2c_opt.scan = 0; /* no bus scan */ | i2c_opt.scan = 0; /* no bus scan */ | ||||
i2c_opt.skip = 0; /* scan all addresses */ | i2c_opt.skip = 0; /* scan all addresses */ | ||||
i2c_opt.reset = 0; /* no bus reset */ | i2c_opt.reset = 0; /* no bus reset */ | ||||
i2c_opt.mode = I2C_MODE_NOTSET; | i2c_opt.mode = I2C_MODE_NOTSET; | ||||
i2c_opt.rdrw = 0; /* Use read/write by default */ | |||||
while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) { | while ((ch = getopt(argc, argv, "a:A:f:d:o:w:c:m:n:sbvrhx")) != -1) { | ||||
switch(ch) { | switch(ch) { | ||||
case 'a': | case 'a': | ||||
i2c_opt.addr = (strtoul(optarg, 0, 16) << 1); | case 'A': | ||||
if (i2c_opt.addr_set) | |||||
usage(); | |||||
i2c_opt.addr = ch == 'a' ? (strtoul(optarg, 0, 16) << 1) : strtoul(optarg, 0, 16); | |||||
if (i2c_opt.addr == 0 && errno == EINVAL) | if (i2c_opt.addr == 0 && errno == EINVAL) | ||||
i2c_opt.addr_set = 0; | i2c_opt.addr_set = 0; | ||||
else | else | ||||
i2c_opt.addr_set = 1; | i2c_opt.addr_set = 1; | ||||
break; | break; | ||||
case 'f': | case 'f': | ||||
dev = optarg; | dev = optarg; | ||||
break; | break; | ||||
Show All 32 Lines | case 'b': | ||||
i2c_opt.binary = 1; | i2c_opt.binary = 1; | ||||
break; | break; | ||||
case 'v': | case 'v': | ||||
i2c_opt.verbose = 1; | i2c_opt.verbose = 1; | ||||
break; | break; | ||||
case 'r': | case 'r': | ||||
i2c_opt.reset = 1; | i2c_opt.reset = 1; | ||||
break; | break; | ||||
case 'x': | |||||
i2c_opt.rdrw = 1; | |||||
break; | |||||
case 'h': | case 'h': | ||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
} | } | ||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
Show All 19 Lines | if ((i2c_opt.addr_set == 0) || | ||||
!(i2c_opt.width == 0 || i2c_opt.width == 8 || | !(i2c_opt.width == 0 || i2c_opt.width == 8 || | ||||
i2c_opt.width == 16)) | i2c_opt.width == 16)) | ||||
usage(); | usage(); | ||||
} | } | ||||
if (i2c_opt.verbose) | if (i2c_opt.verbose) | ||||
fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, " | fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, " | ||||
"offset: 0x%02x, width: %u, count: %u\n", dev, | "offset: 0x%02x, width: %u, count: %u\n", dev, | ||||
i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off, | i2c_opt.addr, i2c_opt.dir, i2c_opt.off, | ||||
i2c_opt.width, i2c_opt.count); | i2c_opt.width, i2c_opt.count); | ||||
if (i2c_opt.scan) | if (i2c_opt.scan) | ||||
exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr)); | exit(scan_bus(cmd, i2c_opt.rdrw, dev, i2c_opt.skip, skip_addr)); | ||||
if (i2c_opt.reset) | if (i2c_opt.reset) | ||||
exit(reset_bus(cmd, dev)); | exit(reset_bus(cmd, dev)); | ||||
i2c_buf = malloc(i2c_opt.count); | i2c_buf = malloc(i2c_opt.count); | ||||
if (i2c_buf == NULL) | if (i2c_buf == NULL) | ||||
err(1, "data malloc"); | err(1, "data malloc"); | ||||
if (i2c_opt.rdrw == 1) { | |||||
error = i2c_rdrw(dev, i2c_opt, i2c_buf); | |||||
} else { | |||||
if (i2c_opt.dir == 'w') { | if (i2c_opt.dir == 'w') { | ||||
error = i2c_write(dev, i2c_opt, i2c_buf); | error = i2c_write(dev, i2c_opt, i2c_buf); | ||||
if (error) { | } else if (i2c_opt.dir == 'r') { | ||||
free(i2c_buf); | error = i2c_read(dev, i2c_opt, i2c_buf); | ||||
return (1); | |||||
} | } | ||||
} | } | ||||
if (i2c_opt.dir == 'r') { | |||||
error = i2c_read(dev, i2c_opt, i2c_buf); | |||||
if (error) { | if (error) { | ||||
free(i2c_buf); | free(i2c_buf); | ||||
return (1); | return (1); | ||||
} | |||||
} | } | ||||
if (i2c_opt.verbose) | if (i2c_opt.verbose) | ||||
fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ? | fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ? | ||||
"read" : "written"); | "read" : "written"); | ||||
i = 0; | i = 0; | ||||
j = 0; | j = 0; | ||||
Show All 21 Lines |