|
|
Subscribe / Log in / New account

Rework of request firmware

From:  Jon Smirl <jonsmirl@gmail.com>
To:  hotplug <linux-hotplug-devel@lists.sourceforge.net>
Subject:  Rework of request firmware
Date:  Sat, 19 Mar 2005 23:06:59 -0500

On Sat, 26 Feb 2005 20:41:16 +0100, Kay Sievers <kay.sievers@vrfy.org> wrote:
> I think the request_firmware() should be redesigned in a generic
> request_user_data(kobj) call. This event would copy arbitrary data into
> a sysfs file and something like KOBJ_REQUEST_DATA would do it.
> 
> The call should create a file inside of a existing device and the event
> handler should copy the data into this file instead of creating a own
> class device for the firmware as we do today.

This is a rework of request firmware to move the attributes from their
own class into the device sysfs directory. I am also generalizing
things so that I can request a post (this is what I need) as well as
firmware. This is a first post of the code, please tell me what I can
do to improve it before I send it to lkml. The changes in radeonfb are
so that I can test it. There are also some debug printf's still in.
Use the attachment with patch, gmail word wraps and I can't stop it.

I made a few changes in the base code:
1) I modified kobj_hotplug() to take an extra function for adding
variables. This lets the event add the normal variable for the kset
and then I can tack mine on too.
2) New event KOBJ_POST. $POST and $FIRMWARE are used to differentiate
post versus firmware load in the script. Should this be two events
instead?
3) The script now needs to be in
/etc/hotplug.d/default/30-post.hotplug. firmware.agent isn't used
anymore. The firmware doesn't need to change or move.
4) I added a pointer to 'struct device' to track the firmware in
sysfs. Is there a better way to do this?
5) I added the time out to /sys/firmware/post_timeout since there is
no /sys/class/firmware any more.
6) How should everything be named? firmware, post, initialization, etc??
7) Should request_firmware() be deprecated forcing the move to
request_firmware_nowait?
8) Should I keep the different signatures on the nowait callbacks?

-- 
Jon Smirl
jonsmirl@gmail.com

===== drivers/acpi/container.c 1.2 vs edited =====
--- 1.2/drivers/acpi/container.c	2004-11-11 02:56:31 -05:00
+++ edited/drivers/acpi/container.c	2005-03-19 01:38:11 -05:00
@@ -182,16 +182,16 @@
 			if (ACPI_FAILURE(status) || !device) {
 				result = container_device_add(&device, handle);
 				if (!result)
-					kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+					kobject_hotplug(&device->kobj, KOBJ_ONLINE, NULL);
 			} else {
 				/* device exist and this is a remove request */
-				kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+				kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 			}
 		}
 		break;
 	case ACPI_NOTIFY_EJECT_REQUEST:
 		if (!acpi_bus_get_device(handle, &device) && device) {
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 		}
 		break;
 	default:
===== drivers/acpi/processor_core.c 1.80 vs edited =====
--- 1.80/drivers/acpi/processor_core.c	2004-12-14 07:14:54 -05:00
+++ edited/drivers/acpi/processor_core.c	2005-03-19 01:38:57 -05:00
@@ -730,7 +730,7 @@
 		return_VALUE(-ENODEV);
 
 	if ((pr->id >=0) && (pr->id < NR_CPUS)) {
-		kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE);
+		kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE, NULL);
 	}
 	return_VALUE(0);
 }
@@ -774,13 +774,13 @@
 		}
 
 		if (pr->id >= 0 && (pr->id < NR_CPUS)) {
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 			break;
 		}
 
 		result = acpi_processor_start(device);
 		if ((!result) && ((pr->id >=0) && (pr->id < NR_CPUS))) {
-			kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+			kobject_hotplug(&device->kobj, KOBJ_ONLINE, NULL);
 		} else {
 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				"Device [%s] failed to start\n",
@@ -801,7 +801,7 @@
 		}
 
 		if ((pr->id < NR_CPUS) && (cpu_present(pr->id)))
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 		break;
 	default:
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
===== drivers/base/cpu.c 1.22 vs edited =====
--- 1.22/drivers/base/cpu.c	2005-01-31 01:33:46 -05:00
+++ edited/drivers/base/cpu.c	2005-03-19 01:39:16 -05:00
@@ -33,7 +33,7 @@
 	case '0':
 		ret = cpu_down(cpu->sysdev.id);
 		if (!ret)
-			kobject_hotplug(&dev->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&dev->kobj, KOBJ_OFFLINE, NULL);
 		break;
 	case '1':
 		ret = cpu_up(cpu->sysdev.id);
===== drivers/base/firmware.c 1.9 vs edited =====
--- 1.9/drivers/base/firmware.c	2004-09-24 14:45:35 -04:00
+++ edited/drivers/base/firmware.c	2005-03-19 11:16:01 -05:00
@@ -14,21 +14,57 @@
 
 static decl_subsys(firmware, NULL, NULL);
 
+int post_timeout = 10; /* In seconds */
+EXPORT_SYMBOL_GPL(post_timeout);
+
 int firmware_register(struct subsystem * s)
 {
 	kset_set_kset_s(s, firmware_subsys);
 	return subsystem_register(s);
 }
+EXPORT_SYMBOL_GPL(firmware_register);
 
 void firmware_unregister(struct subsystem * s)
 {
 	subsystem_unregister(s);
 }
+EXPORT_SYMBOL_GPL(firmware_unregister);
+
+/**
+ * post_timeout_store:
+ * Description:
+ *	Sets the number of seconds to wait for the post/firmware load.
+ *	Once this expires an error will be return to the driver and no
+ *	firmware will be provided.
+ *
+ *	Note: zero means 'wait for ever'
+ *
+ **/
+static ssize_t post_timeout_store(struct subsystem *subsys, const
char *buf, size_t count)
+{
+	post_timeout = simple_strtol(buf, NULL, 10);
+	return count;
+}
+static ssize_t post_timeout_show(struct subsystem *subsys, char *buf)
+{
+	return sprintf(buf, "%d\n", post_timeout);
+}
+static struct subsys_attribute post_timeout_attr = {
+	.attr = {.name = "post_timeout", .mode = 0644, .owner = THIS_MODULE},
+	.show = post_timeout_show,
+	.store = post_timeout_store
+};
 
 int __init firmware_init(void)
 {
-	return subsystem_register(&firmware_subsys);
+	int error;
+	if ((error = subsystem_register(&firmware_subsys)))
+		return error;
+	
+	if ((error = subsys_create_file(&firmware_subsys, &post_timeout_attr))) {
+		subsystem_unregister(&firmware_subsys);
+		return error;
+	}
+	return 0;
 }
 
-EXPORT_SYMBOL_GPL(firmware_register);
-EXPORT_SYMBOL_GPL(firmware_unregister);
===== drivers/base/firmware_class.c 1.25 vs edited =====
--- 1.25/drivers/base/firmware_class.c	2004-11-26 15:26:48 -05:00
+++ edited/drivers/base/firmware_class.c	2005-03-19 21:11:15 -05:00
@@ -1,7 +1,8 @@
 /*
- * firmware_class.c - Multi purpose firmware loading support
+ * firmware_class.c - Multi purpose post support
  *
  * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ * Copyright (c) 2005 Jon Smirl <jonsmirl@gmail.com>
  *
  * Please see Documentation/firmware_class/ for more information.
  *
@@ -20,7 +21,7 @@
 #include "base.h"
 
 MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
-MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_DESCRIPTION("Multi purpose post support");
 MODULE_LICENSE("GPL");
 
 enum {
@@ -30,8 +31,6 @@
 	FW_STATUS_READY,
 };
 
-static int loading_timeout = 10;	/* In seconds */
-
 /* fw_lock could be moved to 'struct firmware_priv' but since it is just
  * guarding for corner cases a global lock should be OK */
 static DECLARE_MUTEX(fw_lock);
