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); 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 cdev_t 205 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 206 int perms, const char *fmt, ...) 207 { 208 cdev_t devfs_dev; 209 __va_list ap; 210 211 /* 212 * compile the cdevsw and install the device 213 */ 214 compile_dev_ops(ops); 215 devfs_dev = devfs_new_cdev(ops, minor); 216 217 /* 218 * Set additional fields (XXX DEVFS interface goes here) 219 */ 220 __va_start(ap, fmt); 221 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 222 32, fmt, ap); 223 __va_end(ap); 224 225 devfs_create_dev(devfs_dev, uid, gid, perms); 226 227 return (devfs_dev); 228 } 229 230 231 cdev_t 232 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 233 int perms, const char *fmt, ...) 234 { 235 cdev_t devfs_dev; 236 __va_list ap; 237 238 /* 239 * compile the cdevsw and install the device 240 */ 241 compile_dev_ops(ops); 242 devfs_dev = devfs_new_cdev(ops, minor); 243 devfs_dev->si_perms = perms; 244 devfs_dev->si_uid = uid; 245 devfs_dev->si_gid = gid; 246 247 /* 248 * Set additional fields (XXX DEVFS interface goes here) 249 */ 250 __va_start(ap, fmt); 251 kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), 252 32, fmt, ap); 253 __va_end(ap); 254 255 reference_dev(devfs_dev); 256 257 return (devfs_dev); 258 } 259 260 void 261 destroy_only_dev(cdev_t dev) 262 { 263 release_dev(dev); 264 release_dev(dev); 265 release_dev(dev); 266 } 267 268 /* 269 * destroy_dev() removes the adhoc association for a device and revectors 270 * its ops to &dead_dev_ops. 271 * 272 * This routine releases the reference count associated with the ADHOC 273 * entry, plus releases the reference count held by the caller. What this 274 * means is that you should not call destroy_dev(make_dev(...)), because 275 * make_dev() does not bump the reference count (beyond what it needs to 276 * create the ad-hoc association). Any procedure that intends to destroy 277 * a device must have its own reference to it first. 278 */ 279 void 280 destroy_dev(cdev_t dev) 281 { 282 if (dev) { 283 devfs_debug(DEVFS_DEBUG_DEBUG, 284 "destroy_dev called for %s\n", 285 dev->si_name); 286 devfs_destroy_dev(dev); 287 } 288 } 289 290 /* 291 * Make sure all asynchronous disk and devfs related operations have 292 * completed. 293 * 294 * Typically called prior to mountroot to ensure that all disks have 295 * been completely probed and on module unload to ensure that ops 296 * structures have been dereferenced. 297 */ 298 void 299 sync_devs(void) 300 { 301 disk_config(NULL); 302 devfs_config(); 303 disk_config(NULL); 304 devfs_config(); 305 } 306 307 int 308 make_dev_alias(cdev_t target, const char *fmt, ...) 309 { 310 __va_list ap; 311 char *name; 312 313 __va_start(ap, fmt); 314 kvasnrprintf(&name, PATH_MAX, 32, fmt, ap); 315 __va_end(ap); 316 317 devfs_make_alias(name, target); 318 kvasfree(&name); 319 320 return 0; 321 } 322 323 extern struct dev_ops default_dev_ops; 324 325 cdev_t 326 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap, 327 d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...) 328 { 329 __va_list ap; 330 cdev_t dev; 331 char *name; 332 333 __va_start(ap, fmt); 334 kvasnrprintf(&name, PATH_MAX, 32, fmt, ap); 335 __va_end(ap); 336 337 if (bitmap != NULL) 338 devfs_clone_bitmap_init(bitmap); 339 340 devfs_clone_handler_add(name, nhandler); 341 dev = make_dev(&default_dev_ops, 0xffff00ff, 342 uid, gid, perms, "%s", name); 343 kvasfree(&name); 344 return dev; 345 } 346 347 void 348 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap) 349 { 350 if (dev == NULL) 351 return; 352 353 devfs_clone_handler_del(dev->si_name); 354 355 if (bitmap != NULL) 356 devfs_clone_bitmap_uninit(bitmap); 357 358 destroy_dev(dev); 359 } 360 361 362 /* 363 * Add a reference to a device. Callers generally add their own references 364 * when they are going to store a device node in a variable for long periods 365 * of time, to prevent a disassociation from free()ing the node. 366 * 367 * Also note that a caller that intends to call destroy_dev() must first 368 * obtain a reference on the device. The ad-hoc reference you get with 369 * make_dev() and friends is NOT sufficient to be able to call destroy_dev(). 370 */ 371 cdev_t 372 reference_dev(cdev_t dev) 373 { 374 //kprintf("reference_dev\n"); 375 376 if (dev != NULL) { 377 sysref_get(&dev->si_sysref); 378 if (dev_ref_debug & 2) { 379 kprintf("reference dev %p %s(minor=%08x) refs=%d\n", 380 dev, devtoname(dev), dev->si_uminor, 381 dev->si_sysref.refcnt); 382 } 383 } 384 return(dev); 385 } 386 387 /* 388 * release a reference on a device. The device will be terminated when the 389 * last reference has been released. 390 * 391 * NOTE: we must use si_umajor to figure out the original major number, 392 * because si_ops could already be pointing at dead_dev_ops. 393 */ 394 void 395 release_dev(cdev_t dev) 396 { 397 //kprintf("release_dev\n"); 398 399 if (dev == NULL) 400 return; 401 sysref_put(&dev->si_sysref); 402 } 403 404 const char * 405 devtoname(cdev_t dev) 406 { 407 int mynor; 408 int len; 409 char *p; 410 const char *dname; 411 412 if (dev == NULL) 413 return("#nodev"); 414 if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') { 415 p = dev->si_name; 416 len = sizeof(dev->si_name); 417 if ((dname = dev_dname(dev)) != NULL) 418 ksnprintf(p, len, "#%s/", dname); 419 else 420 ksnprintf(p, len, "#%d/", major(dev)); 421 len -= strlen(p); 422 p += strlen(p); 423 mynor = minor(dev); 424 if (mynor < 0 || mynor > 255) 425 ksnprintf(p, len, "%#x", (u_int)mynor); 426 else 427 ksnprintf(p, len, "%d", mynor); 428 } 429 return (dev->si_name); 430 } 431 432