"Fossies" - the Fresh Open Source Software Archive

Member "i2c-tools-4.3/tools/i2cbusses.c" (22 Jul 2021, 11358 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 "i2cbusses.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     i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
    3                Part of user-space programs to access for I2C
    4                devices.
    5     Copyright (c) 1999-2003  Frodo Looijaard <frodol@dds.nl> and
    6                              Mark D. Studebaker <mdsxyz123@yahoo.com>
    7     Copyright (C) 2008-2012  Jean Delvare <jdelvare@suse.de>
    8 
    9     This program is free software; you can redistribute it and/or modify
   10     it under the terms of the GNU General Public License as published by
   11     the Free Software Foundation; either version 2 of the License, or
   12     (at your option) any later version.
   13 
   14     This program is distributed in the hope that it will be useful,
   15     but WITHOUT ANY WARRANTY; without even the implied warranty of
   16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17     GNU General Public License for more details.
   18 
   19     You should have received a copy of the GNU General Public License
   20     along with this program; if not, write to the Free Software
   21     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   22     MA 02110-1301 USA.
   23 */
   24 
   25 /* For strdup and snprintf */
   26 #define _BSD_SOURCE 1 /* for glibc <= 2.19 */
   27 #define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
   28 
   29 #include <sys/types.h>
   30 #include <sys/stat.h>
   31 #include <sys/param.h>  /* for NAME_MAX */
   32 #include <sys/ioctl.h>
   33 #include <string.h>
   34 #include <strings.h>    /* for strcasecmp() */
   35 #include <stdio.h>
   36 #include <stdlib.h>
   37 #include <unistd.h>
   38 #include <limits.h>
   39 #include <dirent.h>
   40 #include <fcntl.h>
   41 #include <errno.h>
   42 #include "i2cbusses.h"
   43 #include <linux/i2c.h>
   44 #include <linux/i2c-dev.h>
   45 
   46 enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
   47 
   48 struct adap_type {
   49     const char *funcs;
   50     const char* algo;
   51 };
   52 
   53 static struct adap_type adap_types[5] = {
   54     { .funcs    = "dummy",
   55       .algo     = "Dummy bus", },
   56     { .funcs    = "isa",
   57       .algo     = "ISA bus", },
   58     { .funcs    = "i2c",
   59       .algo     = "I2C adapter", },
   60     { .funcs    = "smbus",
   61       .algo     = "SMBus adapter", },
   62     { .funcs    = "unknown",
   63       .algo     = "N/A", },
   64 };
   65 
   66 static enum adt i2c_get_funcs(int i2cbus)
   67 {
   68     unsigned long funcs;
   69     int file;
   70     char filename[20];
   71     enum adt ret;
   72 
   73     file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
   74     if (file < 0)
   75         return adt_unknown;
   76 
   77     if (ioctl(file, I2C_FUNCS, &funcs) < 0)
   78         ret = adt_unknown;
   79     else if (funcs & I2C_FUNC_I2C)
   80         ret = adt_i2c;
   81     else if (funcs & (I2C_FUNC_SMBUS_BYTE |
   82               I2C_FUNC_SMBUS_BYTE_DATA |
   83               I2C_FUNC_SMBUS_WORD_DATA))
   84         ret = adt_smbus;
   85     else
   86         ret = adt_dummy;
   87 
   88     close(file);
   89     return ret;
   90 }
   91 
   92 /* Remove trailing spaces from a string
   93    Return the new string length including the trailing NUL */
   94 static int rtrim(char *s)
   95 {
   96     int i;
   97 
   98     for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
   99         s[i] = '\0';
  100     return i + 2;
  101 }
  102 
  103 void free_adapters(struct i2c_adap *adapters)
  104 {
  105     int i;
  106 
  107     for (i = 0; adapters[i].name; i++)
  108         free(adapters[i].name);
  109     free(adapters);
  110 }
  111 
  112 /* We allocate space for the adapters in bunches. The last item is a
  113    terminator, so here we start with room for 7 adapters, which should
  114    be enough in most cases. If not, we allocate more later as needed. */
  115 #define BUNCH   8
  116 
  117 /* n must match the size of adapters at calling time */
  118 static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
  119 {
  120     struct i2c_adap *new_adapters;
  121 
  122     new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
  123     if (!new_adapters) {
  124         free_adapters(adapters);
  125         return NULL;
  126     }
  127     memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
  128 
  129     return new_adapters;
  130 }
  131 
  132 static int sort_i2c_busses(const void *a, const void *b)
  133 {
  134     const struct i2c_adap *adap1 = a;
  135     const struct i2c_adap *adap2 = b;
  136 
  137     return adap1->nr - adap2->nr;
  138 }
  139 
  140 struct i2c_adap *gather_i2c_busses(void)
  141 {
  142     char s[120];
  143     struct dirent *de, *dde;
  144     DIR *dir, *ddir;
  145     FILE *f;
  146     char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
  147     int foundsysfs = 0;
  148     int len, count = 0;
  149     struct i2c_adap *adapters;
  150 
  151     adapters = calloc(BUNCH, sizeof(struct i2c_adap));
  152     if (!adapters)
  153         return NULL;
  154 
  155     /* look in /proc/bus/i2c */
  156     if ((f = fopen("/proc/bus/i2c", "r"))) {
  157         while (fgets(s, 120, f)) {
  158             char *algo, *name, *type, *all;
  159             int len_algo, len_name, len_type;
  160             int i2cbus;
  161 
  162             algo = strrchr(s, '\t');
  163             *(algo++) = '\0';
  164             len_algo = rtrim(algo);
  165 
  166             name = strrchr(s, '\t');
  167             *(name++) = '\0';
  168             len_name = rtrim(name);
  169 
  170             type = strrchr(s, '\t');
  171             *(type++) = '\0';
  172             len_type = rtrim(type);
  173 
  174             sscanf(s, "i2c-%d", &i2cbus);
  175 
  176             if ((count + 1) % BUNCH == 0) {
  177                 /* We need more space */
  178                 adapters = more_adapters(adapters, count + 1);
  179                 if (!adapters)
  180                     return NULL;
  181             }
  182 
  183             all = malloc(len_name + len_type + len_algo);
  184             if (all == NULL) {
  185                 free_adapters(adapters);
  186                 return NULL;
  187             }
  188             adapters[count].nr = i2cbus;
  189             adapters[count].name = strcpy(all, name);
  190             adapters[count].funcs = strcpy(all + len_name, type);
  191             adapters[count].algo = strcpy(all + len_name + len_type,
  192                               algo);
  193             count++;
  194         }
  195         fclose(f);
  196         goto done;
  197     }
  198 
  199     /* look in sysfs */
  200     /* First figure out where sysfs was mounted */
  201     if ((f = fopen("/proc/mounts", "r")) == NULL) {
  202         goto done;
  203     }
  204     while (fgets(n, NAME_MAX, f)) {
  205         sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
  206         if (strcasecmp(fstype, "sysfs") == 0) {
  207             foundsysfs++;
  208             break;
  209         }
  210     }
  211     fclose(f);
  212     if (! foundsysfs) {
  213         goto done;
  214     }
  215 
  216     /* Bus numbers in i2c-adapter don't necessarily match those in
  217        i2c-dev and what we really care about are the i2c-dev numbers.
  218        Unfortunately the names are harder to get in i2c-dev */
  219     strcat(sysfs, "/class/i2c-dev");
  220     if(!(dir = opendir(sysfs)))
  221         goto done;
  222     /* go through the busses */
  223     while ((de = readdir(dir)) != NULL) {
  224         if (!strcmp(de->d_name, "."))
  225             continue;
  226         if (!strcmp(de->d_name, ".."))
  227             continue;
  228 
  229         /* this should work for kernels 2.6.5 or higher and */
  230         /* is preferred because is unambiguous */
  231         len = snprintf(n, NAME_MAX, "%s/%s/name", sysfs, de->d_name);
  232         if (len >= NAME_MAX) {
  233             fprintf(stderr, "%s: path truncated\n", n);
  234             continue;
  235         }
  236         f = fopen(n, "r");
  237         /* this seems to work for ISA */
  238         if(f == NULL) {
  239             len = snprintf(n, NAME_MAX, "%s/%s/device/name", sysfs,
  240                        de->d_name);
  241             if (len >= NAME_MAX) {
  242                 fprintf(stderr, "%s: path truncated\n", n);
  243                 continue;
  244             }
  245             f = fopen(n, "r");
  246         }
  247         /* non-ISA is much harder */
  248         /* and this won't find the correct bus name if a driver
  249            has more than one bus */
  250         if(f == NULL) {
  251             len = snprintf(n, NAME_MAX, "%s/%s/device", sysfs,
  252                        de->d_name);
  253             if (len >= NAME_MAX) {
  254                 fprintf(stderr, "%s: path truncated\n", n);
  255                 continue;
  256             }
  257             if(!(ddir = opendir(n)))
  258                 continue;
  259             while ((dde = readdir(ddir)) != NULL) {
  260                 if (!strcmp(dde->d_name, "."))
  261                     continue;
  262                 if (!strcmp(dde->d_name, ".."))
  263                     continue;
  264                 if ((!strncmp(dde->d_name, "i2c-", 4))) {
  265                     len = snprintf(n, NAME_MAX,
  266                                "%s/%s/device/%s/name",
  267                                sysfs, de->d_name,
  268                                dde->d_name);
  269                     if (len >= NAME_MAX) {
  270                         fprintf(stderr,
  271                             "%s: path truncated\n",
  272                             n);
  273                         continue;
  274                     }
  275                     if((f = fopen(n, "r")))
  276                         goto found;
  277                 }
  278             }
  279         }
  280 
  281 found:
  282         if (f != NULL) {
  283             int i2cbus;
  284             enum adt type;
  285             char *px;
  286 
  287             px = fgets(s, 120, f);
  288             fclose(f);
  289             if (!px) {
  290                 fprintf(stderr, "%s: read error\n", n);
  291                 continue;
  292             }
  293             if ((px = strchr(s, '\n')) != NULL)
  294                 *px = 0;
  295             if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
  296                 continue;
  297             if (!strncmp(s, "ISA ", 4)) {
  298                 type = adt_isa;
  299             } else {
  300                 /* Attempt to probe for adapter capabilities */
  301                 type = i2c_get_funcs(i2cbus);
  302             }
  303 
  304             if ((count + 1) % BUNCH == 0) {
  305                 /* We need more space */
  306                 adapters = more_adapters(adapters, count + 1);
  307                 if (!adapters)
  308                     return NULL;
  309             }
  310 
  311             adapters[count].nr = i2cbus;
  312             adapters[count].name = strdup(s);
  313             if (adapters[count].name == NULL) {
  314                 free_adapters(adapters);
  315                 return NULL;
  316             }
  317             adapters[count].funcs = adap_types[type].funcs;
  318             adapters[count].algo = adap_types[type].algo;
  319             count++;
  320         }
  321     }
  322     closedir(dir);
  323 
  324 done:
  325     /* Sort by bus number for convenience */
  326     qsort(adapters, count, sizeof(struct i2c_adap), sort_i2c_busses);
  327 
  328     return adapters;
  329 }
  330 
  331 static int lookup_i2c_bus_by_name(const char *bus_name)
  332 {
  333     struct i2c_adap *adapters;
  334     int i, i2cbus = -1;
  335 
  336     adapters = gather_i2c_busses();
  337     if (adapters == NULL) {
  338         fprintf(stderr, "Error: Out of memory!\n");
  339         return -3;
  340     }
  341 
  342     /* Walk the list of i2c busses, looking for the one with the
  343        right name */
  344     for (i = 0; adapters[i].name; i++) {
  345         if (strcmp(adapters[i].name, bus_name) == 0) {
  346             if (i2cbus >= 0) {
  347                 fprintf(stderr,
  348                     "Error: I2C bus name is not unique!\n");
  349                 i2cbus = -4;
  350                 goto done;
  351             }
  352             i2cbus = adapters[i].nr;
  353         }
  354     }
  355 
  356     if (i2cbus == -1)
  357         fprintf(stderr, "Error: I2C bus name doesn't match any "
  358             "bus present!\n");
  359 
  360 done:
  361     free_adapters(adapters);
  362     return i2cbus;
  363 }
  364 
  365 /*
  366  * Parse an I2CBUS command line argument and return the corresponding
  367  * bus number, or a negative value if the bus is invalid.
  368  */
  369 int lookup_i2c_bus(const char *i2cbus_arg)
  370 {
  371     unsigned long i2cbus;
  372     char *end;
  373 
  374     i2cbus = strtoul(i2cbus_arg, &end, 0);
  375     if (*end || !*i2cbus_arg) {
  376         /* Not a number, maybe a name? */
  377         return lookup_i2c_bus_by_name(i2cbus_arg);
  378     }
  379     if (i2cbus > 0xFFFFF) {
  380         fprintf(stderr, "Error: I2C bus out of range!\n");
  381         return -2;
  382     }
  383 
  384     return i2cbus;
  385 }
  386 
  387 /*
  388  * Parse a CHIP-ADDRESS command line argument and return the corresponding
  389  * chip address, or a negative value if the address is invalid.
  390  */
  391 int parse_i2c_address(const char *address_arg, int all_addrs)
  392 {
  393     long address;
  394     char *end;
  395     long min_addr = 0x08;
  396     long max_addr = 0x77;
  397 
  398     address = strtol(address_arg, &end, 0);
  399     if (*end || !*address_arg) {
  400         fprintf(stderr, "Error: Chip address is not a number!\n");
  401         return -1;
  402     }
  403 
  404     if (all_addrs) {
  405         min_addr = 0x00;
  406         max_addr = 0x7f;
  407     }
  408 
  409     if (address < min_addr || address > max_addr) {
  410         fprintf(stderr, "Error: Chip address out of range "
  411             "(0x%02lx-0x%02lx)!\n", min_addr, max_addr);
  412         return -2;
  413     }
  414 
  415     return address;
  416 }
  417 
  418 int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
  419 {
  420     int file, len;
  421 
  422     len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
  423     if (len >= (int)size) {
  424         fprintf(stderr, "%s: path truncated\n", filename);
  425         return -EOVERFLOW;
  426     }
  427     file = open(filename, O_RDWR);
  428 
  429     if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
  430         len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
  431         if (len >= (int)size) {
  432             fprintf(stderr, "%s: path truncated\n", filename);
  433             return -EOVERFLOW;
  434         }
  435         file = open(filename, O_RDWR);
  436     }
  437 
  438     if (file < 0 && !quiet) {
  439         if (errno == ENOENT) {
  440             fprintf(stderr, "Error: Could not open file "
  441                 "`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
  442                 i2cbus, i2cbus, strerror(ENOENT));
  443         } else {
  444             fprintf(stderr, "Error: Could not open file "
  445                 "`%s': %s\n", filename, strerror(errno));
  446             if (errno == EACCES)
  447                 fprintf(stderr, "Run as root?\n");
  448         }
  449     }
  450 
  451     return file;
  452 }
  453 
  454 int set_slave_addr(int file, int address, int force)
  455 {
  456     /* With force, let the user read from/write to the registers
  457        even when a driver is also running */
  458     if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
  459         fprintf(stderr,
  460             "Error: Could not set address to 0x%02x: %s\n",
  461             address, strerror(errno));
  462         return -errno;
  463     }
  464 
  465     return 0;
  466 }