kern_conf.c revision 126080
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 126080 2004-02-21 21:10:55Z 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
4769774Sphkstatic MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
4848936Sphk
49111622Sphk/* Built at compile time from sys/conf/majors */
50111622Sphkextern unsigned char reserved_majors[256];
51111622Sphk
5250522Sphk/*
5350522Sphk * This is the number of hash-buckets.  Experiements with 'real-life'
5450522Sphk * udev_t's show that a prime halfway between two powers of two works
5550522Sphk * best.
5650522Sphk */
5748936Sphk#define DEVT_HASH 83
5850522Sphk
5950522Sphk/* The number of dev_t'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
71120514Sphkint
72120514Sphknullop(void)
73120514Sphk{
7485603Sphk
75120514Sphk	return (0);
76120514Sphk}
77120514Sphk
78120514Sphkint
79120514Sphkeopnotsupp(void)
80120514Sphk{
81120514Sphk
82120514Sphk	return (EOPNOTSUPP);
83120514Sphk}
84120514Sphk
85111179Sphkstatic int
86111179Sphkenxio(void)
87111179Sphk{
88111179Sphk	return (ENXIO);
89111179Sphk}
90111179Sphk
91120514Sphkstatic int
92120514Sphkenodev(void)
93120514Sphk{
94120514Sphk	return (ENODEV);
95120514Sphk}
96120514Sphk
97120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
98120514Sphk
99111179Sphk#define dead_open	(d_open_t *)enxio
100111179Sphk#define dead_close	(d_close_t *)enxio
101111179Sphk#define dead_read	(d_read_t *)enxio
102111179Sphk#define dead_write	(d_write_t *)enxio
103111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
104120514Sphk#define dead_poll	(d_poll_t *)enodev
105120514Sphk#define dead_mmap	(d_mmap_t *)enodev
106111179Sphk
107111179Sphkstatic void
108111179Sphkdead_strategy(struct bio *bp)
109111179Sphk{
110111179Sphk
111111179Sphk	biofinish(bp, NULL, ENXIO);
112111179Sphk}
113111179Sphk
114111220Sphk#define dead_dump	(dumper_t *)enxio
115111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
116111179Sphk
117111179Sphkstatic struct cdevsw dead_cdevsw = {
118126080Sphk	.d_version =	D_VERSION,
119126080Sphk	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
120111815Sphk	.d_open =	dead_open,
121111815Sphk	.d_close =	dead_close,
122111815Sphk	.d_read =	dead_read,
123111815Sphk	.d_write =	dead_write,
124111815Sphk	.d_ioctl =	dead_ioctl,
125111815Sphk	.d_poll =	dead_poll,
126111815Sphk	.d_mmap =	dead_mmap,
127111815Sphk	.d_strategy =	dead_strategy,
128111815Sphk	.d_name =	"dead",
129111815Sphk	.d_maj =	255,
130111815Sphk	.d_dump =	dead_dump,
131111815Sphk	.d_kqfilter =	dead_kqfilter
132111179Sphk};
133111179Sphk
134120514Sphk/* Default methods if driver does not specify method */
135111179Sphk
136120514Sphk#define null_open	(d_open_t *)nullop
137120514Sphk#define null_close	(d_close_t *)nullop
138120514Sphk#define no_read		(d_read_t *)enodev
139120514Sphk#define no_write	(d_write_t *)enodev
140120514Sphk#define no_ioctl	(d_ioctl_t *)enodev
141120514Sphk#define no_mmap		(d_mmap_t *)enodev
142120514Sphk
143120514Sphkstatic int
144120514Sphkno_kqfilter(dev_t dev __unused, struct knote *kn __unused)
145120514Sphk{
146120514Sphk
147120514Sphk	return (1);
148120514Sphk}
149120514Sphk
150120514Sphkstatic void
151120514Sphkno_strategy(struct bio *bp)
152120514Sphk{
153120514Sphk
154120514Sphk	biofinish(bp, NULL, ENODEV);
155120514Sphk}
156120514Sphk
157120514Sphkstatic int
158120514Sphkno_poll(dev_t dev __unused, int events, struct thread *td __unused)
159120514Sphk{
160120514Sphk	/*
161120514Sphk	 * Return true for read/write.  If the user asked for something
162120514Sphk	 * special, return POLLNVAL, so that clients have a way of
163120514Sphk	 * determining reliably whether or not the extended
164120514Sphk	 * functionality is present without hard-coding knowledge
165120514Sphk	 * of specific filesystem implementations.
166120514Sphk	 * Stay in sync with vop_nopoll().
167120514Sphk	 */
168120514Sphk	if (events & ~POLLSTANDARD)
169120514Sphk		return (POLLNVAL);
170120514Sphk
171120514Sphk	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
172120514Sphk}
173120514Sphk
174120514Sphk#define no_dump		(dumper_t *)enodev
175120514Sphk
17647640Sphkstruct cdevsw *
17747640Sphkdevsw(dev_t dev)
17851927Sphk{
17949679Sphk	if (dev->si_devsw)
18049679Sphk		return (dev->si_devsw);
181111179Sphk	return (&dead_cdevsw);
18247640Sphk}
18347640Sphk
18447640Sphk/*
18547028Sphk * dev_t and u_dev_t primitives
18647028Sphk */
18747028Sphk
18851927Sphkint
18947028Sphkmajor(dev_t x)
19047028Sphk{
19148936Sphk	if (x == NODEV)
19248936Sphk		return NOUDEV;
19348936Sphk	return((x->si_udev >> 8) & 0xff);
19447028Sphk}
19547028Sphk
19647028Sphkint
19747028Sphkminor(dev_t x)
19847028Sphk{
19948936Sphk	if (x == NODEV)
20048936Sphk		return NOUDEV;
20148936Sphk	return(x->si_udev & 0xffff00ff);
20247028Sphk}
20347028Sphk
20449826Sphkint
20566067Sphkdev2unit(dev_t x)
20649826Sphk{
20749826Sphk	int i;
20849826Sphk
20949826Sphk	if (x == NODEV)
21049826Sphk		return NOUDEV;
21149826Sphk	i = minor(x);
21249826Sphk	return ((i & 0xff) | (i >> 8));
21349826Sphk}
21449826Sphk
21566067Sphkint
21666067Sphkunit2minor(int unit)
21766067Sphk{
21866067Sphk
21974522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
22066067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
22166067Sphk}
22266067Sphk
22364880Sphkstatic dev_t
22464880Sphkallocdev(void)
22564880Sphk{
22664880Sphk	static int stashed;
227104043Sphk	struct cdev *si;
22864880Sphk
229103101Sphk	if (LIST_FIRST(&dev_free)) {
230103101Sphk		si = LIST_FIRST(&dev_free);
231103101Sphk		LIST_REMOVE(si, si_hash);
232103101Sphk	} else if (stashed >= DEVT_STASH) {
233104043Sphk		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
234111146Sphk		    M_USE_RESERVE | M_ZERO | M_WAITOK);
23564880Sphk	} else {
23664880Sphk		si = devt_stash + stashed++;
23777215Sphk		bzero(si, sizeof *si);
238103101Sphk		si->si_flags |= SI_STASHED;
23964880Sphk	}
240110317Sphk	si->__si_namebuf[0] = '\0';
241110317Sphk	si->si_name = si->__si_namebuf;
24277215Sphk	LIST_INIT(&si->si_children);
24373942Smckusick	TAILQ_INIT(&si->si_snapshots);
24464880Sphk	return (si);
24564880Sphk}
24664880Sphk
24747680Sphkdev_t
24847028Sphkmakedev(int x, int y)
24947028Sphk{
250104043Sphk	struct cdev *si;
25148936Sphk	udev_t	udev;
25248936Sphk	int hash;
25348936Sphk
25455414Sphk	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
25571342Sphk		panic("makedev of NOUDEV");
25648936Sphk	udev = (x << 8) | y;
25748936Sphk	hash = udev % DEVT_HASH;
25850549Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
25948936Sphk		if (si->si_udev == udev)
26048936Sphk			return (si);
26148936Sphk	}
26264880Sphk	si = allocdev();
26348936Sphk	si->si_udev = udev;
26450549Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
265125850Sbde	return (si);
26647028Sphk}
26747028Sphk
26850549Sphkvoid
26950549Sphkfreedev(dev_t dev)
27050549Sphk{
27150549Sphk
27250549Sphk	if (!free_devt)
27350549Sphk		return;
27450549Sphk	if (SLIST_FIRST(&dev->si_hlist))
27550549Sphk		return;
27651927Sphk	if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
27750549Sphk		return;
27850549Sphk	LIST_REMOVE(dev, si_hash);
27950549Sphk	if (dev->si_flags & SI_STASHED) {
28050549Sphk		bzero(dev, sizeof(*dev));
28177215Sphk		dev->si_flags |= SI_STASHED;
28250549Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
28350549Sphk	} else {
28450549Sphk		FREE(dev, M_DEVT);
28550549Sphk	}
28650549Sphk}
28750549Sphk
28847028Sphkudev_t
28947028Sphkdev2udev(dev_t x)
29047028Sphk{
29148936Sphk	if (x == NODEV)
292125850Sbde		return (NOUDEV);
29348936Sphk	return (x->si_udev);
29447028Sphk}
29547028Sphk
29647028Sphkdev_t
29747028Sphkudev2dev(udev_t x, int b)
29847028Sphk{
29955414Sphk
30055414Sphk	if (x == NOUDEV)
30155414Sphk		return (NODEV);
30248864Sphk	switch (b) {
303125850Sbde	case 0:
304125850Sbde		return (makedev(umajor(x), uminor(x)));
305125850Sbde	default:
306125850Sbde		Debugger("udev2dev(...,X)");
307125850Sbde		return (NODEV);
30848864Sphk	}
30947028Sphk}
31047028Sphk
31147028Sphkint
31247028Sphkuminor(udev_t dev)
31347028Sphk{
314125850Sbde	return (dev & 0xffff00ff);
31547028Sphk}
31647028Sphk
31747028Sphkint
31847028Sphkumajor(udev_t dev)
31947028Sphk{
320125850Sbde	return ((dev & 0xff00) >> 8);
32147028Sphk}
32247028Sphk
32347028Sphkudev_t
32448859Sphkmakeudev(int x, int y)
32547028Sphk{
326125850Sbde	return ((x << 8) | y);
32747028Sphk}
32847028Sphk
329125846Sphkstatic void
330126077Sphkfind_major(struct cdevsw *devsw)
33149535Sphk{
33249535Sphk	int i;
33349535Sphk
334126077Sphk	for (i = NUMCDEVSW - 1; i > 0; i--)
335126077Sphk		if (reserved_majors[i] != i)
336126077Sphk			break;
337126077Sphk	KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
338126077Sphk	devsw->d_maj = i;
339126077Sphk	reserved_majors[i] = i;
340126077Sphk}
341126077Sphk
342126077Sphkstatic void
343126077Sphkprep_cdevsw(struct cdevsw *devsw)
344126077Sphk{
345126077Sphk
346126078Sphk	if (devsw->d_flags & D_TTY) {
347126078Sphk		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
348126078Sphk		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
349126078Sphk		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
350126078Sphk		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
351126078Sphk	}
352126078Sphk
353120514Sphk	if (devsw->d_open == NULL)	devsw->d_open = null_open;
354120514Sphk	if (devsw->d_close == NULL)	devsw->d_close = null_close;
355120514Sphk	if (devsw->d_read == NULL)	devsw->d_read = no_read;
356120514Sphk	if (devsw->d_write == NULL)	devsw->d_write = no_write;
357120514Sphk	if (devsw->d_ioctl == NULL)	devsw->d_ioctl = no_ioctl;
358120514Sphk	if (devsw->d_poll == NULL)	devsw->d_poll = no_poll;
359120514Sphk	if (devsw->d_mmap == NULL)	devsw->d_mmap = no_mmap;
360120514Sphk	if (devsw->d_strategy == NULL)	devsw->d_strategy = no_strategy;
361120514Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
362120514Sphk	if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = no_kqfilter;
363111622Sphk	if (devsw->d_maj == MAJOR_AUTO) {
364126077Sphk		find_major(devsw);
365111626Sphk	} else {
366112035Sphk		if (devsw->d_maj == 256)	/* XXX: tty_cons.c is magic */
367112035Sphk			devsw->d_maj = 0;
368111626Sphk		KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256,
369111626Sphk		    ("Invalid major (%d) in make_dev", devsw->d_maj));
370111626Sphk		if (reserved_majors[devsw->d_maj] != devsw->d_maj) {
371111626Sphk			printf("WARNING: driver \"%s\" used %s %d\n",
372111626Sphk			    devsw->d_name, "unreserved major device number",
373111626Sphk			    devsw->d_maj);
374111626Sphk			reserved_majors[devsw->d_maj] = devsw->d_maj;
375111626Sphk		}
376111622Sphk	}
377125846Sphk}
378111622Sphk
379125846Sphkdev_t
380125850Sbdemake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms,
381125850Sbde    const char *fmt, ...)
382125846Sphk{
383125850Sbde	dev_t dev;
384125846Sphk	va_list ap;
385125846Sphk	int i;
386125846Sphk
387125846Sphk	KASSERT((minor & ~0xffff00ff) == 0,
388125846Sphk	    ("Invalid minor (0x%x) in make_dev", minor));
389125846Sphk	prep_cdevsw(devsw);
39049535Sphk	dev = makedev(devsw->d_maj, minor);
391120529Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
392120529Sphk	    dev->si_flags & SI_NAMED &&
393120529Sphk	    dev->si_devsw == devsw) {
394120529Sphk		/*
395120529Sphk		 * This is allowed as it removes races and generally
396120529Sphk		 * simplifies cloning devices.
397120529Sphk		 */
398120529Sphk		return (dev);
399120529Sphk	}
40065747Sphk	if (dev->si_flags & SI_NAMED) {
40165747Sphk		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
40265747Sphk		    dev->si_name);
40385603Sphk		panic("don't do that");
40465747Sphk	}
40549535Sphk	va_start(ap, fmt);
406110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
407110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
408110318Sphk		printf("WARNING: Device name truncated! (%s)",
409110318Sphk		    dev->__si_namebuf);
410110318Sphk	}
41149535Sphk	va_end(ap);
41249535Sphk	dev->si_devsw = devsw;
41364880Sphk	dev->si_uid = uid;
41464880Sphk	dev->si_gid = gid;
41564880Sphk	dev->si_mode = perms;
41665747Sphk	dev->si_flags |= SI_NAMED;
41750092Sjulian
418111730Sphk	devfs_create(dev);
41949535Sphk	return (dev);
42049535Sphk}
42149535Sphk
42285076Sjlemonint
42385076Sjlemondev_named(dev_t pdev, const char *name)
42485076Sjlemon{
42585076Sjlemon	dev_t cdev;
42685076Sjlemon
42785076Sjlemon	if (strcmp(devtoname(pdev), name) == 0)
42885076Sjlemon		return (1);
42985076Sjlemon	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
43085076Sjlemon		if (strcmp(devtoname(cdev), name) == 0)
43185076Sjlemon			return (1);
43285076Sjlemon	return (0);
43385076Sjlemon}
43485076Sjlemon
43577215Sphkvoid
43677215Sphkdev_depends(dev_t pdev, dev_t cdev)
43777215Sphk{
43877215Sphk
43977215Sphk	cdev->si_parent = pdev;
44077215Sphk	cdev->si_flags |= SI_CHILD;
44177215Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
44277215Sphk}
44377215Sphk
44464880Sphkdev_t
44581068Simpmake_dev_alias(dev_t pdev, const char *fmt, ...)
44664880Sphk{
44764880Sphk	dev_t	dev;
44864880Sphk	va_list ap;
44964880Sphk	int i;
45064880Sphk
45164880Sphk	dev = allocdev();
45264880Sphk	dev->si_flags |= SI_ALIAS;
45365747Sphk	dev->si_flags |= SI_NAMED;
45477215Sphk	dev_depends(pdev, dev);
45564880Sphk	va_start(ap, fmt);
456110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
457110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
458110318Sphk		printf("WARNING: Device name truncated! (%s)",
459110318Sphk		    dev->__si_namebuf);
460110318Sphk	}
46164880Sphk	va_end(ap);
46264880Sphk
463111730Sphk	devfs_create(dev);
46464880Sphk	return (dev);
46564880Sphk}
46664880Sphk
46750549Sphkvoid
46853000Sphkdestroy_dev(dev_t dev)
46950549Sphk{
47065747Sphk
47165747Sphk	if (!(dev->si_flags & SI_NAMED)) {
47265747Sphk		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
47365747Sphk		    major(dev), minor(dev));
47485603Sphk		panic("don't do that");
47565747Sphk	}
47665747Sphk
477111730Sphk	devfs_destroy(dev);
478126077Sphk	dev->si_flags &= ~SI_NAMED;
479126077Sphk
48077215Sphk	if (dev->si_flags & SI_CHILD) {
48177215Sphk		LIST_REMOVE(dev, si_siblings);
48277215Sphk		dev->si_flags &= ~SI_CHILD;
48377215Sphk	}
48477215Sphk	while (!LIST_EMPTY(&dev->si_children))
48577215Sphk		destroy_dev(LIST_FIRST(&dev->si_children));
486126077Sphk	if (dev->si_flags & SI_CLONELIST) {
487126077Sphk		LIST_REMOVE(dev, si_clone);
488126077Sphk		dev->si_flags &= ~SI_CLONELIST;
489126077Sphk	}
49050549Sphk	dev->si_drv1 = 0;
49150549Sphk	dev->si_drv2 = 0;
49250549Sphk	dev->si_devsw = 0;
49395446Sbde	bzero(&dev->__si_u, sizeof(dev->__si_u));
49465747Sphk	dev->si_flags &= ~SI_ALIAS;
49550549Sphk	freedev(dev);
49650549Sphk}
49750549Sphk
49851225Sbdeconst char *
49949982Sbillfdevtoname(dev_t dev)
50049982Sbillf{
50150549Sphk	char *p;
50251225Sbde	int mynor;
50349982Sbillf
50450549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
50550549Sphk		p = dev->si_name;
50650549Sphk		if (devsw(dev))
50750549Sphk			sprintf(p, "#%s/", devsw(dev)->d_name);
50850549Sphk		else
50950549Sphk			sprintf(p, "#%d/", major(dev));
51050549Sphk		p += strlen(p);
51151225Sbde		mynor = minor(dev);
51251225Sbde		if (mynor < 0 || mynor > 255)
51351225Sbde			sprintf(p, "%#x", (u_int)mynor);
51451225Sbde		else
51551225Sbde			sprintf(p, "%d", mynor);
51650549Sphk	}
51749982Sbillf	return (dev->si_name);
51849982Sbillf}
51965374Sphk
52065374Sphkint
52191998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
52265374Sphk{
52365374Sphk	int u, i;
52465374Sphk
52575519Sbrian	i = strlen(stem);
52675519Sbrian	if (bcmp(stem, name, i) != 0)
52765374Sphk		return (0);
52865374Sphk	if (!isdigit(name[i]))
52965374Sphk		return (0);
53065374Sphk	u = 0;
53186461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
53286461Sphk		return (0);
53365374Sphk	while (isdigit(name[i])) {
53465374Sphk		u *= 10;
53565374Sphk		u += name[i++] - '0';
53665374Sphk	}
537104523Sgreen	if (u > 0xffffff)
538104523Sgreen		return (0);
53965374Sphk	*unit = u;
54065374Sphk	if (namep)
54165374Sphk		*namep = &name[i];
54265374Sphk	if (name[i])
54365374Sphk		return (2);
54465374Sphk	return (1);
54565374Sphk}
54665632Sphk
54765632Sphk/*
548126077Sphk * Helper functions for cloning device drivers.
549126077Sphk *
550126077Sphk * The objective here is to make it unnecessary for the device drivers to
551126077Sphk * use rman or similar to manage their unit number space.  Due to the way
552126077Sphk * we do "on-demand" devices, using rman or other "private" methods
553126077Sphk * will be very tricky to lock down properly once we lock down this file.
554126077Sphk *
555126077Sphk * Instead we give the drivers these routines which puts the dev_t's that
556126077Sphk * are to be managed on their own list, and gives the driver the ability
557126077Sphk * to ask for the first free unit number or a given specified unit number.
558126077Sphk *
559126077Sphk * In addition these routines support paired devices (pty, nmdm and similar)
560126077Sphk * by respecting a number of "flag" bits in the minor number.
561126077Sphk *
562126077Sphk */
563126077Sphk
564126077Sphkstruct clonedevs {
565126077Sphk	LIST_HEAD(,cdev)	head;
566126077Sphk};
567126077Sphk
568126077Sphkint
569126077Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, dev_t *dp, u_int extra)
570126077Sphk{
571126077Sphk	struct clonedevs *cd;
572126077Sphk	dev_t dev, dl, de;
573126077Sphk	int unit, low, u;
574126077Sphk
575126077Sphk	KASSERT(!(extra & CLONE_UNITMASK),
576126077Sphk	     ("Illegal extra bits (0x%x) in clone_create", extra));
577126077Sphk	KASSERT(*up <= CLONE_UNITMASK,
578126077Sphk	     ("Too high unit (0x%x) in clone_create", *up));
579126077Sphk
580126077Sphk	if (csw->d_maj == MAJOR_AUTO)
581126077Sphk		find_major(csw);
582126077Sphk	/* if clonedevs have not been initialized, we do it here */
583126077Sphk	cd = *cdp;
584126077Sphk	if (cd == NULL) {
585126077Sphk		cd = malloc(sizeof *cd, M_DEVBUF, M_WAITOK | M_ZERO);
586126077Sphk		LIST_INIT(&cd->head);
587126077Sphk		*cdp = cd;
588126077Sphk	}
589126077Sphk
590126077Sphk	/*
591126077Sphk	 * Search the list for a lot of things in one go:
592126077Sphk	 *   A preexisting match is returned immediately.
593126077Sphk	 *   The lowest free unit number if we are passed -1, and the place
594126077Sphk	 *	 in the list where we should insert that new element.
595126077Sphk	 *   The place to insert a specified unit number, if applicable
596126077Sphk	 *       the end of the list.
597126077Sphk	 */
598126077Sphk	unit = *up;
599126077Sphk	low = 0;
600126077Sphk	de = dl = NULL;
601126077Sphk	LIST_FOREACH(dev, &cd->head, si_clone) {
602126077Sphk		u = dev2unit(dev);
603126077Sphk		if (u == (unit | extra)) {
604126077Sphk			*dp = dev;
605126077Sphk			return (0);
606126077Sphk		}
607126077Sphk		if (unit == -1 && u == low) {
608126077Sphk			low++;
609126077Sphk			de = dev;
610126077Sphk			continue;
611126077Sphk		}
612126077Sphk		if (u > unit) {
613126077Sphk			dl = dev;
614126077Sphk			break;
615126077Sphk		}
616126077Sphk		de = dev;
617126077Sphk	}
618126077Sphk	if (unit == -1)
619126077Sphk		unit = low;
620126077Sphk	dev = makedev(csw->d_maj, unit2minor(unit | extra));
621126077Sphk	KASSERT(!(dev->si_flags & SI_CLONELIST),
622126077Sphk	    ("Dev %p should not be on clonelist", dev));
623126077Sphk	if (dl != NULL)
624126077Sphk		LIST_INSERT_BEFORE(dl, dev, si_clone);
625126077Sphk	else if (de != NULL)
626126077Sphk		LIST_INSERT_AFTER(de, dev, si_clone);
627126077Sphk	else
628126077Sphk		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
629126077Sphk	dev->si_flags |= SI_CLONELIST;
630126077Sphk	*up = unit;
631126077Sphk	return (1);
632126077Sphk}
633126077Sphk
634126077Sphk/*
635126077Sphk * Kill everything still on the list.  The driver should already have
636126077Sphk * disposed of any softc hung of the dev_t's at this time.
637126077Sphk */
638126077Sphkvoid
639126077Sphkclone_cleanup(struct clonedevs **cdp)
640126077Sphk{
641126077Sphk	dev_t dev, tdev;
642126077Sphk	struct clonedevs *cd;
643126077Sphk
644126077Sphk	cd = *cdp;
645126077Sphk	if (cd == NULL)
646126077Sphk		return;
647126077Sphk	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
648126077Sphk		KASSERT(dev->si_flags & SI_NAMED,
649126077Sphk		    ("Driver has goofed in cloning underways udev %x", dev->si_udev));
650126077Sphk		destroy_dev(dev);
651126077Sphk	}
652126077Sphk	free(cd, M_DEVBUF);
653126077Sphk	*cdp = NULL;
654126077Sphk}
655126077Sphk
656126077Sphk/*
65765632Sphk * Helper sysctl for devname(3).  We're given a {u}dev_t and return
65865632Sphk * the name, if any, registered by the device driver.
65965632Sphk */
66065632Sphkstatic int
66165632Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
66265632Sphk{
66365632Sphk	int error;
66465632Sphk	udev_t ud;
66565632Sphk	dev_t dev;
66665632Sphk
66765632Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
66865632Sphk	if (error)
66965632Sphk		return (error);
67071342Sphk	if (ud == NOUDEV)
67171342Sphk		return(EINVAL);
67265632Sphk	dev = makedev(umajor(ud), uminor(ud));
67365632Sphk	if (dev->si_name[0] == '\0')
67465632Sphk		error = ENOENT;
67565632Sphk	else
67665632Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
67765632Sphk	freedev(dev);
67865632Sphk	return (error);
67965632Sphk}
68065632Sphk
68167905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
68265632Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
683