Subject: Device Properties: The Last Incarnation
To: None <tech-kern@netbsd.org>
From: None <eeh@netbsd.org>
List: tech-kern
Date: 03/03/2001 01:56:00
These are the machine-independent parts of the device
properties implementation.  It should cause no impact
on existing drivers unless they attempt to use device
properties during attach.

Locators are a bit of a problem.  Wildcarded locators
are all `-1' which doesn't help much, and always adding
them to the device node by config_search/config_attach
would overwrite any locators that the parent bus has
attached.  So, instead, I prepend them with `loc-'
when creating properties.

A sample attach implementation using locators will
be sent separately.

Eduado


Index: sys/device.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/device.h,v
retrieving revision 1.45
diff -u -r1.45 device.h
--- sys/device.h	2000/12/01 02:07:04	1.45
+++ sys/device.h	2001/03/03 01:48:49
@@ -88,6 +88,7 @@
  * Note that all ``system'' device types are listed here.
  */
 enum devclass {
+	DV_EMPTY,
 	DV_DULL,		/* generic, no special info */
 	DV_CPU,			/* CPU (carries resource utilization) */
 	DV_DISK,		/* disk drive (label, etc) */
@@ -104,18 +105,43 @@
 	DVACT_DEACTIVATE	/* deactivate the device */
 };
 
+/*
+ * Device properties list entry.
+ *
+ * It contains both the data and name string within it.  The
+ * size of dp_val can be calculated as:
+ *
+ *	(dp_len + strlen(dp_name) + 1)
+ *
+ * rounded up to sizeof(long).
+ */
+#define	PROP_WAIT	1
+#define	PROP_SEARCH	2
+#define	PROP_STRING	4
+#define	PROP_CONST	8
+
+struct devprop {
+	SLIST_ENTRY(devprop) dp_list;
+	size_t dp_len;
+	void *dp_val;		/* This contains the value data and name string */
+	const char dp_name[1];	/* This contains the name string and maybe value. */
+};
+
 struct device {
 	enum	devclass dv_class;	/* this device's classification */
 	TAILQ_ENTRY(device) dv_list;	/* entry on list of all devices */
 	struct	cfdata *dv_cfdata;	/* config data that found us */
 	int	dv_unit;		/* device unit number */
+	int	dv_flags;		/* misc. flags; see below */
 	char	dv_xname[16];		/* external name (name + unit) */
 	struct	device *dv_parent;	/* pointer to parent device */
-	int	dv_flags;		/* misc. flags; see below */
+	SLIST_HEAD(devpropslist, devprop) dv_props;
+	void	*dv_private;
 };
 
 /* dv_flags */
 #define	DVF_ACTIVE	0x0001		/* device is activated */
+#define	DVF_SOFTC	0x0002		/* separate softc has been allocated. */
 
 TAILQ_HEAD(devicelist, device);
 
@@ -232,6 +258,16 @@
 	int	pdev_count;
 };
 
+/*
+ * Macros to support new driver functionality.
+ */
+#define	DEV_PRIVATE(d)			(d)->dv_private
+#define	DEV_CFA_DECL(n, s, m, a) \
+struct cfattach n = { -(s), m, a }
+
+#define	DEV_PROTECT(dev)
+#define	DEV_UNPROTECT(dev)
+
 #ifdef _KERNEL
 
 extern struct devicelist alldevs;	/* list of all devices */
@@ -241,12 +277,38 @@
 extern __volatile int config_pending; 	/* semaphore for mountroot */
 
 void configure(void);
-struct cfdata *config_search(cfmatch_t, struct device *, void *);
-struct cfdata *config_rootsearch(cfmatch_t, const char *, void *);
-struct device *config_found_sm(struct device *, void *, cfprint_t, cfmatch_t);
+
+/* Old functions */
 struct device *config_rootfound(const char *, void *);
