All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jon Smirl <jonsmirl@gmail.com>
To: linux-hotplug@vger.kernel.org
Subject: Rework of request firmware
Date: Sun, 20 Mar 2005 04:06:59 +0000	[thread overview]
Message-ID: <9e473391050319200625032789@mail.gmail.com> (raw)

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

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) {

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: firmware.patch --]
[-- Type: text/x-diff; name="firmware.patch", Size: 28315 bytes --]

===== 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) {

[-- Attachment #3: 30-post.hotplug --]
[-- Type: application/octet-stream, Size: 1343 bytes --]

#!/bin/sh
#
# Firmware-specific hotplug policy agent.
#
# Kernel firmware hotplug params include:
#
#       ACTION=%s [add or remove]
#       DEVPATH=%s [in 2.5 kernels, /sys/$DEVPATH]
#       FIRMWARE=%s
#
# HISTORY:
#
# 24-Jul-2003   Initial version of "new" hotplug agent.
#
# $Id: firmware.agent,v 1.3 2004/03/14 15:52:56 ukai Exp $
#

cd /etc/hotplug
. ./hotplug.functions
# DEBUG=yes export DEBUG

# directory of the firmware files
FIRMWARE_DIR=/lib/firmware

# mountpoint of sysfs
SYSFS=$(sed -n 's/^.* \([^ ]*\) sysfs .*$/\1/p' /proc/mounts)

# use /proc for 2.4 kernels
if [ "$SYSFS" = "" ]; then
    SYSFS=/proc
fi

#
# What to do with this firmware hotplug event?
#
case "$ACTION" in

post)
    if [ ! -e $SYSFS/$DEVPATH/post_status ]; then
        sleep 1
    fi

    if [ "$FIRMWARE" != "" ]; then
        if [ -f "$FIRMWARE_DIR/$FIRMWARE" ]; then
            echo 1 > $SYSFS/$DEVPATH/post_status
            cp "$FIRMWARE_DIR/$FIRMWARE" $SYSFS/$DEVPATH/post_data
            echo 0 > $SYSFS/$DEVPATH/post_status
        else
            echo -1 > $SYSFS/$DEVPATH/post_status
        fi
    fi
    if [ "$POST" != "" ]; then
	echo "Posting! $POST" >/post
        echo 1 > $SYSFS/$DEVPATH/post_status
        exec $POST "$@"
        echo 0 > $SYSFS/$DEVPATH/post_status
	echo "Posted! $POST" >>/post
    fi
    ;;

*)
    ;;

esac

             reply	other threads:[~2005-03-20  4:06 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-03-20  4:06 Jon Smirl [this message]
2005-03-20 13:37 ` Rework of request firmware Kay Sievers
2005-03-20 15:35 ` Jon Smirl
2005-03-20 16:47 ` Jon Smirl
2005-03-20 17:16 ` Kay Sievers
2005-03-20 17:19 ` David Zeuthen
2005-03-20 17:39 ` Kay Sievers
2005-03-20 17:52 ` Kay Sievers
2005-03-20 17:52 ` Darren Salt
2005-03-20 18:00 ` Kay Sievers
2005-03-20 18:24 ` Jon Smirl
2005-03-20 19:02 ` Jon Smirl
2005-03-20 19:17 ` Kay Sievers
2005-03-20 19:27 ` Kay Sievers
2005-03-20 19:50 ` Jon Smirl
2005-03-20 20:25 ` Kay Sievers
2005-03-20 20:39 ` Jon Smirl
2005-03-21  2:37 ` Kay Sievers
2005-03-22  0:29 ` Linas Vepstas
2005-03-22  2:25 ` Kay Sievers
2005-03-22  3:06 ` Jon Smirl
2005-03-22  8:27 ` Roman Kagan
2005-03-22 10:45 ` Kay Sievers
2005-03-22 10:55 ` Kay Sievers
2005-03-22 14:37 ` Jon Smirl
2005-03-22 17:53 ` Linas Vepstas
2005-03-22 18:09 ` Linas Vepstas
2005-03-22 18:43 ` Jon Smirl
2005-03-23  1:08 ` 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=9e473391050319200625032789@mail.gmail.com \
    --to=jonsmirl@gmail.com \
    --cc=linux-hotplug@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.