@@ -49,69 +48,41 @@
 static inline void
 fw_load_abort(struct firmware_priv *fw_priv)
 {
+	printk(KERN_ERR "fw_load_abort\n");
 	set_bit(FW_STATUS_ABORT, &fw_priv->status);
 	wmb();
 	complete(&fw_priv->completion);
 }
 
-static ssize_t
-firmware_timeout_show(struct class *class, char *buf)
-{
-	return sprintf(buf, "%d\n", loading_timeout);
-}
-
-/**
- * firmware_timeout_store:
- * Description:
- *	Sets the number of seconds to wait for the firmware.  Once
- *	this expires an error will be return to the driver and no
- *	firmware will be provided.
- *
- *	Note: zero means 'wait for ever'
- *
- **/
-static ssize_t
-firmware_timeout_store(struct class *class, const char *buf, size_t count)
-{
-	loading_timeout = simple_strtol(buf, NULL, 10);
-	return count;
-}
-
-static CLASS_ATTR(timeout, 0644, firmware_timeout_show,
firmware_timeout_store);
-
-static void  fw_class_dev_release(struct class_device *class_dev);
-int firmware_class_hotplug(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
-
-static struct class firmware_class = {
-	.name		= "firmware",
-	.hotplug	= firmware_class_hotplug,
-	.release	= fw_class_dev_release,
-};
-
 int
-firmware_class_hotplug(struct class_device *class_dev, char **envp,
+post_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
 		       int num_envp, char *buffer, int buffer_size)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct firmware_priv *fw_priv = dev->post_data;
 	int i = 0, len = 0;
 
 	if (!test_bit(FW_STATUS_READY, &fw_priv->status))
 		return -ENODEV;
 
-	if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			"FIRMWARE=%s", fw_priv->fw_id))
-		return -ENOMEM;
-
+	if (fw_priv->fw) {
+		if (add_hotplug_env_var(envp, num_envp, &i, buffer,
+		    buffer_size, &len, "FIRMWARE=%s", fw_priv->fw_id))
+			return -ENOMEM;
+	} else {
+		if (add_hotplug_env_var(envp, num_envp, &i, buffer,
+		    buffer_size, &len, "POST=%s", fw_priv->fw_id))
+			return -ENOMEM;
+	}
 	envp[i] = NULL;
 
 	return 0;
 }
 
 static ssize_t
-firmware_loading_show(struct class_device *class_dev, char *buf)
+post_status_show(struct device *dev, char *buf)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct firmware_priv *fw_priv = dev->post_data;
 	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
 	return sprintf(buf, "%d\n", loading);
 }
@@ -126,13 +97,13 @@
  *	-1: Conclude the load with an error and discard any written data.
  **/
 static ssize_t
-firmware_loading_store(struct class_device *class_dev,
-		       const char *buf, size_t count)
+post_status_store(struct device *dev, const char *buf, size_t count)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
-	int loading = simple_strtol(buf, NULL, 10);
+	struct firmware_priv *fw_priv = dev->post_data;
+	int status = simple_strtol(buf, NULL, 10);
 
-	switch (loading) {
+	printk(KERN_ERR "post_status_store: status %d\n", status);
+	switch (status) {
 	case 1:
 		down(&fw_lock);
 		vfree(fw_priv->fw->data);
@@ -151,7 +122,7 @@
 		/* fallthrough */
 	default:
 		printk(KERN_ERR "%s: unexpected value (%d)\n", __FUNCTION__,
-		       loading);
+		       status);
 		/* fallthrough */
 	case -1:
 		fw_load_abort(fw_priv);
@@ -161,15 +132,14 @@
 	return count;
 }
 
-static CLASS_DEVICE_ATTR(loading, 0644,
-			firmware_loading_show, firmware_loading_store);
+static DEVICE_ATTR(post_status, 0644, post_status_show, post_status_store);
 
 static ssize_t
-firmware_data_read(struct kobject *kobj,
-		   char *buffer, loff_t offset, size_t count)
+post_data_read(struct kobject *kobj, 
+	       char *buffer, loff_t offset, size_t count)
 {
-	struct class_device *class_dev = to_class_dev(kobj);
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct firmware_priv *fw_priv = dev->post_data;
 	struct firmware *fw;
 	ssize_t ret_count = count;
 
@@ -191,6 +161,7 @@
 	up(&fw_lock);
 	return ret_count;
 }
+
 static int
 fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
 {
@@ -225,8 +196,8 @@
  *	the driver as a firmware image.
  **/
 static ssize_t
-firmware_data_write(struct kobject *kobj,
-		    char *buffer, loff_t offset, size_t count)
+post_data_write(struct kobject *kobj, 
+		char *buffer, loff_t offset, size_t count)
 {
 	struct class_device *class_dev = to_class_dev(kobj);
 	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
@@ -254,124 +225,82 @@
 	return retval;
 }
 static struct bin_attribute firmware_attr_data_tmpl = {
-	.attr = {.name = "data", .mode = 0644, .owner = THIS_MODULE},
+	.attr = {.name = "post_data", .mode = 0644, .owner = THIS_MODULE},
 	.size = 0,
-	.read = firmware_data_read,
-	.write = firmware_data_write,
+	.read = post_data_read,
+	.write = post_data_write,
 };
 
 static void
-fw_class_dev_release(struct class_device *class_dev)
-{
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
-
-	kfree(fw_priv);
-	kfree(class_dev);
-
-	module_put(THIS_MODULE);
-}
-
-static void
-firmware_class_timeout(u_long data)
+post_timeout_abort(u_long data)
 {
 	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+	printk(KERN_ERR "post_timeout_abort: time out\n");
 	fw_load_abort(fw_priv);
 }
 
-static inline void
-fw_setup_class_device_id(struct class_device *class_dev, struct device *dev)
-{
-	/* XXX warning we should watch out for name collisions */
-	strlcpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE);
-}
-
 static int
-fw_register_class_device(struct class_device **class_dev_p,
-			 const char *fw_name, struct device *device)
+fw_register_device(const char *fw_name, struct device *device)
 {
 	int retval;
 	struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
 						GFP_KERNEL);
-	struct class_device *class_dev = kmalloc(sizeof (struct class_device),
-						 GFP_KERNEL);
-
-	*class_dev_p = NULL;
 
-	if (!fw_priv || !class_dev) {
+	if (!fw_priv) {
 		printk(KERN_ERR "%s: kmalloc failed\n", __FUNCTION__);
 		retval = -ENOMEM;
 		goto error_kfree;
 	}
 	memset(fw_priv, 0, sizeof (*fw_priv));
-	memset(class_dev, 0, sizeof (*class_dev));
 
 	init_completion(&fw_priv->completion);
 	fw_priv->attr_data = firmware_attr_data_tmpl;
 	strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);
 
-	fw_priv->timeout.function = firmware_class_timeout;
+	fw_priv->timeout.function = post_timeout_abort;
 	fw_priv->timeout.data = (u_long) fw_priv;
 	init_timer(&fw_priv->timeout);
+	
+	device->post_data = fw_priv;
 
-	fw_setup_class_device_id(class_dev, device);
-	class_dev->dev = device;
-	class_dev->class = &firmware_class;
-	class_set_devdata(class_dev, fw_priv);
-	retval = class_device_register(class_dev);
-	if (retval) {
-		printk(KERN_ERR "%s: class_device_register failed\n",
-		       __FUNCTION__);
-		goto error_kfree;
-	}
-	*class_dev_p = class_dev;
 	return 0;
 
 error_kfree:
 	kfree(fw_priv);
-	kfree(class_dev);
 	return retval;
 }
 
 static int
-fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p,
-		      const char *fw_name, struct device *device)
+fw_setup_device(struct firmware *fw, const char *fw_name, struct
device *device)
 {
-	struct class_device *class_dev;
 	struct firmware_priv *fw_priv;
 	int retval;
 
-	*class_dev_p = NULL;
-	retval = fw_register_class_device(&class_dev, fw_name, device);
+	retval = fw_register_device(fw_name, device);
 	if (retval)
 		goto out;
 
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	fw_priv = class_get_devdata(class_dev);
-
+	fw_priv = device->post_data;
 	fw_priv->fw = fw;
-	retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->attr_data);
+	
+	retval = sysfs_create_bin_file(&device->kobj, &fw_priv->attr_data);
 	if (retval) {
 		printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
 		       __FUNCTION__);
-		goto error_unreg;
+		goto out;
 	}
 
-	retval = class_device_create_file(class_dev,
-					  &class_device_attr_loading);
-	if (retval) {
-		printk(KERN_ERR "%s: class_device_create_file failed\n",
-		       __FUNCTION__);
-		goto error_unreg;
+	if (fw) {
+		retval = device_create_file(device, &dev_attr_post_status);
+		if (retval) {
+			printk(KERN_ERR "%s: device_create_file failed\n",
+			       __FUNCTION__);
+			goto out;
+		}
 	}
-
 	set_bit(FW_STATUS_READY, &fw_priv->status);
-	*class_dev_p = class_dev;
 	goto out;
 
-error_unreg:
-	class_device_unregister(class_dev);
 out:
 	return retval;
 }
@@ -388,62 +317,79 @@
  *	should be distinctive enough not to be confused with any other
  *	firmware image for this or any other device.
  **/
-int
-request_firmware(const struct firmware **firmware_p, const char *name,
+static int
+request(const struct firmware **firmware_p, const char *name,
 		 struct device *device)
 {
-	struct class_device *class_dev;
 	struct firmware_priv *fw_priv;
-	struct firmware *firmware;
+	struct firmware *firmware = NULL;
 	int retval;
 
-	if (!firmware_p)
-		return -EINVAL;
-
-	*firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL);
-	if (!firmware) {
-		printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
-		       __FUNCTION__);
-		retval = -ENOMEM;
-		goto out;
+	if (firmware_p) {
+		*firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL);
+		if (!firmware) {
+			printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
+			       __FUNCTION__);
+			retval = -ENOMEM;
+			goto out;
+		}
+		memset(firmware, 0, sizeof (*firmware));
 	}
-	memset(firmware, 0, sizeof (*firmware));
 
-	retval = fw_setup_class_device(firmware, &class_dev, name, device);
+	retval = fw_setup_device(firmware, name, device);
 	if (retval)
 		goto error_kfree_fw;
 
-	fw_priv = class_get_devdata(class_dev);
+	fw_priv = device->post_data;
 
-	if (loading_timeout) {
-		fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
+	if (post_timeout) {
+		fw_priv->timeout.expires = jiffies + post_timeout * HZ;
 		add_timer(&fw_priv->timeout);
 	}
 
-	kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
+	printk(KERN_ERR "request_firmware: hotplug event\n");
+	kobject_hotplug(&device->kobj, KOBJ_POST, post_hotplug);
 	wait_for_completion(&fw_priv->completion);
 	set_bit(FW_STATUS_DONE, &fw_priv->status);
+	printk(KERN_ERR "request_firmware: complete\n");
 
 	del_timer_sync(&fw_priv->timeout);
 
-	down(&fw_lock);
-	if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
-		retval = -ENOENT;
-		release_firmware(fw_priv->fw);
-		*firmware_p = NULL;
+	if (firmware_p) {
+		down(&fw_lock);
+		if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
+			retval = -ENOENT;
+			release_firmware(fw_priv->fw);
+			*firmware_p = NULL;
+		}
+		fw_priv->fw = NULL;
+		up(&fw_lock);
+	
+		sysfs_remove_bin_file(&device->kobj, &fw_priv->attr_data);
 	}
-	fw_priv->fw = NULL;
-	up(&fw_lock);
-	class_device_unregister(class_dev);
+	device_remove_file(device, &dev_attr_post_status);
+	
 	goto out;
 
 error_kfree_fw:
-	kfree(firmware);
-	*firmware_p = NULL;
+	if (firmware) {
+		kfree(firmware);
+		*firmware_p = NULL;
+	}
 out:
 	return retval;
 }
 
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+		 struct device *device)
+{
+	if (!firmware_p)
+		return -EINVAL;
+	return request(firmware_p, name, device);
+}
+EXPORT_SYMBOL(request_firmware);
+
 /**
  * release_firmware: - release the resource associated with a firmware image
  **/
@@ -455,6 +401,7 @@
 		kfree(fw);
 	}
 }
+EXPORT_SYMBOL(release_firmware);
 
 /**
  * register_firmware: - provide a firmware image for later usage
@@ -472,6 +419,7 @@
 	 * decide if firmware caching is reasonable just leave it as a
 	 * noop */
 }
+EXPORT_SYMBOL(register_firmware);
 
 /* Async support */
 struct firmware_work {
@@ -480,11 +428,12 @@
 	const char *name;
 	struct device *device;
 	void *context;
-	void (*cont)(const struct firmware *fw, void *context);
+	void (*cont_fw)(const struct firmware *fw, void *context);
+	void (*cont_post)(void *context);
 };
 
 static int
-request_firmware_work_func(void *arg)
+request_post_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
@@ -492,10 +441,15 @@
 		WARN_ON(1);
 		return 0;
 	}
-	daemonize("%s/%s", "firmware", fw_work->name);
-	request_firmware(&fw, fw_work->name, fw_work->device);
-	fw_work->cont(fw, fw_work->context);
-	release_firmware(fw);
+	daemonize("%s/%s", "post", fw_work->name);
+	if (fw_work->cont_fw) {
+		request(&fw, fw_work->name, fw_work->device);
+		fw_work->cont_fw(fw, fw_work->context);
+		release_firmware(fw);
+	} else {
+		request(NULL, fw_work->name, fw_work->device);
+		fw_work->cont_post(fw_work->context);
+	}
 	module_put(fw_work->module);
 	kfree(fw_work);
 	return 0;
@@ -515,11 +469,12 @@
  *	@fw may be %NULL if firmware request fails.
  *
  **/
-int
-request_firmware_nowait(
+static int
+request_nowait(
 	struct module *module,
 	const char *name, struct device *device, void *context,
-	void (*cont)(const struct firmware *fw, void *context))
+	void (*cont_fw)(const struct firmware *fw, void *context),
+	void (*cont_post)(void *context))
 {
 	struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
 						GFP_ATOMIC);
@@ -537,47 +492,37 @@
 		.name = name,
 		.device = device,
 		.context = context,
-		.cont = cont,
+		.cont_fw = cont_fw,
+		.cont_post = cont_post,
 	};
 
-	ret = kernel_thread(request_firmware_work_func, fw_work,
+	ret = kernel_thread(request_post_work_func, fw_work,
 			    CLONE_FS | CLONE_FILES);
 
 	if (ret < 0) {
-		fw_work->cont(NULL, fw_work->context);
+		if (fw_work->cont_fw)
+			fw_work->cont_fw(NULL, fw_work->context);
+		if (fw_work->cont_post)
+			fw_work->cont_post(fw_work->context);
 		return ret;
 	}
 	return 0;
 }
 
-static int __init
-firmware_class_init(void)
+int
+request_firmware_nowait(struct module *module, const char *name,
+			struct device *device, void *context,
+			void (*cont_fw)(const struct firmware *fw, void *context))
 {
-	int error;
-	error = class_register(&firmware_class);
-	if (error) {
-		printk(KERN_ERR "%s: class_register failed\n", __FUNCTION__);
-		return error;
-	}
-	error = class_create_file(&firmware_class, &class_attr_timeout);
-	if (error) {
-		printk(KERN_ERR "%s: class_create_file failed\n",
-		       __FUNCTION__);
-		class_unregister(&firmware_class);
-	}
-	return error;
-
+	return request_nowait(module, name, device, context, cont_fw, NULL);
 }
