kern_conf.c revision 135843
111126Sjulian/*-
2103722Sphk * Copyright (c) 1999-2002 Poul-Henning Kamp
311126Sjulian * All rights reserved.
411126Sjulian *
511126Sjulian * Redistribution and use in source and binary forms, with or without
611126Sjulian * modification, are permitted provided that the following conditions
711126Sjulian * are met:
811126Sjulian * 1. Redistributions of source code must retain the above copyright
911126Sjulian *    notice, this list of conditions and the following disclaimer.
1011126Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1111126Sjulian *    notice, this list of conditions and the following disclaimer in the
1211126Sjulian *    documentation and/or other materials provided with the distribution.
1311126Sjulian *
14103722Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15103722Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1611126Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17103722Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1811126Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1911126Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2011126Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2111126Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2211126Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2311126Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2411126Sjulian * SUCH DAMAGE.
2511126Sjulian */
2611126Sjulian
27116182Sobrien#include <sys/cdefs.h>
28116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 135843 2004-09-27 06:18:25Z phk $");
29116182Sobrien
3011126Sjulian#include <sys/param.h>
3148936Sphk#include <sys/kernel.h>
3283366Sjulian#include <sys/systm.h>
33111179Sphk#include <sys/bio.h>
3490737Sgreen#include <sys/lock.h>
3590737Sgreen#include <sys/mutex.h>
3650549Sphk#include <sys/sysctl.h>
3736735Sdfr#include <sys/module.h>
3848936Sphk#include <sys/malloc.h>
3911126Sjulian#include <sys/conf.h>
4012954Sjulian#include <sys/vnode.h>
4148936Sphk#include <sys/queue.h>
42120514Sphk#include <sys/poll.h>
4365374Sphk#include <sys/ctype.h>
44126078Sphk#include <sys/tty.h>
4549535Sphk#include <machine/stdarg.h>
4611126Sjulian
47131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
4848936Sphk
49111622Sphk/* Built at compile time from sys/conf/majors */
50111622Sphkextern unsigned char reserved_majors[256];
51111622Sphk
5250522Sphk/*
53130936Sle * This is the number of hash-buckets.  Experiments with 'real-life'
54130640Sphk * dev_t's show that a prime halfway between two powers of two works
5550522Sphk * best.
5650522Sphk */
5748936Sphk#define DEVT_HASH 83
5850522Sphk
59130585Sphk/* The number of struct cdev *'s we can create before malloc(9) kick in.  */
6048936Sphk#define DEVT_STASH 50
6148936Sphk
62104043Sphkstatic struct cdev devt_stash[DEVT_STASH];
6348936Sphk
64104043Sphkstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
6548936Sphk
66104043Sphkstatic LIST_HEAD(, cdev) dev_free;
6750549Sphk
6850549Sphkstatic int free_devt;
6950549SphkSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
7050549Sphk
71126082Sphkstatic struct mtx devmtx;
72130585Sphkstatic void freedev(struct cdev *dev);
73130640Sphkstatic struct cdev *newdev(int x, int y);
74126082Sphk
75130640Sphk
76135600Sphkvoid
77135600Sphkdev_lock(void)
78126082Sphk{
79126082Sphk	if (!mtx_initialized(&devmtx))
80131996Sphk		mtx_init(&devmtx, "cdev", NULL, MTX_DEF);
81126082Sphk	mtx_lock(&devmtx);
82126082Sphk}
83126082Sphk
84135600Sphkvoid
85135600Sphkdev_unlock(void)
86126082Sphk{
87135704Sphk
88126082Sphk	mtx_unlock(&devmtx);
89126082Sphk}
90126082Sphk
91126082Sphkvoid
92130585Sphkdev_ref(struct cdev *dev)
93126082Sphk{
94135704Sphk
95135600Sphk	dev_lock();
96126082Sphk	dev->si_refcount++;
97135600Sphk	dev_unlock();
98126082Sphk}
99126082Sphk
100126082Sphkvoid
101130585Sphkdev_rel(struct cdev *dev)
102126082Sphk{
103135600Sphk
104126082Sphk	dev->si_refcount--;
105126082Sphk	KASSERT(dev->si_refcount >= 0,
106126082Sphk	    ("dev_rel(%s) gave negative count", devtoname(dev)));
107126082Sphk	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
108126082Sphk		LIST_REMOVE(dev, si_list);
109126082Sphk		freedev(dev);
110126082Sphk	}
111126082Sphk}
112135704Sphkstruct cdevsw *
113135704Sphkdev_refthread(struct cdev *dev)
114135704Sphk{
115135704Sphk	struct cdevsw *csw;
116126082Sphk
117135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
118135704Sphk	dev_lock();
119135704Sphk	csw = dev->si_devsw;
120135704Sphk	if (csw != NULL)
121135704Sphk		dev->si_threadcount++;
122135704Sphk	dev_unlock();
123135704Sphk	return (csw);
124135704Sphk}
125135704Sphk
126135704Sphkvoid
127135704Sphkdev_relthread(struct cdev *dev)
128135704Sphk{
129135704Sphk
130135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
131135704Sphk	dev_lock();
132135704Sphk	dev->si_threadcount--;
133135704Sphk	dev_unlock();
134135704Sphk}
135135704Sphk
136120514Sphkint
137120514Sphknullop(void)
138120514Sphk{
13985603Sphk
140120514Sphk	return (0);
141120514Sphk}
142120514Sphk
143120514Sphkint
144120514Sphkeopnotsupp(void)
145120514Sphk{
146120514Sphk
147120514Sphk	return (EOPNOTSUPP);
148120514Sphk}
149120514Sphk
150111179Sphkstatic int
151111179Sphkenxio(void)
152111179Sphk{
153111179Sphk	return (ENXIO);
154111179Sphk}
155111179Sphk
156120514Sphkstatic int
157120514Sphkenodev(void)
158120514Sphk{
159120514Sphk	return (ENODEV);
160120514Sphk}
161120514Sphk
162120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
163120514Sphk
164111179Sphk#define dead_open	(d_open_t *)enxio
165111179Sphk#define dead_close	(d_close_t *)enxio
166111179Sphk#define dead_read	(d_read_t *)enxio
167111179Sphk#define dead_write	(d_write_t *)enxio
168111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
169120514Sphk#define dead_poll	(d_poll_t *)enodev
170120514Sphk#define dead_mmap	(d_mmap_t *)enodev
171111179Sphk
172111179Sphkstatic void
173111179Sphkdead_strategy(struct bio *bp)
174111179Sphk{
175111179Sphk
176111179Sphk	biofinish(bp, NULL, ENXIO);
177111179Sphk}
178111179Sphk
179111220Sphk#define dead_dump	(dumper_t *)enxio
180111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
181111179Sphk
182111179Sphkstatic struct cdevsw dead_cdevsw = {
183126080Sphk	.d_version =	D_VERSION,
184126080Sphk	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
185111815Sphk	.d_open =	dead_open,
186111815Sphk	.d_close =	dead_close,
187111815Sphk	.d_read =	dead_read,
188111815Sphk	.d_write =	dead_write,
189111815Sphk	.d_ioctl =	dead_ioctl,
190111815Sphk	.d_poll =	dead_poll,
191111815Sphk	.d_mmap =	dead_mmap,
192111815Sphk	.d_strategy =	dead_strategy,
193111815Sphk	.d_name =	"dead",
194111815Sphk	.d_maj =	255,
195111815Sphk	.d_dump =	dead_dump,
196111815Sphk	.d_kqfilter =	dead_kqfilter
197111179Sphk};
198111179Sphk
199120514Sphk/* Default methods if driver does not specify method */
200111179Sphk
201120514Sphk#define null_open	(d_open_t *)nullop
202120514Sphk#define null_close	(d_close_t *)nullop
203120514Sphk#define no_read		(d_read_t *)enodev
204120514Sphk#define no_write	(d_write_t *)enodev
205120514Sphk#define no_ioctl	(d_ioctl_t *)enodev
206120514Sphk#define no_mmap		(d_mmap_t *)enodev
207133741Sjmg#define no_kqfilter	(d_kqfilter_t *)enodev
208120514Sphk
209120514Sphkstatic void
210120514Sphkno_strategy(struct bio *bp)
211120514Sphk{
212120514Sphk
213120514Sphk	biofinish(bp, NULL, ENODEV);
214120514Sphk}
215120514Sphk
216120514Sphkstatic int
217130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
218120514Sphk{
219120514Sphk	/*
220120514Sphk	 * Return true for read/write.  If the user asked for something
221120514Sphk	 * special, return POLLNVAL, so that clients have a way of
222120514Sphk	 * determining reliably whether or not the extended
223120514Sphk	 * functionality is present without hard-coding knowledge
224120514Sphk	 * of specific filesystem implementations.
225120514Sphk	 * Stay in sync with vop_nopoll().
226120514Sphk	 */
227120514Sphk	if (events & ~POLLSTANDARD)
228120514Sphk		return (POLLNVAL);
229120514Sphk
230120514Sphk	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
231120514Sphk}
232120514Sphk
233120514Sphk#define no_dump		(dumper_t *)enodev
234120514Sphk
23547640Sphk/*
236130936Sle * struct cdev * and u_dev_t primitives
23747028Sphk */
23847028Sphk
23951927Sphkint
240130585Sphkmajor(struct cdev *x)
24147028Sphk{
242130640Sphk	if (x == NULL)
243130640Sphk		return NODEV;
24448936Sphk	return((x->si_udev >> 8) & 0xff);
24547028Sphk}
24647028Sphk
24747028Sphkint
248130585Sphkminor(struct cdev *x)
24947028Sphk{
250130640Sphk	if (x == NULL)
251130640Sphk		return NODEV;
25248936Sphk	return(x->si_udev & 0xffff00ff);
25347028Sphk}
25447028Sphk
25549826Sphkint
256130585Sphkdev2unit(struct cdev *x)
25749826Sphk{
25849826Sphk	int i;
25949826Sphk
260130640Sphk	if (x == NULL)
261130640Sphk		return NODEV;
26249826Sphk	i = minor(x);
26349826Sphk	return ((i & 0xff) | (i >> 8));
26449826Sphk}
26549826Sphk
26666067Sphkint
26766067Sphkunit2minor(int unit)
26866067Sphk{
26966067Sphk
27074522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
27166067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
27266067Sphk}
27366067Sphk
274130585Sphkstatic struct cdev *
27564880Sphkallocdev(void)
27664880Sphk{
27764880Sphk	static int stashed;
278104043Sphk	struct cdev *si;
27964880Sphk
280103101Sphk	if (LIST_FIRST(&dev_free)) {
281103101Sphk		si = LIST_FIRST(&dev_free);
282126156Sphk		LIST_REMOVE(si, si_hash);
283103101Sphk	} else if (stashed >= DEVT_STASH) {
284104043Sphk		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
285111146Sphk		    M_USE_RESERVE | M_ZERO | M_WAITOK);
28664880Sphk	} else {
28764880Sphk		si = devt_stash + stashed++;
28877215Sphk		bzero(si, sizeof *si);
289103101Sphk		si->si_flags |= SI_STASHED;
29064880Sphk	}
291110317Sphk	si->__si_namebuf[0] = '\0';
292110317Sphk	si->si_name = si->__si_namebuf;
29377215Sphk	LIST_INIT(&si->si_children);
29464880Sphk	return (si);
29564880Sphk}
29664880Sphk
297130585Sphkstatic struct cdev *
298130640Sphknewdev(int x, int y)
29947028Sphk{
300104043Sphk	struct cdev *si;
301130640Sphk	dev_t	udev;
30248936Sphk	int hash;
30348936Sphk
304130640Sphk	if (x == umajor(NODEV) && y == uminor(NODEV))
305130640Sphk		panic("newdev of NODEV");
30648936Sphk	udev = (x << 8) | y;
30748936Sphk	hash = udev % DEVT_HASH;
30850549Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
30948936Sphk		if (si->si_udev == udev)
31048936Sphk			return (si);
31148936Sphk	}
31264880Sphk	si = allocdev();
31348936Sphk	si->si_udev = udev;
31450549Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
315125850Sbde	return (si);
31647028Sphk}
31747028Sphk
318126082Sphkstatic void
319130585Sphkfreedev(struct cdev *dev)
32050549Sphk{
32150549Sphk
32250549Sphk	if (dev->si_flags & SI_STASHED) {
32350549Sphk		bzero(dev, sizeof(*dev));
32477215Sphk		dev->si_flags |= SI_STASHED;
32550549Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
32650549Sphk	} else {
32750549Sphk		FREE(dev, M_DEVT);
32850549Sphk	}
32950549Sphk}
33050549Sphk
331130640Sphkdev_t
332130585Sphkdev2udev(struct cdev *x)
33347028Sphk{
334130640Sphk	if (x == NULL)
335130640Sphk		return (NODEV);
33648936Sphk	return (x->si_udev);
33747028Sphk}
33847028Sphk
339130585Sphkstruct cdev *
340130640Sphkfindcdev(dev_t udev)
34147028Sphk{
342126081Sphk	struct cdev *si;
343126081Sphk	int hash;
34455414Sphk
345130640Sphk	if (udev == NODEV)
346130640Sphk		return (NULL);
347126081Sphk	hash = udev % DEVT_HASH;
348126081Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
349126081Sphk		if (si->si_udev == udev)
350126081Sphk			return (si);
35148864Sphk	}
352130640Sphk	return (NULL);
35347028Sphk}
35447028Sphk
35547028Sphkint
356130640Sphkuminor(dev_t dev)
35747028Sphk{
358125850Sbde	return (dev & 0xffff00ff);
35947028Sphk}
36047028Sphk
36147028Sphkint
362130640Sphkumajor(dev_t dev)
36347028Sphk{
364125850Sbde	return ((dev & 0xff00) >> 8);
36547028Sphk}
36647028Sphk
367125846Sphkstatic void
368126077Sphkfind_major(struct cdevsw *devsw)
36949535Sphk{
37049535Sphk	int i;
37149535Sphk
372126077Sphk	for (i = NUMCDEVSW - 1; i > 0; i--)
373126077Sphk		if (reserved_majors[i] != i)
374126077Sphk			break;
375126077Sphk	KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
376126077Sphk	devsw->d_maj = i;
377126077Sphk	reserved_majors[i] = i;
378126082Sphk	devsw->d_flags |= D_ALLOCMAJ;
379126077Sphk}
380126077Sphk
381126077Sphkstatic void
382126082Sphkfini_cdevsw(struct cdevsw *devsw)
383126082Sphk{
384126082Sphk	if (devsw->d_flags & D_ALLOCMAJ) {
385126082Sphk		reserved_majors[devsw->d_maj] = 0;
386126082Sphk		devsw->d_maj = MAJOR_AUTO;
387126082Sphk		devsw->d_flags &= ~D_ALLOCMAJ;
388126156Sphk	} else if (devsw->d_maj == 0)
389126156Sphk		devsw->d_maj = 256;
390126156Sphk	devsw->d_flags &= ~D_INIT;
391126082Sphk}
392126082Sphk
393126082Sphkstatic void
394126077Sphkprep_cdevsw(struct cdevsw *devsw)
395126077Sphk{
396126077Sphk
397135600Sphk	dev_lock();
398126082Sphk
399126082Sphk	if (devsw->d_version != D_VERSION_00) {
400126082Sphk		printf(
401126082Sphk		    "WARNING: Device driver \"%s\" has wrong version %s\n",
402126082Sphk		    devsw->d_name, "and is disabled.  Recompile KLD module.");
403126082Sphk		devsw->d_open = dead_open;
404126082Sphk		devsw->d_close = dead_close;
405126082Sphk		devsw->d_read = dead_read;
406126082Sphk		devsw->d_write = dead_write;
407126082Sphk		devsw->d_ioctl = dead_ioctl;
408126082Sphk		devsw->d_poll = dead_poll;
409126082Sphk		devsw->d_mmap = dead_mmap;
410126082Sphk		devsw->d_strategy = dead_strategy;
411126082Sphk		devsw->d_dump = dead_dump;
412126082Sphk		devsw->d_kqfilter = dead_kqfilter;
413126082Sphk	}
414126082Sphk
415126078Sphk	if (devsw->d_flags & D_TTY) {
416129943Sphk		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
417126078Sphk		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
418126078Sphk		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
419126078Sphk		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
420126078Sphk		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
421126078Sphk	}
422126078Sphk
423120514Sphk	if (devsw->d_open == NULL)	devsw->d_open = null_open;
424120514Sphk	if (devsw->d_close == NULL)	devsw->d_close = null_close;
425120514Sphk	if (devsw->d_read == NULL)	devsw->d_read = no_read;
426120514Sphk	if (devsw->d_write == NULL)	devsw->d_write = no_write;
427120514Sphk	if (devsw->d_ioctl == NULL)	devsw->d_ioctl = no_ioctl;
428120514Sphk	if (devsw->d_poll == NULL)	devsw->d_poll = no_poll;
429120514Sphk	if (devsw->d_mmap == NULL)	devsw->d_mmap = no_mmap;
430120514Sphk	if (devsw->d_strategy == NULL)	devsw->d_strategy = no_strategy;
431120514Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
432120514Sphk	if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = no_kqfilter;
433126082Sphk
434126082Sphk	LIST_INIT(&devsw->d_devs);
435126082Sphk
436126082Sphk	devsw->d_flags |= D_INIT;
437126082Sphk
438111622Sphk	if (devsw->d_maj == MAJOR_AUTO) {
439126077Sphk		find_major(devsw);
440111626Sphk	} else {
441112035Sphk		if (devsw->d_maj == 256)	/* XXX: tty_cons.c is magic */
442112035Sphk			devsw->d_maj = 0;
443111626Sphk		KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256,
444111626Sphk		    ("Invalid major (%d) in make_dev", devsw->d_maj));
445111626Sphk		if (reserved_majors[devsw->d_maj] != devsw->d_maj) {
446111626Sphk			printf("WARNING: driver \"%s\" used %s %d\n",
447111626Sphk			    devsw->d_name, "unreserved major device number",
448111626Sphk			    devsw->d_maj);
449111626Sphk			reserved_majors[devsw->d_maj] = devsw->d_maj;
450111626Sphk		}
451111622Sphk	}
452135600Sphk	dev_unlock();
453125846Sphk}
454111622Sphk
455130585Sphkstruct cdev *
456126082Sphkmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
457125846Sphk{
458130585Sphk	struct cdev *dev;
459125846Sphk	va_list ap;
460125846Sphk	int i;
461125846Sphk
462126082Sphk	KASSERT((minornr & ~0xffff00ff) == 0,
463126082Sphk	    ("Invalid minor (0x%x) in make_dev", minornr));
464126082Sphk
465126082Sphk	if (!(devsw->d_flags & D_INIT))
466126082Sphk		prep_cdevsw(devsw);
467130640Sphk	dev = newdev(devsw->d_maj, minornr);
468120529Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
469120529Sphk	    dev->si_flags & SI_NAMED &&
470120529Sphk	    dev->si_devsw == devsw) {
471120529Sphk		/*
472120529Sphk		 * This is allowed as it removes races and generally
473120529Sphk		 * simplifies cloning devices.
474126082Sphk		 * XXX: still ??
475120529Sphk		 */
476120529Sphk		return (dev);
477120529Sphk	}
478135600Sphk	dev_lock();
479126082Sphk	KASSERT(!(dev->si_flags & SI_NAMED),
480126082Sphk	    ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)",
481126082Sphk	    devsw->d_name, major(dev), minor(dev), devtoname(dev)));
482126082Sphk
48349535Sphk	va_start(ap, fmt);
484110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
485110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
486134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
487110318Sphk		    dev->__si_namebuf);
488110318Sphk	}
48949535Sphk	va_end(ap);
49049535Sphk	dev->si_devsw = devsw;
49164880Sphk	dev->si_uid = uid;
49264880Sphk	dev->si_gid = gid;
49364880Sphk	dev->si_mode = perms;
49465747Sphk	dev->si_flags |= SI_NAMED;
49550092Sjulian
496126082Sphk	LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list);
497111730Sphk	devfs_create(dev);
498135600Sphk	dev_unlock();
49949535Sphk	return (dev);
50049535Sphk}
50149535Sphk
50285076Sjlemonint
503130585Sphkdev_named(struct cdev *pdev, const char *name)
50485076Sjlemon{
505130585Sphk	struct cdev *cdev;
50685076Sjlemon
50785076Sjlemon	if (strcmp(devtoname(pdev), name) == 0)
50885076Sjlemon		return (1);
50985076Sjlemon	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
51085076Sjlemon		if (strcmp(devtoname(cdev), name) == 0)
51185076Sjlemon			return (1);
51285076Sjlemon	return (0);
51385076Sjlemon}
51485076Sjlemon
51577215Sphkvoid
516130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev)
51777215Sphk{
51877215Sphk
519135600Sphk	dev_lock();
52077215Sphk	cdev->si_parent = pdev;
52177215Sphk	cdev->si_flags |= SI_CHILD;
52277215Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
523135600Sphk	dev_unlock();
52477215Sphk}
52577215Sphk
526130585Sphkstruct cdev *
527130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
52864880Sphk{
529130585Sphk	struct cdev *dev;
53064880Sphk	va_list ap;
53164880Sphk	int i;
53264880Sphk
53364880Sphk	dev = allocdev();
534135600Sphk	dev_lock();
53564880Sphk	dev->si_flags |= SI_ALIAS;
53665747Sphk	dev->si_flags |= SI_NAMED;
53764880Sphk	va_start(ap, fmt);
538110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
539110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
540134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
541110318Sphk		    dev->__si_namebuf);
542110318Sphk	}
54364880Sphk	va_end(ap);
54464880Sphk
545111730Sphk	devfs_create(dev);
546135600Sphk	dev_unlock();
547126082Sphk	dev_depends(pdev, dev);
54864880Sphk	return (dev);
54964880Sphk}
55064880Sphk
551126082Sphkstatic void
552130585Sphkidestroy_dev(struct cdev *dev)
55350549Sphk{
554135843Sphk	struct cdevsw *csw;
555135843Sphk
556135843Sphk	KASSERT(dev->si_flags & SI_NAMED,
557135843Sphk	    ("WARNING: Driver mistake: destroy_dev on %d/%d\n",
558135843Sphk	    major(dev), minor(dev)));
55965747Sphk
560111730Sphk	devfs_destroy(dev);
561126082Sphk
562126082Sphk	/* Remove name marking */
563126077Sphk	dev->si_flags &= ~SI_NAMED;
564126077Sphk
565126082Sphk	/* If we are a child, remove us from the parents list */
56677215Sphk	if (dev->si_flags & SI_CHILD) {
56777215Sphk		LIST_REMOVE(dev, si_siblings);
56877215Sphk		dev->si_flags &= ~SI_CHILD;
56977215Sphk	}
570126082Sphk
571126082Sphk	/* Kill our children */
57277215Sphk	while (!LIST_EMPTY(&dev->si_children))
573126082Sphk		idestroy_dev(LIST_FIRST(&dev->si_children));
574126082Sphk
575126082Sphk	/* Remove from clone list */
576126077Sphk	if (dev->si_flags & SI_CLONELIST) {
577126077Sphk		LIST_REMOVE(dev, si_clone);
578126077Sphk		dev->si_flags &= ~SI_CLONELIST;
579126077Sphk	}
580126082Sphk
581135843Sphk	csw = dev->si_devsw;
582135843Sphk	dev->si_devsw = NULL;
583135843Sphk	while (csw->d_purge != NULL && dev->si_threadcount) {
584135843Sphk		printf("Purging %lu threads from %s\n",
585135843Sphk		    dev->si_threadcount, devtoname(dev));
586135843Sphk		csw->d_purge(dev);
587135843Sphk		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
588135843Sphk	}
589135843Sphk
590135843Sphk	dev->si_drv1 = 0;
591135843Sphk	dev->si_drv2 = 0;
592135843Sphk	bzero(&dev->__si_u, sizeof(dev->__si_u));
593135843Sphk
594126082Sphk	if (!(dev->si_flags & SI_ALIAS)) {
595126082Sphk		/* Remove from cdevsw list */
596126082Sphk		LIST_REMOVE(dev, si_list);
597126082Sphk
598130585Sphk		/* If cdevsw has no struct cdev *'s, clean it */
599126082Sphk		if (LIST_EMPTY(&dev->si_devsw->d_devs))
600126082Sphk			fini_cdevsw(dev->si_devsw);
601126082Sphk
602126082Sphk		LIST_REMOVE(dev, si_hash);
603126082Sphk	}
60465747Sphk	dev->si_flags &= ~SI_ALIAS;
605135843Sphk
606126082Sphk	if (dev->si_refcount > 0) {
607126082Sphk		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
608126082Sphk	} else {
609126082Sphk		freedev(dev);
610126082Sphk	}
61150549Sphk}
61250549Sphk
613126082Sphkvoid
614130585Sphkdestroy_dev(struct cdev *dev)
615126082Sphk{
616126082Sphk
617135600Sphk	dev_lock();
618126082Sphk	idestroy_dev(dev);
619135600Sphk	dev_unlock();
620126082Sphk}
621126082Sphk
62251225Sbdeconst char *
623130585Sphkdevtoname(struct cdev *dev)
62449982Sbillf{
62550549Sphk	char *p;
626135712Sphk	struct cdevsw *csw;
62751225Sbde	int mynor;
62849982Sbillf
62950549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
63050549Sphk		p = dev->si_name;
631135712Sphk		sprintf(p, "#%d", major(dev));
63250549Sphk		p += strlen(p);
633135712Sphk		csw = dev_refthread(dev);
634135712Sphk		if (csw != NULL) {
635135712Sphk			sprintf(p, "(%s)", csw->d_name);
636135712Sphk			dev_relthread(dev);
637135712Sphk		}
638135712Sphk		p += strlen(p);
63951225Sbde		mynor = minor(dev);
64051225Sbde		if (mynor < 0 || mynor > 255)
641135712Sphk			sprintf(p, "/%#x", (u_int)mynor);
64251225Sbde		else
643135712Sphk			sprintf(p, "/%d", mynor);
64450549Sphk	}
64549982Sbillf	return (dev->si_name);
64649982Sbillf}
64765374Sphk
64865374Sphkint
64991998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
65065374Sphk{
65165374Sphk	int u, i;
65265374Sphk
65375519Sbrian	i = strlen(stem);
65475519Sbrian	if (bcmp(stem, name, i) != 0)
65565374Sphk		return (0);
65665374Sphk	if (!isdigit(name[i]))
65765374Sphk		return (0);
65865374Sphk	u = 0;
65986461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
66086461Sphk		return (0);
66165374Sphk	while (isdigit(name[i])) {
66265374Sphk		u *= 10;
66365374Sphk		u += name[i++] - '0';
66465374Sphk	}
665104523Sgreen	if (u > 0xffffff)
666104523Sgreen		return (0);
66765374Sphk	*unit = u;
66865374Sphk	if (namep)
66965374Sphk		*namep = &name[i];
67065374Sphk	if (name[i])
67165374Sphk		return (2);
67265374Sphk	return (1);
67365374Sphk}
67465632Sphk
67565632Sphk/*
676126077Sphk * Helper functions for cloning device drivers.
677126077Sphk *
678126077Sphk * The objective here is to make it unnecessary for the device drivers to
679126077Sphk * use rman or similar to manage their unit number space.  Due to the way
680126077Sphk * we do "on-demand" devices, using rman or other "private" methods
681126077Sphk * will be very tricky to lock down properly once we lock down this file.
682126077Sphk *
683130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s
684130936Sle * that are to be managed on their own list, and gives the driver the ability
685126077Sphk * to ask for the first free unit number or a given specified unit number.
686126077Sphk *
687126077Sphk * In addition these routines support paired devices (pty, nmdm and similar)
688126077Sphk * by respecting a number of "flag" bits in the minor number.
689126077Sphk *
690126077Sphk */
691126077Sphk
692126077Sphkstruct clonedevs {
693126077Sphk	LIST_HEAD(,cdev)	head;
694126077Sphk};
695126077Sphk
696126845Sphkvoid
697126845Sphkclone_setup(struct clonedevs **cdp)
698126845Sphk{
699126845Sphk
700126845Sphk	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
701126845Sphk	LIST_INIT(&(*cdp)->head);
702126845Sphk}
703126845Sphk
704126077Sphkint
705130585Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
706126077Sphk{
707126077Sphk	struct clonedevs *cd;
708130585Sphk	struct cdev *dev, *dl, *de;
709126077Sphk	int unit, low, u;
710126077Sphk
711126845Sphk	KASSERT(*cdp != NULL,
712126845Sphk	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
713126077Sphk	KASSERT(!(extra & CLONE_UNITMASK),
714126845Sphk	    ("Illegal extra bits (0x%x) in clone_create", extra));
715126077Sphk	KASSERT(*up <= CLONE_UNITMASK,
716126845Sphk	    ("Too high unit (0x%x) in clone_create", *up));
717126077Sphk
718126077Sphk	if (csw->d_maj == MAJOR_AUTO)
719126077Sphk		find_major(csw);
720126077Sphk
721126077Sphk	/*
722126077Sphk	 * Search the list for a lot of things in one go:
723126077Sphk	 *   A preexisting match is returned immediately.
724126077Sphk	 *   The lowest free unit number if we are passed -1, and the place
725126077Sphk	 *	 in the list where we should insert that new element.
726126077Sphk	 *   The place to insert a specified unit number, if applicable
727126077Sphk	 *       the end of the list.
728126077Sphk	 */
729126077Sphk	unit = *up;
730126849Sphk	low = extra;
731126077Sphk	de = dl = NULL;
732126845Sphk	cd = *cdp;
733126077Sphk	LIST_FOREACH(dev, &cd->head, si_clone) {
734126077Sphk		u = dev2unit(dev);
735126077Sphk		if (u == (unit | extra)) {
736126077Sphk			*dp = dev;
737126077Sphk			return (0);
738126077Sphk		}
739126077Sphk		if (unit == -1 && u == low) {
740126077Sphk			low++;
741126077Sphk			de = dev;
742126077Sphk			continue;
743126077Sphk		}
744126849Sphk		if (u > (unit | extra)) {
745126077Sphk			dl = dev;
746126077Sphk			break;
747126077Sphk		}
748126077Sphk		de = dev;
749126077Sphk	}
750126077Sphk	if (unit == -1)
751126849Sphk		unit = low & CLONE_UNITMASK;
752130640Sphk	dev = newdev(csw->d_maj, unit2minor(unit | extra));
753126077Sphk	KASSERT(!(dev->si_flags & SI_CLONELIST),
754126077Sphk	    ("Dev %p should not be on clonelist", dev));
755126077Sphk	if (dl != NULL)
756126077Sphk		LIST_INSERT_BEFORE(dl, dev, si_clone);
757126077Sphk	else if (de != NULL)
758126077Sphk		LIST_INSERT_AFTER(de, dev, si_clone);
759126077Sphk	else
760126077Sphk		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
761126077Sphk	dev->si_flags |= SI_CLONELIST;
762126077Sphk	*up = unit;
763126077Sphk	return (1);
764126077Sphk}
765126077Sphk
766126077Sphk/*
767126077Sphk * Kill everything still on the list.  The driver should already have
768130585Sphk * disposed of any softc hung of the struct cdev *'s at this time.
769126077Sphk */
770126077Sphkvoid
771126077Sphkclone_cleanup(struct clonedevs **cdp)
772126077Sphk{
773130585Sphk	struct cdev *dev, *tdev;
774126077Sphk	struct clonedevs *cd;
775126077Sphk
776126077Sphk	cd = *cdp;
777126077Sphk	if (cd == NULL)
778126077Sphk		return;
779126077Sphk	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
780126077Sphk		KASSERT(dev->si_flags & SI_NAMED,
781126077Sphk		    ("Driver has goofed in cloning underways udev %x", dev->si_udev));
782126077Sphk		destroy_dev(dev);
783126077Sphk	}
784126077Sphk	free(cd, M_DEVBUF);
785126077Sphk	*cdp = NULL;
786126077Sphk}
787126077Sphk
788126077Sphk/*
789130936Sle * Helper sysctl for devname(3).  We're given a struct cdev * and return
79065632Sphk * the name, if any, registered by the device driver.
79165632Sphk */
79265632Sphkstatic int
79365632Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
79465632Sphk{
79565632Sphk	int error;
796130640Sphk	dev_t ud;
797130585Sphk	struct cdev *dev;
79865632Sphk
79965632Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
80065632Sphk	if (error)
80165632Sphk		return (error);
802130640Sphk	if (ud == NODEV)
80371342Sphk		return(EINVAL);
804130640Sphk	dev = findcdev(ud);
805130640Sphk	if (dev == NULL)
80665632Sphk		error = ENOENT;
80765632Sphk	else
80865632Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
80965632Sphk	return (error);
81065632Sphk}
81165632Sphk
81267905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
81365632Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
814