+#if 0
+struct cfdata *config_rootsearch(cfmatch_t, const char *, void *);
+struct cfdata *config_search(cfmatch_t, struct device *, void *);
 struct device *config_attach(struct device *, struct cfdata *, void *,
-    cfprint_t);
+			     cfprint_t, struct device *);
+#endif
+/* Transitional functions */
+struct device *config_found_sad(struct device *, void *, cfprint_t, cfmatch_t, 
+				struct device *);
+struct cfdata *config_search_ad(cfmatch_t, struct device *, void *, struct device *);
+struct cfdata *config_rootsearch_ad(cfmatch_t, const char *, void *, 
+				    struct device *);
+struct device *config_attach_ad(struct device *, struct cfdata *, void *, 
+				cfprint_t, struct device *);
+
+/* New functions */
+struct device *dev_config_create(struct device *, int);
+#if 0
+struct device *dev_config_found(struct device *, struct device *, 
+				cfmatch_t, cfprint_t);
+struct cfdata *dev_config_search(struct device *, struct device *, cfmatch_t);
+struct device *dev_config_attach(struct device *, struct device *, 
+				 struct cfdata *, cfprint_t);
+#else
+#define dev_config_found(d, c, m, p)	config_found_sad((d), NULL, (p), (m), (c))
+#define	dev_config_search(d, c, f)	config_search_ad((f), (d), NULL, (c))
+#define	dev_config_attach(d, n, c, p)	config_attach_ad((d), (c), NULL, (p), (n))
+#endif
+
 int config_detach(struct device *, int);
 int config_activate(struct device *);
 int config_deactivate(struct device *);
@@ -265,11 +327,21 @@
 void	evcnt_detach(struct evcnt *);
 
 /* compatibility definitions */
-#define config_found(d, a, p)	config_found_sm((d), (a), (p), NULL)
+#define	config_search(f, d, a)		config_search_ad((f), (d), (a), NULL)
+#define	config_rootsearch(f, d, a)	config_search_ad((f), (d), (a), NULL)
+#define config_found(d, a, p)		config_found_sm((d), (a), (p), NULL)
+#define config_found_sm(d, a, p, s)	config_found_sad((d), (a), (p), (s), NULL)
+#define	config_attach(d, c, a, p)	config_attach_ad((d), (c), (a), (p), NULL)
 
 /* convenience definitions */
 #define	device_lookup(cfd, unit)					\
 	(((unit) < (cfd)->cd_ndevs) ? (cfd)->cd_devs[(unit)] : NULL)
+
+/* Device properties */
+int dev_setprop(struct device *, const char *, void *, size_t, int);
+int dev_copyprobs(struct device *, struct device *, int);
+size_t dev_getprop(struct device *, const char *, void *, size_t, int);
+int dev_delprop(struct device *, const char *, int);
 
 #endif /* _KERNEL */
 
Index: kern/subr_autoconf.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/subr_autoconf.c,v
retrieving revision 1.55
diff -u -r1.55 subr_autoconf.c
--- kern/subr_autoconf.c	2000/07/08 18:11:02	1.55
+++ kern/subr_autoconf.c	2001/03/03 01:48:50
@@ -97,6 +97,9 @@
  * Autoconfiguration subroutines.
  */
 
+static void zap_devprops(struct device *);
+static struct device *dev_create(struct device *, ssize_t, int);
+
 /*
  * ioconf.c exports exactly two names: cfdata and cfroots.  All system
  * devices and drivers are found via these tables.
@@ -115,7 +118,9 @@
 };
 
 static char *number(char *, int);
-static void mapply(struct matchinfo *, struct cfdata *);
+static void mapply(struct matchinfo *, struct cfdata *, struct device *);
+static void locset(struct device *, struct cfdata *);
+static void locrm(struct device *, struct cfdata *);
 
 struct deferred_config {
 	TAILQ_ENTRY(deferred_config) dc_queue;
@@ -172,27 +177,75 @@
 }
 
 /*
+ * Set locators as properties on device node.
+ */
+static void
+locset(struct device *d, struct cfdata *cf)
+{
+	int i;
+	int flags = PROP_CONST;
+	char buf[32];
+
+	if (!cold) flags |= PROP_WAIT;
+
+	for (i=0; cf->cf_locnames[i]; i++) {
+		sprintf(buf, "loc-%s", cf->cf_locnames[i]);
+		dev_setprop(d, buf, &cf->cf_loc[i], 
+			    sizeof(int), flags);
+	}
+}
+
+/*
+ * Remove locator properties from device node.
+ */
+static void
+locrm(struct device *d, struct cfdata *cf)
+{
+	int i;
+	char buf[32];
+
+	for (i=0; cf->cf_locnames[i]; i++) {
+		sprintf(buf, "loc-%s", cf->cf_locnames[i]);
+		dev_delprop(d, buf, 0);
+	}
+}
+
+/*
  * Apply the matching function and choose the best.  This is used
  * a few times and we want to keep the code small.
  */
 static void
-mapply(struct matchinfo *m, struct cfdata *cf)
+mapply(struct matchinfo *m, struct cfdata *cf, struct device *child)
 {
 	int pri;
+	void *aux = m->aux;
 
+	if ((ssize_t)cf->cf_attach->ca_devsize < 0) {
+		/* New-style device driver */
+		if (!child)
+			panic("mapply: no device for new-style driver\n");
+		locset(child, cf);
+		child->dv_private = aux;
+		aux = child;
+	}
 	if (m->fn != NULL)
+		/* Someday the submatch function should use properties too. */
 		pri = (*m->fn)(m->parent, cf, m->aux);
 	else {
 	        if (cf->cf_attach->ca_match == NULL) {
 			panic("mapply: no match function for '%s' device\n",
 			    cf->cf_driver->cd_name);
 		}
-		pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux);
+		pri = (*cf->cf_attach->ca_match)(m->parent, cf, aux);
 	}
 	if (pri > m->pri) {
 		m->match = cf;
 		m->pri = pri;
 	}
+	if (child) {
+		locrm(child, cf);
+		child->dv_private = NULL;
+	}
 }
 
 /*
@@ -207,7 +260,8 @@
  * can be ignored).
  */
 struct cfdata *
-config_search(cfmatch_t fn, struct device *parent, void *aux)
+config_search_ad(cfmatch_t fn, struct device *parent, void *aux,
+		 struct device *child)
 {
 	struct cfdata *cf;
 	short *p;
@@ -218,6 +272,7 @@
 	m.aux = aux;
 	m.match = NULL;
 	m.pri = 0;
+
 	for (cf = cfdata; cf->cf_driver; cf++) {
 		/*
 		 * Skip cf if no longer eligible, otherwise scan through
@@ -227,7 +282,7 @@
 			continue;
 		for (p = cf->cf_parents; *p >= 0; p++)
 			if (parent->dv_cfdata == &cfdata[*p])
-				mapply(&m, cf);
+				mapply(&m, cf, child);
 	}
 	return (m.match);
 }
@@ -237,7 +292,8 @@
  * This is much like config_search, but there is no parent.
  */
 struct cfdata *
-config_rootsearch(cfmatch_t fn, const char *rootname, void *aux)
+config_rootsearch_ad(cfmatch_t fn, const char *rootname, void *aux, 
+		     struct device *root)
 {
 	struct cfdata *cf;
 	short *p;
@@ -256,7 +312,7 @@
 	for (p = cfroots; *p >= 0; p++) {
 		cf = &cfdata[*p];
 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
-			mapply(&m, cf);
+			mapply(&m, cf, root);
 	}
 	return (m.match);
 }
@@ -272,13 +328,18 @@
  * not configured, call the given `print' function and return 0.
  */
 struct device *
-config_found_sm(struct device *parent, void *aux, cfprint_t print,
-    cfmatch_t submatch)
+config_found_sad(struct device *parent, void *aux, cfprint_t print,
+    cfmatch_t submatch, struct device *d)
 {
 	struct cfdata *cf;
 
-	if ((cf = config_search(submatch, parent, aux)) != NULL)
-		return (config_attach(parent, cf, aux, print));
+	/* 
+	 * Creating the dummy device here is really optional.  But
+	 * it allows old-style bus drivers to attach new-style children.
+	 */
+	if (!d) d = dev_create(ROOT, 0, cold ? M_NOWAIT : M_WAITOK);
+	if ((cf = config_search_ad(submatch, parent, aux, d)) != NULL)
+		return (config_attach_ad(parent, cf, aux, print, d));
 	if (print)
 		printf("%s", msgs[(*print)(aux, parent->dv_xname)]);
 	return (NULL);
@@ -291,9 +352,11 @@
 config_rootfound(const char *rootname, void *aux)
 {
 	struct cfdata *cf;
+	struct device *root;
 
-	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
-		return (config_attach(ROOT, cf, aux, (cfprint_t)NULL));
+	root = dev_create(ROOT, 0, cold ? M_NOWAIT : M_WAITOK);
+	if ((cf = config_rootsearch_ad((cfmatch_t)NULL, rootname, aux, root)) != NULL)
+		return (config_attach_ad(ROOT, cf, aux, (cfprint_t)NULL, root));
 	printf("root device %s not configured\n", rootname);
 	return (NULL);
 }
@@ -313,23 +376,51 @@
 }
 
 /*
+ * Allocate an empty device node.  If `parent' is provided then the
+ * new node is attached to the tree under `parent'.  If `size'
+ * is provided, then the new device node is allocated to be that
+ * size.
+ */
+static struct device *
+dev_create(struct device *parent, ssize_t size, int wait) {
+	struct device * dev;
+
+	/* get memory for all device vars */
+	if (size == 0) 
+		size = sizeof(struct device);
+	dev = (struct device *)malloc(size, M_DEVBUF, wait);
+	if (!dev)
+	    panic("dev_create: memory allocation for device failed");
+	memset(dev, 0, size);
+	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);	/* link up */
+	dev->dv_class = DV_EMPTY;
+	if (parent)
+		dev->dv_parent = parent;
+	SLIST_INIT(&dev->dv_props);
+	return (dev);
+}
+
+/*
  * Attach a found device.  Allocates memory for device variables.
  */
 struct device *
-config_attach(struct device *parent, struct cfdata *cf, void *aux,
-	cfprint_t print)
+config_attach_ad(struct device *parent, struct cfdata *cf, void *aux,
+	cfprint_t print, struct device *propdev)
 {
 	struct device *dev;
 	struct cfdriver *cd;
 	struct cfattach *ca;
 	size_t lname, lunit;
+	ssize_t softsize;
 	const char *xunit;
 	int myunit;
 	char num[10];
 
 	cd = cf->cf_driver;
 	ca = cf->cf_attach;
-	if (ca->ca_devsize < sizeof(struct device))
+	softsize = ca->ca_devsize;
+	if (softsize < 0) softsize = -softsize;
+	if (softsize < sizeof(struct device))
 		panic("config_attach");
 #ifndef __BROKEN_CONFIG_UNIT_USAGE
 	if (cf->cf_fstate == FSTATE_STAR) {
@@ -360,19 +451,46 @@
 		panic("config_attach: device name too long");
 
 	/* get memory for all device vars */
-	dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF,
-	    cold ? M_NOWAIT : M_WAITOK);
-	if (!dev)
-	    panic("config_attach: memory allocation for device softc failed");
-	memset(dev, 0, ca->ca_devsize);
-	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);	/* link up */
+	if (softsize < 0) {
+		/* New-style device */
+		if (propdev) {
+			/* Great, we can use the propdev. */
+			dev = propdev;
+		} else {
+			dev = dev_create(parent, 0, cold ? M_NOWAIT : M_WAITOK);
+			if (!dev)
+				panic("config_attach: memory allocation for device failed");
+		}
+		dev->dv_private = malloc(softsize, M_DEVBUF,
+					 cold ? M_NOWAIT : M_WAITOK);
+		if (!dev->dv_private)
+			panic("config_attach: memory allocation for device softc failed");
+		dev->dv_flags |= DVF_SOFTC;
+	} else {
+		dev = (struct device *)malloc(softsize, M_DEVBUF,
+					      cold ? M_NOWAIT : M_WAITOK);
+		if (!dev)
+			panic("config_attach: memory allocation for device softc failed");
+		memset(dev, 0, softsize);
+		TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);	/* link up */
+		SLIST_INIT(&dev->dv_props);
+		dev->dv_private = dev;		/* Point private to ourself... */
+	}
 	dev->dv_class = cd->cd_class;
 	dev->dv_cfdata = cf;
 	dev->dv_unit = myunit;
 	memcpy(dev->dv_xname, cd->cd_name, lname);
 	memcpy(dev->dv_xname + lname, xunit, lunit);
 	dev->dv_parent = parent;
