Browse Source
Delete i2ctransfer
Delete i2ctransfer
A maintenance branch isn't the right place for a brand new tool. Now that i2c tools version 4.0 has been released, there's no reason to keep i2ctransfer in the 3.1 branch, so delete it. Signed-off-by: Jean Delvare <jdelvare@suse.de> Acked-by: Wolfram Sang <wsa@the-dreams.de>i2c-tools-3.1
7 changed files with 5 additions and 511 deletions
-
8tools/Module.mk
-
2tools/i2cdetect.8
-
2tools/i2cdump.8
-
2tools/i2cget.8
-
2tools/i2cset.8
-
159tools/i2ctransfer.8
-
341tools/i2ctransfer.c
@ -1,159 +0,0 @@ |
|||
.TH i2ctransfer 8 "February 2017" |
|||
.SH "NAME" |
|||
i2ctransfer \- send user-defined I2C messages in one transfer |
|||
|
|||
.SH SYNOPSIS |
|||
.B i2ctransfer |
|||
.RB [ -f ] |
|||
.RB [ -y ] |
|||
.RB [ -v ] |
|||
.I i2cbus desc |
|||
.RI [ data ] |
|||
.RI [ desc |
|||
.RI [ data ]] |
|||
.RI ... |
|||
.br |
|||
.B i2ctransfer |
|||
.B -V |
|||
|
|||
.SH DESCRIPTION |
|||
.B i2ctransfer |
|||
is a program to create I2C messages and send them combined as one transfer. |
|||
For read messages, the contents of the received buffers are printed to stdout, one line per read message. |
|||
.br |
|||
Please note the difference between a |
|||
.I transfer |
|||
and a |
|||
.I message |
|||
here. |
|||
A transfer may consist of multiple messages and is started with a START condition and ends with a STOP condition as described in the I2C specification. |
|||
Messages within the transfer are concatenated using the REPEATED START condition which is described there as well. |
|||
There are some advantages of having multiple messages in one transfer. |
|||
First, some devices keep their internal states for REPEATED START but reset them after a STOP. |
|||
Second, you cannot get interrupted during one transfer, but it might happen between multiple transfers. |
|||
Interruption could happen on hardware level by another I2C master on the bus, or at software level by another I2C user who got its transfer scheduled between yours. |
|||
This program helps you to create proper transfers for your needs. |
|||
|
|||
.SH OPTIONS |
|||
.TP |
|||
.B -f |
|||
Force access to the device even if it is already busy. |
|||
By default, |
|||
.B i2ctransfer |
|||
will refuse to access addresses marked as reserved by the I2C standard or to 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 |
|||
.B i2ctransfer |
|||
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, |
|||
.B i2ctransfer |
|||
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 -v |
|||
Enable verbose output. |
|||
It will print infos about all messages sent, i.e. not only for read messages but also for write messages. |
|||
.TP |
|||
.B -V |
|||
Display the version and exit. |
|||
|
|||
.SH ARGUMENTS |
|||
.PP |
|||
The first parameter |
|||
.I i2cbus |
|||
indicates the number or name of the I2C bus to be used. |
|||
This number should correspond to one of the busses listed by |
|||
.B i2cdetect -l. |
|||
|
|||
.PP |
|||
The next parameter is one or multiple |
|||
.I desc |
|||
blocks. |
|||
The number of blocks is limited by the Linux Kernel and defined by I2C_RDWR_IOCTL_MAX_MSGS (42 as of v4.10). |
|||
.I desc |
|||
blocks are composed like this: |
|||
|
|||
.I {r|w}<length_of_message>[@address] |
|||
|
|||
.TP |
|||
.B {r|w} |
|||
specifies if the message is read or write |
|||
.TP |
|||
.B <length_of_message> |
|||
specifies the number of bytes read or written in this message. |
|||
It is parsed as an unsigned 16 bit integer, but note that the Linux Kernel applies an additional upper limit (8192 as of v4.10). |
|||
.TP |
|||
.B [@address] |
|||
specifies the 7-bit address of the chip to be accessed for this message, and is an integer. |
|||
If omitted, reuse the previous address. |
|||
Normally, addresses outside the range of 0x03-0x77 and addresses with a kernel driver attached to them will be blocked. |
|||
With |
|||
.I -f |
|||
(force), all addresses can be used. |
|||
Be very careful when using that! |
|||
10-bit addresses are currently not supported at all. |
|||
|
|||
.PP |
|||
If the I2C message is a write, then a |
|||
.I data |
|||
block with the data to be written follows. |
|||
It consists of |
|||
.I <length_of_message> |
|||
bytes which can be marked with the usual prefixes for hexadecimal, octal, etc. |
|||
To make it easier to create larger data blocks easily, the data byte can have a suffix. |
|||
|
|||
.TP |
|||
= |
|||
keep value constant until end of message (i.e. 0= means 0, 0, 0, ...) |
|||
.TP |
|||
+ |
|||
increase value by 1 until end of message (i.e. 0+ means 0, 1, 2, ...) |
|||
.TP |
|||
- |
|||
decrease value by 1 until end of message (i.e. 0xff- means 0xff, 0xfe, 0xfd, ...) |
|||
.TP |
|||
p |
|||
use value as seed for an 8 bit pseudo random sequence (i.e. 0p means 0x00, 0x50, 0xb0, ...) |
|||
|
|||
.SH EXAMPLES |
|||
.PP |
|||
On bus 0, from an EEPROM at address 0x50, read 8 byte from offset 0x64 |
|||
(first message writes one byte to set the memory pointer to 0x64, second message reads from the same chip): |
|||
.nf |
|||
.RS |
|||
# i2ctransfer 0 w1@0x50 0x64 r8 |
|||
.RE |
|||
.fi |
|||
.PP |
|||
For the same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0 |
|||
(one write message; first byte sets the memory pointer to 0x42, 0xff is the first data byte, all following data bytes are decreased by one): |
|||
.nf |
|||
.RS |
|||
# i2ctransfer 0 w17@0x50 0x42 0xff- |
|||
.RE |
|||
.fi |
|||
|
|||
.SH WARNING |
|||
.B i2ctransfer |
|||
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 AUTHORS |
|||
Wolfram Sang, based on |
|||
.B i2cget |
|||
by Jean Delvare |
|||
|
|||
This manual page was originally written by Wolfram Sang based on the manual |
|||
for |
|||
.B i2cset |
|||
by David Z Maze <dmaze@debian.org>. |
|||
|
|||
.SH SEE ALSO |
|||
.BR i2cdetect (8), i2cdump (8), i2cget (8), i2cset (8) |
@ -1,341 +0,0 @@ |
|||
/* |
|||
i2ctransfer.c - A user-space program to send concatenated i2c messages |
|||
Copyright (C) 2015-17 Wolfram Sang <wsa@sang-engineering.com> |
|||
Copyright (C) 2015-17 Renesas Electronics Corporation |
|||
|
|||
Based on i2cget.c: |
|||
Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de> |
|||
|
|||
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. |
|||
*/ |
|||
|
|||
#include <sys/ioctl.h> |
|||
#include <errno.h> |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#include <linux/i2c-dev.h> |
|||
#include "i2cbusses.h" |
|||
#include "util.h" |
|||
#include "../version.h" |
|||
|
|||
enum parse_state { |
|||
PARSE_GET_DESC, |
|||
PARSE_GET_DATA, |
|||
}; |
|||
|
|||
#define PRINT_STDERR (1 << 0) |
|||
#define PRINT_READ_BUF (1 << 1) |
|||
#define PRINT_WRITE_BUF (1 << 2) |
|||
#define PRINT_HEADER (1 << 3) |
|||
|
|||
static void help(void) |
|||
{ |
|||
fprintf(stderr, |
|||
"Usage: i2ctransfer [-f] [-y] [-v] [-V] I2CBUS DESC [DATA] [DESC [DATA]]...\n" |
|||
" I2CBUS is an integer or an I2C bus name\n" |
|||
" DESC describes the transfer in the form: {r|w}LENGTH[@address]\n" |
|||
" 1) read/write-flag 2) LENGTH (range 0-65535) 3) I2C address (use last one if omitted)\n" |
|||
" DATA are LENGTH bytes for a write message. They can be shortened by a suffix:\n" |
|||
" = (keep value constant until LENGTH)\n" |
|||
" + (increase value by 1 until LENGTH)\n" |
|||
" - (decrease value by 1 until LENGTH)\n" |
|||
" p (use pseudo random generator until LENGTH with value as seed)\n\n" |
|||
"Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):\n" |
|||
" # i2ctransfer 0 w1@0x50 0x64 r8\n" |
|||
"Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):\n" |
|||
" # i2ctransfer 0 w17@0x50 0x42 0xff-\n"); |
|||
} |
|||
|
|||
static int check_funcs(int file) |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
if (!(funcs & I2C_FUNC_I2C)) { |
|||
fprintf(stderr, MISSING_FUNC_FMT, "I2C transfers"); |
|||
return -1; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static void print_msgs(struct i2c_msg *msgs, __u32 nmsgs, unsigned flags) |
|||
{ |
|||
FILE *output = flags & PRINT_STDERR ? stderr : stdout; |
|||
unsigned i; |
|||
__u16 j; |
|||
|
|||
for (i = 0; i < nmsgs; i++) { |
|||
int read = msgs[i].flags & I2C_M_RD; |
|||
int print_buf = (read && (flags & PRINT_READ_BUF)) || |
|||
(!read && (flags & PRINT_WRITE_BUF)); |
|||
|
|||
if (flags & PRINT_HEADER) |
|||
fprintf(output, "msg %u: addr 0x%02x, %s, len %u", |
|||
i, msgs[i].addr, read ? "read" : "write", msgs[i].len); |
|||
|
|||
if (msgs[i].len && print_buf) { |
|||
if (flags & PRINT_HEADER) |
|||
fprintf(output, ", buf "); |
|||
for (j = 0; j < msgs[i].len - 1; j++) |
|||
fprintf(output, "0x%02x ", msgs[i].buf[j]); |
|||
/* Print final byte with newline */ |
|||
fprintf(output, "0x%02x\n", msgs[i].buf[j]); |
|||
} else if (flags & PRINT_HEADER) { |
|||
fprintf(output, "\n"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static int confirm(const char *filename, struct i2c_msg *msgs, __u32 nmsgs) |
|||
{ |
|||
fprintf(stderr, "WARNING! This program can confuse your I2C bus, cause data loss and worse!\n"); |
|||
fprintf(stderr, "I will send the following messages to device file %s:\n", filename); |
|||
print_msgs(msgs, nmsgs, PRINT_STDERR | PRINT_HEADER | PRINT_WRITE_BUF); |
|||
|
|||
fprintf(stderr, "Continue? [y/N] "); |
|||
fflush(stderr); |
|||
if (!user_ack(0)) { |
|||
fprintf(stderr, "Aborting on user request.\n"); |
|||
return 0; |
|||
} |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
char filename[20]; |
|||
int i2cbus, address = -1, file, arg_idx = 1, nmsgs = 0, nmsgs_sent, i; |
|||
int force = 0, yes = 0, version = 0, verbose = 0; |
|||
struct i2c_msg msgs[I2C_RDRW_IOCTL_MAX_MSGS]; |
|||
enum parse_state state = PARSE_GET_DESC; |
|||
unsigned buf_idx = 0; |
|||
|
|||
for (i = 0; i < I2C_RDRW_IOCTL_MAX_MSGS; i++) |
|||
msgs[i].buf = NULL; |
|||
|
|||
/* handle (optional) arg_idx first */ |
|||
while (arg_idx < argc && argv[arg_idx][0] == '-') { |
|||
switch (argv[arg_idx][1]) { |
|||
case 'V': version = 1; break; |
|||
case 'v': verbose = 1; break; |
|||
case 'f': force = 1; break; |
|||
case 'y': yes = 1; break; |
|||
default: |
|||
fprintf(stderr, "Error: Unsupported option \"%s\"!\n", |
|||
argv[arg_idx]); |
|||
help(); |
|||
exit(1); |
|||
} |
|||
arg_idx++; |
|||
} |
|||
|
|||
if (version) { |
|||
fprintf(stderr, "i2ctransfer version %s\n", VERSION); |
|||
exit(0); |
|||
} |
|||
|
|||
if (arg_idx == argc) { |
|||
help(); |
|||
exit(1); |
|||
} |
|||
|
|||
i2cbus = lookup_i2c_bus(argv[arg_idx++]); |
|||
if (i2cbus < 0) |
|||
exit(1); |
|||
|
|||
file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0); |
|||
if (file < 0 || check_funcs(file)) |
|||
exit(1); |
|||
|
|||
while (arg_idx < argc) { |
|||
char *arg_ptr = argv[arg_idx]; |
|||
unsigned long len, raw_data; |
|||
__u16 flags; |
|||
__u8 data; |
|||
char *end, *buf; |
|||
|
|||
if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) { |
|||
fprintf(stderr, "Error: Too many messages (max: %d)\n", |
|||
I2C_RDRW_IOCTL_MAX_MSGS); |
|||
goto err_out; |
|||
} |
|||
|
|||
switch (state) { |
|||
case PARSE_GET_DESC: |
|||
flags = 0; |
|||
|
|||
switch (*arg_ptr++) { |
|||
case 'r': flags |= I2C_M_RD; break; |
|||
case 'w': break; |
|||
default: |
|||
fprintf(stderr, "Error: Invalid direction\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
|
|||
len = strtoul(arg_ptr, &end, 0); |
|||
if (len > 0xffff || arg_ptr == end) { |
|||
fprintf(stderr, "Error: Length invalid\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
|
|||
arg_ptr = end; |
|||
if (*arg_ptr) { |
|||
if (*arg_ptr++ != '@') { |
|||
fprintf(stderr, "Error: Unknown separator after length\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
|
|||
/* We skip 10-bit support for now. If we want it, |
|||
* it should be marked with a 't' flag before |
|||
* the address here. |
|||
*/ |
|||
|
|||
if (!force) { |
|||
address = parse_i2c_address(arg_ptr); |
|||
if (address < 0) |
|||
goto err_out_with_arg; |
|||
|
|||
/* Ensure address is not busy */ |
|||
if (set_slave_addr(file, address, 0)) |
|||
goto err_out_with_arg; |
|||
} else { |
|||
/* 'force' allows whole address range */ |
|||
address = strtol(arg_ptr, &end, 0); |
|||
if (arg_ptr == end || *end || address > 0x7f) { |
|||
fprintf(stderr, "Error: Invalid chip address\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
} |
|||
} else { |
|||
/* Reuse last address if possible */ |
|||
if (address < 0) { |
|||
fprintf(stderr, "Error: No address given\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
} |
|||
|
|||
msgs[nmsgs].addr = address; |
|||
msgs[nmsgs].flags = flags; |
|||
msgs[nmsgs].len = len; |
|||
|
|||
if (len) { |
|||
buf = malloc(len); |
|||
if (!buf) { |
|||
fprintf(stderr, "Error: No memory for buffer\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
memset(buf, 0, len); |
|||
msgs[nmsgs].buf = buf; |
|||
} |
|||
|
|||
if (flags & I2C_M_RD || len == 0) { |
|||
nmsgs++; |
|||
} else { |
|||
buf_idx = 0; |
|||
state = PARSE_GET_DATA; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case PARSE_GET_DATA: |
|||
raw_data = strtoul(arg_ptr, &end, 0); |
|||
if (raw_data > 0xff || arg_ptr == end) { |
|||
fprintf(stderr, "Error: Invalid data byte\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
data = raw_data; |
|||
len = msgs[nmsgs].len; |
|||
|
|||
while (buf_idx < len) { |
|||
msgs[nmsgs].buf[buf_idx++] = data; |
|||
|
|||
if (!*end) |
|||
break; |
|||
|
|||
switch (*end) { |
|||
/* Pseudo randomness (8 bit AXR with a=13 and b=27) */ |
|||
case 'p': |
|||
data = (data ^ 27) + 13; |
|||
data = (data << 1) | (data >> 7); |
|||
break; |
|||
case '+': data++; break; |
|||
case '-': data--; break; |
|||
case '=': break; |
|||
default: |
|||
fprintf(stderr, "Error: Invalid data byte suffix\n"); |
|||
goto err_out_with_arg; |
|||
} |
|||
} |
|||
|
|||
if (buf_idx == len) { |
|||
nmsgs++; |
|||
state = PARSE_GET_DESC; |
|||
} |
|||
|
|||
break; |
|||
|
|||
default: |
|||
/* Should never happen */ |
|||
fprintf(stderr, "Internal Error: Unknown state in state machine!\n"); |
|||
goto err_out; |
|||
} |
|||
|
|||
arg_idx++; |
|||
} |
|||
|
|||
if (state != PARSE_GET_DESC || nmsgs == 0) { |
|||
fprintf(stderr, "Error: Incomplete message\n"); |
|||
goto err_out; |
|||
} |
|||
|
|||
if (yes || confirm(filename, msgs, nmsgs)) { |
|||
struct i2c_rdwr_ioctl_data rdwr; |
|||
|
|||
rdwr.msgs = msgs; |
|||
rdwr.nmsgs = nmsgs; |
|||
nmsgs_sent = ioctl(file, I2C_RDWR, &rdwr); |
|||
if (nmsgs_sent < 0) { |
|||
fprintf(stderr, "Error: Sending messages failed: %s\n", strerror(errno)); |
|||
goto err_out; |
|||
} else if (nmsgs_sent < nmsgs) { |
|||
fprintf(stderr, "Warning: only %d/%d messages were sent\n", nmsgs_sent, nmsgs); |
|||
} |
|||
|
|||
print_msgs(msgs, nmsgs_sent, PRINT_READ_BUF | (verbose ? PRINT_HEADER | PRINT_WRITE_BUF : 0)); |
|||
} |
|||
|
|||
close(file); |
|||
|
|||
for (i = 0; i < nmsgs; i++) |
|||
free(msgs[i].buf); |
|||
|
|||
exit(0); |
|||
|
|||
err_out_with_arg: |
|||
fprintf(stderr, "Error: faulty argument is '%s'\n", argv[arg_idx]); |
|||
err_out: |
|||
close(file); |
|||
|
|||
for (i = 0; i <= nmsgs; i++) |
|||
free(msgs[i].buf); |
|||
|
|||
exit(1); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue