"Fossies" - the Fresh Open Source Software Archive

Member "i2c-tools-4.3/tools/i2cdump.c" (22 Jul 2021, 12212 Bytes) of package /linux/misc/i2c-tools-4.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "i2cdump.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.2_vs_4.3.

    1 /*
    2     i2cdump.c - a user-space program to dump I2C registers
    3     Copyright (C) 2002-2003  Frodo Looijaard <frodol@dds.nl>, and
    4                              Mark D. Studebaker <mdsxyz123@yahoo.com>
    5     Copyright (C) 2004-2021  Jean Delvare <jdelvare@suse.de>
    6 
    7     This program is free software; you can redistribute it and/or modify
    8     it under the terms of the GNU General Public License as published by
    9     the Free Software Foundation; either version 2 of the License, or
   10     (at your option) any later version.
   11 
   12     This program is distributed in the hope that it will be useful,
   13     but WITHOUT ANY WARRANTY; without even the implied warranty of
   14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15     GNU General Public License for more details.
   16 
   17     You should have received a copy of the GNU General Public License
   18     along with this program; if not, write to the Free Software
   19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   20     MA 02110-1301 USA.
   21 */
   22 
   23 #include <sys/ioctl.h>
   24 #include <errno.h>
   25 #include <string.h>
   26 #include <stdio.h>
   27 #include <stdlib.h>
   28 #include <unistd.h>
   29 #include <linux/i2c.h>
   30 #include <linux/i2c-dev.h>
   31 #include <i2c/smbus.h>
   32 #include "i2cbusses.h"
   33 #include "util.h"
   34 #include "../version.h"
   35 
   36 static void help(void)
   37 {
   38     fprintf(stderr,
   39         "Usage: i2cdump [-f] [-y] [-r first-last] [-a] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]\n"
   40         "  I2CBUS is an integer or an I2C bus name\n"
   41         "  ADDRESS is an integer (0x08 - 0x77, or 0x00 - 0x7f if -a is given)\n"
   42         "  MODE is one of:\n"
   43         "    b (byte, default)\n"
   44         "    w (word)\n"
   45         "    W (word on even register addresses)\n"
   46         "    s (SMBus block, deprecated)\n"
   47         "    i (I2C block)\n"
   48         "    c (consecutive byte)\n"
   49         "    Append p for SMBus PEC\n");
   50 }
   51 
   52 static int check_funcs(int file, int size, int pec)
   53 {
   54     unsigned long funcs;
   55 
   56     /* check adapter functionality */
   57     if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
   58         fprintf(stderr, "Error: Could not get the adapter "
   59             "functionality matrix: %s\n", strerror(errno));
   60         return -1;
   61     }
   62 
   63     switch(size) {
   64     case I2C_SMBUS_BYTE:
   65         if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
   66             fprintf(stderr, MISSING_FUNC_FMT, "SMBus receive byte");
   67             return -1;
   68         }
   69         if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) {
   70             fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte");
   71             return -1;
   72         }
   73         break;
   74 
   75     case I2C_SMBUS_BYTE_DATA:
   76         if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
   77             fprintf(stderr, MISSING_FUNC_FMT, "SMBus read byte");
   78             return -1;
   79         }
   80         break;
   81 
   82     case I2C_SMBUS_WORD_DATA:
   83         if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) {
   84             fprintf(stderr, MISSING_FUNC_FMT, "SMBus read word");
   85             return -1;
   86         }
   87         break;
   88 
   89     case I2C_SMBUS_BLOCK_DATA:
   90         if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
   91             fprintf(stderr, MISSING_FUNC_FMT, "SMBus block read");
   92             return -1;
   93         }
   94         break;
   95 
   96     case I2C_SMBUS_I2C_BLOCK_DATA:
   97         if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
   98             fprintf(stderr, MISSING_FUNC_FMT, "I2C block read");
   99             return -1;
  100         }
  101         break;
  102     }
  103 
  104     if (pec
  105      && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) {
  106         fprintf(stderr, "Warning: Adapter does "
  107             "not seem to support PEC\n");
  108     }
  109 
  110     return 0;
  111 }
  112 
  113 int main(int argc, char *argv[])
  114 {
  115     char *end;
  116     int i, j, res, i2cbus, address, size, file;
  117     int bank = 0, bankreg = 0x4E, old_bank = 0;
  118     char filename[20];
  119     int block[256], s_length = 0;
  120     int pec = 0, even = 0;
  121     int flags = 0;
  122     int force = 0, yes = 0, version = 0, all_addrs = 0;
  123     const char *range = NULL;
  124     int first = 0x00, last = 0xff;
  125 
  126     /* handle (optional) flags first */
  127     while (1+flags < argc && argv[1+flags][0] == '-') {
  128         switch (argv[1+flags][1]) {
  129         case 'V': version = 1; break;
  130         case 'f': force = 1; break;
  131         case 'r': range = argv[1+(++flags)]; break;
  132         case 'y': yes = 1; break;
  133         case 'a': all_addrs = 1; break;
  134         default:
  135             fprintf(stderr, "Error: Unsupported option "
  136                 "\"%s\"!\n", argv[1+flags]);
  137             help();
  138             exit(1);
  139         }
  140         flags++;
  141     }
  142 
  143     if (version) {
  144         fprintf(stderr, "i2cdump version %s\n", VERSION);
  145         exit(0);
  146     }
  147 
  148     if (argc < flags + 2) {
  149         fprintf(stderr, "Error: No i2c-bus specified!\n");
  150         help();
  151         exit(1);
  152     }
  153     i2cbus = lookup_i2c_bus(argv[flags+1]);
  154     if (i2cbus < 0) {
  155         help();
  156         exit(1);
  157     }
  158 
  159     if (argc < flags + 3) {
  160         fprintf(stderr, "Error: No address specified!\n");
  161         help();
  162         exit(1);
  163     }
  164     address = parse_i2c_address(argv[flags+2], all_addrs);
  165     if (address < 0) {
  166         help();
  167         exit(1);
  168     }
  169 
  170     if (argc < flags + 4) {
  171         fprintf(stderr, "No size specified (using byte-data access)\n");
  172         size = I2C_SMBUS_BYTE_DATA;
  173     } else if (!strncmp(argv[flags+3], "b", 1)) {
  174         size = I2C_SMBUS_BYTE_DATA;
  175         pec = argv[flags+3][1] == 'p';
  176     } else if (!strncmp(argv[flags+3], "w", 1)) {
  177         size = I2C_SMBUS_WORD_DATA;
  178         pec = argv[flags+3][1] == 'p';
  179     } else if (!strncmp(argv[flags+3], "W", 1)) {
  180         size = I2C_SMBUS_WORD_DATA;
  181         even = 1;
  182     } else if (!strncmp(argv[flags+3], "s", 1)) {
  183         size = I2C_SMBUS_BLOCK_DATA;
  184         fprintf(stderr,
  185             "SMBus block mode is deprecated, please use i2cget instead\n");
  186         pec = argv[flags+3][1] == 'p';
  187     } else if (!strncmp(argv[flags+3], "c", 1)) {
  188         size = I2C_SMBUS_BYTE;
  189         pec = argv[flags+3][1] == 'p';
  190     } else if (!strcmp(argv[flags+3], "i"))
  191         size = I2C_SMBUS_I2C_BLOCK_DATA;
  192     else {
  193         fprintf(stderr, "Error: Invalid mode!\n");
  194         help();
  195         exit(1);
  196     }
  197 
  198     if (argc > flags + 4) {
  199         bank = strtol(argv[flags+4], &end, 0);
  200         if (*end || size == I2C_SMBUS_I2C_BLOCK_DATA) {
  201             fprintf(stderr, "Error: Invalid bank number!\n");
  202             help();
  203             exit(1);
  204         }
  205         if ((size == I2C_SMBUS_BYTE_DATA || size == I2C_SMBUS_WORD_DATA)
  206          && (bank < 0 || bank > 15)) {
  207             fprintf(stderr, "Error: bank out of range!\n");
  208             help();
  209             exit(1);
  210         }
  211         if (size == I2C_SMBUS_BLOCK_DATA
  212          && (bank < 0 || bank > 0xff)) {
  213             fprintf(stderr, "Error: block command out of range!\n");
  214             help();
  215             exit(1);
  216         }
  217 
  218         if (argc > flags + 5) {
  219             bankreg = strtol(argv[flags+5], &end, 0);
  220             if (*end || size == I2C_SMBUS_BLOCK_DATA) {
  221                 fprintf(stderr, "Error: Invalid bank register "
  222                     "number!\n");
  223                 help();
  224                 exit(1);
  225             }
  226             if (bankreg < 0 || bankreg > 0xff) {
  227                 fprintf(stderr, "Error: bank out of range "
  228                     "(0-0xff)!\n");
  229                 help();
  230                 exit(1);
  231             }
  232         }
  233     }
  234 
  235     /* Parse optional range string */
  236     if (range) {
  237         char *dash;
  238 
  239         first = strtol(range, &dash, 0);
  240         if (dash == range || *dash != '-'
  241          || first < 0 || first > 0xff) {
  242             fprintf(stderr, "Error: Invalid range parameter!\n");
  243             exit(1);
  244         }
  245         last = strtol(++dash, &end, 0);
  246         if (end == dash || *end != '\0'
  247          || last < first || last > 0xff) {
  248             fprintf(stderr, "Error: Invalid range parameter!\n");
  249             exit(1);
  250         }
  251 
  252         /* Check mode constraints */
  253         switch (size) {
  254         case I2C_SMBUS_BYTE:
  255         case I2C_SMBUS_BYTE_DATA:
  256         case I2C_SMBUS_I2C_BLOCK_DATA:
  257             break;
  258         case I2C_SMBUS_WORD_DATA:
  259             if (!even || (!(first%2) && last%2))
  260                 break;
  261             /* Fall through */
  262         default:
  263             fprintf(stderr,
  264                 "Error: Range parameter not compatible with selected mode!\n");
  265             exit(1);
  266         }
  267     }
  268 
  269     file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
  270     if (file < 0
  271      || check_funcs(file, size, pec)
  272      || set_slave_addr(file, address, force))
  273         exit(1);
  274 
  275     if (pec) {
  276         if (ioctl(file, I2C_PEC, 1) < 0) {
  277             fprintf(stderr, "Error: Could not set PEC: %s\n",
  278                 strerror(errno));
  279             exit(1);
  280         }
  281     }
  282 
  283     if (!yes) {
  284         fprintf(stderr, "WARNING! This program can confuse your I2C "
  285             "bus, cause data loss and worse!\n");
  286 
  287         fprintf(stderr, "I will probe file %s, address 0x%x, mode "
  288             "%s\n", filename, address,
  289             size == I2C_SMBUS_BLOCK_DATA ? "smbus block" :
  290             size == I2C_SMBUS_I2C_BLOCK_DATA ? "i2c block" :
  291             size == I2C_SMBUS_BYTE ? "byte consecutive read" :
  292             size == I2C_SMBUS_BYTE_DATA ? "byte" : "word");
  293         if (pec)
  294             fprintf(stderr, "PEC checking enabled.\n");
  295         if (even)
  296             fprintf(stderr, "Only probing even register "
  297                 "addresses.\n");
  298         if (bank) {
  299             if (size == I2C_SMBUS_BLOCK_DATA)
  300                 fprintf(stderr, "Using command 0x%02x.\n",
  301                     bank);
  302             else
  303                 fprintf(stderr, "Probing bank %d using bank "
  304                     "register 0x%02x.\n", bank, bankreg);
  305         }
  306         if (range) {
  307             fprintf(stderr,
  308                 "Probe range limited to 0x%02x-0x%02x.\n",
  309                 first, last);
  310         }
  311 
  312         fprintf(stderr, "Continue? [Y/n] ");
  313         fflush(stderr);
  314         if (!user_ack(1)) {
  315             fprintf(stderr, "Aborting on user request.\n");
  316             exit(0);
  317         }
  318     }
  319 
  320     /* See Winbond w83781d data sheet for bank details */
  321     if (bank && size != I2C_SMBUS_BLOCK_DATA) {
  322         res = i2c_smbus_read_byte_data(file, bankreg);
  323         if (res >= 0) {
  324             old_bank = res;
  325             res = i2c_smbus_write_byte_data(file, bankreg,
  326                 bank | (old_bank & 0xf0));
  327         }
  328         if (res < 0) {
  329             fprintf(stderr, "Error: Bank switching failed\n");
  330             exit(1);
  331         }
  332     }
  333 
  334     /* handle all but word data */
  335     if (size != I2C_SMBUS_WORD_DATA || even) {
  336         /* do the block transaction */
  337         if (size == I2C_SMBUS_BLOCK_DATA
  338          || size == I2C_SMBUS_I2C_BLOCK_DATA) {
  339             unsigned char cblock[288];
  340 
  341             if (size == I2C_SMBUS_BLOCK_DATA) {
  342                 res = i2c_smbus_read_block_data(file, bank,
  343                       cblock);
  344                 /* Remember returned block length for a nicer
  345                    display later */
  346                 s_length = res;
  347                 last = res - 1;
  348             } else {
  349                 for (res = first; res <= last; res += i) {
  350                     i = i2c_smbus_read_i2c_block_data(file,
  351                         res, 32, cblock + res);
  352                     if (i <= 0) {
  353                         res = i;
  354                         break;
  355                     }
  356                 }
  357             }
  358             if (res <= 0) {
  359                 fprintf(stderr, "Error: Block read failed, "
  360                     "return code %d\n", res);
  361                 exit(1);
  362             }
  363             for (i = first; i <= last; i++)
  364                 block[i] = cblock[i];
  365         }
  366 
  367         if (size == I2C_SMBUS_BYTE) {
  368             res = i2c_smbus_write_byte(file, first);
  369             if(res != 0) {
  370                 fprintf(stderr, "Error: Write start address "
  371                     "failed, return code %d\n", res);
  372                 exit(1);
  373             }
  374         }
  375 
  376         printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f"
  377                "    0123456789abcdef\n");
  378         for (i = 0; i < 256; i+=16) {
  379             if (size == I2C_SMBUS_BLOCK_DATA && i >= s_length)
  380                 break;
  381             if (i/16 < first/16)
  382                 continue;
  383             if (i/16 > last/16)
  384                 break;
  385 
  386             printf("%02x: ", i);
  387             for (j = 0; j < 16; j++) {
  388                 fflush(stdout);
  389                 /* Skip unwanted registers */
  390                 if (i+j < first || i+j > last) {
  391                     printf("   ");
  392                     if (size == I2C_SMBUS_WORD_DATA) {
  393                         printf("   ");
  394                         j++;
  395                     }
  396                     continue;
  397                 }
  398 
  399                 if (size == I2C_SMBUS_BYTE_DATA) {
  400                     block[i+j] = res =
  401                       i2c_smbus_read_byte_data(file, i+j);
  402                 } else if (size == I2C_SMBUS_WORD_DATA) {
  403                     res = i2c_smbus_read_word_data(file,
  404                                        i+j);
  405                     if (res < 0) {
  406                         block[i+j] = res;
  407                         block[i+j+1] = res;
  408                     } else {
  409                         block[i+j] = res & 0xff;
  410                         block[i+j+1] = res >> 8;
  411                     }
  412                 } else if (size == I2C_SMBUS_BYTE) {
  413                     block[i+j] = res =
  414                       i2c_smbus_read_byte(file);
  415                 } else
  416                     res = block[i+j];
  417 
  418                 if (size == I2C_SMBUS_BLOCK_DATA
  419                  && i+j >= s_length) {
  420                     printf("   ");
  421                 } else if (res < 0) {
  422                     printf("XX ");
  423                     if (size == I2C_SMBUS_WORD_DATA)
  424                         printf("XX ");
  425                 } else {
  426                     printf("%02x ", block[i+j]);
  427                     if (size == I2C_SMBUS_WORD_DATA)
  428                         printf("%02x ", block[i+j+1]);
  429                 }
  430                 if (size == I2C_SMBUS_WORD_DATA)
  431                     j++;
  432             }
  433             printf("   ");
  434 
  435             for (j = 0; j < 16; j++) {
  436                 if (size == I2C_SMBUS_BLOCK_DATA
  437                  && i+j >= s_length)
  438                     break;
  439                 /* Skip unwanted registers */
  440                 if (i+j < first || i+j > last) {
  441                     printf(" ");
  442                     continue;
  443                 }
  444 
  445                 res = block[i+j];
  446                 if (res < 0)
  447                     printf("X");
  448                 else
  449                 if ((res & 0xff) == 0x00
  450                  || (res & 0xff) == 0xff)
  451                     printf(".");
  452                 else
  453                 if ((res & 0xff) < 32
  454                  || (res & 0xff) >= 127)
  455                     printf("?");
  456                 else
  457                     printf("%c", res & 0xff);
  458             }
  459             printf("\n");
  460         }
  461     } else {
  462         printf("     0,8  1,9  2,a  3,b  4,c  5,d  6,e  7,f\n");
  463         for (i = 0; i < 256; i+=8) {
  464             if (i/8 < first/8)
  465                 continue;
  466             if (i/8 > last/8)
  467                 break;
  468 
  469             printf("%02x: ", i);
  470             for (j = 0; j < 8; j++) {
  471                 /* Skip unwanted registers */
  472                 if (i+j < first || i+j > last) {
  473                     printf("     ");
  474                     continue;
  475                 }
  476 
  477                 res = i2c_smbus_read_word_data(file, i+j);
  478                 if (res < 0)
  479                     printf("XXXX ");
  480                 else
  481                     printf("%04x ", res & 0xffff);
  482             }
  483             printf("\n");
  484         }
  485     }
  486     if (bank && size != I2C_SMBUS_BLOCK_DATA) {
  487         i2c_smbus_write_byte_data(file, bankreg, old_bank);
  488     }
  489     exit(0);
  490 }