-static void __exit
-firmware_class_exit(void)
+EXPORT_SYMBOL(request_firmware_nowait);
+
+int
+request_post_nowait(struct module *module, const char *name,
+		    struct device *device, void *context,
+		    void (*cont_post)(void *context))
 {
-	class_unregister(&firmware_class);
+	return request_nowait(module, name, device, context, NULL, cont_post);
 }
-
-module_init(firmware_class_init);
-module_exit(firmware_class_exit);
-
-EXPORT_SYMBOL(release_firmware);
-EXPORT_SYMBOL(request_firmware);
-EXPORT_SYMBOL(request_firmware_nowait);
-EXPORT_SYMBOL(register_firmware);
+EXPORT_SYMBOL(request_post_nowait);
===== drivers/video/aty/radeon_base.c 1.44 vs edited =====
--- 1.44/drivers/video/aty/radeon_base.c	2005-03-10 03:39:11 -05:00
+++ edited/drivers/video/aty/radeon_base.c	2005-03-19 21:17:49 -05:00
@@ -71,6 +71,7 @@
 #include <linux/vmalloc.h>
 #include <linux/device.h>
 #include <linux/i2c.h>
+#include <linux/firmware.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -2206,6 +2207,9 @@
 	.read	= radeon_show_edid2,
 };
 
+static void radeon_post_finished(void *data) {
+	printk(KERN_ERR "radeon_post_finished\n");
+}
 
 static int radeonfb_pci_register (struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
@@ -2410,6 +2414,8 @@
 	}
 #endif
 
+	request_post_nowait(THIS_MODULE, "vbios.vm86", &pdev->dev, rinfo,
radeon_post_finished);
+
 	printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
 
 	if (rinfo->bios_seg)
@@ -2453,7 +2459,19 @@
         if (!rinfo)
                 return;
  
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+
 	radeonfb_pm_exit(rinfo);
+
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
 
 #if 0
 	/* restore original state
===== include/linux/device.h 1.138 vs edited =====
--- 1.138/include/linux/device.h	2005-03-09 12:03:56 -05:00
+++ edited/include/linux/device.h	2005-03-19 11:11:34 -05:00
@@ -271,6 +271,7 @@
 	void		*driver_data;	/* data private to the driver */
 	void		*platform_data;	/* Platform specific data (e.g. ACPI,
 					   BIOS data relevant to device) */
+	void		*post_data;	/* active during firmware load or post */
 	struct dev_pm_info	power;
 
 	u32		detach_state;	/* State to enter when device is
@@ -399,6 +400,7 @@
 /* drivers/base/firmware.c */
 extern int firmware_register(struct subsystem *);
 extern void firmware_unregister(struct subsystem *);
+extern int post_timeout; /* In seconds */
 
 /* debugging and troubleshooting/diagnostic helpers. */
 #define dev_printk(level, dev, format, arg...)	\
===== include/linux/firmware.h 1.3 vs edited =====
--- 1.3/include/linux/firmware.h	2005-02-02 04:49:28 -05:00
+++ edited/include/linux/firmware.h	2005-03-19 21:12:52 -05:00
@@ -1,20 +1,27 @@
 #ifndef _LINUX_FIRMWARE_H
 #define _LINUX_FIRMWARE_H
+#include <linux/device.h>
 #include <linux/module.h>
 #include <linux/types.h>
+
 #define FIRMWARE_NAME_MAX 30 
+
 struct firmware {
 	size_t size;
 	u8 *data;
 };
-struct device;
+
+int request_post_nowait(struct module *module, const char *name,
+			struct device *device, void *context,
+			void (*cont_post)(void *context));
+
 int request_firmware(const struct firmware **fw, const char *name,
 		     struct device *device);
-int request_firmware_nowait(
-	struct module *module,
-	const char *name, struct device *device, void *context,
-	void (*cont)(const struct firmware *fw, void *context));
-
+int request_firmware_nowait(struct module *module, const char *name,
+		struct device *device, void *context, 
+		void (*cont_fw)(const struct firmware *fw, void *context));
 void release_firmware(const struct firmware *fw);
+
 void register_firmware(const char *name, const u8 *data, size_t size);
+
 #endif
===== include/linux/kobject.h 1.39 vs edited =====
--- 1.39/include/linux/kobject.h	2005-03-09 12:04:09 -05:00
+++ edited/include/linux/kobject.h	2005-03-19 01:32:55 -05:00
@@ -242,13 +242,19 @@
 extern void subsys_remove_file(struct subsystem * , struct subsys_attribute
*);
 
 #ifdef CONFIG_HOTPLUG
-void kobject_hotplug(struct kobject *kobj, enum kobject_action action);
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action,
+		     int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+		     int num_envp, char *buffer, int buffer_size)
+		    );
 int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
 			char *buffer, int buffer_size, int *cur_len,
 			const char *format, ...)
 	__attribute__((format (printf, 7, 8)));
 #else
-static inline void kobject_hotplug(struct kobject *kobj, enum
kobject_action action) { }
+static inline void kobject_hotplug(struct kobject *kobj, enum
kobject_action action,
+				   int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+				   int num_envp, char *buffer, int buffer_size)
+				  ) { }
 static inline int add_hotplug_env_var(char **envp, int num_envp, int
*cur_index,
 				      char *buffer, int buffer_size, int *cur_len, 
 				      const char *format, ...)
===== include/linux/kobject_uevent.h 1.6 vs edited =====
--- 1.6/include/linux/kobject_uevent.h	2004-11-08 14:43:30 -05:00
+++ edited/include/linux/kobject_uevent.h	2005-03-17 10:36:00 -05:00
@@ -29,6 +29,7 @@
 	KOBJ_UMOUNT	= (__force kobject_action_t) 0x05,	/* umount event for
block devices */
 	KOBJ_OFFLINE	= (__force kobject_action_t) 0x06,	/* offline event for
hotplug devices */
 	KOBJ_ONLINE	= (__force kobject_action_t) 0x07,	/* online event for
hotplug devices */
+	KOBJ_POST 	= (__force kobject_action_t) 0x08,	/* post event supports
user space initialization */
 };
 
 
===== lib/kobject.c 1.58 vs edited =====
--- 1.58/lib/kobject.c	2005-03-09 12:04:09 -05:00
+++ edited/lib/kobject.c	2005-03-19 01:39:37 -05:00
@@ -185,7 +185,7 @@
 		if (parent)
 			kobject_put(parent);
 	} else {
-		kobject_hotplug(kobj, KOBJ_ADD);
+		kobject_hotplug(kobj, KOBJ_ADD, NULL);
 	}
 
 	return error;
@@ -301,7 +301,7 @@
 
 void kobject_del(struct kobject * kobj)
 {
-	kobject_hotplug(kobj, KOBJ_REMOVE);
+	kobject_hotplug(kobj, KOBJ_REMOVE, NULL);
 	sysfs_remove_dir(kobj);
 	unlink(kobj);
 }
===== lib/kobject_uevent.c 1.18 vs edited =====
--- 1.18/lib/kobject_uevent.c	2005-01-08 00:44:13 -05:00
+++ edited/lib/kobject_uevent.c	2005-03-19 01:48:04 -05:00
@@ -44,6 +44,8 @@
 		return "offline";
 	case KOBJ_ONLINE:
 		return "online";
+	case KOBJ_POST:
+		return "post";
 	default:
 		return NULL;
 	}
@@ -187,7 +189,9 @@
  * @action: action that is happening (usually "ADD" or "REMOVE")
  * @kobj: struct kobject that the action is happening to
  */
