diff -up i2c-tools-3.1.0/tools/i2cbusses.c.load-i2cdev i2c-tools-3.1.0/tools/i2cbusses.c --- i2c-tools-3.1.0/tools/i2cbusses.c.load-i2cdev 2010-11-26 11:25:32.000000000 +0100 +++ i2c-tools-3.1.0/tools/i2cbusses.c 2017-04-05 08:43:08.135938481 +0200 @@ -37,9 +37,16 @@ #include #include #include +#ifdef USE_LIBKMOD + #include +#endif #include "i2cbusses.h" #include +#define BUFLEN (NAME_MAX > 512 ? NAME_MAX : 512) +#define I2C_DEV_MOD_NAME "i2c_dev" +#define I2C_DEV_SUBPATH "/class/i2c-dev" + enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown }; struct adap_type { @@ -60,6 +67,145 @@ static struct adap_type adap_types[5] = .algo = "N/A", }, }; +/* Get and cache sysfs mount point. */ +static const char *get_sysfs_mount_point(void) { + static const char *mp = NULL; + + if (mp == NULL) { + char sysfs[NAME_MAX]; + char fstype[NAME_MAX]; + char line[BUFLEN]; + FILE *f; + + if ((f = fopen("/proc/mounts", "r")) == NULL) { + perror("failed to read /proc/mounts: "); + goto done; + } + while (fgets(line, BUFLEN, f)) { + sscanf(line, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype); + if (strcasecmp(fstype, "sysfs") == 0) { + mp = strdup(sysfs); + if (mp == NULL) + perror("strdup: "); + break; + } + } + fclose(f); + } +done: + return mp; +} + +/* Get an absolute path of i2c device directory. Free the result when not + * needed. */ +static char *get_i2c_dev_path(void) { + char *path = NULL; + int mplen, splen; + const char *mp = get_sysfs_mount_point(); + + if (mp != NULL) { + mplen = strlen(mp); + splen = strlen(I2C_DEV_SUBPATH); + path = malloc(mplen + splen + 1); + if (path == NULL) { + perror("malloc: "); + } else { + strncpy(path, mp, mplen); + strncpy(path + mplen, I2C_DEV_SUBPATH, splen); + path[mplen+splen] = '\0'; + } + } + return path; +} + +/** + * Try to load i2c_dev kernel mode. Do nothing if module is already loaded. + * Returns 1 on success, 0 otherwise. + */ +static int try_load_i2c_dev_mod(void) { + int err = 0, loaded = 0; + char errbuf[BUFLEN] = { 0 }; +#ifdef USE_LIBKMOD + int flags = 0; + struct kmod_ctx *ctx; + struct kmod_list *l, *list = NULL; + + ctx = kmod_new(NULL, NULL); + if (!ctx) { + snprintf(errbuf, BUFLEN, "kmod_new() failed!"); + goto done; + } + if (kmod_module_new_from_lookup(ctx, I2C_DEV_MOD_NAME, &list) < 0 || list == NULL) { + snprintf(errbuf, BUFLEN, I2C_DEV_MOD_NAME " module lookup failed"); + goto ctx_unref; + } + + flags |= KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY; + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + err = kmod_module_probe_insert_module(mod, flags, NULL, NULL, NULL, NULL); + if (err == -ENOENT) { + snprintf(errbuf, BUFLEN, + "unknown symbol in module \"%s\", or unknown parameter (see dmesg)", + kmod_module_get_name(mod)); + } else if (err < 0) { + snprintf(errbuf, BUFLEN, "(module %s): %s", + kmod_module_get_name(mod), strerror(-err)); + } else { + kmod_module_unref(mod); + ++loaded; + break; + } + kmod_module_unref(mod); + } + + kmod_module_unref_list(list); +ctx_unref: + kmod_unref(ctx); +#else /* Try to load the module with modprobe. */ + struct stat st; + char *dev_path = get_i2c_dev_path(); + + /* First check whether the module is already loaded or built-in. */ + if (dev_path == NULL) + goto done; + err = stat(dev_path, &st); + if (err < 0) { + if (errno != ENOENT) { + snprintf(errbuf, BUFLEN, "can not stat \"%s\": %s", dev_path, + strerror(errno)); + goto err; + } else { + err = 0; + } + } else { + ++loaded; + goto done; + } + + err = system("modprobe " I2C_DEV_MOD_NAME); + if (err < 0) { + snprintf(errbuf, BUFLEN, "failed to execute modprobe command"); + } else if (err > 0) { + snprintf(errbuf, BUFLEN, "modprobe command exited with code %d", + WEXITSTATUS(err)); + } else { + ++loaded; + goto done; + } + +err: +#endif + if (errbuf[0]) + fprintf(stderr, "Failed to load required " I2C_DEV_MOD_NAME + " kernel module: %s\n", errbuf); +done: +#ifndef USE_LIBKMOD + free(dev_path); +#endif + return loaded; +} + static enum adt i2c_get_funcs(int i2cbus) { unsigned long funcs; @@ -132,8 +278,7 @@ struct i2c_adap *gather_i2c_busses(void) struct dirent *de, *dde; DIR *dir, *ddir; FILE *f; - char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX]; - int foundsysfs = 0; + char *sysfs = NULL, n[NAME_MAX]; int count=0; struct i2c_adap *adapters; @@ -185,29 +330,19 @@ struct i2c_adap *gather_i2c_busses(void) goto done; } - /* look in sysfs */ - /* First figure out where sysfs was mounted */ - if ((f = fopen("/proc/mounts", "r")) == NULL) { + sysfs = get_i2c_dev_path(); + if (sysfs == 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; + if(!(dir = opendir(sysfs))) { + if (!try_load_i2c_dev_mod()) + goto done; + if ((!(dir = opendir(sysfs)))) + goto done; + } /* go through the busses */ while ((de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".")) @@ -272,14 +407,15 @@ found: /* We need more space */ adapters = more_adapters(adapters, count + 1); if (!adapters) - return NULL; + goto done; } adapters[count].nr = i2cbus; adapters[count].name = strdup(s); if (adapters[count].name == NULL) { free_adapters(adapters); - return NULL; + adapters = NULL; + goto done; } adapters[count].funcs = adap_types[type].funcs; adapters[count].algo = adap_types[type].algo; @@ -289,6 +425,7 @@ found: closedir(dir); done: + free(sysfs); return adapters; } @@ -380,8 +517,21 @@ int open_i2c_dev(int i2cbus, char *filen file = open(filename, O_RDWR); if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) { + if (!(try_load_i2c_dev_mod())) + fprintf(stderr, "Cannot load i2c-dev module.\n"); + else + 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 && (errno == ENOENT || errno == ENOTDIR)) { + if (!(try_load_i2c_dev_mod())) + fprintf(stderr, "Cannot load i2c-dev module.\n"); + else + file = open(filename, O_RDWR); + } } if (file < 0 && !quiet) { diff -up i2c-tools-3.1.0/tools/Module.mk.load-i2cdev i2c-tools-3.1.0/tools/Module.mk --- i2c-tools-3.1.0/tools/Module.mk.load-i2cdev 2009-01-19 16:11:33.000000000 +0100 +++ i2c-tools-3.1.0/tools/Module.mk 2017-04-05 08:41:10.554057368 +0200 @@ -15,6 +15,11 @@ TOOLS_CFLAGS := -Wstrict-prototypes -Wsh TOOLS_TARGETS := i2cdetect i2cdump i2cset i2cget +ifeq ($(shell pkg-config --exists libkmod && echo 1), 1) + TOOLS_CFLAGS += $(shell pkg-config --cflags libkmod) -DUSE_LIBKMOD + LDFLAGS += $(shell pkg-config --libs libkmod) +endif + # # Programs #