From patchwork Thu Nov 7 14:00:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ond=C5=99ej_Lyson=C4=9Bk?= X-Patchwork-Id: 1191170 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="CzBjGZSp"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 4784mn13YBz9sNx for ; Fri, 8 Nov 2019 01:00:40 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726810AbfKGOAj (ORCPT ); Thu, 7 Nov 2019 09:00:39 -0500 Received: from us-smtp-2.mimecast.com ([205.139.110.61]:59672 "EHLO us-smtp-delivery-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726754AbfKGOAj (ORCPT ); Thu, 7 Nov 2019 09:00:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1573135237; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=KyRLq4Ve3M2A1oneenpTnlCLHTlXWb5NRh38aJI9yyA=; b=CzBjGZSp7OyTa2tiLYIkmscSR+zllHoZF0IofPNpz+v6lHApgxtEknPamaqUYdCEYomE3R GJW0PLTGLmnKesdZoiMY09NZxboYfwRutrqK/dMZ1mf9IMHaQg83037l2V7wZL9HPjrL1L nStNVzNeGvOBF86qIGuRxygkegtSMBY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-38-w1ZOD-4mP8i3wL4BJnzipg-1; Thu, 07 Nov 2019 09:00:35 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8ABCC107ACC4; Thu, 7 Nov 2019 14:00:34 +0000 (UTC) Received: from olysonek-rh.redhat.com (unknown [10.40.205.146]) by smtp.corp.redhat.com (Postfix) with ESMTP id E217F600F8; Thu, 7 Nov 2019 14:00:31 +0000 (UTC) From: =?utf-8?q?Ond=C5=99ej_Lyson=C4=9Bk?= To: linux-i2c@vger.kernel.org Cc: jdelvare@suse.de, =?utf-8?q?Ond=C5=99ej_Lyson=C4=9Bk?= Subject: [PATCH i2c-tools] tools: Load the i2c-dev kernel module when needed Date: Thu, 7 Nov 2019 15:00:06 +0100 Message-Id: <20191107140006.14413-1-olysonek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: w1ZOD-4mP8i3wL4BJnzipg-1 X-Mimecast-Spam-Score: 0 Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org 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 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. 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 --- 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.mk +++ 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 #include +#ifdef USE_LIBKMOD +#include + +#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) { + 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) {