Re: [PATCH i2c-tools] tools: Load the i2c-dev kernel module when needed

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jean,

Jean Delvare <jdelvare@xxxxxxx> writes:

> Hi Ondřej,
>
> On Thu,  7 Nov 2019 15:00:06 +0100, Ondřej Lysoněk wrote:
>> The i2c-dev kernel module is necessary to access I2C adapters from
>> userspace, so it's needed by all the I2C tools. The module is not
>> loaded automatically, so when e.g. i2cdetect is run without i2c-dev
>> loaded, it gives a false impression that no I2C adapters exist, which
>> is not very user-friendly:
>> 
>>  # i2cdetect -l
>>  # modprobe i2c-dev
>>  # i2cdetect -l
>> i2c-0	i2c       	i915 gmbus ssc                  	I2C adapter
>> i2c-1	i2c       	i915 gmbus vga                  	I2C adapter
>> i2c-2	i2c       	i915 gmbus panel                	I2C adapter
>> i2c-3	i2c       	i915 gmbus dpc                  	I2C adapter
>> i2c-4	i2c       	i915 gmbus dpb                  	I2C adapter
>> i2c-5	i2c       	i915 gmbus dpd                  	I2C adapter
>> i2c-6	i2c       	DPDDC-C                         	I2C adapter
>> i2c-7	i2c       	DPDDC-D                         	I2C adapter
>
> Yeah, I agree, I meant to do this for a long time, actually I have a
> proof-of-concept patch for this on my disk from 2006 :-(
>
>> This patch makes all the tools autoload i2c-dev when needed. The code
>> to load the module is compiled in only if libkmod is present on the
>> system.
>
> What is the added value of libkmod here? I mean, how is this any better
> than:
>
> 	system("modprobe i2c-dev");
>
> which is what my patch was doing?

Using libkmod should be faster as it avoids executing the shell and then
modprobe. I could use system("modprobe i2c-dev") in the #else branch
when libkmod is not available, though.

>
>> This resolves the following Fedora bug:
>> https://bugzilla.redhat.com/show_bug.cgi?id=913203
>> 
>> The patch is based on a previous version of the patch by Michal Minar,
>> posted on linux-i2c some time ago:
>> https://marc.info/?l=linux-i2c&m=140195625915505&w=2
>> 
>> The patch is also inspired by the GPLv2+ modprobe from kmod.
>> 
>> Signed-off-by: Ondřej Lysoněk <olysonek@xxxxxxxxxx>
>> ---
>>  tools/Module.mk   | 11 +++++-
>>  tools/i2cbusses.c | 97 +++++++++++++++++++++++++++++++++++++++++------
>>  2 files changed, 95 insertions(+), 13 deletions(-)
>> 
>> diff --git a/tools/Module.mk b/tools/Module.mk
>> index 693102f..17ef8d4 100644
>> --- a/tools/Module.mkles joues
>> +++ b/tools/Module.mk
>> @@ -12,10 +12,17 @@ TOOLS_DIR	:= tools
>>  TOOLS_CFLAGS	:= -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
>>  		   -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
>>  		   -W -Wundef -Wmissing-prototypes -Iinclude
>> +TOOLS_LDFLAGS	:=
>> +
>> +ifeq ($(shell pkg-config --exists libkmod && echo 1), 1)
>> +TOOLS_CFLAGS	+= $(shell pkg-config --cflags libkmod) -DUSE_LIBKMOD
>> +TOOLS_LDFLAGS	+= $(shell pkg-config --libs libkmod)
>> +endif
>> +
>>  ifeq ($(USE_STATIC_LIB),1)
>> -TOOLS_LDFLAGS	:= $(LIB_DIR)/$(LIB_STLIBNAME)
>> +TOOLS_LDFLAGS	+= $(LIB_DIR)/$(LIB_STLIBNAME)
>>  else
>> -TOOLS_LDFLAGS	:= -L$(LIB_DIR) -li2c
>> +TOOLS_LDFLAGS	+= -L$(LIB_DIR) -li2c
>>  endif
>>  
>>  TOOLS_TARGETS	:= i2cdetect i2cdump i2cset i2cget i2ctransfer
>> diff --git a/tools/i2cbusses.c b/tools/i2cbusses.c
>> index b4f00ae..5f3ad42 100644
>> --- a/tools/i2cbusses.c
>> +++ b/tools/i2cbusses.c
>> @@ -43,6 +43,14 @@
>>  #include <linux/i2c.h>
>>  #include <linux/i2c-dev.h>
>>  
>> +#ifdef USE_LIBKMOD
>> +#include <libkmod.h>
>> +
>> +#define I2C_DEV_MOD_NAME "i2c_dev"
>> +#define MODULE_LOAD_ERR_MSG "Error: Failed to load required " \
>> +                             I2C_DEV_MOD_NAME " kernel module: "
>> +#endif
>> +
>>  enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
>>  
>>  struct adap_type {
>> @@ -63,6 +71,61 @@ static struct adap_type adap_types[5] = {
>>  	  .algo		= "N/A", },
>>  };
>>  
>> +
>> +/**
>> + * Try to load the i2c_dev kernel module.
>> + * Returns 1 on success, 0 otherwise.
>> + */
>> +static int load_i2c_dev_module(int quiet) {
>> +	int ret = 0;
>> +#ifdef USE_LIBKMOD
>> +	int flags = 0;
>> +	struct kmod_ctx *ctx;
>> +	struct kmod_list *l, *list = NULL;
>> +
>> +	ctx = kmod_new(NULL, NULL);
>> +	if (!ctx) {
>> +		if (!quiet)
>> +			fprintf(stderr,
>> +				MODULE_LOAD_ERR_MSG "kmod_new() failed\n");
>> +		goto out;
>> +	}
>> +	if (kmod_module_new_from_lookup(ctx, I2C_DEV_MOD_NAME, &list) < 0
>> +	    || list == NULL) {
>> +		if (!quiet)
>> +			fprintf(stderr, MODULE_LOAD_ERR_MSG
>> +				I2C_DEV_MOD_NAME " module lookup failed\n");
>> +		goto ctx_unref;
>> +	}
>> +
>> +	flags |= KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY;
>> +	kmod_list_foreach(l, list) {
>> +		struct kmod_module *mod = kmod_module_get_module(l);
>> +		int res;
>> +		res = kmod_module_probe_insert_module(mod, flags, NULL, NULL,
>> +		                                      NULL, NULL);
>> +		ret = (res >= 0);
>> +		if (res == -ENOENT && !quiet) {
>> +			fprintf(stderr, MODULE_LOAD_ERR_MSG
>> +			        "unknown symbol in module \"%s\", or unknown parameter (see dmesg)\n",
>> +			        kmod_module_get_name(mod));
>> +		} else if (res < 0 && !quiet) {
>> +			fprintf(stderr, MODULE_LOAD_ERR_MSG "(module %s): %s\n",
>> +			        kmod_module_get_name(mod), strerror(-res));
>> +		}
>> +		kmod_module_unref(mod);
>> +	}
>> +
>> +	kmod_module_unref_list(list);
>> +ctx_unref:
>> +	kmod_unref(ctx);
>> +out:
>> +#else
>> +	(void) quiet;
>> +#endif
>> +	return ret;
>> +}
>> +
>>  static enum adt i2c_get_funcs(int i2cbus)
>>  {
>>  	unsigned long funcs;
>> @@ -209,8 +272,12 @@ struct i2c_adap *gather_i2c_busses(void)
>>  	   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 (!load_i2c_dev_module(0))
>> +			goto done;
>> +		if ((!(dir = opendir(sysfs))))
>> +			goto done;
>> +	}
>>  	/* go through the busses */
>>  	while ((de = readdir(dir)) != NULL) {
>>  		if (!strcmp(de->d_name, "."))
>> @@ -407,21 +474,29 @@ int parse_i2c_address(const char *address_arg, int all_addrs)
>>  int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
>>  {
>>  	int file, len;
>> +	int i;
>>  
>> -	len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
>> -	if (len >= (int)size) {
>> -		fprintf(stderr, "%s: path truncated\n", filename);
>> -		return -EOVERFLOW;
>> -	}
>> -	file = open(filename, O_RDWR);
>> -
>> -	if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
>> -		len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
>> +	for (i = 0; i < 2; ++i) {
>
> Sorry, what are you doing here?

I'm attempting to load /dev/i2c/%d or /dev/i2c-%d. If none of them
exist, I load the i2c-dev module and try again. Perhaps it would be
clearer if I used goto instead of the for loop. Or move opening the
files to a separate function.

>
>> +		len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
>>  		if (len >= (int)size) {
>>  			fprintf(stderr, "%s: path truncated\n", filename);
>>  			return -EOVERFLOW;
>>  		}
>>  		file = open(filename, O_RDWR);
>> +
>> +		if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
>> +			len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
>> +			if (len >= (int)size) {
>> +				fprintf(stderr, "%s: path truncated\n", filename);
>> +				return -EOVERFLOW;
>> +			}
>> +			file = open(filename, O_RDWR);
>> +		}
>> +
>> +		if (file >= 0 || (errno != ENOENT && errno != ENOTDIR))
>> +			break;
>> +		if (i > 0 || !load_i2c_dev_module(quiet))
>> +			break;
>>  	}
>>  
>>  	if (file < 0 && !quiet) {
>
>
> -- 
> Jean Delvare
> SUSE L3 Support

Thanks,
Ondra





[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux