All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Torokhov <dtor_core@ameritech.net>
To: Frank Sorenson <frank@tuxrocks.com>
Cc: LKML <linux-kernel@vger.kernel.org>, Valdis.Kletnieks@vt.edu
Subject: Re: [PATCH 0/5] I8K driver facelift
Date: Mon, 21 Mar 2005 00:12:29 -0500	[thread overview]
Message-ID: <200503210012.29477.dtor_core@ameritech.net> (raw)
In-Reply-To: <423951E3.6070804@tuxrocks.com>

[-- Attachment #1: Type: text/plain, Size: 1043 bytes --]

Hi,

On Thursday 17 March 2005 04:46, Frank Sorenson wrote:
> Dmitry Torokhov wrote:
> | Hrm, can we be a little more explicit and not poke in the sysfs guts right
> | in the driver? What do you think about the patch below athat implements
> | "attribute arrays"? And I am attaching cumulative i8k patch using these
> | arrays so they can be tested.
> 
> Also, with power_status being a module parameter defaulting to 0/off, we
> can leave out the device_create_file for the i8k_power_status_attr.  No
> need to expose it in sysfs if it will always return -EIO.
>

Since root can toggle power_status through sysfs at runtime (see
/sys/modules/i8k/parameters/power_status) I think it is simplier to have
the file always created. 

I have implemented arrays of groups of attributes:

[dtor@core ~]$ ls -R /sys/bus/platform/devices/i8k/fan/
/sys/bus/platform/devices/i8k/fan/:
0  1

/sys/bus/platform/devices/i8k/fan/0:
speed  state

/sys/bus/platform/devices/i8k/fan/1:
speed  state

Please give it a try when you have time. Thanks!

-- 
Dmitry

[-- Attachment #2: attr-array.patch --]
[-- Type: text/x-diff, Size: 6439 bytes --]

===================================================================

sysfs: add support for attribute arrays so it is possible to create
       a number of similar attributes all sharing the same show and
       store handlers:

          ../<attr_name>/0
          ../<attr_name>/1
               ...
          ../<attr_name>/<n>

       Number of attributes can be specified at run-time.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>


 fs/sysfs/Makefile     |    2 
 fs/sysfs/array.c      |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sysfs.h |   20 +++++-
 3 files changed, 182 insertions(+), 2 deletions(-)

Index: dtor/fs/sysfs/array.c
===================================================================
--- /dev/null
+++ dtor/fs/sysfs/array.c
@@ -0,0 +1,162 @@
+/*
+ * fs/sysfs/array.c - Operations for adding/removing arrays of attributes.
+ *
+ * Copyright (c) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * This file is released under the GPL v2.
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include "sysfs.h"
+
+struct attr_element {
+	struct attribute attr;
+	unsigned int idx;
+	char name[4];
+};
+#define to_attr_element(e)	container_of(e, struct attr_element, attr)
+
+struct attr_array {
+	const struct enumerated_attr *base_attr;
+	unsigned int n_elements;
+	struct kobject kobj;
+	struct attr_element elements[0];
+};
+#define to_attr_array(obj)	container_of(obj, struct attr_array, kobj)
+
+static ssize_t
+attr_array_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct attr_element *element = to_attr_element(attr);
+	struct attr_array *array = to_attr_array(kobj);
+	ssize_t ret = 0;
+
+	if (array->base_attr->show)
+		ret = array->base_attr->show(kobj->parent, element->idx, buf);
+
+	return ret;
+}
+
+static ssize_t
+attr_array_store(struct kobject *kobj, struct attribute *attr,
+		 const char *buf, size_t count)
+{
+	struct attr_element *element = to_attr_element(attr);
+	struct attr_array *array = to_attr_array(kobj);
+	ssize_t ret = 0;
+
+	if (array->base_attr->store)
+		ret = array->base_attr->store(kobj->parent, element->idx, buf, count);
+
+	return ret;
+}
+
+static struct sysfs_ops attr_array_sysfs_ops = {
+	.show	= attr_array_show,
+	.store	= attr_array_store,
+};
+
+static void attr_array_release(struct kobject *kobj)
+{
+	kfree(to_attr_array(kobj));
+}
+
+static struct kobj_type ktype_attr_array = {
+	.release	= attr_array_release,
+	.sysfs_ops	= &attr_array_sysfs_ops,
+};
+
+
+static int create_array_element(struct attr_array *array,
+				unsigned int idx)
+{
+	struct attr_element *element = &array->elements[idx];
+
+	snprintf(element->name, sizeof(element->name), "%d", idx);
+	element->idx = idx;
+	element->attr = array->base_attr->attr;
+	element->attr.name = element->name;
+
+	return sysfs_create_file(&array->kobj, &element->attr);
+}
+
+static inline void remove_array_element(struct attr_array *array,
+					unsigned int idx)
+{
+	sysfs_remove_file(&array->kobj, &array->elements[idx].attr);
+}
+
+int sysfs_create_array(struct kobject *kobj,
+		       const struct enumerated_attr *attr,
+		       unsigned int n_elements)
+{
+	struct attr_array *array;
+	size_t size;
+	unsigned int i;
+	int error;
+
+	BUG_ON(!kobj || !kobj->dentry);
+	BUG_ON(!attr_name(*attr));
+
+	if (n_elements > 999)
+		return -EINVAL;
+
+	size = sizeof(struct attr_array) +
+	       sizeof(struct attr_element) * n_elements;
+
+	array = kmalloc(size, GFP_KERNEL);
+	if (!array)
+		return -ENOMEM;
+
+	memset(array, 0, size);
+	array->base_attr = attr;
+	array->n_elements = n_elements;
+
+	array->kobj.ktype = &ktype_attr_array;
+	array->kobj.parent = kobj;
+	kobject_set_name(&array->kobj, attr_name(*attr));
+
+	error = kobject_register(&array->kobj);
+	if (error)
+		goto fail;
+
+	for (i = 0; i < n_elements; i++) {
+		error = create_array_element(array, i);
+		if (error) {
+			while (--i)
+				remove_array_element(array, i);
+			goto fail;
+		}
+	}
+
+	return 0;
+
+ fail:
+	kfree(array);
+	return error;
+}
+
+void sysfs_remove_array(struct kobject *kobj,
+			const struct enumerated_attr *attr)
+{
+	struct attr_array *array;
+	struct dentry *dir;
+	unsigned int i;
+
+	dir = sysfs_get_dentry(kobj->dentry, attr_name(*attr));
+
+	if (dir) {
+		array = to_attr_array(to_kobj(dir));
+		for (i = 0; i < array->n_elements; i++)
+			remove_array_element(array, i);
+		kobject_unregister(&array->kobj);
+	}
+
+	dput(dir);
+}
+
+EXPORT_SYMBOL_GPL(sysfs_create_array);
+EXPORT_SYMBOL_GPL(sysfs_remove_array);
Index: dtor/include/linux/sysfs.h
===================================================================
--- dtor.orig/include/linux/sysfs.h
+++ dtor/include/linux/sysfs.h
@@ -26,7 +26,12 @@ struct attribute_group {
 	struct attribute	** attrs;
 };
 
+struct enumerated_attr {
+	struct attribute	attr;
 
+	ssize_t	(*show)(struct kobject *, unsigned int index, char *);
+	ssize_t	(*store)(struct kobject *, unsigned int index, const char *, size_t);
+};
 
 /**
  * Use these macros to make defining attributes easier. See include/linux/device.h
@@ -102,7 +107,7 @@ sysfs_update_file(struct kobject *, cons
 extern void
 sysfs_remove_file(struct kobject *, const struct attribute *);
 
-extern int 
+extern int
 sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
 
 extern void
@@ -114,6 +119,9 @@ int sysfs_remove_bin_file(struct kobject
 int sysfs_create_group(struct kobject *, const struct attribute_group *);
 void sysfs_remove_group(struct kobject *, const struct attribute_group *);
 
+int sysfs_create_array(struct kobject *, const struct enumerated_attr *, unsigned int);
+void sysfs_remove_array(struct kobject *, const struct enumerated_attr *);
+
 #else /* CONFIG_SYSFS */
 
 static inline int sysfs_create_dir(struct kobject * k)
@@ -177,6 +185,16 @@ static inline void sysfs_remove_group(st
 	;
 }
 
+static inline int sysfs_create_array(struct kobject * k, const struct enumerated_attr * a, unsigned int n)
+{
+	return 0;
+}
+
+static inline void sysfs_remove_array(struct kobject * k, const struct enumerated_attr * a)
+{
+	;
+}
+
 #endif /* CONFIG_SYSFS */
 
 #endif /* _SYSFS_H_ */
Index: dtor/fs/sysfs/Makefile
===================================================================
--- dtor.orig/fs/sysfs/Makefile
+++ dtor/fs/sysfs/Makefile
@@ -3,4 +3,4 @@
 #
 
 obj-y		:= inode.o file.o dir.o symlink.o mount.o bin.o \
-		   group.o
+		   group.o array.o

[-- Attachment #3: attr-group-array.patch --]
[-- Type: text/x-diff, Size: 7263 bytes --]

===================================================================

sysfs: add support for arrays of groups of attributes:

          ../<group_name>/0/<attr_a>
          ../<group_name>/0/<attr_b>
          ../<group_name>/1/<attr_a>
          ../<group_name>/1/<attr_b>
                 ...
          ../<group_name>/<n>/<attr_a>
          ../<group_name>/<n>/<attr_b>

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>


 fs/sysfs/Makefile      |    2 
 fs/sysfs/group-array.c |  206 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sysfs.h  |   18 ++++
 3 files changed, 225 insertions(+), 1 deletion(-)

Index: dtor/fs/sysfs/Makefile
===================================================================
--- dtor.orig/fs/sysfs/Makefile
+++ dtor/fs/sysfs/Makefile
@@ -3,4 +3,4 @@
 #
 
 obj-y		:= inode.o file.o dir.o symlink.o mount.o bin.o \
-		   group.o array.o
+		   group.o array.o group-array.o
Index: dtor/include/linux/sysfs.h
===================================================================
--- dtor.orig/include/linux/sysfs.h
+++ dtor/include/linux/sysfs.h
@@ -33,6 +33,11 @@ struct enumerated_attr {
 	ssize_t	(*store)(struct kobject *, unsigned int index, const char *, size_t);
 };
 
+struct attribute_group_array {
+	char			* name;
+	struct enumerated_attr	* attrs;
+};
+
 /**
  * Use these macros to make defining attributes easier. See include/linux/device.h
  * for examples..
@@ -122,6 +127,9 @@ void sysfs_remove_group(struct kobject *
 int sysfs_create_array(struct kobject *, const struct enumerated_attr *, unsigned int);
 void sysfs_remove_array(struct kobject *, const struct enumerated_attr *);
 
+int sysfs_create_group_array(struct kobject *, const struct attribute_group_array *, unsigned int);
+void sysfs_remove_group_array(struct kobject *, const struct attribute_group_array *);
+
 #else /* CONFIG_SYSFS */
 
 static inline int sysfs_create_dir(struct kobject * k)
@@ -195,6 +203,16 @@ static inline void sysfs_remove_array(st
 	;
 }
 
+static inline int sysfs_create_group_array(struct kobject * k, const struct attribute_group_array * a, unsigned int n)
+{
+	return 0;
+}
+
+static inline void sysfs_remove_group_array(struct kobject * k, const struct attribute_group_array * a)
+{
+	;
+}
+
 #endif /* CONFIG_SYSFS */
 
 #endif /* _SYSFS_H_ */
Index: dtor/fs/sysfs/group-array.c
===================================================================
--- /dev/null
+++ dtor/fs/sysfs/group-array.c
@@ -0,0 +1,206 @@
+/*
+ * fs/sysfs/group-array.c - Operations for adding/removing arrays of groups
+ *			    of attributes.
+ *
+ * Copyright (c) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * This file is released under the GPL v2.
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include "sysfs.h"
+
+#define to_enumerated_attr(obj)	container_of(obj, struct enumerated_attr, attr)
+
+struct attr_group {
+	struct kobject kobj;
+	struct kobject *attr_owner;
+	unsigned int idx;
+};
+#define to_attr_group(obj)	container_of(obj, struct attr_group, kobj)
+
+struct attr_array {
+	struct kobject kobj;
+	unsigned int n_elements;
+	struct enumerated_attr *attrs;
+};
+#define to_attr_array(obj)	container_of(obj, struct attr_array, kobj)
+
+static ssize_t
+attr_group_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct enumerated_attr *eattr = to_enumerated_attr(attr);
+	struct attr_group *group = to_attr_group(kobj);
+	ssize_t ret = 0;
+
+	if (eattr->show)
+		ret = eattr->show(group->attr_owner, group->idx, buf);
+
+	return ret;
+}
+
+static ssize_t
+attr_group_store(struct kobject *kobj, struct attribute *attr,
+		 const char *buf, size_t count)
+{
+	struct enumerated_attr *eattr = to_enumerated_attr(attr);
+	struct attr_group *group = to_attr_group(kobj);
+	ssize_t ret = 0;
+
+	if (eattr->store)
+		ret = eattr->store(group->attr_owner, group->idx, buf, count);
+
+	return ret;
+}
+
+static struct sysfs_ops attr_group_sysfs_ops = {
+	.show	= attr_group_show,
+	.store	= attr_group_store,
+};
+
+static void attr_group_release(struct kobject *kobj)
+{
+	struct attr_group *group = to_attr_group(kobj);
+
+	kobject_put(group->attr_owner);
+	kfree(group);
+}
+
+static struct kobj_type ktype_attr_group = {
+	.release	= attr_group_release,
+	.sysfs_ops	= &attr_group_sysfs_ops,
+};
+
+static void attr_array_release(struct kobject *kobj)
+{
+	kfree(to_attr_array(kobj));
+}
+
+static struct kobj_type ktype_attr_array = {
+	.release	= attr_array_release,
+};
+
+static int create_group(struct attr_array *array, unsigned int idx)
+{
+	struct attr_group *group;
+	struct enumerated_attr *eattr, *eattr2;
+	int err;
+
+	group = kmalloc(sizeof(struct attr_group), GFP_KERNEL);
+	if (!group)
+		return -ENOMEM;
+
+	memset(group, 0, sizeof(struct attr_group));
+	kobject_set_name(&group->kobj, "%d", idx);
+	group->kobj.ktype = &ktype_attr_group;
+	group->kobj.parent = &array->kobj;
+	group->attr_owner = kobject_get(array->kobj.parent);
+	group->idx = idx;
+
+	err = kobject_register(&group->kobj);
+	if (err)
+		goto fail1;
+
+	for (eattr = array->attrs; attr_name(*eattr); eattr++) {
+		err = sysfs_create_file(&group->kobj, &eattr->attr);
+		if (err)
+			goto fail2;
+	}
+
+	return 0;
+
+ fail2:	for (eattr2 = array->attrs; eattr2 != eattr; eattr2++)
+		sysfs_remove_file(&group->kobj, &eattr2->attr);
+ fail1:	kfree(group);
+	return err;
+}
+
+static void remove_group(struct attr_array *array, unsigned int idx)
+{
+	struct attr_group *group;
+	struct enumerated_attr *eattr;
+	struct dentry *dir;
+	char name[4];
+
+	snprintf(name, sizeof(name), "%d", idx);
+	dir = sysfs_get_dentry(array->kobj.dentry, name);
+	if (dir) {
+		group = to_attr_group(to_kobj(dir));
+		for (eattr = array->attrs; attr_name(*eattr); eattr++)
+			sysfs_remove_file(&group->kobj, &eattr->attr);
+		kobject_unregister(&group->kobj);
+	}
+
+	dput(dir);
+}
+
+int sysfs_create_group_array(struct kobject *kobj,
+			     const struct attribute_group_array *a,
+			     unsigned int n_elements)
+{
+	struct attr_array *array;
+	unsigned int i;
+	int error;
+
+	BUG_ON(!kobj || !kobj->dentry);
+	BUG_ON(!a->name);
+
+	if (n_elements > 999)
+		return -EINVAL;
+
+	array = kmalloc(sizeof(struct attr_array), GFP_KERNEL);
+	if (!array)
+		return -ENOMEM;
+
+	memset(array, 0, sizeof(struct attr_array));
+	array->attrs = a->attrs;
+	array->n_elements = n_elements;
+
+	array->kobj.ktype = &ktype_attr_array;
+	array->kobj.parent = kobj;
+	kobject_set_name(&array->kobj, a->name);
+
+	error = kobject_register(&array->kobj);
+	if (error)
+		goto fail;
+
+	for (i = 0; i < n_elements; i++) {
+		error = create_group(array, i);
+		if (error) {
+			while (--i)
+				remove_group(array, i);
+			goto fail;
+		}
+	}
+
+	return 0;
+
+ fail:
+	kfree(array);
+	return error;
+}
+
+void sysfs_remove_group_array(struct kobject *kobj,
+			      const struct attribute_group_array *a)
+{
+	struct attr_array *array;
+	struct dentry *dir;
+	unsigned int i;
+
+	dir = sysfs_get_dentry(kobj->dentry, a->name);
+
+	if (dir) {
+		array = to_attr_array(to_kobj(dir));
+		for (i = 0; i < array->n_elements; i++)
+			remove_group(array, i);
+		kobject_unregister(&array->kobj);
+	}
+
+	dput(dir);
+}
+
+EXPORT_SYMBOL_GPL(sysfs_create_group_array);
+EXPORT_SYMBOL_GPL(sysfs_remove_group_array);

[-- Attachment #4: i8k-cumulative.patch --]
[-- Type: text/x-diff, Size: 32614 bytes --]

 Documentation/kernel-parameters.txt |    3 
 arch/i386/kernel/dmi_scan.c         |    1 
 drivers/char/i8k.c                  | 1005 +++++++++++++++---------------------
 drivers/char/misc.c                 |    4 
 include/linux/dmi.h                 |    1 
 5 files changed, 444 insertions(+), 570 deletions(-)

Index: dtor/arch/i386/kernel/dmi_scan.c
===================================================================
--- dtor.orig/arch/i386/kernel/dmi_scan.c
+++ dtor/arch/i386/kernel/dmi_scan.c
@@ -416,6 +416,7 @@ static void __init dmi_decode(struct dmi
 			dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
 			dmi_printk(("Serial Number: %s\n",
 				dmi_string(dm, data[7])));
+			dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
 			break;
 		case 2:
 			dmi_printk(("Board Vendor: %s\n",
Index: dtor/Documentation/kernel-parameters.txt
===================================================================
--- dtor.orig/Documentation/kernel-parameters.txt
+++ dtor/Documentation/kernel-parameters.txt
@@ -545,6 +545,9 @@ running once the system is up.
 
 	i810=		[HW,DRM]
 
+	i8k.ignore_dmi	[HW] Continue probing hardware even if DMI data
+			indicates that the driver is running on unsupported
+			hardware.
 	i8k.force	[HW] Activate i8k driver even if SMM BIOS signature
 			does not match list of supported models.
 	i8k.power_status
Index: dtor/drivers/char/misc.c
===================================================================
--- dtor.orig/drivers/char/misc.c
+++ dtor/drivers/char/misc.c
@@ -67,7 +67,6 @@ extern int rtc_DP8570A_init(void);
 extern int rtc_MK48T08_init(void);
 extern int pmu_device_init(void);
 extern int tosh_init(void);
-extern int i8k_init(void);
 
 #ifdef CONFIG_PROC_FS
 static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
@@ -317,9 +316,6 @@ static int __init misc_init(void)
 #ifdef CONFIG_TOSHIBA
 	tosh_init();
 #endif
-#ifdef CONFIG_I8K
-	i8k_init();
-#endif
 	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
 		printk("unable to get major %d for misc devices\n",
 		       MISC_MAJOR);
Index: dtor/include/linux/dmi.h
===================================================================
--- dtor.orig/include/linux/dmi.h
+++ dtor/include/linux/dmi.h
@@ -9,6 +9,7 @@ enum dmi_field {
 	DMI_SYS_VENDOR,
 	DMI_PRODUCT_NAME,
 	DMI_PRODUCT_VERSION,
+	DMI_PRODUCT_SERIAL,
 	DMI_BOARD_VENDOR,
 	DMI_BOARD_NAME,
 	DMI_BOARD_VERSION,
Index: dtor/drivers/char/i8k.c
===================================================================
--- dtor.orig/drivers/char/i8k.c
+++ dtor/drivers/char/i8k.c
@@ -20,13 +20,15 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
-#include <linux/apm_bios.h>
+#include <linux/seq_file.h>
+#include <linux/dmi.h>
+#include <linux/device.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
 #include <linux/i8k.h>
 
-#define I8K_VERSION		"1.13 14/05/2002"
+#define I8K_VERSION		"1.14 21/02/2005"
 
 #define I8K_SMM_FN_STATUS	0x0025
 #define I8K_SMM_POWER_STATUS	0x0069
@@ -34,7 +36,8 @@
 #define I8K_SMM_GET_FAN		0x00a3
 #define I8K_SMM_GET_SPEED	0x02a3
 #define I8K_SMM_GET_TEMP	0x10a3
-#define I8K_SMM_GET_DELL_SIG	0xffa3
+#define I8K_SMM_GET_DELL_SIG1	0xfea3
+#define I8K_SMM_GET_DELL_SIG2	0xffa3
 #define I8K_SMM_BIOS_VERSION	0x00a6
 
 #define I8K_FAN_MULT		30
@@ -52,18 +55,7 @@
 
 #define I8K_TEMPERATURE_BUG	1
 
-#define DELL_SIGNATURE		"Dell Computer"
-
-static char *supported_models[] = {
-    "Inspiron",
-    "Latitude",
-    NULL
-};
-
-static char system_vendor[48] = "?";
-static char product_name [48] = "?";
-static char bios_version [4]  = "?";
-static char serial_number[16] = "?";
+static char bios_version[4];
 
 MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
 MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@@ -73,6 +65,10 @@ static int force;
 module_param(force, bool, 0);
 MODULE_PARM_DESC(force, "Force loading without checking for supported models");
 
+static int ignore_dmi;
+module_param(ignore_dmi, bool, 0);
+MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
+
 static int restricted;
 module_param(restricted, bool, 0);
 MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
@@ -81,69 +77,76 @@ static int power_status;
 module_param(power_status, bool, 0600);
 MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
 
-static ssize_t i8k_read(struct file *, char __user *, size_t, loff_t *);
+static int i8k_open_fs(struct inode *inode, struct file *file);
 static int i8k_ioctl(struct inode *, struct file *, unsigned int,
 		     unsigned long);
 
 static struct file_operations i8k_fops = {
-    .read	= i8k_read,
-    .ioctl	= i8k_ioctl,
+	.open		= i8k_open_fs,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.ioctl		= i8k_ioctl,
 };
 
-typedef struct {
-    unsigned int eax;
-    unsigned int ebx __attribute__ ((packed));
-    unsigned int ecx __attribute__ ((packed));
-    unsigned int edx __attribute__ ((packed));
-    unsigned int esi __attribute__ ((packed));
-    unsigned int edi __attribute__ ((packed));
-} SMMRegisters;
-
-typedef struct {
-    u8	type;
-    u8	length;
-    u16	handle;
-} DMIHeader;
+static struct device_driver i8k_driver = {
+	.name		= "i8k",
+	.bus		= &platform_bus_type,
+};
+
+static struct platform_device *i8k_device;
+
+struct smm_regs {
+	unsigned int eax;
+	unsigned int ebx __attribute__ ((packed));
+	unsigned int ecx __attribute__ ((packed));
+	unsigned int edx __attribute__ ((packed));
+	unsigned int esi __attribute__ ((packed));
+	unsigned int edi __attribute__ ((packed));
+};
+
+static inline char *i8k_get_dmi_data(int field)
+{
+	return dmi_get_system_info(field) ? : "N/A";
+}
 
 /*
  * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
  */
-static int i8k_smm(SMMRegisters *regs)
+static int i8k_smm(struct smm_regs *regs)
 {
-    int rc;
-    int eax = regs->eax;
+	int rc;
+	int eax = regs->eax;
+
+	asm("pushl %%eax\n\t"
+	    "movl 0(%%eax),%%edx\n\t"
+	    "push %%edx\n\t"
+	    "movl 4(%%eax),%%ebx\n\t"
+	    "movl 8(%%eax),%%ecx\n\t"
+	    "movl 12(%%eax),%%edx\n\t"
+	    "movl 16(%%eax),%%esi\n\t"
+	    "movl 20(%%eax),%%edi\n\t"
+	    "popl %%eax\n\t"
+	    "out %%al,$0xb2\n\t"
+	    "out %%al,$0x84\n\t"
+	    "xchgl %%eax,(%%esp)\n\t"
+	    "movl %%ebx,4(%%eax)\n\t"
+	    "movl %%ecx,8(%%eax)\n\t"
+	    "movl %%edx,12(%%eax)\n\t"
+	    "movl %%esi,16(%%eax)\n\t"
+	    "movl %%edi,20(%%eax)\n\t"
+	    "popl %%edx\n\t"
+	    "movl %%edx,0(%%eax)\n\t"
+	    "lahf\n\t"
+	    "shrl $8,%%eax\n\t"
+	    "andl $1,%%eax\n":"=a"(rc)
+	    :    "a"(regs)
+	    :    "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
 
-    asm("pushl %%eax\n\t" \
-	"movl 0(%%eax),%%edx\n\t" \
-	"push %%edx\n\t" \
-	"movl 4(%%eax),%%ebx\n\t" \
-	"movl 8(%%eax),%%ecx\n\t" \
-	"movl 12(%%eax),%%edx\n\t" \
-	"movl 16(%%eax),%%esi\n\t" \
-	"movl 20(%%eax),%%edi\n\t" \
-	"popl %%eax\n\t" \
-	"out %%al,$0xb2\n\t" \
-	"out %%al,$0x84\n\t" \
-	"xchgl %%eax,(%%esp)\n\t"
-	"movl %%ebx,4(%%eax)\n\t" \
-	"movl %%ecx,8(%%eax)\n\t" \
-	"movl %%edx,12(%%eax)\n\t" \
-	"movl %%esi,16(%%eax)\n\t" \
-	"movl %%edi,20(%%eax)\n\t" \
-	"popl %%edx\n\t" \
-	"movl %%edx,0(%%eax)\n\t" \
-	"lahf\n\t" \
-	"shrl $8,%%eax\n\t" \
-	"andl $1,%%eax\n" \
-	: "=a" (rc)
-	: "a" (regs)
-	: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
-
-    if ((rc != 0) || ((regs->eax & 0xffff) == 0xffff) || (regs->eax == eax)) {
-	return -EINVAL;
-    }
+	if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
+		return -EINVAL;
 
-    return 0;
+	return 0;
 }
 
 /*
@@ -152,24 +155,9 @@ static int i8k_smm(SMMRegisters *regs)
  */
 static int i8k_get_bios_version(void)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
-
-    regs.eax = I8K_SMM_BIOS_VERSION;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-
-    return regs.eax;
-}
+	struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, };
 
-/*
- * Read the machine id.
- */
-static int i8k_get_serial_number(unsigned char *buff)
-{
-    strlcpy(buff, serial_number, sizeof(serial_number));
-    return 0;
+	return i8k_smm(&regs) ? : regs.eax;
 }
 
 /*
@@ -177,24 +165,22 @@ static int i8k_get_serial_number(unsigne
  */
 static int i8k_get_fn_status(void)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
+	struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
+	int rc;
 
-    regs.eax = I8K_SMM_FN_STATUS;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-
-    switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
-    case I8K_FN_UP:
-	return I8K_VOL_UP;
-    case I8K_FN_DOWN:
-	return I8K_VOL_DOWN;
-    case I8K_FN_MUTE:
-	return I8K_VOL_MUTE;
-    default:
-	return 0;
-    }
+	if ((rc = i8k_smm(&regs)) < 0)
+		return rc;
+
+	switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
+	case I8K_FN_UP:
+		return I8K_VOL_UP;
+	case I8K_FN_DOWN:
+		return I8K_VOL_DOWN;
+	case I8K_FN_MUTE:
+		return I8K_VOL_MUTE;
+	default:
+		return 0;
+	}
 }
 
 /*
@@ -202,20 +188,13 @@ static int i8k_get_fn_status(void)
  */
 static int i8k_get_power_status(void)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
