2007-05-08 01:04:08

by Lennart Poettering

[permalink] [raw]
Subject: [PATCH] DMI-based module autoloading

From: Lennart Poettering <[email protected]>

Hi!

The patch below adds DMI/SMBIOS based module autoloading to the Linux
kernel. The idea is to load laptop drivers automatically (and other
drivers which cannot be autoloaded otherwise), based on the DMI system
identification information of the BIOS.

Right now most distros manually try to load all available laptop
drivers on bootup in the hope that at least one of them loads
successfully. This patch does away with all that, and uses udev to
automatically load matching drivers on the right machines.

Basically the patch just exports the DMI information that has been
parsed by the kernel anyway to userspace via a sysfs device
/sys/class/dmi/id and makes sure that proper modalias attributes are
available. Besides adding the "modalias" attribute it also adds
attributes for a few other DMI fields which might be useful for
writing udev rules.

This patch is not an attempt to export the entire DMI/SMBIOS data to
userspace. We already have "dmidecode" which parses the complete DMI
info from userspace. The purpose of this patch is machine model
identification and good udev integration.

To take advantage of DMI based module autoloading, a driver should
export one or more MODULE_ALIAS fields similar to these:

MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");

These lines are specific to my msi-laptop.c driver. They are basically
just a concatenation of a few carefully selected DMI fields with all
potentially bad characters stripped.

Besides laptop drivers, modules like "hdaps", the i2c modules
and the hwmon modules are good candidates for "dmi:" MODULE_ALIAS
lines.

Besides merely exporting the DMI data via sysfs the patch adds
support for a few more DMI fields. Especially the CHASSIS fields are
very useful to identify different laptop modules. The patch also adds
working MODULE_ALIAS lines to my msi-laptop.c driver.

I'd like to thank Kay Sievers for helping me to clean up this patch
for posting it on lkml.

Patch is against Linus' current GIT HEAD. Should probably apply to
older kernels as well without modification.

Please merge,

Lennart

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Lennart Poettering <[email protected]>
---
drivers/firmware/Kconfig | 9 +
drivers/firmware/Makefile | 1
drivers/firmware/dmi-id.c | 227 ++++++++++++++++++++++++++++++++++++++++++++
drivers/firmware/dmi_scan.c | 73 ++++++++++++--
drivers/misc/msi-laptop.c | 44 +++++++-
include/linux/dmi.h | 8 +
6 files changed, 353 insertions(+), 9 deletions(-)

--- linux-2.6.orig/drivers/firmware/Kconfig 2007-05-04 17:51:46.000000000 +0200
+++ linux-2.6/drivers/firmware/Kconfig 2007-05-06 20:23:02.000000000 +0200
@@ -84,4 +84,13 @@ config DCDBAS
Say Y or M here to enable the driver for use by Dell systems
management software such as Dell OpenManage.

