kern_conf.c revision 154029
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 154029 2006-01-04 17:40:54Z bz $");
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>
3636735Sdfr#include <sys/module.h>
3748936Sphk#include <sys/malloc.h>
3811126Sjulian#include <sys/conf.h>
3912954Sjulian#include <sys/vnode.h>
4048936Sphk#include <sys/queue.h>
41120514Sphk#include <sys/poll.h>
4265374Sphk#include <sys/ctype.h>
43126078Sphk#include <sys/tty.h>
44147982Srwatson#include <sys/ucred.h>
4549535Sphk#include <machine/stdarg.h>
4611126Sjulian
47149144Sphk#include <fs/devfs/devfs_int.h>
48149144Sphk
49131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
5048936Sphk
51150342Sphkstruct mtx devmtx;
52142242Sphkstatic void destroy_devl(struct cdev *dev);
53147982Srwatsonstatic struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr,
54147982Srwatson	    struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
55147982Srwatson	    va_list ap);
56126082Sphk
57135600Sphkvoid
58135600Sphkdev_lock(void)
59126082Sphk{
60151450Sjhb
61126082Sphk	mtx_lock(&devmtx);
62126082Sphk}
63126082Sphk
64135600Sphkvoid
65135600Sphkdev_unlock(void)
66126082Sphk{
67135704Sphk
68126082Sphk	mtx_unlock(&devmtx);
69126082Sphk}
70126082Sphk
71126082Sphkvoid
72144385Sphkdev_ref(struct cdev *dev)
73144385Sphk{
74144385Sphk
75144385Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
76144385Sphk	mtx_lock(&devmtx);
77144385Sphk	dev->si_refcount++;
78144385Sphk	mtx_unlock(&devmtx);
79144385Sphk}
80144385Sphk
81144385Sphkvoid
82144384Sphkdev_refl(struct cdev *dev)
83126082Sphk{
84135704Sphk
85142232Sphk	mtx_assert(&devmtx, MA_OWNED);
86126082Sphk	dev->si_refcount++;
87126082Sphk}
88126082Sphk
89126082Sphkvoid
90142242Sphkdev_rel(struct cdev *dev)
91126082Sphk{
92142242Sphk	int flag = 0;
93135600Sphk
94136014Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
95136014Sphk	dev_lock();
96126082Sphk	dev->si_refcount--;
97126082Sphk	KASSERT(dev->si_refcount >= 0,
98126082Sphk	    ("dev_rel(%s) gave negative count", devtoname(dev)));
99150342Sphk#if 0
100142242Sphk	if (dev->si_usecount == 0 &&
101142242Sphk	    (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
102150342Sphk		;
103150342Sphk	else
104150342Sphk#endif
105154029Sbz	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
106126082Sphk		LIST_REMOVE(dev, si_list);
107136014Sphk		flag = 1;
108136014Sphk	}
109136014Sphk	dev_unlock();
110136014Sphk	if (flag)
111150342Sphk		devfs_free(dev);
112126082Sphk}
113136014Sphk
114135704Sphkstruct cdevsw *
115135704Sphkdev_refthread(struct cdev *dev)
116135704Sphk{
117135704Sphk	struct cdevsw *csw;
118126082Sphk
119135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
120135704Sphk	dev_lock();
121135704Sphk	csw = dev->si_devsw;
122135704Sphk	if (csw != NULL)
123135704Sphk		dev->si_threadcount++;
124135704Sphk	dev_unlock();
125135704Sphk	return (csw);
126135704Sphk}
127135704Sphk
128135704Sphkvoid
129135704Sphkdev_relthread(struct cdev *dev)
130135704Sphk{
131135704Sphk
132135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
133135704Sphk	dev_lock();
134135704Sphk	dev->si_threadcount--;
135135704Sphk	dev_unlock();
136135704Sphk}
137135704Sphk
138120514Sphkint
139120514Sphknullop(void)
140120514Sphk{
14185603Sphk
142120514Sphk	return (0);
143120514Sphk}
144120514Sphk
145120514Sphkint
146120514Sphkeopnotsupp(void)
147120514Sphk{
148120514Sphk
149120514Sphk	return (EOPNOTSUPP);
150120514Sphk}
151120514Sphk
152111179Sphkstatic int
153111179Sphkenxio(void)
154111179Sphk{
155111179Sphk	return (ENXIO);
156111179Sphk}
157111179Sphk
158120514Sphkstatic int
159120514Sphkenodev(void)
160120514Sphk{
161120514Sphk	return (ENODEV);
162120514Sphk}
163120514Sphk
164120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
165120514Sphk
166111179Sphk#define dead_open	(d_open_t *)enxio
167111179Sphk#define dead_close	(d_close_t *)enxio
168111179Sphk#define dead_read	(d_read_t *)enxio
169111179Sphk#define dead_write	(d_write_t *)enxio
170111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
171120514Sphk#define dead_poll	(d_poll_t *)enodev
172120514Sphk#define dead_mmap	(d_mmap_t *)enodev
173111179Sphk
174111179Sphkstatic void
175111179Sphkdead_strategy(struct bio *bp)
176111179Sphk{
177111179Sphk
178111179Sphk	biofinish(bp, NULL, ENXIO);
179111179Sphk}
180111179Sphk
181111220Sphk#define dead_dump	(dumper_t *)enxio
182111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
183111179Sphk
184111179Sphkstatic struct cdevsw dead_cdevsw = {
185126080Sphk	.d_version =	D_VERSION,
186126080Sphk	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
187111815Sphk	.d_open =	dead_open,
188111815Sphk	.d_close =	dead_close,
189111815Sphk	.d_read =	dead_read,
190111815Sphk	.d_write =	dead_write,
191111815Sphk	.d_ioctl =	dead_ioctl,
192111815Sphk	.d_poll =	dead_poll,
193111815Sphk	.d_mmap =	dead_mmap,
194111815Sphk	.d_strategy =	dead_strategy,
195111815Sphk	.d_name =	"dead",
196111815Sphk	.d_dump =	dead_dump,
197111815Sphk	.d_kqfilter =	dead_kqfilter
198111179Sphk};
199111179Sphk
200120514Sphk/* Default methods if driver does not specify method */
201111179Sphk
202120514Sphk#define null_open	(d_open_t *)nullop
203120514Sphk#define null_close	(d_close_t *)nullop
204120514Sphk#define no_read		(d_read_t *)enodev
205120514Sphk#define no_write	(d_write_t *)enodev
206120514Sphk#define no_ioctl	(d_ioctl_t *)enodev
207120514Sphk#define no_mmap		(d_mmap_t *)enodev
208133741Sjmg#define no_kqfilter	(d_kqfilter_t *)enodev
209120514Sphk
210120514Sphkstatic void
211120514Sphkno_strategy(struct bio *bp)
212120514Sphk{
213120514Sphk
214120514Sphk	biofinish(bp, NULL, ENODEV);
215120514Sphk}
216120514Sphk
217120514Sphkstatic int
218130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
219120514Sphk{
220120514Sphk	/*
221120514Sphk	 * Return true for read/write.  If the user asked for something
222120514Sphk	 * special, return POLLNVAL, so that clients have a way of
223120514Sphk	 * determining reliably whether or not the extended
224120514Sphk	 * functionality is present without hard-coding knowledge
225120514Sphk	 * of specific filesystem implementations.
226120514Sphk	 * Stay in sync with vop_nopoll().
227120514Sphk	 */
228120514Sphk	if (events & ~POLLSTANDARD)
229120514Sphk		return (POLLNVAL);
230120514Sphk
231120514Sphk	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
232120514Sphk}
233120514Sphk
234120514Sphk#define no_dump		(dumper_t *)enodev
235120514Sphk
236149177Sphkstatic int
237149177Sphkgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
238149177Sphk{
239149177Sphk	int retval;
240149177Sphk
241149177Sphk	mtx_lock(&Giant);
242149177Sphk	retval = dev->si_devsw->d_gianttrick->
243149177Sphk	    d_open(dev, oflags, devtype, td);
244149177Sphk	mtx_unlock(&Giant);
245149177Sphk	return (retval);
246149177Sphk}
247149177Sphk
248149177Sphkstatic int
249149177Sphkgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
250149177Sphk{
251149177Sphk	int retval;
252149177Sphk
253149177Sphk	mtx_lock(&Giant);
254149177Sphk	retval = dev->si_devsw->d_gianttrick->
255149177Sphk	    d_fdopen(dev, oflags, td, fdidx);
256149177Sphk	mtx_unlock(&Giant);
257149177Sphk	return (retval);
258149177Sphk}
259149177Sphk
260149177Sphkstatic int
261149177Sphkgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
262149177Sphk{
263149177Sphk	int retval;
264149177Sphk
265149177Sphk	mtx_lock(&Giant);
266149177Sphk	retval = dev->si_devsw->d_gianttrick->
267149177Sphk	    d_close(dev, fflag, devtype, td);
268149177Sphk	mtx_unlock(&Giant);
269149177Sphk	return (retval);
270149177Sphk}
271149177Sphk
272149177Sphkstatic void
273149177Sphkgiant_strategy(struct bio *bp)
274149177Sphk{
275149177Sphk
276149177Sphk	mtx_lock(&Giant);
277149177Sphk	bp->bio_dev->si_devsw->d_gianttrick->
278149177Sphk	    d_strategy(bp);
279149177Sphk	mtx_unlock(&Giant);
280149177Sphk}
281149177Sphk
282149177Sphkstatic int
283149177Sphkgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
284149177Sphk{
285149177Sphk	int retval;
286149177Sphk
287149177Sphk	mtx_lock(&Giant);
288149177Sphk	retval = dev->si_devsw->d_gianttrick->
289149177Sphk	    d_ioctl(dev, cmd, data, fflag, td);
290149177Sphk	mtx_unlock(&Giant);
291149177Sphk	return (retval);
292149177Sphk}
293149177Sphk
294149177Sphkstatic int
295149177Sphkgiant_read(struct cdev *dev, struct uio *uio, int ioflag)
296149177Sphk{
297149177Sphk	int retval;
298149177Sphk
299149177Sphk	mtx_lock(&Giant);
300149177Sphk	retval = dev->si_devsw->d_gianttrick->
301149177Sphk	    d_read(dev, uio, ioflag);
302149177Sphk	mtx_unlock(&Giant);
303149177Sphk	return (retval);
304149177Sphk}
305149177Sphk
306149177Sphkstatic int
307149177Sphkgiant_write(struct cdev *dev, struct uio *uio, int ioflag)
308149177Sphk{
309149177Sphk	int retval;
310149177Sphk
311149177Sphk	mtx_lock(&Giant);
312149177Sphk	retval = dev->si_devsw->d_gianttrick->
313149177Sphk		d_write(dev, uio, ioflag);
314149177Sphk	mtx_unlock(&Giant);
315149177Sphk	return (retval);
316149177Sphk}
317149177Sphk
318149177Sphkstatic int
319149177Sphkgiant_poll(struct cdev *dev, int events, struct thread *td)
320149177Sphk{
321149177Sphk	int retval;
322149177Sphk
323149177Sphk	mtx_lock(&Giant);
324149177Sphk	retval = dev->si_devsw->d_gianttrick->
325149177Sphk	    d_poll(dev, events, td);
326149177Sphk	mtx_unlock(&Giant);
327149177Sphk	return (retval);
328149177Sphk}
329149177Sphk
330149177Sphkstatic int
331149177Sphkgiant_kqfilter(struct cdev *dev, struct knote *kn)
332149177Sphk{
333149177Sphk	int retval;
334149177Sphk
335149177Sphk	mtx_lock(&Giant);
336149177Sphk	retval = dev->si_devsw->d_gianttrick->
337149177Sphk	    d_kqfilter(dev, kn);
338149177Sphk	mtx_unlock(&Giant);
339149177Sphk	return (retval);
340149177Sphk}
341149177Sphk
342149177Sphkstatic int
343149177Sphkgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
344149177Sphk{
345149177Sphk	int retval;
346149177Sphk
347149177Sphk	mtx_lock(&Giant);
348149177Sphk	retval = dev->si_devsw->d_gianttrick->
349149177Sphk	    d_mmap(dev, offset, paddr, nprot);
350149177Sphk	mtx_unlock(&Giant);
351149177Sphk	return (retval);
352149177Sphk}
353149177Sphk
354149177Sphk
35547640Sphk/*
356130936Sle * struct cdev * and u_dev_t primitives
35747028Sphk */
35847028Sphk
35947028Sphkint
360130585Sphkminor(struct cdev *x)
36147028Sphk{
362130640Sphk	if (x == NULL)
363130640Sphk		return NODEV;
364143631Sphk	return(x->si_drv0 & MAXMINOR);
36547028Sphk}
36647028Sphk
36749826Sphkint
368130585Sphkdev2unit(struct cdev *x)
36949826Sphk{
37049826Sphk
371130640Sphk	if (x == NULL)
372130640Sphk		return NODEV;
373140964Sphk	return (minor2unit(minor(x)));
37449826Sphk}
37549826Sphk
376143282Sphku_int
377143282Sphkminor2unit(u_int _minor)
378140963Sphk{
379140963Sphk
380140969Sphk	KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
381143282Sphk	return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
382140963Sphk}
383140963Sphk
384140963Sphkint
38566067Sphkunit2minor(int unit)
38666067Sphk{
38766067Sphk
38874522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
38966067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
39066067Sphk}
39166067Sphk
392130585Sphkstatic struct cdev *
393144281Sphknewdev(struct cdevsw *csw, int y, struct cdev *si)
39447028Sphk{
395140733Sphk	struct cdev *si2;
396130640Sphk	dev_t	udev;
39748936Sphk
398140733Sphk	mtx_assert(&devmtx, MA_OWNED);
399144292Sphk	udev = y;
400144281Sphk	LIST_FOREACH(si2, &csw->d_devs, si_list) {
401143631Sphk		if (si2->si_drv0 == udev) {
402150342Sphk			devfs_free(si);
403140733Sphk			return (si2);
404140733Sphk		}
40548936Sphk	}
406143631Sphk	si->si_drv0 = udev;
407150342Sphk	si->si_devsw = csw;
408144281Sphk	LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
409125850Sbde	return (si);
41047028Sphk}
41147028Sphk
41247028Sphkint
413130640Sphkuminor(dev_t dev)
41447028Sphk{
415140969Sphk	return (dev & MAXMINOR);
41647028Sphk}
41747028Sphk
41847028Sphkint
419130640Sphkumajor(dev_t dev)
42047028Sphk{
421140969Sphk	return ((dev & ~MAXMINOR) >> 8);
42247028Sphk}
42347028Sphk
424125846Sphkstatic void
425144292Sphkfini_cdevsw(struct cdevsw *devsw)
42649535Sphk{
427149324Sphk	struct cdevsw *gt;
42849535Sphk
429149324Sphk	if (devsw->d_gianttrick != NULL) {
430149324Sphk		gt = devsw->d_gianttrick;
431149324Sphk		memcpy(devsw, gt, sizeof *devsw);
432149324Sphk		free(gt, M_DEVT);
433149324Sphk		devsw->d_gianttrick = NULL;
434149324Sphk	}
435126156Sphk	devsw->d_flags &= ~D_INIT;
436126082Sphk}
437126082Sphk
438126082Sphkstatic void
439126077Sphkprep_cdevsw(struct cdevsw *devsw)
440126077Sphk{
441149177Sphk	struct cdevsw *dsw2;
442126077Sphk
443149177Sphk	if (devsw->d_flags & D_NEEDGIANT)
444149177Sphk		dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
445149177Sphk	else
446149177Sphk		dsw2 = NULL;
447135600Sphk	dev_lock();
448126082Sphk
449143746Sphk	if (devsw->d_version != D_VERSION_01) {
450126082Sphk		printf(
451126082Sphk		    "WARNING: Device driver \"%s\" has wrong version %s\n",
452126082Sphk		    devsw->d_name, "and is disabled.  Recompile KLD module.");
453126082Sphk		devsw->d_open = dead_open;
454126082Sphk		devsw->d_close = dead_close;
455126082Sphk		devsw->d_read = dead_read;
456126082Sphk		devsw->d_write = dead_write;
457126082Sphk		devsw->d_ioctl = dead_ioctl;
458126082Sphk		devsw->d_poll = dead_poll;
459126082Sphk		devsw->d_mmap = dead_mmap;
460126082Sphk		devsw->d_strategy = dead_strategy;
461126082Sphk		devsw->d_dump = dead_dump;
462126082Sphk		devsw->d_kqfilter = dead_kqfilter;
463126082Sphk	}
464126082Sphk
465126078Sphk	if (devsw->d_flags & D_TTY) {
466129943Sphk		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
467126078Sphk		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
468126078Sphk		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
469126078Sphk		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
470126078Sphk		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
471126078Sphk	}
472126078Sphk
473149177Sphk	if (devsw->d_flags & D_NEEDGIANT) {
474149177Sphk		if (devsw->d_gianttrick == NULL) {
475149177Sphk			memcpy(dsw2, devsw, sizeof *dsw2);
476149177Sphk			devsw->d_gianttrick = dsw2;
477149177Sphk		} else
478149177Sphk			free(dsw2, M_DEVT);
479149177Sphk	}
480149177Sphk
481149177Sphk#define FIXUP(member, noop, giant) 				\
482149177Sphk	do {							\
483149177Sphk		if (devsw->member == NULL) {			\
484149177Sphk			devsw->member = noop;			\
485149177Sphk		} else if (devsw->d_flags & D_NEEDGIANT)	\
486149177Sphk			devsw->member = giant;			\
487149177Sphk		}						\
488149177Sphk	while (0)
489149177Sphk
490149177Sphk	FIXUP(d_open,		null_open,	giant_open);
491149177Sphk	FIXUP(d_fdopen,		NULL,		giant_fdopen);
492149177Sphk	FIXUP(d_close,		null_close,	giant_close);
493149177Sphk	FIXUP(d_read,		no_read,	giant_read);
494149177Sphk	FIXUP(d_write,		no_write,	giant_write);
495149177Sphk	FIXUP(d_ioctl,		no_ioctl,	giant_ioctl);
496149177Sphk	FIXUP(d_poll,		no_poll,	giant_poll);
497149177Sphk	FIXUP(d_mmap,		no_mmap,	giant_mmap);
498149177Sphk	FIXUP(d_strategy,	no_strategy,	giant_strategy);
499149177Sphk	FIXUP(d_kqfilter,	no_kqfilter,	giant_kqfilter);
500149177Sphk
501120514Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
502126082Sphk
503126082Sphk	LIST_INIT(&devsw->d_devs);
504126082Sphk
505126082Sphk	devsw->d_flags |= D_INIT;
506126082Sphk
507135600Sphk	dev_unlock();
508125846Sphk}
509111622Sphk
510147982Srwatsonstatic struct cdev *
511147982Srwatsonmake_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
512147982Srwatson    gid_t gid, int mode, const char *fmt, va_list ap)
513125846Sphk{
514130585Sphk	struct cdev *dev;
515125846Sphk	int i;
516125846Sphk
517140969Sphk	KASSERT((minornr & ~MAXMINOR) == 0,
518126082Sphk	    ("Invalid minor (0x%x) in make_dev", minornr));
519126082Sphk
520144385Sphk	if (!(devsw->d_flags & D_INIT))
521126082Sphk		prep_cdevsw(devsw);
522150342Sphk	dev = devfs_alloc();
523140733Sphk	dev_lock();
524144281Sphk	dev = newdev(devsw, minornr, dev);
525120529Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
526150342Sphk	    dev->si_flags & SI_NAMED) {
527120529Sphk		/*
528120529Sphk		 * This is allowed as it removes races and generally
529120529Sphk		 * simplifies cloning devices.
530126082Sphk		 * XXX: still ??
531120529Sphk		 */
532140733Sphk		dev_unlock();
533120529Sphk		return (dev);
534120529Sphk	}
535126082Sphk	KASSERT(!(dev->si_flags & SI_NAMED),
536144281Sphk	    ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
537144281Sphk	    devsw->d_name, minor(dev), devtoname(dev)));
538126082Sphk
539110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
540110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
541134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
542110318Sphk		    dev->__si_namebuf);
543110318Sphk	}
544136947Sphk
54565747Sphk	dev->si_flags |= SI_NAMED;
546147982Srwatson	if (cr != NULL)
547147982Srwatson		dev->si_cred = crhold(cr);
548147982Srwatson	else
549147982Srwatson		dev->si_cred = NULL;
550144385Sphk	dev->si_uid = uid;
551144385Sphk	dev->si_gid = gid;
552144385Sphk	dev->si_mode = mode;
55350092Sjulian
554111730Sphk	devfs_create(dev);
555135600Sphk	dev_unlock();
55649535Sphk	return (dev);
55749535Sphk}
55849535Sphk
559147982Srwatsonstruct cdev *
560147982Srwatsonmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode,
561147982Srwatson    const char *fmt, ...)
562147982Srwatson{
563147982Srwatson	struct cdev *dev;
564147982Srwatson	va_list ap;
565147982Srwatson
566147982Srwatson	va_start(ap, fmt);
567147982Srwatson	dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap);
568147982Srwatson	va_end(ap);
569147982Srwatson	return (dev);
570147982Srwatson}
571147982Srwatson
572147982Srwatsonstruct cdev *
573147982Srwatsonmake_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
574147982Srwatson    gid_t gid, int mode, const char *fmt, ...)
575147982Srwatson{
576147982Srwatson	struct cdev *dev;
577147982Srwatson	va_list ap;
578147982Srwatson
579147982Srwatson	va_start(ap, fmt);
580147982Srwatson	dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap);
581147982Srwatson	va_end(ap);
582147982Srwatson
583147982Srwatson	return (dev);
584147982Srwatson}
585147982Srwatson
586150342Sphkstatic void
587150342Sphkdev_dependsl(struct cdev *pdev, struct cdev *cdev)
588150342Sphk{
589150342Sphk
590150342Sphk	cdev->si_parent = pdev;
591150342Sphk	cdev->si_flags |= SI_CHILD;
592150342Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
593150342Sphk}
594150342Sphk
595150342Sphk
59677215Sphkvoid
597130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev)
59877215Sphk{
59977215Sphk
600135600Sphk	dev_lock();
601150342Sphk	dev_dependsl(pdev, cdev);
602135600Sphk	dev_unlock();
60377215Sphk}
60477215Sphk
605130585Sphkstruct cdev *
606130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
60764880Sphk{
608130585Sphk	struct cdev *dev;
60964880Sphk	va_list ap;
61064880Sphk	int i;
61164880Sphk
612150342Sphk	dev = devfs_alloc();
613135600Sphk	dev_lock();
61464880Sphk	dev->si_flags |= SI_ALIAS;
61565747Sphk	dev->si_flags |= SI_NAMED;
61664880Sphk	va_start(ap, fmt);
617110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
618110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
619134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
620110318Sphk		    dev->__si_namebuf);
621110318Sphk	}
62264880Sphk	va_end(ap);
62364880Sphk
624111730Sphk	devfs_create(dev);
625135600Sphk	dev_unlock();
626126082Sphk	dev_depends(pdev, dev);
62764880Sphk	return (dev);
62864880Sphk}
62964880Sphk
630126082Sphkstatic void
631142242Sphkdestroy_devl(struct cdev *dev)
63250549Sphk{
633135843Sphk	struct cdevsw *csw;
634135843Sphk
635142242Sphk	mtx_assert(&devmtx, MA_OWNED);
636135843Sphk	KASSERT(dev->si_flags & SI_NAMED,
637144281Sphk	    ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
638154029Sbz
639111730Sphk	devfs_destroy(dev);
640126082Sphk
641126082Sphk	/* Remove name marking */
642126077Sphk	dev->si_flags &= ~SI_NAMED;
643126077Sphk
644126082Sphk	/* If we are a child, remove us from the parents list */
64577215Sphk	if (dev->si_flags & SI_CHILD) {
64677215Sphk		LIST_REMOVE(dev, si_siblings);
64777215Sphk		dev->si_flags &= ~SI_CHILD;
64877215Sphk	}
649126082Sphk
650126082Sphk	/* Kill our children */
65177215Sphk	while (!LIST_EMPTY(&dev->si_children))
652142242Sphk		destroy_devl(LIST_FIRST(&dev->si_children));
653126082Sphk
654126082Sphk	/* Remove from clone list */
655126077Sphk	if (dev->si_flags & SI_CLONELIST) {
656126077Sphk		LIST_REMOVE(dev, si_clone);
657126077Sphk		dev->si_flags &= ~SI_CLONELIST;
658126077Sphk	}
659126082Sphk
660135843Sphk	csw = dev->si_devsw;
661135934Sgreen	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
662135934Sgreen	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
663135843Sphk		printf("Purging %lu threads from %s\n",
664135843Sphk		    dev->si_threadcount, devtoname(dev));
665135843Sphk		csw->d_purge(dev);
666135843Sphk		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
667135843Sphk	}
668135934Sgreen	if (csw != NULL && csw->d_purge != NULL)
669135844Sphk		printf("All threads purged from %s\n", devtoname(dev));
670135843Sphk
671135843Sphk	dev->si_drv1 = 0;
672135843Sphk	dev->si_drv2 = 0;
673135843Sphk	bzero(&dev->__si_u, sizeof(dev->__si_u));
674135843Sphk
675126082Sphk	if (!(dev->si_flags & SI_ALIAS)) {
676126082Sphk		/* Remove from cdevsw list */
677126082Sphk		LIST_REMOVE(dev, si_list);
678126082Sphk
679150342Sphk		/* If cdevsw has no more struct cdev *'s, clean it */
680135844Sphk		if (LIST_EMPTY(&csw->d_devs))
681135844Sphk			fini_cdevsw(csw);
682126082Sphk	}
68365747Sphk	dev->si_flags &= ~SI_ALIAS;
684135843Sphk
685126082Sphk	if (dev->si_refcount > 0) {
686126082Sphk		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
687126082Sphk	} else {
688150342Sphk		devfs_free(dev);
689126082Sphk	}
69050549Sphk}
69150549Sphk
692126082Sphkvoid
693130585Sphkdestroy_dev(struct cdev *dev)
694126082Sphk{
695126082Sphk
696135600Sphk	dev_lock();
697142242Sphk	destroy_devl(dev);
698135600Sphk	dev_unlock();
699126082Sphk}
700126082Sphk
70151225Sbdeconst char *
702130585Sphkdevtoname(struct cdev *dev)
70349982Sbillf{
70450549Sphk	char *p;
705135712Sphk	struct cdevsw *csw;
70651225Sbde	int mynor;
70749982Sbillf
70850549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
70950549Sphk		p = dev->si_name;
710135712Sphk		csw = dev_refthread(dev);
711135712Sphk		if (csw != NULL) {
712135712Sphk			sprintf(p, "(%s)", csw->d_name);
713135712Sphk			dev_relthread(dev);
714135712Sphk		}
715135712Sphk		p += strlen(p);
71651225Sbde		mynor = minor(dev);
71751225Sbde		if (mynor < 0 || mynor > 255)
718135712Sphk			sprintf(p, "/%#x", (u_int)mynor);
71951225Sbde		else
720135712Sphk			sprintf(p, "/%d", mynor);
72150549Sphk	}
72249982Sbillf	return (dev->si_name);
72349982Sbillf}
72465374Sphk
72565374Sphkint
72691998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
72765374Sphk{
72865374Sphk	int u, i;
72965374Sphk
73075519Sbrian	i = strlen(stem);
73175519Sbrian	if (bcmp(stem, name, i) != 0)
73265374Sphk		return (0);
73365374Sphk	if (!isdigit(name[i]))
73465374Sphk		return (0);
73565374Sphk	u = 0;
73686461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
73786461Sphk		return (0);
73865374Sphk	while (isdigit(name[i])) {
73965374Sphk		u *= 10;
74065374Sphk		u += name[i++] - '0';
74165374Sphk	}
742104523Sgreen	if (u > 0xffffff)
743104523Sgreen		return (0);
74465374Sphk	*unit = u;
74565374Sphk	if (namep)
74665374Sphk		*namep = &name[i];
74765374Sphk	if (name[i])
74865374Sphk		return (2);
74965374Sphk	return (1);
75065374Sphk}
75165632Sphk
75265632Sphk/*
753126077Sphk * Helper functions for cloning device drivers.
754126077Sphk *
755126077Sphk * The objective here is to make it unnecessary for the device drivers to
756126077Sphk * use rman or similar to manage their unit number space.  Due to the way
757126077Sphk * we do "on-demand" devices, using rman or other "private" methods
758126077Sphk * will be very tricky to lock down properly once we lock down this file.
759126077Sphk *
760130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s
761130936Sle * that are to be managed on their own list, and gives the driver the ability
762126077Sphk * to ask for the first free unit number or a given specified unit number.
763126077Sphk *
764126077Sphk * In addition these routines support paired devices (pty, nmdm and similar)
765126077Sphk * by respecting a number of "flag" bits in the minor number.
766126077Sphk *
767126077Sphk */
768126077Sphk
769126077Sphkstruct clonedevs {
770126077Sphk	LIST_HEAD(,cdev)	head;
771126077Sphk};
772126077Sphk
773126845Sphkvoid
774126845Sphkclone_setup(struct clonedevs **cdp)
775126845Sphk{
776126845Sphk
777126845Sphk	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
778126845Sphk	LIST_INIT(&(*cdp)->head);
779126845Sphk}
780126845Sphk
781126077Sphkint
782130585Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
783126077Sphk{
784126077Sphk	struct clonedevs *cd;
785140733Sphk	struct cdev *dev, *ndev, *dl, *de;
786126077Sphk	int unit, low, u;
787126077Sphk
788126845Sphk	KASSERT(*cdp != NULL,
789126845Sphk	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
790126077Sphk	KASSERT(!(extra & CLONE_UNITMASK),
791126845Sphk	    ("Illegal extra bits (0x%x) in clone_create", extra));
792126077Sphk	KASSERT(*up <= CLONE_UNITMASK,
793126845Sphk	    ("Too high unit (0x%x) in clone_create", *up));
794126077Sphk
795142726Sphk	if (!(csw->d_flags & D_INIT))
796142726Sphk		prep_cdevsw(csw);
797126077Sphk
798126077Sphk	/*
799126077Sphk	 * Search the list for a lot of things in one go:
800126077Sphk	 *   A preexisting match is returned immediately.
801126077Sphk	 *   The lowest free unit number if we are passed -1, and the place
802126077Sphk	 *	 in the list where we should insert that new element.
803126077Sphk	 *   The place to insert a specified unit number, if applicable
804126077Sphk	 *       the end of the list.
805126077Sphk	 */
806126077Sphk	unit = *up;
807150342Sphk	ndev = devfs_alloc();
808140733Sphk	dev_lock();
809126849Sphk	low = extra;
810126077Sphk	de = dl = NULL;
811126845Sphk	cd = *cdp;
812126077Sphk	LIST_FOREACH(dev, &cd->head, si_clone) {
813140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
814140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
815126077Sphk		u = dev2unit(dev);
816126077Sphk		if (u == (unit | extra)) {
817126077Sphk			*dp = dev;
818150342Sphk			devfs_free(ndev);
819140733Sphk			dev_unlock();
820126077Sphk			return (0);
821126077Sphk		}
822126077Sphk		if (unit == -1 && u == low) {
823126077Sphk			low++;
824126077Sphk			de = dev;
825126077Sphk			continue;
826150793Sphk		} else if (u < (unit | extra)) {
827150793Sphk			de = dev;
828150793Sphk			continue;
829150793Sphk		} else if (u > (unit | extra)) {
830126077Sphk			dl = dev;
831126077Sphk			break;
832126077Sphk		}
833126077Sphk	}
834126077Sphk	if (unit == -1)
835126849Sphk		unit = low & CLONE_UNITMASK;
836144281Sphk	dev = newdev(csw, unit2minor(unit | extra), ndev);
837140733Sphk	if (dev->si_flags & SI_CLONELIST) {
838140733Sphk		printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
839150793Sphk		printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
840140733Sphk		LIST_FOREACH(dev, &cd->head, si_clone) {
841140733Sphk			printf("\t%p %s\n", dev, dev->si_name);
842140733Sphk		}
843140733Sphk		panic("foo");
844140733Sphk	}
845126077Sphk	KASSERT(!(dev->si_flags & SI_CLONELIST),
846140733Sphk	    ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
847126077Sphk	if (dl != NULL)
848126077Sphk		LIST_INSERT_BEFORE(dl, dev, si_clone);
849126077Sphk	else if (de != NULL)
850126077Sphk		LIST_INSERT_AFTER(de, dev, si_clone);
851126077Sphk	else
852126077Sphk		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
853126077Sphk	dev->si_flags |= SI_CLONELIST;
854126077Sphk	*up = unit;
855140733Sphk	dev_unlock();
856126077Sphk	return (1);
857126077Sphk}
858126077Sphk
859126077Sphk/*
860126077Sphk * Kill everything still on the list.  The driver should already have
861130585Sphk * disposed of any softc hung of the struct cdev *'s at this time.
862126077Sphk */
863126077Sphkvoid
864126077Sphkclone_cleanup(struct clonedevs **cdp)
865126077Sphk{
866130585Sphk	struct cdev *dev, *tdev;
867126077Sphk	struct clonedevs *cd;
868126077Sphk
869126077Sphk	cd = *cdp;
870126077Sphk	if (cd == NULL)
871126077Sphk		return;
872140733Sphk	dev_lock();
873126077Sphk	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
874140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
875140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
876126077Sphk		KASSERT(dev->si_flags & SI_NAMED,
877143631Sphk		    ("Driver has goofed in cloning underways udev %x", dev->si_drv0));
878142242Sphk		destroy_devl(dev);
879126077Sphk	}
880140733Sphk	dev_unlock();
881126077Sphk	free(cd, M_DEVBUF);
882126077Sphk	*cdp = NULL;
883126077Sphk}
884