+	struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
+	int rc;
+
+	if ((rc = i8k_smm(&regs)) < 0)
+		return rc;
 
-    regs.eax = I8K_SMM_POWER_STATUS;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-
-    switch (regs.eax & 0xff) {
-    case I8K_POWER_AC:
-	return I8K_AC;
-    default:
-	return I8K_BATTERY;
-    }
+	return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
 }
 
 /*
@@ -223,16 +202,10 @@ static int i8k_get_power_status(void)
  */
 static int i8k_get_fan_status(int fan)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
+	struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
 
-    regs.eax = I8K_SMM_GET_FAN;
-    regs.ebx = fan & 0xff;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-
-    return (regs.eax & 0xff);
+	regs.ebx = fan & 0xff;
+	return i8k_smm(&regs) ? : regs.eax & 0xff;
 }
 
 /*
@@ -240,16 +213,10 @@ static int i8k_get_fan_status(int fan)
  */
 static int i8k_get_fan_speed(int fan)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
-
-    regs.eax = I8K_SMM_GET_SPEED;
-    regs.ebx = fan & 0xff;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
+	struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
 
-    return (regs.eax & 0xffff) * I8K_FAN_MULT;
+	regs.ebx = fan & 0xff;
+	return i8k_smm(&regs) ? : (regs.eax & 0xffff) * I8K_FAN_MULT;
 }
 
 /*
@@ -257,532 +224,438 @@ static int i8k_get_fan_speed(int fan)
  */
 static int i8k_set_fan(int fan, int speed)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
