1/* i2ctools.c - i2c tools 2 * 3 * Copyright 2018 The Android Open Source Project 4 * 5 * https://www.kernel.org/doc/Documentation/i2c/ 6 * 7 * Note: -y must have the same value in each toy for `confirm`. 8 * 9 * TODO: i2cdetect -q/-r and the "auto" mode? 10 * TODO: i2cdump non-byte modes, -r FIRST-LAST? 11 * TODO: i2cget non-byte modes? default to current read address? 12 * TODO: i2cset -r? -m MASK? c/s modes, p mode modifier? 13 14USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_BIN)) 15USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_BIN)) 16USE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_BIN)) 17USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_BIN)) 18 19config I2CDETECT 20 bool "i2cdetect" 21 default y 22 help 23 usage: i2cdetect [-aqry] BUS [FIRST LAST] 24 usage: i2cdetect -F BUS 25 usage: i2cdetect -l 26 27 Detect i2c devices. 28 29 -a All addresses (0x00-0x7f rather than 0x03-0x77 or FIRST-LAST) 30 -F Show functionality 31 -l List available buses 32 -q Probe with SMBus Quick Write (default) 33 -r Probe with SMBus Read Byte 34 -y Answer "yes" to confirmation prompts (for script use) 35 36config I2CDUMP 37 bool "i2cdump" 38 default y 39 help 40 usage: i2cdump [-fy] BUS CHIP 41 42 Dump i2c registers. 43 44 -f Force access to busy devices 45 -y Answer "yes" to confirmation prompts (for script use) 46 47config I2CGET 48 bool "i2cget" 49 default y 50 help 51 usage: i2cget [-fy] BUS CHIP ADDR 52 53 Read an i2c register. 54 55 -f Force access to busy devices 56 -y Answer "yes" to confirmation prompts (for script use) 57 58config I2CSET 59 bool "i2cset" 60 default y 61 help 62 usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE 63 64 Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block. 65 66 -f Force access to busy devices 67 -y Answer "yes" to confirmation prompts (for script use) 68*/ 69 70#define FOR_i2cdetect 71#define TT this.i2ctools 72#include "toys.h" 73 74#include <linux/i2c.h> 75#include <linux/i2c-dev.h> 76 77printf_format static void confirm(const char *fmt, ...) 78{ 79 va_list va; 80 81 if (FLAG(y)) return; 82 83 va_start(va, fmt); 84 vfprintf(stderr, fmt, va); 85 va_end(va); 86 if (!yesno(1)) error_exit("Exiting"); 87} 88 89static int i2c_open(int bus, int slave, int chip) 90{ 91 int fd; 92 93 snprintf(toybuf, sizeof(toybuf), "/dev/i2c-%d", bus); 94 fd = xopen(toybuf, O_RDONLY); 95 if (slave) xioctl(fd, slave, (void *)(long)chip); 96 return fd; 97} 98 99static unsigned long i2c_get_funcs(int bus) 100{ 101 int fd = i2c_open(bus, 0, 0); 102 unsigned long result; 103 104 xioctl(fd, I2C_FUNCS, &result); 105 close(fd); 106 return result; 107} 108 109static int i2c_read_byte(int fd, int addr, int *byte) 110{ 111 union i2c_smbus_data data; 112 struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_READ, 113 .size = I2C_SMBUS_BYTE_DATA, .command = addr, .data = &data }; 114 115 memset(&data, 0, sizeof(data)); 116 if (ioctl(fd, I2C_SMBUS, &ioctl_data)==-1) return -1; 117 *byte = data.byte; 118 return 0; 119} 120 121static int i2c_quick_write(int fd, int addr) 122{ 123 struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_QUICK, 124 .size = 0, .command = addr }; 125 126 return ioctl(fd, I2C_SMBUS, &ioctl_data); 127} 128 129static void i2cdetect_dash_F(int bus) 130{ 131 struct { int mask; const char *name; } funcs[] = { 132 {I2C_FUNC_I2C, "I2C"}, 133 {I2C_FUNC_SMBUS_QUICK, "SMBus Quick Command"}, 134 {I2C_FUNC_SMBUS_WRITE_BYTE, "SMBus Send Byte"}, 135 {I2C_FUNC_SMBUS_READ_BYTE, "SMBus Receive Byte"}, 136 {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "SMBus Write Byte"}, 137 {I2C_FUNC_SMBUS_READ_BYTE_DATA, "SMBus Read Byte"}, 138 {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "SMBus Write Word"}, 139 {I2C_FUNC_SMBUS_READ_WORD_DATA, "SMBus Read Word"}, 140 {I2C_FUNC_SMBUS_PROC_CALL, "SMBus Process Call"}, 141 {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "SMBus Write Block"}, 142 {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "SMBus Read Block"}, 143 {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "SMBus Block Process Call"}, 144 {I2C_FUNC_SMBUS_PEC, "SMBus PEC"}, 145 {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C Write Block"}, 146 {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C Read Block"}, 147 }; 148 unsigned long sup = i2c_get_funcs(bus); 149 int i; 150 151 printf("Functionalities implemented by %s:\n", toybuf); 152 for (i = 0; i < ARRAY_LEN(funcs); ++i) 153 printf("%-32s %s\n", funcs[i].name, (sup & funcs[i].mask) ? "yes" : "no"); 154} 155 156static int i2cdetect_dash_l(struct dirtree *node) 157{ 158 int suffix_len = strlen("/name"); 159 int bus; 160 char *fname, *p; 161 unsigned long funcs; 162 163 if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself. 164 165 if (sscanf(node->name, "i2c-%d", &bus)!=1) return 0; 166 funcs = i2c_get_funcs(bus); 167 168 fname = dirtree_path(node, &suffix_len); 169 strcat(fname, "/name"); 170 xreadfile(fname, toybuf, sizeof(toybuf)); 171 free(fname); 172 if ((p = strchr(toybuf, '\n'))) *p = 0; 173 174 // "i2c-1 i2c Synopsys DesignWare I2C adapter I2C adapter" 175 printf("%s\t%-10s\t%-32s\t%s\n", node->name, 176 (funcs & I2C_FUNC_I2C) ? "i2c" : "?", toybuf, 177 (funcs & I2C_FUNC_I2C) ? "I2C Adapter" : "?"); 178 179 return 0; 180} 181 182void i2cdetect_main(void) 183{ 184 if (FLAG(l)) { 185 if (toys.optc) error_exit("-l doesn't take arguments"); 186 dirtree_flagread("/sys/class/i2c-dev", DIRTREE_SHUTUP, i2cdetect_dash_l); 187 } else if (FLAG(F)) { 188 if (toys.optc != 1) error_exit("-F BUS"); 189 i2cdetect_dash_F(atolx_range(*toys.optargs, 0, INT_MAX)); 190 } else { 191 int bus, first = 0x03, last = 0x77, fd, row, addr, byte; 192 193 if (FLAG(a)) { 194 first = 0x00; 195 last = 0x7f; 196 } 197 198 if (toys.optc!=1 && toys.optc!=3) help_exit("Needs 1 or 3 arguments"); 199 bus = atolx_range(*toys.optargs, 0, INT_MAX); 200 if (toys.optc==3) { 201 first = atolx_range(toys.optargs[1], 0, 0x7f); 202 last = atolx_range(toys.optargs[2], 0, 0x7f); 203 if (first > last) error_exit("first > last"); 204 } 205 206 confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus); 207 208 fd = i2c_open(bus, 0, 0); 209 printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); 210 for (row = 0; row <= 0x70; row += 16) { 211 xprintf("%02x:", row & 0xf0); 212 for (addr = row; addr<row+16; ++addr) { 213 if (addr<first || addr>last) printf(" "); 214 else { 215 if (ioctl(fd, I2C_SLAVE, addr) == -1) { 216 if (errno == EBUSY) { 217 xprintf(" UU"); 218 continue; 219 } 220 perror_exit("ioctl(I2C_SLAVE)"); 221 } 222 if ((FLAG(r) ? i2c_read_byte(fd, addr, &byte) 223 : i2c_quick_write(fd, addr)) == -1) xprintf(" --"); 224 else xprintf(" %02x", addr); 225 } 226 } 227 putchar('\n'); 228 } 229 close(fd); 230 } 231} 232 233#define CLEANUP_i2cdetect 234#define FOR_i2cdump 235#include "generated/flags.h" 236 237void i2cdump_main(void) 238{ 239 int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 240 int chip = atolx_range(toys.optargs[1], 0, 0x7f); 241 int fd, row, addr, byte; 242 243 confirm("Dump chip 0x%02x on bus %d?", chip, bus); 244 245 fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 246 printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef\n"); 247 for (row = 0; row<=0xf0; row += 16) { 248 xprintf("%02x:", row & 0xf0); 249 for (addr = row; addr<row+16; ++addr) { 250 if (!i2c_read_byte(fd, addr, &byte)) printf(" %02x", byte); 251 else { 252 printf(" XX"); 253 byte = 'X'; 254 } 255 toybuf[addr-row] = isprint(byte) ? byte : (byte ? '?' : '.'); 256 } 257 printf(" %16.16s\n", toybuf); 258 } 259 close(fd); 260} 261 262#define CLEANUP_i2cdump 263#define FOR_i2cget 264#include "generated/flags.h" 265 266void i2cget_main(void) 267{ 268 int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 269 int chip = atolx_range(toys.optargs[1], 0, 0x7f); 270 int addr = atolx_range(toys.optargs[2], 0, 0xff); 271 int fd, byte; 272 273 confirm("Read register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus); 274 275 fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 276 if (i2c_read_byte(fd, addr, &byte)==-1) perror_exit("i2c_read_byte"); 277 printf("0x%02x\n", byte); 278 close(fd); 279} 280 281#define CLEANUP_i2cget 282#define FOR_i2cset 283#include "generated/flags.h" 284 285void i2cset_main(void) 286{ 287 int bus = atolx_range(toys.optargs[0], 0, INT_MAX); 288 int chip = atolx_range(toys.optargs[1], 0, 0x7f); 289 int addr = atolx_range(toys.optargs[2], 0, 0xff); 290 char *mode = toys.optargs[toys.optc-1]; 291 int fd, i; 292 struct i2c_smbus_ioctl_data ioctl_data; 293 union i2c_smbus_data data; 294 295 memset(&data, 0, sizeof(data)); 296 if (strlen(mode)!=1) help_exit("mode too long"); 297 if (*mode=='b' && toys.optc==5) { 298 ioctl_data.size = I2C_SMBUS_BYTE_DATA; 299 data.byte = atolx_range(toys.optargs[3], 0, 0xff); 300 } else if (*mode=='w' && toys.optc==5) { 301 ioctl_data.size = I2C_SMBUS_WORD_DATA; 302 data.word = atolx_range(toys.optargs[3], 0, 0xffff); 303 } else if (*mode=='i' && toys.optc>=5) { 304 if (toys.optc-4>I2C_SMBUS_BLOCK_MAX) error_exit("too much data"); 305 ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA; 306 for (i = 0; i<toys.optc-4; ++i) 307 data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff); 308 data.block[0] = toys.optc-4; 309 } else help_exit("syntax error"); 310 311 confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus); 312 313 fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip); 314 ioctl_data.read_write = I2C_SMBUS_WRITE; 315 ioctl_data.command = addr; 316 ioctl_data.data = &data; 317 xioctl(fd, I2C_SMBUS, &ioctl_data); 318 close(fd); 319} 320