-void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action,
+		int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+		int num_envp, char *buffer, int buffer_size))
 {
 	char *argv [3];
 	char **envp = NULL;
@@ -279,6 +283,17 @@
 	if (hotplug_ops->hotplug) {
 		/* have the kset specific function add its stuff */
 		retval = hotplug_ops->hotplug (kset, kobj,
+				  &envp[i], NUM_ENVP - i, scratch,
+				  BUFFER_SIZE - (scratch - buffer));
+		if (retval) {
+			pr_debug ("%s - hotplug() returned %d\n",
+				  __FUNCTION__, retval);
+			goto exit;
+		}
+	}
+	if (hotplug) {
+		/* have the call specific function add on its stuff */
+		retval = hotplug (kset, kobj,
 				  &envp[i], NUM_ENVP - i, scratch,
 				  BUFFER_SIZE - (scratch - buffer));
 		if (retval) {

===== drivers/acpi/container.c 1.2 vs edited =====
--- 1.2/drivers/acpi/container.c	2004-11-11 02:56:31 -05:00
+++ edited/drivers/acpi/container.c	2005-03-19 01:38:11 -05:00
@@ -182,16 +182,16 @@
 			if (ACPI_FAILURE(status) || !device) {
 				result = container_device_add(&device, handle);
 				if (!result)
-					kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+					kobject_hotplug(&device->kobj, KOBJ_ONLINE, NULL);
 			} else {
 				/* device exist and this is a remove request */
-				kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+				kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 			}
 		}
 		break;
 	case ACPI_NOTIFY_EJECT_REQUEST:
 		if (!acpi_bus_get_device(handle, &device) && device) {
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 		}
 		break;
 	default:
===== drivers/acpi/processor_core.c 1.80 vs edited =====
--- 1.80/drivers/acpi/processor_core.c	2004-12-14 07:14:54 -05:00
+++ edited/drivers/acpi/processor_core.c	2005-03-19 01:38:57 -05:00
@@ -730,7 +730,7 @@
 		return_VALUE(-ENODEV);
 
 	if ((pr->id >=0) && (pr->id < NR_CPUS)) {
-		kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE);
+		kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE, NULL);
 	}
 	return_VALUE(0);
 }
@@ -774,13 +774,13 @@
 		}
 
 		if (pr->id >= 0 && (pr->id < NR_CPUS)) {
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 			break;
 		}
 
 		result = acpi_processor_start(device);
 		if ((!result) && ((pr->id >=0) && (pr->id < NR_CPUS))) {
-			kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+			kobject_hotplug(&device->kobj, KOBJ_ONLINE, NULL);
 		} else {
 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				"Device [%s] failed to start\n",
@@ -801,7 +801,7 @@
 		}
 
 		if ((pr->id < NR_CPUS) && (cpu_present(pr->id)))
-			kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&device->kobj, KOBJ_OFFLINE, NULL);
 		break;
 	default:
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
===== drivers/base/cpu.c 1.22 vs edited =====
--- 1.22/drivers/base/cpu.c	2005-01-31 01:33:46 -05:00
+++ edited/drivers/base/cpu.c	2005-03-19 01:39:16 -05:00
@@ -33,7 +33,7 @@
 	case '0':
 		ret = cpu_down(cpu->sysdev.id);
 		if (!ret)
-			kobject_hotplug(&dev->kobj, KOBJ_OFFLINE);
+			kobject_hotplug(&dev->kobj, KOBJ_OFFLINE, NULL);
 		break;
 	case '1':
 		ret = cpu_up(cpu->sysdev.id);
===== drivers/base/firmware.c 1.9 vs edited =====
--- 1.9/drivers/base/firmware.c	2004-09-24 14:45:35 -04:00
+++ edited/drivers/base/firmware.c	2005-03-19 11:16:01 -05:00
@@ -14,21 +14,57 @@
 
 static decl_subsys(firmware, NULL, NULL);
 
+int post_timeout = 10; /* In seconds */
+EXPORT_SYMBOL_GPL(post_timeout);
+
 int firmware_register(struct subsystem * s)
 {
 	kset_set_kset_s(s, firmware_subsys);
 	return subsystem_register(s);
 }
+EXPORT_SYMBOL_GPL(firmware_register);
 
 void firmware_unregister(struct subsystem * s)
 {
 	subsystem_unregister(s);
 }
+EXPORT_SYMBOL_GPL(firmware_unregister);
+
+/**
+ * post_timeout_store:
+ * Description:
+ *	Sets the number of seconds to wait for the post/firmware load.
+ *	Once this expires an error will be return to the driver and no
+ *	firmware will be provided.
+ *
+ *	Note: zero means 'wait for ever'
+ *
+ **/
+static ssize_t post_timeout_store(struct subsystem *subsys, const char *buf, size_t
count)
+{
+	post_timeout = simple_strtol(buf, NULL, 10);
+	return count;
+}
+static ssize_t post_timeout_show(struct subsystem *subsys, char *buf)
+{
+	return sprintf(buf, "%d\n", post_timeout);
+}
+static struct subsys_attribute post_timeout_attr = {
+	.attr = {.name = "post_timeout", .mode = 0644, .owner = THIS_MODULE},
+	.show = post_timeout_show,
+	.store = post_timeout_store
+};
 
 int __init firmware_init(void)
 {
-	return subsystem_register(&firmware_subsys);
+	int error;
+	if ((error = subsystem_register(&firmware_subsys)))
+		return error;
+	
+	if ((error = subsys_create_file(&firmware_subsys, &post_timeout_attr))) {
+		subsystem_unregister(&firmware_subsys);
+		return error;
+	}
+	return 0;
 }
 
-EXPORT_SYMBOL_GPL(firmware_register);
-EXPORT_SYMBOL_GPL(firmware_unregister);
===== drivers/base/firmware_class.c 1.25 vs edited =====
--- 1.25/drivers/base/firmware_class.c	2004-11-26 15:26:48 -05:00
+++ edited/drivers/base/firmware_class.c	2005-03-19 21:11:15 -05:00
@@ -1,7 +1,8 @@
 /*
- * firmware_class.c - Multi purpose firmware loading support
+ * firmware_class.c - Multi purpose post support
  *
  * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ * Copyright (c) 2005 Jon Smirl <jonsmirl@gmail.com>
  *
  * Please see Documentation/firmware_class/ for more information.
  *
@@ -20,7 +21,7 @@
 #include "base.h"
 
 MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
-MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_DESCRIPTION("Multi purpose post support");
 MODULE_LICENSE("GPL");
 
 enum {
@@ -30,8 +31,6 @@
 	FW_STATUS_READY,
 };
 
-static int loading_timeout = 10;	/* In seconds */
-
 /* fw_lock could be moved to 'struct firmware_priv' but since it is just
  * guarding for corner cases a global lock should be OK */
 static DECLARE_MUTEX(fw_lock);
@@ -49,69 +48,41 @@
 static inline void
 fw_load_abort(struct firmware_priv *fw_priv)
 {
+	printk(KERN_ERR "fw_load_abort\n");
 	set_bit(FW_STATUS_ABORT, &fw_priv->status);
 	wmb();
 	complete(&fw_priv->completion);
 }
 