+	struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
 
-    speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
+	speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
+	regs.ebx = (fan & 0xff) | (speed << 8);
 
-    regs.eax = I8K_SMM_SET_FAN;
-    regs.ebx = (fan & 0xff) | (speed << 8);
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-
-    return (i8k_get_fan_status(fan));
+	return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
 }
 
 /*
  * Read the cpu temperature.
  */
-static int i8k_get_cpu_temp(void)
+static int i8k_get_temp(int sensor)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
-    int temp;
+	struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
+	int rc;
+	int temp;
 
 #ifdef I8K_TEMPERATURE_BUG
-    static int prev = 0;
+	static int prev;
 #endif
+	regs.ebx = sensor & 0xff;
+	if ((rc = i8k_smm(&regs)) < 0)
+		return rc;
 
-    regs.eax = I8K_SMM_GET_TEMP;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
-    temp = regs.eax & 0xff;
+	temp = regs.eax & 0xff;
 
 #ifdef I8K_TEMPERATURE_BUG
-    /*
-     * Sometimes the temperature sensor returns 0x99, which is out of range.
-     * In this case we return (once) the previous cached value. For example:
-     # 1003655137 00000058 00005a4b
-     # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
-     # 1003655139 00000054 00005c52
-     */
-    if (temp > I8K_MAX_TEMP) {
-	temp = prev;
-	prev = I8K_MAX_TEMP;
-    } else {
-	prev = temp;
-    }
+	/*
+	 * Sometimes the temperature sensor returns 0x99, which is out of range.
+	 * In this case we return (once) the previous cached value. For example:
+	 # 1003655137 00000058 00005a4b
+	 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
+	 # 1003655139 00000054 00005c52
+	 */
+	if (temp > I8K_MAX_TEMP) {
+		temp = prev;
+		prev = I8K_MAX_TEMP;
+	} else {
+		prev = temp;
+	}
 #endif
 
