1 /* 2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com> All rights reserved. 3 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Terrence R. Lambert 4 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Julian R. Elishcer, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $DragonFly: src/sys/kern/kern_device.c,v 1.13 2004/09/15 03:21:03 dillon Exp $ 29 */ 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/sysctl.h> 33 #include <sys/systm.h> 34 #include <sys/module.h> 35 #include <sys/malloc.h> 36 #include <sys/conf.h> 37 #include <sys/vnode.h> 38 #include <sys/queue.h> 39 #include <sys/msgport.h> 40 #include <sys/device.h> 41 #include <machine/stdarg.h> 42 #include <sys/proc.h> 43 #include <sys/thread2.h> 44 #include <sys/msgport2.h> 45 46 static struct cdevlink *cdevbase[NUMCDEVSW]; 47 48 static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg); 49 50 struct cdevsw dead_cdevsw; 51 52 /* 53 * Initialize a message port to serve as the default message-handling port 54 * for device operations. This message port provides compatibility with 55 * traditional cdevsw dispatch functions by running them synchronously. 56 * 57 * YYY NOTE: ms_cmd can now hold a function pointer, should this code be 58 * converted from an integer op to a function pointer with a flag to 59 * indicate legacy operation? 60 */ 61 static void 62 init_default_cdevsw_port(lwkt_port_t port) 63 { 64 lwkt_initport(port, NULL); 65 port->mp_putport = cdevsw_putport; 66 } 67 68 static 69 int 70 cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg) 71 { 72 cdevallmsg_t msg = (cdevallmsg_t)lmsg; 73 struct cdevsw *devsw = msg->am_msg.dev->si_devsw; 74 int error; 75 76 /* 77 * Run the device switch function synchronously in the context of the 78 * caller and return a synchronous error code (anything not EASYNC). 79 */ 80 switch(msg->am_lmsg.ms_cmd.cm_op) { 81 case CDEV_CMD_OPEN: 82 error = devsw->old_open( 83 msg->am_open.msg.dev, 84 msg->am_open.oflags, 85 msg->am_open.devtype, 86 msg->am_open.td); 87 break; 88 case CDEV_CMD_CLOSE: 89 error = devsw->old_close( 90 msg->am_close.msg.dev, 91 msg->am_close.fflag, 92 msg->am_close.devtype, 93 msg->am_close.td); 94 break; 95 case CDEV_CMD_STRATEGY: 96 devsw->old_strategy(msg->am_strategy.bp); 97 error = 0; 98 break; 99 case CDEV_CMD_IOCTL: 100 error = devsw->old_ioctl( 101 msg->am_ioctl.msg.dev, 102 msg->am_ioctl.cmd, 103 msg->am_ioctl.data, 104 msg->am_ioctl.fflag, 105 msg->am_ioctl.td); 106 break; 107 case CDEV_CMD_DUMP: 108 error = devsw->old_dump( 109 msg->am_dump.msg.dev, 110 msg->am_dump.count, 111 msg->am_dump.blkno, 112 msg->am_dump.secsize); 113 break; 114 case CDEV_CMD_PSIZE: 115 msg->am_psize.result = devsw->old_psize(msg->am_psize.msg.dev); 116 error = 0; /* XXX */ 117 break; 118 case CDEV_CMD_READ: 119 error = devsw->old_read( 120 msg->am_read.msg.dev, 121 msg->am_read.uio, 122 msg->am_read.ioflag); 123 break; 124 case CDEV_CMD_WRITE: 125 error = devsw->old_write( 126 msg->am_read.msg.dev, 127 msg->am_read.uio, 128 msg->am_read.ioflag); 129 break; 130 case CDEV_CMD_POLL: 131 msg->am_poll.events = devsw->old_poll( 132 msg->am_poll.msg.dev, 133 msg->am_poll.events, 134 msg->am_poll.td); 135 error = 0; 136 break; 137 case CDEV_CMD_KQFILTER: 138 msg->am_kqfilter.result = devsw->old_kqfilter( 139 msg->am_kqfilter.msg.dev, 140 msg->am_kqfilter.kn); 141 error = 0; 142 break; 143 case CDEV_CMD_MMAP: 144 msg->am_mmap.result = devsw->old_mmap( 145 msg->am_mmap.msg.dev, 146 msg->am_mmap.offset, 147 msg->am_mmap.nprot); 148 error = 0; /* XXX */ 149 break; 150 default: 151 error = ENOSYS; 152 break; 153 } 154 KKASSERT(error != EASYNC); 155 return(error); 156 } 157 158 static __inline 159 lwkt_port_t 160 _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd) 161 { 162 lwkt_initmsg_simple(&msg->msg, cmd); 163 msg->dev = dev; 164 return(dev->si_port); 165 } 166 167 int 168 dev_dopen(dev_t dev, int oflags, int devtype, thread_t td) 169 { 170 struct cdevmsg_open msg; 171 lwkt_port_t port; 172 173 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN); 174 if (port == NULL) 175 return(ENXIO); 176 msg.oflags = oflags; 177 msg.devtype = devtype; 178 msg.td = td; 179 return(lwkt_domsg(port, &msg.msg.msg)); 180 } 181 182 int 183 dev_dclose(dev_t dev, int fflag, int devtype, thread_t td) 184 { 185 struct cdevmsg_close msg; 186 lwkt_port_t port; 187 188 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE); 189 if (port == NULL) 190 return(ENXIO); 191 msg.fflag = fflag; 192 msg.devtype = devtype; 193 msg.td = td; 194 return(lwkt_domsg(port, &msg.msg.msg)); 195 } 196 197 void 198 dev_dstrategy(dev_t dev, struct buf *bp) 199 { 200 struct cdevmsg_strategy msg; 201 lwkt_port_t port; 202 203 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY); 204 KKASSERT(port); /* 'nostrategy' function is NULL YYY */ 205 msg.bp = bp; 206 lwkt_domsg(port, &msg.msg.msg); 207 } 208 209 int 210 dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td) 211 { 212 struct cdevmsg_ioctl msg; 213 lwkt_port_t port; 214 215 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL); 216 if (port == NULL) 217 return(ENXIO); 218 msg.cmd = cmd; 219 msg.data = data; 220 msg.fflag = fflag; 221 msg.td = td; 222 return(lwkt_domsg(port, &msg.msg.msg)); 223 } 224 225 /* 226 * note: the disk layer is expected to set count, blkno, and secsize before 227 * forwarding the message. 228 */ 229 int 230 dev_ddump(dev_t dev) 231 { 232 struct cdevmsg_dump msg; 233 lwkt_port_t port; 234 235 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); 236 if (port == NULL) 237 return(ENXIO); 238 msg.count = 0; 239 msg.blkno = 0; 240 msg.secsize = 0; 241 return(lwkt_domsg(port, &msg.msg.msg)); 242 } 243 244 int 245 dev_dpsize(dev_t dev) 246 { 247 struct cdevmsg_psize msg; 248 lwkt_port_t port; 249 int error; 250 251 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE); 252 if (port == NULL) 253 return(-1); 254 error = lwkt_domsg(port, &msg.msg.msg); 255 if (error == 0) 256 return(msg.result); 257 return(-1); 258 } 259 260 int 261 dev_dread(dev_t dev, struct uio *uio, int ioflag) 262 { 263 struct cdevmsg_read msg; 264 lwkt_port_t port; 265 266 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ); 267 if (port == NULL) 268 return(ENXIO); 269 msg.uio = uio; 270 msg.ioflag = ioflag; 271 return(lwkt_domsg(port, &msg.msg.msg)); 272 } 273 274 int 275 dev_dwrite(dev_t dev, struct uio *uio, int ioflag) 276 { 277 struct cdevmsg_write msg; 278 lwkt_port_t port; 279 280 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE); 281 if (port == NULL) 282 return(ENXIO); 283 msg.uio = uio; 284 msg.ioflag = ioflag; 285 return(lwkt_domsg(port, &msg.msg.msg)); 286 } 287 288 int 289 dev_dpoll(dev_t dev, int events, thread_t td) 290 { 291 struct cdevmsg_poll msg; 292 lwkt_port_t port; 293 int error; 294 295 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL); 296 if (port == NULL) 297 return(ENXIO); 298 msg.events = events; 299 msg.td = td; 300 error = lwkt_domsg(port, &msg.msg.msg); 301 if (error == 0) 302 return(msg.events); 303 return(seltrue(dev, msg.events, td)); 304 } 305 306 int 307 dev_dkqfilter(dev_t dev, struct knote *kn) 308 { 309 struct cdevmsg_kqfilter msg; 310 lwkt_port_t port; 311 int error; 312 313 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER); 314 if (port == NULL) 315 return(ENXIO); 316 msg.kn = kn; 317 error = lwkt_domsg(port, &msg.msg.msg); 318 if (error == 0) 319 return(msg.result); 320 return(ENODEV); 321 } 322 323 int 324 dev_dmmap(dev_t dev, vm_offset_t offset, int nprot) 325 { 326 struct cdevmsg_mmap msg; 327 lwkt_port_t port; 328 int error; 329 330 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP); 331 if (port == NULL) 332 return(-1); 333 msg.offset = offset; 334 msg.nprot = nprot; 335 error = lwkt_domsg(port, &msg.msg.msg); 336 if (error == 0) 337 return(msg.result); 338 return(-1); 339 } 340 341 const char * 342 dev_dname(dev_t dev) 343 { 344 return(dev->si_devsw->d_name); 345 } 346 347 int 348 dev_dflags(dev_t dev) 349 { 350 return(dev->si_devsw->d_flags); 351 } 352 353 int 354 dev_dmaj(dev_t dev) 355 { 356 return(dev->si_devsw->d_maj); 357 } 358 359 lwkt_port_t 360 dev_dport(dev_t dev) 361 { 362 return(dev->si_port); 363 } 364 365 /* 366 * Convert a cdevsw template into the real thing, filling in fields the 367 * device left empty with appropriate defaults. 368 */ 369 void 370 compile_devsw(struct cdevsw *devsw) 371 { 372 static lwkt_port devsw_compat_port; 373 374 if (devsw_compat_port.mp_putport == NULL) 375 init_default_cdevsw_port(&devsw_compat_port); 376 377 if (devsw->old_open == NULL) 378 devsw->old_open = noopen; 379 if (devsw->old_close == NULL) 380 devsw->old_close = noclose; 381 if (devsw->old_read == NULL) 382 devsw->old_read = noread; 383 if (devsw->old_write == NULL) 384 devsw->old_write = nowrite; 385 if (devsw->old_ioctl == NULL) 386 devsw->old_ioctl = noioctl; 387 if (devsw->old_poll == NULL) 388 devsw->old_poll = nopoll; 389 if (devsw->old_mmap == NULL) 390 devsw->old_mmap = nommap; 391 if (devsw->old_strategy == NULL) 392 devsw->old_strategy = nostrategy; 393 if (devsw->old_dump == NULL) 394 devsw->old_dump = nodump; 395 if (devsw->old_psize == NULL) 396 devsw->old_psize = nopsize; 397 if (devsw->old_kqfilter == NULL) 398 devsw->old_kqfilter = nokqfilter; 399 400 if (devsw->d_port == NULL) 401 devsw->d_port = &devsw_compat_port; 402 if (devsw->d_clone == NULL) 403 devsw->d_clone = noclone; 404 } 405 406 /* 407 * This makes a cdevsw entry visible to userland (e.g /dev/<blah>). 408 * 409 * The kernel can overload a major number with multiple cdevsw's but only 410 * the one installed in cdevbase[] is visible to userland. make_dev() does 411 * not automatically call cdevsw_add() (nor do we want it to, since 412 * partition-managed disk devices are overloaded on top of the raw device). 413 */ 414 int 415 cdevsw_add(struct cdevsw *devsw, u_int mask, u_int match) 416 { 417 int maj; 418 struct cdevlink *link; 419 420 compile_devsw(devsw); 421 maj = devsw->d_maj; 422 if (maj < 0 || maj >= NUMCDEVSW) { 423 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", 424 devsw->d_name, maj); 425 return (EINVAL); 426 } 427 for (link = cdevbase[maj]; link; link = link->next) { 428 /* 429 * If we get an exact match we usurp the target, but we only print 430 * a warning message if a different device switch is installed. 431 */ 432 if (link->mask == mask && link->match == match) { 433 if (link->devsw != devsw) { 434 printf("WARNING: \"%s\" (%p) is usurping \"%s\"'s (%p)" 435 " cdevsw[]\n", 436 devsw->d_name, devsw, 437 link->devsw->d_name, link->devsw); 438 link->devsw = devsw; 439 ++devsw->d_refs; 440 } 441 return(0); 442 } 443 /* 444 * XXX add additional warnings for overlaps 445 */ 446 } 447 448 link = malloc(sizeof(struct cdevlink), M_DEVBUF, M_INTWAIT|M_ZERO); 449 link->mask = mask; 450 link->match = match; 451 link->devsw = devsw; 452 link->next = cdevbase[maj]; 453 cdevbase[maj] = link; 454 ++devsw->d_refs; 455 return(0); 456 } 457 458 /* 459 * Should only be used by udev2dev(). 460 * 461 * If the minor number is -1, we match the first cdevsw we find for this 462 * major. 463 * 464 * Note that this function will return NULL if the minor number is not within 465 * the bounds of the installed mask(s). 466 */ 467 struct cdevsw * 468 cdevsw_get(int x, int y) 469 { 470 struct cdevlink *link; 471 472 if (x < 0 || x >= NUMCDEVSW) 473 return(NULL); 474 for (link = cdevbase[x]; link; link = link->next) { 475 if (y == -1 || (link->mask & y) == link->match) 476 return(link->devsw); 477 } 478 return(NULL); 479 } 480 481 /* 482 * Use the passed cdevsw as a template to create our intercept cdevsw, 483 * and install and return ours. 484 */ 485 struct cdevsw * 486 cdevsw_add_override(dev_t backing_dev, u_int mask, u_int match) 487 { 488 struct cdevsw *devsw; 489 struct cdevsw *bsw = backing_dev->si_devsw; 490 491 devsw = malloc(sizeof(struct cdevsw), M_DEVBUF, M_INTWAIT|M_ZERO); 492 devsw->d_name = bsw->d_name; 493 devsw->d_maj = bsw->d_maj; 494 devsw->d_flags = bsw->d_flags; 495 compile_devsw(devsw); 496 cdevsw_add(devsw, mask, match); 497 498 return(devsw); 499 } 500 501 /* 502 * Override a device's port, returning the previously installed port. This 503 * is XXX very dangerous. 504 */ 505 lwkt_port_t 506 cdevsw_dev_override(dev_t dev, lwkt_port_t port) 507 { 508 lwkt_port_t oport; 509 510 oport = dev->si_port; 511 dev->si_port = port; 512 return(oport); 513 } 514 515 /* 516 * Remove a cdevsw entry from the cdevbase[] major array so no new user opens 517 * can be performed, and destroy all devices installed in the hash table 518 * which are associated with this cdevsw. (see destroy_all_dev()). 519 */ 520 int 521 cdevsw_remove(struct cdevsw *devsw, u_int mask, u_int match) 522 { 523 int maj = devsw->d_maj; 524 struct cdevlink *link; 525 struct cdevlink **plink; 526 527 if (maj < 0 || maj >= NUMCDEVSW) { 528 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", 529 devsw->d_name, maj); 530 return EINVAL; 531 } 532 if (devsw != &dead_cdevsw) 533 destroy_all_dev(devsw, mask, match); 534 for (plink = &cdevbase[maj]; (link = *plink) != NULL; plink = &link->next) { 535 if (link->mask == mask && link->match == match) { 536 if (link->devsw == devsw) 537 break; 538 printf("%s: ERROR: cannot remove from cdevsw[], its major" 539 " number %d was stolen by %s\n", 540 devsw->d_name, maj, 541 link->devsw->d_name 542 ); 543 } 544 } 545 if (link == NULL) { 546 printf("%s(%d)[%08x/%08x]: WARNING: cdevsw removed multiple times!\n", 547 devsw->d_name, maj, mask, match); 548 } else { 549 *plink = link->next; 550 --devsw->d_refs; /* XXX cdevsw_release() / record refs */ 551 free(link, M_DEVBUF); 552 } 553 if (cdevbase[maj] == NULL && devsw->d_refs != 0) { 554 printf("%s(%d)[%08x/%08x]: Warning: cdevsw_remove() called while " 555 "%d device refs still exist!\n", 556 devsw->d_name, maj, mask, match, devsw->d_refs); 557 } else { 558 printf("%s: cdevsw removed\n", devsw->d_name); 559 } 560 return 0; 561 } 562 563 /* 564 * Release a cdevsw entry. When the ref count reaches zero, recurse 565 * through the stack. 566 */ 567 void 568 cdevsw_release(struct cdevsw *devsw) 569 { 570 --devsw->d_refs; 571 if (devsw->d_refs == 0) { 572 /* XXX */ 573 } 574 } 575 576