-static ssize_t
-firmware_timeout_show(struct class *class, char *buf)
-{
-	return sprintf(buf, "%d\n", loading_timeout);
-}
-
-/**
- * firmware_timeout_store:
- * Description:
- *	Sets the number of seconds to wait for the firmware.  Once
- *	this expires an error will be return to the driver and no
- *	firmware will be provided.
- *
- *	Note: zero means 'wait for ever'
- *
- **/
-static ssize_t
-firmware_timeout_store(struct class *class, const char *buf, size_t count)
-{
-	loading_timeout = simple_strtol(buf, NULL, 10);
-	return count;
-}
-
-static CLASS_ATTR(timeout, 0644, firmware_timeout_show,
firmware_timeout_store);
-
-static void  fw_class_dev_release(struct class_device *class_dev);
-int firmware_class_hotplug(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
-
-static struct class firmware_class = {
-	.name		= "firmware",
-	.hotplug	= firmware_class_hotplug,
-	.release	= fw_class_dev_release,
-};
-
 int
-firmware_class_hotplug(struct class_device *class_dev, char **envp,
+post_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
 		       int num_envp, char *buffer, int buffer_size)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct firmware_priv *fw_priv = dev->post_data;
 	int i = 0, len = 0;
 
 	if (!test_bit(FW_STATUS_READY, &fw_priv->status))
 		return -ENODEV;
 
-	if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			"FIRMWARE=%s", fw_priv->fw_id))
-		return -ENOMEM;
-
+	if (fw_priv->fw) {
+		if (add_hotplug_env_var(envp, num_envp, &i, buffer,
+		    buffer_size, &len, "FIRMWARE=%s", fw_priv->fw_id))
+			return -ENOMEM;
+	} else {
+		if (add_hotplug_env_var(envp, num_envp, &i, buffer,
+		    buffer_size, &len, "POST=%s", fw_priv->fw_id))
+			return -ENOMEM;
+	}
 	envp[i] = NULL;
 
 	return 0;
 }
 
 static ssize_t
-firmware_loading_show(struct class_device *class_dev, char *buf)
+post_status_show(struct device *dev, char *buf)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct firmware_priv *fw_priv = dev->post_data;
 	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
 	return sprintf(buf, "%d\n", loading);
 }
@@ -126,13 +97,13 @@
  *	-1: Conclude the load with an error and discard any written data.
  **/
 static ssize_t
-firmware_loading_store(struct class_device *class_dev,
-		       const char *buf, size_t count)
+post_status_store(struct device *dev, const char *buf, size_t count)
 {
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
-	int loading = simple_strtol(buf, NULL, 10);
+	struct firmware_priv *fw_priv = dev->post_data;
+	int status = simple_strtol(buf, NULL, 10);
 
-	switch (loading) {
+	printk(KERN_ERR "post_status_store: status %d\n", status);
+	switch (status) {
 	case 1:
 		down(&fw_lock);
 		vfree(fw_priv->fw->data);
@@ -151,7 +122,7 @@
 		/* fallthrough */
 	default:
 		printk(KERN_ERR "%s: unexpected value (%d)\n", __FUNCTION__,
-		       loading);
+		       status);
 		/* fallthrough */
 	case -1:
 		fw_load_abort(fw_priv);
@@ -161,15 +132,14 @@
 	return count;
 }
 
-static CLASS_DEVICE_ATTR(loading, 0644,
-			firmware_loading_show, firmware_loading_store);
+static DEVICE_ATTR(post_status, 0644, post_status_show, post_status_store);
 
 static ssize_t
-firmware_data_read(struct kobject *kobj,
-		   char *buffer, loff_t offset, size_t count)
+post_data_read(struct kobject *kobj, 
+	       char *buffer, loff_t offset, size_t count)
 {
-	struct class_device *class_dev = to_class_dev(kobj);
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct firmware_priv *fw_priv = dev->post_data;
 	struct firmware *fw;
 	ssize_t ret_count = count;
 
@@ -191,6 +161,7 @@
 	up(&fw_lock);
 	return ret_count;
 }
+
 static int
 fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
 {
@@ -225,8 +196,8 @@
  *	the driver as a firmware image.
  **/
 static ssize_t
-firmware_data_write(struct kobject *kobj,
-		    char *buffer, loff_t offset, size_t count)
+post_data_write(struct kobject *kobj, 
+		char *buffer, loff_t offset, size_t count)
 {
 	struct class_device *class_dev = to_class_dev(kobj);
 	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
@@ -254,124 +225,82 @@
 	return retval;
 }
 static struct bin_attribute firmware_attr_data_tmpl = {
-	.attr = {.name = "data", .mode = 0644, .owner = THIS_MODULE},
+	.attr = {.name = "post_data", .mode = 0644, .owner = THIS_MODULE},
 	.size = 0,
-	.read = firmware_data_read,
-	.write = firmware_data_write,
+	.read = post_data_read,
+	.write = post_data_write,
 };
 
 static void
-fw_class_dev_release(struct class_device *class_dev)
-{
-	struct firmware_priv *fw_priv = class_get_devdata(class_dev);
-
-	kfree(fw_priv);
-	kfree(class_dev);
-
-	module_put(THIS_MODULE);
-}
-
-static void
-firmware_class_timeout(u_long data)
+post_timeout_abort(u_long data)
 {
 	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+	printk(KERN_ERR "post_timeout_abort: time out\n");
 	fw_load_abort(fw_priv);
 }
 
-static inline void
-fw_setup_class_device_id(struct class_device *class_dev, struct device *dev)
-{
-	/* XXX warning we should watch out for name collisions */
-	strlcpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE);
-}
-
 static int
-fw_register_class_device(struct class_device **class_dev_p,
-			 const char *fw_name, struct device *device)
+fw_register_device(const char *fw_name, struct device *device)
 {
 	int retval;
 	struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
 						GFP_KERNEL);
-	struct class_device *class_dev = kmalloc(sizeof (struct class_device),
-						 GFP_KERNEL);
-
-	*class_dev_p = NULL;
 
-	if (!fw_priv || !class_dev) {
+	if (!fw_priv) {
 		printk(KERN_ERR "%s: kmalloc failed\n", __FUNCTION__);
 		retval = -ENOMEM;
 		goto error_kfree;
 	}
 	memset(fw_priv, 0, sizeof (*fw_priv));
-	memset(class_dev, 0, sizeof (*class_dev));
 
 	init_completion(&fw_priv->completion);
 	fw_priv->attr_data = firmware_attr_data_tmpl;
 	strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);
 
-	fw_priv->timeout.function = firmware_class_timeout;
+	fw_priv->timeout.function = post_timeout_abort;
 	fw_priv->timeout.data = (u_long) fw_priv;
 	init_timer(&fw_priv->timeout);
+	
+	device->post_data = fw_priv;
 
-	fw_setup_class_device_id(class_dev, device);
-	class_dev->dev = device;
-	class_dev->class = &firmware_class;
-	class_set_devdata(class_dev, fw_priv);
-	retval = class_device_register(class_dev);
-	if (retval) {
-		printk(KERN_ERR "%s: class_device_register failed\n",
-		       __FUNCTION__);
-		goto error_kfree;
-	}
-	*class_dev_p = class_dev;
 	return 0;
 
 error_kfree:
 	kfree(fw_priv);
-	kfree(class_dev);
 	return retval;
 }
 
 static int
-fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p,
-		      const char *fw_name, struct device *device)
+fw_setup_device(struct firmware *fw, const char *fw_name, struct device
*device)
 {
-	struct class_device *class_dev;
 	struct firmware_priv *fw_priv;
 	int retval;
 
-	*class_dev_p = NULL;
-	retval = fw_register_class_device(&class_dev, fw_name, device);
+	retval = fw_register_device(fw_name, device);
 	if (retval)
 		goto out;
 
-	/* Need to pin this module until class device is destroyed */
-	__module_get(THIS_MODULE);
-
-	fw_priv = class_get_devdata(class_dev);
-
+	fw_priv = device->post_data;
 	fw_priv->fw = fw;
-	retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->attr_data);
+	
+	retval = sysfs_create_bin_file(&device->kobj, &fw_priv->attr_data);
 	if (retval) {
 		printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
 		       __FUNCTION__);
-		goto error_unreg;
+		goto out;
 	}
 