-    return temp;
+	return temp;
 }
 
-static int i8k_get_dell_signature(void)
+static int i8k_get_dell_signature(int req_fn)
 {
-    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
-    int rc;
+	struct smm_regs regs = { .eax = req_fn, };
+	int rc;
 
-    regs.eax = I8K_SMM_GET_DELL_SIG;
-    if ((rc=i8k_smm(&regs)) < 0) {
-	return rc;
-    }
+	if ((rc = i8k_smm(&regs)) < 0)
+		return rc;
 
-    if ((regs.eax == 1145651527) && (regs.edx == 1145392204)) {
-	return 0;
-    } else {
-	return -1;
-    }
+	return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
 }
 
 static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
 		     unsigned long arg)
 {
-    int val;
-    int speed;
-    unsigned char buff[16];
-    int __user *argp = (int __user *)arg;
-
-    if (!argp)
-	return -EINVAL;
-
-    switch (cmd) {
-    case I8K_BIOS_VERSION:
-	val = i8k_get_bios_version();
-	break;
-
-    case I8K_MACHINE_ID:
-	memset(buff, 0, 16);
-	val = i8k_get_serial_number(buff);
-	break;
-
-    case I8K_FN_STATUS:
-	val = i8k_get_fn_status();
-	break;
-
-    case I8K_POWER_STATUS:
-	val = i8k_get_power_status();
-	break;
-
-    case I8K_GET_TEMP:
-	val = i8k_get_cpu_temp();
-	break;
-
-    case I8K_GET_SPEED:
-	if (copy_from_user(&val, argp, sizeof(int))) {
-	    return -EFAULT;
-	}
-	val = i8k_get_fan_speed(val);
-	break;
+	int val = 0;
+	int speed;
+	unsigned char buff[16];
+	int __user *argp = (int __user *)arg;
 
-    case I8K_GET_FAN:
-	if (copy_from_user(&val, argp, sizeof(int))) {
-	    return -EFAULT;
-	}
-	val = i8k_get_fan_status(val);
-	break;
+	if (!argp)
+		return -EINVAL;
 
-    case I8K_SET_FAN:
-	if (restricted && !capable(CAP_SYS_ADMIN)) {
-	    return -EPERM;
-	}
-	if (copy_from_user(&val, argp, sizeof(int))) {
-	    return -EFAULT;
-	}
-	if (copy_from_user(&speed, argp+1, sizeof(int))) {
-	    return -EFAULT;
-	}
-	val = i8k_set_fan(val, speed);
-	break;
+	switch (cmd) {
+	case I8K_BIOS_VERSION:
+		val = i8k_get_bios_version();
+		break;
 
-    default:
-	return -EINVAL;
-    }
-
-    if (val < 0) {
-	return val;
-    }
-
-    switch (cmd) {
-    case I8K_BIOS_VERSION:
-	if (copy_to_user(argp, &val, 4)) {
-	    return -EFAULT;
-	}
-	break;
-    case I8K_MACHINE_ID:
-	if (copy_to_user(argp, buff, 16)) {
-	    return -EFAULT;
+	case I8K_MACHINE_ID:
+		memset(buff, 0, 16);
+		strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff));
+		break;
+
+	case I8K_FN_STATUS:
+		val = i8k_get_fn_status();
+		break;
+
+	case I8K_POWER_STATUS:
+		val = i8k_get_power_status();
+		break;
+
+	case I8K_GET_TEMP:
+		val = i8k_get_temp(0);
+		break;
+
+	case I8K_GET_SPEED:
+		if (copy_from_user(&val, argp, sizeof(int)))
+			return -EFAULT;
+
+		val = i8k_get_fan_speed(val);
+		break;
+
+	case I8K_GET_FAN:
+		if (copy_from_user(&val, argp, sizeof(int)))
+			return -EFAULT;
+
+		val = i8k_get_fan_status(val);
+		break;
+
+	case I8K_SET_FAN:
+		if (restricted && !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&val, argp, sizeof(int)))
+			return -EFAULT;
+
+		if (copy_from_user(&speed, argp + 1, sizeof(int)))
+			return -EFAULT;
+
+		val = i8k_set_fan(val, speed);
+		break;
+
+	default:
+		return -EINVAL;
 	}
