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.3 2003/07/24 23:52:38 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 cdevsw *cdevsw[NUMCDEVSW]; 47 static struct lwkt_port *cdevport[NUMCDEVSW]; 48 49 static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg); 50 51 /* 52 * Initialize a message port to serve as the default message-handling port 53 * for device operations. This message port provides compatibility with 54 * traditional cdevsw dispatch functions. There are two primary modes: 55 * 56 * mp_td is NULL: The d_autoq mask is ignored and all messages are translated 57 * into directly, synchronous cdevsw calls. 58 * 59 * mp_td not NULL: The d_autoq mask is used to determine which messages should 60 * be queued and which should be handled synchronously. 61 * 62 * Don't worry too much about optimizing this code, the critical devices 63 * will implement their own port messaging functions directly. 64 */ 65 static void 66 init_default_cdevsw_port(lwkt_port_t port) 67 { 68 lwkt_init_port(port, NULL); 69 port->mp_beginmsg = cdevsw_putport; 70 } 71 72 static 73 int 74 cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg) 75 { 76 cdevallmsg_t msg = (cdevallmsg_t)lmsg; 77 struct cdevsw *csw = msg->am_msg.csw; 78 int error; 79 80 /* 81 * If queueable then officially queue the message 82 */ 83 if (port->mp_td) { 84 int mask = (1 << (msg->am_lmsg.ms_cmd & MSG_SUBCMD_MASK)); 85 if (csw->d_autoq & mask) 86 return(lwkt_putport(port, &msg->am_lmsg)); 87 } 88 89 /* 90 * Run the device switch function synchronously in the context of the 91 * caller and return a synchronous error code (anything not EASYNC). 92 */ 93 switch(msg->am_lmsg.ms_cmd) { 94 case CDEV_CMD_OPEN: 95 error = csw->old_open( 96 msg->am_open.msg.dev, 97 msg->am_open.oflags, 98 msg->am_open.devtype, 99 msg->am_open.td); 100 break; 101 case CDEV_CMD_CLOSE: 102 error = csw->old_close( 103 msg->am_close.msg.dev, 104 msg->am_close.fflag, 105 msg->am_close.devtype, 106 msg->am_close.td); 107 break; 108 case CDEV_CMD_STRATEGY: 109 csw->old_strategy(msg->am_strategy.bp); 110 error = 0; 111 break; 112 case CDEV_CMD_IOCTL: 113 error = csw->old_ioctl( 114 msg->am_ioctl.msg.dev, 115 msg->am_ioctl.cmd, 116 msg->am_ioctl.data, 117 msg->am_ioctl.fflag, 118 msg->am_ioctl.td); 119 break; 120 case CDEV_CMD_DUMP: 121 error = csw->old_dump(msg->am_ioctl.msg.dev); 122 break; 123 case CDEV_CMD_PSIZE: 124 msg->am_psize.result = csw->old_psize(msg->am_psize.msg.dev); 125 error = 0; /* XXX */ 126 break; 127 case CDEV_CMD_READ: 128 error = csw->old_read( 129 msg->am_read.msg.dev, 130 msg->am_read.uio, 131 msg->am_read.ioflag); 132 break; 133 case CDEV_CMD_WRITE: 134 error = csw->old_write( 135 msg->am_read.msg.dev, 136 msg->am_read.uio, 137 msg->am_read.ioflag); 138 break; 139 case CDEV_CMD_POLL: 140 msg->am_poll.events = csw->old_poll( 141 msg->am_poll.msg.dev, 142 msg->am_poll.events, 143 msg->am_poll.td); 144 error = 0; 145 break; 146 case CDEV_CMD_KQFILTER: 147 msg->am_kqfilter.result = csw->old_kqfilter( 148 msg->am_kqfilter.msg.dev, 149 msg->am_kqfilter.kn); 150 error = 0; 151 break; 152 case CDEV_CMD_MMAP: 153 msg->am_mmap.result = csw->old_mmap( 154 msg->am_mmap.msg.dev, 155 msg->am_mmap.offset, 156 msg->am_mmap.nprot); 157 error = 0; /* XXX */ 158 break; 159 default: 160 error = ENOSYS; 161 break; 162 } 163 KKASSERT(error != EASYNC); 164 return(error); 165 } 166 167 /* 168 * These device dispatch functions provide convenient entry points for 169 * any code wishing to make a dev call. 170 * 171 * YYY we ought to be able to optimize the port lookup by caching it in 172 * the dev_t structure itself. 173 */ 174 static __inline 175 struct cdevsw * 176 _devsw(dev_t dev) 177 { 178 if (dev->si_devsw) 179 return (dev->si_devsw); 180 return(cdevsw[major(dev)]); 181 } 182 183 static __inline 184 lwkt_port_t 185 _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd) 186 { 187 struct cdevsw *csw; 188 189 lwkt_initmsg(&msg->msg, cmd); 190 msg->dev = dev; 191 msg->csw = csw = _devsw(dev); 192 if (csw != NULL) { /* YYY too hackish */ 193 KKASSERT(csw->d_port); /* YYY too hackish */ 194 if (cdevport[major(dev)]) /* YYY too hackish */ 195 return(cdevport[major(dev)]); 196 return(csw->d_port); 197 } 198 return(NULL); 199 } 200 201 int 202 dev_dopen(dev_t dev, int oflags, int devtype, thread_t td) 203 { 204 struct cdevmsg_open msg; 205 lwkt_port_t port; 206 207 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN); 208 if (port == NULL) 209 return(ENXIO); 210 msg.oflags = oflags; 211 msg.devtype = devtype; 212 msg.td = td; 213 return(lwkt_domsg(port, &msg.msg.msg)); 214 } 215 216 int 217 dev_dclose(dev_t dev, int fflag, int devtype, thread_t td) 218 { 219 struct cdevmsg_close msg; 220 lwkt_port_t port; 221 222 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE); 223 if (port == NULL) 224 return(ENXIO); 225 msg.fflag = fflag; 226 msg.devtype = devtype; 227 msg.td = td; 228 return(lwkt_domsg(port, &msg.msg.msg)); 229 } 230 231 void 232 dev_dstrategy(dev_t dev, struct buf *bp) 233 { 234 struct cdevmsg_strategy msg; 235 lwkt_port_t port; 236 237 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY); 238 KKASSERT(port); /* 'nostrategy' function is NULL YYY */ 239 msg.bp = bp; 240 lwkt_domsg(port, &msg.msg.msg); 241 } 242 243 int 244 dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td) 245 { 246 struct cdevmsg_ioctl msg; 247 lwkt_port_t port; 248 249 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL); 250 if (port == NULL) 251 return(ENXIO); 252 msg.cmd = cmd; 253 msg.data = data; 254 msg.fflag = fflag; 255 msg.td = td; 256 return(lwkt_domsg(port, &msg.msg.msg)); 257 } 258 259 int 260 dev_ddump(dev_t dev) 261 { 262 struct cdevmsg_dump msg; 263 lwkt_port_t port; 264 265 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); 266 if (port == NULL) 267 return(ENXIO); 268 return(lwkt_domsg(port, &msg.msg.msg)); 269 } 270 271 int 272 dev_dpsize(dev_t dev) 273 { 274 struct cdevmsg_psize msg; 275 lwkt_port_t port; 276 int error; 277 278 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE); 279 if (port == NULL) 280 return(-1); 281 error = lwkt_domsg(port, &msg.msg.msg); 282 if (error == 0) 283 return(msg.result); 284 return(-1); 285 } 286 287 int 288 dev_dread(dev_t dev, struct uio *uio, int ioflag) 289 { 290 struct cdevmsg_read msg; 291 lwkt_port_t port; 292 293 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ); 294 if (port == NULL) 295 return(ENXIO); 296 msg.uio = uio; 297 msg.ioflag = ioflag; 298 return(lwkt_domsg(port, &msg.msg.msg)); 299 } 300 301 int 302 dev_dwrite(dev_t dev, struct uio *uio, int ioflag) 303 { 304 struct cdevmsg_write msg; 305 lwkt_port_t port; 306 307 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE); 308 if (port == NULL) 309 return(ENXIO); 310 msg.uio = uio; 311 msg.ioflag = ioflag; 312 return(lwkt_domsg(port, &msg.msg.msg)); 313 } 314 315 int 316 dev_dpoll(dev_t dev, int events, thread_t td) 317 { 318 struct cdevmsg_poll msg; 319 lwkt_port_t port; 320 int error; 321 322 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL); 323 if (port == NULL) 324 return(ENXIO); 325 msg.events = events; 326 msg.td = td; 327 error = lwkt_domsg(port, &msg.msg.msg); 328 if (error == 0) 329 return(msg.events); 330 return(seltrue(dev, msg.events, td)); 331 } 332 333 int 334 dev_dkqfilter(dev_t dev, struct knote *kn) 335 { 336 struct cdevmsg_kqfilter msg; 337 lwkt_port_t port; 338 int error; 339 340 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER); 341 if (port == NULL) 342 return(ENXIO); 343 msg.kn = kn; 344 error = lwkt_domsg(port, &msg.msg.msg); 345 if (error == 0) 346 return(msg.result); 347 return(ENODEV); 348 } 349 350 int 351 dev_dmmap(dev_t dev, vm_offset_t offset, int nprot) 352 { 353 struct cdevmsg_mmap msg; 354 lwkt_port_t port; 355 int error; 356 357 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP); 358 if (port == NULL) 359 return(-1); 360 msg.offset = offset; 361 msg.nprot = nprot; 362 error = lwkt_domsg(port, &msg.msg.msg); 363 if (error == 0) 364 return(msg.result); 365 return(-1); 366 } 367 368 int 369 dev_port_dopen(lwkt_port_t port, dev_t dev, int oflags, int devtype, thread_t td) 370 { 371 struct cdevmsg_open msg; 372 373 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN); 374 if (port == NULL) 375 return(ENXIO); 376 msg.oflags = oflags; 377 msg.devtype = devtype; 378 msg.td = td; 379 return(lwkt_domsg(port, &msg.msg.msg)); 380 } 381 382 int 383 dev_port_dclose(lwkt_port_t port, dev_t dev, int fflag, int devtype, thread_t td) 384 { 385 struct cdevmsg_close msg; 386 387 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE); 388 if (port == NULL) 389 return(ENXIO); 390 msg.fflag = fflag; 391 msg.devtype = devtype; 392 msg.td = td; 393 return(lwkt_domsg(port, &msg.msg.msg)); 394 } 395 396 void 397 dev_port_dstrategy(lwkt_port_t port, dev_t dev, struct buf *bp) 398 { 399 struct cdevmsg_strategy msg; 400 401 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY); 402 KKASSERT(port); /* 'nostrategy' function is NULL YYY */ 403 msg.bp = bp; 404 lwkt_domsg(port, &msg.msg.msg); 405 } 406 407 int 408 dev_port_dioctl(lwkt_port_t port, dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td) 409 { 410 struct cdevmsg_ioctl msg; 411 412 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL); 413 if (port == NULL) 414 return(ENXIO); 415 msg.cmd = cmd; 416 msg.data = data; 417 msg.fflag = fflag; 418 msg.td = td; 419 return(lwkt_domsg(port, &msg.msg.msg)); 420 } 421 422 int 423 dev_port_ddump(lwkt_port_t port, dev_t dev) 424 { 425 struct cdevmsg_dump msg; 426 427 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); 428 if (port == NULL) 429 return(ENXIO); 430 return(lwkt_domsg(port, &msg.msg.msg)); 431 } 432 433 int 434 dev_port_dpsize(lwkt_port_t port, dev_t dev) 435 { 436 struct cdevmsg_psize msg; 437 int error; 438 439 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE); 440 if (port == NULL) 441 return(-1); 442 error = lwkt_domsg(port, &msg.msg.msg); 443 if (error == 0) 444 return(msg.result); 445 return(-1); 446 } 447 448 int 449 dev_port_dread(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag) 450 { 451 struct cdevmsg_read msg; 452 453 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ); 454 if (port == NULL) 455 return(ENXIO); 456 msg.uio = uio; 457 msg.ioflag = ioflag; 458 return(lwkt_domsg(port, &msg.msg.msg)); 459 } 460 461 int 462 dev_port_dwrite(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag) 463 { 464 struct cdevmsg_write msg; 465 466 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE); 467 if (port == NULL) 468 return(ENXIO); 469 msg.uio = uio; 470 msg.ioflag = ioflag; 471 return(lwkt_domsg(port, &msg.msg.msg)); 472 } 473 474 int 475 dev_port_dpoll(lwkt_port_t port, dev_t dev, int events, thread_t td) 476 { 477 struct cdevmsg_poll msg; 478 int error; 479 480 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL); 481 if (port == NULL) 482 return(ENXIO); 483 msg.events = events; 484 msg.td = td; 485 error = lwkt_domsg(port, &msg.msg.msg); 486 if (error == 0) 487 return(msg.events); 488 return(seltrue(dev, msg.events, td)); 489 } 490 491 int 492 dev_port_dkqfilter(lwkt_port_t port, dev_t dev, struct knote *kn) 493 { 494 struct cdevmsg_kqfilter msg; 495 int error; 496 497 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER); 498 if (port == NULL) 499 return(ENXIO); 500 msg.kn = kn; 501 error = lwkt_domsg(port, &msg.msg.msg); 502 if (error == 0) 503 return(msg.result); 504 return(ENODEV); 505 } 506 507 int 508 dev_port_dmmap(lwkt_port_t port, dev_t dev, vm_offset_t offset, int nprot) 509 { 510 struct cdevmsg_mmap msg; 511 int error; 512 513 _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP); 514 if (port == NULL) 515 return(-1); 516 msg.offset = offset; 517 msg.nprot = nprot; 518 error = lwkt_domsg(port, &msg.msg.msg); 519 if (error == 0) 520 return(msg.result); 521 return(-1); 522 } 523 524 const char * 525 dev_dname(dev_t dev) 526 { 527 struct cdevsw *csw; 528 529 if ((csw = _devsw(dev)) != NULL) 530 return(csw->d_name); 531 return(NULL); 532 } 533 534 int 535 dev_dflags(dev_t dev) 536 { 537 struct cdevsw *csw; 538 539 if ((csw = _devsw(dev)) != NULL) 540 return(csw->d_flags); 541 return(0); 542 } 543 544 int 545 dev_dmaj(dev_t dev) 546 { 547 struct cdevsw *csw; 548 549 if ((csw = _devsw(dev)) != NULL) 550 return(csw->d_maj); 551 return(0); 552 } 553 554 lwkt_port_t 555 dev_dport(dev_t dev) 556 { 557 struct cdevsw *csw; 558 559 if ((csw = _devsw(dev)) != NULL) { 560 if (cdevport[major(dev)]) /* YYY too hackish */ 561 return(cdevport[major(dev)]); 562 return(csw->d_port); 563 } 564 return(NULL); 565 } 566 567 #if 0 568 /* 569 * cdevsw[] array functions, moved from kern/kern_conf.c 570 */ 571 struct cdevsw * 572 devsw(dev_t dev) 573 { 574 return(_devsw(dev)); 575 } 576 #endif 577 578 /* 579 * Convert a cdevsw template into the real thing, filling in fields the 580 * device left empty with appropriate defaults. 581 */ 582 void 583 compile_devsw(struct cdevsw *devsw) 584 { 585 static lwkt_port devsw_compat_port; 586 587 if (devsw_compat_port.mp_beginmsg == NULL) 588 init_default_cdevsw_port(&devsw_compat_port); 589 590 if (devsw->old_open == NULL) 591 devsw->old_open = noopen; 592 if (devsw->old_close == NULL) 593 devsw->old_close = noclose; 594 if (devsw->old_read == NULL) 595 devsw->old_read = noread; 596 if (devsw->old_write == NULL) 597 devsw->old_write = nowrite; 598 if (devsw->old_ioctl == NULL) 599 devsw->old_ioctl = noioctl; 600 if (devsw->old_poll == NULL) 601 devsw->old_poll = nopoll; 602 if (devsw->old_mmap == NULL) 603 devsw->old_mmap = nommap; 604 if (devsw->old_strategy == NULL) 605 devsw->old_strategy = nostrategy; 606 if (devsw->old_dump == NULL) 607 devsw->old_dump = nodump; 608 if (devsw->old_psize == NULL) 609 devsw->old_psize = nopsize; 610 if (devsw->old_kqfilter == NULL) 611 devsw->old_kqfilter = nokqfilter; 612 613 if (devsw->d_port == NULL) 614 devsw->d_port = &devsw_compat_port; 615 } 616 617 /* 618 * Add a cdevsw entry 619 */ 620 int 621 cdevsw_add(struct cdevsw *newentry) 622 { 623 compile_devsw(newentry); 624 if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) { 625 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", 626 newentry->d_name, newentry->d_maj); 627 return (EINVAL); 628 } 629 if (cdevsw[newentry->d_maj]) { 630 printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n", 631 newentry->d_name, cdevsw[newentry->d_maj]->d_name); 632 } 633 cdevsw[newentry->d_maj] = newentry; 634 return (0); 635 } 636 637 /* 638 * Add a cdevsw entry and override the port. 639 */ 640 lwkt_port_t 641 cdevsw_add_override(struct cdevsw *newentry, lwkt_port_t port) 642 { 643 int error; 644 645 if ((error = cdevsw_add(newentry)) == 0) 646 cdevport[newentry->d_maj] = port; 647 return(newentry->d_port); 648 } 649 650 lwkt_port_t 651 cdevsw_dev_override(dev_t dev, lwkt_port_t port) 652 { 653 struct cdevsw *csw; 654 655 KKASSERT(major(dev) >= 0 && major(dev) < NUMCDEVSW); 656 if ((csw = _devsw(dev)) != NULL) { 657 cdevport[major(dev)] = port; 658 return(csw->d_port); 659 } 660 return(NULL); 661 } 662 663 /* 664 * Remove a cdevsw entry 665 */ 666 int 667 cdevsw_remove(struct cdevsw *oldentry) 668 { 669 if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) { 670 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", 671 oldentry->d_name, oldentry->d_maj); 672 return EINVAL; 673 } 674 cdevsw[oldentry->d_maj] = NULL; 675 cdevport[oldentry->d_maj] = NULL; 676 return 0; 677 } 678 679