-	retval = class_device_create_file(class_dev,
-					  &class_device_attr_loading);
-	if (retval) {
-		printk(KERN_ERR "%s: class_device_create_file failed\n",
-		       __FUNCTION__);
-		goto error_unreg;
+	if (fw) {
+		retval = device_create_file(device, &dev_attr_post_status);
+		if (retval) {
+			printk(KERN_ERR "%s: device_create_file failed\n",
+			       __FUNCTION__);
+			goto out;
+		}
 	}
-
 	set_bit(FW_STATUS_READY, &fw_priv->status);
-	*class_dev_p = class_dev;
 	goto out;
 
-error_unreg:
-	class_device_unregister(class_dev);
 out:
 	return retval;
 }
@@ -388,62 +317,79 @@
  *	should be distinctive enough not to be confused with any other
  *	firmware image for this or any other device.
  **/
-int
-request_firmware(const struct firmware **firmware_p, const char *name,
+static int
+request(const struct firmware **firmware_p, const char *name,
 		 struct device *device)
 {
-	struct class_device *class_dev;
 	struct firmware_priv *fw_priv;
-	struct firmware *firmware;
+	struct firmware *firmware = NULL;
 	int retval;
 
-	if (!firmware_p)
-		return -EINVAL;
-
-	*firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL);
-	if (!firmware) {
-		printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
-		       __FUNCTION__);
-		retval = -ENOMEM;
-		goto out;
+	if (firmware_p) {
+		*firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL);
+		if (!firmware) {
+			printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
+			       __FUNCTION__);
+			retval = -ENOMEM;
+			goto out;
+		}
+		memset(firmware, 0, sizeof (*firmware));
 	}
-	memset(firmware, 0, sizeof (*firmware));
 
-	retval = fw_setup_class_device(firmware, &class_dev, name, device);
+	retval = fw_setup_device(firmware, name, device);
 	if (retval)
 		goto error_kfree_fw;
 
-	fw_priv = class_get_devdata(class_dev);
+	fw_priv = device->post_data;
 
-	if (loading_timeout) {
-		fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
+	if (post_timeout) {
+		fw_priv->timeout.expires = jiffies + post_timeout * HZ;
 		add_timer(&fw_priv->timeout);
 	}
 
-	kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
+	printk(KERN_ERR "request_firmware: hotplug event\n");
+	kobject_hotplug(&device->kobj, KOBJ_POST, post_hotplug);
 	wait_for_completion(&fw_priv->completion);
 	set_bit(FW_STATUS_DONE, &fw_priv->status);
+	printk(KERN_ERR "request_firmware: complete\n");
 
 	del_timer_sync(&fw_priv->timeout);
 
-	down(&fw_lock);
-	if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
-		retval = -ENOENT;
-		release_firmware(fw_priv->fw);
-		*firmware_p = NULL;
+	if (firmware_p) {
+		down(&fw_lock);
+		if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
+			retval = -ENOENT;
+			release_firmware(fw_priv->fw);
+			*firmware_p = NULL;
+		}
+		fw_priv->fw = NULL;
+		up(&fw_lock);
+	
+		sysfs_remove_bin_file(&device->kobj, &fw_priv->attr_data);
 	}
-	fw_priv->fw = NULL;
-	up(&fw_lock);
-	class_device_unregister(class_dev);
+	device_remove_file(device, &dev_attr_post_status);
+	
 	goto out;
 
 error_kfree_fw:
-	kfree(firmware);
-	*firmware_p = NULL;
+	if (firmware) {
+		kfree(firmware);
+		*firmware_p = NULL;
+	}
 out:
 	return retval;
 }
 
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+		 struct device *device)
+{
+	if (!firmware_p)
+		return -EINVAL;
+	return request(firmware_p, name, device);
+}
+EXPORT_SYMBOL(request_firmware);
+
 /**
  * release_firmware: - release the resource associated with a firmware image
  **/
@@ -455,6 +401,7 @@
 		kfree(fw);
 	}
 }
+EXPORT_SYMBOL(release_firmware);
 
 /**
  * register_firmware: - provide a firmware image for later usage
@@ -472,6 +419,7 @@
 	 * decide if firmware caching is reasonable just leave it as a
 	 * noop */
 }
+EXPORT_SYMBOL(register_firmware);
 
 /* Async support */
 struct firmware_work {
@@ -480,11 +428,12 @@
 	const char *name;
 	struct device *device;
 	void *context;
-	void (*cont)(const struct firmware *fw, void *context);
+	void (*cont_fw)(const struct firmware *fw, void *context);
+	void (*cont_post)(void *context);
 };
 
 static int
-request_firmware_work_func(void *arg)
+request_post_work_func(void *arg)
 {
 	struct firmware_work *fw_work = arg;
 	const struct firmware *fw;
@@ -492,10 +441,15 @@
 		WARN_ON(1);
 		return 0;
 	}
-	daemonize("%s/%s", "firmware", fw_work->name);
-	request_firmware(&fw, fw_work->name, fw_work->device);
-	fw_work->cont(fw, fw_work->context);
-	release_firmware(fw);
+	daemonize("%s/%s", "post", fw_work->name);
+	if (fw_work->cont_fw) {
+		request(&fw, fw_work->name, fw_work->device);
+		fw_work->cont_fw(fw, fw_work->context);
+		release_firmware(fw);
+	} else {
+		request(NULL, fw_work->name, fw_work->device);
+		fw_work->cont_post(fw_work->context);
+	}
 	module_put(fw_work->module);
 	kfree(fw_work);
 	return 0;
@@ -515,11 +469,12 @@
  *	@fw may be %NULL if firmware request fails.
  *
  **/
-int
-request_firmware_nowait(
+static int
+request_nowait(
 	struct module *module,
 	const char *name, struct device *device, void *context,
-	void (*cont)(const struct firmware *fw, void *context))
+	void (*cont_fw)(const struct firmware *fw, void *context),
+	void (*cont_post)(void *context))
 {
 	struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
 						GFP_ATOMIC);
@@ -537,47 +492,37 @@
 		.name = name,
 		.device = device,
 		.context = context,
-		.cont = cont,
+		.cont_fw = cont_fw,
+		.cont_post = cont_post,
 	};
 
-	ret = kernel_thread(request_firmware_work_func, fw_work,
+	ret = kernel_thread(request_post_work_func, fw_work,
 			    CLONE_FS | CLONE_FILES);
 
 	if (ret < 0) {
-		fw_work->cont(NULL, fw_work->context);
+		if (fw_work->cont_fw)
+			fw_work->cont_fw(NULL, fw_work->context);
+		if (fw_work->cont_post)
+			fw_work->cont_post(fw_work->context);
 		return ret;
 	}
 	return 0;
 }
 
-static int __init
-firmware_class_init(void)
+int
+request_firmware_nowait(struct module *module, const char *name,
+			struct device *device, void *context,
+			void (*cont_fw)(const struct firmware *fw, void *context))
 {
-	int error;
-	error = class_register(&firmware_class);
-	if (error) {
-		printk(KERN_ERR "%s: class_register failed\n", __FUNCTION__);
-		return error;
-	}
-	error = class_create_file(&firmware_class, &class_attr_timeout);
-	if (error) {
-		printk(KERN_ERR "%s: class_create_file failed\n",
-		       __FUNCTION__);
-		class_unregister(&firmware_class);
-	}
-	return error;
-
+	return request_nowait(module, name, device, context, cont_fw, NULL);
 }
-static void __exit
-firmware_class_exit(void)
+EXPORT_SYMBOL(request_firmware_nowait);
+
+int
+request_post_nowait(struct module *module, const char *name,
+		    struct device *device, void *context,
+		    void (*cont_post)(void *context))
 {
-	class_unregister(&firmware_class);
+	return request_nowait(module, name, device, context, NULL, cont_post);
 }