-	break;
-    default:
-	if (copy_to_user(argp, &val, sizeof(int))) {
-	    return -EFAULT;
+
+	if (val < 0)
+		return val;
+
+	switch (cmd) {
+	case I8K_BIOS_VERSION:
+		if (copy_to_user(argp, &val, 4))
+			return -EFAULT;
+
+		break;
+	case I8K_MACHINE_ID:
+		if (copy_to_user(argp, buff, 16))
+			return -EFAULT;
+
+		break;
+	default:
+		if (copy_to_user(argp, &val, sizeof(int)))
+			return -EFAULT;
+
+		break;
 	}
-	break;
-    }
 
-    return 0;
+	return 0;
 }
 
 /*
  * Print the information for /proc/i8k.
  */
-static int i8k_get_info(char *buffer, char **start, off_t fpos, int length)
+static int i8k_proc_show(struct seq_file *seq, void *offset)
 {
-    int n, fn_key, cpu_temp, ac_power;
-    int left_fan, right_fan, left_speed, right_speed;
-
-    cpu_temp     = i8k_get_cpu_temp();			/* 11100 µs */
-    left_fan     = i8k_get_fan_status(I8K_FAN_LEFT);	/*   580 µs */
-    right_fan    = i8k_get_fan_status(I8K_FAN_RIGHT);	/*   580 µs */
-    left_speed   = i8k_get_fan_speed(I8K_FAN_LEFT);	/*   580 µs */
-    right_speed  = i8k_get_fan_speed(I8K_FAN_RIGHT);	/*   580 µs */
-    fn_key       = i8k_get_fn_status();			/*   750 µs */
-    if (power_status) {
-	ac_power = i8k_get_power_status();		/* 14700 µs */
-    } else {
-	ac_power = -1;
-    }
-
-    /*
-     * Info:
-     *
-     * 1)  Format version (this will change if format changes)
-     * 2)  BIOS version
-     * 3)  BIOS machine ID
-     * 4)  Cpu temperature
-     * 5)  Left fan status
-     * 6)  Right fan status
-     * 7)  Left fan speed
-     * 8)  Right fan speed
-     * 9)  AC power
-     * 10) Fn Key status
-     */
-    n = sprintf(buffer, "%s %s %s %d %d %d %d %d %d %d\n",
-		I8K_PROC_FMT,
-		bios_version,
-		serial_number,
-		cpu_temp,
-		left_fan,
-		right_fan,
-		left_speed,
-		right_speed,
-		ac_power,
-		fn_key);
-
-    return n;
-}
-
-static ssize_t i8k_read(struct file *f, char __user *buffer, size_t len, loff_t *fpos)
-{
-    int n;
-    char info[128];
-
-    n = i8k_get_info(info, NULL, 0, 128);
-    if (n <= 0) {
-	return n;
-    }
-
-    if (*fpos >= n) {
-	return 0;
-    }
+	int fn_key, cpu_temp, ac_power;
+	int left_fan, right_fan, left_speed, right_speed;
 
-    if ((*fpos + len) >= n) {
-	len = n - *fpos;
-    }
+	cpu_temp	= i8k_get_temp(0);			/* 11100 µs */
+	left_fan	= i8k_get_fan_status(I8K_FAN_LEFT);	/*   580 µs */
+	right_fan	= i8k_get_fan_status(I8K_FAN_RIGHT);	/*   580 µs */
+	left_speed	= i8k_get_fan_speed(I8K_FAN_LEFT);	/*   580 µs */
+	right_speed	= i8k_get_fan_speed(I8K_FAN_RIGHT);	/*   580 µs */
+	fn_key		= i8k_get_fn_status();			/*   750 µs */
+	if (power_status)
+		ac_power = i8k_get_power_status();		/* 14700 µs */
+	else
+		ac_power = -1;
 
-    if (copy_to_user(buffer, info, len) != 0) {
-	return -EFAULT;
-    }
-
-    *fpos += len;
-    return len;
+	/*
+	 * Info:
+	 *
+	 * 1)  Format version (this will change if format changes)
+	 * 2)  BIOS version
+	 * 3)  BIOS machine ID
+	 * 4)  Cpu temperature
+	 * 5)  Left fan status
+	 * 6)  Right fan status
+	 * 7)  Left fan speed
+	 * 8)  Right fan speed
+	 * 9)  AC power
+	 * 10) Fn Key status
+	 */
+	return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
+			  I8K_PROC_FMT,
+			  bios_version,
+			  dmi_get_system_info(DMI_PRODUCT_SERIAL) ? : "N/A",
+			  cpu_temp,
+			  left_fan, right_fan, left_speed, right_speed,
+			  ac_power, fn_key);
 }
 
