|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /*
- i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
- Part of user-space programs to access for I2C
- devices.
- Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
- Mark D. Studebaker <mdsxyz123@yahoo.com>
- Copyright (C) 2008-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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
- */
-
- /* For strdup and snprintf */
- #define _BSD_SOURCE 1
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/param.h> /* for NAME_MAX */
- #include <sys/ioctl.h>
- #include <string.h>
- #include <strings.h> /* for strcasecmp() */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <limits.h>
- #include <dirent.h>
- #include <fcntl.h>
- #include <errno.h>
- #include "i2cbusses.h"
- #include <linux/i2c-dev.h>
-
- enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
-
- struct adap_type {
- const char *funcs;
- const char* algo;
- };
-
- static struct adap_type adap_types[5] = {
- { .funcs = "dummy",
- .algo = "Dummy bus", },
- { .funcs = "isa",
- .algo = "ISA bus", },
- { .funcs = "i2c",
- .algo = "I2C adapter", },
- { .funcs = "smbus",
- .algo = "SMBus adapter", },
- { .funcs = "unknown",
- .algo = "N/A", },
- };
-
- static enum adt i2c_get_funcs(int i2cbus)
- {
- unsigned long funcs;
- int file;
- char filename[20];
- enum adt ret;
-
- file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
- if (file < 0)
- return adt_unknown;
-
- if (ioctl(file, I2C_FUNCS, &funcs) < 0)
- ret = adt_unknown;
- else if (funcs & I2C_FUNC_I2C)
- ret = adt_i2c;
- else if (funcs & (I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA))
- ret = adt_smbus;
- else
- ret = adt_dummy;
-
- close(file);
- return ret;
- }
-
- /* Remove trailing spaces from a string
- Return the new string length including the trailing NUL */
- static int rtrim(char *s)
- {
- int i;
-
- for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
- s[i] = '\0';
- return i + 2;
- }
-
- void free_adapters(struct i2c_adap *adapters)
- {
- int i;
-
- for (i = 0; adapters[i].name; i++)
- free(adapters[i].name);
- free(adapters);
- }
-
- /* We allocate space for the adapters in bunches. The last item is a
- terminator, so here we start with room for 7 adapters, which should
- be enough in most cases. If not, we allocate more later as needed. */
- #define BUNCH 8
-
- /* n must match the size of adapters at calling time */
- static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
- {
- struct i2c_adap *new_adapters;
-
- new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
- if (!new_adapters) {
- free_adapters(adapters);
- return NULL;
- }
- memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
-
- return new_adapters;
- }
-
- struct i2c_adap *gather_i2c_busses(void)
- {
- char s[120];
- struct dirent *de, *dde;
- DIR *dir, *ddir;
- FILE *f;
- char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
- int foundsysfs = 0;
- int count=0;
- struct i2c_adap *adapters;
-
- adapters = calloc(BUNCH, sizeof(struct i2c_adap));
- if (!adapters)
- return NULL;
-
- /* look in /proc/bus/i2c */
- if ((f = fopen("/proc/bus/i2c", "r"))) {
- while (fgets(s, 120, f)) {
- char *algo, *name, *type, *all;
- int len_algo, len_name, len_type;
- int i2cbus;
-
- algo = strrchr(s, '\t');
- *(algo++) = '\0';
- len_algo = rtrim(algo);
-
- name = strrchr(s, '\t');
- *(name++) = '\0';
- len_name = rtrim(name);
-
- type = strrchr(s, '\t');
- *(type++) = '\0';
- len_type = rtrim(type);
-
- sscanf(s, "i2c-%d", &i2cbus);
-
- if ((count + 1) % BUNCH == 0) {
- /* We need more space */
- adapters = more_adapters(adapters, count + 1);
- if (!adapters)
- return NULL;
- }
-
- all = malloc(len_name + len_type + len_algo);
- if (all == NULL) {
- free_adapters(adapters);
- return NULL;
- }
- adapters[count].nr = i2cbus;
- adapters[count].name = strcpy(all, name);
- adapters[count].funcs = strcpy(all + len_name, type);
- adapters[count].algo = strcpy(all + len_name + len_type,
- algo);
- count++;
- }
- fclose(f);
- goto done;
- }
-
- /* look in sysfs */
- /* First figure out where sysfs was mounted */
- if ((f = fopen("/proc/mounts", "r")) == NULL) {
- goto done;
- }
- while (fgets(n, NAME_MAX, f)) {
- sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
- if (strcasecmp(fstype, "sysfs") == 0) {
- foundsysfs++;
- break;
- }
- }
- fclose(f);
- if (! foundsysfs) {
- goto done;
- }
-
- /* Bus numbers in i2c-adapter don't necessarily match those in
- i2c-dev and what we really care about are the i2c-dev numbers.
- Unfortunately the names are harder to get in i2c-dev */
- strcat(sysfs, "/class/i2c-dev");
- if(!(dir = opendir(sysfs)))
- goto done;
- /* go through the busses */
- while ((de = readdir(dir)) != NULL) {
- if (!strcmp(de->d_name, "."))
- continue;
- if (!strcmp(de->d_name, ".."))
- continue;
-
- /* this should work for kernels 2.6.5 or higher and */
- /* is preferred because is unambiguous */
- sprintf(n, "%s/%s/name", sysfs, de->d_name);
- f = fopen(n, "r");
- /* this seems to work for ISA */
- if(f == NULL) {
- sprintf(n, "%s/%s/device/name", sysfs, de->d_name);
- f = fopen(n, "r");
- }
- /* non-ISA is much harder */
- /* and this won't find the correct bus name if a driver
- has more than one bus */
- if(f == NULL) {
- sprintf(n, "%s/%s/device", sysfs, de->d_name);
- if(!(ddir = opendir(n)))
- continue;
- while ((dde = readdir(ddir)) != NULL) {
- if (!strcmp(dde->d_name, "."))
- continue;
- if (!strcmp(dde->d_name, ".."))
- continue;
- if ((!strncmp(dde->d_name, "i2c-", 4))) {
- sprintf(n, "%s/%s/device/%s/name",
- sysfs, de->d_name, dde->d_name);
- if((f = fopen(n, "r")))
- goto found;
- }
- }
- }
-
- found:
- if (f != NULL) {
- int i2cbus;
- enum adt type;
- char *px;
-
- px = fgets(s, 120, f);
- fclose(f);
- if (!px) {
- fprintf(stderr, "%s: read error\n", n);
- continue;
- }
- if ((px = strchr(s, '\n')) != NULL)
- *px = 0;
- if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
- continue;
- if (!strncmp(s, "ISA ", 4)) {
- type = adt_isa;
- } else {
- /* Attempt to probe for adapter capabilities */
- type = i2c_get_funcs(i2cbus);
- }
-
- if ((count + 1) % BUNCH == 0) {
- /* We need more space */
- adapters = more_adapters(adapters, count + 1);
- if (!adapters)
- return NULL;
- }
-
- adapters[count].nr = i2cbus;
- adapters[count].name = strdup(s);
- if (adapters[count].name == NULL) {
- free_adapters(adapters);
- return NULL;
- }
- adapters[count].funcs = adap_types[type].funcs;
- adapters[count].algo = adap_types[type].algo;
- count++;
- }
- }
- closedir(dir);
-
- done:
- return adapters;
- }
-
- static int lookup_i2c_bus_by_name(const char *bus_name)
- {
- struct i2c_adap *adapters;
- int i, i2cbus = -1;
-
- adapters = gather_i2c_busses();
- if (adapters == NULL) {
- fprintf(stderr, "Error: Out of memory!\n");
- return -3;
- }
-
- /* Walk the list of i2c busses, looking for the one with the
- right name */
- for (i = 0; adapters[i].name; i++) {
- if (strcmp(adapters[i].name, bus_name) == 0) {
- if (i2cbus >= 0) {
- fprintf(stderr,
- "Error: I2C bus name is not unique!\n");
- i2cbus = -4;
- goto done;
- }
- i2cbus = adapters[i].nr;
- }
- }
-
- if (i2cbus == -1)
- fprintf(stderr, "Error: I2C bus name doesn't match any "
- "bus present!\n");
-
- done:
- free_adapters(adapters);
- return i2cbus;
- }
-
- /*
- * Parse an I2CBUS command line argument and return the corresponding
- * bus number, or a negative value if the bus is invalid.
- */
- int lookup_i2c_bus(const char *i2cbus_arg)
- {
- unsigned long i2cbus;
- char *end;
-
- i2cbus = strtoul(i2cbus_arg, &end, 0);
- if (*end || !*i2cbus_arg) {
- /* Not a number, maybe a name? */
- return lookup_i2c_bus_by_name(i2cbus_arg);
- }
- if (i2cbus > 0xFFFFF) {
- fprintf(stderr, "Error: I2C bus out of range!\n");
- return -2;
- }
-
- return i2cbus;
- }
-
- /*
- * Parse a CHIP-ADDRESS command line argument and return the corresponding
- * chip address, or a negative value if the address is invalid.
- */
- int parse_i2c_address(const char *address_arg)
- {
- long address;
- char *end;
-
- address = strtol(address_arg, &end, 0);
- if (*end || !*address_arg) {
- fprintf(stderr, "Error: Chip address is not a number!\n");
- return -1;
- }
- if (address < 0x03 || address > 0x77) {
- fprintf(stderr, "Error: Chip address out of range "
- "(0x03-0x77)!\n");
- return -2;
- }
-
- return address;
- }
-
- int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
- {
- int file;
-
- snprintf(filename, size, "/dev/i2c/%d", i2cbus);
- filename[size - 1] = '\0';
- file = open(filename, O_RDWR);
-
- if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
- sprintf(filename, "/dev/i2c-%d", i2cbus);
- file = open(filename, O_RDWR);
- }
-
- if (file < 0 && !quiet) {
- if (errno == ENOENT) {
- fprintf(stderr, "Error: Could not open file "
- "`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
- i2cbus, i2cbus, strerror(ENOENT));
- } else {
- fprintf(stderr, "Error: Could not open file "
- "`%s': %s\n", filename, strerror(errno));
- if (errno == EACCES)
- fprintf(stderr, "Run as root?\n");
- }
- }
-
- return file;
- }
-
- int set_slave_addr(int file, int address, int force)
- {
- /* With force, let the user read from/write to the registers
- even when a driver is also running */
- if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
- fprintf(stderr,
- "Error: Could not set address to 0x%02x: %s\n",
- address, strerror(errno));
- return -errno;
- }
-
- return 0;
- }
|