+config DMIID
+ bool "Export DMI identification via sysfs to userspace"
+ depends on DMI
+ default y
+ help
+ Say Y here if you want to query SMBIOS/DMI system identification
+ information from userspace through /sys/class/dmi/id/ or if you want
+ DMI-based module auto-loading.
+
endmenu
--- linux-2.6.orig/drivers/firmware/dmi-id.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6/drivers/firmware/dmi-id.c 2007-05-08 01:53:56.000000000 +0200
@@ -0,0 +1,227 @@
+/*-*-linux-c-*-*/
+
+/*
+ * Export SMBIOS/DMI info via sysfs to userspace
+ *
+ * Copyright 2007, Lennart Poettering
+ *
+ * Licensed under GPLv2
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/device.h>
+#include <linux/autoconf.h>
+
+#define DEFINE_DMI_ATTR(_name, _mode, _show) \
+static struct device_attribute sys_dmi_##_name##_attr = \
+ __ATTR(_name, _mode, _show, NULL);
+
+#define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field) \
+static ssize_t sys_dmi_##_name##_show(struct device *dev, \
+ struct device_attribute *attr, char *page) \
+{ \
+ ssize_t len; \
+ len = snprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field)); \
+ page[PAGE_SIZE-2] = '\n'; \
+ page[PAGE_SIZE-1] = 0; \
+ return min_t(ssize_t, len, PAGE_SIZE); \
+} \
+DEFINE_DMI_ATTR(_name, _mode, sys_dmi_##_name##_show);
+
+DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor, 0444, DMI_BIOS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(bios_version, 0444, DMI_BIOS_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(bios_date, 0444, DMI_BIOS_DATE);
+DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor, 0444, DMI_SYS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME);
+DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID);
+DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME);
+DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(board_serial, 0400, DMI_BOARD_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag, 0444, DMI_BOARD_ASSET_TAG);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor, 0444, DMI_CHASSIS_VENDOR);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_type, 0444, DMI_CHASSIS_TYPE);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_version, 0444, DMI_CHASSIS_VERSION);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial, 0400, DMI_CHASSIS_SERIAL);
+DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG);
+
+static void ascii_filter(char *d, const char *s)
+{
+ /* Filter out characters we don't want to see in the modalias string */
+ for (; *s; s++)
+ if (*s > ' ' && *s < 127 && *s != ':')
+ *(d++) = *s;
+
+ *d = 0;
+}
+
+static ssize_t get_modalias(char *buffer, size_t buffer_size)
+{
+ struct mafield {
+ const char *prefix;
+ int field;
+ };
+
+ static const struct mafield fields[] = {
+ { "bvn", DMI_BIOS_VENDOR },
+ { "bvr", DMI_BIOS_VERSION },
+ { "bd", DMI_BIOS_DATE },
+ { "svn", DMI_SYS_VENDOR },
+ { "pn", DMI_PRODUCT_NAME },
+ { "pvr", DMI_PRODUCT_VERSION },
+ { "rvn", DMI_BOARD_VENDOR },
+ { "rn", DMI_BOARD_NAME },
+ { "rvr", DMI_BOARD_VERSION },
+ { "cvn", DMI_CHASSIS_VENDOR },
+ { "ct", DMI_CHASSIS_TYPE },
+ { "cvr", DMI_CHASSIS_VERSION },
+ { NULL, DMI_NONE }
+ };
+
+ ssize_t l, left;
+ char *p;
+ const struct mafield *f;
+
+ strcpy(buffer, "dmi");
+ p = buffer + 3; left = buffer_size-5;
+
+ for (f = fields; f->prefix && left > 0; f++) {
+ const char *c;
+ char *t;
+
+ c = dmi_get_system_info(f->field);
+ if (!c)
+ continue;
+
+ t = kmalloc(strlen(c), GFP_KERNEL);
+ if (!t)
+ break;
+ ascii_filter(t, c);
+ l = snprintf(p, left, ":%s%s", f->prefix, t);
+ kfree(t);
+
+ if (l > left)
+ l = left;
+
+ p += l;
+ left -= l;
+ }
+
+ snprintf(p, left, ":");
+
+ return p - buffer + 1;
+}
+
+static ssize_t sys_dmi_modalias_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ ssize_t r;
+ r = get_modalias(page, PAGE_SIZE-1);
+ strcat(page, "\n");
+ return r+1;
+}
+
+DEFINE_DMI_ATTR(modalias, 0444, sys_dmi_modalias_show);
+
+static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2];
+
+static struct attribute_group sys_dmi_attribute_group = {
+ .attrs = sys_dmi_attributes
+};
+
+static struct attribute_group* sys_dmi_attribute_groups[] = {
+ &sys_dmi_attribute_group,
+ NULL
+};
+
+static int dmi_dev_uevent(struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ strcpy(buffer, "MODALIAS=");
+ get_modalias(buffer+9, buffer_size-9);
+ envp[0] = buffer;
+ envp[1] = NULL;
+
+ return 0;
+}
+
+static struct class dmi_class = {
+ .name = "dmi",
+ .dev_release = (void(*)(struct device *)) kfree,
+ .dev_uevent = dmi_dev_uevent,
+};
+
+static struct device *dmi_dev;
+
+/* Initialization */
+
+#define ADD_DMI_ATTR(_name, _field) \
+ if (dmi_get_system_info(_field)) \
+ sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr;
+
+extern int dmi_available;
+
+int __init dmi_id_init(void)
+{
+ int ret, i;
+
+ if (!dmi_available)
+ return -ENODEV;
+
+ /* Not necessarily all DMI fields are available on all
+ * systems, hence let's built an attribute table of just
+ * what's available */
+ i = 0;
+ ADD_DMI_ATTR(bios_vendor, DMI_BIOS_VENDOR);
+ ADD_DMI_ATTR(bios_version, DMI_BIOS_VERSION);
+ ADD_DMI_ATTR(bios_date, DMI_BIOS_DATE);
+ ADD_DMI_ATTR(sys_vendor, DMI_SYS_VENDOR);
+ ADD_DMI_ATTR(product_name, DMI_PRODUCT_NAME);
+ ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION);
+ ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL);
+ ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID);
+ ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR);
+ ADD_DMI_ATTR(board_name, DMI_BOARD_NAME);
+ ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION);
+ ADD_DMI_ATTR(board_serial, DMI_BOARD_SERIAL);
+ ADD_DMI_ATTR(board_asset_tag, DMI_BOARD_ASSET_TAG);
+ ADD_DMI_ATTR(chassis_vendor, DMI_CHASSIS_VENDOR);
+ ADD_DMI_ATTR(chassis_type, DMI_CHASSIS_TYPE);
+ ADD_DMI_ATTR(chassis_version, DMI_CHASSIS_VERSION);
+ ADD_DMI_ATTR(chassis_serial, DMI_CHASSIS_SERIAL);
+ ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG);
+ sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr;
+
+ ret = class_register(&dmi_class);
+ if (ret)
+ return ret;
+
+ dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL);
+ if (!dmi_dev) {
+ ret = -ENOMEM;
+ goto fail_class_unregister;
+ }
+
+ dmi_dev->class = &dmi_class;
+ strcpy(dmi_dev->bus_id, "id");
+ dmi_dev->groups = sys_dmi_attribute_groups;
+
+ ret = device_register(dmi_dev);
+ if (ret)
+ goto fail_class_unregister;
+
+ return 0;
+
+fail_class_unregister:
+
+ class_unregister(&dmi_class);
+
+ return ret;
+}
+
+arch_initcall(dmi_id_init);
--- linux-2.6.orig/drivers/firmware/Makefile 2007-05-04 17:51:46.000000000 +0200
+++ linux-2.6/drivers/firmware/Makefile 2007-04-14 18:53:20.000000000 +0200
@@ -7,3 +7,4 @@ obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_PCDP) += pcdp.o
obj-$(CONFIG_DELL_RBU) += dell_rbu.o
obj-$(CONFIG_DCDBAS) += dcdbas.o
+obj-$(CONFIG_DMIID) += dmi-id.o
--- linux-2.6.orig/drivers/firmware/dmi_scan.c 2007-05-04 17:51:46.000000000 +0200
+++ linux-2.6/drivers/firmware/dmi_scan.c 2007-05-08 01:56:03.000000000 +0200
@@ -84,6 +84,7 @@ static int __init dmi_checksum(u8 *buf)