-	dev->dv_flags = DVF_ACTIVE;	/* always initially active */
+	dev->dv_flags |= DVF_ACTIVE;	/* always initially active */
+	if (propdev && propdev != dev) {
+		/* Inherit (steal) properties from dummy device */
+		SLIST_FIRST(&dev->dv_props) = 
+			SLIST_FIRST(&propdev->dv_props);
+		SLIST_INIT(&propdev->dv_props);
+		/* destroy propdev */
+		config_detach(propdev, 0);
+	}
 
 	if (parent == ROOT)
 		printf("%s (root)", dev->dv_xname);
@@ -457,6 +575,20 @@
 #endif
 	int rv = 0, i;
 
+	if (dev->dv_class == DV_EMPTY) {
+		/*
+		 * The device is a dummy that never fully attached.
+		 * Simply remove it from the global list and free it.
+		 */
+		TAILQ_REMOVE(&alldevs, dev, dv_list);
+		zap_devprops(dev);
+		if (dev->dv_flags & DVF_SOFTC)
+			/* Separately allocated softc */
+			free(dev->dv_private, M_DEVBUF);
+		free(dev, M_DEVBUF);
+		return (0);
+	}
+	
 	cf = dev->dv_cfdata;
 #ifdef DIAGNOSTIC
 	if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
@@ -464,7 +596,7 @@
 #endif
 	ca = cf->cf_attach;
 	cd = cf->cf_driver;
-
+	
 	/*
 	 * Ensure the device is deactivated.  If the device doesn't
 	 * have an activation entry point, we allow DVF_ACTIVE to
@@ -473,7 +605,7 @@
 	 */
 	if (ca->ca_activate != NULL)
 		rv = config_deactivate(dev);
-
+	
 	/*
 	 * Try to detach the device.  If that's not possible, then
 	 * we either panic() (for the forced but failed case), or
@@ -490,13 +622,13 @@
 			return (rv);
 		else
 			panic("config_detach: forced detach of %s failed (%d)",
-			    dev->dv_xname, rv);
+			      dev->dv_xname, rv);
 	}
-
+	
 	/*
 	 * The device has now been successfully detached.
 	 */
-
+	
 #ifdef DIAGNOSTIC
 	/*
 	 * Sanity: If you're successfully detached, you should have no
@@ -505,15 +637,15 @@
 	 * the list.)
 	 */
 	for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
-	    d = TAILQ_NEXT(d, dv_list)) {
+	     d = TAILQ_NEXT(d, dv_list)) {
 		if (d->dv_parent == dev) {
 			printf("config_detach: detached device %s"
-			    " has children %s\n", dev->dv_xname, d->dv_xname);
+			       " has children %s\n", dev->dv_xname, d->dv_xname);
 			panic("config_detach");
 		}
 	}
 #endif
-
+	
 	/*
 	 * Mark cfdata to show that the unit can be reused, if possible.
 	 */
@@ -544,9 +676,15 @@
 	/*
 	 * Remove from cfdriver's array, tell the world, and free softc.
 	 */
-	cd->cd_devs[dev->dv_unit] = NULL;
-	if ((flags & DETACH_QUIET) == 0)
-		printf("%s detached\n", dev->dv_xname);
+	if (cd) {
+		cd->cd_devs[dev->dv_unit] = NULL;
+		if ((flags & DETACH_QUIET) == 0)
+			printf("%s detached\n", dev->dv_xname);
+	}
+	zap_devprops(dev);
+	if (dev->dv_flags & DVF_SOFTC)
+		/* Separately allocated softc */
+		free(dev->dv_private, M_DEVBUF);
 	free(dev, M_DEVBUF);
 
 	/*
@@ -762,4 +900,186 @@
 {
 
 	TAILQ_REMOVE(&allevents, ev, ev_list);
+}
+
+/*
+ * Device property management routines.
+ */
+#ifdef DEBUG
+int devprop_debug = 1;
+#endif
+
+/*
+ * Create a dummy device you can attach properties to.
+ */
+struct device *
+dev_config_create(struct device *parent, int flags)
+{
+	struct device *dev;
+
+	dev = dev_create(parent, 0, (flags & PROP_WAIT) ? M_WAITOK : M_NOWAIT);
+	return (dev);
+}
+
+int 
+dev_setprop(struct device *dev, const char *name, void *val, 
+			size_t len, int flags)
+{
+	struct devprop *prop;
+	size_t blen;
+	size_t nlen;
+	
+#ifdef DEBUG
+	if (devprop_debug)
+		printf("dev_setprop(%p, %s, %p %s, %ld, %x)\n", 
+		       dev, name, val, (flags & PROP_STRING) ? 
+		       (char *)val : "", len, flags);
+#endif
+	/* First clear out any duplicates.... */
+	dev_delprop(dev, name, 0);
+
+	/* Calculate how large a buffer we need. */
+	nlen = strlen(name);
+	blen = sizeof(struct devprop) + nlen;
+	if ((flags & PROP_CONST) == 0) 
+		/* Allocate storage for prop. */
+		blen += len;
+	
+	prop = (struct devprop *)malloc(blen, M_DEVBUF, 
+		(flags & PROP_WAIT)? M_WAITOK : M_NOWAIT);
+	if (!prop) {
+#ifdef DEBUG
+		if (devprop_debug) {
+			printf("dev_setprop failed: no memory for %ld\n",
+			       blen);
+		}
+#endif
+		if (flags == M_NOWAIT)
+			return EAGAIN;
+		return ENOMEM;
+	}
+
+	/* Set up the devprop structure. */
+	if (flags & PROP_STRING)
+		prop->dp_len = -len;
+	else
+		prop->dp_len = len;
+	strcpy((char *)&prop->dp_name[0], name);
+	if (flags & PROP_CONST) {
+		/* Use existing storage */
+		prop->dp_val = val;
+	} else {
+		/* Copy data */
+		prop->dp_val = (void *)&prop->dp_name[nlen+1];
+		bcopy(val, (caddr_t)prop->dp_val, len);
+	}
+
+	SLIST_INSERT_HEAD(&dev->dv_props, prop, dp_list);
+
+	return 0;
+}
+
+size_t 
+dev_getprop(struct device *dev, const char *name, void *val, 
+			size_t len, int flags)
+{
+	struct devprop *prop;
+	ssize_t plen;
+
+#ifdef DEBUG
+	if (devprop_debug) {
+		printf("dev_getprop(%p, %s, %p, %ld, %x)\n", 
+		       dev, name, val, len, flags);
+		if (SLIST_EMPTY(&dev->dv_props)) 
+			printf("devlist %p empty\n", dev);
+	}
+#endif
+	SLIST_FOREACH(prop, &dev->dv_props, dp_list) {
+#ifdef DEBUG
+		if (devprop_debug)
+			printf("checking %s %s ", prop->dp_name, 
+			       (ssize_t)prop->dp_len < 0 ? (char *)prop->dp_val : "");
+#endif
+		if (strcmp(name, prop->dp_name) == 0) {
+			/* Found */
+#ifdef DEBUG
+			if (devprop_debug)
+				printf("found %s %p %s len %ld at %p\n",
+				       prop->dp_name,
+				       prop->dp_val, 
+				       (ssize_t)prop->dp_len < 0 ? 
+				       (char *)prop->dp_val : "",
+				       prop->dp_len,
+				       prop);
+#endif
+			plen = prop->dp_len;
+
+			/* Fix len of STRING props. */
+			if (plen < 0) plen = -plen;
+
+			/* Copy only as much as we can. */
+			len = min(len, plen);
+			bcopy(prop->dp_val, val, len);
+			return (plen);
+		}
+	}
+	/* Not found -- try md_getprop */
+#if defined (get_mdprop)
+	{
+		ssize_t res = dev_mdgetprop(dev->dv_parent, name, val
+					len, flags);
+		if (res != -1) return (res);
+	}
+#endif
+	/* Not found -- try parents? */
+	if (flags & PROP_SEARCH) {
+		if (dev->dv_parent)
+			return (dev_getprop(dev->dv_parent, name,
+					    val, len, flags));
+	}
+#ifdef DEBUG
+	if (devprop_debug) {
+		printf("%s no found\n", name);
+	}
+#endif
+	return (-1);
+}
+
+int 
+dev_delprop(struct device *dev, const char *name, int flags)
+{
+	struct devprop *prop;
+
+#ifdef DEBUG_N
+	if (devprop_debug)
+		printf("dev_delprop(%p, %s, %x)\n", 
+		       dev, name, flags);
+#endif
+	/* If no name is specified, zap all properties. */
+	if (!name) {
+		zap_devprops(dev);
+		return (0);
+	}
+	SLIST_FOREACH(prop, &dev->dv_props, dp_list) {
+		if (strcmp(name, prop->dp_name) == 0) {
+			/* Found */
+			SLIST_REMOVE(&dev->dv_props, prop, devprop, dp_list);
+			free(prop, M_DEVBUF);
+			return (0);
+		}
+	}
+	return (0);
+}
+
+/* Delete all device properties 'cause we're detaching the device. */
+void
+zap_devprops(struct device *dev)
+{
+	struct devprop *prop;
+
+	while (!SLIST_EMPTY(&dev->dv_props)) {
+		prop = SLIST_FIRST(&dev->dv_props);
+		SLIST_REMOVE_HEAD(&dev->dv_props, dp_list);
+		free(prop, M_DEVBUF);
+	}
 }