1 /*- 2 * Parts Copyright (c) 1995 Terrence R. Lambert 3 * Copyright (c) 1995 Julian R. Elischer 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Terrence R. Lambert. 17 * 4. The name Terrence R. Lambert may not be used to endorse or promote 18 * products derived from this software without specific prior written 19 * permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD: src/sys/kern/kern_conf.c,v 1.73.2.3 2003/03/10 02:18:25 imp Exp $ 34 * $DragonFly: src/sys/kern/kern_conf.c,v 1.23 2007/05/09 00:53:34 dillon Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/kernel.h> 39 #include <sys/sysctl.h> 40 #include <sys/systm.h> 41 #include <sys/module.h> 42 #include <sys/malloc.h> 43 #include <sys/conf.h> 44 #include <sys/vnode.h> 45 #include <sys/queue.h> 46 #include <sys/device.h> 47 #include <sys/disk.h> 48 #include <machine/stdarg.h> 49 50 #include <sys/sysref2.h> 51 52 #include <sys/devfs.h> 53 54 MALLOC_DEFINE(M_DEVT, "cdev_t", "dev_t storage"); 55 56 static int free_devt; 57 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, ""); 58 int dev_ref_debug = 0; 59 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, ""); 60 61 /* 62 * cdev_t and u_dev_t primitives. Note that the major number is always 63 * extracted from si_umajor, not from si_devsw, because si_devsw is replaced 64 * when a device is destroyed. 65 */ 66 int 67 major(cdev_t dev) 68 { 69 if (dev == NULL) 70 return NOUDEV; 71 return(dev->si_umajor); 72 } 73 74 int 75 minor(cdev_t dev) 76 { 77 if (dev == NULL) 78 return NOUDEV; 79 return(dev->si_uminor); 80 } 81 82 /* 83 * Compatibility function with old udev_t format to convert the 84 * non-consecutive minor space into a consecutive minor space. 85 */ 86 int 87 lminor(cdev_t dev) 88 { 89 int y; 90 91 if (dev == NULL) 92 return NOUDEV; 93 y = dev->si_uminor; 94 if (y & 0x0000ff00) 95 return NOUDEV; 96 return ((y & 0xff) | (y >> 8)); 97 } 98 99 /* 100 * Convert a device pointer to an old style device number. Return NOUDEV 101 * if the device is invalid or if the device (maj,min) cannot be converted 102 * to an old style udev_t. 103 */ 104 udev_t 105 dev2udev(cdev_t dev) 106 { 107 if (dev == NULL) 108 return NOUDEV; 109 110 return (udev_t)dev->si_inode; 111 } 112 113 /* 114 * Convert a device number to a device pointer. The device is referenced 115 * ad-hoc, meaning that the caller should call reference_dev() if it wishes 116 * to keep ahold of the returned structure long term. 117 * 118 * The returned device is associated with the currently installed cdevsw 119 * for the requested major number. NULL is returned if the major number 120 * has not been registered. 121 */ 122 cdev_t 123 udev2dev(udev_t x, int b) 124 { 125 if (x == NOUDEV || b != 0) 126 return(NULL); 127 128 return devfs_find_device_by_udev(x); 129 } 130 131 int 132 dev_is_good(cdev_t dev) 133 { 134 if (dev != NULL && dev->si_ops != &dead_dev_ops) 135 return(1); 136 return(0); 137 } 138 139 /* 140 * Various user device number extraction and conversion routines 141 */ 142 int 143 uminor(udev_t dev) 144 { 145 if (dev == NOUDEV) 146 return(-1); 147 return(dev & 0xffff00ff); 148 } 149 150 int 151 umajor(udev_t dev) 152 { 153 if (dev == NOUDEV) 154 return(-1); 155 return((dev & 0xff00) >> 8); 156 } 157 158 udev_t 159 makeudev(int x, int y) 160 { 161 if ((x & 0xffffff00) || (y & 0x0000ff00)) 162 return NOUDEV; 163 return ((x << 8) | y); 164 } 165 166 /* 167 * Create an internal or external device. 168 * 169 * This routine creates and returns an unreferenced ad-hoc entry for the 170 * device which will remain intact until the device is destroyed. If the 171 * caller intends to store the device pointer it must call reference_dev() 172 * to retain a real reference to the device. 173 * 174 * If an entry already exists, this function will set (or override) 175 * its cred requirements and name (XXX DEVFS interface). 176 */ 177 cdev_t 178 make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 179 int perms, const char *fmt, ...) 180 { 181 cdev_t devfs_dev; 182 __va_list ap; 183 184 /* 185 * compile the cdevsw and install the device 186 */ 187 compile_dev_ops(ops); 188 189 devfs_dev = devfs_new_cdev(ops, minor, NULL); 190 __va_start(ap, fmt); 191 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 192 32, fmt, ap); 193 __va_end(ap); 194 195 devfs_debug(DEVFS_DEBUG_INFO, 196 "make_dev called for %s\n", 197 devfs_dev->si_name); 198 devfs_create_dev(devfs_dev, uid, gid, perms); 199 200 return (devfs_dev); 201 } 202 203 /* 204 * make_dev_covering has equivalent functionality to make_dev, except that it 205 * also takes the cdev of the underlying device. Hence this function should 206 * only be used by systems and drivers which create devices covering others 207 */ 208 cdev_t 209 make_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor, 210 uid_t uid, gid_t gid, int perms, const char *fmt, ...) 211 { 212 cdev_t devfs_dev; 213 __va_list ap; 214 215 /* 216 * compile the cdevsw and install the device 217 */ 218 compile_dev_ops(ops); 219 220 devfs_dev = devfs_new_cdev(ops, minor, bops); 221 __va_start(ap, fmt); 222 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 223 32, fmt, ap); 224 __va_end(ap); 225 226 devfs_debug(DEVFS_DEBUG_INFO, 227 "make_dev called for %s\n", 228 devfs_dev->si_name); 229 devfs_create_dev(devfs_dev, uid, gid, perms); 230 231 return (devfs_dev); 232 } 233 234 235 236 cdev_t 237 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 238 int perms, const char *fmt, ...) 239 { 240 cdev_t devfs_dev; 241 __va_list ap; 242 243 /* 244 * compile the cdevsw and install the device 245 */ 246 compile_dev_ops(ops); 247 devfs_dev = devfs_new_cdev(ops, minor, NULL); 248 249 /* 250 * Set additional fields (XXX DEVFS interface goes here) 251 */ 252 __va_start(ap, fmt); 253 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 254 32, fmt, ap); 255 __va_end(ap); 256 257 devfs_create_dev(devfs_dev, uid, gid, perms); 258 259 return (devfs_dev); 260 } 261 262 263 cdev_t 264 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 265 int perms, const char *fmt, ...) 266 { 267 cdev_t devfs_dev; 268 __va_list ap; 269 270 /* 271 * compile the cdevsw and install the device 272 */ 273 compile_dev_ops(ops); 274 devfs_dev = devfs_new_cdev(ops, minor, NULL); 275 devfs_dev->si_perms = perms; 276 devfs_dev->si_uid = uid; 277 devfs_dev->si_gid = gid; 278 279 /* 280 * Set additional fields (XXX DEVFS interface goes here) 281 */ 282 __va_start(ap, fmt); 283 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 284 32, fmt, ap); 285 __va_end(ap); 286 287 reference_dev(devfs_dev); 288 289 return (devfs_dev); 290 } 291 292 void 293 destroy_only_dev(cdev_t dev) 294 { 295 release_dev(dev); 296 release_dev(dev); 297 release_dev(dev); 298 } 299 300 /* 301 * destroy_dev() removes the adhoc association for a device and revectors 302 * its ops to &dead_dev_ops. 303 * 304 * This routine releases the reference count associated with the ADHOC 305 * entry, plus releases the reference count held by the caller. What this 306 * means is that you should not call destroy_dev(make_dev(...)), because 307 * make_dev() does not bump the reference count (beyond what it needs to 308 * create the ad-hoc association). Any procedure that intends to destroy 309 * a device must have its own reference to it first. 310 */ 311 void 312 destroy_dev(cdev_t dev) 313 { 314 if (dev) { 315 devfs_debug(DEVFS_DEBUG_DEBUG, 316 "destroy_dev called for %s\n", 317 dev->si_name); 318 devfs_destroy_dev(dev); 319 } 320 } 321 322 /* 323 * Make sure all asynchronous disk and devfs related operations have 324 * completed. 325 * 326 * Typically called prior to mountroot to ensure that all disks have 327 * been completely probed and on module unload to ensure that ops 328 * structures have been dereferenced. 329 */ 330 void 331 sync_devs(void) 332 { 333 disk_config(NULL); 334 devfs_config(); 335 disk_config(NULL); 336 devfs_config(); 337 } 338 339 int 340 make_dev_alias(cdev_t target, const char *fmt, ...) 341 { 342 __va_list ap; 343 char *name; 344 345 __va_start(ap, fmt); 346 kvasnrprintf(&name, PATH_MAX, 32, fmt, ap); 347 __va_end(ap); 348 349 devfs_make_alias(name, target); 350 kvasfree(&name); 351 352 return 0; 353 } 354 355 extern struct dev_ops default_dev_ops; 356 357 cdev_t 358 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap, 359 d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...) 360 { 361 __va_list ap; 362 cdev_t dev; 363 char *name; 364 365 __va_start(ap, fmt); 366 kvasnrprintf(&name, PATH_MAX, 32, fmt, ap); 367 __va_end(ap); 368 369 if (bitmap != NULL) 370 devfs_clone_bitmap_init(bitmap); 371 372 devfs_clone_handler_add(name, nhandler); 373 dev = make_dev_covering(&default_dev_ops, ops, 0xffff00ff, 374 uid, gid, perms, "%s", name); 375 kvasfree(&name); 376 return dev; 377 } 378 379 void 380 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap) 381 { 382 if (dev == NULL) 383 return; 384 385 devfs_clone_handler_del(dev->si_name); 386 387 if (bitmap != NULL) 388 devfs_clone_bitmap_uninit(bitmap); 389 390 destroy_dev(dev); 391 } 392 393 394 /* 395 * Add a reference to a device. Callers generally add their own references 396 * when they are going to store a device node in a variable for long periods 397 * of time, to prevent a disassociation from free()ing the node. 398 * 399 * Also note that a caller that intends to call destroy_dev() must first 400 * obtain a reference on the device. The ad-hoc reference you get with 401 * make_dev() and friends is NOT sufficient to be able to call destroy_dev(). 402 */ 403 cdev_t 404 reference_dev(cdev_t dev) 405 { 406 //kprintf("reference_dev\n"); 407 408 if (dev != NULL) { 409 sysref_get(&dev->si_sysref); 410 if (dev_ref_debug & 2) { 411 kprintf("reference dev %p %s(minor=%08x) refs=%d\n", 412 dev, devtoname(dev), dev->si_uminor, 413 dev->si_sysref.refcnt); 414 } 415 } 416 return(dev); 417 } 418 419 /* 420 * release a reference on a device. The device will be terminated when the 421 * last reference has been released. 422 * 423 * NOTE: we must use si_umajor to figure out the original major number, 424 * because si_ops could already be pointing at dead_dev_ops. 425 */ 426 void 427 release_dev(cdev_t dev) 428 { 429 //kprintf("release_dev\n"); 430 431 if (dev == NULL) 432 return; 433 sysref_put(&dev->si_sysref); 434 } 435 436 const char * 437 devtoname(cdev_t dev) 438 { 439 int mynor; 440 int len; 441 char *p; 442 const char *dname; 443 444 if (dev == NULL) 445 return("#nodev"); 446 if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 447 p = dev->si_name; 448 len = sizeof(dev->si_name); 449 if ((dname = dev_dname(dev)) != NULL) 450 ksnprintf(p, len, "#%s/", dname); 451 else 452 ksnprintf(p, len, "#%d/", major(dev)); 453 len -= strlen(p); 454 p += strlen(p); 455 mynor = minor(dev); 456 if (mynor < 0 || mynor > 255) 457 ksnprintf(p, len, "%#x", (u_int)mynor); 458 else 459 ksnprintf(p, len, "%d", mynor); 460 } 461 return (dev->si_name); 462 } 463 464