static char *dmi_ident[DMI_STRING_MAX];
static LIST_HEAD(dmi_devices);
+int dmi_available;

/*
* Save a DMI string
@@ -102,6 +103,51 @@ static void __init dmi_save_ident(struct
dmi_ident[slot] = p;
}

+static void __init dmi_save_uuid(struct dmi_header *dm, int slot, int index)
+{
+ u8 *d = (u8*) dm + index;
+ char *s;
+ int is_ff = 1, is_00 = 1, i;
+
+ if (dmi_ident[slot])
+ return;
+
+ for (i = 0; i < 16 && (is_ff || is_00); i++) {
+ if(d[i] != 0x00) is_ff = 0;
+ if(d[i] != 0xFF) is_00 = 0;
+ }
+
+ if (is_ff || is_00)
+ return;
+
+ s = dmi_alloc(16*2+4+1);
+ if (!s)
+ return;
+
+ sprintf(s,
+ "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
+ d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+
+ dmi_ident[slot] = s;
+}
+
+static void __init dmi_save_type(struct dmi_header *dm, int slot, int index)
+{
+ u8 *d = (u8*) dm + index;
+ char *s;
+
+ if (dmi_ident[slot])
+ return;
+
+ s = dmi_alloc(4);
+ if (!s)
+ return;
+
+ sprintf(s, "%u", *d & 0x7F);
+ dmi_ident[slot] = s;
+}
+
static void __init dmi_save_devices(struct dmi_header *dm)
{
int i, count = (dm->length - sizeof(struct dmi_header)) / 2;
@@ -192,11 +238,21 @@ static void __init dmi_decode(struct dmi
dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
+ dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8);
break;
case 2: /* Base Board Information */
dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
dmi_save_ident(dm, DMI_BOARD_NAME, 5);
dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
+ dmi_save_ident(dm, DMI_BOARD_SERIAL, 7);
+ dmi_save_ident(dm, DMI_BOARD_ASSET_TAG, 8);
+ break;
+ case 3: /* Chassis Information */
+ dmi_save_ident(dm, DMI_CHASSIS_VENDOR, 4);
+ dmi_save_type(dm, DMI_CHASSIS_TYPE, 5);
+ dmi_save_ident(dm, DMI_CHASSIS_VERSION, 6);
+ dmi_save_ident(dm, DMI_CHASSIS_SERIAL, 7);
+ dmi_save_ident(dm, DMI_CHASSIS_ASSET_TAG, 8);
break;
case 10: /* Onboard Devices Information */
dmi_save_devices(dm);
@@ -243,18 +299,20 @@ void __init dmi_scan_machine(void)
if (efi.smbios == EFI_INVALID_TABLE_ADDR)
goto out;

