kern_conf.c revision 135934
111126Sjulian/*- 2103722Sphk * Copyright (c) 1999-2002 Poul-Henning Kamp 311126Sjulian * All rights reserved. 411126Sjulian * 511126Sjulian * Redistribution and use in source and binary forms, with or without 611126Sjulian * modification, are permitted provided that the following conditions 711126Sjulian * are met: 811126Sjulian * 1. Redistributions of source code must retain the above copyright 911126Sjulian * notice, this list of conditions and the following disclaimer. 1011126Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1111126Sjulian * notice, this list of conditions and the following disclaimer in the 1211126Sjulian * documentation and/or other materials provided with the distribution. 1311126Sjulian * 14103722Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15103722Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1611126Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17103722Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1811126Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1911126Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2011126Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2111126Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2211126Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2311126Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2411126Sjulian * SUCH DAMAGE. 2511126Sjulian */ 2611126Sjulian 27116182Sobrien#include <sys/cdefs.h> 28116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 135934 2004-09-29 16:38:38Z green $"); 29116182Sobrien 3011126Sjulian#include <sys/param.h> 3148936Sphk#include <sys/kernel.h> 3283366Sjulian#include <sys/systm.h> 33111179Sphk#include <sys/bio.h> 3490737Sgreen#include <sys/lock.h> 3590737Sgreen#include <sys/mutex.h> 3650549Sphk#include <sys/sysctl.h> 3736735Sdfr#include <sys/module.h> 3848936Sphk#include <sys/malloc.h> 3911126Sjulian#include <sys/conf.h> 4012954Sjulian#include <sys/vnode.h> 4148936Sphk#include <sys/queue.h> 42120514Sphk#include <sys/poll.h> 4365374Sphk#include <sys/ctype.h> 44126078Sphk#include <sys/tty.h> 4549535Sphk#include <machine/stdarg.h> 4611126Sjulian 47131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); 4848936Sphk 49111622Sphk/* Built at compile time from sys/conf/majors */ 50111622Sphkextern unsigned char reserved_majors[256]; 51111622Sphk 5250522Sphk/* 53130936Sle * This is the number of hash-buckets. Experiments with 'real-life' 54130640Sphk * dev_t's show that a prime halfway between two powers of two works 5550522Sphk * best. 5650522Sphk */ 5748936Sphk#define DEVT_HASH 83 5850522Sphk 59130585Sphk/* The number of struct cdev *'s we can create before malloc(9) kick in. */ 6048936Sphk#define DEVT_STASH 50 6148936Sphk 62104043Sphkstatic struct cdev devt_stash[DEVT_STASH]; 6348936Sphk 64104043Sphkstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH]; 6548936Sphk 66104043Sphkstatic LIST_HEAD(, cdev) dev_free; 6750549Sphk 6850549Sphkstatic int free_devt; 6950549SphkSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, ""); 7050549Sphk 71126082Sphkstatic struct mtx devmtx; 72130585Sphkstatic void freedev(struct cdev *dev); 73130640Sphkstatic struct cdev *newdev(int x, int y); 74126082Sphk 75130640Sphk 76135600Sphkvoid 77135600Sphkdev_lock(void) 78126082Sphk{ 79126082Sphk if (!mtx_initialized(&devmtx)) 80131996Sphk mtx_init(&devmtx, "cdev", NULL, MTX_DEF); 81126082Sphk mtx_lock(&devmtx); 82126082Sphk} 83126082Sphk 84135600Sphkvoid 85135600Sphkdev_unlock(void) 86126082Sphk{ 87135704Sphk 88126082Sphk mtx_unlock(&devmtx); 89126082Sphk} 90126082Sphk 91126082Sphkvoid 92130585Sphkdev_ref(struct cdev *dev) 93126082Sphk{ 94135704Sphk 95135600Sphk dev_lock(); 96126082Sphk dev->si_refcount++; 97135600Sphk dev_unlock(); 98126082Sphk} 99126082Sphk 100126082Sphkvoid 101130585Sphkdev_rel(struct cdev *dev) 102126082Sphk{ 103135600Sphk 104126082Sphk dev->si_refcount--; 105126082Sphk KASSERT(dev->si_refcount >= 0, 106126082Sphk ("dev_rel(%s) gave negative count", devtoname(dev))); 107126082Sphk if (dev->si_devsw == NULL && dev->si_refcount == 0) { 108126082Sphk LIST_REMOVE(dev, si_list); 109126082Sphk freedev(dev); 110126082Sphk } 111126082Sphk} 112135704Sphkstruct cdevsw * 113135704Sphkdev_refthread(struct cdev *dev) 114135704Sphk{ 115135704Sphk struct cdevsw *csw; 116126082Sphk 117135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 118135704Sphk dev_lock(); 119135704Sphk csw = dev->si_devsw; 120135704Sphk if (csw != NULL) 121135704Sphk dev->si_threadcount++; 122135704Sphk dev_unlock(); 123135704Sphk return (csw); 124135704Sphk} 125135704Sphk 126135704Sphkvoid 127135704Sphkdev_relthread(struct cdev *dev) 128135704Sphk{ 129135704Sphk 130135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 131135704Sphk dev_lock(); 132135704Sphk dev->si_threadcount--; 133135704Sphk dev_unlock(); 134135704Sphk} 135135704Sphk 136120514Sphkint 137120514Sphknullop(void) 138120514Sphk{ 13985603Sphk 140120514Sphk return (0); 141120514Sphk} 142120514Sphk 143120514Sphkint 144120514Sphkeopnotsupp(void) 145120514Sphk{ 146120514Sphk 147120514Sphk return (EOPNOTSUPP); 148120514Sphk} 149120514Sphk 150111179Sphkstatic int 151111179Sphkenxio(void) 152111179Sphk{ 153111179Sphk return (ENXIO); 154111179Sphk} 155111179Sphk 156120514Sphkstatic int 157120514Sphkenodev(void) 158120514Sphk{ 159120514Sphk return (ENODEV); 160120514Sphk} 161120514Sphk 162120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */ 163120514Sphk 164111179Sphk#define dead_open (d_open_t *)enxio 165111179Sphk#define dead_close (d_close_t *)enxio 166111179Sphk#define dead_read (d_read_t *)enxio 167111179Sphk#define dead_write (d_write_t *)enxio 168111179Sphk#define dead_ioctl (d_ioctl_t *)enxio 169120514Sphk#define dead_poll (d_poll_t *)enodev 170120514Sphk#define dead_mmap (d_mmap_t *)enodev 171111179Sphk 172111179Sphkstatic void 173111179Sphkdead_strategy(struct bio *bp) 174111179Sphk{ 175111179Sphk 176111179Sphk biofinish(bp, NULL, ENXIO); 177111179Sphk} 178111179Sphk 179111220Sphk#define dead_dump (dumper_t *)enxio 180111179Sphk#define dead_kqfilter (d_kqfilter_t *)enxio 181111179Sphk 182111179Sphkstatic struct cdevsw dead_cdevsw = { 183126080Sphk .d_version = D_VERSION, 184126080Sphk .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */ 185111815Sphk .d_open = dead_open, 186111815Sphk .d_close = dead_close, 187111815Sphk .d_read = dead_read, 188111815Sphk .d_write = dead_write, 189111815Sphk .d_ioctl = dead_ioctl, 190111815Sphk .d_poll = dead_poll, 191111815Sphk .d_mmap = dead_mmap, 192111815Sphk .d_strategy = dead_strategy, 193111815Sphk .d_name = "dead", 194111815Sphk .d_maj = 255, 195111815Sphk .d_dump = dead_dump, 196111815Sphk .d_kqfilter = dead_kqfilter 197111179Sphk}; 198111179Sphk 199120514Sphk/* Default methods if driver does not specify method */ 200111179Sphk 201120514Sphk#define null_open (d_open_t *)nullop 202120514Sphk#define null_close (d_close_t *)nullop 203120514Sphk#define no_read (d_read_t *)enodev 204120514Sphk#define no_write (d_write_t *)enodev 205120514Sphk#define no_ioctl (d_ioctl_t *)enodev 206120514Sphk#define no_mmap (d_mmap_t *)enodev 207133741Sjmg#define no_kqfilter (d_kqfilter_t *)enodev 208120514Sphk 209120514Sphkstatic void 210120514Sphkno_strategy(struct bio *bp) 211120514Sphk{ 212120514Sphk 213120514Sphk biofinish(bp, NULL, ENODEV); 214120514Sphk} 215120514Sphk 216120514Sphkstatic int 217130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 218120514Sphk{ 219120514Sphk /* 220120514Sphk * Return true for read/write. If the user asked for something 221120514Sphk * special, return POLLNVAL, so that clients have a way of 222120514Sphk * determining reliably whether or not the extended 223120514Sphk * functionality is present without hard-coding knowledge 224120514Sphk * of specific filesystem implementations. 225120514Sphk * Stay in sync with vop_nopoll(). 226120514Sphk */ 227120514Sphk if (events & ~POLLSTANDARD) 228120514Sphk return (POLLNVAL); 229120514Sphk 230120514Sphk return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); 231120514Sphk} 232120514Sphk 233120514Sphk#define no_dump (dumper_t *)enodev 234120514Sphk 23547640Sphk/* 236130936Sle * struct cdev * and u_dev_t primitives 23747028Sphk */ 23847028Sphk 23951927Sphkint 240130585Sphkmajor(struct cdev *x) 24147028Sphk{ 242130640Sphk if (x == NULL) 243130640Sphk return NODEV; 24448936Sphk return((x->si_udev >> 8) & 0xff); 24547028Sphk} 24647028Sphk 24747028Sphkint 248130585Sphkminor(struct cdev *x) 24947028Sphk{ 250130640Sphk if (x == NULL) 251130640Sphk return NODEV; 25248936Sphk return(x->si_udev & 0xffff00ff); 25347028Sphk} 25447028Sphk 25549826Sphkint 256130585Sphkdev2unit(struct cdev *x) 25749826Sphk{ 25849826Sphk int i; 25949826Sphk 260130640Sphk if (x == NULL) 261130640Sphk return NODEV; 26249826Sphk i = minor(x); 26349826Sphk return ((i & 0xff) | (i >> 8)); 26449826Sphk} 26549826Sphk 26666067Sphkint 26766067Sphkunit2minor(int unit) 26866067Sphk{ 26966067Sphk 27074522Sphk KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit)); 27166067Sphk return ((unit & 0xff) | ((unit << 8) & ~0xffff)); 27266067Sphk} 27366067Sphk 274130585Sphkstatic struct cdev * 27564880Sphkallocdev(void) 27664880Sphk{ 27764880Sphk static int stashed; 278104043Sphk struct cdev *si; 27964880Sphk 280103101Sphk if (LIST_FIRST(&dev_free)) { 281103101Sphk si = LIST_FIRST(&dev_free); 282126156Sphk LIST_REMOVE(si, si_hash); 283103101Sphk } else if (stashed >= DEVT_STASH) { 284104043Sphk MALLOC(si, struct cdev *, sizeof(*si), M_DEVT, 285111146Sphk M_USE_RESERVE | M_ZERO | M_WAITOK); 28664880Sphk } else { 28764880Sphk si = devt_stash + stashed++; 28877215Sphk bzero(si, sizeof *si); 289103101Sphk si->si_flags |= SI_STASHED; 29064880Sphk } 291110317Sphk si->__si_namebuf[0] = '\0'; 292110317Sphk si->si_name = si->__si_namebuf; 29377215Sphk LIST_INIT(&si->si_children); 29464880Sphk return (si); 29564880Sphk} 29664880Sphk 297130585Sphkstatic struct cdev * 298130640Sphknewdev(int x, int y) 29947028Sphk{ 300104043Sphk struct cdev *si; 301130640Sphk dev_t udev; 30248936Sphk int hash; 30348936Sphk 304130640Sphk if (x == umajor(NODEV) && y == uminor(NODEV)) 305130640Sphk panic("newdev of NODEV"); 30648936Sphk udev = (x << 8) | y; 30748936Sphk hash = udev % DEVT_HASH; 30850549Sphk LIST_FOREACH(si, &dev_hash[hash], si_hash) { 30948936Sphk if (si->si_udev == udev) 31048936Sphk return (si); 31148936Sphk } 31264880Sphk si = allocdev(); 31348936Sphk si->si_udev = udev; 31450549Sphk LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash); 315125850Sbde return (si); 31647028Sphk} 31747028Sphk 318126082Sphkstatic void 319130585Sphkfreedev(struct cdev *dev) 32050549Sphk{ 32150549Sphk 32250549Sphk if (dev->si_flags & SI_STASHED) { 32350549Sphk bzero(dev, sizeof(*dev)); 32477215Sphk dev->si_flags |= SI_STASHED; 32550549Sphk LIST_INSERT_HEAD(&dev_free, dev, si_hash); 32650549Sphk } else { 32750549Sphk FREE(dev, M_DEVT); 32850549Sphk } 32950549Sphk} 33050549Sphk 331130640Sphkdev_t 332130585Sphkdev2udev(struct cdev *x) 33347028Sphk{ 334130640Sphk if (x == NULL) 335130640Sphk return (NODEV); 33648936Sphk return (x->si_udev); 33747028Sphk} 33847028Sphk 339130585Sphkstruct cdev * 340130640Sphkfindcdev(dev_t udev) 34147028Sphk{ 342126081Sphk struct cdev *si; 343126081Sphk int hash; 34455414Sphk 345130640Sphk if (udev == NODEV) 346130640Sphk return (NULL); 347126081Sphk hash = udev % DEVT_HASH; 348126081Sphk LIST_FOREACH(si, &dev_hash[hash], si_hash) { 349126081Sphk if (si->si_udev == udev) 350126081Sphk return (si); 35148864Sphk } 352130640Sphk return (NULL); 35347028Sphk} 35447028Sphk 35547028Sphkint 356130640Sphkuminor(dev_t dev) 35747028Sphk{ 358125850Sbde return (dev & 0xffff00ff); 35947028Sphk} 36047028Sphk 36147028Sphkint 362130640Sphkumajor(dev_t dev) 36347028Sphk{ 364125850Sbde return ((dev & 0xff00) >> 8); 36547028Sphk} 36647028Sphk 367125846Sphkstatic void 368126077Sphkfind_major(struct cdevsw *devsw) 36949535Sphk{ 37049535Sphk int i; 37149535Sphk 372126077Sphk for (i = NUMCDEVSW - 1; i > 0; i--) 373126077Sphk if (reserved_majors[i] != i) 374126077Sphk break; 375126077Sphk KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name)); 376126077Sphk devsw->d_maj = i; 377126077Sphk reserved_majors[i] = i; 378126082Sphk devsw->d_flags |= D_ALLOCMAJ; 379126077Sphk} 380126077Sphk 381126077Sphkstatic void 382126082Sphkfini_cdevsw(struct cdevsw *devsw) 383126082Sphk{ 384126082Sphk if (devsw->d_flags & D_ALLOCMAJ) { 385126082Sphk reserved_majors[devsw->d_maj] = 0; 386126082Sphk devsw->d_maj = MAJOR_AUTO; 387126082Sphk devsw->d_flags &= ~D_ALLOCMAJ; 388126156Sphk } else if (devsw->d_maj == 0) 389126156Sphk devsw->d_maj = 256; 390126156Sphk devsw->d_flags &= ~D_INIT; 391126082Sphk} 392126082Sphk 393126082Sphkstatic void 394126077Sphkprep_cdevsw(struct cdevsw *devsw) 395126077Sphk{ 396126077Sphk 397135600Sphk dev_lock(); 398126082Sphk 399126082Sphk if (devsw->d_version != D_VERSION_00) { 400126082Sphk printf( 401126082Sphk "WARNING: Device driver \"%s\" has wrong version %s\n", 402126082Sphk devsw->d_name, "and is disabled. Recompile KLD module."); 403126082Sphk devsw->d_open = dead_open; 404126082Sphk devsw->d_close = dead_close; 405126082Sphk devsw->d_read = dead_read; 406126082Sphk devsw->d_write = dead_write; 407126082Sphk devsw->d_ioctl = dead_ioctl; 408126082Sphk devsw->d_poll = dead_poll; 409126082Sphk devsw->d_mmap = dead_mmap; 410126082Sphk devsw->d_strategy = dead_strategy; 411126082Sphk devsw->d_dump = dead_dump; 412126082Sphk devsw->d_kqfilter = dead_kqfilter; 413126082Sphk } 414126082Sphk 415126078Sphk if (devsw->d_flags & D_TTY) { 416129943Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl; 417126078Sphk if (devsw->d_read == NULL) devsw->d_read = ttyread; 418126078Sphk if (devsw->d_write == NULL) devsw->d_write = ttywrite; 419126078Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; 420126078Sphk if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; 421126078Sphk } 422126078Sphk 423120514Sphk if (devsw->d_open == NULL) devsw->d_open = null_open; 424120514Sphk if (devsw->d_close == NULL) devsw->d_close = null_close; 425120514Sphk if (devsw->d_read == NULL) devsw->d_read = no_read; 426120514Sphk if (devsw->d_write == NULL) devsw->d_write = no_write; 427120514Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = no_ioctl; 428120514Sphk if (devsw->d_poll == NULL) devsw->d_poll = no_poll; 429120514Sphk if (devsw->d_mmap == NULL) devsw->d_mmap = no_mmap; 430120514Sphk if (devsw->d_strategy == NULL) devsw->d_strategy = no_strategy; 431120514Sphk if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 432120514Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter; 433126082Sphk 434126082Sphk LIST_INIT(&devsw->d_devs); 435126082Sphk 436126082Sphk devsw->d_flags |= D_INIT; 437126082Sphk 438111622Sphk if (devsw->d_maj == MAJOR_AUTO) { 439126077Sphk find_major(devsw); 440111626Sphk } else { 441112035Sphk if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */ 442112035Sphk devsw->d_maj = 0; 443111626Sphk KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256, 444111626Sphk ("Invalid major (%d) in make_dev", devsw->d_maj)); 445111626Sphk if (reserved_majors[devsw->d_maj] != devsw->d_maj) { 446111626Sphk printf("WARNING: driver \"%s\" used %s %d\n", 447111626Sphk devsw->d_name, "unreserved major device number", 448111626Sphk devsw->d_maj); 449111626Sphk reserved_majors[devsw->d_maj] = devsw->d_maj; 450111626Sphk } 451111622Sphk } 452135600Sphk dev_unlock(); 453125846Sphk} 454111622Sphk 455130585Sphkstruct cdev * 456126082Sphkmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...) 457125846Sphk{ 458130585Sphk struct cdev *dev; 459125846Sphk va_list ap; 460125846Sphk int i; 461125846Sphk 462126082Sphk KASSERT((minornr & ~0xffff00ff) == 0, 463126082Sphk ("Invalid minor (0x%x) in make_dev", minornr)); 464126082Sphk 465126082Sphk if (!(devsw->d_flags & D_INIT)) 466126082Sphk prep_cdevsw(devsw); 467130640Sphk dev = newdev(devsw->d_maj, minornr); 468120529Sphk if (dev->si_flags & SI_CHEAPCLONE && 469120529Sphk dev->si_flags & SI_NAMED && 470120529Sphk dev->si_devsw == devsw) { 471120529Sphk /* 472120529Sphk * This is allowed as it removes races and generally 473120529Sphk * simplifies cloning devices. 474126082Sphk * XXX: still ?? 475120529Sphk */ 476120529Sphk return (dev); 477120529Sphk } 478135600Sphk dev_lock(); 479126082Sphk KASSERT(!(dev->si_flags & SI_NAMED), 480126082Sphk ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)", 481126082Sphk devsw->d_name, major(dev), minor(dev), devtoname(dev))); 482126082Sphk 48349535Sphk va_start(ap, fmt); 484110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 485110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 486134501Spjd printf("WARNING: Device name truncated! (%s)\n", 487110318Sphk dev->__si_namebuf); 488110318Sphk } 48949535Sphk va_end(ap); 49049535Sphk dev->si_devsw = devsw; 49164880Sphk dev->si_uid = uid; 49264880Sphk dev->si_gid = gid; 49364880Sphk dev->si_mode = perms; 49465747Sphk dev->si_flags |= SI_NAMED; 49550092Sjulian 496126082Sphk LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list); 497111730Sphk devfs_create(dev); 498135600Sphk dev_unlock(); 49949535Sphk return (dev); 50049535Sphk} 50149535Sphk 50285076Sjlemonint 503130585Sphkdev_named(struct cdev *pdev, const char *name) 50485076Sjlemon{ 505130585Sphk struct cdev *cdev; 50685076Sjlemon 50785076Sjlemon if (strcmp(devtoname(pdev), name) == 0) 50885076Sjlemon return (1); 50985076Sjlemon LIST_FOREACH(cdev, &pdev->si_children, si_siblings) 51085076Sjlemon if (strcmp(devtoname(cdev), name) == 0) 51185076Sjlemon return (1); 51285076Sjlemon return (0); 51385076Sjlemon} 51485076Sjlemon 51577215Sphkvoid 516130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev) 51777215Sphk{ 51877215Sphk 519135600Sphk dev_lock(); 52077215Sphk cdev->si_parent = pdev; 52177215Sphk cdev->si_flags |= SI_CHILD; 52277215Sphk LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 523135600Sphk dev_unlock(); 52477215Sphk} 52577215Sphk 526130585Sphkstruct cdev * 527130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...) 52864880Sphk{ 529130585Sphk struct cdev *dev; 53064880Sphk va_list ap; 53164880Sphk int i; 53264880Sphk 53364880Sphk dev = allocdev(); 534135600Sphk dev_lock(); 53564880Sphk dev->si_flags |= SI_ALIAS; 53665747Sphk dev->si_flags |= SI_NAMED; 53764880Sphk va_start(ap, fmt); 538110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 539110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 540134501Spjd printf("WARNING: Device name truncated! (%s)\n", 541110318Sphk dev->__si_namebuf); 542110318Sphk } 54364880Sphk va_end(ap); 54464880Sphk 545111730Sphk devfs_create(dev); 546135600Sphk dev_unlock(); 547126082Sphk dev_depends(pdev, dev); 54864880Sphk return (dev); 54964880Sphk} 55064880Sphk 551126082Sphkstatic void 552130585Sphkidestroy_dev(struct cdev *dev) 55350549Sphk{ 554135843Sphk struct cdevsw *csw; 555135843Sphk 556135843Sphk KASSERT(dev->si_flags & SI_NAMED, 557135843Sphk ("WARNING: Driver mistake: destroy_dev on %d/%d\n", 558135843Sphk major(dev), minor(dev))); 55965747Sphk 560111730Sphk devfs_destroy(dev); 561126082Sphk 562126082Sphk /* Remove name marking */ 563126077Sphk dev->si_flags &= ~SI_NAMED; 564126077Sphk 565126082Sphk /* If we are a child, remove us from the parents list */ 56677215Sphk if (dev->si_flags & SI_CHILD) { 56777215Sphk LIST_REMOVE(dev, si_siblings); 56877215Sphk dev->si_flags &= ~SI_CHILD; 56977215Sphk } 570126082Sphk 571126082Sphk /* Kill our children */ 57277215Sphk while (!LIST_EMPTY(&dev->si_children)) 573126082Sphk idestroy_dev(LIST_FIRST(&dev->si_children)); 574126082Sphk 575126082Sphk /* Remove from clone list */ 576126077Sphk if (dev->si_flags & SI_CLONELIST) { 577126077Sphk LIST_REMOVE(dev, si_clone); 578126077Sphk dev->si_flags &= ~SI_CLONELIST; 579126077Sphk } 580126082Sphk 581135843Sphk csw = dev->si_devsw; 582135934Sgreen dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 583135934Sgreen while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 584135843Sphk printf("Purging %lu threads from %s\n", 585135843Sphk dev->si_threadcount, devtoname(dev)); 586135843Sphk csw->d_purge(dev); 587135843Sphk msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 588135843Sphk } 589135934Sgreen if (csw != NULL && csw->d_purge != NULL) 590135844Sphk printf("All threads purged from %s\n", devtoname(dev)); 591135843Sphk 592135843Sphk dev->si_drv1 = 0; 593135843Sphk dev->si_drv2 = 0; 594135843Sphk bzero(&dev->__si_u, sizeof(dev->__si_u)); 595135843Sphk 596126082Sphk if (!(dev->si_flags & SI_ALIAS)) { 597126082Sphk /* Remove from cdevsw list */ 598126082Sphk LIST_REMOVE(dev, si_list); 599126082Sphk 600130585Sphk /* If cdevsw has no struct cdev *'s, clean it */ 601135844Sphk if (LIST_EMPTY(&csw->d_devs)) 602135844Sphk fini_cdevsw(csw); 603126082Sphk 604126082Sphk LIST_REMOVE(dev, si_hash); 605126082Sphk } 60665747Sphk dev->si_flags &= ~SI_ALIAS; 607135843Sphk 608126082Sphk if (dev->si_refcount > 0) { 609126082Sphk LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 610126082Sphk } else { 611126082Sphk freedev(dev); 612126082Sphk } 61350549Sphk} 61450549Sphk 615126082Sphkvoid 616130585Sphkdestroy_dev(struct cdev *dev) 617126082Sphk{ 618126082Sphk 619135600Sphk dev_lock(); 620126082Sphk idestroy_dev(dev); 621135600Sphk dev_unlock(); 622126082Sphk} 623126082Sphk 62451225Sbdeconst char * 625130585Sphkdevtoname(struct cdev *dev) 62649982Sbillf{ 62750549Sphk char *p; 628135712Sphk struct cdevsw *csw; 62951225Sbde int mynor; 63049982Sbillf 63150549Sphk if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 63250549Sphk p = dev->si_name; 633135712Sphk sprintf(p, "#%d", major(dev)); 63450549Sphk p += strlen(p); 635135712Sphk csw = dev_refthread(dev); 636135712Sphk if (csw != NULL) { 637135712Sphk sprintf(p, "(%s)", csw->d_name); 638135712Sphk dev_relthread(dev); 639135712Sphk } 640135712Sphk p += strlen(p); 64151225Sbde mynor = minor(dev); 64251225Sbde if (mynor < 0 || mynor > 255) 643135712Sphk sprintf(p, "/%#x", (u_int)mynor); 64451225Sbde else 645135712Sphk sprintf(p, "/%d", mynor); 64650549Sphk } 64749982Sbillf return (dev->si_name); 64849982Sbillf} 64965374Sphk 65065374Sphkint 65191998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit) 65265374Sphk{ 65365374Sphk int u, i; 65465374Sphk 65575519Sbrian i = strlen(stem); 65675519Sbrian if (bcmp(stem, name, i) != 0) 65765374Sphk return (0); 65865374Sphk if (!isdigit(name[i])) 65965374Sphk return (0); 66065374Sphk u = 0; 66186461Sphk if (name[i] == '0' && isdigit(name[i+1])) 66286461Sphk return (0); 66365374Sphk while (isdigit(name[i])) { 66465374Sphk u *= 10; 66565374Sphk u += name[i++] - '0'; 66665374Sphk } 667104523Sgreen if (u > 0xffffff) 668104523Sgreen return (0); 66965374Sphk *unit = u; 67065374Sphk if (namep) 67165374Sphk *namep = &name[i]; 67265374Sphk if (name[i]) 67365374Sphk return (2); 67465374Sphk return (1); 67565374Sphk} 67665632Sphk 67765632Sphk/* 678126077Sphk * Helper functions for cloning device drivers. 679126077Sphk * 680126077Sphk * The objective here is to make it unnecessary for the device drivers to 681126077Sphk * use rman or similar to manage their unit number space. Due to the way 682126077Sphk * we do "on-demand" devices, using rman or other "private" methods 683126077Sphk * will be very tricky to lock down properly once we lock down this file. 684126077Sphk * 685130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s 686130936Sle * that are to be managed on their own list, and gives the driver the ability 687126077Sphk * to ask for the first free unit number or a given specified unit number. 688126077Sphk * 689126077Sphk * In addition these routines support paired devices (pty, nmdm and similar) 690126077Sphk * by respecting a number of "flag" bits in the minor number. 691126077Sphk * 692126077Sphk */ 693126077Sphk 694126077Sphkstruct clonedevs { 695126077Sphk LIST_HEAD(,cdev) head; 696126077Sphk}; 697126077Sphk 698126845Sphkvoid 699126845Sphkclone_setup(struct clonedevs **cdp) 700126845Sphk{ 701126845Sphk 702126845Sphk *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 703126845Sphk LIST_INIT(&(*cdp)->head); 704126845Sphk} 705126845Sphk 706126077Sphkint 707130585Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra) 708126077Sphk{ 709126077Sphk struct clonedevs *cd; 710130585Sphk struct cdev *dev, *dl, *de; 711126077Sphk int unit, low, u; 712126077Sphk 713126845Sphk KASSERT(*cdp != NULL, 714126845Sphk ("clone_setup() not called in driver \"%s\"", csw->d_name)); 715126077Sphk KASSERT(!(extra & CLONE_UNITMASK), 716126845Sphk ("Illegal extra bits (0x%x) in clone_create", extra)); 717126077Sphk KASSERT(*up <= CLONE_UNITMASK, 718126845Sphk ("Too high unit (0x%x) in clone_create", *up)); 719126077Sphk 720126077Sphk if (csw->d_maj == MAJOR_AUTO) 721126077Sphk find_major(csw); 722126077Sphk 723126077Sphk /* 724126077Sphk * Search the list for a lot of things in one go: 725126077Sphk * A preexisting match is returned immediately. 726126077Sphk * The lowest free unit number if we are passed -1, and the place 727126077Sphk * in the list where we should insert that new element. 728126077Sphk * The place to insert a specified unit number, if applicable 729126077Sphk * the end of the list. 730126077Sphk */ 731126077Sphk unit = *up; 732126849Sphk low = extra; 733126077Sphk de = dl = NULL; 734126845Sphk cd = *cdp; 735126077Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 736126077Sphk u = dev2unit(dev); 737126077Sphk if (u == (unit | extra)) { 738126077Sphk *dp = dev; 739126077Sphk return (0); 740126077Sphk } 741126077Sphk if (unit == -1 && u == low) { 742126077Sphk low++; 743126077Sphk de = dev; 744126077Sphk continue; 745126077Sphk } 746126849Sphk if (u > (unit | extra)) { 747126077Sphk dl = dev; 748126077Sphk break; 749126077Sphk } 750126077Sphk de = dev; 751126077Sphk } 752126077Sphk if (unit == -1) 753126849Sphk unit = low & CLONE_UNITMASK; 754130640Sphk dev = newdev(csw->d_maj, unit2minor(unit | extra)); 755126077Sphk KASSERT(!(dev->si_flags & SI_CLONELIST), 756126077Sphk ("Dev %p should not be on clonelist", dev)); 757126077Sphk if (dl != NULL) 758126077Sphk LIST_INSERT_BEFORE(dl, dev, si_clone); 759126077Sphk else if (de != NULL) 760126077Sphk LIST_INSERT_AFTER(de, dev, si_clone); 761126077Sphk else 762126077Sphk LIST_INSERT_HEAD(&cd->head, dev, si_clone); 763126077Sphk dev->si_flags |= SI_CLONELIST; 764126077Sphk *up = unit; 765126077Sphk return (1); 766126077Sphk} 767126077Sphk 768126077Sphk/* 769126077Sphk * Kill everything still on the list. The driver should already have 770130585Sphk * disposed of any softc hung of the struct cdev *'s at this time. 771126077Sphk */ 772126077Sphkvoid 773126077Sphkclone_cleanup(struct clonedevs **cdp) 774126077Sphk{ 775130585Sphk struct cdev *dev, *tdev; 776126077Sphk struct clonedevs *cd; 777126077Sphk 778126077Sphk cd = *cdp; 779126077Sphk if (cd == NULL) 780126077Sphk return; 781126077Sphk LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { 782126077Sphk KASSERT(dev->si_flags & SI_NAMED, 783126077Sphk ("Driver has goofed in cloning underways udev %x", dev->si_udev)); 784126077Sphk destroy_dev(dev); 785126077Sphk } 786126077Sphk free(cd, M_DEVBUF); 787126077Sphk *cdp = NULL; 788126077Sphk} 789126077Sphk 790126077Sphk/* 791130936Sle * Helper sysctl for devname(3). We're given a struct cdev * and return 79265632Sphk * the name, if any, registered by the device driver. 79365632Sphk */ 79465632Sphkstatic int 79565632Sphksysctl_devname(SYSCTL_HANDLER_ARGS) 79665632Sphk{ 79765632Sphk int error; 798130640Sphk dev_t ud; 799130585Sphk struct cdev *dev; 80065632Sphk 80165632Sphk error = SYSCTL_IN(req, &ud, sizeof (ud)); 80265632Sphk if (error) 80365632Sphk return (error); 804130640Sphk if (ud == NODEV) 80571342Sphk return(EINVAL); 806130640Sphk dev = findcdev(ud); 807130640Sphk if (dev == NULL) 80865632Sphk error = ENOENT; 80965632Sphk else 81065632Sphk error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1); 81165632Sphk return (error); 81265632Sphk} 81365632Sphk 81467905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY, 81565632Sphk NULL, 0, sysctl_devname, "", "devname(3) handler"); 816