--- linux-ac7/drivers/usb/Config.in	Sat May 20 09:21:51 2000
+++ linux/drivers/usb/Config.in	Sun Jun  4 12:53:16 2000
@@ -10,6 +10,9 @@
 
 comment 'Miscellaneous USB options'
    bool '  Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
+   if [ "$CONFIG_KMOD" != "n" ]; then
+      bool '  Kernel USB Daemon (EXPERIMENTAL)' CONFIG_USB_KUSBD
+   fi
 
 comment 'USB Controllers'
    if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
--- linux-ac7/drivers/usb/hub.c	Sat May 20 11:23:54 2000
+++ linux/drivers/usb/hub.c	Sun Jun  4 14:38:47 2000
@@ -533,7 +533,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 */
+#ifndef	CONFIG_USB_KUSBD
+	exit_files(current);
+#endif
 	daemonize();
 
 	/* Setup a nice name */
--- linux-ac7/drivers/usb/usb.c	Sun Jun  4 13:29:32 2000
+++ linux/drivers/usb/usb.c	Sun Jun  4 14:34:26 2000
@@ -24,6 +24,16 @@
 #include <linux/bitops.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>  /* for in_interrupt() */
+
+#ifdef	CONFIG_USB_KUSBD
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#endif
+
 #ifdef CONFIG_USB_DEBUG
 	#define DEBUG
 #else
@@ -158,6 +168,8 @@
 	return NULL;
 }
 
+#if 0
+
 /*
  * calc_bus_time:
  *
@@ -235,6 +247,8 @@
 	return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? 0 : -1;
 } /* end check_bandwidth_alloc */
 
+#endif
+
 /*
  * New functions for (de)registering a controller
  */
@@ -421,6 +435,153 @@
 	return -1;
 }
 
+#ifdef	CONFIG_USB_KUSBD
+
+// XXX something in this prevents compiling as a module ...
+
+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)
+{
+	static char *argv [] = { "/etc/usb/policy", 0 };
+
+	void **param = (void **) argument;
+	char *envp [20];
+	struct usb_device *dev = (struct usb_device *) param [1];
+	char buf [256];
+	char *scratch = &buf [0];
+	int i = 0;
+	int status;
+	
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/usr/sbin:/bin:/usr/bin";
+#ifdef	DEBUG
+	envp [i++] = "DEBUG=kusbd";
+#endif
+
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "ACTION=%s", (char *) param [0]) + 1;
+
+#ifdef	CONFIG_USB_DEVICEFS
+	// XXX how can we avoid hardwiring this intelligence?
+	// if it's merged with devfs, the names will all change
+	// (bus1/device3 not 001/003; and /devfs or /dev).
+
+	envp [i++] = "DEVFS=/proc/bus/usb";
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+		dev->bus->busnum, dev->devnum) + 1;
+#endif
+
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+		dev->descriptor.idVendor,
+		dev->descriptor.idProduct);
+	scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+
+	envp [i++] = scratch;
+	if (dev->descriptor.bDeviceClass == 0
+			&& dev->descriptor.bNumConfigurations == 1
+			&& dev->actconfig->bNumInterfaces == 1) {
+		int alt = dev->actconfig->interface [0].act_altsetting;
+		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;
+	} else
+		scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+			dev->descriptor.bDeviceClass,
+			dev->descriptor.bDeviceSubClass,
+			dev->descriptor.bDeviceProtocol) + 1;
+
+	// XXX report DRIVERS=... binding info
+
+	envp [i++] = 0;
+
+	// assert: (scratch - buf) < sizeof buf
+
+	info ("kusbd %s: invoke %s", (char *) param [0], argv [0]);
+	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, which must permit fork/exec/...
+// otherwise, calls are from initting an HC root hub (e.g. by user level
+// modprobe or non-modular kernel boot).
+
+static void spawn_usbd_policy (char *verb, struct usb_device *dev)
+{
+	void *params [2] = { verb, (char *) dev };
+	int pid = kernel_thread (exec_usbd_policy, (void *) params, 0);
+	int pid2, retval;
+	mm_segment_t fs;
+
+	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);
+	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 %d", retval);
+}
+
+#endif
+
+
 /*
  * This entrypoint gets called for each new device.
  *
@@ -446,11 +607,24 @@
 		dbg("unhandled interfaces on device");
 
 	if (!claimed) {
-		warn("This device is not recognized by any installed USB driver.");
+		warn("This USB device is not recognized by any loaded driver.");
 #ifdef DEBUG
 		usb_show_device(dev);
 #endif
 	}
+
+#ifdef	CONFIG_USB_KUSBD
+	/*
+	 * Always let user mode policy services know about new devices, even
+	 * if a driver claimed one of their interfaces.  Additional drivers
+	 * might be needed for other interfaces, or to access vendor-specific
+	 * facilities.  System services may need to be (re)configured before
+	 * they can make use of this new device.
+	 */
+	spawn_usbd_policy ("add", dev);
+
+	// XXX where to report the "remove" action; usb_free_dev below?
+#endif
 }
 
 /*