-static char* __init string_trim(char *s, int size)
+static int i8k_open_fs(struct inode *inode, struct file *file)
 {
-    int len;
-    char *p;
-
-    if ((len = strlen(s)) > size) {
-	len = size;
-    }
+	return single_open(file, i8k_proc_show, NULL);
+}
 
-    for (p=s+len-1; len && (*p==' '); len--,p--) {
-	*p = '\0';
-    }
 
-    return s;
+static ssize_t i8k_sysfs_temp_show(struct kobject *kobj, unsigned int idx, char *buf)
+{
+	int temp = i8k_get_temp(idx);
+	return temp < 0 ? -EIO : sprintf(buf, "%d\n", temp);
 }
 
-/* DMI code, stolen from arch/i386/kernel/dmi_scan.c */
+static struct enumerated_attr i8k_temp_attr =
+	__ATTR(temp, 0444, i8k_sysfs_temp_show, NULL);
 
-/*
- * |<-- dmi->length -->|
- * |                   |
- * |dmi header    s=N  | string1,\0, ..., stringN,\0, ..., \0
- *                |                       |
- *                +-----------------------+
- */
-static char* __init dmi_string(DMIHeader *dmi, u8 s)
+static ssize_t i8k_sysfs_fan_show(struct kobject *kobj, unsigned int idx, char *buf)
+{
+	int status = i8k_get_fan_status(idx);
+	return status < 0 ? -EIO : sprintf(buf, "%d\n", status);
+}
+
+static ssize_t i8k_sysfs_fan_set(struct kobject *kobj, unsigned int idx, const char *buf, size_t count)
 {
-    u8 *p;
+	unsigned long state;
+	char *rest;
 
-    if (!s) {
-	return "";
-    }
-    s--;
+	if (restricted && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
 
-    p = (u8 *)dmi + dmi->length;
-    while (s > 0) {
-	p += strlen(p);
-	p++;
-	s--;
-    }
+	state = simple_strtoul(buf, &rest, 10);
+	if (*rest || state > I8K_FAN_MAX)
+		return -EINVAL;
 
-    return p;
+	if (i8k_set_fan(idx, state) < 0)
+		return -EIO;
+
+	return count;
 }
 
-static void __init dmi_decode(DMIHeader *dmi)
+static ssize_t i8k_sysfs_fan_speed_show(struct kobject *kobj, unsigned int idx, char *buf)
 {
-    u8 *data = (u8 *) dmi;
-    char *p;
+	int speed = i8k_get_fan_speed(idx);
+	return speed < 0 ? -EIO : sprintf(buf, "%d\n", speed);
+}
 
-#ifdef I8K_DEBUG
-    int i;
-    printk("%08x ", (int)data);
-    for (i=0; i<data[1] && i<64; i++) {
-	printk("%02x ", data[i]);
-    }
-    printk("\n");
-#endif
+static struct enumerated_attr i8k_fan_attrs[] = {
+	__ATTR(state, 0644, i8k_sysfs_fan_show, i8k_sysfs_fan_set),
+	__ATTR(speed, 0444, i8k_sysfs_fan_speed_show, NULL),
+	__ATTR_NULL
+};
 
-    switch (dmi->type) {
-    case  0:	/* BIOS Information */
-	p = dmi_string(dmi,data[5]);
-	if (*p) {
-	    strlcpy(bios_version, p, sizeof(bios_version));
-	    string_trim(bios_version, sizeof(bios_version));
-	}
-	break;	
-    case 1:	/* System Information */
-	p = dmi_string(dmi,data[4]);
-	if (*p) {
-	    strlcpy(system_vendor, p, sizeof(system_vendor));
-	    string_trim(system_vendor, sizeof(system_vendor));
-	}
-	p = dmi_string(dmi,data[5]);
-	if (*p) {
-	    strlcpy(product_name, p, sizeof(product_name));
-	    string_trim(product_name, sizeof(product_name));
-	}
-	p = dmi_string(dmi,data[7]);
-	if (*p) {
-	    strlcpy(serial_number, p, sizeof(serial_number));
-	    string_trim(serial_number, sizeof(serial_number));
-	}
-	break;
-    }
-}
+static struct attribute_group_array i8k_fan_attr_array = {
+	.name = "fan",
+	.attrs = i8k_fan_attrs,
+};
 
