[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20110531155047.3462.78078.sendpatchset@prarit.bos.redhat.com>
Date: Tue, 31 May 2011 11:50:47 -0400
From: Prarit Bhargava <prarit@...hat.com>
To: linux-kernel@...r.kernel.org, linux-ia64@...r.kernel.org,
x86@...nel.org, linux-acpi@...r.kernel.org,
linux-ide@...r.kernel.org,
openipmi-developer@...ts.sourceforge.net,
platform-driver-x86@...r.kernel.org, linux-crypto@...r.kernel.org,
dri-devel@...ts.freedesktop.org, lm-sensors@...sensors.org,
linux-i2c@...r.kernel.org, linux-ide@...r.kernel.org,
linux-input@...r.kernel.org, linux-media@...r.kernel.org,
linux-mmc@...r.kernel.org, netdev@...r.kernel.org,
linux-pci@...r.kernel.org, rtc-linux@...glegroups.com,
evel@...verdev.osuosl.org, linux-usb@...r.kernel.org,
device-drivers-devel@...ckfin.uclinux.org,
linux-watchdog@...r.kernel.org
Cc: grant.likely@...retlab.ca, dz@...ian.org, rpurdie@...ys.net,
eric.piel@...mplin-utc.net, abelay@....edu, johnpol@....mipt.ru,
Prarit Bhargava <prarit@...hat.com>
Subject: [RFC PATCH 01/35] System Firmware Interface
This patch introduces a general System Firmware interface to the kernel, called
sysfw.
Inlcluded in this interface is the ability to search a standard set of fields,
sysfw_lookup(). The fields are currently based upon the x86 and ia64 SMBIOS
fields but exapandable to fields that other arches may introduce. Also
included is the ability to search and match against those fields, and run
a callback function against the matches, sysfw_callback().
Modify module code to use sysfw instead of old DMI interface.
---
drivers/firmware/Kconfig | 25 ++++
drivers/firmware/Makefile | 3 +-
drivers/firmware/sysfw-sysfs.c | 266 +++++++++++++++++++++++++++++++++++++++
drivers/firmware/sysfw.c | 163 ++++++++++++++++++++++++
include/linux/mod_devicetable.h | 62 +++++++++
include/linux/sysfw.h | 111 ++++++++++++++++
init/main.c | 3 +
scripts/mod/file2alias.c | 52 ++++----
8 files changed, 658 insertions(+), 27 deletions(-)
create mode 100644 drivers/firmware/sysfw-sysfs.c
create mode 100644 drivers/firmware/sysfw.c
create mode 100644 include/linux/sysfw.h
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index efba163..79a1b9d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -157,6 +157,31 @@ config SIGMA
If unsure, say N here. Drivers that need these helpers will select
this option automatically.
+config SYSTEM_FIRMWARE
+ bool "System Firmware Interface"
+ default y
+ help
+ Enables common System Firmware Interface to export system firmware
+ (SMBIOS, DMI, etc.) information to kernel and userspace.
+
+config SYSTEM_FIRMWARE_SYSFS
+ bool "Export System Firmware identification via sysfs to userspace"
+ depends on SYSTEM_FIRMWARE
+ help
+ Say Y here if you want to query system identification information
+ from userspace through /sys/class/sysfw/id/ or if you want
+ system firmware (sysfw) based module auto-loading.
+
+config SYSTEM_FIRMWARE_DMI_COMPAT
+ bool "Export dmi compatibility class in sysfs"
+ depends on SYSTEM_FIRMWARE
+ default y
+ help
+ This exposes /sys/class/dmi/* as a pointer to the sysfw class for
+ old software. This is purely a backwards compatability feature and
+ should not be used in new user space software. Please see
+ Documentation/ABI for details.
+
source "drivers/firmware/google/Kconfig"
endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 47338c9..41bc64a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_SIGMA) += sigma.o
-
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
+obj-$(CONFIG_SYSTEM_FIRMWARE) += sysfw.o
+obj-$(CONFIG_SYSTEM_FIRMWARE_SYSFS) += sysfw-sysfs.o
diff --git a/drivers/firmware/sysfw-sysfs.c b/drivers/firmware/sysfw-sysfs.c
new file mode 100644
index 0000000..9d68d2a
--- /dev/null
+++ b/drivers/firmware/sysfw-sysfs.c
@@ -0,0 +1,266 @@
+/*
+ * Export sysfs sysfw_field's to userspace
+ *
+ * Updated 2011, Prarit Bhargava, prarit@...hat.com
+ * Copyright 2007, Lennart Poettering
+ *
+ * Licensed under GPLv2
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sysfw.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/sysfw.h>
+
+struct sysfw_device_attribute {
+ struct device_attribute dev_attr;
+ int field;
+};
+#define to_sysfw_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct sysfw_device_attribute, dev_attr)
+
+static ssize_t sys_sysfw_field_show(struct device *dev,
+ struct device_attribute *attr,
+ char *page)
+{
+ int field = to_sysfw_dev_attr(attr)->field;
+ ssize_t len;
+ len = scnprintf(page, PAGE_SIZE, "%s\n", sysfw_lookup(field));
+ page[len-1] = '\n';
+ return len;
+}
+
+#define SYSFW_ATTR(_name, _mode, _show, _field) \
+ { .dev_attr = __ATTR(_name, _mode, _show, NULL), \
+ .field = _field }
+
+#define DEFINE_SYSFW_ATTR_WITH_SHOW(_name, _mode, _field) \
+static struct sysfw_device_attribute sys_sysfw_##_name##_attr = \
+ SYSFW_ATTR(_name, _mode, sys_sysfw_field_show, _field);
+
+DEFINE_SYSFW_ATTR_WITH_SHOW(bios_vendor, 0444, SYSFW_BIOS_VENDOR);
+DEFINE_SYSFW_ATTR_WITH_SHOW(bios_version, 0444, SYSFW_BIOS_VERSION);
+DEFINE_SYSFW_ATTR_WITH_SHOW(bios_date, 0444, SYSFW_BIOS_DATE);
+DEFINE_SYSFW_ATTR_WITH_SHOW(sys_vendor, 0444, SYSFW_SYS_VENDOR);
+DEFINE_SYSFW_ATTR_WITH_SHOW(product_name, 0444, SYSFW_PRODUCT_NAME);
+DEFINE_SYSFW_ATTR_WITH_SHOW(product_version, 0444, SYSFW_PRODUCT_VERSION);
+DEFINE_SYSFW_ATTR_WITH_SHOW(product_serial, 0400, SYSFW_PRODUCT_SERIAL);
+DEFINE_SYSFW_ATTR_WITH_SHOW(product_uuid, 0400, SYSFW_PRODUCT_UUID);
+DEFINE_SYSFW_ATTR_WITH_SHOW(board_vendor, 0444, SYSFW_BOARD_VENDOR);
+DEFINE_SYSFW_ATTR_WITH_SHOW(board_name, 0444, SYSFW_BOARD_NAME);
+DEFINE_SYSFW_ATTR_WITH_SHOW(board_version, 0444, SYSFW_BOARD_VERSION);
+DEFINE_SYSFW_ATTR_WITH_SHOW(board_serial, 0400, SYSFW_BOARD_SERIAL);
+DEFINE_SYSFW_ATTR_WITH_SHOW(board_asset_tag, 0444, SYSFW_BOARD_ASSET_TAG);
+DEFINE_SYSFW_ATTR_WITH_SHOW(chassis_vendor, 0444, SYSFW_CHASSIS_VENDOR);
+DEFINE_SYSFW_ATTR_WITH_SHOW(chassis_type, 0444, SYSFW_CHASSIS_TYPE);
+DEFINE_SYSFW_ATTR_WITH_SHOW(chassis_version, 0444, SYSFW_CHASSIS_VERSION);
+DEFINE_SYSFW_ATTR_WITH_SHOW(chassis_serial, 0400, SYSFW_CHASSIS_SERIAL);
+DEFINE_SYSFW_ATTR_WITH_SHOW(chassis_asset_tag, 0444, SYSFW_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)
+{
+ static const struct mafield {
+ const char *prefix;
+ int field;
+ } fields[] = {
+ { "bvn", SYSFW_BIOS_VENDOR },
+ { "bvr", SYSFW_BIOS_VERSION },
+ { "bd", SYSFW_BIOS_DATE },
+ { "svn", SYSFW_SYS_VENDOR },
+ { "pn", SYSFW_PRODUCT_NAME },
+ { "pvr", SYSFW_PRODUCT_VERSION },
+ { "rvn", SYSFW_BOARD_VENDOR },
+ { "rn", SYSFW_BOARD_NAME },
+ { "rvr", SYSFW_BOARD_VERSION },
+ { "cvn", SYSFW_CHASSIS_VENDOR },
+ { "ct", SYSFW_CHASSIS_TYPE },
+ { "cvr", SYSFW_CHASSIS_VERSION },
+ { NULL, SYSFW_NONE }
+ };
+
+ ssize_t l, left;
+ char *p;
+ const struct mafield *f;
+
+ strcpy(buffer, "sysfw");
+ p = buffer + 3; left = buffer_size - 4;
+
+ for (f = fields; f->prefix && left > 0; f++) {
+ const char *c;
+ char *t;
+
+ c = sysfw_lookup(f->field);
+ if (!c)
+ continue;
+
+ t = kmalloc(strlen(c) + 1, GFP_KERNEL);
+ if (!t)
+ break;
+ ascii_filter(t, c);
+ l = scnprintf(p, left, ":%s%s", f->prefix, t);
+ kfree(t);
+
+ p += l;
+ left -= l;
+ }
+
+ p[0] = ':';
+ p[1] = 0;
+
+ return p - buffer + 1;
+}
+
+static ssize_t sys_sysfw_modalias_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ ssize_t r;
+ r = get_modalias(page, PAGE_SIZE-1);
+ page[r] = '\n';
+ page[r+1] = 0;
+ return r+1;
+}
+
+static struct device_attribute sys_sysfw_modalias_attr =
+ __ATTR(modalias, 0444, sys_sysfw_modalias_show, NULL);
+
+static struct attribute *sys_sysfw_attributes[SYSFW_STRING_MAX+2];
+
+static struct attribute_group sys_sysfw_attribute_group = {
+ .attrs = sys_sysfw_attributes,
+};
+
+static const struct attribute_group *sys_sysfw_attribute_groups[] = {
+ &sys_sysfw_attribute_group,
+ NULL
+};
+
+static int sysfw_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ ssize_t len;
+
+ if (add_uevent_var(env, "MODALIAS="))
+ return -ENOMEM;
+ len = get_modalias(&env->buf[env->buflen - 1],
+ sizeof(env->buf) - env->buflen);
+ if (len >= (sizeof(env->buf) - env->buflen))
+ return -ENOMEM;
+ env->buflen += len;
+ return 0;
+}
+
+static struct class sysfw_class = {
+ .name = "sysfw",
+ .dev_release = (void(*)(struct device *)) kfree,
+ .dev_uevent = sysfw_dev_uevent,
+};
+
+/* Initialization */
+
+#define ADD_SYSFW_ATTR(_name, _field) \
+ if (sysfw_lookup(_field)) \
+ sys_sysfw_attributes[i++] = \
+ &sys_sysfw_##_name##_attr.dev_attr.attr;
+
+/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
+ sysfw_id_init! */
+static void __init sysfw_id_init_attr_table(void)
+{
+ int i;
+
+ /* Not necessarily all SYSFW fields are available on all
+ * systems, hence let's built an attribute table of just
+ * what's available */
+ i = 0;
+ ADD_SYSFW_ATTR(bios_vendor, SYSFW_BIOS_VENDOR);
+ ADD_SYSFW_ATTR(bios_version, SYSFW_BIOS_VERSION);
+ ADD_SYSFW_ATTR(bios_date, SYSFW_BIOS_DATE);
+ ADD_SYSFW_ATTR(sys_vendor, SYSFW_SYS_VENDOR);
+ ADD_SYSFW_ATTR(product_name, SYSFW_PRODUCT_NAME);
+ ADD_SYSFW_ATTR(product_version, SYSFW_PRODUCT_VERSION);
+ ADD_SYSFW_ATTR(product_serial, SYSFW_PRODUCT_SERIAL);
+ ADD_SYSFW_ATTR(product_uuid, SYSFW_PRODUCT_UUID);
+ ADD_SYSFW_ATTR(board_vendor, SYSFW_BOARD_VENDOR);
+ ADD_SYSFW_ATTR(board_name, SYSFW_BOARD_NAME);
+ ADD_SYSFW_ATTR(board_version, SYSFW_BOARD_VERSION);
+ ADD_SYSFW_ATTR(board_serial, SYSFW_BOARD_SERIAL);
+ ADD_SYSFW_ATTR(board_asset_tag, SYSFW_BOARD_ASSET_TAG);
+ ADD_SYSFW_ATTR(chassis_vendor, SYSFW_CHASSIS_VENDOR);
+ ADD_SYSFW_ATTR(chassis_type, SYSFW_CHASSIS_TYPE);
+ ADD_SYSFW_ATTR(chassis_version, SYSFW_CHASSIS_VERSION);
+ ADD_SYSFW_ATTR(chassis_serial, SYSFW_CHASSIS_SERIAL);
+ ADD_SYSFW_ATTR(chassis_asset_tag, SYSFW_CHASSIS_ASSET_TAG);
+ sys_sysfw_attributes[i++] = &sys_sysfw_modalias_attr.attr;
+}
+
+static struct device sysfw_dev;
+
+#ifdef CONFIG_SYSTEM_FIRMWARE_DMI_COMPAT
+static struct class_compat *dmi_compat_class;
+
+/* Hopefully someday we can get rid of this.*/
+static int __init sysfw_legacy_dmi(void)
+{
+ int ret;
+
+ dmi_compat_class = class_compat_register("dmi");
+ if (!dmi_compat_class)
+ return -ENOMEM;
+
+ ret = class_compat_create_link(dmi_compat_class, &sysfw_dev, NULL);
+ if (ret)
+ return ret;
+ return 0;
+}
+#else
+static int __init sysfw_legacy_dmi(void)
+{
+ return 0;
+}
+#endif
+
+static int __init sysfw_id_init(void)
+{
+ int ret;
+
+ sysfw_id_init_attr_table();
+
+ ret = class_register(&sysfw_class);
+ if (ret)
+ return ret;
+
+ sysfw_dev.class = &sysfw_class;
+ dev_set_name(&sysfw_dev, "id");
+ sysfw_dev.groups = sys_sysfw_attribute_groups;
+
+ ret = device_register(&sysfw_dev);
+ if (ret)
+ goto out;
+
+ /* Failing to setup legacy DMI is NOT a fatal error */
+ ret = sysfw_legacy_dmi();
+ if (ret)
+ printk(KERN_ERR "SYSFW: DMI Legacy sysfs did not "
+ "initialize.\n");
+
+ return 0;
+
+out:
+ class_unregister(&sysfw_class);
+
+ return ret;
+}
+
+arch_initcall(sysfw_id_init);
diff --git a/drivers/firmware/sysfw.c b/drivers/firmware/sysfw.c
new file mode 100644
index 0000000..bcf5737
--- /dev/null
+++ b/drivers/firmware/sysfw.c
@@ -0,0 +1,163 @@
+/*
+ * System Firmware (sysfw) support
+ *
+ * started by Prarit Bhargava, Copyright (C) 2011 Red Hat, Inc.
+ *
+ * SYSFW interface to export commonly used values from System Firmware
+ *
+ * Some bits copied directly from original x86 and ia64 dmi*.c files
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/sysfw.h>
+
+/* Global sysfw instance */
+static struct sysfw_driver *sysfw;
+
+/* register a firmware driver, no need for sysfw_driver_unregister() */
+/**
+ * sysfw_driver_register - register a firmware driver
+ * @driver: sysfw_driver struct representing driver to register
+ *
+ * This function registers a sysfw driver. Since the FW exists for the
+ * lifetime of the system there is no need for an unregister function.
+ */
+int __init sysfw_driver_register(struct sysfw_driver *driver)
+{
+ int ret;
+
+ if (!driver->init || !driver->name) {
+ WARN(1, "System Firmware Interface: driver error\n");
+ return -EFAULT;
+ }
+
+ sysfw = driver;
+ ret = sysfw->init();
+ if (ret)
+ return -ENODEV;
+
+ return 0;
+}
+
+/* called in start_kernel(), after memory management has been initialized. */
+void __init sysfw_init_late(void)
+{
+ if (sysfw && sysfw->late)
+ sysfw->late();
+}
+
+/**
+ * sysfw_lookup - find a sysfw value for a given field.
+ * @field: field to lookup
+ *
+ * This function calls into the system firmware and returns an appropriate
+ * string.
+ */
+const char *sysfw_lookup(int field)
+{
+ if (sysfw && sysfw->lookup)
+ return sysfw->lookup(field);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sysfw_lookup);
+
+/**
+ * sysfw_vendor_is - checks to see if the vendor fields in the firmware
+ * contain a string
+ * @str: string to search for
+ *
+ * Returns true/false if the string is in any of the SYSFW_*VENDOR* fields.
+ */
+bool sysfw_vendor_is(const char *str)
+{
+ static int fields[] = { SYSFW_BIOS_VENDOR, SYSFW_BIOS_VERSION,
+ SYSFW_SYS_VENDOR, SYSFW_PRODUCT_NAME,
+ SYSFW_PRODUCT_VERSION, SYSFW_BOARD_VENDOR,
+ SYSFW_BOARD_NAME, SYSFW_BOARD_VERSION,
+ SYSFW_NONE };
+ int i;
+
+ if (!sysfw)
+ return false;
+
+ if (!sysfw->lookup)
+ return false;
+
+ for (i = 0; fields[i] != SYSFW_NONE; i++) {
+ if (sysfw_lookup(i) && strstr(sysfw_lookup(i), str))
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(sysfw_vendor_is);
+
+/**
+ * sysfw_get_date - returns the date of the firmware in YYYYMMDD format.
+ *
+ * Retuns the date the firmware was built in YYYYMMDD for easy comparisons.
+ */
+int sysfw_get_date(void)
+{
+ if (sysfw && sysfw->date)
+ return sysfw->date();
+ return 0;
+}
+EXPORT_SYMBOL(sysfw_get_date);
+
+/* compares and matches the strings in sysfw_id */
+static bool sysfw_match_fields(const struct sysfw_id *id, int exactmatch)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(id->matches); i++) {
+ int s = id->matches[i].slot;
+ if (s == SYSFW_NONE)
+ break;
+ if (!sysfw_lookup(s))
+ continue;
+ if (exactmatch &&
+ !strcmp(sysfw_lookup(s), id->matches[i].substr))
+ continue;
+ if (!exactmatch &&
+ strstr(sysfw_lookup(s), id->matches[i].substr))
+ continue;
+ /* No match */
+ return false;
+ }
+ return true;
+}
+
+static bool sysfw_is_end_of_table(const struct sysfw_id *id)
+{
+ return id->matches[0].slot == SYSFW_NONE;
+}
+
+/**
+ * sysfw_callback - go through a list and run a function on all matching ids
+ * @list: list of ids to search
+ *
+ * This function takes a list of fields and strings to compare, attempts to
+ * compare the strings to the sysfw fields and runs a callback function for
+ * each positive comparison. The comparison can stop by returning 0 in
+ * the callback function.
+ */
+const struct sysfw_id *sysfw_callback(const struct sysfw_id *list)
+{
+ const struct sysfw_id *id;
+
+ if (!sysfw)
+ return NULL;
+
+ for (id = list; !sysfw_is_end_of_table(id); id++)
+ if (sysfw_match_fields(id, id->exactmatch)) {
+ if (id->callback)
+ id->callback(id);
+ return id;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(sysfw_callback);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index ae28e93..a206188 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -482,6 +482,68 @@ struct dmi_system_id {
#define DMI_MATCH(a, b) { a, b }
+/*
+ * sysfw field - right now these are based on x86/ia64's SMBIOS fields. But
+ * other arches are certainly welcome to add additional fields. The
+ * sys_lookup() functions must be careful to return NULL on non-populated
+ * fields.
+ */
+enum sysfw_field {
+ SYSFW_NONE,
+ SYSFW_BIOS_VENDOR,
+ SYSFW_BIOS_VERSION,
+ SYSFW_BIOS_DATE,
+ SYSFW_SYS_VENDOR,
+ SYSFW_PRODUCT_NAME,
+ SYSFW_PRODUCT_VERSION,
+ SYSFW_PRODUCT_SERIAL,
+ SYSFW_PRODUCT_UUID,
+ SYSFW_BOARD_VENDOR,
+ SYSFW_BOARD_NAME,
+ SYSFW_BOARD_VERSION,
+ SYSFW_BOARD_SERIAL,
+ SYSFW_BOARD_ASSET_TAG,
+ SYSFW_CHASSIS_VENDOR,
+ SYSFW_CHASSIS_TYPE,
+ SYSFW_CHASSIS_VERSION,
+ SYSFW_CHASSIS_SERIAL,
+ SYSFW_CHASSIS_ASSET_TAG,
+ SYSFW_STRING_MAX,
+};
+
+struct sysfw_strmatch {
+ unsigned char slot;
+ char substr[79];
+};
+
+#ifndef __KERNEL__
+struct sysfw_id {
+ kernel_ulong_t callback;
+ kernel_ulong_t ident;
+ struct sysfw_strmatch matches[4];
+ kernel_ulong_t driver_data
+ __attribute__((aligned(sizeof(kernel_ulong_t))));
+ kernel_ulong_t exactmatch;
+};
+#else
+struct sysfw_id {
+ int (*callback)(const struct sysfw_id *);
+ const char *ident;
+ struct sysfw_strmatch matches[4];
+ void *driver_data;
+ kernel_ulong_t exactmatch;
+};
+/*
+ * struct sysfw_device_id appears during expansion of
+ * "MODULE_DEVICE_TABLE(sysfw, x)". Compiler doesn't look inside it
+ * but this is enough for gcc 3.4.6 to error out:
+ * error: storage size of '__mod_sysfw_device_table' isn't known
+ */
+#define sysfw_device_id sysfw_system_id
+#endif
+
+#define SYSFW_MATCH(a, b) { a, b }
+
#define PLATFORM_NAME_SIZE 20
#define PLATFORM_MODULE_PREFIX "platform:"
diff --git a/include/linux/sysfw.h b/include/linux/sysfw.h
new file mode 100644
index 0000000..06a0c8c
--- /dev/null
+++ b/include/linux/sysfw.h
@@ -0,0 +1,111 @@
+/*
+ * include/linux/sysfw.h
+ *
+ * System Firmware (sysfw) Interface
+ */
+#ifndef _SYSFW_H
+#define _SYSFW_H
+
+#include <linux/mod_devicetable.h>
+
+/*
+ * sysfw_id and sysfw_field (the SYSFW_*) enums are defined in
+ * mod_devicetable.h
+ */
+
+/*
+ * sysfw_driver struct -- passed in to sysfw_init() by every firmware
+ * driver
+ */
+struct sysfw_driver {
+ const char *name;
+ /* find & evaluate firmware */
+ int (*init)(void);
+ /* late init, done after memory management is initialized */
+ int (*late)(void);
+ /* find a specific value in sysfw_field */
+ const char * (*lookup)(int field);
+ /* return date in YYYYMMDD format */
+ int (*date)(void);
+};
+
+#ifdef CONFIG_SYSTEM_FIRMWARE
+/**
+ * sysfw_driver_register - register a firmware driver
+ * @driver: sysfw_driver struct representing driver to register
+ *
+ * This function registers a sysfw driver. Since the FW exists for the
+ * lifetime of the system there is no need for an unregister function.
+ */
+extern int sysfw_driver_register(struct sysfw_driver *driver);
+
+/**
+ * sysfw_lookup - find a sysfw value for a given field.
+ * @field: field to lookup
+ *
+ * This function calls into the system firmware and returns an appropriate
+ * string.
+ */
+extern const char *sysfw_lookup(int field);
+
+/**
+ * sysfw_get_date - returns the date of the firmware in YYYYMMDD format.
+ *
+ * Retuns the date the firmware was built in YYYYMMDD for easy comparisons.
+ */
+extern int sysfw_get_date(void);
+
+/**
+ * sysfw_vendor_is - checks to see if the vendor fields in the firmware
+ * contain a string
+ * @str: string to search for
+ *
+ * Returns true/false if the string is in any of the SYSFW_*VENDOR* fields.
+ */
+extern bool sysfw_vendor_is(const char *str);
+
+/**
+ * sysfw_callback - go through a list and run a function on all matching ids
+ * @list: list of ids to search
+ *
+ * This function takes a list of fields and strings to compare, attempts to
+ * compare the strings to the sysfw fields and runs a callback function for
+ * each positive comparison. The comparison can stop by returning 0 in
+ * the callback function. The function returns the first match in the list.
+ */
+extern const struct sysfw_id *sysfw_callback(const struct sysfw_id *list);
+
+/* Kernel internal only, run after memory management has been initialized */
+extern void sysfw_init_late(void);
+
+#else /* CONFIG_SYSTEM_FIRMWARE */
+
+static inline int sysfw_driver_register(struct sysfw_driver *driver)
+{
+ return -1;
+}
+static inline const char *sysfw_lookup(int field)
+{
+ return NULL;
+}
+static inline int sysfw_get_date(void)
+{
+ return 0;
+}
+static inline bool sysfw_vendor_is(const char *str)
+{
+ return false;
+}
+static const struct sysfw_id *sysfw_callback(const struct sysfw_id *list)
+{
+ return NULL;
+}
+static inline void sysfw_init_late(void)
+{
+ return;
+}
+#endif /* CONFIG_SYSTEM_FIRMWARE */
+
+#define sysfw_vendor_contains(str) sysfw_vendor_is(str)
+
+#endif /* _SYSFW_H */
diff --git a/init/main.c b/init/main.c
index cafba67..68e1136 100644
--- a/init/main.c
+++ b/init/main.c
@@ -68,6 +68,7 @@
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
+#include <linux/sysfw.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -553,6 +554,8 @@ asmlinkage void __init start_kernel(void)
kmem_cache_init_late();
+ sysfw_init_late();
+
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index e26e2fb..92a27e3 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -753,26 +753,26 @@ static int do_spi_entry(const char *filename, struct spi_device_id *id,
return 1;
}
-static const struct dmifield {
+static const struct sysfwfield {
const char *prefix;
int field;
-} dmi_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 }
+} sysfw_fields[] = {
+ { "bvn", SYSFW_BIOS_VENDOR },
+ { "bvr", SYSFW_BIOS_VERSION },
+ { "bd", SYSFW_BIOS_DATE },
+ { "svn", SYSFW_SYS_VENDOR },
+ { "pn", SYSFW_PRODUCT_NAME },
+ { "pvr", SYSFW_PRODUCT_VERSION },
+ { "rvn", SYSFW_BOARD_VENDOR },
+ { "rn", SYSFW_BOARD_NAME },
+ { "rvr", SYSFW_BOARD_VERSION },
+ { "cvn", SYSFW_CHASSIS_VENDOR },
+ { "ct", SYSFW_CHASSIS_TYPE },
+ { "cvr", SYSFW_CHASSIS_VERSION },
+ { NULL, SYSFW_NONE }
};
-static void dmi_ascii_filter(char *d, const char *s)
+static void sysfw_ascii_filter(char *d, const char *s)
{
/* Filter out characters we don't want to see in the modalias string */
for (; *s; s++)
@@ -783,20 +783,20 @@ static void dmi_ascii_filter(char *d, const char *s)
}
-static int do_dmi_entry(const char *filename, struct dmi_system_id *id,
- char *alias)
+static int do_sysfw_entry(const char *filename, struct sysfw_id *id,
+ char *alias)
{
int i, j;
- sprintf(alias, "dmi*");
+ sprintf(alias, "sysfw*");
- for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) {
+ for (i = 0; i < ARRAY_SIZE(sysfw_fields); i++) {
for (j = 0; j < 4; j++) {
if (id->matches[j].slot &&
- id->matches[j].slot == dmi_fields[i].field) {
+ id->matches[j].slot == sysfw_fields[i].field) {
sprintf(alias + strlen(alias), ":%s*",
- dmi_fields[i].prefix);
- dmi_ascii_filter(alias + strlen(alias),
+ sysfw_fields[i].prefix);
+ sysfw_ascii_filter(alias + strlen(alias),
id->matches[j].substr);
strcat(alias, "*");
}
@@ -1002,10 +1002,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
do_table(symval, sym->st_size,
sizeof(struct spi_device_id), "spi",
do_spi_entry, mod);
- else if (sym_is(symname, "__mod_dmi_device_table"))
+ else if (sym_is(symname, "__mod_sysfw_device_table"))
do_table(symval, sym->st_size,
- sizeof(struct dmi_system_id), "dmi",
- do_dmi_entry, mod);
+ sizeof(struct sysfw_id), "sysfw",
+ do_sysfw_entry, mod);
else if (sym_is(symname, "__mod_platform_device_table"))
do_table(symval, sym->st_size,
sizeof(struct platform_device_id), "platform",
--
1.7.5.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists