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.1/sys/kern/kern_conf.c 158179 2006-04-30 16:44:43Z cvs2svn $");
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
61 mtx_lock(&devmtx);
62 }
63
64 void
65 dev_unlock(void)
66 {
67
68 mtx_unlock(&devmtx);
69 }
70
71 void
72 dev_ref(struct cdev *dev)
73 {
74
75 mtx_assert(&devmtx, MA_NOTOWNED);
76 mtx_lock(&devmtx);
77 dev->si_refcount++;
78 mtx_unlock(&devmtx);
79 }
80
81 void
82 dev_refl(struct cdev *dev)
83 {
84
85 mtx_assert(&devmtx, MA_OWNED);
86 dev->si_refcount++;
87 }
88
89 void
90 dev_rel(struct cdev *dev)
91 {
92 int flag = 0;
93
94 mtx_assert(&devmtx, MA_NOTOWNED);
95 dev_lock();
96 dev->si_refcount--;
97 KASSERT(dev->si_refcount >= 0,
98 ("dev_rel(%s) gave negative count", devtoname(dev)));
99 #if 0
100 if (dev->si_usecount == 0 &&
101 (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
102 ;
103 else
104 #endif
105 if (dev->si_devsw == NULL && dev->si_refcount == 0) {
106 LIST_REMOVE(dev, si_list);
107 flag = 1;
108 }
109 dev_unlock();
110 if (flag)
111 devfs_free(dev);
112 }
113
114 struct cdevsw *
115 dev_refthread(struct cdev *dev)
116 {
117 struct cdevsw *csw;
118
119 mtx_assert(&devmtx, MA_NOTOWNED);
120 dev_lock();
121 csw = dev->si_devsw;
122 if (csw != NULL)
123 dev->si_threadcount++;
124 dev_unlock();
125 return (csw);
126 }
127
128 void
129 dev_relthread(struct cdev *dev)
130 {
131
132 mtx_assert(&devmtx, MA_NOTOWNED);
133 dev_lock();
134 dev->si_threadcount--;
135 dev_unlock();
136 }
137
138 int
139 nullop(void)
140 {
141
142 return (0);
143 }
144
145 int
146 eopnotsupp(void)
147 {
148
149 return (EOPNOTSUPP);
150 }
151
152 static int
153 enxio(void)
154 {
155 return (ENXIO);
156 }
157
158 static int
159 enodev(void)
160 {
161 return (ENODEV);
162 }
163
164 /* Define a dead_cdevsw for use when devices leave unexpectedly. */
165
166 #define dead_open (d_open_t *)enxio
167 #define dead_close (d_close_t *)enxio
168 #define dead_read (d_read_t *)enxio
169 #define dead_write (d_write_t *)enxio
170 #define dead_ioctl (d_ioctl_t *)enxio
171 #define dead_poll (d_poll_t *)enodev
172 #define dead_mmap (d_mmap_t *)enodev
173
174 static void
175 dead_strategy(struct bio *bp)
176 {
177
178 biofinish(bp, NULL, ENXIO);
179 }
180
181 #define dead_dump (dumper_t *)enxio
182 #define dead_kqfilter (d_kqfilter_t *)enxio
183
184 static struct cdevsw dead_cdevsw = {
185 .d_version = D_VERSION,
186 .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
187 .d_open = dead_open,
188 .d_close = dead_close,
189 .d_read = dead_read,
190 .d_write = dead_write,
191 .d_ioctl = dead_ioctl,
192 .d_poll = dead_poll,
193 .d_mmap = dead_mmap,
194 .d_strategy = dead_strategy,
195 .d_name = "dead",
196 .d_dump = dead_dump,
197 .d_kqfilter = dead_kqfilter
198 };
199
200 /* Default methods if driver does not specify method */
201
202 #define null_open (d_open_t *)nullop
203 #define null_close (d_close_t *)nullop
204 #define no_read (d_read_t *)enodev
205 #define no_write (d_write_t *)enodev
206 #define no_ioctl (d_ioctl_t *)enodev
207 #define no_mmap (d_mmap_t *)enodev
208 #define no_kqfilter (d_kqfilter_t *)enodev
209
210 static void
211 no_strategy(struct bio *bp)
212 {
213
214 biofinish(bp, NULL, ENODEV);
215 }
216
217 static int
218 no_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
219 {
220 /*
221 * Return true for read/write. If the user asked for something
222 * special, return POLLNVAL, so that clients have a way of
223 * determining reliably whether or not the extended
224 * functionality is present without hard-coding knowledge
225 * of specific filesystem implementations.
226 * Stay in sync with vop_nopoll().
227 */
228 if (events & ~POLLSTANDARD)
229 return (POLLNVAL);
230
231 return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
232 }
233
234 #define no_dump (dumper_t *)enodev
235
236 static int
237 giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
238 {
239 int retval;
240
241 mtx_lock(&Giant);
242 retval = dev->si_devsw->d_gianttrick->
243 d_open(dev, oflags, devtype, td);
244 mtx_unlock(&Giant);
245 return (retval);
246 }
247
248 static int
249 giant_fdopen(struct cdev *dev, int oflags, struct thread *td, int fdidx)
250 {
251 int retval;
252
253 mtx_lock(&Giant);
254 retval = dev->si_devsw->d_gianttrick->
255 d_fdopen(dev, oflags, td, fdidx);
256 mtx_unlock(&Giant);
257 return (retval);
258 }
259
260 static int
261 giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
262 {
263 int retval;
264
265 mtx_lock(&Giant);
266 retval = dev->si_devsw->d_gianttrick->
267 d_close(dev, fflag, devtype, td);
268 mtx_unlock(&Giant);
269 return (retval);
270 }
271
272 static void
273 giant_strategy(struct bio *bp)
274 {
275
276 mtx_lock(&Giant);
277 bp->bio_dev->si_devsw->d_gianttrick->
278 d_strategy(bp);
279 mtx_unlock(&Giant);
280 }
281
282 static int
283 giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
284 {
285 int retval;
286
287 mtx_lock(&Giant);
288 retval = dev->si_devsw->d_gianttrick->
289 d_ioctl(dev, cmd, data, fflag, td);
290 mtx_unlock(&Giant);
291 return (retval);
292 }
293
294 static int
295 giant_read(struct cdev *dev, struct uio *uio, int ioflag)
296 {
297 int retval;
298
299 mtx_lock(&Giant);
300 retval = dev->si_devsw->d_gianttrick->
301 d_read(dev, uio, ioflag);
302 mtx_unlock(&Giant);
303 return (retval);
304 }
305
306 static int
307 giant_write(struct cdev *dev, struct uio *uio, int ioflag)
308 {
309 int retval;
310
311 mtx_lock(&Giant);
312 retval = dev->si_devsw->d_gianttrick->
313 d_write(dev, uio, ioflag);
314 mtx_unlock(&Giant);
315 return (retval);
316 }
317
318 static int
319 giant_poll(struct cdev *dev, int events, struct thread *td)
320 {
321 int retval;
322
323 mtx_lock(&Giant);
324 retval = dev->si_devsw->d_gianttrick->
325 d_poll(dev, events, td);
326 mtx_unlock(&Giant);
327 return (retval);
328 }
329
330 static int
331 giant_kqfilter(struct cdev *dev, struct knote *kn)
332 {
333 int retval;
334
335 mtx_lock(&Giant);
336 retval = dev->si_devsw->d_gianttrick->
337 d_kqfilter(dev, kn);
338 mtx_unlock(&Giant);
339 return (retval);
340 }
341
342 static int
343 giant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
344 {
345 int retval;
346
347 mtx_lock(&Giant);
348 retval = dev->si_devsw->d_gianttrick->
349 d_mmap(dev, offset, paddr, nprot);
350 mtx_unlock(&Giant);
351 return (retval);
352 }
353
354
355 /*
356 * struct cdev * and u_dev_t primitives
357 */
358
359 int
360 minor(struct cdev *x)
361 {
362 if (x == NULL)
363 return NODEV;
364 return(x->si_drv0 & MAXMINOR);
365 }
366
367 int
368 dev2unit(struct cdev *x)
369 {
370
371 if (x == NULL)
372 return NODEV;
373 return (minor2unit(minor(x)));
374 }
375
376 u_int
377 minor2unit(u_int _minor)
378 {
379
380 KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor));
381 return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00));
382 }
383
384 int
385 unit2minor(int unit)
386 {
387
388 KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
389 return ((unit & 0xff) | ((unit << 8) & ~0xffff));
390 }
391
392 static struct cdev *
393 newdev(struct cdevsw *csw, int y, struct cdev *si)
394 {
395 struct cdev *si2;
396 dev_t udev;
397
398 mtx_assert(&devmtx, MA_OWNED);
399 udev = y;
400 LIST_FOREACH(si2, &csw->d_devs, si_list) {
401 if (si2->si_drv0 == udev) {
402 devfs_free(si);
403 return (si2);
404 }
405 }
406 si->si_drv0 = udev;
407 si->si_devsw = csw;
408 LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
409 return (si);
410 }
411
412 int
413 uminor(dev_t dev)
414 {
415 return (dev & MAXMINOR);
416 }
417
418 int
419 umajor(dev_t dev)
420 {
421 return ((dev & ~MAXMINOR) >> 8);
422 }
423
424 static void
425 fini_cdevsw(struct cdevsw *devsw)
426 {
427 struct cdevsw *gt;
428
429 if (devsw->d_gianttrick != NULL) {
430 gt = devsw->d_gianttrick;
431 memcpy(devsw, gt, sizeof *devsw);
432 free(gt, M_DEVT);
433 devsw->d_gianttrick = NULL;
434 }
435 devsw->d_flags &= ~D_INIT;
436 }
437
438 static void
439 prep_cdevsw(struct cdevsw *devsw)
440 {
441 struct cdevsw *dsw2;
442
443 if (devsw->d_flags & D_NEEDGIANT)
444 dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
445 else
446 dsw2 = NULL;
447 dev_lock();
448
449 if (devsw->d_version != D_VERSION_01) {
450 printf(
451 "WARNING: Device driver \"%s\" has wrong version %s\n",
452 devsw->d_name, "and is disabled. Recompile KLD module.");
453 devsw->d_open = dead_open;
454 devsw->d_close = dead_close;
455 devsw->d_read = dead_read;
456 devsw->d_write = dead_write;
457 devsw->d_ioctl = dead_ioctl;
458 devsw->d_poll = dead_poll;
459 devsw->d_mmap = dead_mmap;
460 devsw->d_strategy = dead_strategy;
461 devsw->d_dump = dead_dump;
462 devsw->d_kqfilter = dead_kqfilter;
463 }
464
465 if (devsw->d_flags & D_TTY) {
466 if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl;
467 if (devsw->d_read == NULL) devsw->d_read = ttyread;
468 if (devsw->d_write == NULL) devsw->d_write = ttywrite;
469 if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter;
470 if (devsw->d_poll == NULL) devsw->d_poll = ttypoll;
471 }
472
473 if (devsw->d_flags & D_NEEDGIANT) {
474 if (devsw->d_gianttrick == NULL) {
475 memcpy(dsw2, devsw, sizeof *dsw2);
476 devsw->d_gianttrick = dsw2;
477 } else
478 free(dsw2, M_DEVT);
479 }
480
481 #define FIXUP(member, noop, giant) \
482 do { \
483 if (devsw->member == NULL) { \
484 devsw->member = noop; \
485 } else if (devsw->d_flags & D_NEEDGIANT) \
486 devsw->member = giant; \
487 } \
488 while (0)
489
490 FIXUP(d_open, null_open, giant_open);
491 FIXUP(d_fdopen, NULL, giant_fdopen);
492 FIXUP(d_close, null_close, giant_close);
493 FIXUP(d_read, no_read, giant_read);
494 FIXUP(d_write, no_write, giant_write);
495 FIXUP(d_ioctl, no_ioctl, giant_ioctl);
496 FIXUP(d_poll, no_poll, giant_poll);
497 FIXUP(d_mmap, no_mmap, giant_mmap);
498 FIXUP(d_strategy, no_strategy, giant_strategy);
499 FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter);
500
501 if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
502
503 LIST_INIT(&devsw->d_devs);
504
505 devsw->d_flags |= D_INIT;
506
507 dev_unlock();
508 }
509
510 static struct cdev *
511 make_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid,
512 gid_t gid, int mode, const char *fmt, va_list ap)
513 {
514 struct cdev *dev;
515 int i;
516
517 KASSERT((minornr & ~MAXMINOR) == 0,
518 ("Invalid minor (0x%x) in make_dev", minornr));
519
520 if (!(devsw->d_flags & D_INIT))
521 prep_cdevsw(devsw);
522 dev = devfs_alloc();
523 dev_lock();
524 dev = newdev(devsw, minornr, dev);
525 if (dev->si_flags & SI_CHEAPCLONE &&
526 dev->si_flags & SI_NAMED) {
527 /*
528 * This is allowed as it removes races and generally
529 * simplifies cloning devices.
530 * XXX: still ??
531 */
532 dev_unlock();
533 return (dev);
534 }
535 KASSERT(!(dev->si_flags & SI_NAMED),
536 ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
537 devsw->d_name, minor(dev), devtoname(dev)));
538
539 i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
540 if (i > (sizeof dev->__si_namebuf - 1)) {
541 printf("WARNING: Device name truncated! (%s)\n",
542 dev->__si_namebuf);
543 }
544
545 dev->si_flags |= SI_NAMED;
546 if (cr != NULL)
547 dev->si_cred = crhold(cr);
548 else
549 dev->si_cred = NULL;
550 dev->si_uid = uid;
551 dev->si_gid = gid;
552 dev->si_mode = mode;
553
554 devfs_create(dev);
555 dev_unlock();
556 return (dev);
557 }
558
559 struct cdev *
560 make_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
572 struct cdev *
573 make_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
586 static void
587 dev_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
596 void
597 dev_depends(struct cdev *pdev, struct cdev *cdev)
598 {
599
600 dev_lock();
601 dev_dependsl(pdev, cdev);
602 dev_unlock();
603 }
604
605 struct cdev *
606 make_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
630 static void
631 destroy_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
692 void
693 destroy_dev(struct cdev *dev)
694 {
695
696 dev_lock();
697 destroy_devl(dev);
698 dev_unlock();
699 }
700
701 const char *
702 devtoname(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
725 int
726 dev_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] == '' && isdigit(name[i+1]))
737 return (0);
738 while (isdigit(name[i])) {
739 u *= 10;
740 u += name[i++] - '';
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
769 struct clonedevs {
770 LIST_HEAD(,cdev) head;
771 };
772
773 void
774 clone_setup(struct clonedevs **cdp)
775 {
776
777 *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
778 LIST_INIT(&(*cdp)->head);
779 }
780
781 int
782 clone_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 */
863 void
864 clone_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 }
Cache object: 68993dd221cbdb878f17b400c932eb9c
|