-static int __init dmi_table(u32 base, int len, int num, void (*fn)(DMIHeader*))
+static ssize_t i8k_sysfs_power_status_show(struct device *dev, char *buf)
 {
-    u8 *buf;
-    u8 *data;
-    DMIHeader *dmi;
-    int i = 1;
-
-    buf = ioremap(base, len);
-    if (buf == NULL) {
-	return -1;
-    }
-    data = buf;
-
-    /*
-     * Stop when we see al the items the table claimed to have
-     * or we run off the end of the table (also happens)
-     */
-    while ((i<num) && ((data-buf) < len)) {
-	dmi = (DMIHeader *)data;
-	/*
-	 * Avoid misparsing crud if the length of the last
-	 * record is crap
-	 */
-	if ((data-buf+dmi->length) >= len) {
-	    break;
-	}
-	fn(dmi);
-	data += dmi->length;
-	/*
-	 * Don't go off the end of the data if there is
-	 * stuff looking like string fill past the end
-	 */
-	while (((data-buf) < len) && (*data || data[1])) {
-	    data++;
-	}
-	data += 2;
-	i++;
-    }
-    iounmap(buf);
-
-    return 0;
-}
-
-static int __init dmi_iterate(void (*decode)(DMIHeader *))
-{
-	unsigned char buf[20];
-	void __iomem *p = ioremap(0xe0000, 0x20000), *q;
-
-	if (!p)
-		return -1;
-
-	for (q = p; q < p + 0x20000; q += 16) {
-		memcpy_fromio(buf, q, 20);
-		if (memcmp(buf, "_DMI_", 5)==0) {
-			u16 num  = buf[13]<<8  | buf[12];
-			u16 len  = buf [7]<<8  | buf [6];
-			u32 base = buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8];
-#ifdef I8K_DEBUG
-			printk(KERN_INFO "DMI %d.%d present.\n",
-			   buf[14]>>4, buf[14]&0x0F);
-			printk(KERN_INFO "%d structures occupying %d bytes.\n",
-			   buf[13]<<8 | buf[12],
-			   buf [7]<<8 | buf[6]);
-			printk(KERN_INFO "DMI table at 0x%08X.\n",
-			   buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8]);
-#endif
-			if (dmi_table(base, len, num, decode)==0) {
-				iounmap(p);
-				return 0;
-			}
-		}
-	}
-	iounmap(p);
-	return -1;
+	int status = power_status ? i8k_get_power_status() : -1;
+	return status < 0 ? -EIO : sprintf(buf, "%d\n", status);
 }
-/* end of DMI code */
 
-/*
- * Get DMI information.
- */
-static int __init i8k_dmi_probe(void)
-{
-    char **p;
+static struct device_attribute i8k_power_status_attr =
+	__ATTR(power_status, 0444, i8k_sysfs_power_status_show, NULL);
 
-    if (dmi_iterate(dmi_decode) != 0) {
-	printk(KERN_INFO "i8k: unable to get DMI information\n");
-	return -ENODEV;
-    }
-
-    if (strncmp(system_vendor,DELL_SIGNATURE,strlen(DELL_SIGNATURE)) != 0) {
-	printk(KERN_INFO "i8k: not running on a Dell system\n");
-	return -ENODEV;
-    }
-
-    for (p=supported_models; ; p++) {
-	if (!*p) {
-	    printk(KERN_INFO "i8k: unsupported model: %s\n", product_name);
-	    return -ENODEV;
-	}
-	if (strncmp(product_name,*p,strlen(*p)) == 0) {
-	    break;
-	}
-    }
 
-    return 0;
-}
+static struct dmi_system_id __initdata i8k_dmi_table[] = {
+	{
+		.ident = "Dell Inspiron",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+		},
+	},
+	{
+		.ident = "Dell Latitude",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+		},
+	},
+	{
+		.ident = "Dell Inspiron 2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+		},
+	},
+	{
+		.ident = "Dell Latitude 2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+		},
+	},
+	{ }
+};
 
 /*
  * Probe for the presence of a supported laptop.
  */
 static int __init i8k_probe(void)
 {
-    char buff[4];
-    int version;
-    int smm_found = 0;
-
-    /*
-     * Get DMI information
-     */
-    if (i8k_dmi_probe() != 0) {
-	printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
-	       system_vendor, product_name, bios_version);
-    }
-
-    /*
-     * Get SMM Dell signature
-     */
-    if (i8k_get_dell_signature() != 0) {
-	printk(KERN_INFO "i8k: unable to get SMM Dell signature\n");
-    } else {
-	smm_found = 1;
-    }
-
-    /*
-     * Get SMM BIOS version.
-     */
-    version = i8k_get_bios_version();
-    if (version <= 0) {
-	printk(KERN_INFO "i8k: unable to get SMM BIOS version\n");
-    } else {
-	smm_found = 1;
-	buff[0] = (version >> 16) & 0xff;
-	buff[1] = (version >>  8) & 0xff;
-	buff[2] = (version)       & 0xff;
-	buff[3] = '\0';
+	char buff[4];
+	int version;
+
 	/*
-	 * If DMI BIOS version is unknown use SMM BIOS version.
+	 * Get DMI information
 	 */
-	if (bios_version[0] == '?') {
-	    strcpy(bios_version, buff);
+	if (!dmi_check_system(i8k_dmi_table)) {
+		if (!ignore_dmi && !force)
+			return -ENODEV;
+
+		printk(KERN_INFO "i8k: not running on a supported Dell system.\n");
+		printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
+			i8k_get_dmi_data(DMI_SYS_VENDOR),
+			i8k_get_dmi_data(DMI_PRODUCT_NAME),
+			i8k_get_dmi_data(DMI_BIOS_VERSION));
 	}
+
+	strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version));
+
 	/*
-	 * Check if the two versions match.
+	 * Get SMM Dell signature
 	 */