- /* This is called as a core_initcall() because it isn't
- * needed during early boot. This also means we can
- * iounmap the space when we're done with it.
- */
+ /* This is called as a core_initcall() because it isn't
+ * needed during early boot. This also means we can
+ * iounmap the space when we're done with it.
+ */
p = dmi_ioremap(efi.smbios, 32);
if (p == NULL)
goto out;

rc = dmi_present(p + 0x10); /* offset of _DMI_ string */
dmi_iounmap(p, 32);
- if (!rc)
+ if (!rc) {
+ dmi_available = 1;
return;
+ }
}
else {
/*
@@ -268,8 +326,10 @@ void __init dmi_scan_machine(void)

for (q = p; q < p + 0x10000; q += 16) {
rc = dmi_present(q);
- if (!rc)
+ if (!rc) {
+ dmi_available = 1;
return;
+ }
}
}
out: printk(KERN_INFO "DMI not present or invalid.\n");
@@ -404,3 +464,4 @@ int dmi_get_year(int field)

return year;
}
+
--- linux-2.6.orig/drivers/misc/msi-laptop.c 2007-05-04 17:52:02.000000000 +0200
+++ linux-2.6/drivers/misc/msi-laptop.c 2007-05-08 01:57:47.000000000 +0200
@@ -23,6 +23,8 @@
* msi-laptop.c - MSI S270 laptop support. This laptop is sold under
* various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
*
+ * Driver also supports S271, S420 models.
+ *
* This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
*
* lcd_level - Screen brightness: contains a single integer in the
@@ -281,25 +283,56 @@ static struct platform_device *msipf_dev

/* Initialization */

+static int dmi_check_cb(struct dmi_system_id *id)
+{
+ printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
+ return 0;
+}
+
static struct dmi_system_id __initdata msi_dmi_table[] = {
{
.ident = "MSI S270",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
- }
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
+ DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "MSI S271",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "MSI S420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
+ },
+ .callback = dmi_check_cb
},
{
.ident = "Medion MD96100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
- }
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
+ DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
+ },
+ .callback = dmi_check_cb
},
{ }
};

