kern_conf.c revision 158684
1178825Sdfr/*-
2233294Sstas * Copyright (c) 1999-2002 Poul-Henning Kamp
3233294Sstas * All rights reserved.
4233294Sstas *
5178825Sdfr * Redistribution and use in source and binary forms, with or without
6233294Sstas * modification, are permitted provided that the following conditions
7233294Sstas * are met:
8233294Sstas * 1. Redistributions of source code must retain the above copyright
9178825Sdfr *    notice, this list of conditions and the following disclaimer.
10233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer in the
12178825Sdfr *    documentation and/or other materials provided with the distribution.
13233294Sstas *
14233294Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24233294Sstas * SUCH DAMAGE.
25233294Sstas */
26233294Sstas
27233294Sstas#include <sys/cdefs.h>
28233294Sstas__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 158684 2006-05-17 06:37:14Z phk $");
29233294Sstas
30233294Sstas#include <sys/param.h>
31233294Sstas#include <sys/kernel.h>
32178825Sdfr#include <sys/systm.h>
33178825Sdfr#include <sys/bio.h>
34233294Sstas#include <sys/lock.h>
35233294Sstas#include <sys/mutex.h>
36178825Sdfr#include <sys/module.h>
37178825Sdfr#include <sys/malloc.h>
38178825Sdfr#include <sys/conf.h>
39178825Sdfr#include <sys/vnode.h>
40178825Sdfr#include <sys/queue.h>
41178825Sdfr#include <sys/poll.h>
42178825Sdfr#include <sys/ctype.h>
43233294Sstas#include <sys/tty.h>
44233294Sstas#include <sys/ucred.h>
45233294Sstas#include <machine/stdarg.h>
46233294Sstas
47178825Sdfr#include <fs/devfs/devfs_int.h>
48233294Sstas
49233294Sstasstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
50233294Sstas
51233294Sstasstruct mtx devmtx;
52178825Sdfrstatic void destroy_devl(struct cdev *dev);
53178825Sdfrstatic struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr,
54178825Sdfr	    struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
55178825Sdfr	    va_list ap);
56178825Sdfr
57178825Sdfrvoid
58178825Sdfrdev_lock(void)
59178825Sdfr{
60178825Sdfr
61178825Sdfr	mtx_lock(&devmtx);
62178825Sdfr}
63178825Sdfr
64178825Sdfrvoid
65178825Sdfrdev_unlock(void)
66178825Sdfr{
67178825Sdfr
68178825Sdfr	mtx_unlock(&devmtx);
69178825Sdfr}
70178825Sdfr
71178825Sdfrvoid
72178825Sdfrdev_ref(struct cdev *dev)
73178825Sdfr{
74178825Sdfr
75178825Sdfr	mtx_assert(&devmtx, MA_NOTOWNED);
76178825Sdfr	mtx_lock(&devmtx);
77178825Sdfr	dev->si_refcount++;
78178825Sdfr	mtx_unlock(&devmtx);
79178825Sdfr}
80178825Sdfr
81178825Sdfrvoid
82178825Sdfrdev_refl(struct cdev *dev)
83178825Sdfr{
84178825Sdfr
85178825Sdfr	mtx_assert(&devmtx, MA_OWNED);
86178825Sdfr	dev->si_refcount++;
87178825Sdfr}
88178825Sdfr
89178825Sdfrvoid
90178825Sdfrdev_rel(struct cdev *dev)
91178825Sdfr{
92178825Sdfr	int flag = 0;
93178825Sdfr
94178825Sdfr	mtx_assert(&devmtx, MA_NOTOWNED);
95178825Sdfr	dev_lock();
96178825Sdfr	dev->si_refcount--;
97178825Sdfr	KASSERT(dev->si_refcount >= 0,
98178825Sdfr	    ("dev_rel(%s) gave negative count", devtoname(dev)));
99178825Sdfr#if 0
100178825Sdfr	if (dev->si_usecount == 0 &&
101178825Sdfr	    (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
102178825Sdfr		;
103178825Sdfr	else
104178825Sdfr#endif
105178825Sdfr	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
106178825Sdfr		LIST_REMOVE(dev, si_list);
107178825Sdfr		flag = 1;
108178825Sdfr	}
109178825Sdfr	dev_unlock();
110178825Sdfr	if (flag)
111178825Sdfr		devfs_free(dev);
112178825Sdfr}
113178825Sdfr
114178825Sdfrstruct cdevsw *
115178825Sdfrdev_refthread(struct cdev *dev)
116178825Sdfr{
117178825Sdfr	struct cdevsw *csw;
118178825Sdfr
119178825Sdfr	mtx_assert(&devmtx, MA_NOTOWNED);
120178825Sdfr	dev_lock();
121178825Sdfr	csw = dev->si_devsw;
122178825Sdfr	if (csw != NULL)
123178825Sdfr		dev->si_threadcount++;
124178825Sdfr	dev_unlock();
125178825Sdfr	return (csw);
126178825Sdfr}
127178825Sdfr
128178825Sdfrvoid
129178825Sdfrdev_relthread(struct cdev *dev)
130178825Sdfr{
131178825Sdfr
132178825Sdfr	mtx_assert(&devmtx, MA_NOTOWNED);
133178825Sdfr	dev_lock();
134178825Sdfr	dev->si_threadcount--;
135178825Sdfr	dev_unlock();
136178825Sdfr}
137178825Sdfr
138178825Sdfrint
139178825Sdfrnullop(void)
140233294Sstas{
141178825Sdfr
142178825Sdfr	return (0);
143233294Sstas}
144178825Sdfr
145233294Sstasint
146178825Sdfreopnotsupp(void)
147178825Sdfr{
148178825Sdfr
149178825Sdfr	return (EOPNOTSUPP);
150178825Sdfr}
151178825Sdfr
152178825Sdfrstatic int
153178825Sdfrenxio(void)
154178825Sdfr{
155233294Sstas	return (ENXIO);
156233294Sstas}
157233294Sstas
158178825Sdfrstatic int
159178825Sdfrenodev(void)
160178825Sdfr{
161178825Sdfr	return (ENODEV);
162178825Sdfr}
163178825Sdfr
164178825Sdfr/* Define a dead_cdevsw for use when devices leave unexpectedly. */
165178825Sdfr
166233294Sstas#define dead_open	(d_open_t *)enxio
167178825Sdfr#define dead_close	(d_close_t *)enxio
168178825Sdfr#define dead_read	(d_read_t *)enxio
169178825Sdfr#define dead_write	(d_write_t *)enxio
170178825Sdfr#define dead_ioctl	(d_ioctl_t *)enxio
171178825Sdfr#define dead_poll	(d_poll_t *)enodev
172178825Sdfr#define dead_mmap	(d_mmap_t *)enodev
173178825Sdfr
174178825Sdfrstatic void
175178825Sdfrdead_strategy(struct bio *bp)
176178825Sdfr{
177178825Sdfr
178178825Sdfr	biofinish(bp, NULL, ENXIO);
179178825Sdfr}
180178825Sdfr
181178825Sdfr#define dead_dump	(dumper_t *)enxio
182178825Sdfr#define dead_kqfilter	(d_kqfilter_t *)enxio
183178825Sdfr
184178825Sdfrstatic struct cdevsw dead_cdevsw = {
185178825Sdfr	.d_version =	D_VERSION,
186178825Sdfr	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
187178825Sdfr	.d_open =	dead_open,
188178825Sdfr	.d_close =	dead_close,
189178825Sdfr	.d_read =	dead_read,
190178825Sdfr	.d_write =	dead_write,
191178825Sdfr	.d_ioctl =	dead_ioctl,
192178825Sdfr	.d_poll =	dead_poll,
193178825Sdfr	.d_mmap =	dead_mmap,
194178825Sdfr	.d_strategy =	dead_strategy,
195178825Sdfr	.d_name =	"dead",
196178825Sdfr	.d_dump =	dead_dump,
197178825Sdfr	.d_kqfilter =	dead_kqfilter
198178825Sdfr};
199178825Sdfr
200178825Sdfr/* Default methods if driver does not specify method */
201178825Sdfr
202178825Sdfr#define null_open	(d_open_t *)nullop
203178825Sdfr#define null_close	(d_close_t *)nullop
204178825Sdfr#define no_read		(d_read_t *)enodev
205178825Sdfr#define no_write	(d_write_t *)enodev
206178825Sdfr#define no_ioctl	(d_ioctl_t *)enodev
207178825Sdfr#define no_mmap		(d_mmap_t *)enodev
208178825Sdfr#define no_kqfilter	(d_kqfilter_t *)enodev
209178825Sdfr
210178825Sdfrstatic void
211178825Sdfrno_strategy(struct bio *bp)
212178825Sdfr{
213178825Sdfr
214178825Sdfr	biofinish(bp, NULL, ENODEV);
215178825Sdfr}
216178825Sdfr
217178825Sdfrstatic int
218178825Sdfrno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
219178825Sdfr{
220178825Sdfr	/*
221178825Sdfr	 * Return true for read/write.  If the user asked for something
222178825Sdfr	 * special, return POLLNVAL, so that clients have a way of
223178825Sdfr	 * determining reliably whether or not the extended
224178825Sdfr	 * functionality is present without hard-coding knowledge
225178825Sdfr	 * of specific filesystem implementations.
226178825Sdfr	 * Stay in sync with vop_nopoll().
227178825Sdfr	 */
228178825Sdfr	if (events & ~POLLSTANDARD)
229178825Sdfr		return (POLLNVAL);
230178825Sdfr
231178825Sdfr	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
232178825Sdfr}
233178825Sdfr
234178825Sdfr#define no_dump		(dumper_t *)enodev
235178825Sdfr
236178825Sdfrstatic int
237178825Sdfrgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
238178825Sdfr{
239178825Sdfr	int retval;
240178825Sdfr
241178825Sdfr	mtx_lock(&Giant);
242178825Sdfr	retval = dev->si_devsw->d_gianttrick->
243178825Sdfr	    d_open(dev, oflags, devtype, td);
244178825Sdfr	mtx_unlock(&Giant);
245178825Sdfr	return (retval);
246178825Sdfr}
247178825Sdfr
248178825Sdfrstatic int
249178825Sdfrgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
250178825Sdfr{
251178825Sdfr	int retval;
252178825Sdfr
253178825Sdfr	mtx_lock(&Giant);
254178825Sdfr	retval = dev->si_devsw->d_gianttrick->
255178825Sdfr	    d_fdopen(dev, oflags, td, fdidx);
256178825Sdfr	mtx_unlock(&Giant);
257178825Sdfr	return (retval);
258178825Sdfr}
259178825Sdfr
260178825Sdfrstatic int
261178825Sdfrgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
262178825Sdfr{
263178825Sdfr	int retval;
264178825Sdfr
265178825Sdfr	mtx_lock(&Giant);
266178825Sdfr	retval = dev->si_devsw->d_gianttrick->
267178825Sdfr	    d_close(dev, fflag, devtype, td);
268178825Sdfr	mtx_unlock(&Giant);
269178825Sdfr	return (retval);
270178825Sdfr}
271178825Sdfr
272178825Sdfrstatic void
273178825Sdfrgiant_strategy(struct bio *bp)
274178825Sdfr{
275178825Sdfr
276178825Sdfr	mtx_lock(&Giant);
277178825Sdfr	bp->bio_dev->si_devsw->d_gianttrick->
278178825Sdfr	    d_strategy(bp);
279178825Sdfr	mtx_unlock(&Giant);
280178825Sdfr}
281178825Sdfr
282178825Sdfrstatic int
283178825Sdfrgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
284178825Sdfr{
285178825Sdfr	int retval;
286178825Sdfr
287178825Sdfr	mtx_lock(&Giant);
288178825Sdfr	retval = dev->si_devsw->d_gianttrick->
289178825Sdfr	    d_ioctl(dev, cmd, data, fflag, td);
290178825Sdfr	mtx_unlock(&Giant);
291178825Sdfr	return (retval);
292178825Sdfr}
293178825Sdfr
294178825Sdfrstatic int
295178825Sdfrgiant_read(struct cdev *dev, struct uio *uio, int ioflag)
296178825Sdfr{
297178825Sdfr	int retval;
298178825Sdfr
299178825Sdfr	mtx_lock(&Giant);
300178825Sdfr	retval = dev->si_devsw->d_gianttrick->
301178825Sdfr	    d_read(dev, uio, ioflag);
302178825Sdfr	mtx_unlock(&Giant);
303178825Sdfr	return (retval);
304178825Sdfr}
305178825Sdfr
306178825Sdfrstatic int
307178825Sdfrgiant_write(struct cdev *dev, struct uio *uio, int ioflag)
308178825Sdfr{
309233294Sstas	int retval;
310178825Sdfr
311178825Sdfr	mtx_lock(&Giant);
312233294Sstas	retval = dev->si_devsw->d_gianttrick->
313178825Sdfr		d_write(dev, uio, ioflag);
314178825Sdfr	mtx_unlock(&Giant);
315178825Sdfr	return (retval);
316178825Sdfr}
317178825Sdfr
318178825Sdfrstatic int
319178825Sdfrgiant_poll(struct cdev *dev, int events, struct thread *td)
320178825Sdfr{
321178825Sdfr	int retval;
322178825Sdfr
323178825Sdfr	mtx_lock(&Giant);
324178825Sdfr	retval = dev->si_devsw->d_gianttrick->
325178825Sdfr	    d_poll(dev, events, td);
326178825Sdfr	mtx_unlock(&Giant);
327178825Sdfr	return (retval);
328233294Sstas}
329178825Sdfr
330178825Sdfrstatic int
331178825Sdfrgiant_kqfilter(struct cdev *dev, struct knote *kn)
332178825Sdfr{
333178825Sdfr	int retval;
334178825Sdfr
335178825Sdfr	mtx_lock(&Giant);
336233294Sstas	retval = dev->si_devsw->d_gianttrick->
337178825Sdfr	    d_kqfilter(dev, kn);
338178825Sdfr	mtx_unlock(&Giant);
339178825Sdfr	return (retval);
340178825Sdfr}
341178825Sdfr
342178825Sdfrstatic int
343178825Sdfrgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
344178825Sdfr{
345178825Sdfr	int retval;
346178825Sdfr
347178825Sdfr	mtx_lock(&Giant);
348178825Sdfr	retval = dev->si_devsw->d_gianttrick->
349178825Sdfr	    d_mmap(dev, offset, paddr, nprot);
350178825Sdfr	mtx_unlock(&Giant);
351178825Sdfr	return (retval);
352178825Sdfr}
353178825Sdfr
354178825Sdfr
355178825Sdfr/*
356178825Sdfr * struct cdev * and u_dev_t primitives
357178825Sdfr */
358178825Sdfr
359178825Sdfrint
360178825Sdfrminor(struct cdev *x)
361178825Sdfr{
362178825Sdfr	if (x == NULL)
363178825Sdfr		return NODEV;
364178825Sdfr	return(x->si_drv0 & MAXMINOR);
365178825Sdfr}
366178825Sdfr
367178825Sdfrint
368178825Sdfrdev2unit(struct cdev *x)
369178825Sdfr{
370178825Sdfr
371178825Sdfr	if (x == NULL)
372233294Sstas		return NODEV;
373178825Sdfr	return (minor2unit(minor(x)));
374178825Sdfr}
375178825Sdfr
376178825Sdfru_int
377178825Sdfrminor2unit(u_int _minor)
378233294Sstas{
379233294Sstas
380178825Sdfr	KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
381178825Sdfr	return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
382178825Sdfr}
383178825Sdfr
384178825Sdfrint
385178825Sdfrunit2minor(int unit)
386178825Sdfr{
387178825Sdfr
388178825Sdfr	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
389178825Sdfr	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
390178825Sdfr}
391178825Sdfr
392178825Sdfrstatic struct cdev *
393178825Sdfrnewdev(struct cdevsw *csw, int y, struct cdev *si)
394178825Sdfr{
395233294Sstas	struct cdev *si2;
396233294Sstas	dev_t	udev;
397233294Sstas
398178825Sdfr	mtx_assert(&devmtx, MA_OWNED);
399178825Sdfr	udev = y;
400178825Sdfr	LIST_FOREACH(si2, &csw->d_devs, si_list) {
401178825Sdfr		if (si2->si_drv0 == udev) {
402178825Sdfr			devfs_free(si);
403178825Sdfr			return (si2);
404178825Sdfr		}
405178825Sdfr	}
406178825Sdfr	si->si_drv0 = udev;
407178825Sdfr	si->si_devsw = csw;
408178825Sdfr	LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
409178825Sdfr	return (si);
410178825Sdfr}
411178825Sdfr
412178825Sdfrint
413178825Sdfruminor(dev_t dev)
414178825Sdfr{
415178825Sdfr	return (dev & MAXMINOR);
416178825Sdfr}
417178825Sdfr
418178825Sdfrint
419178825Sdfrumajor(dev_t dev)
420178825Sdfr{
421178825Sdfr	return ((dev & ~MAXMINOR) >> 8);
422178825Sdfr}
423178825Sdfr
424233294Sstasstatic void
425178825Sdfrfini_cdevsw(struct cdevsw *devsw)
426178825Sdfr{
427178825Sdfr	struct cdevsw *gt;
428178825Sdfr
429178825Sdfr	if (devsw->d_gianttrick != NULL) {
430178825Sdfr		gt = devsw->d_gianttrick;
431178825Sdfr		memcpy(devsw, gt, sizeof *devsw);
432178825Sdfr		free(gt, M_DEVT);
433178825Sdfr		devsw->d_gianttrick = NULL;
434178825Sdfr	}
435178825Sdfr	devsw->d_flags &= ~D_INIT;
436178825Sdfr}
437178825Sdfr
438178825Sdfrstatic void
439178825Sdfrprep_cdevsw(struct cdevsw *devsw)
440178825Sdfr{
441178825Sdfr	struct cdevsw *dsw2;
442178825Sdfr
443178825Sdfr	if (devsw->d_flags & D_NEEDGIANT)
444178825Sdfr		dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
445178825Sdfr	else
446178825Sdfr		dsw2 = NULL;
447178825Sdfr	dev_lock();
448178825Sdfr
449178825Sdfr	if (devsw->d_version != D_VERSION_01) {
450178825Sdfr		printf(
451178825Sdfr		    "WARNING: Device driver \"%s\" has wrong version %s\n",
452178825Sdfr		    devsw->d_name == NULL ? "???" : devsw->d_name,
453178825Sdfr		    "and is disabled.  Recompile KLD module.");
454178825Sdfr		devsw->d_open = dead_open;
455178825Sdfr		devsw->d_close = dead_close;
456178825Sdfr		devsw->d_read = dead_read;
457178825Sdfr		devsw->d_write = dead_write;
458178825Sdfr		devsw->d_ioctl = dead_ioctl;
459178825Sdfr		devsw->d_poll = dead_poll;
460178825Sdfr		devsw->d_mmap = dead_mmap;
461178825Sdfr		devsw->d_strategy = dead_strategy;
462178825Sdfr		devsw->d_dump = dead_dump;
463178825Sdfr		devsw->d_kqfilter = dead_kqfilter;
464178825Sdfr	}
465178825Sdfr
466178825Sdfr	if (devsw->d_flags & D_TTY) {
467178825Sdfr		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
468178825Sdfr		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
469178825Sdfr		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
470178825Sdfr		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
471178825Sdfr		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
472178825Sdfr	}
473178825Sdfr
474178825Sdfr	if (devsw->d_flags & D_NEEDGIANT) {
475178825Sdfr		if (devsw->d_gianttrick == NULL) {
476178825Sdfr			memcpy(dsw2, devsw, sizeof *dsw2);
477178825Sdfr			devsw->d_gianttrick = dsw2;
478178825Sdfr		} else
479178825Sdfr			free(dsw2, M_DEVT);
480178825Sdfr	}
481178825Sdfr
482178825Sdfr#define FIXUP(member, noop, giant) 				\
483178825Sdfr	do {							\
484178825Sdfr		if (devsw->member == NULL) {			\
485178825Sdfr			devsw->member = noop;			\
486178825Sdfr		} else if (devsw->d_flags & D_NEEDGIANT)	\
487178825Sdfr			devsw->member = giant;			\
488178825Sdfr		}						\
489178825Sdfr	while (0)
490178825Sdfr
491178825Sdfr	FIXUP(d_open,		null_open,	giant_open);
492178825Sdfr	FIXUP(d_fdopen,		NULL,		giant_fdopen);
493178825Sdfr	FIXUP(d_close,		null_close,	giant_close);
494178825Sdfr	FIXUP(d_read,		no_read,	giant_read);
495178825Sdfr	FIXUP(d_write,		no_write,	giant_write);
496178825Sdfr	FIXUP(d_ioctl,		no_ioctl,	giant_ioctl);
497178825Sdfr	FIXUP(d_poll,		no_poll,	giant_poll);
498178825Sdfr	FIXUP(d_mmap,		no_mmap,	giant_mmap);
499178825Sdfr	FIXUP(d_strategy,	no_strategy,	giant_strategy);
500178825Sdfr	FIXUP(d_kqfilter,	no_kqfilter,	giant_kqfilter);
501178825Sdfr
502178825Sdfr	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
503178825Sdfr
504178825Sdfr	LIST_INIT(&devsw->d_devs);
505178825Sdfr
506178825Sdfr	devsw->d_flags |= D_INIT;
507178825Sdfr
508178825Sdfr	dev_unlock();
509178825Sdfr}
510178825Sdfr
511178825Sdfrstatic struct cdev *
512178825Sdfrmake_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
513178825Sdfr    gid_t gid, int mode, const char *fmt, va_list ap)
514178825Sdfr{
515178825Sdfr	struct cdev *dev;
516178825Sdfr	int i;
517178825Sdfr
518178825Sdfr	KASSERT((minornr & ~MAXMINOR) == 0,
519178825Sdfr	    ("Invalid minor (0x%x) in make_dev", minornr));
520178825Sdfr
521178825Sdfr	if (!(devsw->d_flags & D_INIT))
522178825Sdfr		prep_cdevsw(devsw);
523178825Sdfr	dev = devfs_alloc();
524233294Sstas	dev_lock();
525233294Sstas	dev = newdev(devsw, minornr, dev);
526178825Sdfr	if (dev->si_flags & SI_CHEAPCLONE &&
527178825Sdfr	    dev->si_flags & SI_NAMED) {
528178825Sdfr		/*
529178825Sdfr		 * This is allowed as it removes races and generally
530178825Sdfr		 * simplifies cloning devices.
531178825Sdfr		 * XXX: still ??
532178825Sdfr		 */
533178825Sdfr		dev_unlock();
534178825Sdfr		return (dev);
535178825Sdfr	}
536178825Sdfr	KASSERT(!(dev->si_flags & SI_NAMED),
537178825Sdfr	    ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
538178825Sdfr	    devsw->d_name, minor(dev), devtoname(dev)));
539178825Sdfr
540178825Sdfr	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
541178825Sdfr	if (i > (sizeof dev->__si_namebuf - 1)) {
542178825Sdfr		printf("WARNING: Device name truncated! (%s)\n",
543178825Sdfr		    dev->__si_namebuf);
544178825Sdfr	}
545178825Sdfr
546178825Sdfr	dev->si_flags |= SI_NAMED;
547178825Sdfr	if (cr != NULL)
548178825Sdfr		dev->si_cred = crhold(cr);
549178825Sdfr	else
550178825Sdfr		dev->si_cred = NULL;
551178825Sdfr	dev->si_uid = uid;
552178825Sdfr	dev->si_gid = gid;
553178825Sdfr	dev->si_mode = mode;
554178825Sdfr
555178825Sdfr	devfs_create(dev);
556178825Sdfr	dev_unlock();
557178825Sdfr	return (dev);
558178825Sdfr}
559178825Sdfr
560178825Sdfrstruct cdev *
561233294Sstasmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode,
562233294Sstas    const char *fmt, ...)
563178825Sdfr{
564178825Sdfr	struct cdev *dev;
565178825Sdfr	va_list ap;
566178825Sdfr
567178825Sdfr	va_start(ap, fmt);
568178825Sdfr	dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap);
569178825Sdfr	va_end(ap);
570178825Sdfr	return (dev);
571178825Sdfr}
572178825Sdfr
573178825Sdfrstruct cdev *
574178825Sdfrmake_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
575178825Sdfr    gid_t gid, int mode, const char *fmt, ...)
576178825Sdfr{
577178825Sdfr	struct cdev *dev;
578178825Sdfr	va_list ap;
579178825Sdfr
580178825Sdfr	va_start(ap, fmt);
581178825Sdfr	dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap);
582178825Sdfr	va_end(ap);
583178825Sdfr
584178825Sdfr	return (dev);
585178825Sdfr}
586178825Sdfr
587178825Sdfrstatic void
588178825Sdfrdev_dependsl(struct cdev *pdev, struct cdev *cdev)
589178825Sdfr{
590178825Sdfr
591178825Sdfr	cdev->si_parent = pdev;
592178825Sdfr	cdev->si_flags |= SI_CHILD;
593178825Sdfr	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
594178825Sdfr}
595178825Sdfr
596178825Sdfr
597178825Sdfrvoid
598178825Sdfrdev_depends(struct cdev *pdev, struct cdev *cdev)
599178825Sdfr{
600178825Sdfr
601178825Sdfr	dev_lock();
602178825Sdfr	dev_dependsl(pdev, cdev);
603178825Sdfr	dev_unlock();
604178825Sdfr}
605178825Sdfr
606178825Sdfrstruct cdev *
607178825Sdfrmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
608178825Sdfr{
609178825Sdfr	struct cdev *dev;
610178825Sdfr	va_list ap;
611178825Sdfr	int i;
612178825Sdfr
613178825Sdfr	dev = devfs_alloc();
614178825Sdfr	dev_lock();
615178825Sdfr	dev->si_flags |= SI_ALIAS;
616178825Sdfr	dev->si_flags |= SI_NAMED;
617178825Sdfr	va_start(ap, fmt);
618178825Sdfr	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
619178825Sdfr	if (i > (sizeof dev->__si_namebuf - 1)) {
620178825Sdfr		printf("WARNING: Device name truncated! (%s)\n",
621178825Sdfr		    dev->__si_namebuf);
622178825Sdfr	}
623178825Sdfr	va_end(ap);
624178825Sdfr
625178825Sdfr	devfs_create(dev);
626178825Sdfr	dev_unlock();
627178825Sdfr	dev_depends(pdev, dev);
628178825Sdfr	return (dev);
629178825Sdfr}
630178825Sdfr
631178825Sdfrstatic void
632178825Sdfrdestroy_devl(struct cdev *dev)
633233294Sstas{
634178825Sdfr	struct cdevsw *csw;
635178825Sdfr
636178825Sdfr	mtx_assert(&devmtx, MA_OWNED);
637178825Sdfr	KASSERT(dev->si_flags & SI_NAMED,
638178825Sdfr	    ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
639178825Sdfr
640178825Sdfr	devfs_destroy(dev);
641178825Sdfr
642178825Sdfr	/* Remove name marking */
643178825Sdfr	dev->si_flags &= ~SI_NAMED;
644178825Sdfr
645178825Sdfr	/* If we are a child, remove us from the parents list */
646178825Sdfr	if (dev->si_flags & SI_CHILD) {
647178825Sdfr		LIST_REMOVE(dev, si_siblings);
648178825Sdfr		dev->si_flags &= ~SI_CHILD;
649178825Sdfr	}
650178825Sdfr
651178825Sdfr	/* Kill our children */
652178825Sdfr	while (!LIST_EMPTY(&dev->si_children))
653178825Sdfr		destroy_devl(LIST_FIRST(&dev->si_children));
654178825Sdfr
655178825Sdfr	/* Remove from clone list */
656178825Sdfr	if (dev->si_flags & SI_CLONELIST) {
657178825Sdfr		LIST_REMOVE(dev, si_clone);
658178825Sdfr		dev->si_flags &= ~SI_CLONELIST;
659178825Sdfr	}
660178825Sdfr
661178825Sdfr	csw = dev->si_devsw;
662178825Sdfr	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
663178825Sdfr	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
664178825Sdfr		csw->d_purge(dev);
665178825Sdfr		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
666178825Sdfr		if (dev->si_threadcount)
667178825Sdfr			printf("Still %lu threads in %s\n",
668178825Sdfr			    dev->si_threadcount, devtoname(dev));
669178825Sdfr	}
670178825Sdfr
671178825Sdfr	dev->si_drv1 = 0;
672178825Sdfr	dev->si_drv2 = 0;
673178825Sdfr	bzero(&dev->__si_u, sizeof(dev->__si_u));
674178825Sdfr
675178825Sdfr	if (!(dev->si_flags & SI_ALIAS)) {
676178825Sdfr		/* Remove from cdevsw list */
677178825Sdfr		LIST_REMOVE(dev, si_list);
678178825Sdfr
679178825Sdfr		/* If cdevsw has no more struct cdev *'s, clean it */
680178825Sdfr		if (LIST_EMPTY(&csw->d_devs))
681178825Sdfr			fini_cdevsw(csw);
682178825Sdfr	}
683178825Sdfr	dev->si_flags &= ~SI_ALIAS;
684178825Sdfr
685178825Sdfr	if (dev->si_refcount > 0) {
686178825Sdfr		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
687178825Sdfr	} else {
688178825Sdfr		devfs_free(dev);
689178825Sdfr	}
690178825Sdfr}
691178825Sdfr
692178825Sdfrvoid
693178825Sdfrdestroy_dev(struct cdev *dev)
694178825Sdfr{
695178825Sdfr
696178825Sdfr	dev_lock();
697178825Sdfr	destroy_devl(dev);
698178825Sdfr	dev_unlock();
699178825Sdfr}
700178825Sdfr
701233294Sstasconst char *
702233294Sstasdevtoname(struct cdev *dev)
703233294Sstas{
704233294Sstas	char *p;
705233294Sstas	struct cdevsw *csw;
706178825Sdfr	int mynor;
707178825Sdfr
708178825Sdfr	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
709178825Sdfr		p = dev->si_name;
710178825Sdfr		csw = dev_refthread(dev);
711233294Sstas		if (csw != NULL) {
712178825Sdfr			sprintf(p, "(%s)", csw->d_name);
713178825Sdfr			dev_relthread(dev);
714178825Sdfr		}
715178825Sdfr		p += strlen(p);
716178825Sdfr		mynor = minor(dev);
717178825Sdfr		if (mynor < 0 || mynor > 255)
718178825Sdfr			sprintf(p, "/%#x", (u_int)mynor);
719233294Sstas		else
720178825Sdfr			sprintf(p, "/%d", mynor);
721178825Sdfr	}
722178825Sdfr	return (dev->si_name);
723233294Sstas}
724178825Sdfr
725178825Sdfrint
726178825Sdfrdev_stdclone(char *name, char **namep, const char *stem, int *unit)
727178825Sdfr{
728178825Sdfr	int u, i;
729178825Sdfr
730233294Sstas	i = strlen(stem);
731178825Sdfr	if (bcmp(stem, name, i) != 0)
732178825Sdfr		return (0);
733233294Sstas	if (!isdigit(name[i]))
734178825Sdfr		return (0);
735178825Sdfr	u = 0;
736178825Sdfr	if (name[i] == '0' && isdigit(name[i+1]))
737178825Sdfr		return (0);
738178825Sdfr	while (isdigit(name[i])) {
739178825Sdfr		u *= 10;
740178825Sdfr		u += name[i++] - '0';
741178825Sdfr	}
742178825Sdfr	if (u > 0xffffff)
743178825Sdfr		return (0);
744178825Sdfr	*unit = u;
745178825Sdfr	if (namep)
746178825Sdfr		*namep = &name[i];
747178825Sdfr	if (name[i])
748178825Sdfr		return (2);
749178825Sdfr	return (1);
750233294Sstas}
751178825Sdfr
752233294Sstas/*
753178825Sdfr * Helper functions for cloning device drivers.
754178825Sdfr *
755178825Sdfr * The objective here is to make it unnecessary for the device drivers to
756178825Sdfr * use rman or similar to manage their unit number space.  Due to the way
757178825Sdfr * we do "on-demand" devices, using rman or other "private" methods
758178825Sdfr * will be very tricky to lock down properly once we lock down this file.
759178825Sdfr *
760178825Sdfr * Instead we give the drivers these routines which puts the struct cdev *'s
761178825Sdfr * that are to be managed on their own list, and gives the driver the ability
762178825Sdfr * to ask for the first free unit number or a given specified unit number.
763178825Sdfr *
764178825Sdfr * In addition these routines support paired devices (pty, nmdm and similar)
765178825Sdfr * by respecting a number of "flag" bits in the minor number.
766178825Sdfr *
767178825Sdfr */
768178825Sdfr
769178825Sdfrstruct clonedevs {
770233294Sstas	LIST_HEAD(,cdev)	head;
771178825Sdfr};
772233294Sstas
773233294Sstasvoid
774233294Sstasclone_setup(struct clonedevs **cdp)
775178825Sdfr{
776178825Sdfr
777233294Sstas	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
778178825Sdfr	LIST_INIT(&(*cdp)->head);
779178825Sdfr}
780178825Sdfr
781178825Sdfrint
782178825Sdfrclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
783178825Sdfr{
784178825Sdfr	struct clonedevs *cd;
785178825Sdfr	struct cdev *dev, *ndev, *dl, *de;
786178825Sdfr	int unit, low, u;
787178825Sdfr
788178825Sdfr	KASSERT(*cdp != NULL,
789178825Sdfr	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
790178825Sdfr	KASSERT(!(extra & CLONE_UNITMASK),
791178825Sdfr	    ("Illegal extra bits (0x%x) in clone_create", extra));
792178825Sdfr	KASSERT(*up <= CLONE_UNITMASK,
793178825Sdfr	    ("Too high unit (0x%x) in clone_create", *up));
794178825Sdfr
795178825Sdfr	if (!(csw->d_flags & D_INIT))
796178825Sdfr		prep_cdevsw(csw);
797178825Sdfr
798178825Sdfr	/*
799178825Sdfr	 * Search the list for a lot of things in one go:
800178825Sdfr	 *   A preexisting match is returned immediately.
801178825Sdfr	 *   The lowest free unit number if we are passed -1, and the place
802178825Sdfr	 *	 in the list where we should insert that new element.
803178825Sdfr	 *   The place to insert a specified unit number, if applicable
804178825Sdfr	 *       the end of the list.
805178825Sdfr	 */
806178825Sdfr	unit = *up;
807178825Sdfr	ndev = devfs_alloc();
808178825Sdfr	dev_lock();
809178825Sdfr	low = extra;
810178825Sdfr	de = dl = NULL;
811233294Sstas	cd = *cdp;
812233294Sstas	LIST_FOREACH(dev, &cd->head, si_clone) {
813233294Sstas		KASSERT(dev->si_flags & SI_CLONELIST,
814233294Sstas		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
815233294Sstas		u = dev2unit(dev);
816233294Sstas		if (u == (unit | extra)) {
817233294Sstas			*dp = dev;
818233294Sstas			devfs_free(ndev);
819233294Sstas			dev_unlock();
820233294Sstas			return (0);
821233294Sstas		}
822233294Sstas		if (unit == -1 && u == low) {
823233294Sstas			low++;
824233294Sstas			de = dev;
825233294Sstas			continue;
826233294Sstas		} else if (u < (unit | extra)) {
827233294Sstas			de = dev;
828233294Sstas			continue;
829233294Sstas		} else if (u > (unit | extra)) {
830233294Sstas			dl = dev;
831233294Sstas			break;
832233294Sstas		}
833233294Sstas	}
834233294Sstas	if (unit == -1)
835233294Sstas		unit = low & CLONE_UNITMASK;
836233294Sstas	dev = newdev(csw, unit2minor(unit | extra), ndev);
837233294Sstas	if (dev->si_flags & SI_CLONELIST) {
838233294Sstas		printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
839233294Sstas		printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
840233294Sstas		LIST_FOREACH(dev, &cd->head, si_clone) {
841233294Sstas			printf("\t%p %s\n", dev, dev->si_name);
842233294Sstas		}
843233294Sstas		panic("foo");
844233294Sstas	}
845233294Sstas	KASSERT(!(dev->si_flags & SI_CLONELIST),
846233294Sstas	    ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
847233294Sstas	if (dl != NULL)
848233294Sstas		LIST_INSERT_BEFORE(dl, dev, si_clone);
849233294Sstas	else if (de != NULL)
850233294Sstas		LIST_INSERT_AFTER(de, dev, si_clone);
851233294Sstas	else
852233294Sstas		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
853233294Sstas	dev->si_flags |= SI_CLONELIST;
854233294Sstas	*up = unit;
855233294Sstas	dev_unlock();
856233294Sstas	return (1);
857233294Sstas}
858233294Sstas
859178825Sdfr/*
860178825Sdfr * Kill everything still on the list.  The driver should already have
861178825Sdfr * disposed of any softc hung of the struct cdev *'s at this time.
862178825Sdfr */
863233294Sstasvoid
864178825Sdfrclone_cleanup(struct clonedevs **cdp)
865178825Sdfr{
866178825Sdfr	struct cdev *dev, *tdev;
867178825Sdfr	struct clonedevs *cd;
868178825Sdfr
869178825Sdfr	cd = *cdp;
870178825Sdfr	if (cd == NULL)
871233294Sstas		return;
872178825Sdfr	dev_lock();
873178825Sdfr	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
874178825Sdfr		KASSERT(dev->si_flags & SI_CLONELIST,
875178825Sdfr		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
876178825Sdfr		KASSERT(dev->si_flags & SI_NAMED,
877178825Sdfr		    ("Driver has goofed in cloning underways udev %x", dev->si_drv0));
878178825Sdfr		destroy_devl(dev);
879178825Sdfr	}
880178825Sdfr	dev_unlock();
881178825Sdfr	free(cd, M_DEVBUF);
882178825Sdfr	*cdp = NULL;
883178825Sdfr}
884178825Sdfr