FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_conf.c
1 /*-
2 * Copyright (c) 1999-2002 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: releng/6.0/sys/kern/kern_conf.c 151001 2005-10-06 15:17:41Z phk $");
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/bio.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/module.h>
37 #include <sys/malloc.h>
38 #include <sys/conf.h>
39 #include <sys/vnode.h>
40 #include <sys/queue.h>
41 #include <sys/poll.h>
42 #include <sys/ctype.h>
43 #include <sys/tty.h>
44 #include <sys/ucred.h>
45 #include <machine/stdarg.h>
46
47 #include <fs/devfs/devfs_int.h>
48
49 static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
50
51 struct mtx devmtx;
52 static void destroy_devl(struct cdev *dev);
53 static struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr,
54 struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
55 va_list ap);
56
57 void
58 dev_lock(void)
59 {
60 if (!mtx_initialized(&devmtx))
61 mtx_init(&devmtx, "cdev", NULL, MTX_DEF);
62 mtx_lock(&devmtx);
63 }
64
65 void
66 dev_unlock(void)
67 {
68
69 mtx_unlock(&devmtx);
70 }
71
72 void
73 dev_ref(struct cdev *dev)
74 {
75
76 mtx_assert(&devmtx, MA_NOTOWNED);
77 mtx_lock(&devmtx);
78 dev->si_refcount++;
79 mtx_unlock(&devmtx);
80 }
81
82 void
83 dev_refl(struct cdev *dev)
84 {
85
86 mtx_assert(&devmtx, MA_OWNED);
87 dev->si_refcount++;
88 }
89
90 void
91 dev_rel(struct cdev *dev)
92 {
93 int flag = 0;
94
95 mtx_assert(&devmtx, MA_NOTOWNED);
96 dev_lock();
97 dev->si_refcount--;
98 KASSERT(dev->si_refcount >= 0,
99 ("dev_rel(%s) gave negative count", devtoname(dev)));
100 #if 0
101 if (dev->si_usecount == 0 &&
102 (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
103 ;
104 else
105 #endif
106 if (dev->si_devsw == NULL && dev->si_refcount == 0) {
107 LIST_REMOVE(dev, si_list);
108 flag = 1;
109 }
110 dev_unlock();
111 if (flag)
112 devfs_free(dev);
113 }
114
115 struct cdevsw *
116 dev_refthread(struct cdev *dev)
117 {
118 struct cdevsw *csw;
119
120 mtx_assert(&devmtx, MA_NOTOWNED);
121 dev_lock();
122 csw = dev->si_devsw;
123 if (csw != NULL)
124 dev->si_threadcount++;
125 dev_unlock();
126 return (csw);
127 }
128
129 void
130 dev_relthread(struct cdev *dev)
131 {
132
133 mtx_assert(&devmtx, MA_NOTOWNED);
134 dev_lock();
135 dev->si_threadcount--;
136 dev_unlock();
137 }
138
139 int
140 nullop(void)
141 {
142
143 return (0);
144 }
145
146 int
147 eopnotsupp(void)
148 {
149
150 return (EOPNOTSUPP);
151 }
152
153 static int
154 enxio(void)
155 {
156 return (ENXIO);
157 }
158
159 static int
160 enodev(void)
161 {
162 return (ENODEV);
163 }
164
165 /* Define a dead_cdevsw for use when devices leave unexpectedly. */
166
167 #define dead_open (d_open_t *)enxio
168 #define dead_close (d_close_t *)enxio
169 #define dead_read (d_read_t *)enxio
170 #define dead_write (d_write_t *)enxio
171 #define dead_ioctl (d_ioctl_t *)enxio
172 #define dead_poll (d_poll_t *)enodev
173 #define dead_mmap (d_mmap_t *)enodev
174
175 static void
176 dead_strategy(struct bio *bp)
177 {
178
179 biofinish(bp, NULL, ENXIO);
180 }
181
182 #define dead_dump (dumper_t *)enxio
183 #define dead_kqfilter (d_kqfilter_t *)enxio
184
185 static struct cdevsw dead_cdevsw = {
186 .d_version = D_VERSION,
187 .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
188 .d_open = dead_open,
189 .d_close = dead_close,
190 .d_read = dead_read,
191 .d_write = dead_write,
192 .d_ioctl = dead_ioctl,
193 .d_poll = dead_poll,
194 .d_mmap = dead_mmap,
195 .d_strategy = dead_strategy,
196 .d_name = "dead",
197 .d_dump = dead_dump,
198 .d_kqfilter = dead_kqfilter
199 };
200
201 /* Default methods if driver does not specify method */
202
203 #define null_open (d_open_t *)nullop
204 #define null_close (d_close_t *)nullop
205 #define no_read (d_read_t *)enodev
206 #define no_write (d_write_t *)enodev
207 #define no_ioctl (d_ioctl_t *)enodev
208 #define no_mmap (d_mmap_t *)enodev
209 #define no_kqfilter (d_kqfilter_t *)enodev
210
211 static void
212 no_strategy(struct bio *bp)
213 {
214
215 biofinish(bp, NULL, ENODEV);
216 }
217
218 static int
219 no_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
220 {
221 /*
222 * Return true for read/write. If the user asked for something
223 * special, return POLLNVAL, so that clients have a way of
224 * determining reliably whether or not the extended
225 * functionality is present without hard-coding knowledge
226 * of specific filesystem implementations.
227 * Stay in sync with vop_nopoll().
228 */
229 if (events & ~POLLSTANDARD)
230 return (POLLNVAL);
231
232 return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
233 }
234
235 #define no_dump (dumper_t *)enodev
236
237 static int
238 giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
239 {
240 int retval;
241
242 mtx_lock(&Giant);
243 retval = dev->si_devsw->d_gianttrick->
244 d_open(dev, oflags, devtype, td);
245 mtx_unlock(&Giant);
246 return (retval);
247 }
248
249 static int
250 giant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
251 {
252 int retval;
253
254 mtx_lock(&Giant);
255 retval = dev->si_devsw->d_gianttrick->
256 d_fdopen(dev, oflags, td, fdidx);
257 mtx_unlock(&Giant);
258 return (retval);
259 }
260
261 static int
262 giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
263 {
264 int retval;
265
266 mtx_lock(&Giant);
267 retval = dev->si_devsw->d_gianttrick->
268 d_close(dev, fflag, devtype, td);
269 mtx_unlock(&Giant);
270 return (retval);
271 }
272
273 static void
274 giant_strategy(struct bio *bp)
275 {
276
277 mtx_lock(&Giant);
278 bp->bio_dev->si_devsw->d_gianttrick->
279 d_strategy(bp);
280 mtx_unlock(&Giant);
281 }
282
283 static int
284 giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
285 {
286 int retval;
287
288 mtx_lock(&Giant);
289 retval = dev->si_devsw->d_gianttrick->
290 d_ioctl(dev, cmd, data, fflag, td);
291 mtx_unlock(&Giant);
292 return (retval);
293 }
294
295 static int
296 giant_read(struct cdev *dev, struct uio *uio, int ioflag)
297 {
298 int retval;
299
300 mtx_lock(&Giant);
301 retval = dev->si_devsw->d_gianttrick->
302 d_read(dev, uio, ioflag);
303 mtx_unlock(&Giant);
304 return (retval);
305 }
306
307 static int
308 giant_write(struct cdev *dev, struct uio *uio, int ioflag)
309 {
310 int retval;
311
312 mtx_lock(&Giant);
313 retval = dev->si_devsw->d_gianttrick->
314 d_write(dev, uio, ioflag);
315 mtx_unlock(&Giant);
316 return (retval);
317 }
318
319 static int
320 giant_poll(struct cdev *dev, int events, struct thread *td)
321 {
322 int retval;
323
324 mtx_lock(&Giant);
325 retval = dev->si_devsw->d_gianttrick->
326 d_poll(dev, events, td);
327 mtx_unlock(&Giant);
328 return (retval);
329 }
330
331 static int
332 giant_kqfilter(struct cdev *dev, struct knote *kn)
333 {
334 int retval;
335
336 mtx_lock(&Giant);
337 retval = dev->si_devsw->d_gianttrick->
338 d_kqfilter(dev, kn);
339 mtx_unlock(&Giant);
340 return (retval);
341 }
342
343 static int
344 giant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
345 {
346 int retval;
347
348 mtx_lock(&Giant);
349 retval = dev->si_devsw->d_gianttrick->
350 d_mmap(dev, offset, paddr, nprot);
351 mtx_unlock(&Giant);
352 return (retval);
353 }
354
355
356 /*
357 * struct cdev * and u_dev_t primitives
358 */
359
360 int
361 minor(struct cdev *x)
362 {
363 if (x == NULL)
364 return NODEV;
365 return(x->si_drv0 & MAXMINOR);
366 }
367
368 int
369 dev2unit(struct cdev *x)
370 {
371
372 if (x == NULL)
373 return NODEV;
374 return (minor2unit(minor(x)));
375 }
376
377 u_int
378 minor2unit(u_int _minor)
379 {
380
381 KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
382 return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
383 }
384
385 int
386 unit2minor(int unit)
387 {
388
389 KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
390 return ((unit & 0xff) | ((unit << 8) & ~0xffff));
391 }
392
393 static struct cdev *
394 newdev(struct cdevsw *csw, int y, struct cdev *si)
395 {
396 struct cdev *si2;
397 dev_t udev;
398
399 mtx_assert(&devmtx, MA_OWNED);
400 udev = y;
401 LIST_FOREACH(si2, &csw->d_devs, si_list) {
402 if (si2->si_drv0 == udev) {
403 devfs_free(si);
404 return (si2);
405 }
406 }
407 si->si_drv0 = udev;
408 si->si_devsw = csw;
409 LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
410 return (si);
411 }
412
413 int
414 uminor(dev_t dev)
415 {
416 return (dev & MAXMINOR);
417 }
418
419 int
420 umajor(dev_t dev)
421 {
422 return ((dev & ~MAXMINOR) >> 8);
423 }
424
425 static void
426 fini_cdevsw(struct cdevsw *devsw)
427 {
428 struct cdevsw *gt;
429
430 if (devsw->d_gianttrick != NULL) {
431 gt = devsw->d_gianttrick;
432 memcpy(devsw, gt, sizeof *devsw);
433 free(gt, M_DEVT);
434 devsw->d_gianttrick = NULL;
435 }
436 devsw->d_flags &= ~D_INIT;
437 }
438
439 static void
440 prep_cdevsw(struct cdevsw *devsw)
441 {
442 struct cdevsw *dsw2;
443
444 if (devsw->d_flags & D_NEEDGIANT)
445 dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
446 else
447 dsw2 = NULL;
448 dev_lock();
449
450 if (devsw->d_version != D_VERSION_01) {
451 printf(
452 "WARNING: Device driver \"%s\" has wrong version %s\n",
453 devsw->d_name, "and is disabled. Recompile KLD module.");
454 devsw->d_open = dead_open;
455 devsw->d_close = dead_close;
456 devsw->d_read = dead_read;
457 devsw->d_write = dead_write;
458 devsw->d_ioctl = dead_ioctl;
459 devsw->d_poll = dead_poll;
460 devsw->d_mmap = dead_mmap;
461 devsw->d_strategy = dead_strategy;
462 devsw->d_dump = dead_dump;
463 devsw->d_kqfilter = dead_kqfilter;
464 }
465
466 if (devsw->d_flags & D_TTY) {
467 if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl;
468 if (devsw->d_read == NULL) devsw->d_read = ttyread;
469 if (devsw->d_write == NULL) devsw->d_write = ttywrite;
470 if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter;
471 if (devsw->d_poll == NULL) devsw->d_poll = ttypoll;
472 }
473
474 if (devsw->d_flags & D_NEEDGIANT) {
475 if (devsw->d_gianttrick == NULL) {
476 memcpy(dsw2, devsw, sizeof *dsw2);
477 devsw->d_gianttrick = dsw2;
478 } else
479 free(dsw2, M_DEVT);
480 }
481
482 #define FIXUP(member, noop, giant) \
483 do { \
484 if (devsw->member == NULL) { \
485 devsw->member = noop; \
486 } else if (devsw->d_flags & D_NEEDGIANT) \
487 devsw->member = giant; \
488 } \
489 while (0)
490
491 FIXUP(d_open, null_open, giant_open);
492 FIXUP(d_fdopen, NULL, giant_fdopen);
493 FIXUP(d_close, null_close, giant_close);
494 FIXUP(d_read, no_read, giant_read);
495 FIXUP(d_write, no_write, giant_write);
496 FIXUP(d_ioctl, no_ioctl, giant_ioctl);
497 FIXUP(d_poll, no_poll, giant_poll);
498 FIXUP(d_mmap, no_mmap, giant_mmap);
499 FIXUP(d_strategy, no_strategy, giant_strategy);
500 FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter);
501
502 if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
503
504 LIST_INIT(&devsw->d_devs);
505
506 devsw->d_flags |= D_INIT;
507
508 dev_unlock();
509 }
510
511 static struct cdev *
512 make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
513 gid_t gid, int mode, const char *fmt, va_list ap)
514 {
515 struct cdev *dev;
516 int i;
517
518 KASSERT((minornr & ~MAXMINOR) == 0,
519 ("Invalid minor (0x%x) in make_dev", minornr));
520
521 if (!(devsw->d_flags & D_INIT))
522 prep_cdevsw(devsw);
523 dev = devfs_alloc();
524 dev_lock();
525 dev = newdev(devsw, minornr, dev);
526 if (dev->si_flags & SI_CHEAPCLONE &&
527 dev->si_flags & SI_NAMED) {
528 /*
529 * This is allowed as it removes races and generally
530 * simplifies cloning devices.
531 * XXX: still ??
532 */
533 dev_unlock();
534 return (dev);
535 }
536 KASSERT(!(dev->si_flags & SI_NAMED),
537 ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
538 devsw->d_name, minor(dev), devtoname(dev)));
539
540 i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
541 if (i > (sizeof dev->__si_namebuf - 1)) {
542 printf("WARNING: Device name truncated! (%s)\n",
543 dev->__si_namebuf);
544 }
545
546 dev->si_flags |= SI_NAMED;
547 if (cr != NULL)
548 dev->si_cred = crhold(cr);
549 else
550 dev->si_cred = NULL;
551 dev->si_uid = uid;
552 dev->si_gid = gid;
553 dev->si_mode = mode;
554
555 devfs_create(dev);
556 dev_unlock();
557 return (dev);
558 }
559
560 struct cdev *
561 make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode,
562 const char *fmt, ...)
563 {
564 struct cdev *dev;
565 va_list ap;
566
567 va_start(ap, fmt);
568 dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap);
569 va_end(ap);
570 return (dev);
571 }
572
573 struct cdev *
574 make_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
575 gid_t gid, int mode, const char *fmt, ...)
576 {
577 struct cdev *dev;
578 va_list ap;
579
580 va_start(ap, fmt);
581 dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap);
582 va_end(ap);
583
584 return (dev);
585 }
586
587 static void
588 dev_dependsl(struct cdev *pdev, struct cdev *cdev)
589 {
590
591 cdev->si_parent = pdev;
592 cdev->si_flags |= SI_CHILD;
593 LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
594 }
595
596
597 void
598 dev_depends(struct cdev *pdev, struct cdev *cdev)
599 {
600
601 dev_lock();
602 dev_dependsl(pdev, cdev);
603 dev_unlock();
604 }
605
606 struct cdev *
607 make_dev_alias(struct cdev *pdev, const char *fmt, ...)
608 {
609 struct cdev *dev;
610 va_list ap;
611 int i;
612
613 dev = devfs_alloc();
614 dev_lock();
615 dev->si_flags |= SI_ALIAS;
616 dev->si_flags |= SI_NAMED;
617 va_start(ap, fmt);
618 i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
619 if (i > (sizeof dev->__si_namebuf - 1)) {
620 printf("WARNING: Device name truncated! (%s)\n",
621 dev->__si_namebuf);
622 }
623 va_end(ap);
624
625 devfs_create(dev);
626 dev_unlock();
627 dev_depends(pdev, dev);
628 return (dev);
629 }
630
631 static void
632 destroy_devl(struct cdev *dev)
633 {
634 struct cdevsw *csw;
635
636 mtx_assert(&devmtx, MA_OWNED);
637 KASSERT(dev->si_flags & SI_NAMED,
638 ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
639
640 devfs_destroy(dev);
641
642 /* Remove name marking */
643 dev->si_flags &= ~SI_NAMED;
644
645 /* If we are a child, remove us from the parents list */
646 if (dev->si_flags & SI_CHILD) {
647 LIST_REMOVE(dev, si_siblings);
648 dev->si_flags &= ~SI_CHILD;
649 }
650
651 /* Kill our children */
652 while (!LIST_EMPTY(&dev->si_children))
653 destroy_devl(LIST_FIRST(&dev->si_children));
654
655 /* Remove from clone list */
656 if (dev->si_flags & SI_CLONELIST) {
657 LIST_REMOVE(dev, si_clone);
658 dev->si_flags &= ~SI_CLONELIST;
659 }
660
661 csw = dev->si_devsw;
662 dev->si_devsw = NULL; /* already NULL for SI_ALIAS */
663 while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
664 printf("Purging %lu threads from %s\n",
665 dev->si_threadcount, devtoname(dev));
666 csw->d_purge(dev);
667 msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
668 }
669 if (csw != NULL && csw->d_purge != NULL)
670 printf("All threads purged from %s\n", devtoname(dev));
671
672 dev->si_drv1 = 0;
673 dev->si_drv2 = 0;
674 bzero(&dev->__si_u, sizeof(dev->__si_u));
675
676 if (!(dev->si_flags & SI_ALIAS)) {
677 /* Remove from cdevsw list */
678 LIST_REMOVE(dev, si_list);
679
680 /* If cdevsw has no more struct cdev *'s, clean it */
681 if (LIST_EMPTY(&csw->d_devs))
682 fini_cdevsw(csw);
683 }
684 dev->si_flags &= ~SI_ALIAS;
685
686 if (dev->si_refcount > 0) {
687 LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
688 } else {
689 devfs_free(dev);
690 }
691 }
692
693 void
694 destroy_dev(struct cdev *dev)
695 {
696
697 dev_lock();
698 destroy_devl(dev);
699 dev_unlock();
700 }
701
702 const char *
703 devtoname(struct cdev *dev)
704 {
705 char *p;
706 struct cdevsw *csw;
707 int mynor;
708
709 if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
710 p = dev->si_name;
711 csw = dev_refthread(dev);
712 if (csw != NULL) {
713 sprintf(p, "(%s)", csw->d_name);
714 dev_relthread(dev);
715 }
716 p += strlen(p);
717 mynor = minor(dev);
718 if (mynor < 0 || mynor > 255)
719 sprintf(p, "/%#x", (u_int)mynor);
720 else
721 sprintf(p, "/%d", mynor);
722 }
723 return (dev->si_name);
724 }
725
726 int
727 dev_stdclone(char *name, char **namep, const char *stem, int *unit)
728 {
729 int u, i;
730
731 i = strlen(stem);
732 if (bcmp(stem, name, i) != 0)
733 return (0);
734 if (!isdigit(name[i]))
735 return (0);
736 u = 0;
737 if (name[i] == '' && isdigit(name[i+1]))
738 return (0);
739 while (isdigit(name[i])) {
740 u *= 10;
741 u += name[i++] - '';
742 }
743 if (u > 0xffffff)
744 return (0);
745 *unit = u;
746 if (namep)
747 *namep = &name[i];
748 if (name[i])
749 return (2);
750 return (1);
751 }
752
753 /*
754 * Helper functions for cloning device drivers.
755 *
756 * The objective here is to make it unnecessary for the device drivers to
757 * use rman or similar to manage their unit number space. Due to the way
758 * we do "on-demand" devices, using rman or other "private" methods
759 * will be very tricky to lock down properly once we lock down this file.
760 *
761 * Instead we give the drivers these routines which puts the struct cdev *'s
762 * that are to be managed on their own list, and gives the driver the ability
763 * to ask for the first free unit number or a given specified unit number.
764 *
765 * In addition these routines support paired devices (pty, nmdm and similar)
766 * by respecting a number of "flag" bits in the minor number.
767 *
768 */
769
770 struct clonedevs {
771 LIST_HEAD(,cdev) head;
772 };
773
774 void
775 clone_setup(struct clonedevs **cdp)
776 {
777
778 *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
779 LIST_INIT(&(*cdp)->head);
780 }
781
782 int
783 clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
784 {
785 struct clonedevs *cd;
786 struct cdev *dev, *ndev, *dl, *de;
787 int unit, low, u;
788
789 KASSERT(*cdp != NULL,
790 ("clone_setup() not called in driver \"%s\"", csw->d_name));
791 KASSERT(!(extra & CLONE_UNITMASK),
792 ("Illegal extra bits (0x%x) in clone_create", extra));
793 KASSERT(*up <= CLONE_UNITMASK,
794 ("Too high unit (0x%x) in clone_create", *up));
795
796 if (!(csw->d_flags & D_INIT))
797 prep_cdevsw(csw);
798
799 /*
800 * Search the list for a lot of things in one go:
801 * A preexisting match is returned immediately.
802 * The lowest free unit number if we are passed -1, and the place
803 * in the list where we should insert that new element.
804 * The place to insert a specified unit number, if applicable
805 * the end of the list.
806 */
807 unit = *up;
808 ndev = devfs_alloc();
809 dev_lock();
810 low = extra;
811 de = dl = NULL;
812 cd = *cdp;
813 LIST_FOREACH(dev, &cd->head, si_clone) {
814 KASSERT(dev->si_flags & SI_CLONELIST,
815 ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
816 u = dev2unit(dev);
817 if (u == (unit | extra)) {
818 *dp = dev;
819 devfs_free(ndev);
820 dev_unlock();
821 return (0);
822 }
823 if (unit == -1 && u == low) {
824 low++;
825 de = dev;
826 continue;
827 } else if (u < (unit | extra)) {
828 de = dev;
829 continue;
830 } else if (u > (unit | extra)) {
831 dl = dev;
832 break;
833 }
834 }
835 if (unit == -1)
836 unit = low & CLONE_UNITMASK;
837 dev = newdev(csw, unit2minor(unit | extra), ndev);
838 if (dev->si_flags & SI_CLONELIST) {
839 printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
840 printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
841 LIST_FOREACH(dev, &cd->head, si_clone) {
842 printf("\t%p %s\n", dev, dev->si_name);
843 }
844 panic("foo");
845 }
846 KASSERT(!(dev->si_flags & SI_CLONELIST),
847 ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
848 if (dl != NULL)
849 LIST_INSERT_BEFORE(dl, dev, si_clone);
850 else if (de != NULL)
851 LIST_INSERT_AFTER(de, dev, si_clone);
852 else
853 LIST_INSERT_HEAD(&cd->head, dev, si_clone);
854 dev->si_flags |= SI_CLONELIST;
855 *up = unit;
856 dev_unlock();
857 return (1);
858 }
859
860 /*
861 * Kill everything still on the list. The driver should already have
862 * disposed of any softc hung of the struct cdev *'s at this time.
863 */
864 void
865 clone_cleanup(struct clonedevs **cdp)
866 {
867 struct cdev *dev, *tdev;
868 struct clonedevs *cd;
869
870 cd = *cdp;
871 if (cd == NULL)
872 return;
873 dev_lock();
874 LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
875 KASSERT(dev->si_flags & SI_CLONELIST,
876 ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
877 KASSERT(dev->si_flags & SI_NAMED,
878 ("Driver has goofed in cloning underways udev %x", dev->si_drv0));
879 destroy_devl(dev);
880 }
881 dev_unlock();
882 free(cd, M_DEVBUF);
883 *cdp = NULL;
884 }
Cache object: 5699c7441185067b9236022c141a6808
|