kern_conf.c revision 135843
155682Smarkm/*-
255682Smarkm * Copyright (c) 1999-2002 Poul-Henning Kamp
355682Smarkm * All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm *
1455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455682Smarkm * SUCH DAMAGE.
2555682Smarkm */
2655682Smarkm
2755682Smarkm#include <sys/cdefs.h>
2855682Smarkm__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 135843 2004-09-27 06:18:25Z phk $");
2955682Smarkm
3055682Smarkm#include <sys/param.h>
3155682Smarkm#include <sys/kernel.h>
3255682Smarkm#include <sys/systm.h>
3355682Smarkm#include <sys/bio.h>
3455682Smarkm#include <sys/lock.h>
3555682Smarkm#include <sys/mutex.h>
3655682Smarkm#include <sys/sysctl.h>
3755682Smarkm#include <sys/module.h>
3855682Smarkm#include <sys/malloc.h>
3955682Smarkm#include <sys/conf.h>
4055682Smarkm#include <sys/vnode.h>
4155682Smarkm#include <sys/queue.h>
4255682Smarkm#include <sys/poll.h>
4355682Smarkm#include <sys/ctype.h>
4455682Smarkm#include <sys/tty.h>
4555682Smarkm#include <machine/stdarg.h>
4655682Smarkm
4755682Smarkmstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
4855682Smarkm
4955682Smarkm/* Built at compile time from sys/conf/majors */
5055682Smarkmextern unsigned char reserved_majors[256];
5155682Smarkm
5255682Smarkm/*
5355682Smarkm * This is the number of hash-buckets.  Experiments with 'real-life'
5455682Smarkm * dev_t's show that a prime halfway between two powers of two works
5555682Smarkm * best.
5655682Smarkm */
5755682Smarkm#define DEVT_HASH 83
5855682Smarkm
5955682Smarkm/* The number of struct cdev *'s we can create before malloc(9) kick in.  */
6055682Smarkm#define DEVT_STASH 50
6155682Smarkm
6255682Smarkmstatic struct cdev devt_stash[DEVT_STASH];
6355682Smarkm
6455682Smarkmstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
6555682Smarkm
6655682Smarkmstatic LIST_HEAD(, cdev) dev_free;
6755682Smarkm
6855682Smarkmstatic int free_devt;
6955682SmarkmSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
7055682Smarkm
7155682Smarkmstatic struct mtx devmtx;
7255682Smarkmstatic void freedev(struct cdev *dev);
7355682Smarkmstatic struct cdev *newdev(int x, int y);
7455682Smarkm
7555682Smarkm
7655682Smarkmvoid
7755682Smarkmdev_lock(void)
7855682Smarkm{
7955682Smarkm	if (!mtx_initialized(&devmtx))
8055682Smarkm		mtx_init(&devmtx, "cdev", NULL, MTX_DEF);
8155682Smarkm	mtx_lock(&devmtx);
8255682Smarkm}
8355682Smarkm
8455682Smarkmvoid
8555682Smarkmdev_unlock(void)
8655682Smarkm{
8755682Smarkm
8855682Smarkm	mtx_unlock(&devmtx);
8955682Smarkm}
9055682Smarkm
9155682Smarkmvoid
9255682Smarkmdev_ref(struct cdev *dev)
9355682Smarkm{
9455682Smarkm
9555682Smarkm	dev_lock();
9655682Smarkm	dev->si_refcount++;
9755682Smarkm	dev_unlock();
9855682Smarkm}
9955682Smarkm
10055682Smarkmvoid
10155682Smarkmdev_rel(struct cdev *dev)
10255682Smarkm{
10355682Smarkm
10455682Smarkm	dev->si_refcount--;
10555682Smarkm	KASSERT(dev->si_refcount >= 0,
10655682Smarkm	    ("dev_rel(%s) gave negative count", devtoname(dev)));
10755682Smarkm	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
10855682Smarkm		LIST_REMOVE(dev, si_list);
10955682Smarkm		freedev(dev);
11055682Smarkm	}
11155682Smarkm}
11255682Smarkmstruct cdevsw *
11355682Smarkmdev_refthread(struct cdev *dev)
11455682Smarkm{
11555682Smarkm	struct cdevsw *csw;
11655682Smarkm
11755682Smarkm	mtx_assert(&devmtx, MA_NOTOWNED);
11855682Smarkm	dev_lock();
11955682Smarkm	csw = dev->si_devsw;
12055682Smarkm	if (csw != NULL)
12155682Smarkm		dev->si_threadcount++;
12255682Smarkm	dev_unlock();
12355682Smarkm	return (csw);
12455682Smarkm}
12555682Smarkm
12655682Smarkmvoid
12755682Smarkmdev_relthread(struct cdev *dev)
12855682Smarkm{
12955682Smarkm
13055682Smarkm	mtx_assert(&devmtx, MA_NOTOWNED);
13155682Smarkm	dev_lock();
13255682Smarkm	dev->si_threadcount--;
13355682Smarkm	dev_unlock();
13455682Smarkm}
13555682Smarkm
13655682Smarkmint
13755682Smarkmnullop(void)
13855682Smarkm{
13955682Smarkm
14055682Smarkm	return (0);
14155682Smarkm}
14255682Smarkm
14355682Smarkmint
14455682Smarkmeopnotsupp(void)
14555682Smarkm{
14655682Smarkm
14755682Smarkm	return (EOPNOTSUPP);
14855682Smarkm}
14955682Smarkm
15055682Smarkmstatic int
15155682Smarkmenxio(void)
15255682Smarkm{
15355682Smarkm	return (ENXIO);
15455682Smarkm}
15555682Smarkm
15655682Smarkmstatic int
15755682Smarkmenodev(void)
15855682Smarkm{
15955682Smarkm	return (ENODEV);
16055682Smarkm}
16155682Smarkm
16255682Smarkm/* Define a dead_cdevsw for use when devices leave unexpectedly. */
16355682Smarkm
16455682Smarkm#define dead_open	(d_open_t *)enxio
16555682Smarkm#define dead_close	(d_close_t *)enxio
16655682Smarkm#define dead_read	(d_read_t *)enxio
16755682Smarkm#define dead_write	(d_write_t *)enxio
16855682Smarkm#define dead_ioctl	(d_ioctl_t *)enxio
16955682Smarkm#define dead_poll	(d_poll_t *)enodev
17055682Smarkm#define dead_mmap	(d_mmap_t *)enodev
17155682Smarkm
17255682Smarkmstatic void
17355682Smarkmdead_strategy(struct bio *bp)
17455682Smarkm{
17555682Smarkm
17655682Smarkm	biofinish(bp, NULL, ENXIO);
17755682Smarkm}
17855682Smarkm
17955682Smarkm#define dead_dump	(dumper_t *)enxio
18055682Smarkm#define dead_kqfilter	(d_kqfilter_t *)enxio
18155682Smarkm
18255682Smarkmstatic struct cdevsw dead_cdevsw = {
18355682Smarkm	.d_version =	D_VERSION,
18455682Smarkm	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
18555682Smarkm	.d_open =	dead_open,
18655682Smarkm	.d_close =	dead_close,
18755682Smarkm	.d_read =	dead_read,
18855682Smarkm	.d_write =	dead_write,
18955682Smarkm	.d_ioctl =	dead_ioctl,
19055682Smarkm	.d_poll =	dead_poll,
19155682Smarkm	.d_mmap =	dead_mmap,
19255682Smarkm	.d_strategy =	dead_strategy,
19355682Smarkm	.d_name =	"dead",
19455682Smarkm	.d_maj =	255,
19555682Smarkm	.d_dump =	dead_dump,
19655682Smarkm	.d_kqfilter =	dead_kqfilter
19755682Smarkm};
19855682Smarkm
19955682Smarkm/* Default methods if driver does not specify method */
20055682Smarkm
20155682Smarkm#define null_open	(d_open_t *)nullop
20255682Smarkm#define null_close	(d_close_t *)nullop
20355682Smarkm#define no_read		(d_read_t *)enodev
20455682Smarkm#define no_write	(d_write_t *)enodev
20555682Smarkm#define no_ioctl	(d_ioctl_t *)enodev
20655682Smarkm#define no_mmap		(d_mmap_t *)enodev
20755682Smarkm#define no_kqfilter	(d_kqfilter_t *)enodev
20855682Smarkm
20955682Smarkmstatic void
21055682Smarkmno_strategy(struct bio *bp)
21155682Smarkm{
21255682Smarkm
21355682Smarkm	biofinish(bp, NULL, ENODEV);
21455682Smarkm}
21555682Smarkm
21655682Smarkmstatic int
21755682Smarkmno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
21855682Smarkm{
21955682Smarkm	/*
22055682Smarkm	 * Return true for read/write.  If the user asked for something
22155682Smarkm	 * special, return POLLNVAL, so that clients have a way of
22255682Smarkm	 * determining reliably whether or not the extended
22355682Smarkm	 * functionality is present without hard-coding knowledge
22455682Smarkm	 * of specific filesystem implementations.
22555682Smarkm	 * Stay in sync with vop_nopoll().
22655682Smarkm	 */
22755682Smarkm	if (events & ~POLLSTANDARD)
22855682Smarkm		return (POLLNVAL);
22955682Smarkm
23055682Smarkm	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
23155682Smarkm}
23255682Smarkm
23355682Smarkm#define no_dump		(dumper_t *)enodev
23455682Smarkm
23555682Smarkm/*
23655682Smarkm * struct cdev * and u_dev_t primitives
23755682Smarkm */
23855682Smarkm
23955682Smarkmint
24055682Smarkmmajor(struct cdev *x)
24155682Smarkm{
24255682Smarkm	if (x == NULL)
24355682Smarkm		return NODEV;
24455682Smarkm	return((x->si_udev >> 8) & 0xff);
24555682Smarkm}
24655682Smarkm
24755682Smarkmint
24855682Smarkmminor(struct cdev *x)
24955682Smarkm{
25055682Smarkm	if (x == NULL)
25155682Smarkm		return NODEV;
25255682Smarkm	return(x->si_udev & 0xffff00ff);
25355682Smarkm}
25455682Smarkm
25555682Smarkmint
25655682Smarkmdev2unit(struct cdev *x)
25755682Smarkm{
25855682Smarkm	int i;
25955682Smarkm
26055682Smarkm	if (x == NULL)
26155682Smarkm		return NODEV;
26255682Smarkm	i = minor(x);
26355682Smarkm	return ((i & 0xff) | (i >> 8));
26455682Smarkm}
26555682Smarkm
26655682Smarkmint
26755682Smarkmunit2minor(int unit)
26855682Smarkm{
26955682Smarkm
27055682Smarkm	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
27155682Smarkm	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
27255682Smarkm}
27355682Smarkm
27455682Smarkmstatic struct cdev *
27555682Smarkmallocdev(void)
27655682Smarkm{
27755682Smarkm	static int stashed;
27855682Smarkm	struct cdev *si;
27955682Smarkm
28055682Smarkm	if (LIST_FIRST(&dev_free)) {
28155682Smarkm		si = LIST_FIRST(&dev_free);
28255682Smarkm		LIST_REMOVE(si, si_hash);
28355682Smarkm	} else if (stashed >= DEVT_STASH) {
28455682Smarkm		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
28555682Smarkm		    M_USE_RESERVE | M_ZERO | M_WAITOK);
28655682Smarkm	} else {
28755682Smarkm		si = devt_stash + stashed++;
28855682Smarkm		bzero(si, sizeof *si);
28955682Smarkm		si->si_flags |= SI_STASHED;
29055682Smarkm	}
29155682Smarkm	si->__si_namebuf[0] = '\0';
29255682Smarkm	si->si_name = si->__si_namebuf;
29355682Smarkm	LIST_INIT(&si->si_children);
29455682Smarkm	return (si);
29555682Smarkm}
29655682Smarkm
29755682Smarkmstatic struct cdev *
29855682Smarkmnewdev(int x, int y)
29955682Smarkm{
30055682Smarkm	struct cdev *si;
30155682Smarkm	dev_t	udev;
30255682Smarkm	int hash;
30355682Smarkm
30455682Smarkm	if (x == umajor(NODEV) && y == uminor(NODEV))
30555682Smarkm		panic("newdev of NODEV");
30655682Smarkm	udev = (x << 8) | y;
30755682Smarkm	hash = udev % DEVT_HASH;
30855682Smarkm	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
30955682Smarkm		if (si->si_udev == udev)
31055682Smarkm			return (si);
31155682Smarkm	}
31255682Smarkm	si = allocdev();
31355682Smarkm	si->si_udev = udev;
31455682Smarkm	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
31555682Smarkm	return (si);
31655682Smarkm}
31755682Smarkm
31855682Smarkmstatic void
31955682Smarkmfreedev(struct cdev *dev)
32055682Smarkm{
32155682Smarkm
32255682Smarkm	if (dev->si_flags & SI_STASHED) {
32355682Smarkm		bzero(dev, sizeof(*dev));
32455682Smarkm		dev->si_flags |= SI_STASHED;
32555682Smarkm		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
32655682Smarkm	} else {
32755682Smarkm		FREE(dev, M_DEVT);
32855682Smarkm	}
32955682Smarkm}
33055682Smarkm
33155682Smarkmdev_t
33255682Smarkmdev2udev(struct cdev *x)
33355682Smarkm{
33455682Smarkm	if (x == NULL)
33555682Smarkm		return (NODEV);
33655682Smarkm	return (x->si_udev);
33755682Smarkm}
33855682Smarkm
33955682Smarkmstruct cdev *
34055682Smarkmfindcdev(dev_t udev)
34155682Smarkm{
34255682Smarkm	struct cdev *si;
34355682Smarkm	int hash;
34455682Smarkm
34555682Smarkm	if (udev == NODEV)
34655682Smarkm		return (NULL);
34755682Smarkm	hash = udev % DEVT_HASH;
34855682Smarkm	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
34955682Smarkm		if (si->si_udev == udev)
35055682Smarkm			return (si);
35155682Smarkm	}
35255682Smarkm	return (NULL);
35355682Smarkm}
35455682Smarkm
35555682Smarkmint
35655682Smarkmuminor(dev_t dev)
35755682Smarkm{
35855682Smarkm	return (dev & 0xffff00ff);
35955682Smarkm}
36055682Smarkm
36155682Smarkmint
36255682Smarkmumajor(dev_t dev)
36355682Smarkm{
36455682Smarkm	return ((dev & 0xff00) >> 8);
36555682Smarkm}
36655682Smarkm
36755682Smarkmstatic void
36855682Smarkmfind_major(struct cdevsw *devsw)
36955682Smarkm{
37055682Smarkm	int i;
37155682Smarkm
37255682Smarkm	for (i = NUMCDEVSW - 1; i > 0; i--)
37355682Smarkm		if (reserved_majors[i] != i)
37455682Smarkm			break;
37555682Smarkm	KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
37655682Smarkm	devsw->d_maj = i;
37755682Smarkm	reserved_majors[i] = i;
37855682Smarkm	devsw->d_flags |= D_ALLOCMAJ;
37955682Smarkm}
38055682Smarkm
38155682Smarkmstatic void
38255682Smarkmfini_cdevsw(struct cdevsw *devsw)
38355682Smarkm{
38455682Smarkm	if (devsw->d_flags & D_ALLOCMAJ) {
38555682Smarkm		reserved_majors[devsw->d_maj] = 0;
38655682Smarkm		devsw->d_maj = MAJOR_AUTO;
38755682Smarkm		devsw->d_flags &= ~D_ALLOCMAJ;
38855682Smarkm	} else if (devsw->d_maj == 0)
38955682Smarkm		devsw->d_maj = 256;
39055682Smarkm	devsw->d_flags &= ~D_INIT;
39155682Smarkm}
39255682Smarkm
39355682Smarkmstatic void
39455682Smarkmprep_cdevsw(struct cdevsw *devsw)
39555682Smarkm{
39655682Smarkm
39755682Smarkm	dev_lock();
39855682Smarkm
39955682Smarkm	if (devsw->d_version != D_VERSION_00) {
40055682Smarkm		printf(
40155682Smarkm		    "WARNING: Device driver \"%s\" has wrong version %s\n",
40255682Smarkm		    devsw->d_name, "and is disabled.  Recompile KLD module.");
40355682Smarkm		devsw->d_open = dead_open;
40455682Smarkm		devsw->d_close = dead_close;
40555682Smarkm		devsw->d_read = dead_read;
40655682Smarkm		devsw->d_write = dead_write;
40755682Smarkm		devsw->d_ioctl = dead_ioctl;
40855682Smarkm		devsw->d_poll = dead_poll;
40955682Smarkm		devsw->d_mmap = dead_mmap;
41055682Smarkm		devsw->d_strategy = dead_strategy;
41155682Smarkm		devsw->d_dump = dead_dump;
41255682Smarkm		devsw->d_kqfilter = dead_kqfilter;
41355682Smarkm	}
41455682Smarkm
41555682Smarkm	if (devsw->d_flags & D_TTY) {
41655682Smarkm		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
41755682Smarkm		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
41855682Smarkm		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
41955682Smarkm		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
42055682Smarkm		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
42155682Smarkm	}
42255682Smarkm
42355682Smarkm	if (devsw->d_open == NULL)	devsw->d_open = null_open;
42455682Smarkm	if (devsw->d_close == NULL)	devsw->d_close = null_close;
42555682Smarkm	if (devsw->d_read == NULL)	devsw->d_read = no_read;
42655682Smarkm	if (devsw->d_write == NULL)	devsw->d_write = no_write;
42755682Smarkm	if (devsw->d_ioctl == NULL)	devsw->d_ioctl = no_ioctl;
42855682Smarkm	if (devsw->d_poll == NULL)	devsw->d_poll = no_poll;
42955682Smarkm	if (devsw->d_mmap == NULL)	devsw->d_mmap = no_mmap;
43055682Smarkm	if (devsw->d_strategy == NULL)	devsw->d_strategy = no_strategy;
43155682Smarkm	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
43255682Smarkm	if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = no_kqfilter;
43355682Smarkm
43455682Smarkm	LIST_INIT(&devsw->d_devs);
43555682Smarkm
43655682Smarkm	devsw->d_flags |= D_INIT;
43755682Smarkm
43855682Smarkm	if (devsw->d_maj == MAJOR_AUTO) {
43955682Smarkm		find_major(devsw);
44055682Smarkm	} else {
44155682Smarkm		if (devsw->d_maj == 256)	/* XXX: tty_cons.c is magic */
44255682Smarkm			devsw->d_maj = 0;
44355682Smarkm		KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256,
44455682Smarkm		    ("Invalid major (%d) in make_dev", devsw->d_maj));
44555682Smarkm		if (reserved_majors[devsw->d_maj] != devsw->d_maj) {
44655682Smarkm			printf("WARNING: driver \"%s\" used %s %d\n",
44755682Smarkm			    devsw->d_name, "unreserved major device number",
44855682Smarkm			    devsw->d_maj);
44955682Smarkm			reserved_majors[devsw->d_maj] = devsw->d_maj;
45055682Smarkm		}
45155682Smarkm	}
45255682Smarkm	dev_unlock();
45355682Smarkm}
45455682Smarkm
45555682Smarkmstruct cdev *
45655682Smarkmmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
45755682Smarkm{
45855682Smarkm	struct cdev *dev;
45955682Smarkm	va_list ap;
46055682Smarkm	int i;
46155682Smarkm
46255682Smarkm	KASSERT((minornr & ~0xffff00ff) == 0,
46355682Smarkm	    ("Invalid minor (0x%x) in make_dev", minornr));
46455682Smarkm
46555682Smarkm	if (!(devsw->d_flags & D_INIT))
46655682Smarkm		prep_cdevsw(devsw);
46755682Smarkm	dev = newdev(devsw->d_maj, minornr);
46855682Smarkm	if (dev->si_flags & SI_CHEAPCLONE &&
46955682Smarkm	    dev->si_flags & SI_NAMED &&
47055682Smarkm	    dev->si_devsw == devsw) {
47155682Smarkm		/*
47255682Smarkm		 * This is allowed as it removes races and generally
47355682Smarkm		 * simplifies cloning devices.
47455682Smarkm		 * XXX: still ??
47555682Smarkm		 */
47655682Smarkm		return (dev);
47755682Smarkm	}
47855682Smarkm	dev_lock();
47955682Smarkm	KASSERT(!(dev->si_flags & SI_NAMED),
48055682Smarkm	    ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)",
48155682Smarkm	    devsw->d_name, major(dev), minor(dev), devtoname(dev)));
48255682Smarkm
48355682Smarkm	va_start(ap, fmt);
48455682Smarkm	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
48555682Smarkm	if (i > (sizeof dev->__si_namebuf - 1)) {
48655682Smarkm		printf("WARNING: Device name truncated! (%s)\n",
48755682Smarkm		    dev->__si_namebuf);
48855682Smarkm	}
48955682Smarkm	va_end(ap);
49055682Smarkm	dev->si_devsw = devsw;
49155682Smarkm	dev->si_uid = uid;
49255682Smarkm	dev->si_gid = gid;
49355682Smarkm	dev->si_mode = perms;
49455682Smarkm	dev->si_flags |= SI_NAMED;
49555682Smarkm
49655682Smarkm	LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list);
49755682Smarkm	devfs_create(dev);
49855682Smarkm	dev_unlock();
49955682Smarkm	return (dev);
50055682Smarkm}
50155682Smarkm
50255682Smarkmint
50355682Smarkmdev_named(struct cdev *pdev, const char *name)
50455682Smarkm{
50555682Smarkm	struct cdev *cdev;
50655682Smarkm
50755682Smarkm	if (strcmp(devtoname(pdev), name) == 0)
50855682Smarkm		return (1);
50955682Smarkm	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
51055682Smarkm		if (strcmp(devtoname(cdev), name) == 0)
51155682Smarkm			return (1);
51255682Smarkm	return (0);
51355682Smarkm}
51455682Smarkm
51555682Smarkmvoid
51655682Smarkmdev_depends(struct cdev *pdev, struct cdev *cdev)
51755682Smarkm{
51855682Smarkm
51955682Smarkm	dev_lock();
52055682Smarkm	cdev->si_parent = pdev;
52155682Smarkm	cdev->si_flags |= SI_CHILD;
52255682Smarkm	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
52355682Smarkm	dev_unlock();
52455682Smarkm}
52555682Smarkm
52655682Smarkmstruct cdev *
52755682Smarkmmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
52855682Smarkm{
52955682Smarkm	struct cdev *dev;
53055682Smarkm	va_list ap;
53155682Smarkm	int i;
53255682Smarkm
53355682Smarkm	dev = allocdev();
53455682Smarkm	dev_lock();
53555682Smarkm	dev->si_flags |= SI_ALIAS;
53655682Smarkm	dev->si_flags |= SI_NAMED;
53755682Smarkm	va_start(ap, fmt);
53855682Smarkm	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
53955682Smarkm	if (i > (sizeof dev->__si_namebuf - 1)) {
54055682Smarkm		printf("WARNING: Device name truncated! (%s)\n",
54155682Smarkm		    dev->__si_namebuf);
54255682Smarkm	}
54355682Smarkm	va_end(ap);
54455682Smarkm
54555682Smarkm	devfs_create(dev);
54655682Smarkm	dev_unlock();
54755682Smarkm	dev_depends(pdev, dev);
54855682Smarkm	return (dev);
54955682Smarkm}
55055682Smarkm
55155682Smarkmstatic void
55255682Smarkmidestroy_dev(struct cdev *dev)
55355682Smarkm{
55455682Smarkm	struct cdevsw *csw;
55555682Smarkm
55655682Smarkm	KASSERT(dev->si_flags & SI_NAMED,
55755682Smarkm	    ("WARNING: Driver mistake: destroy_dev on %d/%d\n",
55855682Smarkm	    major(dev), minor(dev)));
55955682Smarkm
56055682Smarkm	devfs_destroy(dev);
56155682Smarkm
56255682Smarkm	/* Remove name marking */
56355682Smarkm	dev->si_flags &= ~SI_NAMED;
56455682Smarkm
56555682Smarkm	/* If we are a child, remove us from the parents list */
56655682Smarkm	if (dev->si_flags & SI_CHILD) {
56755682Smarkm		LIST_REMOVE(dev, si_siblings);
56855682Smarkm		dev->si_flags &= ~SI_CHILD;
56955682Smarkm	}
57055682Smarkm
57155682Smarkm	/* Kill our children */
57255682Smarkm	while (!LIST_EMPTY(&dev->si_children))
57355682Smarkm		idestroy_dev(LIST_FIRST(&dev->si_children));
57455682Smarkm
57555682Smarkm	/* Remove from clone list */
57655682Smarkm	if (dev->si_flags & SI_CLONELIST) {
57755682Smarkm		LIST_REMOVE(dev, si_clone);
57855682Smarkm		dev->si_flags &= ~SI_CLONELIST;
57955682Smarkm	}
58055682Smarkm
58155682Smarkm	csw = dev->si_devsw;
58255682Smarkm	dev->si_devsw = NULL;
58355682Smarkm	while (csw->d_purge != NULL && dev->si_threadcount) {
58455682Smarkm		printf("Purging %lu threads from %s\n",
58555682Smarkm		    dev->si_threadcount, devtoname(dev));
58655682Smarkm		csw->d_purge(dev);
58755682Smarkm		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
58855682Smarkm	}
58955682Smarkm
59055682Smarkm	dev->si_drv1 = 0;
59155682Smarkm	dev->si_drv2 = 0;
59255682Smarkm	bzero(&dev->__si_u, sizeof(dev->__si_u));
59355682Smarkm
59455682Smarkm	if (!(dev->si_flags & SI_ALIAS)) {
59555682Smarkm		/* Remove from cdevsw list */
59655682Smarkm		LIST_REMOVE(dev, si_list);
59755682Smarkm
59855682Smarkm		/* If cdevsw has no struct cdev *'s, clean it */
59955682Smarkm		if (LIST_EMPTY(&dev->si_devsw->d_devs))
60055682Smarkm			fini_cdevsw(dev->si_devsw);
60155682Smarkm
60255682Smarkm		LIST_REMOVE(dev, si_hash);
60355682Smarkm	}
60455682Smarkm	dev->si_flags &= ~SI_ALIAS;
60555682Smarkm
60655682Smarkm	if (dev->si_refcount > 0) {
60755682Smarkm		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
60855682Smarkm	} else {
60955682Smarkm		freedev(dev);
61055682Smarkm	}
61155682Smarkm}
61255682Smarkm
61355682Smarkmvoid
61455682Smarkmdestroy_dev(struct cdev *dev)
61555682Smarkm{
61655682Smarkm
61755682Smarkm	dev_lock();
61855682Smarkm	idestroy_dev(dev);
61955682Smarkm	dev_unlock();
62055682Smarkm}
62155682Smarkm
62255682Smarkmconst char *
62355682Smarkmdevtoname(struct cdev *dev)
62455682Smarkm{
62555682Smarkm	char *p;
62655682Smarkm	struct cdevsw *csw;
62755682Smarkm	int mynor;
62855682Smarkm
62955682Smarkm	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
63055682Smarkm		p = dev->si_name;
63155682Smarkm		sprintf(p, "#%d", major(dev));
63255682Smarkm		p += strlen(p);
63355682Smarkm		csw = dev_refthread(dev);
63455682Smarkm		if (csw != NULL) {
63555682Smarkm			sprintf(p, "(%s)", csw->d_name);
63655682Smarkm			dev_relthread(dev);
63755682Smarkm		}
63855682Smarkm		p += strlen(p);
63955682Smarkm		mynor = minor(dev);
64055682Smarkm		if (mynor < 0 || mynor > 255)
64155682Smarkm			sprintf(p, "/%#x", (u_int)mynor);
64255682Smarkm		else
64355682Smarkm			sprintf(p, "/%d", mynor);
64455682Smarkm	}
64555682Smarkm	return (dev->si_name);
64655682Smarkm}
64755682Smarkm
64855682Smarkmint
64955682Smarkmdev_stdclone(char *name, char **namep, const char *stem, int *unit)
65055682Smarkm{
65155682Smarkm	int u, i;
65255682Smarkm
65355682Smarkm	i = strlen(stem);
65455682Smarkm	if (bcmp(stem, name, i) != 0)
65555682Smarkm		return (0);
65655682Smarkm	if (!isdigit(name[i]))
65755682Smarkm		return (0);
65855682Smarkm	u = 0;
65955682Smarkm	if (name[i] == '0' && isdigit(name[i+1]))
66055682Smarkm		return (0);
66155682Smarkm	while (isdigit(name[i])) {
66255682Smarkm		u *= 10;
66355682Smarkm		u += name[i++] - '0';
66455682Smarkm	}
66555682Smarkm	if (u > 0xffffff)
66655682Smarkm		return (0);
66755682Smarkm	*unit = u;
66855682Smarkm	if (namep)
66955682Smarkm		*namep = &name[i];
67055682Smarkm	if (name[i])
67155682Smarkm		return (2);
67255682Smarkm	return (1);
67355682Smarkm}
67455682Smarkm
67555682Smarkm/*
67655682Smarkm * Helper functions for cloning device drivers.
67755682Smarkm *
67855682Smarkm * The objective here is to make it unnecessary for the device drivers to
67955682Smarkm * use rman or similar to manage their unit number space.  Due to the way
68055682Smarkm * we do "on-demand" devices, using rman or other "private" methods
68155682Smarkm * will be very tricky to lock down properly once we lock down this file.
68255682Smarkm *
68355682Smarkm * Instead we give the drivers these routines which puts the struct cdev *'s
68455682Smarkm * that are to be managed on their own list, and gives the driver the ability
68555682Smarkm * to ask for the first free unit number or a given specified unit number.
68655682Smarkm *
68755682Smarkm * In addition these routines support paired devices (pty, nmdm and similar)
68855682Smarkm * by respecting a number of "flag" bits in the minor number.
68955682Smarkm *
69055682Smarkm */
69155682Smarkm
69255682Smarkmstruct clonedevs {
69355682Smarkm	LIST_HEAD(,cdev)	head;
69455682Smarkm};
69555682Smarkm
69655682Smarkmvoid
69755682Smarkmclone_setup(struct clonedevs **cdp)
69855682Smarkm{
69955682Smarkm
70055682Smarkm	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
70155682Smarkm	LIST_INIT(&(*cdp)->head);
70255682Smarkm}
703
704int
705clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
706{
707	struct clonedevs *cd;
708	struct cdev *dev, *dl, *de;
709	int unit, low, u;
710
711	KASSERT(*cdp != NULL,
712	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
713	KASSERT(!(extra & CLONE_UNITMASK),
714	    ("Illegal extra bits (0x%x) in clone_create", extra));
715	KASSERT(*up <= CLONE_UNITMASK,
716	    ("Too high unit (0x%x) in clone_create", *up));
717
718	if (csw->d_maj == MAJOR_AUTO)
719		find_major(csw);
720
721	/*
722	 * Search the list for a lot of things in one go:
723	 *   A preexisting match is returned immediately.
724	 *   The lowest free unit number if we are passed -1, and the place
725	 *	 in the list where we should insert that new element.
726	 *   The place to insert a specified unit number, if applicable
727	 *       the end of the list.
728	 */
729	unit = *up;
730	low = extra;
731	de = dl = NULL;
732	cd = *cdp;
733	LIST_FOREACH(dev, &cd->head, si_clone) {
734		u = dev2unit(dev);
735		if (u == (unit | extra)) {
736			*dp = dev;
737			return (0);
738		}
739		if (unit == -1 && u == low) {
740			low++;
741			de = dev;
742			continue;
743		}
744		if (u > (unit | extra)) {
745			dl = dev;
746			break;
747		}
748		de = dev;
749	}
750	if (unit == -1)
751		unit = low & CLONE_UNITMASK;
752	dev = newdev(csw->d_maj, unit2minor(unit | extra));
753	KASSERT(!(dev->si_flags & SI_CLONELIST),
754	    ("Dev %p should not be on clonelist", dev));
755	if (dl != NULL)
756		LIST_INSERT_BEFORE(dl, dev, si_clone);
757	else if (de != NULL)
758		LIST_INSERT_AFTER(de, dev, si_clone);
759	else
760		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
761	dev->si_flags |= SI_CLONELIST;
762	*up = unit;
763	return (1);
764}
765
766/*
767 * Kill everything still on the list.  The driver should already have
768 * disposed of any softc hung of the struct cdev *'s at this time.
769 */
770void
771clone_cleanup(struct clonedevs **cdp)
772{
773	struct cdev *dev, *tdev;
774	struct clonedevs *cd;
775
776	cd = *cdp;
777	if (cd == NULL)
778		return;
779	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
780		KASSERT(dev->si_flags & SI_NAMED,
781		    ("Driver has goofed in cloning underways udev %x", dev->si_udev));
782		destroy_dev(dev);
783	}
784	free(cd, M_DEVBUF);
785	*cdp = NULL;
786}
787
788/*
789 * Helper sysctl for devname(3).  We're given a struct cdev * and return
790 * the name, if any, registered by the device driver.
791 */
792static int
793sysctl_devname(SYSCTL_HANDLER_ARGS)
794{
795	int error;
796	dev_t ud;
797	struct cdev *dev;
798
799	error = SYSCTL_IN(req, &ud, sizeof (ud));
800	if (error)
801		return (error);
802	if (ud == NODEV)
803		return(EINVAL);
804	dev = findcdev(ud);
805	if (dev == NULL)
806		error = ENOENT;
807	else
808		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
809	return (error);
810}
811
812SYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
813	NULL, 0, sysctl_devname, "", "devname(3) handler");
814