summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r--sys/kern/kern_conf.c137
1 files changed, 129 insertions, 8 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index c99173d..62033c7 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -324,10 +324,22 @@ makeudev(int x, int y)
}
static void
-prep_cdevsw(struct cdevsw *devsw)
+find_major(struct cdevsw *devsw)
{
int i;
+ for (i = NUMCDEVSW - 1; i > 0; i--)
+ if (reserved_majors[i] != i)
+ break;
+ KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
+ devsw->d_maj = i;
+ reserved_majors[i] = i;
+}
+
+static void
+prep_cdevsw(struct cdevsw *devsw)
+{
+
if (devsw->d_open == NULL) devsw->d_open = null_open;
if (devsw->d_close == NULL) devsw->d_close = null_close;
if (devsw->d_read == NULL) devsw->d_read = no_read;
@@ -339,12 +351,7 @@ prep_cdevsw(struct cdevsw *devsw)
if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter;
if (devsw->d_maj == MAJOR_AUTO) {
- for (i = NUMCDEVSW - 1; i > 0; i--)
- if (reserved_majors[i] != i)
- break;
- KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
- devsw->d_maj = i;
- reserved_majors[i] = i;
+ find_major(devsw);
} else {
if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */
devsw->d_maj = 0;
@@ -458,17 +465,22 @@ destroy_dev(dev_t dev)
}
devfs_destroy(dev);
+ dev->si_flags &= ~SI_NAMED;
+
if (dev->si_flags & SI_CHILD) {
LIST_REMOVE(dev, si_siblings);
dev->si_flags &= ~SI_CHILD;
}
while (!LIST_EMPTY(&dev->si_children))
destroy_dev(LIST_FIRST(&dev->si_children));
+ if (dev->si_flags & SI_CLONELIST) {
+ LIST_REMOVE(dev, si_clone);
+ dev->si_flags &= ~SI_CLONELIST;
+ }
dev->si_drv1 = 0;
dev->si_drv2 = 0;
dev->si_devsw = 0;
bzero(&dev->__si_u, sizeof(dev->__si_u));
- dev->si_flags &= ~SI_NAMED;
dev->si_flags &= ~SI_ALIAS;
freedev(dev);
}
@@ -523,6 +535,115 @@ dev_stdclone(char *name, char **namep, const char *stem, int *unit)
}
/*
+ * Helper functions for cloning device drivers.
+ *
+ * The objective here is to make it unnecessary for the device drivers to
+ * use rman or similar to manage their unit number space. Due to the way
+ * we do "on-demand" devices, using rman or other "private" methods
+ * will be very tricky to lock down properly once we lock down this file.
+ *
+ * Instead we give the drivers these routines which puts the dev_t's that
+ * are to be managed on their own list, and gives the driver the ability
+ * to ask for the first free unit number or a given specified unit number.
+ *
+ * In addition these routines support paired devices (pty, nmdm and similar)
+ * by respecting a number of "flag" bits in the minor number.
+ *
+ */
+
+struct clonedevs {
+ LIST_HEAD(,cdev) head;
+};
+
+int
+clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, dev_t *dp, u_int extra)
+{
+ struct clonedevs *cd;
+ dev_t dev, dl, de;
+ int unit, low, u;
+
+ KASSERT(!(extra & CLONE_UNITMASK),
+ ("Illegal extra bits (0x%x) in clone_create", extra));
+ KASSERT(*up <= CLONE_UNITMASK,
+ ("Too high unit (0x%x) in clone_create", *up));
+
+ if (csw->d_maj == MAJOR_AUTO)
+ find_major(csw);
+ /* if clonedevs have not been initialized, we do it here */
+ cd = *cdp;
+ if (cd == NULL) {
+ cd = malloc(sizeof *cd, M_DEVBUF, M_WAITOK | M_ZERO);
+ LIST_INIT(&cd->head);
+ *cdp = cd;
+ }
+
+ /*
+ * Search the list for a lot of things in one go:
+ * A preexisting match is returned immediately.
+ * The lowest free unit number if we are passed -1, and the place
+ * in the list where we should insert that new element.
+ * The place to insert a specified unit number, if applicable
+ * the end of the list.
+ */
+ unit = *up;
+ low = 0;
+ de = dl = NULL;
+ LIST_FOREACH(dev, &cd->head, si_clone) {
+ u = dev2unit(dev);
+ if (u == (unit | extra)) {
+ *dp = dev;
+ return (0);
+ }
+ if (unit == -1 && u == low) {
+ low++;
+ de = dev;
+ continue;
+ }
+ if (u > unit) {
+ dl = dev;
+ break;
+ }
+ de = dev;
+ }
+ if (unit == -1)
+ unit = low;
+ dev = makedev(csw->d_maj, unit2minor(unit | extra));
+ KASSERT(!(dev->si_flags & SI_CLONELIST),
+ ("Dev %p should not be on clonelist", dev));
+ if (dl != NULL)
+ LIST_INSERT_BEFORE(dl, dev, si_clone);
+ else if (de != NULL)
+ LIST_INSERT_AFTER(de, dev, si_clone);
+ else
+ LIST_INSERT_HEAD(&cd->head, dev, si_clone);
+ dev->si_flags |= SI_CLONELIST;
+ *up = unit;
+ return (1);
+}
+
+/*
+ * Kill everything still on the list. The driver should already have
+ * disposed of any softc hung of the dev_t's at this time.
+ */
+void
+clone_cleanup(struct clonedevs **cdp)
+{
+ dev_t dev, tdev;
+ struct clonedevs *cd;
+
+ cd = *cdp;
+ if (cd == NULL)
+ return;
+ LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
+ KASSERT(dev->si_flags & SI_NAMED,
+ ("Driver has goofed in cloning underways udev %x", dev->si_udev));
+ destroy_dev(dev);
+ }
+ free(cd, M_DEVBUF);
+ *cdp = NULL;
+}
+
+/*
* Helper sysctl for devname(3). We're given a {u}dev_t and return
* the name, if any, registered by the device driver.
*/
OpenPOWER on IntegriCloud