-
static int __init msi_init(void)
{
int ret;
@@ -394,3 +427,8 @@ MODULE_AUTHOR("Lennart Poettering");
MODULE_DESCRIPTION("MSI Laptop Support");
MODULE_VERSION(MSI_DRIVER_VERSION);
MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
+MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
--- linux-2.6.orig/include/linux/dmi.h 2007-05-04 17:53:06.000000000 +0200
+++ linux-2.6/include/linux/dmi.h 2007-05-06 19:14:44.000000000 +0200
@@ -12,9 +12,17 @@ enum dmi_field {
DMI_PRODUCT_NAME,
DMI_PRODUCT_VERSION,
DMI_PRODUCT_SERIAL,
+ DMI_PRODUCT_UUID,
DMI_BOARD_VENDOR,
DMI_BOARD_NAME,
DMI_BOARD_VERSION,
+ DMI_BOARD_SERIAL,
+ DMI_BOARD_ASSET_TAG,
+ DMI_CHASSIS_VENDOR,
+ DMI_CHASSIS_TYPE,
+ DMI_CHASSIS_VERSION,
+ DMI_CHASSIS_SERIAL,
+ DMI_CHASSIS_ASSET_TAG,
DMI_STRING_MAX,
};



2007-05-08 01:26:38

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH] DMI-based module autoloading

On Tue, May 08, 2007 at 02:54:29AM +0200, Lennart Poettering wrote:
> To take advantage of DMI based module autoloading, a driver should
> export one or more MODULE_ALIAS fields similar to these:
>
> MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
> MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
> MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
> MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");

Is there any way to automatically generate these aliases (like we do for
PCI and USB), in a way that is easier for the developer, instead of
having to figure out how to create such a "wierd" string like these by
hand?

> These lines are specific to my msi-laptop.c driver. They are basically
> just a concatenation of a few carefully selected DMI fields with all
> potentially bad characters stripped.

What is a "bad character" here?

thanks,

greg k-h

2007-05-08 03:07:08

by Björn Steinbrink

[permalink] [raw]
Subject: Re: [PATCH] DMI-based module autoloading

On 2007.05.08 02:54:29 +0200, Lennart Poettering wrote:
> From: Lennart Poettering <[email protected]>
> +#define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field) \

Hm, too many tabs here? Wraps for me with tabsize=8.

> +static ssize_t sys_dmi_##_name##_show(struct device *dev, \
> + struct device_attribute *attr, char *page) \
> +{ \
> + ssize_t len; \
> + len = snprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field)); \
> + page[PAGE_SIZE-2] = '\n'; \
> + page[PAGE_SIZE-1] = 0; \
> + return min_t(ssize_t, len, PAGE_SIZE); \
> +} \

If you return PAGE_SIZE here, that includes the trailing 0, while len
does not. Seems not to cause any problems, but scnprintf already handles
that, so how about this?

ssize_t len;
len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field));
page[len - 1] = '\n';
return len;

> +DEFINE_DMI_ATTR(_name, _mode, sys_dmi_##_name##_show);
> +
> +DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor, 0444, DMI_BIOS_VENDOR);
> +DEFINE_DMI_ATTR_WITH_SHOW(bios_version, 0444, DMI_BIOS_VERSION);
> +DEFINE_DMI_ATTR_WITH_SHOW(bios_date, 0444, DMI_BIOS_DATE);
> +DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor, 0444, DMI_SYS_VENDOR);
> +DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME);
> +DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION);
> +DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL);
> +DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID);
> +DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR);
> +DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME);
> +DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION);
> +DEFINE_DMI_ATTR_WITH_SHOW(board_serial, 0400, DMI_BOARD_SERIAL);
> +DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag, 0444, DMI_BOARD_ASSET_TAG);
> +DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor, 0444, DMI_CHASSIS_VENDOR);
> +DEFINE_DMI_ATTR_WITH_SHOW(chassis_type, 0444, DMI_CHASSIS_TYPE);
> +DEFINE_DMI_ATTR_WITH_SHOW(chassis_version, 0444, DMI_CHASSIS_VERSION);
> +DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial, 0400, DMI_CHASSIS_SERIAL);
> +DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG);

Mixed tab/space alignment.

> +static void ascii_filter(char *d, const char *s)
> +{
> + /* Filter out characters we don't want to see in the modalias string */
> + for (; *s; s++)
> + if (*s > ' ' && *s < 127 && *s != ':')
> + *(d++) = *s;
> +
> + *d = 0;
> +}
> +
> +static ssize_t get_modalias(char *buffer, size_t buffer_size)
> +{
> + struct mafield {
> + const char *prefix;
> + int field;
> + };
> +

Trailing whitespace (also in a few more places)

> + static const struct mafield fields[] = {
> + { "bvn", DMI_BIOS_VENDOR },
> + { "bvr", DMI_BIOS_VERSION },
> + { "bd", DMI_BIOS_DATE },
> + { "svn", DMI_SYS_VENDOR },
> + { "pn", DMI_PRODUCT_NAME },
> + { "pvr", DMI_PRODUCT_VERSION },
> + { "rvn", DMI_BOARD_VENDOR },
> + { "rn", DMI_BOARD_NAME },
> + { "rvr", DMI_BOARD_VERSION },
> + { "cvn", DMI_CHASSIS_VENDOR },
> + { "ct", DMI_CHASSIS_TYPE },
> + { "cvr", DMI_CHASSIS_VERSION },
> + { NULL, DMI_NONE }

Mixed tab/space alignment.

> + };
> +
> + ssize_t l, left;
> + char *p;
> + const struct mafield *f;
> +
> + strcpy(buffer, "dmi");
> + p = buffer + 3; left = buffer_size-5;
> +
> + for (f = fields; f->prefix && left > 0; f++) {
> + const char *c;
> + char *t;
> +
> + c = dmi_get_system_info(f->field);
> + if (!c)
> + continue;
> +
> + t = kmalloc(strlen(c), GFP_KERNEL);
> + if (!t)
> + break;
> + ascii_filter(t, c);
> + l = snprintf(p, left, ":%s%s", f->prefix, t);

Looks like a candidate for scnprintf, too, avoids the l > left
comparison below.

> + kfree(t);
> +
> + if (l > left)
> + l = left;
> +
> + p += l;
> + left -= l;
> + }
> +
> + snprintf(p, left, ":");

I assume that the colon is strictly required as -5 is used above to
calculate how much space is left. But "left" might be 0 if the above
snprintf had to truncate a string, thus the colon would not be written,
although there's space left. Fails for left == 1, too.

Bj?rn

2007-05-08 12:41:43

by Lennart Poettering

[permalink] [raw]
Subject: Re: [PATCH] DMI-based module autoloading

On Mon, 07.05.07 18:27, Greg KH ([email protected]) wrote:

Hi!

> On Tue, May 08, 2007 at 02:54:29AM +0200, Lennart Poettering wrote:
> > To take advantage of DMI based module autoloading, a driver should
> > export one or more MODULE_ALIAS fields similar to these:
> >
> > MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
> > MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
> > MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
> > MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
>
> Is there any way to automatically generate these aliases (like we do for
> PCI and USB), in a way that is easier for the developer, instead of
> having to figure out how to create such a "wierd" string like these by
> hand?

Automatically? You mean by having some kind of macro that generates
the string from its parameters or some kind of structure and than use
"file2alias" to generate the alias lines automatically?

I am not sure if that is a good idea, because people really need to
clean up their strings manually before putting them into
MODULE_ALIAS. DMI data blocks usually contain a lot of rubbish. People should
really think twice when writing down those lines.

It's not that difficult to write these strings by hand, they are not
that weird. Also, on systems with this patch applied all you need to
do is run "cat /sys/class/dmi/id/modalias" to get the proper
MODULE_ALIAS string for your respective machine. Then, just replace
all rubbish with "*" and you are done.

If required I can hack up a simple script that generates the data from
dmidecode.

> > These lines are specific to my msi-laptop.c driver. They are basically
> > just a concatenation of a few carefully selected DMI fields with all
> > potentially bad characters stripped.
>
> What is a "bad character" here?

All characters <= 32 and >= 127, and ':'. The latter we use for
separating the DMI fields in the modalias strings, hence we don't want
the DMI data to use that character.

Lennart

--
Lennart Poettering; lennart [at] poettering [dot] net
ICQ# 11060553; GPG 0x1A015CC4; http://0pointer.net/lennart/