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: src/sys/kern/kern_conf.c,v 1.155 2004/08/15 06:24:40 jmg Exp $");
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/sysctl.h>
37 #include <sys/module.h>
38 #include <sys/malloc.h>
39 #include <sys/conf.h>
40 #include <sys/vnode.h>
41 #include <sys/queue.h>
42 #include <sys/poll.h>
43 #include <sys/ctype.h>
44 #include <sys/tty.h>
45 #include <machine/stdarg.h>
46
47 static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
48
49 /* Built at compile time from sys/conf/majors */
50 extern unsigned char reserved_majors[256];
51
52 /*
53 * This is the number of hash-buckets. Experiments with 'real-life'
54 * dev_t's show that a prime halfway between two powers of two works
55 * best.
56 */
57 #define DEVT_HASH 83
58
59 /* The number of struct cdev *'s we can create before malloc(9) kick in. */
60 #define DEVT_STASH 50
61
62 static struct cdev devt_stash[DEVT_STASH];
63
64 static LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
65
66 static LIST_HEAD(, cdev) dev_free;
67
68 static int free_devt;
69 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
70
71 static struct mtx devmtx;
72 static void freedev(struct cdev *dev);
73 static struct cdev *newdev(int x, int y);
74
75
76 static void
77 devlock(void)
78 {
79 if (!mtx_initialized(&devmtx))
80 mtx_init(&devmtx, "cdev", NULL, MTX_DEF);
81 mtx_lock(&devmtx);
82 }
83
84 static void
85 devunlock(void)
86 {
87 mtx_unlock(&devmtx);
88 }
89
90 void
91 dev_ref(struct cdev *dev)
92 {
93 devlock();
94 dev->si_refcount++;
95 devunlock();
96 }
97
98 void
99 dev_rel(struct cdev *dev)
100 {
101 devlock();
102 dev->si_refcount--;
103 KASSERT(dev->si_refcount >= 0,
104 ("dev_rel(%s) gave negative count", devtoname(dev)));
105 if (dev->si_devsw == NULL && dev->si_refcount == 0) {
106 LIST_REMOVE(dev, si_list);
107 freedev(dev);
108 }
109 devunlock();
110 }
111
112 void
113 cdevsw_ref(struct cdevsw *csw)
114 {
115 devlock();
116 csw->d_refcount++;
117 devunlock();
118 }
119
120 void
121 cdevsw_rel(struct cdevsw *csw)
122 {
123 devlock();
124 csw->d_refcount--;
125 KASSERT(csw->d_refcount >= 0,
126 ("cdevsw_vrel(%s) gave negative count", csw->d_name));
127 devunlock();
128 }
129
130 int
131 nullop(void)
132 {
133
134 return (0);
135 }
136
137 int
138 eopnotsupp(void)
139 {
140
141 return (EOPNOTSUPP);
142 }
143
144 static int
145 enxio(void)
146 {
147 return (ENXIO);
148 }
149
150 static int
151 enodev(void)
152 {
153 return (ENODEV);
154 }
155
156 /* Define a dead_cdevsw for use when devices leave unexpectedly. */
157
158 #define dead_open (d_open_t *)enxio
159 #define dead_close (d_close_t *)enxio
160 #define dead_read (d_read_t *)enxio
161 #define dead_write (d_write_t *)enxio
162 #define dead_ioctl (d_ioctl_t *)enxio
163 #define dead_poll (d_poll_t *)enodev
164 #define dead_mmap (d_mmap_t *)enodev
165
166 static void
167 dead_strategy(struct bio *bp)
168 {
169
170 biofinish(bp, NULL, ENXIO);
171 }
172
173 #define dead_dump (dumper_t *)enxio
174 #define dead_kqfilter (d_kqfilter_t *)enxio
175
176 static struct cdevsw dead_cdevsw = {
177 .d_version = D_VERSION,
178 .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
179 .d_open = dead_open,
180 .d_close = dead_close,
181 .d_read = dead_read,
182 .d_write = dead_write,
183 .d_ioctl = dead_ioctl,
184 .d_poll = dead_poll,
185 .d_mmap = dead_mmap,
186 .d_strategy = dead_strategy,
187 .d_name = "dead",
188 .d_maj = 255,
189 .d_dump = dead_dump,
190 .d_kqfilter = dead_kqfilter
191 };
192
193 /* Default methods if driver does not specify method */
194
195 #define null_open (d_open_t *)nullop
196 #define null_close (d_close_t *)nullop
197 #define no_read (d_read_t *)enodev
198 #define no_write (d_write_t *)enodev
199 #define no_ioctl (d_ioctl_t *)enodev
200 #define no_mmap (d_mmap_t *)enodev
201 #define no_kqfilter (d_kqfilter_t *)enodev
202
203 static void
204 no_strategy(struct bio *bp)
205 {
206
207 biofinish(bp, NULL, ENODEV);
208 }
209
210 static int
211 no_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
212 {
213 /*
214 * Return true for read/write. If the user asked for something
215 * special, return POLLNVAL, so that clients have a way of
216 * determining reliably whether or not the extended
217 * functionality is present without hard-coding knowledge
218 * of specific filesystem implementations.
219 * Stay in sync with vop_nopoll().
220 */
221 if (events & ~POLLSTANDARD)
222 return (POLLNVAL);
223
224 return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
225 }
226
227 #define no_dump (dumper_t *)enodev
228
229 struct cdevsw *
230 devsw(struct cdev *dev)
231 {
232 if (dev->si_devsw != NULL)
233 return (dev->si_devsw);
234 return (&dead_cdevsw);
235 }
236
237 /*
238 * struct cdev * and u_dev_t primitives
239 */
240
241 int
242 major(struct cdev *x)
243 {
244 if (x == NULL)
245 return NODEV;
246 return((x->si_udev >> 8) & 0xff);
247 }
248
249 int
250 minor(struct cdev *x)
251 {
252 if (x == NULL)
253 return NODEV;
254 return(x->si_udev & 0xffff00ff);
255 }
256
257 int
258 dev2unit(struct cdev *x)
259 {
260 int i;
261
262 if (x == NULL)
263 return NODEV;
264 i = minor(x);
265 return ((i & 0xff) | (i >> 8));
266 }
267
268 int
269 unit2minor(int unit)
270 {
271
272 KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
273 return ((unit & 0xff) | ((unit << 8) & ~0xffff));
274 }
275
276 static struct cdev *
277 allocdev(void)
278 {
279 static int stashed;
280 struct cdev *si;
281
282 if (LIST_FIRST(&dev_free)) {
283 si = LIST_FIRST(&dev_free);
284 LIST_REMOVE(si, si_hash);
285 } else if (stashed >= DEVT_STASH) {
286 MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
287 M_USE_RESERVE | M_ZERO | M_WAITOK);
288 } else {
289 si = devt_stash + stashed++;
290 bzero(si, sizeof *si);
291 si->si_flags |= SI_STASHED;
292 }
293 si->__si_namebuf[0] = '\0';
294 si->si_name = si->__si_namebuf;
295 LIST_INIT(&si->si_children);
296 TAILQ_INIT(&si->si_snapshots);
297 return (si);
298 }
299
300 static struct cdev *
301 newdev(int x, int y)
302 {
303 struct cdev *si;
304 dev_t udev;
305 int hash;
306
307 if (x == umajor(NODEV) && y == uminor(NODEV))
308 panic("newdev of NODEV");
309 udev = (x << 8) | y;
310 hash = udev % DEVT_HASH;
311 LIST_FOREACH(si, &dev_hash[hash], si_hash) {
312 if (si->si_udev == udev)
313 return (si);
314 }
315 si = allocdev();
316 si->si_udev = udev;
317 LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
318 return (si);
319 }
320
321 static void
322 freedev(struct cdev *dev)
323 {
324
325 if (dev->si_flags & SI_STASHED) {
326 bzero(dev, sizeof(*dev));
327 dev->si_flags |= SI_STASHED;
328 LIST_INSERT_HEAD(&dev_free, dev, si_hash);
329 } else {
330 FREE(dev, M_DEVT);
331 }
332 }
333
334 dev_t
335 dev2udev(struct cdev *x)
336 {
337 if (x == NULL)
338 return (NODEV);
339 return (x->si_udev);
340 }
341
342 struct cdev *
343 findcdev(dev_t udev)
344 {
345 struct cdev *si;
346 int hash;
347
348 if (udev == NODEV)
349 return (NULL);
350 hash = udev % DEVT_HASH;
351 LIST_FOREACH(si, &dev_hash[hash], si_hash) {
352 if (si->si_udev == udev)
353 return (si);
354 }
355 return (NULL);
356 }
357
358 int
359 uminor(dev_t dev)
360 {
361 return (dev & 0xffff00ff);
362 }
363
364 int
365 umajor(dev_t dev)
366 {
367 return ((dev & 0xff00) >> 8);
368 }
369
370 static void
371 find_major(struct cdevsw *devsw)
372 {
373 int i;
374
375 for (i = NUMCDEVSW - 1; i > 0; i--)
376 if (reserved_majors[i] != i)
377 break;
378 KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
379 devsw->d_maj = i;
380 reserved_majors[i] = i;
381 devsw->d_flags |= D_ALLOCMAJ;
382 }
383
384 static void
385 fini_cdevsw(struct cdevsw *devsw)
386 {
387 if (devsw->d_flags & D_ALLOCMAJ) {
388 reserved_majors[devsw->d_maj] = 0;
389 devsw->d_maj = MAJOR_AUTO;
390 devsw->d_flags &= ~D_ALLOCMAJ;
391 } else if (devsw->d_maj == 0)
392 devsw->d_maj = 256;
393 devsw->d_flags &= ~D_INIT;
394 }
395
396 static void
397 prep_cdevsw(struct cdevsw *devsw)
398 {
399
400 devlock();
401
402 if (devsw->d_version != D_VERSION_00) {
403 printf(
404 "WARNING: Device driver \"%s\" has wrong version %s\n",
405 devsw->d_name, "and is disabled. Recompile KLD module.");
406 devsw->d_open = dead_open;
407 devsw->d_close = dead_close;
408 devsw->d_read = dead_read;
409 devsw->d_write = dead_write;
410 devsw->d_ioctl = dead_ioctl;
411 devsw->d_poll = dead_poll;
412 devsw->d_mmap = dead_mmap;
413 devsw->d_strategy = dead_strategy;
414 devsw->d_dump = dead_dump;
415 devsw->d_kqfilter = dead_kqfilter;
416 }
417
418 if (devsw->d_flags & D_TTY) {
419 if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl;
420 if (devsw->d_read == NULL) devsw->d_read = ttyread;
421 if (devsw->d_write == NULL) devsw->d_write = ttywrite;
422 if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter;
423 if (devsw->d_poll == NULL) devsw->d_poll = ttypoll;
424 }
425
426 if (devsw->d_open == NULL) devsw->d_open = null_open;
427 if (devsw->d_close == NULL) devsw->d_close = null_close;
428 if (devsw->d_read == NULL) devsw->d_read = no_read;
429 if (devsw->d_write == NULL) devsw->d_write = no_write;
430 if (devsw->d_ioctl == NULL) devsw->d_ioctl = no_ioctl;
431 if (devsw->d_poll == NULL) devsw->d_poll = no_poll;
432 if (devsw->d_mmap == NULL) devsw->d_mmap = no_mmap;
433 if (devsw->d_strategy == NULL) devsw->d_strategy = no_strategy;
434 if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
435 if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter;
436
437 LIST_INIT(&devsw->d_devs);
438
439 devsw->d_flags |= D_INIT;
440
441 if (devsw->d_maj == MAJOR_AUTO) {
442 find_major(devsw);
443 } else {
444 if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */
445 devsw->d_maj = 0;
446 KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256,
447 ("Invalid major (%d) in make_dev", devsw->d_maj));
448 if (reserved_majors[devsw->d_maj] != devsw->d_maj) {
449 printf("WARNING: driver \"%s\" used %s %d\n",
450 devsw->d_name, "unreserved major device number",
451 devsw->d_maj);
452 reserved_majors[devsw->d_maj] = devsw->d_maj;
453 }
454 }
455 devunlock();
456 }
457
458 struct cdev *
459 make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
460 {
461 struct cdev *dev;
462 va_list ap;
463 int i;
464
465 KASSERT((minornr & ~0xffff00ff) == 0,
466 ("Invalid minor (0x%x) in make_dev", minornr));
467
468 if (!(devsw->d_flags & D_INIT))
469 prep_cdevsw(devsw);
470 dev = newdev(devsw->d_maj, minornr);
471 if (dev->si_flags & SI_CHEAPCLONE &&
472 dev->si_flags & SI_NAMED &&
473 dev->si_devsw == devsw) {
474 /*
475 * This is allowed as it removes races and generally
476 * simplifies cloning devices.
477 * XXX: still ??
478 */
479 return (dev);
480 }
481 devlock();
482 KASSERT(!(dev->si_flags & SI_NAMED),
483 ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)",
484 devsw->d_name, major(dev), minor(dev), devtoname(dev)));
485
486 va_start(ap, fmt);
487 i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
488 if (i > (sizeof dev->__si_namebuf - 1)) {
489 printf("WARNING: Device name truncated! (%s)",
490 dev->__si_namebuf);
491 }
492 va_end(ap);
493 dev->si_devsw = devsw;
494 dev->si_uid = uid;
495 dev->si_gid = gid;
496 dev->si_mode = perms;
497 dev->si_flags |= SI_NAMED;
498
499 LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list);
500 devfs_create(dev);
501 devunlock();
502 return (dev);
503 }
504
505 int
506 dev_named(struct cdev *pdev, const char *name)
507 {
508 struct cdev *cdev;
509
510 if (strcmp(devtoname(pdev), name) == 0)
511 return (1);
512 LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
513 if (strcmp(devtoname(cdev), name) == 0)
514 return (1);
515 return (0);
516 }
517
518 void
519 dev_depends(struct cdev *pdev, struct cdev *cdev)
520 {
521
522 devlock();
523 cdev->si_parent = pdev;
524 cdev->si_flags |= SI_CHILD;
525 LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
526 devunlock();
527 }
528
529 struct cdev *
530 make_dev_alias(struct cdev *pdev, const char *fmt, ...)
531 {
532 struct cdev *dev;
533 va_list ap;
534 int i;
535
536 dev = allocdev();
537 devlock();
538 dev->si_flags |= SI_ALIAS;
539 dev->si_flags |= SI_NAMED;
540 va_start(ap, fmt);
541 i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
542 if (i > (sizeof dev->__si_namebuf - 1)) {
543 printf("WARNING: Device name truncated! (%s)",
544 dev->__si_namebuf);
545 }
546 va_end(ap);
547
548 devfs_create(dev);
549 devunlock();
550 dev_depends(pdev, dev);
551 return (dev);
552 }
553
554 static void
555 idestroy_dev(struct cdev *dev)
556 {
557 if (!(dev->si_flags & SI_NAMED)) {
558 printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
559 major(dev), minor(dev));
560 panic("don't do that");
561 }
562
563 devfs_destroy(dev);
564
565 /* Remove name marking */
566 dev->si_flags &= ~SI_NAMED;
567
568 /* If we are a child, remove us from the parents list */
569 if (dev->si_flags & SI_CHILD) {
570 LIST_REMOVE(dev, si_siblings);
571 dev->si_flags &= ~SI_CHILD;
572 }
573
574 /* Kill our children */
575 while (!LIST_EMPTY(&dev->si_children))
576 idestroy_dev(LIST_FIRST(&dev->si_children));
577
578 /* Remove from clone list */
579 if (dev->si_flags & SI_CLONELIST) {
580 LIST_REMOVE(dev, si_clone);
581 dev->si_flags &= ~SI_CLONELIST;
582 }
583
584 if (!(dev->si_flags & SI_ALIAS)) {
585 /* Remove from cdevsw list */
586 LIST_REMOVE(dev, si_list);
587
588 /* If cdevsw has no struct cdev *'s, clean it */
589 if (LIST_EMPTY(&dev->si_devsw->d_devs))
590 fini_cdevsw(dev->si_devsw);
591
592 LIST_REMOVE(dev, si_hash);
593 }
594 dev->si_drv1 = 0;
595 dev->si_drv2 = 0;
596 dev->si_devsw = NULL;
597 bzero(&dev->__si_u, sizeof(dev->__si_u));
598 dev->si_flags &= ~SI_ALIAS;
599 if (dev->si_refcount > 0) {
600 LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
601 } else {
602 freedev(dev);
603 }
604 }
605
606 void
607 destroy_dev(struct cdev *dev)
608 {
609
610 devlock();
611 idestroy_dev(dev);
612 devunlock();
613 }
614
615 const char *
616 devtoname(struct cdev *dev)
617 {
618 char *p;
619 int mynor;
620
621 if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
622 p = dev->si_name;
623 if (devsw(dev))
624 sprintf(p, "#%s/", devsw(dev)->d_name);
625 else
626 sprintf(p, "#%d/", major(dev));
627 p += strlen(p);
628 mynor = minor(dev);
629 if (mynor < 0 || mynor > 255)
630 sprintf(p, "%#x", (u_int)mynor);
631 else
632 sprintf(p, "%d", mynor);
633 }
634 return (dev->si_name);
635 }
636
637 int
638 dev_stdclone(char *name, char **namep, const char *stem, int *unit)
639 {
640 int u, i;
641
642 i = strlen(stem);
643 if (bcmp(stem, name, i) != 0)
644 return (0);
645 if (!isdigit(name[i]))
646 return (0);
647 u = 0;
648 if (name[i] == '' && isdigit(name[i+1]))
649 return (0);
650 while (isdigit(name[i])) {
651 u *= 10;
652 u += name[i++] - '';
653 }
654 if (u > 0xffffff)
655 return (0);
656 *unit = u;
657 if (namep)
658 *namep = &name[i];
659 if (name[i])
660 return (2);
661 return (1);
662 }
663
664 /*
665 * Helper functions for cloning device drivers.
666 *
667 * The objective here is to make it unnecessary for the device drivers to
668 * use rman or similar to manage their unit number space. Due to the way
669 * we do "on-demand" devices, using rman or other "private" methods
670 * will be very tricky to lock down properly once we lock down this file.
671 *
672 * Instead we give the drivers these routines which puts the struct cdev *'s
673 * that are to be managed on their own list, and gives the driver the ability
674 * to ask for the first free unit number or a given specified unit number.
675 *
676 * In addition these routines support paired devices (pty, nmdm and similar)
677 * by respecting a number of "flag" bits in the minor number.
678 *
679 */
680
681 struct clonedevs {
682 LIST_HEAD(,cdev) head;
683 };
684
685 void
686 clone_setup(struct clonedevs **cdp)
687 {
688
689 *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
690 LIST_INIT(&(*cdp)->head);
691 }
692
693 int
694 clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra)
695 {
696 struct clonedevs *cd;
697 struct cdev *dev, *dl, *de;
698 int unit, low, u;
699
700 KASSERT(*cdp != NULL,
701 ("clone_setup() not called in driver \"%s\"", csw->d_name));
702 KASSERT(!(extra & CLONE_UNITMASK),
703 ("Illegal extra bits (0x%x) in clone_create", extra));
704 KASSERT(*up <= CLONE_UNITMASK,
705 ("Too high unit (0x%x) in clone_create", *up));
706
707 if (csw->d_maj == MAJOR_AUTO)
708 find_major(csw);
709
710 /*
711 * Search the list for a lot of things in one go:
712 * A preexisting match is returned immediately.
713 * The lowest free unit number if we are passed -1, and the place
714 * in the list where we should insert that new element.
715 * The place to insert a specified unit number, if applicable
716 * the end of the list.
717 */
718 unit = *up;
719 low = extra;
720 de = dl = NULL;
721 cd = *cdp;
722 LIST_FOREACH(dev, &cd->head, si_clone) {
723 u = dev2unit(dev);
724 if (u == (unit | extra)) {
725 *dp = dev;
726 return (0);
727 }
728 if (unit == -1 && u == low) {
729 low++;
730 de = dev;
731 continue;
732 }
733 if (u > (unit | extra)) {
734 dl = dev;
735 break;
736 }
737 de = dev;
738 }
739 if (unit == -1)
740 unit = low & CLONE_UNITMASK;
741 dev = newdev(csw->d_maj, unit2minor(unit | extra));
742 KASSERT(!(dev->si_flags & SI_CLONELIST),
743 ("Dev %p should not be on clonelist", dev));
744 if (dl != NULL)
745 LIST_INSERT_BEFORE(dl, dev, si_clone);
746 else if (de != NULL)
747 LIST_INSERT_AFTER(de, dev, si_clone);
748 else
749 LIST_INSERT_HEAD(&cd->head, dev, si_clone);
750 dev->si_flags |= SI_CLONELIST;
751 *up = unit;
752 return (1);
753 }
754
755 /*
756 * Kill everything still on the list. The driver should already have
757 * disposed of any softc hung of the struct cdev *'s at this time.
758 */
759 void
760 clone_cleanup(struct clonedevs **cdp)
761 {
762 struct cdev *dev, *tdev;
763 struct clonedevs *cd;
764
765 cd = *cdp;
766 if (cd == NULL)
767 return;
768 LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
769 KASSERT(dev->si_flags & SI_NAMED,
770 ("Driver has goofed in cloning underways udev %x", dev->si_udev));
771 destroy_dev(dev);
772 }
773 free(cd, M_DEVBUF);
774 *cdp = NULL;
775 }
776
777 /*
778 * Helper sysctl for devname(3). We're given a struct cdev * and return
779 * the name, if any, registered by the device driver.
780 */
781 static int
782 sysctl_devname(SYSCTL_HANDLER_ARGS)
783 {
784 int error;
785 dev_t ud;
786 struct cdev *dev;
787
788 error = SYSCTL_IN(req, &ud, sizeof (ud));
789 if (error)
790 return (error);
791 if (ud == NODEV)
792 return(EINVAL);
793 dev = findcdev(ud);
794 if (dev == NULL)
795 error = ENOENT;
796 else
797 error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
798 return (error);
799 }
800
801 SYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
802 NULL, 0, sysctl_devname, "", "devname(3) handler");
Cache object: 2efeda141a5d3702883b5fc89e647dab
|