kern_conf.c revision 163529
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 163529 2006-10-20 07:59:50Z kib $");
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
128163529Skibstruct cdevsw *
129163529Skibdevvn_refthread(struct vnode *vp, struct cdev **devp)
130163529Skib{
131163529Skib	struct cdevsw *csw;
132163529Skib
133163529Skib	mtx_assert(&devmtx, MA_NOTOWNED);
134163529Skib	csw = NULL;
135163529Skib	dev_lock();
136163529Skib	*devp = vp->v_rdev;
137163529Skib	if (*devp != NULL) {
138163529Skib		csw = (*devp)->si_devsw;
139163529Skib		if (csw != NULL)
140163529Skib			(*devp)->si_threadcount++;
141163529Skib	}
142163529Skib	dev_unlock();
143163529Skib	return (csw);
144163529Skib}
145163529Skib
146135704Sphkvoid
147135704Sphkdev_relthread(struct cdev *dev)
148135704Sphk{
149135704Sphk
150135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
151135704Sphk	dev_lock();
152135704Sphk	dev->si_threadcount--;
153135704Sphk	dev_unlock();
154135704Sphk}
155135704Sphk
156120514Sphkint
157120514Sphknullop(void)
158120514Sphk{
15985603Sphk
160120514Sphk	return (0);
161120514Sphk}
162120514Sphk
163120514Sphkint
164120514Sphkeopnotsupp(void)
165120514Sphk{
166120514Sphk
167120514Sphk	return (EOPNOTSUPP);
168120514Sphk}
169120514Sphk
170111179Sphkstatic int
171111179Sphkenxio(void)
172111179Sphk{
173111179Sphk	return (ENXIO);
174111179Sphk}
175111179Sphk
176120514Sphkstatic int
177120514Sphkenodev(void)
178120514Sphk{
179120514Sphk	return (ENODEV);
180120514Sphk}
181120514Sphk
182120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
183120514Sphk
184111179Sphk#define dead_open	(d_open_t *)enxio
185111179Sphk#define dead_close	(d_close_t *)enxio
186111179Sphk#define dead_read	(d_read_t *)enxio
187111179Sphk#define dead_write	(d_write_t *)enxio
188111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
189120514Sphk#define dead_poll	(d_poll_t *)enodev
190120514Sphk#define dead_mmap	(d_mmap_t *)enodev
191111179Sphk
192111179Sphkstatic void
193111179Sphkdead_strategy(struct bio *bp)
194111179Sphk{
195111179Sphk
196111179Sphk	biofinish(bp, NULL, ENXIO);
197111179Sphk}
198111179Sphk
199111220Sphk#define dead_dump	(dumper_t *)enxio
200111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
201111179Sphk
202111179Sphkstatic struct cdevsw dead_cdevsw = {
203126080Sphk	.d_version =	D_VERSION,
204126080Sphk	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
205111815Sphk	.d_open =	dead_open,
206111815Sphk	.d_close =	dead_close,
207111815Sphk	.d_read =	dead_read,
208111815Sphk	.d_write =	dead_write,
209111815Sphk	.d_ioctl =	dead_ioctl,
210111815Sphk	.d_poll =	dead_poll,
211111815Sphk	.d_mmap =	dead_mmap,
212111815Sphk	.d_strategy =	dead_strategy,
213111815Sphk	.d_name =	"dead",
214111815Sphk	.d_dump =	dead_dump,
215111815Sphk	.d_kqfilter =	dead_kqfilter
216111179Sphk};
217111179Sphk
218120514Sphk/* Default methods if driver does not specify method */
219111179Sphk
220120514Sphk#define null_open	(d_open_t *)nullop
221120514Sphk#define null_close	(d_close_t *)nullop
222120514Sphk#define no_read		(d_read_t *)enodev
223120514Sphk#define no_write	(d_write_t *)enodev
224120514Sphk#define no_ioctl	(d_ioctl_t *)enodev
225120514Sphk#define no_mmap		(d_mmap_t *)enodev
226133741Sjmg#define no_kqfilter	(d_kqfilter_t *)enodev
227120514Sphk
228120514Sphkstatic void
229120514Sphkno_strategy(struct bio *bp)
230120514Sphk{
231120514Sphk
232120514Sphk	biofinish(bp, NULL, ENODEV);
233120514Sphk}
234120514Sphk
235120514Sphkstatic int
236130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
237120514Sphk{
238120514Sphk	/*
239120514Sphk	 * Return true for read/write.  If the user asked for something
240120514Sphk	 * special, return POLLNVAL, so that clients have a way of
241120514Sphk	 * determining reliably whether or not the extended
242120514Sphk	 * functionality is present without hard-coding knowledge
243120514Sphk	 * of specific filesystem implementations.
244120514Sphk	 * Stay in sync with vop_nopoll().
245120514Sphk	 */
246120514Sphk	if (events & ~POLLSTANDARD)
247120514Sphk		return (POLLNVAL);
248120514Sphk
249120514Sphk	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
250120514Sphk}
251120514Sphk
252120514Sphk#define no_dump		(dumper_t *)enodev
253120514Sphk
254149177Sphkstatic int
255149177Sphkgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
256149177Sphk{
257149177Sphk	int retval;
258149177Sphk
259149177Sphk	mtx_lock(&Giant);
260149177Sphk	retval = dev->si_devsw->d_gianttrick->
261149177Sphk	    d_open(dev, oflags, devtype, td);
262149177Sphk	mtx_unlock(&Giant);
263149177Sphk	return (retval);
264149177Sphk}
265149177Sphk
266149177Sphkstatic int
267149177Sphkgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
268149177Sphk{
269149177Sphk	int retval;
270149177Sphk
271149177Sphk	mtx_lock(&Giant);
272149177Sphk	retval = dev->si_devsw->d_gianttrick->
273149177Sphk	    d_fdopen(dev, oflags, td, fdidx);
274149177Sphk	mtx_unlock(&Giant);
275149177Sphk	return (retval);
276149177Sphk}
277149177Sphk
278149177Sphkstatic int
279149177Sphkgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
280149177Sphk{
281149177Sphk	int retval;
282149177Sphk
283149177Sphk	mtx_lock(&Giant);
284149177Sphk	retval = dev->si_devsw->d_gianttrick->
285149177Sphk	    d_close(dev, fflag, devtype, td);
286149177Sphk	mtx_unlock(&Giant);
287149177Sphk	return (retval);
288149177Sphk}
289149177Sphk
290149177Sphkstatic void
291149177Sphkgiant_strategy(struct bio *bp)
292149177Sphk{
293149177Sphk
294149177Sphk	mtx_lock(&Giant);
295149177Sphk	bp->bio_dev->si_devsw->d_gianttrick->
296149177Sphk	    d_strategy(bp);
297149177Sphk	mtx_unlock(&Giant);
298149177Sphk}
299149177Sphk
300149177Sphkstatic int
301149177Sphkgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
302149177Sphk{
303149177Sphk	int retval;
304149177Sphk
305149177Sphk	mtx_lock(&Giant);
306149177Sphk	retval = dev->si_devsw->d_gianttrick->
307149177Sphk	    d_ioctl(dev, cmd, data, fflag, td);
308149177Sphk	mtx_unlock(&Giant);
309149177Sphk	return (retval);
310149177Sphk}
311149177Sphk
312149177Sphkstatic int
313149177Sphkgiant_read(struct cdev *dev, struct uio *uio, int ioflag)
314149177Sphk{
315149177Sphk	int retval;
316149177Sphk
317149177Sphk	mtx_lock(&Giant);
318149177Sphk	retval = dev->si_devsw->d_gianttrick->
319149177Sphk	    d_read(dev, uio, ioflag);
320149177Sphk	mtx_unlock(&Giant);
321149177Sphk	return (retval);
322149177Sphk}
323149177Sphk
324149177Sphkstatic int
325149177Sphkgiant_write(struct cdev *dev, struct uio *uio, int ioflag)
326149177Sphk{
327149177Sphk	int retval;
328149177Sphk
329149177Sphk	mtx_lock(&Giant);
330149177Sphk	retval = dev->si_devsw->d_gianttrick->
331149177Sphk		d_write(dev, uio, ioflag);
332149177Sphk	mtx_unlock(&Giant);
333149177Sphk	return (retval);
334149177Sphk}
335149177Sphk
336149177Sphkstatic int
337149177Sphkgiant_poll(struct cdev *dev, int events, struct thread *td)
338149177Sphk{
339149177Sphk	int retval;
340149177Sphk
341149177Sphk	mtx_lock(&Giant);
342149177Sphk	retval = dev->si_devsw->d_gianttrick->
343149177Sphk	    d_poll(dev, events, td);
344149177Sphk	mtx_unlock(&Giant);
345149177Sphk	return (retval);
346149177Sphk}
347149177Sphk
348149177Sphkstatic int
349149177Sphkgiant_kqfilter(struct cdev *dev, struct knote *kn)
350149177Sphk{
351149177Sphk	int retval;
352149177Sphk
353149177Sphk	mtx_lock(&Giant);
354149177Sphk	retval = dev->si_devsw->d_gianttrick->
355149177Sphk	    d_kqfilter(dev, kn);
356149177Sphk	mtx_unlock(&Giant);
357149177Sphk	return (retval);
358149177Sphk}
359149177Sphk
360149177Sphkstatic int
361149177Sphkgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
362149177Sphk{
363149177Sphk	int retval;
364149177Sphk
365149177Sphk	mtx_lock(&Giant);
366149177Sphk	retval = dev->si_devsw->d_gianttrick->
367149177Sphk	    d_mmap(dev, offset, paddr, nprot);
368149177Sphk	mtx_unlock(&Giant);
369149177Sphk	return (retval);
370149177Sphk}
371149177Sphk
372149177Sphk
37347640Sphk/*
374130936Sle * struct cdev * and u_dev_t primitives
37547028Sphk */
37647028Sphk
37747028Sphkint
378130585Sphkminor(struct cdev *x)
37947028Sphk{
380130640Sphk	if (x == NULL)
381130640Sphk		return NODEV;
382143631Sphk	return(x->si_drv0 & MAXMINOR);
38347028Sphk}
38447028Sphk
38549826Sphkint
386130585Sphkdev2unit(struct cdev *x)
38749826Sphk{
38849826Sphk
389130640Sphk	if (x == NULL)
390130640Sphk		return NODEV;
391140964Sphk	return (minor2unit(minor(x)));
39249826Sphk}
39349826Sphk
394143282Sphku_int
395143282Sphkminor2unit(u_int _minor)
396140963Sphk{
397140963Sphk
398140969Sphk	KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
399143282Sphk	return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
400140963Sphk}
401140963Sphk
402140963Sphkint
40366067Sphkunit2minor(int unit)
40466067Sphk{
40566067Sphk
40674522Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
40766067Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
40866067Sphk}
40966067Sphk
410130585Sphkstatic struct cdev *
411144281Sphknewdev(struct cdevsw *csw, int y, struct cdev *si)
41247028Sphk{
413140733Sphk	struct cdev *si2;
414130640Sphk	dev_t	udev;
41548936Sphk
416140733Sphk	mtx_assert(&devmtx, MA_OWNED);
417144292Sphk	udev = y;
418144281Sphk	LIST_FOREACH(si2, &csw->d_devs, si_list) {
419143631Sphk		if (si2->si_drv0 == udev) {
420150342Sphk			devfs_free(si);
421140733Sphk			return (si2);
422140733Sphk		}
42348936Sphk	}
424143631Sphk	si->si_drv0 = udev;
425150342Sphk	si->si_devsw = csw;
426144281Sphk	LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
427125850Sbde	return (si);
42847028Sphk}
42947028Sphk
43047028Sphkint
431130640Sphkuminor(dev_t dev)
43247028Sphk{
433140969Sphk	return (dev & MAXMINOR);
43447028Sphk}
43547028Sphk
43647028Sphkint
437130640Sphkumajor(dev_t dev)
43847028Sphk{
439140969Sphk	return ((dev & ~MAXMINOR) >> 8);
44047028Sphk}
44147028Sphk
442125846Sphkstatic void
443144292Sphkfini_cdevsw(struct cdevsw *devsw)
44449535Sphk{
445149324Sphk	struct cdevsw *gt;
44649535Sphk
447149324Sphk	if (devsw->d_gianttrick != NULL) {
448149324Sphk		gt = devsw->d_gianttrick;
449149324Sphk		memcpy(devsw, gt, sizeof *devsw);
450149324Sphk		free(gt, M_DEVT);
451149324Sphk		devsw->d_gianttrick = NULL;
452149324Sphk	}
453126156Sphk	devsw->d_flags &= ~D_INIT;
454126082Sphk}
455126082Sphk
456126082Sphkstatic void
457126077Sphkprep_cdevsw(struct cdevsw *devsw)
458126077Sphk{
459149177Sphk	struct cdevsw *dsw2;
460126077Sphk
461149177Sphk	if (devsw->d_flags & D_NEEDGIANT)
462149177Sphk		dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
463149177Sphk	else
464149177Sphk		dsw2 = NULL;
465135600Sphk	dev_lock();
466126082Sphk
467143746Sphk	if (devsw->d_version != D_VERSION_01) {
468126082Sphk		printf(
469126082Sphk		    "WARNING: Device driver \"%s\" has wrong version %s\n",
470154266Salfred		    devsw->d_name == NULL ? "???" : devsw->d_name,
471154266Salfred		    "and is disabled.  Recompile KLD module.");
472126082Sphk		devsw->d_open = dead_open;
473126082Sphk		devsw->d_close = dead_close;
474126082Sphk		devsw->d_read = dead_read;
475126082Sphk		devsw->d_write = dead_write;
476126082Sphk		devsw->d_ioctl = dead_ioctl;
477126082Sphk		devsw->d_poll = dead_poll;
478126082Sphk		devsw->d_mmap = dead_mmap;
479126082Sphk		devsw->d_strategy = dead_strategy;
480126082Sphk		devsw->d_dump = dead_dump;
481126082Sphk		devsw->d_kqfilter = dead_kqfilter;
482126082Sphk	}
483126082Sphk
484126078Sphk	if (devsw->d_flags & D_TTY) {
485129943Sphk		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
486126078Sphk		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
487126078Sphk		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
488126078Sphk		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
489126078Sphk		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
490126078Sphk	}
491126078Sphk
492149177Sphk	if (devsw->d_flags & D_NEEDGIANT) {
493149177Sphk		if (devsw->d_gianttrick == NULL) {
494149177Sphk			memcpy(dsw2, devsw, sizeof *dsw2);
495149177Sphk			devsw->d_gianttrick = dsw2;
496149177Sphk		} else
497149177Sphk			free(dsw2, M_DEVT);
498149177Sphk	}
499149177Sphk
500149177Sphk#define FIXUP(member, noop, giant) 				\
501149177Sphk	do {							\
502149177Sphk		if (devsw->member == NULL) {			\
503149177Sphk			devsw->member = noop;			\
504149177Sphk		} else if (devsw->d_flags & D_NEEDGIANT)	\
505149177Sphk			devsw->member = giant;			\
506149177Sphk		}						\
507149177Sphk	while (0)
508149177Sphk
509149177Sphk	FIXUP(d_open,		null_open,	giant_open);
510149177Sphk	FIXUP(d_fdopen,		NULL,		giant_fdopen);
511149177Sphk	FIXUP(d_close,		null_close,	giant_close);
512149177Sphk	FIXUP(d_read,		no_read,	giant_read);
513149177Sphk	FIXUP(d_write,		no_write,	giant_write);
514149177Sphk	FIXUP(d_ioctl,		no_ioctl,	giant_ioctl);
515149177Sphk	FIXUP(d_poll,		no_poll,	giant_poll);
516149177Sphk	FIXUP(d_mmap,		no_mmap,	giant_mmap);
517149177Sphk	FIXUP(d_strategy,	no_strategy,	giant_strategy);
518149177Sphk	FIXUP(d_kqfilter,	no_kqfilter,	giant_kqfilter);
519149177Sphk
520120514Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
521126082Sphk
522126082Sphk	LIST_INIT(&devsw->d_devs);
523126082Sphk
524126082Sphk	devsw->d_flags |= D_INIT;
525126082Sphk
526135600Sphk	dev_unlock();
527125846Sphk}
528111622Sphk
529147982Srwatsonstatic struct cdev *
530147982Srwatsonmake_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
531147982Srwatson    gid_t gid, int mode, const char *fmt, va_list ap)
532125846Sphk{
533130585Sphk	struct cdev *dev;
534125846Sphk	int i;
535125846Sphk
536140969Sphk	KASSERT((minornr & ~MAXMINOR) == 0,
537126082Sphk	    ("Invalid minor (0x%x) in make_dev", minornr));
538126082Sphk
539144385Sphk	if (!(devsw->d_flags & D_INIT))
540126082Sphk		prep_cdevsw(devsw);
541150342Sphk	dev = devfs_alloc();
542140733Sphk	dev_lock();
543144281Sphk	dev = newdev(devsw, minornr, dev);
544120529Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
545150342Sphk	    dev->si_flags & SI_NAMED) {
546120529Sphk		/*
547120529Sphk		 * This is allowed as it removes races and generally
548120529Sphk		 * simplifies cloning devices.
549126082Sphk		 * XXX: still ??
550120529Sphk		 */
551140733Sphk		dev_unlock();
552120529Sphk		return (dev);
553120529Sphk	}
554126082Sphk	KASSERT(!(dev->si_flags & SI_NAMED),
555144281Sphk	    ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
556144281Sphk	    devsw->d_name, minor(dev), devtoname(dev)));
557126082Sphk
558110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
559110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
560134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
561110318Sphk		    dev->__si_namebuf);
562110318Sphk	}
563136947Sphk
56465747Sphk	dev->si_flags |= SI_NAMED;
565147982Srwatson	if (cr != NULL)
566147982Srwatson		dev->si_cred = crhold(cr);
567147982Srwatson	else
568147982Srwatson		dev->si_cred = NULL;
569144385Sphk	dev->si_uid = uid;
570144385Sphk	dev->si_gid = gid;
571144385Sphk	dev->si_mode = mode;
57250092Sjulian
573111730Sphk	devfs_create(dev);
574135600Sphk	dev_unlock();
57549535Sphk	return (dev);
57649535Sphk}
57749535Sphk
578147982Srwatsonstruct cdev *
579147982Srwatsonmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode,
580147982Srwatson    const char *fmt, ...)
581147982Srwatson{
582147982Srwatson	struct cdev *dev;
583147982Srwatson	va_list ap;
584147982Srwatson
585147982Srwatson	va_start(ap, fmt);
586147982Srwatson	dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap);
587147982Srwatson	va_end(ap);
588147982Srwatson	return (dev);
589147982Srwatson}
590147982Srwatson
591147982Srwatsonstruct cdev *
592147982Srwatsonmake_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
593147982Srwatson    gid_t gid, int mode, const char *fmt, ...)
594147982Srwatson{
595147982Srwatson	struct cdev *dev;
596147982Srwatson	va_list ap;
597147982Srwatson
598147982Srwatson	va_start(ap, fmt);
599147982Srwatson	dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap);
600147982Srwatson	va_end(ap);
601147982Srwatson
602147982Srwatson	return (dev);
603147982Srwatson}
604147982Srwatson
605150342Sphkstatic void
606150342Sphkdev_dependsl(struct cdev *pdev, struct cdev *cdev)
607150342Sphk{
608150342Sphk
609150342Sphk	cdev->si_parent = pdev;
610150342Sphk	cdev->si_flags |= SI_CHILD;
611150342Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
612150342Sphk}
613150342Sphk
614150342Sphk
61577215Sphkvoid
616130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev)
61777215Sphk{
61877215Sphk
619135600Sphk	dev_lock();
620150342Sphk	dev_dependsl(pdev, cdev);
621135600Sphk	dev_unlock();
62277215Sphk}
62377215Sphk
624130585Sphkstruct cdev *
625130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
62664880Sphk{
627130585Sphk	struct cdev *dev;
62864880Sphk	va_list ap;
62964880Sphk	int i;
63064880Sphk
631150342Sphk	dev = devfs_alloc();
632135600Sphk	dev_lock();
63364880Sphk	dev->si_flags |= SI_ALIAS;
63465747Sphk	dev->si_flags |= SI_NAMED;
63564880Sphk	va_start(ap, fmt);
636110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
637110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
638134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
639110318Sphk		    dev->__si_namebuf);
640110318Sphk	}
64164880Sphk	va_end(ap);
64264880Sphk
643111730Sphk	devfs_create(dev);
644135600Sphk	dev_unlock();
645126082Sphk	dev_depends(pdev, dev);
64664880Sphk	return (dev);
64764880Sphk}
64864880Sphk
649126082Sphkstatic void
650142242Sphkdestroy_devl(struct cdev *dev)
65150549Sphk{
652135843Sphk	struct cdevsw *csw;
653135843Sphk
654142242Sphk	mtx_assert(&devmtx, MA_OWNED);
655135843Sphk	KASSERT(dev->si_flags & SI_NAMED,
656144281Sphk	    ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
657154029Sbz
658111730Sphk	devfs_destroy(dev);
659126082Sphk
660126082Sphk	/* Remove name marking */
661126077Sphk	dev->si_flags &= ~SI_NAMED;
662126077Sphk
663126082Sphk	/* If we are a child, remove us from the parents list */
66477215Sphk	if (dev->si_flags & SI_CHILD) {
66577215Sphk		LIST_REMOVE(dev, si_siblings);
66677215Sphk		dev->si_flags &= ~SI_CHILD;
66777215Sphk	}
668126082Sphk
669126082Sphk	/* Kill our children */
67077215Sphk	while (!LIST_EMPTY(&dev->si_children))
671142242Sphk		destroy_devl(LIST_FIRST(&dev->si_children));
672126082Sphk
673126082Sphk	/* Remove from clone list */
674126077Sphk	if (dev->si_flags & SI_CLONELIST) {
675126077Sphk		LIST_REMOVE(dev, si_clone);
676126077Sphk		dev->si_flags &= ~SI_CLONELIST;
677126077Sphk	}
678126082Sphk
679163328Stegge	dev->si_refcount++;	/* Avoid race with dev_rel() */
680135843Sphk	csw = dev->si_devsw;
681135934Sgreen	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
682135934Sgreen	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
683135843Sphk		csw->d_purge(dev);
684135843Sphk		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
685158684Sphk		if (dev->si_threadcount)
686158684Sphk			printf("Still %lu threads in %s\n",
687158684Sphk			    dev->si_threadcount, devtoname(dev));
688135843Sphk	}
689163328Stegge	while (dev->si_threadcount != 0) {
690163328Stegge		/* Use unique dummy wait ident */
691163328Stegge		msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10);
692163328Stegge	}
693135843Sphk
694135843Sphk	dev->si_drv1 = 0;
695135843Sphk	dev->si_drv2 = 0;
696135843Sphk	bzero(&dev->__si_u, sizeof(dev->__si_u));
697135843Sphk
698126082Sphk	if (!(dev->si_flags & SI_ALIAS)) {
699126082Sphk		/* Remove from cdevsw list */
700126082Sphk		LIST_REMOVE(dev, si_list);
701126082Sphk
702150342Sphk		/* If cdevsw has no more struct cdev *'s, clean it */
703135844Sphk		if (LIST_EMPTY(&csw->d_devs))
704135844Sphk			fini_cdevsw(csw);
705126082Sphk	}
70665747Sphk	dev->si_flags &= ~SI_ALIAS;
707163328Stegge	dev->si_refcount--;	/* Avoid race with dev_rel() */
708135843Sphk
709126082Sphk	if (dev->si_refcount > 0) {
710126082Sphk		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
711126082Sphk	} else {
712150342Sphk		devfs_free(dev);
713126082Sphk	}
71450549Sphk}
71550549Sphk
716126082Sphkvoid
717130585Sphkdestroy_dev(struct cdev *dev)
718126082Sphk{
719126082Sphk
720135600Sphk	dev_lock();
721142242Sphk	destroy_devl(dev);
722135600Sphk	dev_unlock();
723126082Sphk}
724126082Sphk
72551225Sbdeconst char *
726130585Sphkdevtoname(struct cdev *dev)
72749982Sbillf{
72850549Sphk	char *p;
729135712Sphk	struct cdevsw *csw;
73051225Sbde	int mynor;
73149982Sbillf
73250549Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
73350549Sphk		p = dev->si_name;
734135712Sphk		csw = dev_refthread(dev);
735135712Sphk		if (csw != NULL) {
736135712Sphk			sprintf(p, "(%s)", csw->d_name);
737135712Sphk			dev_relthread(dev);
738135712Sphk		}
739135712Sphk		p += strlen(p);
74051225Sbde		mynor = minor(dev);
74151225Sbde		if (mynor < 0 || mynor > 255)
742135712Sphk			sprintf(p, "/%#x", (u_int)mynor);
74351225Sbde		else
744135712Sphk			sprintf(p, "/%d", mynor);
74550549Sphk	}
74649982Sbillf	return (dev->si_name);
74749982Sbillf}
74865374Sphk
74965374Sphkint
75091998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
75165374Sphk{
75265374Sphk	int u, i;
75365374Sphk
75475519Sbrian	i = strlen(stem);
75575519Sbrian	if (bcmp(stem, name, i) != 0)
75665374Sphk		return (0);
75765374Sphk	if (!isdigit(name[i]))
75865374Sphk		return (0);
75965374Sphk	u = 0;
76086461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
76186461Sphk		return (0);
76265374Sphk	while (isdigit(name[i])) {
76365374Sphk		u *= 10;
76465374Sphk		u += name[i++] - '0';
76565374Sphk	}
766104523Sgreen	if (u > 0xffffff)
767104523Sgreen		return (0);
76865374Sphk	*unit = u;
76965374Sphk	if (namep)
77065374Sphk		*namep = &name[i];
77165374Sphk	if (name[i])
77265374Sphk		return (2);
77365374Sphk	return (1);
77465374Sphk}
77565632Sphk
77665632Sphk/*
777126077Sphk * Helper functions for cloning device drivers.
778126077Sphk *
779126077Sphk * The objective here is to make it unnecessary for the device drivers to
780126077Sphk * use rman or similar to manage their unit number space.  Due to the way
781126077Sphk * we do "on-demand" devices, using rman or other "private" methods
782126077Sphk * will be very tricky to lock down properly once we lock down this file.
783126077Sphk *
784130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s
785130936Sle * that are to be managed on their own list, and gives the driver the ability
786126077Sphk * to ask for the first free unit number or a given specified unit number.
787126077Sphk *
788126077Sphk * In addition these routines support paired devices (pty, nmdm and similar)
789126077Sphk * by respecting a number of "flag" bits in the minor number.
790126077Sphk *
791126077Sphk */
792126077Sphk
793126077Sphkstruct clonedevs {
794126077Sphk	LIST_HEAD(,cdev)	head;
795126077Sphk};
796126077Sphk
797126845Sphkvoid
798126845Sphkclone_setup(struct clonedevs **cdp)
799126845Sphk{
800126845Sphk
801126845Sphk	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
802126845Sphk	LIST_INIT(&(*cdp)->head);
803126845Sphk}
804126845Sphk
805126077Sphkint
806130585Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
807126077Sphk{
808126077Sphk	struct clonedevs *cd;
809140733Sphk	struct cdev *dev, *ndev, *dl, *de;
810126077Sphk	int unit, low, u;
811126077Sphk
812126845Sphk	KASSERT(*cdp != NULL,
813126845Sphk	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
814126077Sphk	KASSERT(!(extra & CLONE_UNITMASK),
815126845Sphk	    ("Illegal extra bits (0x%x) in clone_create", extra));
816126077Sphk	KASSERT(*up <= CLONE_UNITMASK,
817126845Sphk	    ("Too high unit (0x%x) in clone_create", *up));
818126077Sphk
819142726Sphk	if (!(csw->d_flags & D_INIT))
820142726Sphk		prep_cdevsw(csw);
821126077Sphk
822126077Sphk	/*
823126077Sphk	 * Search the list for a lot of things in one go:
824126077Sphk	 *   A preexisting match is returned immediately.
825126077Sphk	 *   The lowest free unit number if we are passed -1, and the place
826126077Sphk	 *	 in the list where we should insert that new element.
827126077Sphk	 *   The place to insert a specified unit number, if applicable
828126077Sphk	 *       the end of the list.
829126077Sphk	 */
830126077Sphk	unit = *up;
831150342Sphk	ndev = devfs_alloc();
832140733Sphk	dev_lock();
833126849Sphk	low = extra;
834126077Sphk	de = dl = NULL;
835126845Sphk	cd = *cdp;
836126077Sphk	LIST_FOREACH(dev, &cd->head, si_clone) {
837140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
838140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
839126077Sphk		u = dev2unit(dev);
840126077Sphk		if (u == (unit | extra)) {
841126077Sphk			*dp = dev;
842150342Sphk			devfs_free(ndev);
843140733Sphk			dev_unlock();
844126077Sphk			return (0);
845126077Sphk		}
846126077Sphk		if (unit == -1 && u == low) {
847126077Sphk			low++;
848126077Sphk			de = dev;
849126077Sphk			continue;
850150793Sphk		} else if (u < (unit | extra)) {
851150793Sphk			de = dev;
852150793Sphk			continue;
853150793Sphk		} else if (u > (unit | extra)) {
854126077Sphk			dl = dev;
855126077Sphk			break;
856126077Sphk		}
857126077Sphk	}
858126077Sphk	if (unit == -1)
859126849Sphk		unit = low & CLONE_UNITMASK;
860144281Sphk	dev = newdev(csw, unit2minor(unit | extra), ndev);
861140733Sphk	if (dev->si_flags & SI_CLONELIST) {
862140733Sphk		printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
863150793Sphk		printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
864140733Sphk		LIST_FOREACH(dev, &cd->head, si_clone) {
865140733Sphk			printf("\t%p %s\n", dev, dev->si_name);
866140733Sphk		}
867140733Sphk		panic("foo");
868140733Sphk	}
869126077Sphk	KASSERT(!(dev->si_flags & SI_CLONELIST),
870140733Sphk	    ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
871126077Sphk	if (dl != NULL)
872126077Sphk		LIST_INSERT_BEFORE(dl, dev, si_clone);
873126077Sphk	else if (de != NULL)
874126077Sphk		LIST_INSERT_AFTER(de, dev, si_clone);
875126077Sphk	else
876126077Sphk		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
877126077Sphk	dev->si_flags |= SI_CLONELIST;
878126077Sphk	*up = unit;
879140733Sphk	dev_unlock();
880126077Sphk	return (1);
881126077Sphk}
882126077Sphk
883126077Sphk/*
884126077Sphk * Kill everything still on the list.  The driver should already have
885130585Sphk * disposed of any softc hung of the struct cdev *'s at this time.
886126077Sphk */
887126077Sphkvoid
888126077Sphkclone_cleanup(struct clonedevs **cdp)
889126077Sphk{
890130585Sphk	struct cdev *dev, *tdev;
891126077Sphk	struct clonedevs *cd;
892126077Sphk
893126077Sphk	cd = *cdp;
894126077Sphk	if (cd == NULL)
895126077Sphk		return;
896140733Sphk	dev_lock();
897126077Sphk	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
898140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
899140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
900126077Sphk		KASSERT(dev->si_flags & SI_NAMED,
901143631Sphk		    ("Driver has goofed in cloning underways udev %x", dev->si_drv0));
902142242Sphk		destroy_devl(dev);
903126077Sphk	}
904140733Sphk	dev_unlock();
905126077Sphk	free(cd, M_DEVBUF);
906126077Sphk	*cdp = NULL;
907126077Sphk}
908