kern_conf.c revision 170950
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 170950 2007-06-19 13:19:23Z kib $"); 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> 3636735Sdfr#include <sys/module.h> 3748936Sphk#include <sys/malloc.h> 3811126Sjulian#include <sys/conf.h> 3912954Sjulian#include <sys/vnode.h> 4048936Sphk#include <sys/queue.h> 41120514Sphk#include <sys/poll.h> 4265374Sphk#include <sys/ctype.h> 43126078Sphk#include <sys/tty.h> 44147982Srwatson#include <sys/ucred.h> 4549535Sphk#include <machine/stdarg.h> 4611126Sjulian 47149144Sphk#include <fs/devfs/devfs_int.h> 48149144Sphk 49131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); 5048936Sphk 51150342Sphkstruct mtx devmtx; 52142242Sphkstatic void destroy_devl(struct cdev *dev); 53147982Srwatsonstatic struct cdev *make_dev_credv(struct cdevsw *devsw, int minornr, 54147982Srwatson struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, 55147982Srwatson va_list ap); 56126082Sphk 57170950Skibstatic struct cdev_priv_list cdevp_free_list = 58170950Skib TAILQ_HEAD_INITIALIZER(cdevp_free_list); 59170950Skib 60135600Sphkvoid 61135600Sphkdev_lock(void) 62126082Sphk{ 63151450Sjhb 64126082Sphk mtx_lock(&devmtx); 65126082Sphk} 66126082Sphk 67170950Skibstatic void 68170950Skibdev_unlock_and_free(void) 69170950Skib{ 70170950Skib struct cdev_priv *cdp; 71170950Skib 72170950Skib mtx_assert(&devmtx, MA_OWNED); 73170950Skib while ((cdp = TAILQ_FIRST(&cdevp_free_list)) != NULL) { 74170950Skib TAILQ_REMOVE(&cdevp_free_list, cdp, cdp_list); 75170950Skib mtx_unlock(&devmtx); 76170950Skib devfs_free(&cdp->cdp_c); 77170950Skib mtx_lock(&devmtx); 78170950Skib } 79170950Skib mtx_unlock(&devmtx); 80170950Skib} 81170950Skib 82170950Skibstatic void 83170950Skibdev_free_devlocked(struct cdev *cdev) 84170950Skib{ 85170950Skib struct cdev_priv *cdp; 86170950Skib 87170950Skib mtx_assert(&devmtx, MA_OWNED); 88170950Skib cdp = cdev->si_priv; 89170950Skib TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list); 90170950Skib} 91170950Skib 92135600Sphkvoid 93135600Sphkdev_unlock(void) 94126082Sphk{ 95135704Sphk 96126082Sphk mtx_unlock(&devmtx); 97126082Sphk} 98126082Sphk 99126082Sphkvoid 100144385Sphkdev_ref(struct cdev *dev) 101144385Sphk{ 102144385Sphk 103144385Sphk mtx_assert(&devmtx, MA_NOTOWNED); 104144385Sphk mtx_lock(&devmtx); 105144385Sphk dev->si_refcount++; 106144385Sphk mtx_unlock(&devmtx); 107144385Sphk} 108144385Sphk 109144385Sphkvoid 110144384Sphkdev_refl(struct cdev *dev) 111126082Sphk{ 112135704Sphk 113142232Sphk mtx_assert(&devmtx, MA_OWNED); 114126082Sphk dev->si_refcount++; 115126082Sphk} 116126082Sphk 117126082Sphkvoid 118142242Sphkdev_rel(struct cdev *dev) 119126082Sphk{ 120142242Sphk int flag = 0; 121135600Sphk 122136014Sphk mtx_assert(&devmtx, MA_NOTOWNED); 123136014Sphk dev_lock(); 124126082Sphk dev->si_refcount--; 125126082Sphk KASSERT(dev->si_refcount >= 0, 126126082Sphk ("dev_rel(%s) gave negative count", devtoname(dev))); 127150342Sphk#if 0 128142242Sphk if (dev->si_usecount == 0 && 129142242Sphk (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED)) 130150342Sphk ; 131150342Sphk else 132150342Sphk#endif 133154029Sbz if (dev->si_devsw == NULL && dev->si_refcount == 0) { 134126082Sphk LIST_REMOVE(dev, si_list); 135136014Sphk flag = 1; 136136014Sphk } 137136014Sphk dev_unlock(); 138136014Sphk if (flag) 139150342Sphk devfs_free(dev); 140126082Sphk} 141136014Sphk 142135704Sphkstruct cdevsw * 143135704Sphkdev_refthread(struct cdev *dev) 144135704Sphk{ 145135704Sphk struct cdevsw *csw; 146126082Sphk 147135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 148135704Sphk dev_lock(); 149135704Sphk csw = dev->si_devsw; 150135704Sphk if (csw != NULL) 151135704Sphk dev->si_threadcount++; 152135704Sphk dev_unlock(); 153135704Sphk return (csw); 154135704Sphk} 155135704Sphk 156163529Skibstruct cdevsw * 157163529Skibdevvn_refthread(struct vnode *vp, struct cdev **devp) 158163529Skib{ 159163529Skib struct cdevsw *csw; 160163529Skib 161163529Skib mtx_assert(&devmtx, MA_NOTOWNED); 162163529Skib csw = NULL; 163163529Skib dev_lock(); 164163529Skib *devp = vp->v_rdev; 165163529Skib if (*devp != NULL) { 166163529Skib csw = (*devp)->si_devsw; 167163529Skib if (csw != NULL) 168163529Skib (*devp)->si_threadcount++; 169163529Skib } 170163529Skib dev_unlock(); 171163529Skib return (csw); 172163529Skib} 173163529Skib 174135704Sphkvoid 175135704Sphkdev_relthread(struct cdev *dev) 176135704Sphk{ 177135704Sphk 178135704Sphk mtx_assert(&devmtx, MA_NOTOWNED); 179135704Sphk dev_lock(); 180135704Sphk dev->si_threadcount--; 181135704Sphk dev_unlock(); 182135704Sphk} 183135704Sphk 184120514Sphkint 185120514Sphknullop(void) 186120514Sphk{ 18785603Sphk 188120514Sphk return (0); 189120514Sphk} 190120514Sphk 191120514Sphkint 192120514Sphkeopnotsupp(void) 193120514Sphk{ 194120514Sphk 195120514Sphk return (EOPNOTSUPP); 196120514Sphk} 197120514Sphk 198111179Sphkstatic int 199111179Sphkenxio(void) 200111179Sphk{ 201111179Sphk return (ENXIO); 202111179Sphk} 203111179Sphk 204120514Sphkstatic int 205120514Sphkenodev(void) 206120514Sphk{ 207120514Sphk return (ENODEV); 208120514Sphk} 209120514Sphk 210120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */ 211120514Sphk 212111179Sphk#define dead_open (d_open_t *)enxio 213111179Sphk#define dead_close (d_close_t *)enxio 214111179Sphk#define dead_read (d_read_t *)enxio 215111179Sphk#define dead_write (d_write_t *)enxio 216111179Sphk#define dead_ioctl (d_ioctl_t *)enxio 217120514Sphk#define dead_poll (d_poll_t *)enodev 218120514Sphk#define dead_mmap (d_mmap_t *)enodev 219111179Sphk 220111179Sphkstatic void 221111179Sphkdead_strategy(struct bio *bp) 222111179Sphk{ 223111179Sphk 224111179Sphk biofinish(bp, NULL, ENXIO); 225111179Sphk} 226111179Sphk 227111220Sphk#define dead_dump (dumper_t *)enxio 228111179Sphk#define dead_kqfilter (d_kqfilter_t *)enxio 229111179Sphk 230111179Sphkstatic struct cdevsw dead_cdevsw = { 231126080Sphk .d_version = D_VERSION, 232126080Sphk .d_flags = D_NEEDGIANT, /* XXX: does dead_strategy need this ? */ 233111815Sphk .d_open = dead_open, 234111815Sphk .d_close = dead_close, 235111815Sphk .d_read = dead_read, 236111815Sphk .d_write = dead_write, 237111815Sphk .d_ioctl = dead_ioctl, 238111815Sphk .d_poll = dead_poll, 239111815Sphk .d_mmap = dead_mmap, 240111815Sphk .d_strategy = dead_strategy, 241111815Sphk .d_name = "dead", 242111815Sphk .d_dump = dead_dump, 243111815Sphk .d_kqfilter = dead_kqfilter 244111179Sphk}; 245111179Sphk 246120514Sphk/* Default methods if driver does not specify method */ 247111179Sphk 248120514Sphk#define null_open (d_open_t *)nullop 249120514Sphk#define null_close (d_close_t *)nullop 250120514Sphk#define no_read (d_read_t *)enodev 251120514Sphk#define no_write (d_write_t *)enodev 252120514Sphk#define no_ioctl (d_ioctl_t *)enodev 253120514Sphk#define no_mmap (d_mmap_t *)enodev 254133741Sjmg#define no_kqfilter (d_kqfilter_t *)enodev 255120514Sphk 256120514Sphkstatic void 257120514Sphkno_strategy(struct bio *bp) 258120514Sphk{ 259120514Sphk 260120514Sphk biofinish(bp, NULL, ENODEV); 261120514Sphk} 262120514Sphk 263120514Sphkstatic int 264130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused) 265120514Sphk{ 266120514Sphk /* 267120514Sphk * Return true for read/write. If the user asked for something 268120514Sphk * special, return POLLNVAL, so that clients have a way of 269120514Sphk * determining reliably whether or not the extended 270120514Sphk * functionality is present without hard-coding knowledge 271120514Sphk * of specific filesystem implementations. 272120514Sphk * Stay in sync with vop_nopoll(). 273120514Sphk */ 274120514Sphk if (events & ~POLLSTANDARD) 275120514Sphk return (POLLNVAL); 276120514Sphk 277120514Sphk return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); 278120514Sphk} 279120514Sphk 280120514Sphk#define no_dump (dumper_t *)enodev 281120514Sphk 282149177Sphkstatic int 283149177Sphkgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 284149177Sphk{ 285149177Sphk int retval; 286149177Sphk 287149177Sphk mtx_lock(&Giant); 288149177Sphk retval = dev->si_devsw->d_gianttrick-> 289149177Sphk d_open(dev, oflags, devtype, td); 290149177Sphk mtx_unlock(&Giant); 291149177Sphk return (retval); 292149177Sphk} 293149177Sphk 294149177Sphkstatic int 295170152Skibgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) 296149177Sphk{ 297149177Sphk int retval; 298149177Sphk 299149177Sphk mtx_lock(&Giant); 300149177Sphk retval = dev->si_devsw->d_gianttrick-> 301170152Skib d_fdopen(dev, oflags, td, fp); 302149177Sphk mtx_unlock(&Giant); 303149177Sphk return (retval); 304149177Sphk} 305149177Sphk 306149177Sphkstatic int 307149177Sphkgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 308149177Sphk{ 309149177Sphk int retval; 310149177Sphk 311149177Sphk mtx_lock(&Giant); 312149177Sphk retval = dev->si_devsw->d_gianttrick-> 313149177Sphk d_close(dev, fflag, devtype, td); 314149177Sphk mtx_unlock(&Giant); 315149177Sphk return (retval); 316149177Sphk} 317149177Sphk 318149177Sphkstatic void 319149177Sphkgiant_strategy(struct bio *bp) 320149177Sphk{ 321149177Sphk 322149177Sphk mtx_lock(&Giant); 323149177Sphk bp->bio_dev->si_devsw->d_gianttrick-> 324149177Sphk d_strategy(bp); 325149177Sphk mtx_unlock(&Giant); 326149177Sphk} 327149177Sphk 328149177Sphkstatic int 329149177Sphkgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 330149177Sphk{ 331149177Sphk int retval; 332149177Sphk 333149177Sphk mtx_lock(&Giant); 334149177Sphk retval = dev->si_devsw->d_gianttrick-> 335149177Sphk d_ioctl(dev, cmd, data, fflag, td); 336149177Sphk mtx_unlock(&Giant); 337149177Sphk return (retval); 338149177Sphk} 339149177Sphk 340149177Sphkstatic int 341149177Sphkgiant_read(struct cdev *dev, struct uio *uio, int ioflag) 342149177Sphk{ 343149177Sphk int retval; 344149177Sphk 345149177Sphk mtx_lock(&Giant); 346149177Sphk retval = dev->si_devsw->d_gianttrick-> 347149177Sphk d_read(dev, uio, ioflag); 348149177Sphk mtx_unlock(&Giant); 349149177Sphk return (retval); 350149177Sphk} 351149177Sphk 352149177Sphkstatic int 353149177Sphkgiant_write(struct cdev *dev, struct uio *uio, int ioflag) 354149177Sphk{ 355149177Sphk int retval; 356149177Sphk 357149177Sphk mtx_lock(&Giant); 358149177Sphk retval = dev->si_devsw->d_gianttrick-> 359149177Sphk d_write(dev, uio, ioflag); 360149177Sphk mtx_unlock(&Giant); 361149177Sphk return (retval); 362149177Sphk} 363149177Sphk 364149177Sphkstatic int 365149177Sphkgiant_poll(struct cdev *dev, int events, struct thread *td) 366149177Sphk{ 367149177Sphk int retval; 368149177Sphk 369149177Sphk mtx_lock(&Giant); 370149177Sphk retval = dev->si_devsw->d_gianttrick-> 371149177Sphk d_poll(dev, events, td); 372149177Sphk mtx_unlock(&Giant); 373149177Sphk return (retval); 374149177Sphk} 375149177Sphk 376149177Sphkstatic int 377149177Sphkgiant_kqfilter(struct cdev *dev, struct knote *kn) 378149177Sphk{ 379149177Sphk int retval; 380149177Sphk 381149177Sphk mtx_lock(&Giant); 382149177Sphk retval = dev->si_devsw->d_gianttrick-> 383149177Sphk d_kqfilter(dev, kn); 384149177Sphk mtx_unlock(&Giant); 385149177Sphk return (retval); 386149177Sphk} 387149177Sphk 388149177Sphkstatic int 389149177Sphkgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 390149177Sphk{ 391149177Sphk int retval; 392149177Sphk 393149177Sphk mtx_lock(&Giant); 394149177Sphk retval = dev->si_devsw->d_gianttrick-> 395149177Sphk d_mmap(dev, offset, paddr, nprot); 396149177Sphk mtx_unlock(&Giant); 397149177Sphk return (retval); 398149177Sphk} 399149177Sphk 400149177Sphk 40147640Sphk/* 402130936Sle * struct cdev * and u_dev_t primitives 40347028Sphk */ 40447028Sphk 40547028Sphkint 406130585Sphkminor(struct cdev *x) 40747028Sphk{ 408130640Sphk if (x == NULL) 409130640Sphk return NODEV; 410143631Sphk return(x->si_drv0 & MAXMINOR); 41147028Sphk} 41247028Sphk 41349826Sphkint 414130585Sphkdev2unit(struct cdev *x) 41549826Sphk{ 41649826Sphk 417130640Sphk if (x == NULL) 418130640Sphk return NODEV; 419140964Sphk return (minor2unit(minor(x))); 42049826Sphk} 42149826Sphk 422143282Sphku_int 423143282Sphkminor2unit(u_int _minor) 424140963Sphk{ 425140963Sphk 426140969Sphk KASSERT((_minor & ~MAXMINOR) == 0, ("Illegal minor %x", _minor)); 427143282Sphk return ((_minor & 0xff) | ((_minor >> 8) & 0xffff00)); 428140963Sphk} 429140963Sphk 430140963Sphkint 43166067Sphkunit2minor(int unit) 43266067Sphk{ 43366067Sphk 43474522Sphk KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit)); 43566067Sphk return ((unit & 0xff) | ((unit << 8) & ~0xffff)); 43666067Sphk} 43766067Sphk 438130585Sphkstatic struct cdev * 439144281Sphknewdev(struct cdevsw *csw, int y, struct cdev *si) 44047028Sphk{ 441140733Sphk struct cdev *si2; 442130640Sphk dev_t udev; 44348936Sphk 444140733Sphk mtx_assert(&devmtx, MA_OWNED); 445144292Sphk udev = y; 446144281Sphk LIST_FOREACH(si2, &csw->d_devs, si_list) { 447143631Sphk if (si2->si_drv0 == udev) { 448170950Skib dev_free_devlocked(si); 449140733Sphk return (si2); 450140733Sphk } 45148936Sphk } 452143631Sphk si->si_drv0 = udev; 453150342Sphk si->si_devsw = csw; 454144281Sphk LIST_INSERT_HEAD(&csw->d_devs, si, si_list); 455125850Sbde return (si); 45647028Sphk} 45747028Sphk 45847028Sphkint 459130640Sphkuminor(dev_t dev) 46047028Sphk{ 461140969Sphk return (dev & MAXMINOR); 46247028Sphk} 46347028Sphk 46447028Sphkint 465130640Sphkumajor(dev_t dev) 46647028Sphk{ 467140969Sphk return ((dev & ~MAXMINOR) >> 8); 46847028Sphk} 46947028Sphk 470125846Sphkstatic void 471144292Sphkfini_cdevsw(struct cdevsw *devsw) 47249535Sphk{ 473149324Sphk struct cdevsw *gt; 47449535Sphk 475149324Sphk if (devsw->d_gianttrick != NULL) { 476149324Sphk gt = devsw->d_gianttrick; 477149324Sphk memcpy(devsw, gt, sizeof *devsw); 478149324Sphk free(gt, M_DEVT); 479149324Sphk devsw->d_gianttrick = NULL; 480149324Sphk } 481126156Sphk devsw->d_flags &= ~D_INIT; 482126082Sphk} 483126082Sphk 484126082Sphkstatic void 485126077Sphkprep_cdevsw(struct cdevsw *devsw) 486126077Sphk{ 487149177Sphk struct cdevsw *dsw2; 488126077Sphk 489149177Sphk if (devsw->d_flags & D_NEEDGIANT) 490149177Sphk dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK); 491149177Sphk else 492149177Sphk dsw2 = NULL; 493135600Sphk dev_lock(); 494126082Sphk 495143746Sphk if (devsw->d_version != D_VERSION_01) { 496126082Sphk printf( 497126082Sphk "WARNING: Device driver \"%s\" has wrong version %s\n", 498154266Salfred devsw->d_name == NULL ? "???" : devsw->d_name, 499154266Salfred "and is disabled. Recompile KLD module."); 500126082Sphk devsw->d_open = dead_open; 501126082Sphk devsw->d_close = dead_close; 502126082Sphk devsw->d_read = dead_read; 503126082Sphk devsw->d_write = dead_write; 504126082Sphk devsw->d_ioctl = dead_ioctl; 505126082Sphk devsw->d_poll = dead_poll; 506126082Sphk devsw->d_mmap = dead_mmap; 507126082Sphk devsw->d_strategy = dead_strategy; 508126082Sphk devsw->d_dump = dead_dump; 509126082Sphk devsw->d_kqfilter = dead_kqfilter; 510126082Sphk } 511126082Sphk 512126078Sphk if (devsw->d_flags & D_TTY) { 513129943Sphk if (devsw->d_ioctl == NULL) devsw->d_ioctl = ttyioctl; 514126078Sphk if (devsw->d_read == NULL) devsw->d_read = ttyread; 515126078Sphk if (devsw->d_write == NULL) devsw->d_write = ttywrite; 516126078Sphk if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = ttykqfilter; 517126078Sphk if (devsw->d_poll == NULL) devsw->d_poll = ttypoll; 518126078Sphk } 519126078Sphk 520149177Sphk if (devsw->d_flags & D_NEEDGIANT) { 521149177Sphk if (devsw->d_gianttrick == NULL) { 522149177Sphk memcpy(dsw2, devsw, sizeof *dsw2); 523149177Sphk devsw->d_gianttrick = dsw2; 524149177Sphk } else 525149177Sphk free(dsw2, M_DEVT); 526149177Sphk } 527149177Sphk 528149177Sphk#define FIXUP(member, noop, giant) \ 529149177Sphk do { \ 530149177Sphk if (devsw->member == NULL) { \ 531149177Sphk devsw->member = noop; \ 532149177Sphk } else if (devsw->d_flags & D_NEEDGIANT) \ 533149177Sphk devsw->member = giant; \ 534149177Sphk } \ 535149177Sphk while (0) 536149177Sphk 537149177Sphk FIXUP(d_open, null_open, giant_open); 538149177Sphk FIXUP(d_fdopen, NULL, giant_fdopen); 539149177Sphk FIXUP(d_close, null_close, giant_close); 540149177Sphk FIXUP(d_read, no_read, giant_read); 541149177Sphk FIXUP(d_write, no_write, giant_write); 542149177Sphk FIXUP(d_ioctl, no_ioctl, giant_ioctl); 543149177Sphk FIXUP(d_poll, no_poll, giant_poll); 544149177Sphk FIXUP(d_mmap, no_mmap, giant_mmap); 545149177Sphk FIXUP(d_strategy, no_strategy, giant_strategy); 546149177Sphk FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); 547149177Sphk 548120514Sphk if (devsw->d_dump == NULL) devsw->d_dump = no_dump; 549126082Sphk 550126082Sphk LIST_INIT(&devsw->d_devs); 551126082Sphk 552126082Sphk devsw->d_flags |= D_INIT; 553126082Sphk 554135600Sphk dev_unlock(); 555125846Sphk} 556111622Sphk 557147982Srwatsonstatic struct cdev * 558147982Srwatsonmake_dev_credv(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, 559147982Srwatson gid_t gid, int mode, const char *fmt, va_list ap) 560125846Sphk{ 561130585Sphk struct cdev *dev; 562125846Sphk int i; 563125846Sphk 564140969Sphk KASSERT((minornr & ~MAXMINOR) == 0, 565126082Sphk ("Invalid minor (0x%x) in make_dev", minornr)); 566126082Sphk 567144385Sphk if (!(devsw->d_flags & D_INIT)) 568126082Sphk prep_cdevsw(devsw); 569150342Sphk dev = devfs_alloc(); 570140733Sphk dev_lock(); 571144281Sphk dev = newdev(devsw, minornr, dev); 572120529Sphk if (dev->si_flags & SI_CHEAPCLONE && 573150342Sphk dev->si_flags & SI_NAMED) { 574120529Sphk /* 575120529Sphk * This is allowed as it removes races and generally 576120529Sphk * simplifies cloning devices. 577126082Sphk * XXX: still ?? 578120529Sphk */ 579170950Skib dev_unlock_and_free(); 580120529Sphk return (dev); 581120529Sphk } 582126082Sphk KASSERT(!(dev->si_flags & SI_NAMED), 583144281Sphk ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", 584144281Sphk devsw->d_name, minor(dev), devtoname(dev))); 585126082Sphk 586110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 587110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 588134501Spjd printf("WARNING: Device name truncated! (%s)\n", 589110318Sphk dev->__si_namebuf); 590110318Sphk } 591136947Sphk 59265747Sphk dev->si_flags |= SI_NAMED; 593147982Srwatson if (cr != NULL) 594147982Srwatson dev->si_cred = crhold(cr); 595147982Srwatson else 596147982Srwatson dev->si_cred = NULL; 597144385Sphk dev->si_uid = uid; 598144385Sphk dev->si_gid = gid; 599144385Sphk dev->si_mode = mode; 60050092Sjulian 601111730Sphk devfs_create(dev); 602135600Sphk dev_unlock(); 60349535Sphk return (dev); 60449535Sphk} 60549535Sphk 606147982Srwatsonstruct cdev * 607147982Srwatsonmake_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int mode, 608147982Srwatson const char *fmt, ...) 609147982Srwatson{ 610147982Srwatson struct cdev *dev; 611147982Srwatson va_list ap; 612147982Srwatson 613147982Srwatson va_start(ap, fmt); 614147982Srwatson dev = make_dev_credv(devsw, minornr, NULL, uid, gid, mode, fmt, ap); 615147982Srwatson va_end(ap); 616147982Srwatson return (dev); 617147982Srwatson} 618147982Srwatson 619147982Srwatsonstruct cdev * 620147982Srwatsonmake_dev_cred(struct cdevsw *devsw, int minornr, struct ucred *cr, uid_t uid, 621147982Srwatson gid_t gid, int mode, const char *fmt, ...) 622147982Srwatson{ 623147982Srwatson struct cdev *dev; 624147982Srwatson va_list ap; 625147982Srwatson 626147982Srwatson va_start(ap, fmt); 627147982Srwatson dev = make_dev_credv(devsw, minornr, cr, uid, gid, mode, fmt, ap); 628147982Srwatson va_end(ap); 629147982Srwatson 630147982Srwatson return (dev); 631147982Srwatson} 632147982Srwatson 633150342Sphkstatic void 634150342Sphkdev_dependsl(struct cdev *pdev, struct cdev *cdev) 635150342Sphk{ 636150342Sphk 637150342Sphk cdev->si_parent = pdev; 638150342Sphk cdev->si_flags |= SI_CHILD; 639150342Sphk LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); 640150342Sphk} 641150342Sphk 642150342Sphk 64377215Sphkvoid 644130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev) 64577215Sphk{ 64677215Sphk 647135600Sphk dev_lock(); 648150342Sphk dev_dependsl(pdev, cdev); 649135600Sphk dev_unlock(); 65077215Sphk} 65177215Sphk 652130585Sphkstruct cdev * 653130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...) 65464880Sphk{ 655130585Sphk struct cdev *dev; 65664880Sphk va_list ap; 65764880Sphk int i; 65864880Sphk 659150342Sphk dev = devfs_alloc(); 660135600Sphk dev_lock(); 66164880Sphk dev->si_flags |= SI_ALIAS; 66265747Sphk dev->si_flags |= SI_NAMED; 66364880Sphk va_start(ap, fmt); 664110318Sphk i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap); 665110318Sphk if (i > (sizeof dev->__si_namebuf - 1)) { 666134501Spjd printf("WARNING: Device name truncated! (%s)\n", 667110318Sphk dev->__si_namebuf); 668110318Sphk } 66964880Sphk va_end(ap); 67064880Sphk 671111730Sphk devfs_create(dev); 672135600Sphk dev_unlock(); 673126082Sphk dev_depends(pdev, dev); 67464880Sphk return (dev); 67564880Sphk} 67664880Sphk 677126082Sphkstatic void 678142242Sphkdestroy_devl(struct cdev *dev) 67950549Sphk{ 680135843Sphk struct cdevsw *csw; 681135843Sphk 682142242Sphk mtx_assert(&devmtx, MA_OWNED); 683135843Sphk KASSERT(dev->si_flags & SI_NAMED, 684144281Sphk ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev))); 685154029Sbz 686111730Sphk devfs_destroy(dev); 687126082Sphk 688126082Sphk /* Remove name marking */ 689126077Sphk dev->si_flags &= ~SI_NAMED; 690126077Sphk 691126082Sphk /* If we are a child, remove us from the parents list */ 69277215Sphk if (dev->si_flags & SI_CHILD) { 69377215Sphk LIST_REMOVE(dev, si_siblings); 69477215Sphk dev->si_flags &= ~SI_CHILD; 69577215Sphk } 696126082Sphk 697126082Sphk /* Kill our children */ 69877215Sphk while (!LIST_EMPTY(&dev->si_children)) 699142242Sphk destroy_devl(LIST_FIRST(&dev->si_children)); 700126082Sphk 701126082Sphk /* Remove from clone list */ 702126077Sphk if (dev->si_flags & SI_CLONELIST) { 703126077Sphk LIST_REMOVE(dev, si_clone); 704126077Sphk dev->si_flags &= ~SI_CLONELIST; 705126077Sphk } 706126082Sphk 707163328Stegge dev->si_refcount++; /* Avoid race with dev_rel() */ 708135843Sphk csw = dev->si_devsw; 709135934Sgreen dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ 710135934Sgreen while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { 711135843Sphk csw->d_purge(dev); 712135843Sphk msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); 713158684Sphk if (dev->si_threadcount) 714158684Sphk printf("Still %lu threads in %s\n", 715158684Sphk dev->si_threadcount, devtoname(dev)); 716135843Sphk } 717163328Stegge while (dev->si_threadcount != 0) { 718163328Stegge /* Use unique dummy wait ident */ 719163328Stegge msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); 720163328Stegge } 721135843Sphk 722135843Sphk dev->si_drv1 = 0; 723135843Sphk dev->si_drv2 = 0; 724135843Sphk bzero(&dev->__si_u, sizeof(dev->__si_u)); 725135843Sphk 726126082Sphk if (!(dev->si_flags & SI_ALIAS)) { 727126082Sphk /* Remove from cdevsw list */ 728126082Sphk LIST_REMOVE(dev, si_list); 729126082Sphk 730150342Sphk /* If cdevsw has no more struct cdev *'s, clean it */ 731135844Sphk if (LIST_EMPTY(&csw->d_devs)) 732135844Sphk fini_cdevsw(csw); 733126082Sphk } 73465747Sphk dev->si_flags &= ~SI_ALIAS; 735163328Stegge dev->si_refcount--; /* Avoid race with dev_rel() */ 736135843Sphk 737126082Sphk if (dev->si_refcount > 0) { 738126082Sphk LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); 739126082Sphk } else { 740170950Skib dev_free_devlocked(dev); 741126082Sphk } 74250549Sphk} 74350549Sphk 744126082Sphkvoid 745130585Sphkdestroy_dev(struct cdev *dev) 746126082Sphk{ 747126082Sphk 748135600Sphk dev_lock(); 749142242Sphk destroy_devl(dev); 750170950Skib dev_unlock_and_free(); 751126082Sphk} 752126082Sphk 75351225Sbdeconst char * 754130585Sphkdevtoname(struct cdev *dev) 75549982Sbillf{ 75650549Sphk char *p; 757135712Sphk struct cdevsw *csw; 75851225Sbde int mynor; 75949982Sbillf 76050549Sphk if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 76150549Sphk p = dev->si_name; 762135712Sphk csw = dev_refthread(dev); 763135712Sphk if (csw != NULL) { 764135712Sphk sprintf(p, "(%s)", csw->d_name); 765135712Sphk dev_relthread(dev); 766135712Sphk } 767135712Sphk p += strlen(p); 76851225Sbde mynor = minor(dev); 76951225Sbde if (mynor < 0 || mynor > 255) 770135712Sphk sprintf(p, "/%#x", (u_int)mynor); 77151225Sbde else 772135712Sphk sprintf(p, "/%d", mynor); 77350549Sphk } 77449982Sbillf return (dev->si_name); 77549982Sbillf} 77665374Sphk 77765374Sphkint 77891998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit) 77965374Sphk{ 78065374Sphk int u, i; 78165374Sphk 78275519Sbrian i = strlen(stem); 78375519Sbrian if (bcmp(stem, name, i) != 0) 78465374Sphk return (0); 78565374Sphk if (!isdigit(name[i])) 78665374Sphk return (0); 78765374Sphk u = 0; 78886461Sphk if (name[i] == '0' && isdigit(name[i+1])) 78986461Sphk return (0); 79065374Sphk while (isdigit(name[i])) { 79165374Sphk u *= 10; 79265374Sphk u += name[i++] - '0'; 79365374Sphk } 794104523Sgreen if (u > 0xffffff) 795104523Sgreen return (0); 79665374Sphk *unit = u; 79765374Sphk if (namep) 79865374Sphk *namep = &name[i]; 79965374Sphk if (name[i]) 80065374Sphk return (2); 80165374Sphk return (1); 80265374Sphk} 80365632Sphk 80465632Sphk/* 805126077Sphk * Helper functions for cloning device drivers. 806126077Sphk * 807126077Sphk * The objective here is to make it unnecessary for the device drivers to 808126077Sphk * use rman or similar to manage their unit number space. Due to the way 809126077Sphk * we do "on-demand" devices, using rman or other "private" methods 810126077Sphk * will be very tricky to lock down properly once we lock down this file. 811126077Sphk * 812130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s 813130936Sle * that are to be managed on their own list, and gives the driver the ability 814126077Sphk * to ask for the first free unit number or a given specified unit number. 815126077Sphk * 816126077Sphk * In addition these routines support paired devices (pty, nmdm and similar) 817126077Sphk * by respecting a number of "flag" bits in the minor number. 818126077Sphk * 819126077Sphk */ 820126077Sphk 821126077Sphkstruct clonedevs { 822126077Sphk LIST_HEAD(,cdev) head; 823126077Sphk}; 824126077Sphk 825126845Sphkvoid 826126845Sphkclone_setup(struct clonedevs **cdp) 827126845Sphk{ 828126845Sphk 829126845Sphk *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); 830126845Sphk LIST_INIT(&(*cdp)->head); 831126845Sphk} 832126845Sphk 833126077Sphkint 834166438Sbmsclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, int extra) 835126077Sphk{ 836126077Sphk struct clonedevs *cd; 837140733Sphk struct cdev *dev, *ndev, *dl, *de; 838126077Sphk int unit, low, u; 839126077Sphk 840126845Sphk KASSERT(*cdp != NULL, 841126845Sphk ("clone_setup() not called in driver \"%s\"", csw->d_name)); 842126077Sphk KASSERT(!(extra & CLONE_UNITMASK), 843126845Sphk ("Illegal extra bits (0x%x) in clone_create", extra)); 844126077Sphk KASSERT(*up <= CLONE_UNITMASK, 845126845Sphk ("Too high unit (0x%x) in clone_create", *up)); 846126077Sphk 847142726Sphk if (!(csw->d_flags & D_INIT)) 848142726Sphk prep_cdevsw(csw); 849126077Sphk 850126077Sphk /* 851126077Sphk * Search the list for a lot of things in one go: 852126077Sphk * A preexisting match is returned immediately. 853126077Sphk * The lowest free unit number if we are passed -1, and the place 854126077Sphk * in the list where we should insert that new element. 855126077Sphk * The place to insert a specified unit number, if applicable 856126077Sphk * the end of the list. 857126077Sphk */ 858126077Sphk unit = *up; 859150342Sphk ndev = devfs_alloc(); 860140733Sphk dev_lock(); 861126849Sphk low = extra; 862126077Sphk de = dl = NULL; 863126845Sphk cd = *cdp; 864126077Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 865140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 866140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 867126077Sphk u = dev2unit(dev); 868126077Sphk if (u == (unit | extra)) { 869126077Sphk *dp = dev; 870170950Skib dev_unlock(); 871150342Sphk devfs_free(ndev); 872126077Sphk return (0); 873126077Sphk } 874126077Sphk if (unit == -1 && u == low) { 875126077Sphk low++; 876126077Sphk de = dev; 877126077Sphk continue; 878150793Sphk } else if (u < (unit | extra)) { 879150793Sphk de = dev; 880150793Sphk continue; 881150793Sphk } else if (u > (unit | extra)) { 882126077Sphk dl = dev; 883126077Sphk break; 884126077Sphk } 885126077Sphk } 886126077Sphk if (unit == -1) 887126849Sphk unit = low & CLONE_UNITMASK; 888144281Sphk dev = newdev(csw, unit2minor(unit | extra), ndev); 889140733Sphk if (dev->si_flags & SI_CLONELIST) { 890140733Sphk printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); 891150793Sphk printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); 892140733Sphk LIST_FOREACH(dev, &cd->head, si_clone) { 893140733Sphk printf("\t%p %s\n", dev, dev->si_name); 894140733Sphk } 895140733Sphk panic("foo"); 896140733Sphk } 897126077Sphk KASSERT(!(dev->si_flags & SI_CLONELIST), 898140733Sphk ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); 899126077Sphk if (dl != NULL) 900126077Sphk LIST_INSERT_BEFORE(dl, dev, si_clone); 901126077Sphk else if (de != NULL) 902126077Sphk LIST_INSERT_AFTER(de, dev, si_clone); 903126077Sphk else 904126077Sphk LIST_INSERT_HEAD(&cd->head, dev, si_clone); 905126077Sphk dev->si_flags |= SI_CLONELIST; 906126077Sphk *up = unit; 907170950Skib dev_unlock_and_free(); 908126077Sphk return (1); 909126077Sphk} 910126077Sphk 911126077Sphk/* 912126077Sphk * Kill everything still on the list. The driver should already have 913130585Sphk * disposed of any softc hung of the struct cdev *'s at this time. 914126077Sphk */ 915126077Sphkvoid 916126077Sphkclone_cleanup(struct clonedevs **cdp) 917126077Sphk{ 918130585Sphk struct cdev *dev, *tdev; 919126077Sphk struct clonedevs *cd; 920126077Sphk 921126077Sphk cd = *cdp; 922126077Sphk if (cd == NULL) 923126077Sphk return; 924140733Sphk dev_lock(); 925126077Sphk LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { 926140733Sphk KASSERT(dev->si_flags & SI_CLONELIST, 927140733Sphk ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); 928126077Sphk KASSERT(dev->si_flags & SI_NAMED, 929143631Sphk ("Driver has goofed in cloning underways udev %x", dev->si_drv0)); 930142242Sphk destroy_devl(dev); 931126077Sphk } 932140733Sphk dev_unlock(); 933126077Sphk free(cd, M_DEVBUF); 934126077Sphk *cdp = NULL; 935126077Sphk} 936