-	if (strncmp(buff,bios_version,sizeof(bios_version)) != 0) {
-	    printk(KERN_INFO "i8k: BIOS version mismatch: %s != %s\n",
-		   buff, bios_version);
+	if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
+	    i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
+		printk(KERN_ERR "i8k: unable to get SMM Dell signature\n");
+		if (!force)
+			return -ENODEV;
 	}
-    }
 
-    if (!smm_found && !force) {
-	return -ENODEV;
-    }
+	/*
+	 * Get SMM BIOS version.
+	 */
+	version = i8k_get_bios_version();
+	if (version <= 0) {
+		printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n");
+	} else {
+		buff[0] = (version >> 16) & 0xff;
+		buff[1] = (version >> 8) & 0xff;
+		buff[2] = (version) & 0xff;
+		buff[3] = '\0';
+		/*
+		 * If DMI BIOS version is unknown use SMM BIOS version.
+		 */
+		if (!dmi_get_system_info(DMI_BIOS_VERSION))
+			strlcpy(bios_version, buff, sizeof(bios_version));
+
+		/*
+		 * Check if the two versions match.
+		 */
+		if (strncmp(buff, bios_version, sizeof(bios_version)) != 0)
+			printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n",
+				buff, bios_version);
+	}
 
-    return 0;
+	return 0;
 }
 
-#ifdef MODULE
-static
-#endif
-int __init i8k_init(void)
+static int __init i8k_create_sysfs_files(void)
 {
-    struct proc_dir_entry *proc_i8k;
+	int err, num;
+
+	err = device_create_file(&i8k_device->dev, &i8k_power_status_attr);
+	if (err)
+		return err;
 
-    /* Are we running on an supported laptop? */
-    if (i8k_probe() != 0) {
-	return -ENODEV;
-    }
+	for (num = 0; i8k_get_fan_status(num) >= 0; num++)
+		/* empty */;
 
-    /* Register the proc entry */
-    proc_i8k = create_proc_info_entry("i8k", 0, NULL, i8k_get_info);
-    if (!proc_i8k) {
-	return -ENOENT;
-    }
-    proc_i8k->proc_fops = &i8k_fops;
-    proc_i8k->owner = THIS_MODULE;
+	err = sysfs_create_group_array(&i8k_device->dev.kobj, &i8k_fan_attr_array, num);
+	if (err)
+		goto fail1;
 
-    printk(KERN_INFO
-	   "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
-	   I8K_VERSION);
+	for (num = 0; i8k_get_temp(num) >= 0; num++)
+		/* empty */;
+
+	err = sysfs_create_array(&i8k_device->dev.kobj, &i8k_temp_attr, num);
+	if (err)
+		goto fail2;
+
+	return 0;
 
-    return 0;
+fail2:	sysfs_remove_group_array(&i8k_device->dev.kobj, &i8k_fan_attr_array);
+fail1:	device_remove_file(&i8k_device->dev, &i8k_power_status_attr);
+	return err;
 }
 
-#ifdef MODULE
-int init_module(void)
+static void __exit i8k_remove_sysfs_files(void)
 {
-    return i8k_init();
+	sysfs_remove_array(&i8k_device->dev.kobj, &i8k_temp_attr);
+	sysfs_remove_group_array(&i8k_device->dev.kobj, &i8k_fan_attr_array);
+	device_remove_file(&i8k_device->dev, &i8k_power_status_attr);
 }
 
-void cleanup_module(void)
+static int __init i8k_init(void)
 {
-    /* Remove the proc entry */
-    remove_proc_entry("i8k", NULL);
+	struct proc_dir_entry *proc_i8k;
+	int err;
 
-    printk(KERN_INFO "i8k: module unloaded\n");
+	/* Are we running on an supported laptop? */
+	if (i8k_probe())
+		return -ENODEV;
+
+	/* Register the proc entry */
+	proc_i8k = create_proc_entry("i8k", 0, NULL);
+	if (!proc_i8k)
+		return -ENOENT;
+
+	proc_i8k->proc_fops = &i8k_fops;
+	proc_i8k->owner = THIS_MODULE;
+
+	err = driver_register(&i8k_driver);
+	if (err)
+		goto fail1;
+
+	i8k_device = platform_device_register_simple("i8k", -1, NULL, 0);
+	if (IS_ERR(i8k_device)) {
+		err = PTR_ERR(i8k_device);
+		goto fail2;
+	}
+
+	err = i8k_create_sysfs_files();
+	if (err)
+		goto fail3;
+
+	printk(KERN_INFO
+	       "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
+	       I8K_VERSION);
+
+	return 0;
+
+fail3:	platform_device_unregister(i8k_device);
+fail2:	driver_unregister(&i8k_driver);
+fail1:	remove_proc_entry("i8k", NULL);
+	return err;
+}
+
+static void __exit i8k_exit(void)
+{
+	i8k_remove_sysfs_files();
+	platform_device_unregister(i8k_device);
+	driver_unregister(&i8k_driver);
+	remove_proc_entry("i8k", NULL);
 }
-#endif
 
-/* end of file */
+module_init(i8k_init);
+module_exit(i8k_exit);

  reply	other threads:[~2005-03-21  5:15 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-02-24  6:10 [PATCH 0/5] I8K driver facelift Dmitry Torokhov
2005-02-24  6:11 ` [PATCH 1/5] I8K - pass though Lindent Dmitry Torokhov
2005-02-24  6:12   ` [PATCH 2/5] I8K - use standard DMI functions Dmitry Torokhov
2005-02-24  6:12     ` [PATCH 3/5] I8K - switch to seq_file Dmitry Torokhov
2005-02-24  6:14       ` [PATCH 4/5] I8K - switch to module_{init|exit} Dmitry Torokhov
2005-02-24  6:14         ` [PATCH 5/5] I8K - convert to platform device (sysfs) Dmitry Torokhov
2005-03-13  3:41 ` [PATCH 0/5] I8K driver facelift Frank Sorenson
2005-03-13  3:59   ` Dmitry Torokhov
2005-03-15  8:12   ` Valdis.Kletnieks
2005-03-15 10:59     ` Giuseppe Bilotta
2005-03-15 17:30       ` Valdis.Kletnieks
2005-03-15 22:34         ` Frank Sorenson
2005-03-16 21:38   ` Frank Sorenson
2005-03-17  6:40     ` Dmitry Torokhov
2005-03-17  9:37       ` Frank Sorenson
2005-03-17 15:05         ` Dmitry Torokhov
2005-03-17  9:46       ` Frank Sorenson
2005-03-21  5:12         ` Dmitry Torokhov [this message]
2005-03-21 22:53           ` Frank Sorenson
2005-03-21 23:55             ` Dmitry Torokhov
2005-03-24  7:25       ` Greg KH
2005-03-24  7:39         ` Dmitry Torokhov
2005-03-24  8:00           ` Greg KH
2005-03-24 14:44             ` Dmitry Torokhov
2005-03-17  8:16     ` Valdis.Kletnieks
2005-03-24  7:24       ` Greg KH
2005-04-13  6:33         ` Dmitry Torokhov
2005-04-13  8:00           ` Greg KH

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200503210012.29477.dtor_core@ameritech.net \
    --to=dtor_core@ameritech.net \
    --cc=Valdis.Kletnieks@vt.edu \
    --cc=frank@tuxrocks.com \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.