-
-module_init(firmware_class_init);
-module_exit(firmware_class_exit);
-
-EXPORT_SYMBOL(release_firmware);
-EXPORT_SYMBOL(request_firmware);
-EXPORT_SYMBOL(request_firmware_nowait);
-EXPORT_SYMBOL(register_firmware);
+EXPORT_SYMBOL(request_post_nowait);
===== drivers/video/aty/radeon_base.c 1.44 vs edited =====
--- 1.44/drivers/video/aty/radeon_base.c	2005-03-10 03:39:11 -05:00
+++ edited/drivers/video/aty/radeon_base.c	2005-03-19 21:17:49 -05:00
@@ -71,6 +71,7 @@
 #include <linux/vmalloc.h>
 #include <linux/device.h>
 #include <linux/i2c.h>
+#include <linux/firmware.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -2206,6 +2207,9 @@
 	.read	= radeon_show_edid2,
 };
 
+static void radeon_post_finished(void *data) {
+	printk(KERN_ERR "radeon_post_finished\n");
+}
 
 static int radeonfb_pci_register (struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
@@ -2410,6 +2414,8 @@
 	}
 #endif
 
+	request_post_nowait(THIS_MODULE, "vbios.vm86", &pdev->dev, rinfo,
radeon_post_finished);
+
 	printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
 
 	if (rinfo->bios_seg)
@@ -2453,7 +2459,19 @@
         if (!rinfo)
                 return;
  
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+
 	radeonfb_pm_exit(rinfo);
+
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
 
 #if 0
 	/* restore original state
===== include/linux/device.h 1.138 vs edited =====
--- 1.138/include/linux/device.h	2005-03-09 12:03:56 -05:00
+++ edited/include/linux/device.h	2005-03-19 11:11:34 -05:00
@@ -271,6 +271,7 @@
 	void		*driver_data;	/* data private to the driver */
 	void		*platform_data;	/* Platform specific data (e.g. ACPI,
 					   BIOS data relevant to device) */
+	void		*post_data;	/* active during firmware load or post */
 	struct dev_pm_info	power;
 
 	u32		detach_state;	/* State to enter when device is
@@ -399,6 +400,7 @@
 /* drivers/base/firmware.c */
 extern int firmware_register(struct subsystem *);
 extern void firmware_unregister(struct subsystem *);
+extern int post_timeout; /* In seconds */
 
 /* debugging and troubleshooting/diagnostic helpers. */
 #define dev_printk(level, dev, format, arg...)	\
===== include/linux/firmware.h 1.3 vs edited =====
--- 1.3/include/linux/firmware.h	2005-02-02 04:49:28 -05:00
+++ edited/include/linux/firmware.h	2005-03-19 21:12:52 -05:00
@@ -1,20 +1,27 @@
 #ifndef _LINUX_FIRMWARE_H
 #define _LINUX_FIRMWARE_H
+#include <linux/device.h>
 #include <linux/module.h>
 #include <linux/types.h>
+
 #define FIRMWARE_NAME_MAX 30 
+
 struct firmware {
 	size_t size;
 	u8 *data;
 };
-struct device;
+
+int request_post_nowait(struct module *module, const char *name,
+			struct device *device, void *context,
+			void (*cont_post)(void *context));
+
 int request_firmware(const struct firmware **fw, const char *name,
 		     struct device *device);
-int request_firmware_nowait(
-	struct module *module,
-	const char *name, struct device *device, void *context,
-	void (*cont)(const struct firmware *fw, void *context));
-
+int request_firmware_nowait(struct module *module, const char *name,
+		struct device *device, void *context, 
+		void (*cont_fw)(const struct firmware *fw, void *context));
 void release_firmware(const struct firmware *fw);
+
 void register_firmware(const char *name, const u8 *data, size_t size);
+
 #endif
===== include/linux/kobject.h 1.39 vs edited =====
--- 1.39/include/linux/kobject.h	2005-03-09 12:04:09 -05:00
+++ edited/include/linux/kobject.h	2005-03-19 01:32:55 -05:00
@@ -242,13 +242,19 @@
 extern void subsys_remove_file(struct subsystem * , struct subsys_attribute
*);
 
 #ifdef CONFIG_HOTPLUG
-void kobject_hotplug(struct kobject *kobj, enum kobject_action action);
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action,
+		     int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+		     int num_envp, char *buffer, int buffer_size)
+		    );
 int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
 			char *buffer, int buffer_size, int *cur_len,
 			const char *format, ...)
 	__attribute__((format (printf, 7, 8)));
 #else
-static inline void kobject_hotplug(struct kobject *kobj, enum kobject_action action) {
}
+static inline void kobject_hotplug(struct kobject *kobj, enum kobject_action
action,
+				   int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+				   int num_envp, char *buffer, int buffer_size)
+				  ) { }
 static inline int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
 				      char *buffer, int buffer_size, int *cur_len, 
 				      const char *format, ...)
===== include/linux/kobject_uevent.h 1.6 vs edited =====
--- 1.6/include/linux/kobject_uevent.h	2004-11-08 14:43:30 -05:00
+++ edited/include/linux/kobject_uevent.h	2005-03-17 10:36:00 -05:00
@@ -29,6 +29,7 @@
 	KOBJ_UMOUNT	= (__force kobject_action_t) 0x05,	/* umount event for block devices
*/
 	KOBJ_OFFLINE	= (__force kobject_action_t) 0x06,	/* offline event for hotplug devices
*/
 	KOBJ_ONLINE	= (__force kobject_action_t) 0x07,	/* online event for hotplug devices
*/
+	KOBJ_POST 	= (__force kobject_action_t) 0x08,	/* post event supports user space initialization
*/
 };
 
 
===== lib/kobject.c 1.58 vs edited =====
--- 1.58/lib/kobject.c	2005-03-09 12:04:09 -05:00
+++ edited/lib/kobject.c	2005-03-19 01:39:37 -05:00
@@ -185,7 +185,7 @@
 		if (parent)
 			kobject_put(parent);
 	} else {
-		kobject_hotplug(kobj, KOBJ_ADD);
+		kobject_hotplug(kobj, KOBJ_ADD, NULL);
 	}
 
 	return error;
@@ -301,7 +301,7 @@
 
 void kobject_del(struct kobject * kobj)
 {
-	kobject_hotplug(kobj, KOBJ_REMOVE);
+	kobject_hotplug(kobj, KOBJ_REMOVE, NULL);
 	sysfs_remove_dir(kobj);
 	unlink(kobj);
 }
===== lib/kobject_uevent.c 1.18 vs edited =====
--- 1.18/lib/kobject_uevent.c	2005-01-08 00:44:13 -05:00
+++ edited/lib/kobject_uevent.c	2005-03-19 01:48:04 -05:00
@@ -44,6 +44,8 @@
 		return "offline";
 	case KOBJ_ONLINE:
 		return "online";
+	case KOBJ_POST:
+		return "post";
 	default:
 		return NULL;
 	}
@@ -187,7 +189,9 @@
  * @action: action that is happening (usually "ADD" or "REMOVE")
  * @kobj: struct kobject that the action is happening to
  */
-void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action,
+		int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+		int num_envp, char *buffer, int buffer_size))
 {
 	char *argv [3];
 	char **envp = NULL;
@@ -279,6 +283,17 @@
 	if (hotplug_ops->hotplug) {
 		/* have the kset specific function add its stuff */
 		retval = hotplug_ops->hotplug (kset, kobj,
+				  &envp[i], NUM_ENVP - i, scratch,
+				  BUFFER_SIZE - (scratch - buffer));
+		if (retval) {
+			pr_debug ("%s - hotplug() returned %d\n",
+				  __FUNCTION__, retval);
+			goto exit;
+		}
+	}
+	if (hotplug) {
+		/* have the call specific function add on its stuff */
+		retval = hotplug (kset, kobj,
 				  &envp[i], NUM_ENVP - i, scratch,
 				  BUFFER_SIZE - (scratch - buffer));
 		if (retval) {



Copyright © 2005, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds