-/* $OpenBSD: subr_autoconf.c,v 1.25 1999/08/08 00:37:09 niklas Exp $ */
+/* $OpenBSD: subr_autoconf.c,v 1.26 2000/04/09 19:23:18 csapuntz Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */
/*
cf->cf_fstate = FSTATE_FOUND;
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
+ device_ref(dev);
if (parent == ROOT)
printf("%s (root)", dev->dv_xname);
if (cd->cd_devs[dev->dv_unit])
panic("config_make_softc: duplicate %s", dev->dv_xname);
+ dev->dv_ref = 1;
+
return (dev);
}
* Unlink from device list.
*/
TAILQ_REMOVE(&alldevs, dev, dv_list);
+ device_unref(dev);
/*
* 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);
- free(dev, M_DEVBUF);
+ device_unref(dev);
/*
* If the device now has no units in use, deallocate its softc array.
*/
}
}
+int
+config_detach_children(parent, flags)
+ struct device *parent;
+ int flags;
+{
+ struct device *dev, *next_dev;
+ int rv = 0;
+
+ /* The config_detach routine may sleep, meaning devices
+ may be added to the queue. However, all devices will
+ be added to the tail of the queue, the queue won't
+ be re-organized, and the subtree of parent here should be locked
+ for purposes of adding/removing children.
+ */
+ for (dev = TAILQ_FIRST(&alldevs);
+ dev != NULL; dev = next_dev) {
+ next_dev = TAILQ_NEXT(dev, dv_list);
+ if (dev->dv_parent == parent &&
+ (rv = config_detach(dev, flags)))
+ return (rv);
+ }
+
+ return (rv);
+}
+
+int
+config_activate_children(parent, act)
+ struct device *parent;
+ enum devact act;
+{
+ struct device *dev, *next_dev;
+ int rv = 0;
+
+ /* The config_deactivate routine may sleep, meaning devices
+ may be added to the queue. However, all devices will
+ be added to the tail of the queue, the queue won't
+ be re-organized, and the subtree of parent here should be locked
+ for purposes of adding/removing children.
+ */
+ for (dev = TAILQ_FIRST(&alldevs);
+ dev != NULL; dev = next_dev) {
+ next_dev = TAILQ_NEXT(dev, dv_list);
+ if (dev->dv_parent == parent) {
+ switch (act) {
+ case DVACT_ACTIVATE:
+ rv = config_activate(dev);
+ break;
+ case DVACT_DEACTIVATE:
+ rv = config_deactivate(dev);
+ break;
+ default:
+#ifdef DIAGNOSTIC
+ printf ("config_activate_children: shouldn't get here");
+#endif
+ rv = EOPNOTSUPP;
+ break;
+
+ }
+
+ if (rv)
+ break;
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Lookup a device in the cfdriver device array. Does not return a
+ * device if it is not active.
+ *
+ * Increments ref count on the device by one, reflecting the
+ * new reference created on the stack.
+ *
+ * Context: process only
+ */
+struct device *
+device_lookup(cd, unit)
+ struct cfdriver *cd;
+ int unit;
+{
+ struct device *dv = NULL;
+
+ if (unit >= 0 && unit <= cd->cd_ndevs)
+ dv = (struct device *)(cd->cd_devs[unit]);
+
+ if (!dv)
+ return (NULL);
+
+ if (!(dv->dv_flags & DVF_ACTIVE))
+ dv = NULL;
+
+ if (dv != NULL)
+ device_ref(dv);
+
+ return (dv);
+}
+
+
+/*
+ * Increments the ref count on the device structure. The device
+ * structure is freed when the ref count hits 0.
+ *
+ * Context: process or interrupt
+ */
+void
+device_ref(dv)
+ struct device *dv;
+{
+ dv->dv_ref++;
+}
+
+/*
+ * Decrement the ref count on the device structure.
+ *
+ * free's the structure when the ref count hits zero and calls the zeroref
+ * function.
+ *
+ * Context: process or interrupt
+ */
+void
+device_unref(dv)
+ struct device *dv;
+{
+ dv->dv_ref--;
+ if (dv->dv_ref == 0) {
+ if (dv->dv_cfdata->cf_attach->ca_zeroref)
+ (*dv->dv_cfdata->cf_attach->ca_zeroref)(dv);
+
+ free(dv, M_DEVBUF);
+ }
+}
+
/*
* Attach an event. These must come from initially-zero space (see
* commented-out assignments below), but that occurs naturally for
-/* $OpenBSD: device.h,v 1.17 1999/08/08 00:37:09 niklas Exp $ */
+/* $OpenBSD: device.h,v 1.18 2000/04/09 19:23:18 csapuntz Exp $ */
/* $NetBSD: device.h,v 1.15 1996/04/09 20:55:24 cgd Exp $ */
/*
DVACT_DEACTIVATE, /* deactivate the device */
};
+#include <sys/lock.h>
+
struct device {
enum devclass dv_class; /* this device's classification */
TAILQ_ENTRY(device) dv_list; /* entry on list of all devices */
char dv_xname[16]; /* external name (name + unit) */
struct device *dv_parent; /* pointer to parent device */
int dv_flags; /* misc. flags; see below */
+ int dv_ref; /* ref count */
};
/* dv_flags */
void (*ca_attach) __P((struct device *, struct device *, void *));
int (*ca_detach) __P((struct device *, int));
int (*ca_activate) __P((struct device *, enum devact));
+ void (*ca_zeroref) __P((struct device *));
};
/* Flags given to config_detach(), and the ca_detach function. */
void config_scan __P((cfscan_t, struct device *));
struct device *config_attach __P((struct device *, void *, void *, cfprint_t));
int config_detach __P((struct device *, int));
+int config_detach_children __P((struct device *, int));
int config_activate __P((struct device *));
int config_deactivate __P((struct device *));
+int config_activate_children __P((struct device *, enum devact));
struct device *config_make_softc __P((struct device *parent,
struct cfdata *cf));
void config_defer __P((struct device *, void (*)(struct device *)));
void evcnt_attach __P((struct device *, const char *, struct evcnt *));
+struct device *device_lookup __P((struct cfdriver *, int unit));
+void device_ref __P((struct device *));
+void device_unref __P((struct device *));
+
/* compatibility definitions */
#define config_found(d, a, p) config_found_sm((d), (a), (p), NULL)
#if 0