kern_conf.c revision 140964
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 140964 2005-01-29 15:10:30Z phk $"); 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 59104043Sphkstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH]; 6048936Sphk 61126082Sphkstatic struct mtx devmtx; 62130585Sphkstatic void freedev(struct cdev *dev); 63140733Sphkstatic struct cdev *newdev(int x, int y, struct cdev *); 64126082Sphk 65135600Sphkvoid 66135600Sphkdev_lock(void) 67126082Sphk{ 68126082Sphk if (!mtx_initialized(&devmtx)) 69131996Sphk mtx_init(&devmtx, "cdev", NULL, MTX_DEF); 70126082Sphk mtx_lock(&devmtx); 71126082Sphk} 72126082Sphk 73135600Sphkvoid 74135600Sphkdev_unlock(void) 75126082Sphk{ 76135704Sphk 77126082Sphk mtx_unlock(&devmtx); 78126082Sphk} 79126082Sphk 80126082Sphkvoid 81130585Sphkdev_ref(struct cdev *dev) 82126082Sphk{ 83135704Sphk 84135600Sphk dev_lock(); 85126082Sphk dev->si_refcount++; 86135600Sphk dev_unlock(); 87126082Sphk} 88126082Sphk 89126082Sphkvoid 90136014Sphkdev_rel(struct vnode *vp) 91126082Sphk{ 92136014Sphk struct cdev *dev; 93136014Sphk int flag; 94135600Sphk 95136014Sphk dev = vp->v_rdev; 96136014Sphk mtx_assert(&devmtx, MA_NOTOWNED); 97136014Sphk dev_lock(); 98136014Sphk SLIST_REMOVE(&dev->si_hlist, vp, vnode, v_specnext); 99136014Sphk dev->si_usecount -= vp->v_usecount; 100136014Sphk vp->v_rdev = NULL; 101126082Sphk dev->si_refcount--; 102126082Sphk KASSERT(dev->si_refcount >= 0, 103126082Sphk ("dev_rel(%s) gave negative count", devtoname(dev))); 104136014Sphk flag = 0; 105126082Sphk if (dev->si_devsw == NULL && dev->si_refcount == 0) { 106126082Sphk LIST_REMOVE(dev, si_list); 107136014Sphk flag = 1; 108136014Sphk } 109136014Sphk dev_unlock(); 110136014Sphk if (flag) 111126082Sphk freedev(dev); 112136014Sphk return; 113126082Sphk} 114136014Sphk 115135704Sphkstruct cdevsw * 116135704Sphkdev_refthread(struct cdev *dev) 117135704Sphk{ 118135704Sphk struct cdevsw *csw; 119126082Sphk 120135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 121135704Sphk dev_lock(); 122135704Sphk csw = dev->si_devsw; 123135704Sphk if (csw != NULL) 124135704Sphk dev->si_threadcount++; 125135704Sphk dev_unlock(); 126135704Sphk return (csw); 127135704Sphk} 128135704Sphk 129135704Sphkvoid 130135704Sphkdev_relthread(struct cdev *dev) 131135704Sphk{ 132135704Sphk 133135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 134135704Sphk dev_lock(); 135135704Sphk dev->si_threadcount--; 136135704Sphk dev_unlock(); 137135704Sphk} 138135704Sphk 139120514Sphkint 140120514Sphknullop(void) 141120514Sphk{ 14285603Sphk 143120514Sphk return (0); 144120514Sphk} 145120514Sphk 146120514Sphkint 147120514Sphkeopnotsupp(void) 148120514Sphk{ 149120514Sphk 150120514Sphk return (EOPNOTSUPP); 151120514Sphk} 152120514Sphk 153111179Sphkstatic int 154111179Sphkenxio(void) 155111179Sphk{ 156111179Sphk return (ENXIO); 157111179Sphk} 158111179Sphk 159120514Sphkstatic int 160120514Sphkenodev(void) 161120514Sphk{ 162120514Sphk return (ENODEV); 163120514Sphk} 164120514Sphk 165120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */ 166120514Sphk 167111179Sphk#define dead_open (d_open_t *)enxio 168111179Sphk#define dead_close (d_close_t *)enxio 169111179Sphk#define dead_read (d_read_t *)enxio 170111179Sphk#define dead_write (d_write_t *)enxio 171111179Sphk#define dead_ioctl (d_ioctl_t *)enxio 172120514Sphk#define dead_poll (d_poll_t *)enodev 173120514Sphk#define dead_mmap (d_mmap_t *)enodev 174111179Sphk 175111179Sphkstatic void 176111179Sphkdead_strategy(struct bio *bp) 177111179Sphk{ 178111179Sphk 179111179Sphk biofinish(bp, NULL, ENXIO); 180111179Sphk} 181111179Sphk 182111220Sphk#define dead_dump (dumper_t *)enxio 183111179Sphk#define dead_kqfilter (d_kqfilter_t *)enxio 184111179Sphk 185111179Sphkstatic struct cdevsw dead_cdevsw = { 186126080Sphk .d_version = D_VERSION, 187126080Sphk .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */ 188111815Sphk .d_open = dead_open, 189111815Sphk .d_close = dead_close, 190111815Sphk .d_read = dead_read, 191111815Sphk .d_write = dead_write, 192111815Sphk .d_ioctl = dead_ioctl, 193111815Sphk .d_poll = dead_poll, 194111815Sphk .d_mmap = dead_mmap, 195111815Sphk .d_strategy = dead_strategy, 196111815Sphk .d_name = "dead", 197111815Sphk .d_maj = 255, 198111815Sphk .d_dump = dead_dump, 199111815Sphk .d_kqfilter = dead_kqfilter 200111179Sphk}; 201111179Sphk 202120514Sphk/* Default methods if driver does not specify method */ 203111179Sphk 204120514Sphk#define null_open (d_open_t *)nullop 205120514Sphk#define null_close (d_close_t *)nullop 206120514Sphk#define no_read (d_read_t *)enodev 207120514Sphk#define no_write (d_write_t *)enodev 208120514Sphk#define no_ioctl (d_ioctl_t *)enodev 209120514Sphk#define no_mmap (d_mmap_t *)enodev 210133741Sjmg#define no_kqfilter (d_kqfilter_t *)enodev 211120514Sphk 212120514Sphkstatic void 213120514Sphkno_strategy(struct bio *bp) 214120514Sphk{ 215120514Sphk 216120514Sphk biofinish(bp, NULL, ENODEV); 217120514Sphk} 218120514Sphk 219120514Sphkstatic int 220130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 221120514Sphk{ 222120514Sphk /* 223120514Sphk * Return true for read/write. If the user asked for something 224120514Sphk * special, return POLLNVAL, so that clients have a way of 225120514Sphk * determining reliably whether or not the extended 226120514Sphk * functionality is present without hard-coding knowledge 227120514Sphk * of specific filesystem implementations. 228120514Sphk * Stay in sync with vop_nopoll(). 229120514Sphk */ 230120514Sphk if (events & ~POLLSTANDARD) 231120514Sphk return (POLLNVAL); 232120514Sphk 233120514Sphk return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); 234120514Sphk} 235120514Sphk 236120514Sphk#define no_dump (dumper_t *)enodev 237120514Sphk 23847640Sphk/* 239130936Sle * struct cdev * and u_dev_t primitives 24047028Sphk */ 24147028Sphk 24251927Sphkint 243130585Sphkmajor(struct cdev *x) 24447028Sphk{ 245130640Sphk if (x == NULL) 246130640Sphk return NODEV; 24748936Sphk return((x->si_udev >> 8) & 0xff); 24847028Sphk} 24947028Sphk 25047028Sphkint 251130585Sphkminor(struct cdev *x) 25247028Sphk{ 253130640Sphk if (x == NULL) 254130640Sphk return NODEV; 25548936Sphk return(x->si_udev & 0xffff00ff); 25647028Sphk} 25747028Sphk 25849826Sphkint 259130585Sphkdev2unit(struct cdev *x) 26049826Sphk{ 26149826Sphk 262130640Sphk if (x == NULL) 263130640Sphk return NODEV; 264140964Sphk return (minor2unit(minor(x))); 26549826Sphk} 26649826Sphk 26766067Sphkint 268140963Sphkminor2unit(int _minor) 269140963Sphk{ 270140963Sphk 271140963Sphk KASSERT((_minor & 0xff00) == 0, ("Illegal minor %x", _minor)); 272140963Sphk return ((_minor & 0xff) | (_minor >> 8)); 273140963Sphk} 274140963Sphk 275140963Sphkint 27666067Sphkunit2minor(int unit) 27766067Sphk{ 27866067Sphk 27974522Sphk KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit)); 28066067Sphk return ((unit & 0xff) | ((unit << 8) & ~0xffff)); 28166067Sphk} 28266067Sphk 283130585Sphkstatic struct cdev * 28464880Sphkallocdev(void) 28564880Sphk{ 286104043Sphk struct cdev *si; 28764880Sphk 288136947Sphk si = malloc(sizeof *si, M_DEVT, M_USE_RESERVE | M_ZERO | M_WAITOK); 289110317Sphk si->si_name = si->__si_namebuf; 29077215Sphk LIST_INIT(&si->si_children); 29164880Sphk return (si); 29264880Sphk} 29364880Sphk 294130585Sphkstatic struct cdev * 295140733Sphknewdev(int x, int y, struct cdev *si) 29647028Sphk{ 297140733Sphk struct cdev *si2; 298130640Sphk dev_t udev; 29948936Sphk int hash; 30048936Sphk 301140733Sphk mtx_assert(&devmtx, MA_OWNED); 302130640Sphk if (x == umajor(NODEV) && y == uminor(NODEV)) 303130640Sphk panic("newdev of NODEV"); 30448936Sphk udev = (x << 8) | y; 30548936Sphk hash = udev % DEVT_HASH; 306140733Sphk LIST_FOREACH(si2, &dev_hash[hash], si_hash) { 307140733Sphk if (si2->si_udev == udev) { 308140733Sphk freedev(si); 309140733Sphk return (si2); 310140733Sphk } 31148936Sphk } 31248936Sphk si->si_udev = udev; 31350549Sphk LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash); 314125850Sbde return (si); 31547028Sphk} 31647028Sphk 317126082Sphkstatic void 318130585Sphkfreedev(struct cdev *dev) 31950549Sphk{ 32050549Sphk 321136947Sphk free(dev, M_DEVT); 32250549Sphk} 32350549Sphk 324130640Sphkdev_t 325130585Sphkdev2udev(struct cdev *x) 32647028Sphk{ 327130640Sphk if (x == NULL) 328130640Sphk return (NODEV); 32948936Sphk return (x->si_udev); 33047028Sphk} 33147028Sphk 332130585Sphkstruct cdev * 333130640Sphkfindcdev(dev_t udev) 33447028Sphk{ 335126081Sphk struct cdev *si; 336126081Sphk int hash; 33755414Sphk 338140733Sphk mtx_assert(&devmtx, MA_NOTOWNED); 339130640Sphk if (udev == NODEV) 340130640Sphk return (NULL); 341140733Sphk dev_lock(); 342126081Sphk hash = udev % DEVT_HASH; 343126081Sphk LIST_FOREACH(si, &dev_hash[hash], si_hash) { 344126081Sphk if (si->si_udev == udev) 345140733Sphk break; 34648864Sphk } 347140733Sphk dev_unlock(); 348140733Sphk return (si); 34947028Sphk} 35047028Sphk 35147028Sphkint 352130640Sphkuminor(dev_t dev) 35347028Sphk{ 354125850Sbde return (dev & 0xffff00ff); 35547028Sphk} 35647028Sphk 35747028Sphkint 358130640Sphkumajor(dev_t dev) 35947028Sphk{ 360125850Sbde return ((dev & 0xff00) >> 8); 36147028Sphk} 36247028Sphk 363125846Sphkstatic void 364126077Sphkfind_major(struct cdevsw *devsw) 36549535Sphk{ 36649535Sphk int i; 36749535Sphk 368126077Sphk for (i = NUMCDEVSW - 1; i > 0; i--) 369126077Sphk if (reserved_majors[i] != i) 370126077Sphk break; 371126077Sphk KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name)); 372126077Sphk devsw->d_maj = i; 373126077Sphk reserved_majors[i] = i; 374126082Sphk devsw->d_flags |= D_ALLOCMAJ; 375126077Sphk} 376126077Sphk 377126077Sphkstatic void 378126082Sphkfini_cdevsw(struct cdevsw *devsw) 379126082Sphk{ 380126082Sphk if (devsw->d_flags & D_ALLOCMAJ) { 381126082Sphk reserved_majors[devsw->d_maj] = 0; 382126082Sphk devsw->d_maj = MAJOR_AUTO; 383126082Sphk devsw->d_flags &= ~D_ALLOCMAJ; 384126156Sphk } else if (devsw->d_maj == 0) 385126156Sphk devsw->d_maj = 256; 386126156Sphk devsw->d_flags &= ~D_INIT; 387126082Sphk} 388126082Sphk 389126082Sphkstatic void 390126077Sphkprep_cdevsw(struct cdevsw *devsw) 391126077Sphk{ 392126077Sphk 393135600Sphk dev_lock(); 394126082Sphk 395126082Sphk if (devsw->d_version != D_VERSION_00) { 396126082Sphk printf( 397126082Sphk "WARNING: Device driver \"%s\" has wrong version %s\n", 398126082Sphk devsw->d_name, "and is disabled. Recompile KLD module."); 399126082Sphk devsw->d_open = dead_open; 400126082Sphk devsw->d_close = dead_close; 401126082Sphk devsw->d_read = dead_read; 402126082Sphk devsw->d_write = dead_write; 403126082Sphk devsw->d_ioctl = dead_ioctl; 404126082Sphk devsw->d_poll = dead_poll; 405126082Sphk devsw->d_mmap = dead_mmap; 406126082Sphk devsw->d_strategy = dead_strategy; 407126082Sphk devsw->d_dump = dead_dump; 408126082Sphk devsw->d_kqfilter = dead_kqfilter; 409126082Sphk } 410126082Sphk 411126078Sphk if (devsw->d_flags & D_TTY) { 412129943Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl; 413126078Sphk if (devsw->d_read == NULL) devsw->d_read = ttyread; 414126078Sphk if (devsw->d_write == NULL) devsw->d_write = ttywrite; 415126078Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; 416126078Sphk if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; 417126078Sphk } 418126078Sphk 419120514Sphk if (devsw->d_open == NULL) devsw->d_open = null_open; 420120514Sphk if (devsw->d_close == NULL) devsw->d_close = null_close; 421120514Sphk if (devsw->d_read == NULL) devsw->d_read = no_read; 422120514Sphk if (devsw->d_write == NULL) devsw->d_write = no_write; 423120514Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = no_ioctl; 424120514Sphk if (devsw->d_poll == NULL) devsw->d_poll = no_poll; 425120514Sphk if (devsw->d_mmap == NULL) devsw->d_mmap = no_mmap; 426120514Sphk if (devsw->d_strategy == NULL) devsw->d_strategy = no_strategy; 427120514Sphk if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 428120514Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter; 429126082Sphk 430126082Sphk LIST_INIT(&devsw->d_devs); 431126082Sphk 432126082Sphk devsw->d_flags |= D_INIT; 433126082Sphk 434111622Sphk if (devsw->d_maj == MAJOR_AUTO) { 435126077Sphk find_major(devsw); 436111626Sphk } else { 437112035Sphk if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */ 438112035Sphk devsw->d_maj = 0; 439111626Sphk KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256, 440111626Sphk ("Invalid major (%d) in make_dev", devsw->d_maj)); 441111626Sphk if (reserved_majors[devsw->d_maj] != devsw->d_maj) { 442111626Sphk printf("WARNING: driver \"%s\" used %s %d\n", 443111626Sphk devsw->d_name, "unreserved major device number", 444111626Sphk devsw->d_maj); 445111626Sphk reserved_majors[devsw->d_maj] = devsw->d_maj; 446111626Sphk } 447111622Sphk } 448135600Sphk dev_unlock(); 449125846Sphk} 450111622Sphk 451130585Sphkstruct cdev * 452126082Sphkmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, const char *fmt, ...) 453125846Sphk{ 454130585Sphk struct cdev *dev; 455125846Sphk va_list ap; 456125846Sphk int i; 457125846Sphk 458126082Sphk KASSERT((minornr & ~0xffff00ff) == 0, 459126082Sphk ("Invalid minor (0x%x) in make_dev", minornr)); 460126082Sphk 461126082Sphk if (!(devsw->d_flags & D_INIT)) 462126082Sphk prep_cdevsw(devsw); 463140733Sphk dev = allocdev(); 464140733Sphk dev_lock(); 465140733Sphk dev = newdev(devsw->d_maj, minornr, dev); 466120529Sphk if (dev->si_flags & SI_CHEAPCLONE && 467120529Sphk dev->si_flags & SI_NAMED && 468120529Sphk dev->si_devsw == devsw) { 469120529Sphk /* 470120529Sphk * This is allowed as it removes races and generally 471120529Sphk * simplifies cloning devices. 472126082Sphk * XXX: still ?? 473120529Sphk */ 474140733Sphk dev_unlock(); 475120529Sphk return (dev); 476120529Sphk } 477126082Sphk KASSERT(!(dev->si_flags & SI_NAMED), 478126082Sphk ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)", 479126082Sphk devsw->d_name, major(dev), minor(dev), devtoname(dev))); 480126082Sphk 48149535Sphk va_start(ap, fmt); 482110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 483110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 484134501Spjd printf("WARNING: Device name truncated! (%s)\n", 485110318Sphk dev->__si_namebuf); 486110318Sphk } 48749535Sphk va_end(ap); 488136947Sphk 48949535Sphk dev->si_devsw = devsw; 49064880Sphk dev->si_uid = uid; 49164880Sphk dev->si_gid = gid; 49264880Sphk dev->si_mode = perms; 49365747Sphk dev->si_flags |= SI_NAMED; 49450092Sjulian 495126082Sphk LIST_INSERT_HEAD(&devsw->d_devs, dev, si_list); 496111730Sphk devfs_create(dev); 497135600Sphk dev_unlock(); 49849535Sphk return (dev); 49949535Sphk} 50049535Sphk 50185076Sjlemonint 502130585Sphkdev_named(struct cdev *pdev, const char *name) 50385076Sjlemon{ 504130585Sphk struct cdev *cdev; 50585076Sjlemon 50685076Sjlemon if (strcmp(devtoname(pdev), name) == 0) 50785076Sjlemon return (1); 50885076Sjlemon LIST_FOREACH(cdev, &pdev->si_children, si_siblings) 50985076Sjlemon if (strcmp(devtoname(cdev), name) == 0) 51085076Sjlemon return (1); 51185076Sjlemon return (0); 51285076Sjlemon} 51385076Sjlemon 51477215Sphkvoid 515130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev) 51677215Sphk{ 51777215Sphk 518135600Sphk dev_lock(); 51977215Sphk cdev->si_parent = pdev; 52077215Sphk cdev->si_flags |= SI_CHILD; 52177215Sphk LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 522135600Sphk dev_unlock(); 52377215Sphk} 52477215Sphk 525130585Sphkstruct cdev * 526130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...) 52764880Sphk{ 528130585Sphk struct cdev *dev; 52964880Sphk va_list ap; 53064880Sphk int i; 53164880Sphk 53264880Sphk dev = allocdev(); 533135600Sphk dev_lock(); 53464880Sphk dev->si_flags |= SI_ALIAS; 53565747Sphk dev->si_flags |= SI_NAMED; 53664880Sphk va_start(ap, fmt); 537110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 538110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 539134501Spjd printf("WARNING: Device name truncated! (%s)\n", 540110318Sphk dev->__si_namebuf); 541110318Sphk } 54264880Sphk va_end(ap); 54364880Sphk 544111730Sphk devfs_create(dev); 545135600Sphk dev_unlock(); 546126082Sphk dev_depends(pdev, dev); 54764880Sphk return (dev); 54864880Sphk} 54964880Sphk 550126082Sphkstatic void 551130585Sphkidestroy_dev(struct cdev *dev) 55250549Sphk{ 553135843Sphk struct cdevsw *csw; 554135843Sphk 555135843Sphk KASSERT(dev->si_flags & SI_NAMED, 556135843Sphk ("WARNING: Driver mistake: destroy_dev on %d/%d\n", 557135843Sphk major(dev), minor(dev))); 55865747Sphk 559111730Sphk devfs_destroy(dev); 560126082Sphk 561126082Sphk /* Remove name marking */ 562126077Sphk dev->si_flags &= ~SI_NAMED; 563126077Sphk 564126082Sphk /* If we are a child, remove us from the parents list */ 56577215Sphk if (dev->si_flags & SI_CHILD) { 56677215Sphk LIST_REMOVE(dev, si_siblings); 56777215Sphk dev->si_flags &= ~SI_CHILD; 56877215Sphk } 569126082Sphk 570126082Sphk /* Kill our children */ 57177215Sphk while (!LIST_EMPTY(&dev->si_children)) 572126082Sphk idestroy_dev(LIST_FIRST(&dev->si_children)); 573126082Sphk 574126082Sphk /* Remove from clone list */ 575126077Sphk if (dev->si_flags & SI_CLONELIST) { 576126077Sphk LIST_REMOVE(dev, si_clone); 577126077Sphk dev->si_flags &= ~SI_CLONELIST; 578126077Sphk } 579126082Sphk 580135843Sphk csw = dev->si_devsw; 581135934Sgreen dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 582135934Sgreen while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 583135843Sphk printf("Purging %lu threads from %s\n", 584135843Sphk dev->si_threadcount, devtoname(dev)); 585135843Sphk csw->d_purge(dev); 586135843Sphk msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 587135843Sphk } 588135934Sgreen if (csw != NULL && csw->d_purge != NULL) 589135844Sphk printf("All threads purged from %s\n", devtoname(dev)); 590135843Sphk 591135843Sphk dev->si_drv1 = 0; 592135843Sphk dev->si_drv2 = 0; 593135843Sphk bzero(&dev->__si_u, sizeof(dev->__si_u)); 594135843Sphk 595126082Sphk if (!(dev->si_flags & SI_ALIAS)) { 596126082Sphk /* Remove from cdevsw list */ 597126082Sphk LIST_REMOVE(dev, si_list); 598126082Sphk 599130585Sphk /* If cdevsw has no struct cdev *'s, clean it */ 600135844Sphk if (LIST_EMPTY(&csw->d_devs)) 601135844Sphk fini_cdevsw(csw); 602126082Sphk 603126082Sphk LIST_REMOVE(dev, si_hash); 604126082Sphk } 60565747Sphk dev->si_flags &= ~SI_ALIAS; 606135843Sphk 607126082Sphk if (dev->si_refcount > 0) { 608126082Sphk LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 609126082Sphk } else { 610126082Sphk freedev(dev); 611126082Sphk } 61250549Sphk} 61350549Sphk 614126082Sphkvoid 615130585Sphkdestroy_dev(struct cdev *dev) 616126082Sphk{ 617126082Sphk 618135600Sphk dev_lock(); 619126082Sphk idestroy_dev(dev); 620135600Sphk dev_unlock(); 621126082Sphk} 622126082Sphk 62351225Sbdeconst char * 624130585Sphkdevtoname(struct cdev *dev) 62549982Sbillf{ 62650549Sphk char *p; 627135712Sphk struct cdevsw *csw; 62851225Sbde int mynor; 62949982Sbillf 63050549Sphk if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 63150549Sphk p = dev->si_name; 632135712Sphk sprintf(p, "#%d", major(dev)); 63350549Sphk p += strlen(p); 634135712Sphk csw = dev_refthread(dev); 635135712Sphk if (csw != NULL) { 636135712Sphk sprintf(p, "(%s)", csw->d_name); 637135712Sphk dev_relthread(dev); 638135712Sphk } 639135712Sphk p += strlen(p); 64051225Sbde mynor = minor(dev); 64151225Sbde if (mynor < 0 || mynor > 255) 642135712Sphk sprintf(p, "/%#x", (u_int)mynor); 64351225Sbde else 644135712Sphk sprintf(p, "/%d", mynor); 64550549Sphk } 64649982Sbillf return (dev->si_name); 64749982Sbillf} 64865374Sphk 64965374Sphkint 65091998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit) 65165374Sphk{ 65265374Sphk int u, i; 65365374Sphk 65475519Sbrian i = strlen(stem); 65575519Sbrian if (bcmp(stem, name, i) != 0) 65665374Sphk return (0); 65765374Sphk if (!isdigit(name[i])) 65865374Sphk return (0); 65965374Sphk u = 0; 66086461Sphk if (name[i] == '0' && isdigit(name[i+1])) 66186461Sphk return (0); 66265374Sphk while (isdigit(name[i])) { 66365374Sphk u *= 10; 66465374Sphk u += name[i++] - '0'; 66565374Sphk } 666104523Sgreen if (u > 0xffffff) 667104523Sgreen return (0); 66865374Sphk *unit = u; 66965374Sphk if (namep) 67065374Sphk *namep = &name[i]; 67165374Sphk if (name[i]) 67265374Sphk return (2); 67365374Sphk return (1); 67465374Sphk} 67565632Sphk 67665632Sphk/* 677126077Sphk * Helper functions for cloning device drivers. 678126077Sphk * 679126077Sphk * The objective here is to make it unnecessary for the device drivers to 680126077Sphk * use rman or similar to manage their unit number space. Due to the way 681126077Sphk * we do "on-demand" devices, using rman or other "private" methods 682126077Sphk * will be very tricky to lock down properly once we lock down this file. 683126077Sphk * 684130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s 685130936Sle * that are to be managed on their own list, and gives the driver the ability 686126077Sphk * to ask for the first free unit number or a given specified unit number. 687126077Sphk * 688126077Sphk * In addition these routines support paired devices (pty, nmdm and similar) 689126077Sphk * by respecting a number of "flag" bits in the minor number. 690126077Sphk * 691126077Sphk */ 692126077Sphk 693126077Sphkstruct clonedevs { 694126077Sphk LIST_HEAD(,cdev) head; 695126077Sphk}; 696126077Sphk 697126845Sphkvoid 698126845Sphkclone_setup(struct clonedevs **cdp) 699126845Sphk{ 700126845Sphk 701126845Sphk *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 702126845Sphk LIST_INIT(&(*cdp)->head); 703126845Sphk} 704126845Sphk 705126077Sphkint 706130585Sphkclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra) 707126077Sphk{ 708126077Sphk struct clonedevs *cd; 709140733Sphk struct cdev *dev, *ndev, *dl, *de; 710126077Sphk int unit, low, u; 711126077Sphk 712126845Sphk KASSERT(*cdp != NULL, 713126845Sphk ("clone_setup() not called in driver \"%s\"", csw->d_name)); 714126077Sphk KASSERT(!(extra & CLONE_UNITMASK), 715126845Sphk ("Illegal extra bits (0x%x) in clone_create", extra)); 716126077Sphk KASSERT(*up <= CLONE_UNITMASK, 717126845Sphk ("Too high unit (0x%x) in clone_create", *up)); 718126077Sphk 719126077Sphk if (csw->d_maj == MAJOR_AUTO) 720126077Sphk find_major(csw); 721126077Sphk 722126077Sphk /* 723126077Sphk * Search the list for a lot of things in one go: 724126077Sphk * A preexisting match is returned immediately. 725126077Sphk * The lowest free unit number if we are passed -1, and the place 726126077Sphk * in the list where we should insert that new element. 727126077Sphk * The place to insert a specified unit number, if applicable 728126077Sphk * the end of the list. 729126077Sphk */ 730126077Sphk unit = *up; 731140733Sphk ndev = allocdev(); 732140733Sphk dev_lock(); 733126849Sphk low = extra; 734126077Sphk de = dl = NULL; 735126845Sphk cd = *cdp; 736126077Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 737140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 738140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 739126077Sphk u = dev2unit(dev); 740126077Sphk if (u == (unit | extra)) { 741126077Sphk *dp = dev; 742140733Sphk freedev(ndev); 743140733Sphk dev_unlock(); 744126077Sphk return (0); 745126077Sphk } 746126077Sphk if (unit == -1 && u == low) { 747126077Sphk low++; 748126077Sphk de = dev; 749126077Sphk continue; 750126077Sphk } 751126849Sphk if (u > (unit | extra)) { 752126077Sphk dl = dev; 753126077Sphk break; 754126077Sphk } 755126077Sphk } 756126077Sphk if (unit == -1) 757126849Sphk unit = low & CLONE_UNITMASK; 758140733Sphk dev = newdev(csw->d_maj, unit2minor(unit | extra), ndev); 759140733Sphk if (dev->si_flags & SI_CLONELIST) { 760140733Sphk printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 761140733Sphk printf("unit=%d\n", unit); 762140733Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 763140733Sphk printf("\t%p %s\n", dev, dev->si_name); 764140733Sphk } 765140733Sphk panic("foo"); 766140733Sphk } 767126077Sphk KASSERT(!(dev->si_flags & SI_CLONELIST), 768140733Sphk ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 769126077Sphk if (dl != NULL) 770126077Sphk LIST_INSERT_BEFORE(dl, dev, si_clone); 771126077Sphk else if (de != NULL) 772126077Sphk LIST_INSERT_AFTER(de, dev, si_clone); 773126077Sphk else 774126077Sphk LIST_INSERT_HEAD(&cd->head, dev, si_clone); 775126077Sphk dev->si_flags |= SI_CLONELIST; 776126077Sphk *up = unit; 777140733Sphk dev_unlock(); 778126077Sphk return (1); 779126077Sphk} 780126077Sphk 781126077Sphk/* 782126077Sphk * Kill everything still on the list. The driver should already have 783130585Sphk * disposed of any softc hung of the struct cdev *'s at this time. 784126077Sphk */ 785126077Sphkvoid 786126077Sphkclone_cleanup(struct clonedevs **cdp) 787126077Sphk{ 788130585Sphk struct cdev *dev, *tdev; 789126077Sphk struct clonedevs *cd; 790126077Sphk 791126077Sphk cd = *cdp; 792126077Sphk if (cd == NULL) 793126077Sphk return; 794140733Sphk dev_lock(); 795126077Sphk LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { 796140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 797140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 798126077Sphk KASSERT(dev->si_flags & SI_NAMED, 799126077Sphk ("Driver has goofed in cloning underways udev %x", dev->si_udev)); 800140733Sphk idestroy_dev(dev); 801126077Sphk } 802140733Sphk dev_unlock(); 803126077Sphk free(cd, M_DEVBUF); 804126077Sphk *cdp = NULL; 805126077Sphk} 806126077Sphk 807126077Sphk/* 808130936Sle * Helper sysctl for devname(3). We're given a struct cdev * and return 80965632Sphk * the name, if any, registered by the device driver. 81065632Sphk */ 81165632Sphkstatic int 81265632Sphksysctl_devname(SYSCTL_HANDLER_ARGS) 81365632Sphk{ 81465632Sphk int error; 815130640Sphk dev_t ud; 816130585Sphk struct cdev *dev; 81765632Sphk 81865632Sphk error = SYSCTL_IN(req, &ud, sizeof (ud)); 81965632Sphk if (error) 82065632Sphk return (error); 821130640Sphk if (ud == NODEV) 82271342Sphk return(EINVAL); 823130640Sphk dev = findcdev(ud); 824130640Sphk if (dev == NULL) 82565632Sphk error = ENOENT; 82665632Sphk else 82765632Sphk error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1); 82865632Sphk return (error); 82965632Sphk} 83065632Sphk 83167905SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY, 83265632Sphk NULL, 0, sysctl_devname, "", "devname(3) handler"); 833