--- linux-2.4.0-test3/Documentation/Configure.help	Tue Jul 11 09:04:02 2000
+++ linux/Documentation/Configure.help	Tue Jul 11 09:04:07 2000
@@ -2507,11 +2507,19 @@
 
 Support for hot-pluggable devices
 CONFIG_HOTPLUG
-  Say Y here if you want to attach devices to your computer that can
-  be attached and detached while the system is running. The most
-  prominent example of this are PCMCIA- or PC-cards, credit-card size
-  devices such as network cards, modems or hard drives which are
+  Say Y here if you want to plug devices into your computer while
+  the system is running, and be able to use them quickly.  In many
+  cases, the devices can likewise be unplugged at any time too.
+
+  One well known example of this are PCMCIA- or PC-cards, credit-card
+  size devices such as network cards, modems or hard drives which are
   plugged into slots found on all modern laptop computers.
+
+  Enable this with USB and KMOD, and your kernel will automatically
+  call out to a user mode "policy agent" to load drivers and other
+  modules needed to use USB devices you plug in.  With a bit of work,
+  it can invoke other device setup tasks.  Get such agent software
+  (at http://www.linux-usb.org/policy.html) and install it.
 
 PCMCIA/Cardbus support
 CONFIG_PCMCIA
--- linux-2.4.0-test3/arch/i386/kernel/apm.c	Tue Jul 11 09:03:36 2000
+++ linux/arch/i386/kernel/apm.c	Tue Jul 11 10:48:51 2000
@@ -1413,7 +1413,7 @@
 
 	kapmd_running = 1;
 
-	exit_files(current);	/* daemonize doesn't do exit_files */
+	// exit_files(current);	/* daemonize doesn't do exit_files */
 	daemonize();
 
 	strcpy(current->comm, "kapmd");
--- linux-2.4.0-test3/drivers/usb/hub.c	Tue Jul 11 09:03:29 2000
+++ linux/drivers/usb/hub.c	Tue Jul 11 09:12:55 2000
@@ -587,7 +587,9 @@
 	 * This thread doesn't need any user-level access,
 	 * so get rid of all our resources
 	 */
-	exit_files(current);  /* daemonize doesn't do exit_files */
+	exit_files(current);
+	current->files = init_task.files;
+	atomic_inc(&current->files->count);
 	daemonize();
 
 	/* Setup a nice name */
--- linux-2.4.0-test3/drivers/usb/usb.c	Tue Jul 11 09:03:29 2000
+++ linux/drivers/usb/usb.c	Tue Jul 11 10:45:53 2000
@@ -25,6 +25,21 @@
 #include <linux/bitops.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>  /* for in_interrupt() */
+
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* waitpid() call glue uses this */
+static int errno;
+#endif
+
+
 #ifdef CONFIG_USB_DEBUG
 	#define DEBUG
 #else
@@ -475,6 +490,194 @@
 	return -1;
 }
 
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+
+/* Invoke /sbin/hotplug (or equiv.) on USB plug/unplug */
+
+static int to_bcd (char *buf, __u16 *bcdValue)
+{
+	int	retval = 0;
+	char	*value = (char *) bcdValue;
+	int	temp;
+
+	if ((temp = value [1] & 0xf0) != 0) {
+		temp >>= 4;
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+
+	temp = value [1] & 0x0f;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	*buf++ = '.';
+	retval++;
+
+	temp = value [0] & 0xf0;
+	temp >>= 4;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	if ((temp = value [0] & 0x0f) != 0) {
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+	*buf++ = 0;
+
+	return retval;
+}
+
+static int exec_usbd_policy (void *argument)
+{
+	void **param = (void **) argument;
+	struct usb_device *dev = (struct usb_device *) param [1];
+	char *argv [10], *envp [20], buf [256];
+	char *scratch = &buf [0];
+	int i = 0, status;
+
+	/* minimal standardized params to hotplug command */
+	argv [0] = hotplug_path;
+	argv [1] = "usb";
+	argv [2] = (char *) param [0];
+	argv [3] = 0;
+
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef	DEBUG
+	/* hint that policy agent should enter no-stdout debug mode */
+	envp [i++] = "DEBUG=kernel";
+#endif
+	/* extensible set of named bus-specific parameters,
+	 * supporting multiple driver selection algorithms.
+	 */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "ACTION=%s", argv [2]) + 1;
+dbg (envp [i - 1]);
+
+#ifdef	CONFIG_USB_DEVICEFS
+	/* If this is available, userspace programs can directly read
+	 * all the device descriptors we didn't tell them about.
+	 *
+	 * XXX how little intelligence can we hardwire?
+	 * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
+	 * (b) naming convention: bus1/device3, 001/003 etc.
+	 */
+	envp [i++] = "DEVFS=/proc/bus/usb";
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+		dev->bus->busnum, dev->devnum) + 1;
+dbg (envp [i - 1]);
+#endif
+
+	/* per-device configuration hacks are always possible */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+		dev->descriptor.idVendor,
+		dev->descriptor.idProduct);
+	scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+dbg (envp [i - 1]);
+
+	/* otherwise, use a simple (so far) generic driver binding model */
+	envp [i++] = scratch;
+	if (dev->descriptor.bDeviceClass == 0) {
+		int alt = dev->actconfig->interface [0].act_altsetting;
+
+		/* simple/common case: one config, one interface, one driver
+		 * unsimple case:  everything else (per-device ...)
+		 */
+		scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+			+ 1;
+		/* INTERFACE-0, INTERFACE-1, ... ? */
+	} else {
+		/* simple/common case: generic device, handled generically */
+		scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+			dev->descriptor.bDeviceClass,
+			dev->descriptor.bDeviceSubClass,
+			dev->descriptor.bDeviceProtocol) + 1;
+	}
+dbg (envp [i - 1]);
+
+	/* XXX report DRIVERS=... binding info */
+
+	envp [i++] = 0;
+
+	/* assert: (scratch - buf) < sizeof buf */
+
+	dbg ("kusbd: %s %s %s", argv [0], argv [1], argv [2]);
+	/* setsid (); ? */
+	status = exec_usermodehelper (argv [0], argv, envp);
+	if (status < 0)
+		err ("failed exec of %s, status = %d", argv [0], status);
+	return status;
+}
+
+/* Calls here are normally from khubd in eventual response to device
+ * change. Else, from initting an HC's root hub ... unless it's an
+ * interrupt context, this invokes site administration/setup policy.
+ *
+ * The policy agent needs to know that the worst case is just a device
+ * disconnecting (timeout?) before it completes its work -- rather than
+ * a risk that some other device will be given the same bus addressing.
+ * Some synchronization (here, waitpid) is important: removes can't start
+ * processing before the add-device processing completes, and vice versa.
+ */
+static void spawn_usbd_policy (char *verb, struct usb_device *dev)
+{
+	void *params [2] = { verb, dev };
+	int pid, pid2, retval;
+	mm_segment_t fs;
+
+	if (in_interrupt ()) {
+	    dbg ("spawn %s dev %d in_interrupt", verb, dev->devnum);
+	    return;
+	}
+
+	if (!hotplug_path [0])
+		return;
+
+	pid = kernel_thread (exec_usbd_policy, (void *) params, 0);
+	if (pid < 0) {
+		err ("fork of usbd policy failed, errno = %d", -pid);
+		return;
+	}
+
+	/* could set signal mask here */
+	fs = get_fs ();
+        set_fs (KERNEL_DS);      /* Allows retval to be in kernel space. */
+	pid2 = waitpid (pid, &retval, __WCLONE); /* "errno" gets assigned */
+	set_fs (fs);
+	/* could restore signal mask here */
+
+        if (pid2 != pid) {
+                err ("waitpid(%d) failed, returning %d\n",
+		       pid, pid2);
+		return;
+        }
+
+	/* some return values should trigger re-spawning
+	 * with updated driver binding info */
+	dbg ("usbd policy returned 0x%x, errno %d", retval, errno);
+}
+
+#else
+
+static inline void
+spawn_usbd_policy (char *verb, struct usb_device *dev)
+{ } 
+
+#endif	/* KMOD && HOTPLUG */
+
+
 /*
  * This entrypoint gets called for each new device.
  *
@@ -500,11 +703,14 @@
 		dbg("unhandled interfaces on device");
 
 	if (!claimed) {
-		warn("This device is not recognized by any installed USB driver.");
+		warn("USB device %d is not claimed by any active driver.",
+			dev->devnum);
 #ifdef DEBUG
 		usb_show_device(dev);
 #endif
 	}
+
+	spawn_usbd_policy ("add", dev);
 }
 
 /*
@@ -1188,6 +1394,8 @@
 	*pdev = NULL;
 
 	info("USB disconnect on device %d", dev->devnum);
+
+	spawn_usbd_policy ("remove", dev);
 
 	if (dev->actconfig) {
 		for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {

