DMI-based module autoloading
Subsystems (2)
PATCH 1/1 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
--- 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(-)
drivers/firmware/Kconfig +9 -0
@@ -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
drivers/firmware/dmi-id.c +227 -0
227 lines not on this page · raw message
drivers/firmware/Makefile +1 -0
@@ -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
drivers/firmware/dmi_scan.c +67 -6
119 lines not on this page · raw message
drivers/misc/msi-laptop.c +41 -3
75 lines not on this page · raw message
include/linux/dmi.h +8 -0
@@ -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, };
Quoted 7 lines · Lennart Poettering
To take advantage of DMI based module autoloading, a driver should export one or more
MODULE_ALIASfields 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?
Quoted 3 lines
These lines are specific to my
msi-laptop.cdriver. 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
Quoted 2 lines · Lennart Poettering
From: Lennart Poettering <[email protected]>
+#defineDEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field)\
Hm, too many tabs here? Wraps for me with tabsize=8.
drivers/firmware/dmi-id.c line 31
Quoted 9 lines
drivers/firmware/dmi-id.c +9
-0+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;
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+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 );
drivers/firmware/dmi-id.c line 51
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+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.
Quoted 7 lines
drivers/firmware/dmi-id.c +7
-0+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; +
drivers/firmware/dmi-id.c line 69
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+ *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)
Quoted 4 lines
drivers/firmware/dmi-id.c +4
-0+static const struct mafield fields[] = { + {"bvn" ,DMI_BIOS_VENDOR }, + {"bvr" ,DMI_BIOS_VERSION }, + {"bd" ,DMI_BIOS_DATE },
drivers/firmware/dmi-id.c line 83
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+ { "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.
Quoted 12 lines
drivers/firmware/dmi-id.c +12
-0+ }; + +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;
drivers/firmware/dmi-id.c line 105
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+ + 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.
drivers/firmware/dmi-id.c line 115
Quoted 10 lines
drivers/firmware/dmi-id.c +10
-0+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
On Mon, 07.05.07 18:27, Greg KH ([email protected]) wrote:
Hi!
Quoted 9 lines · Lennart Poettering
Quoted 7 lines
To take advantage of DMI based module autoloading, a driver should export one or more
MODULE_ALIASfields 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:*");
Quoted 4 lines
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.
Quoted 5 lines
Quoted 3 lines
These lines are specific to my
msi-laptop.cdriver. 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
Signature · 2 lines
Lennart Poettering; lennart [at] poettering [dot] net ICQ# 11060553; GPG 0x1A015CC4; http://0pointer.net/lennart/