kern_conf.c revision 154029
155714Skris/*-
255714Skris * Copyright (c) 1999-2002 Poul-Henning Kamp
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8296465Sdelphij * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296465Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296465Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455714Skris * SUCH DAMAGE.
2555714Skris */
2655714Skris
2755714Skris#include <sys/cdefs.h>
2855714Skris__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 154029 2006-01-04 17:40:54Z bz $");
2955714Skris
3055714Skris#include <sys/param.h>
3155714Skris#include <sys/kernel.h>
3255714Skris#include <sys/systm.h>
3355714Skris#include <sys/bio.h>
3455714Skris#include <sys/lock.h>
3555714Skris#include <sys/mutex.h>
3655714Skris#include <sys/module.h>
37296465Sdelphij#include <sys/malloc.h>
3855714Skris#include <sys/conf.h>
3955714Skris#include <sys/vnode.h>
40296465Sdelphij#include <sys/queue.h>
4155714Skris#include <sys/poll.h>
4255714Skris#include <sys/ctype.h>
4355714Skris#include <sys/tty.h>
4455714Skris#include <sys/ucred.h>
4555714Skris#include <machine/stdarg.h>
4655714Skris
4755714Skris#include <fs/devfs/devfs_int.h>
4855714Skris
4955714Skrisstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
5055714Skris
5155714Skrisstruct mtx devmtx;
52296465Sdelphijstatic void destroy_devl(struct cdev *dev);
5355714Skrisstatic struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr,
5455714Skris	    struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
5555714Skris	    va_list ap);
5655714Skris
5755714Skrisvoid
5855714Skrisdev_lock(void)
5955714Skris{
60296465Sdelphij
61296465Sdelphij	mtx_lock(&devmtx);
62296465Sdelphij}
63296465Sdelphij
64296465Sdelphijvoid
6555714Skrisdev_unlock(void)
6655714Skris{
6755714Skris
68296465Sdelphij	mtx_unlock(&devmtx);
6955714Skris}
70296465Sdelphij
71296465Sdelphijvoid
72296465Sdelphijdev_ref(struct cdev *dev)
73296465Sdelphij{
74296465Sdelphij
75296465Sdelphij	mtx_assert(&devmtx, MA_NOTOWNED);
76296465Sdelphij	mtx_lock(&devmtx);
7768651Skris	dev->si_refcount++;
7855714Skris	mtx_unlock(&devmtx);
7955714Skris}
8055714Skris
8155714Skrisvoid
8255714Skrisdev_refl(struct cdev *dev)
83296465Sdelphij{
84296465Sdelphij
85296465Sdelphij	mtx_assert(&devmtx, MA_OWNED);
86296465Sdelphij	dev->si_refcount++;
87296465Sdelphij}
88296465Sdelphij
8955714Skrisvoid
90296465Sdelphijdev_rel(struct cdev *dev)
91296465Sdelphij{
92296465Sdelphij	int flag = 0;
93296465Sdelphij
94296465Sdelphij	mtx_assert(&devmtx, MA_NOTOWNED);
95296465Sdelphij	dev_lock();
9655714Skris	dev->si_refcount--;
9755714Skris	KASSERT(dev->si_refcount >= 0,
98296465Sdelphij	    ("dev_rel(%s) gave negative count", devtoname(dev)));
99296465Sdelphij#if 0
100296465Sdelphij	if (dev->si_usecount == 0 &&
101296465Sdelphij	    (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
102296465Sdelphij		;
103296465Sdelphij	else
10455714Skris#endif
105296465Sdelphij	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
106296465Sdelphij		LIST_REMOVE(dev, si_list);
107296465Sdelphij		flag = 1;
10855714Skris	}
10955714Skris	dev_unlock();
11055714Skris	if (flag)
11155714Skris		devfs_free(dev);
11255714Skris}
11355714Skris
11455714Skrisstruct cdevsw *
115296465Sdelphijdev_refthread(struct cdev *dev)
116296465Sdelphij{
117296465Sdelphij	struct cdevsw *csw;
118296465Sdelphij
119296465Sdelphij	mtx_assert(&devmtx, MA_NOTOWNED);
12055714Skris	dev_lock();
121296465Sdelphij	csw = dev->si_devsw;
122296465Sdelphij	if (csw != NULL)
123296465Sdelphij		dev->si_threadcount++;
124296465Sdelphij	dev_unlock();
125296465Sdelphij	return (csw);
126296465Sdelphij}
127296465Sdelphij
128296465Sdelphijvoid
129296465Sdelphijdev_relthread(struct cdev *dev)
130296465Sdelphij{
13155714Skris
13255714Skris	mtx_assert(&devmtx, MA_NOTOWNED);
13355714Skris	dev_lock();
13455714Skris	dev->si_threadcount--;
13568651Skris	dev_unlock();
13655714Skris}
13755714Skris
138296465Sdelphijint
139296465Sdelphijnullop(void)
140296465Sdelphij{
141296465Sdelphij
142296465Sdelphij	return (0);
143296465Sdelphij}
144296465Sdelphij
145296465Sdelphijint
146296465Sdelphijeopnotsupp(void)
147296465Sdelphij{
148296465Sdelphij
149296465Sdelphij	return (EOPNOTSUPP);
150296465Sdelphij}
151296465Sdelphij
152296465Sdelphijstatic int
153296465Sdelphijenxio(void)
154296465Sdelphij{
155296465Sdelphij	return (ENXIO);
15655714Skris}
157296465Sdelphij
158296465Sdelphijstatic int
159296465Sdelphijenodev(void)
160160814Ssimon{
161160814Ssimon	return (ENODEV);
162160814Ssimon}
163296465Sdelphij
164296465Sdelphij/* Define a dead_cdevsw for use when devices leave unexpectedly. */
165296465Sdelphij
166296465Sdelphij#define dead_open	(d_open_t *)enxio
167296465Sdelphij#define dead_close	(d_close_t *)enxio
168296465Sdelphij#define dead_read	(d_read_t *)enxio
169296465Sdelphij#define dead_write	(d_write_t *)enxio
170296465Sdelphij#define dead_ioctl	(d_ioctl_t *)enxio
171296465Sdelphij#define dead_poll	(d_poll_t *)enodev
172296465Sdelphij#define dead_mmap	(d_mmap_t *)enodev
173160814Ssimon
174160814Ssimonstatic void
175160814Ssimondead_strategy(struct bio *bp)
176296465Sdelphij{
177296465Sdelphij
178296465Sdelphij	biofinish(bp, NULL, ENXIO);
179296465Sdelphij}
180296465Sdelphij
181296465Sdelphij#define dead_dump	(dumper_t *)enxio
182296465Sdelphij#define dead_kqfilter	(d_kqfilter_t *)enxio
183296465Sdelphij
184296465Sdelphijstatic struct cdevsw dead_cdevsw = {
185296465Sdelphij	.d_version =	D_VERSION,
186296465Sdelphij	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
187296465Sdelphij	.d_open =	dead_open,
188296465Sdelphij	.d_close =	dead_close,
189296465Sdelphij	.d_read =	dead_read,
190296465Sdelphij	.d_write =	dead_write,
191296465Sdelphij	.d_ioctl =	dead_ioctl,
192296465Sdelphij	.d_poll =	dead_poll,
193296465Sdelphij	.d_mmap =	dead_mmap,
194296465Sdelphij	.d_strategy =	dead_strategy,
195296465Sdelphij	.d_name =	"dead",
196296465Sdelphij	.d_dump =	dead_dump,
197296465Sdelphij	.d_kqfilter =	dead_kqfilter
198296465Sdelphij};
199296465Sdelphij
200296465Sdelphij/* Default methods if driver does not specify method */
201296465Sdelphij
202296465Sdelphij#define null_open	(d_open_t *)nullop
203296465Sdelphij#define null_close	(d_close_t *)nullop
204296465Sdelphij#define no_read		(d_read_t *)enodev
205296465Sdelphij#define no_write	(d_write_t *)enodev
206296465Sdelphij#define no_ioctl	(d_ioctl_t *)enodev
207296465Sdelphij#define no_mmap		(d_mmap_t *)enodev
208296465Sdelphij#define no_kqfilter	(d_kqfilter_t *)enodev
20955714Skris
210160814Ssimonstatic void
21155714Skrisno_strategy(struct bio *bp)
212296465Sdelphij{
213296465Sdelphij
21455714Skris	biofinish(bp, NULL, ENODEV);
21555714Skris}
216296465Sdelphij
217296465Sdelphijstatic int
218296465Sdelphijno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
219296465Sdelphij{
220296465Sdelphij	/*
221296465Sdelphij	 * Return true for read/write.  If the user asked for something
222296465Sdelphij	 * special, return POLLNVAL, so that clients have a way of
22355714Skris	 * determining reliably whether or not the extended
224296465Sdelphij	 * functionality is present without hard-coding knowledge
225296465Sdelphij	 * of specific filesystem implementations.
226296465Sdelphij	 * Stay in sync with vop_nopoll().
227296465Sdelphij	 */
228296465Sdelphij	if (events & ~POLLSTANDARD)
229296465Sdelphij		return (POLLNVAL);
230296465Sdelphij
231296465Sdelphij	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
232296465Sdelphij}
233296465Sdelphij
234296465Sdelphij#define no_dump		(dumper_t *)enodev
235296465Sdelphij
236296465Sdelphijstatic int
237296465Sdelphijgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
238296465Sdelphij{
239296465Sdelphij	int retval;
240296465Sdelphij
241296465Sdelphij	mtx_lock(&Giant);
242296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
243296465Sdelphij	    d_open(dev, oflags, devtype, td);
244296465Sdelphij	mtx_unlock(&Giant);
245296465Sdelphij	return (retval);
246296465Sdelphij}
247296465Sdelphij
248296465Sdelphijstatic int
249296465Sdelphijgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
250296465Sdelphij{
251296465Sdelphij	int retval;
252296465Sdelphij
253296465Sdelphij	mtx_lock(&Giant);
254296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
255296465Sdelphij	    d_fdopen(dev, oflags, td, fdidx);
256296465Sdelphij	mtx_unlock(&Giant);
257296465Sdelphij	return (retval);
258296465Sdelphij}
259296465Sdelphij
260296465Sdelphijstatic int
261296465Sdelphijgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
262296465Sdelphij{
263296465Sdelphij	int retval;
264296465Sdelphij
265296465Sdelphij	mtx_lock(&Giant);
266296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
267296465Sdelphij	    d_close(dev, fflag, devtype, td);
268296465Sdelphij	mtx_unlock(&Giant);
269296465Sdelphij	return (retval);
270296465Sdelphij}
271296465Sdelphij
272296465Sdelphijstatic void
273296465Sdelphijgiant_strategy(struct bio *bp)
274296465Sdelphij{
275296465Sdelphij
276296465Sdelphij	mtx_lock(&Giant);
277296465Sdelphij	bp->bio_dev->si_devsw->d_gianttrick->
278296465Sdelphij	    d_strategy(bp);
279296465Sdelphij	mtx_unlock(&Giant);
280296465Sdelphij}
281296465Sdelphij
282296465Sdelphijstatic int
28355714Skrisgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
284160814Ssimon{
28555714Skris	int retval;
286296465Sdelphij
287296465Sdelphij	mtx_lock(&Giant);
288296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
289296465Sdelphij	    d_ioctl(dev, cmd, data, fflag, td);
29055714Skris	mtx_unlock(&Giant);
291296465Sdelphij	return (retval);
292296465Sdelphij}
29355714Skris
294296465Sdelphijstatic int
295296465Sdelphijgiant_read(struct cdev *dev, struct uio *uio, int ioflag)
29655714Skris{
297296465Sdelphij	int retval;
298296465Sdelphij
29955714Skris	mtx_lock(&Giant);
300296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
30159191Skris	    d_read(dev, uio, ioflag);
30255714Skris	mtx_unlock(&Giant);
303296465Sdelphij	return (retval);
304296465Sdelphij}
305296465Sdelphij
306296465Sdelphijstatic int
307296465Sdelphijgiant_write(struct cdev *dev, struct uio *uio, int ioflag)
308296465Sdelphij{
309296465Sdelphij	int retval;
310296465Sdelphij
311296465Sdelphij	mtx_lock(&Giant);
312296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
313296465Sdelphij		d_write(dev, uio, ioflag);
314296465Sdelphij	mtx_unlock(&Giant);
315296465Sdelphij	return (retval);
316296465Sdelphij}
317296465Sdelphij
318296465Sdelphijstatic int
319296465Sdelphijgiant_poll(struct cdev *dev, int events, struct thread *td)
320296465Sdelphij{
321296465Sdelphij	int retval;
322296465Sdelphij
323296465Sdelphij	mtx_lock(&Giant);
324296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
325296465Sdelphij	    d_poll(dev, events, td);
326296465Sdelphij	mtx_unlock(&Giant);
327296465Sdelphij	return (retval);
328296465Sdelphij}
329296465Sdelphij
33068651Skrisstatic int
331296465Sdelphijgiant_kqfilter(struct cdev *dev, struct knote *kn)
332296465Sdelphij{
333296465Sdelphij	int retval;
334296465Sdelphij
33555714Skris	mtx_lock(&Giant);
336296465Sdelphij	retval = dev->si_devsw->d_gianttrick->
337296465Sdelphij	    d_kqfilter(dev, kn);
338296465Sdelphij	mtx_unlock(&Giant);
339296465Sdelphij	return (retval);
340296465Sdelphij}
341296465Sdelphij
342296465Sdelphijstatic int
343296465Sdelphijgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
344109998Smarkm{
345296465Sdelphij	int retval;
346296465Sdelphij
347296465Sdelphij	mtx_lock(&Giant);
348160814Ssimon	retval = dev->si_devsw->d_gianttrick->
349296465Sdelphij	    d_mmap(dev, offset, paddr, nprot);
350160814Ssimon	mtx_unlock(&Giant);
35155714Skris	return (retval);
352296465Sdelphij}
35355714Skris
35468651Skris
35568651Skris/*
356127128Snectar * struct cdev * and u_dev_t primitives
357296465Sdelphij */
358127128Snectar
359296465Sdelphijint
360127128Snectarminor(struct cdev *x)
361296465Sdelphij{
362127128Snectar	if (x == NULL)
363296465Sdelphij		return NODEV;
364127128Snectar	return(x->si_drv0 & MAXMINOR);
365296465Sdelphij}
366127128Snectar
367296465Sdelphijint
368160814Ssimondev2unit(struct cdev *x)
369296465Sdelphij{
370160814Ssimon
371296465Sdelphij	if (x == NULL)
372160814Ssimon		return NODEV;
373296465Sdelphij	return (minor2unit(minor(x)));
374160814Ssimon}
375296465Sdelphij
376160814Ssimonu_int
377296465Sdelphijminor2unit(u_int _minor)
378160814Ssimon{
379296465Sdelphij
38068651Skris	KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
381205128Ssimon	return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
382296465Sdelphij}
383205128Ssimon
384296465Sdelphijint
385296465Sdelphijunit2minor(int unit)
386296465Sdelphij{
387296465Sdelphij
388296465Sdelphij	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
389160814Ssimon	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
390160814Ssimon}
391296465Sdelphij
392296465Sdelphijstatic struct cdev *
393296465Sdelphijnewdev(struct cdevsw *csw, int y, struct cdev *si)
394296465Sdelphij{
395160814Ssimon	struct cdev *si2;
39668651Skris	dev_t	udev;
397296465Sdelphij
398296465Sdelphij	mtx_assert(&devmtx, MA_OWNED);
399296465Sdelphij	udev = y;
400296465Sdelphij	LIST_FOREACH(si2, &csw->d_devs, si_list) {
401296465Sdelphij		if (si2->si_drv0 == udev) {
40255714Skris			devfs_free(si);
40355714Skris			return (si2);
404296465Sdelphij		}
40555714Skris	}
40655714Skris	si->si_drv0 = udev;
407160814Ssimon	si->si_devsw = csw;
408109998Smarkm	LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
409109998Smarkm	return (si);
410160814Ssimon}
411109998Smarkm
41259191Skrisint
41368651Skrisuminor(dev_t dev)
41468651Skris{
41568651Skris	return (dev & MAXMINOR);
41659191Skris}
417109998Smarkm
418296465Sdelphijint
41968651Skrisumajor(dev_t dev)
42055714Skris{
42155714Skris	return ((dev & ~MAXMINOR) >> 8);
42255714Skris}
42355714Skris
42455714Skrisstatic void
42555714Skrisfini_cdevsw(struct cdevsw *devsw)
42655714Skris{
42755714Skris	struct cdevsw *gt;
42855714Skris
42955714Skris	if (devsw->d_gianttrick != NULL) {
430296465Sdelphij		gt = devsw->d_gianttrick;
431296465Sdelphij		memcpy(devsw, gt, sizeof *devsw);
43255714Skris		free(gt, M_DEVT);
43355714Skris		devsw->d_gianttrick = NULL;
434296465Sdelphij	}
43555714Skris	devsw->d_flags &= ~D_INIT;
436296465Sdelphij}
43755714Skris
43855714Skrisstatic void
43959191Skrisprep_cdevsw(struct cdevsw *devsw)
440296465Sdelphij{
44155714Skris	struct cdevsw *dsw2;
44255714Skris
44355714Skris	if (devsw->d_flags & D_NEEDGIANT)
44455714Skris		dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
44555714Skris	else
446296465Sdelphij		dsw2 = NULL;
44755714Skris	dev_lock();
448296465Sdelphij
44955714Skris	if (devsw->d_version != D_VERSION_01) {
450296465Sdelphij		printf(
451296465Sdelphij		    "WARNING: Device driver \"%s\" has wrong version %s\n",
452296465Sdelphij		    devsw->d_name, "and is disabled.  Recompile KLD module.");
453296465Sdelphij		devsw->d_open = dead_open;
45455714Skris		devsw->d_close = dead_close;
45555714Skris		devsw->d_read = dead_read;
456296465Sdelphij		devsw->d_write = dead_write;
457296465Sdelphij		devsw->d_ioctl = dead_ioctl;
458296465Sdelphij		devsw->d_poll = dead_poll;
459296465Sdelphij		devsw->d_mmap = dead_mmap;
460296465Sdelphij		devsw->d_strategy = dead_strategy;
46155714Skris		devsw->d_dump = dead_dump;
462296465Sdelphij		devsw->d_kqfilter = dead_kqfilter;
463296465Sdelphij	}
464296465Sdelphij
465296465Sdelphij	if (devsw->d_flags & D_TTY) {
466296465Sdelphij		if (devsw->d_ioctl == NULL)	devsw->d_ioctl = ttyioctl;
467296465Sdelphij		if (devsw->d_read == NULL)	devsw->d_read = ttyread;
468296465Sdelphij		if (devsw->d_write == NULL)	devsw->d_write = ttywrite;
469296465Sdelphij		if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = ttykqfilter;
470296465Sdelphij		if (devsw->d_poll == NULL)	devsw->d_poll = ttypoll;
471296465Sdelphij	}
47255714Skris
47359191Skris	if (devsw->d_flags & D_NEEDGIANT) {
474296465Sdelphij		if (devsw->d_gianttrick == NULL) {
475296465Sdelphij			memcpy(dsw2, devsw, sizeof *dsw2);
476296465Sdelphij			devsw->d_gianttrick = dsw2;
47759191Skris		} else
47859191Skris			free(dsw2, M_DEVT);
47959191Skris	}
480296465Sdelphij
481160814Ssimon#define FIXUP(member, noop, giant) 				\
482160814Ssimon	do {							\
483296465Sdelphij		if (devsw->member == NULL) {			\
48489837Skris			devsw->member = noop;			\
485296465Sdelphij		} else if (devsw->d_flags & D_NEEDGIANT)	\
486296465Sdelphij			devsw->member = giant;			\
487160814Ssimon		}						\
488160814Ssimon	while (0)
48955714Skris
490160814Ssimon	FIXUP(d_open,		null_open,	giant_open);
491160814Ssimon	FIXUP(d_fdopen,		NULL,		giant_fdopen);
492160814Ssimon	FIXUP(d_close,		null_close,	giant_close);
493160814Ssimon	FIXUP(d_read,		no_read,	giant_read);
494160814Ssimon	FIXUP(d_write,		no_write,	giant_write);
495160814Ssimon	FIXUP(d_ioctl,		no_ioctl,	giant_ioctl);
496160814Ssimon	FIXUP(d_poll,		no_poll,	giant_poll);
497160814Ssimon	FIXUP(d_mmap,		no_mmap,	giant_mmap);
498160814Ssimon	FIXUP(d_strategy,	no_strategy,	giant_strategy);
499296465Sdelphij	FIXUP(d_kqfilter,	no_kqfilter,	giant_kqfilter);
500296465Sdelphij
501296465Sdelphij	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
502160814Ssimon
503296465Sdelphij	LIST_INIT(&devsw->d_devs);
504296465Sdelphij
505160814Ssimon	devsw->d_flags |= D_INIT;
506296465Sdelphij
507160814Ssimon	dev_unlock();
508160814Ssimon}
509160814Ssimon
510160814Ssimonstatic struct cdev *
511160814Ssimonmake_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
512160814Ssimon    gid_t gid, int mode, const char *fmt, va_list ap)
513296465Sdelphij{
514296465Sdelphij	struct cdev *dev;
515296465Sdelphij	int i;
516160814Ssimon
517160814Ssimon	KASSERT((minornr & ~MAXMINOR) == 0,
518160814Ssimon	    ("Invalid minor (0x%x) in make_dev", minornr));
519160814Ssimon
520160814Ssimon	if (!(devsw->d_flags & D_INIT))
521160814Ssimon		prep_cdevsw(devsw);
522160814Ssimon	dev = devfs_alloc();
523296465Sdelphij	dev_lock();
524296465Sdelphij	dev = newdev(devsw, minornr, dev);
525160814Ssimon	if (dev->si_flags & SI_CHEAPCLONE &&
526160814Ssimon	    dev->si_flags & SI_NAMED) {
527160814Ssimon		/*
528160814Ssimon		 * This is allowed as it removes races and generally
529296465Sdelphij		 * simplifies cloning devices.
530296465Sdelphij		 * XXX: still ??
531160814Ssimon		 */
532296465Sdelphij		dev_unlock();
533296465Sdelphij		return (dev);
534296465Sdelphij	}
535160814Ssimon	KASSERT(!(dev->si_flags & SI_NAMED),
536296465Sdelphij	    ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
537296465Sdelphij	    devsw->d_name, minor(dev), devtoname(dev)));
538296465Sdelphij
539160814Ssimon	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
540160814Ssimon	if (i > (sizeof dev->__si_namebuf - 1)) {
541160814Ssimon		printf("WARNING: Device name truncated! (%s)\n",
542296465Sdelphij		    dev->__si_namebuf);
543296465Sdelphij	}
544160814Ssimon
545160814Ssimon	dev->si_flags |= SI_NAMED;
546160814Ssimon	if (cr != NULL)
547296465Sdelphij		dev->si_cred = crhold(cr);
548296465Sdelphij	else
549296465Sdelphij		dev->si_cred = NULL;
550296465Sdelphij	dev->si_uid = uid;
551296465Sdelphij	dev->si_gid = gid;
552160814Ssimon	dev->si_mode = mode;
55355714Skris
55455714Skris	devfs_create(dev);
55555714Skris	dev_unlock();
55655714Skris	return (dev);
557}
558
559struct cdev *
560make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode,
561    const char *fmt, ...)
562{
563	struct cdev *dev;
564	va_list ap;
565
566	va_start(ap, fmt);
567	dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap);
568	va_end(ap);
569	return (dev);
570}
571
572struct cdev *
573make_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
574    gid_t gid, int mode, const char *fmt, ...)
575{
576	struct cdev *dev;
577	va_list ap;
578
579	va_start(ap, fmt);
580	dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap);
581	va_end(ap);
582
583	return (dev);
584}
585
586static void
587dev_dependsl(struct cdev *pdev, struct cdev *cdev)
588{
589
590	cdev->si_parent = pdev;
591	cdev->si_flags |= SI_CHILD;
592	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
593}
594
595
596void
597dev_depends(struct cdev *pdev, struct cdev *cdev)
598{
599
600	dev_lock();
601	dev_dependsl(pdev, cdev);
602	dev_unlock();
603}
604
605struct cdev *
606make_dev_alias(struct cdev *pdev, const char *fmt, ...)
607{
608	struct cdev *dev;
609	va_list ap;
610	int i;
611
612	dev = devfs_alloc();
613	dev_lock();
614	dev->si_flags |= SI_ALIAS;
615	dev->si_flags |= SI_NAMED;
616	va_start(ap, fmt);
617	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
618	if (i > (sizeof dev->__si_namebuf - 1)) {
619		printf("WARNING: Device name truncated! (%s)\n",
620		    dev->__si_namebuf);
621	}
622	va_end(ap);
623
624	devfs_create(dev);
625	dev_unlock();
626	dev_depends(pdev, dev);
627	return (dev);
628}
629
630static void
631destroy_devl(struct cdev *dev)
632{
633	struct cdevsw *csw;
634
635	mtx_assert(&devmtx, MA_OWNED);
636	KASSERT(dev->si_flags & SI_NAMED,
637	    ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
638
639	devfs_destroy(dev);
640
641	/* Remove name marking */
642	dev->si_flags &= ~SI_NAMED;
643
644	/* If we are a child, remove us from the parents list */
645	if (dev->si_flags & SI_CHILD) {
646		LIST_REMOVE(dev, si_siblings);
647		dev->si_flags &= ~SI_CHILD;
648	}
649
650	/* Kill our children */
651	while (!LIST_EMPTY(&dev->si_children))
652		destroy_devl(LIST_FIRST(&dev->si_children));
653
654	/* Remove from clone list */
655	if (dev->si_flags & SI_CLONELIST) {
656		LIST_REMOVE(dev, si_clone);
657		dev->si_flags &= ~SI_CLONELIST;
658	}
659
660	csw = dev->si_devsw;
661	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
662	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
663		printf("Purging %lu threads from %s\n",
664		    dev->si_threadcount, devtoname(dev));
665		csw->d_purge(dev);
666		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
667	}
668	if (csw != NULL && csw->d_purge != NULL)
669		printf("All threads purged from %s\n", devtoname(dev));
670
671	dev->si_drv1 = 0;
672	dev->si_drv2 = 0;
673	bzero(&dev->__si_u, sizeof(dev->__si_u));
674
675	if (!(dev->si_flags & SI_ALIAS)) {
676		/* Remove from cdevsw list */
677		LIST_REMOVE(dev, si_list);
678
679		/* If cdevsw has no more struct cdev *'s, clean it */
680		if (LIST_EMPTY(&csw->d_devs))
681			fini_cdevsw(csw);
682	}
683	dev->si_flags &= ~SI_ALIAS;
684
685	if (dev->si_refcount > 0) {
686		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
687	} else {
688		devfs_free(dev);
689	}
690}
691
692void
693destroy_dev(struct cdev *dev)
694{
695
696	dev_lock();
697	destroy_devl(dev);
698	dev_unlock();
699}
700
701const char *
702devtoname(struct cdev *dev)
703{
704	char *p;
705	struct cdevsw *csw;
706	int mynor;
707
708	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
709		p = dev->si_name;
710		csw = dev_refthread(dev);
711		if (csw != NULL) {
712			sprintf(p, "(%s)", csw->d_name);
713			dev_relthread(dev);
714		}
715		p += strlen(p);
716		mynor = minor(dev);
717		if (mynor < 0 || mynor > 255)
718			sprintf(p, "/%#x", (u_int)mynor);
719		else
720			sprintf(p, "/%d", mynor);
721	}
722	return (dev->si_name);
723}
724
725int
726dev_stdclone(char *name, char **namep, const char *stem, int *unit)
727{
728	int u, i;
729
730	i = strlen(stem);
731	if (bcmp(stem, name, i) != 0)
732		return (0);
733	if (!isdigit(name[i]))
734		return (0);
735	u = 0;
736	if (name[i] == '0' && isdigit(name[i+1]))
737		return (0);
738	while (isdigit(name[i])) {
739		u *= 10;
740		u += name[i++] - '0';
741	}
742	if (u > 0xffffff)
743		return (0);
744	*unit = u;
745	if (namep)
746		*namep = &name[i];
747	if (name[i])
748		return (2);
749	return (1);
750}
751
752/*
753 * Helper functions for cloning device drivers.
754 *
755 * The objective here is to make it unnecessary for the device drivers to
756 * use rman or similar to manage their unit number space.  Due to the way
757 * we do "on-demand" devices, using rman or other "private" methods
758 * will be very tricky to lock down properly once we lock down this file.
759 *
760 * Instead we give the drivers these routines which puts the struct cdev *'s
761 * that are to be managed on their own list, and gives the driver the ability
762 * to ask for the first free unit number or a given specified unit number.
763 *
764 * In addition these routines support paired devices (pty, nmdm and similar)
765 * by respecting a number of "flag" bits in the minor number.
766 *
767 */
768
769struct clonedevs {
770	LIST_HEAD(,cdev)	head;
771};
772
773void
774clone_setup(struct clonedevs **cdp)
775{
776
777	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
778	LIST_INIT(&(*cdp)->head);
779}
780
781int
782clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
783{
784	struct clonedevs *cd;
785	struct cdev *dev, *ndev, *dl, *de;
786	int unit, low, u;
787
788	KASSERT(*cdp != NULL,
789	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
790	KASSERT(!(extra & CLONE_UNITMASK),
791	    ("Illegal extra bits (0x%x) in clone_create", extra));
792	KASSERT(*up <= CLONE_UNITMASK,
793	    ("Too high unit (0x%x) in clone_create", *up));
794
795	if (!(csw->d_flags & D_INIT))
796		prep_cdevsw(csw);
797
798	/*
799	 * Search the list for a lot of things in one go:
800	 *   A preexisting match is returned immediately.
801	 *   The lowest free unit number if we are passed -1, and the place
802	 *	 in the list where we should insert that new element.
803	 *   The place to insert a specified unit number, if applicable
804	 *       the end of the list.
805	 */
806	unit = *up;
807	ndev = devfs_alloc();
808	dev_lock();
809	low = extra;
810	de = dl = NULL;
811	cd = *cdp;
812	LIST_FOREACH(dev, &cd->head, si_clone) {
813		KASSERT(dev->si_flags & SI_CLONELIST,
814		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
815		u = dev2unit(dev);
816		if (u == (unit | extra)) {
817			*dp = dev;
818			devfs_free(ndev);
819			dev_unlock();
820			return (0);
821		}
822		if (unit == -1 && u == low) {
823			low++;
824			de = dev;
825			continue;
826		} else if (u < (unit | extra)) {
827			de = dev;
828			continue;
829		} else if (u > (unit | extra)) {
830			dl = dev;
831			break;
832		}
833	}
834	if (unit == -1)
835		unit = low & CLONE_UNITMASK;
836	dev = newdev(csw, unit2minor(unit | extra), ndev);
837	if (dev->si_flags & SI_CLONELIST) {
838		printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
839		printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
840		LIST_FOREACH(dev, &cd->head, si_clone) {
841			printf("\t%p %s\n", dev, dev->si_name);
842		}
843		panic("foo");
844	}
845	KASSERT(!(dev->si_flags & SI_CLONELIST),
846	    ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
847	if (dl != NULL)
848		LIST_INSERT_BEFORE(dl, dev, si_clone);
849	else if (de != NULL)
850		LIST_INSERT_AFTER(de, dev, si_clone);
851	else
852		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
853	dev->si_flags |= SI_CLONELIST;
854	*up = unit;
855	dev_unlock();
856	return (1);
857}
858
859/*
860 * Kill everything still on the list.  The driver should already have
861 * disposed of any softc hung of the struct cdev *'s at this time.
862 */
863void
864clone_cleanup(struct clonedevs **cdp)
865{
866	struct cdev *dev, *tdev;
867	struct clonedevs *cd;
868
869	cd = *cdp;
870	if (cd == NULL)
871		return;
872	dev_lock();
873	LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
874		KASSERT(dev->si_flags & SI_CLONELIST,
875		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
876		KASSERT(dev->si_flags & SI_NAMED,
877		    ("Driver has goofed in cloning underways udev %x", dev->si_drv0));
878		destroy_devl(dev);
879	}
880	dev_unlock();
881	free(cd, M_DEVBUF);
882	*cdp = NULL;
883}
884