1 /* 2 * (MPSAFE) 3 * 4 * Copyright (c) 1995 Ugen J.S.Antsilevich 5 * 6 * Redistribution and use in source forms, with and without modification, 7 * are permitted provided that this entire comment appears intact. 8 * 9 * Redistribution in binary form may occur without any restrictions. 10 * Obviously, it would be nice if you gave credit where credit is due 11 * but requiring it would be too onerous. 12 * 13 * This software is provided ``AS IS'' without any warranties of any kind. 14 * 15 * Snoop stuff. 16 * 17 * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $ 18 * $DragonFly: src/sys/dev/misc/snp/snp.c,v 1.19 2007/05/08 02:31:41 dillon Exp $ 19 */ 20 21 #include "use_snp.h" 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/filio.h> 25 #include <sys/malloc.h> 26 #include <sys/tty.h> 27 #include <sys/conf.h> 28 #include <sys/event.h> 29 #include <sys/kernel.h> 30 #include <sys/queue.h> 31 #include <sys/snoop.h> 32 #include <sys/thread2.h> 33 #include <sys/vnode.h> 34 #include <sys/device.h> 35 #include <sys/devfs.h> 36 37 static l_close_t snplclose; 38 static l_write_t snplwrite; 39 static d_open_t snpopen; 40 static d_close_t snpclose; 41 static d_read_t snpread; 42 static d_write_t snpwrite; 43 static d_ioctl_t snpioctl; 44 static d_kqfilter_t snpkqfilter; 45 static d_clone_t snpclone; 46 DEVFS_DECLARE_CLONE_BITMAP(snp); 47 48 static void snpfilter_detach(struct knote *); 49 static int snpfilter_rd(struct knote *, long); 50 static int snpfilter_wr(struct knote *, long); 51 52 #if NSNP <= 1 53 #define SNP_PREALLOCATED_UNITS 4 54 #else 55 #define SNP_PREALLOCATED_UNITS NSNP 56 #endif 57 58 #define CDEV_MAJOR 53 59 static struct dev_ops snp_ops = { 60 { "snp", CDEV_MAJOR, 0 }, 61 .d_open = snpopen, 62 .d_close = snpclose, 63 .d_read = snpread, 64 .d_write = snpwrite, 65 .d_ioctl = snpioctl, 66 .d_kqfilter = snpkqfilter 67 }; 68 69 static struct linesw snpdisc = { 70 ttyopen, snplclose, ttread, snplwrite, 71 l_nullioctl, ttyinput, ttstart, ttymodem 72 }; 73 74 /* 75 * This is the main snoop per-device structure. 76 */ 77 struct snoop { 78 LIST_ENTRY(snoop) snp_list; /* List glue. */ 79 cdev_t snp_target; /* Target tty device. */ 80 struct tty *snp_tty; /* Target tty pointer. */ 81 u_long snp_len; /* Possible length. */ 82 u_long snp_base; /* Data base. */ 83 u_long snp_blen; /* Used length. */ 84 caddr_t snp_buf; /* Allocation pointer. */ 85 int snp_flags; /* Flags. */ 86 struct kqinfo snp_kq; /* Kqueue info. */ 87 int snp_olddisc; /* Old line discipline. */ 88 }; 89 90 /* 91 * Possible flags. 92 */ 93 #define SNOOP_ASYNC 0x0002 94 #define SNOOP_OPEN 0x0004 95 #define SNOOP_RWAIT 0x0008 96 #define SNOOP_OFLOW 0x0010 97 #define SNOOP_DOWN 0x0020 98 99 /* 100 * Other constants. 101 */ 102 #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 103 * 4K tested to be the minimum 104 * for which on normal tty 105 * usage there is no need to 106 * allocate more. 107 */ 108 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 109 * If we grow more,something 110 * really bad in this world.. 111 */ 112 113 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 114 /* 115 * The number of the "snoop" line discipline. This gets determined at 116 * module load time. 117 */ 118 static int snooplinedisc; 119 120 121 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 122 123 static struct tty *snpdevtotty (cdev_t dev); 124 static int snp_detach (struct snoop *snp); 125 static int snp_down (struct snoop *snp); 126 static int snp_in (struct snoop *snp, char *buf, int n); 127 static int snp_modevent (module_t mod, int what, void *arg); 128 129 static int 130 snplclose(struct tty *tp, int flag) 131 { 132 struct snoop *snp; 133 int error; 134 135 lwkt_gettoken(&tty_token); 136 snp = tp->t_sc; 137 error = snp_down(snp); 138 if (error != 0) { 139 lwkt_reltoken(&tty_token); 140 return (error); 141 } 142 error = ttylclose(tp, flag); 143 lwkt_reltoken(&tty_token); 144 return (error); 145 } 146 147 static int 148 snplwrite(struct tty *tp, struct uio *uio, int flag) 149 { 150 struct iovec iov; 151 struct uio uio2; 152 struct snoop *snp; 153 int error, ilen; 154 char *ibuf; 155 156 lwkt_gettoken(&tty_token); 157 error = 0; 158 ibuf = NULL; 159 snp = tp->t_sc; 160 while (uio->uio_resid > 0) { 161 ilen = (int)szmin(512, uio->uio_resid); 162 ibuf = kmalloc(ilen, M_SNP, M_WAITOK); 163 error = uiomove(ibuf, (size_t)ilen, uio); 164 if (error != 0) 165 break; 166 snp_in(snp, ibuf, ilen); 167 /* Hackish, but probably the least of all evils. */ 168 iov.iov_base = ibuf; 169 iov.iov_len = ilen; 170 uio2.uio_iov = &iov; 171 uio2.uio_iovcnt = 1; 172 uio2.uio_offset = 0; 173 uio2.uio_resid = ilen; 174 uio2.uio_segflg = UIO_SYSSPACE; 175 uio2.uio_rw = UIO_WRITE; 176 uio2.uio_td = uio->uio_td; 177 error = ttwrite(tp, &uio2, flag); 178 if (error != 0) 179 break; 180 kfree(ibuf, M_SNP); 181 ibuf = NULL; 182 } 183 if (ibuf != NULL) 184 kfree(ibuf, M_SNP); 185 lwkt_reltoken(&tty_token); 186 return (error); 187 } 188 189 static struct tty * 190 snpdevtotty(cdev_t dev) 191 { 192 if ((dev_dflags(dev) & D_TTY) == 0) 193 return (NULL); 194 return (dev->si_tty); 195 } 196 197 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 198 * interactive mode write is 3 bytes 199 * length for function keys... 200 */ 201 202 static int 203 snpwrite(struct dev_write_args *ap) 204 { 205 cdev_t dev = ap->a_head.a_dev; 206 struct uio *uio = ap->a_uio; 207 struct snoop *snp; 208 struct tty *tp; 209 int error, i, len; 210 unsigned char c[SNP_INPUT_BUF]; 211 212 lwkt_gettoken(&tty_token); 213 snp = dev->si_drv1; 214 tp = snp->snp_tty; 215 if (tp == NULL) { 216 lwkt_reltoken(&tty_token); 217 return (EIO); 218 } 219 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 220 tp->t_line == snooplinedisc) 221 goto tty_input; 222 223 kprintf("Snoop: attempt to write to bad tty.\n"); 224 lwkt_reltoken(&tty_token); 225 return (EIO); 226 227 tty_input: 228 if (!(tp->t_state & TS_ISOPEN)) { 229 lwkt_reltoken(&tty_token); 230 return (EIO); 231 } 232 233 while (uio->uio_resid > 0) { 234 len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF); 235 if ((error = uiomove(c, (size_t)len, uio)) != 0) { 236 lwkt_reltoken(&tty_token); 237 return (error); 238 } 239 for (i=0; i < len; i++) { 240 if (ttyinput(c[i], tp)) { 241 lwkt_reltoken(&tty_token); 242 return (EIO); 243 } 244 } 245 } 246 lwkt_reltoken(&tty_token); 247 return (0); 248 } 249 250 251 static int 252 snpread(struct dev_read_args *ap) 253 { 254 cdev_t dev = ap->a_head.a_dev; 255 struct uio *uio = ap->a_uio; 256 struct snoop *snp; 257 int error, len, n, nblen; 258 caddr_t from; 259 char *nbuf; 260 261 lwkt_gettoken(&tty_token); 262 snp = dev->si_drv1; 263 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 264 ("snoop buffer error")); 265 266 if (snp->snp_tty == NULL) { 267 lwkt_reltoken(&tty_token); 268 return (EIO); 269 } 270 271 snp->snp_flags &= ~SNOOP_RWAIT; 272 273 do { 274 if (snp->snp_len == 0) { 275 if (ap->a_ioflag & IO_NDELAY) { 276 lwkt_reltoken(&tty_token); 277 return (EWOULDBLOCK); 278 } 279 snp->snp_flags |= SNOOP_RWAIT; 280 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0); 281 if (error != 0) { 282 lwkt_reltoken(&tty_token); 283 return (error); 284 } 285 } 286 } while (snp->snp_len == 0); 287 288 n = snp->snp_len; 289 290 error = 0; 291 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 292 len = (int)szmin(uio->uio_resid, snp->snp_len); 293 from = (caddr_t)(snp->snp_buf + snp->snp_base); 294 if (len == 0) 295 break; 296 297 error = uiomove(from, (size_t)len, uio); 298 snp->snp_base += len; 299 snp->snp_len -= len; 300 } 301 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 302 snp->snp_flags &= ~SNOOP_OFLOW; 303 } 304 crit_enter(); 305 nblen = snp->snp_blen; 306 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 307 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 308 nblen = nblen / 2; 309 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 310 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 311 kfree(snp->snp_buf, M_SNP); 312 snp->snp_buf = nbuf; 313 snp->snp_blen = nblen; 314 snp->snp_base = 0; 315 } 316 } 317 crit_exit(); 318 319 lwkt_reltoken(&tty_token); 320 return (error); 321 } 322 323 /* 324 * NOTE: Must be called with tty_token held 325 */ 326 static int 327 snp_in(struct snoop *snp, char *buf, int n) 328 { 329 int s_free, s_tail; 330 int len, nblen; 331 caddr_t from, to; 332 char *nbuf; 333 334 ASSERT_LWKT_TOKEN_HELD(&tty_token); 335 KASSERT(n >= 0, ("negative snoop char count")); 336 337 if (n == 0) 338 return (0); 339 340 if (snp->snp_flags & SNOOP_DOWN) { 341 kprintf("Snoop: more data to down interface.\n"); 342 return (0); 343 } 344 345 if (snp->snp_flags & SNOOP_OFLOW) { 346 kprintf("Snoop: buffer overflow.\n"); 347 /* 348 * On overflow we just repeat the standart close 349 * procedure...yes , this is waste of space but.. Then next 350 * read from device will fail if one would recall he is 351 * snooping and retry... 352 */ 353 354 return (snp_down(snp)); 355 } 356 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 357 s_free = snp->snp_blen - snp->snp_len; 358 359 360 if (n > s_free) { 361 crit_enter(); 362 nblen = snp->snp_blen; 363 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 364 nblen = snp->snp_blen * 2; 365 s_free = nblen - (snp->snp_len + snp->snp_base); 366 } 367 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) { 368 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 369 kfree(snp->snp_buf, M_SNP); 370 snp->snp_buf = nbuf; 371 snp->snp_blen = nblen; 372 snp->snp_base = 0; 373 } else { 374 snp->snp_flags |= SNOOP_OFLOW; 375 if (snp->snp_flags & SNOOP_RWAIT) { 376 snp->snp_flags &= ~SNOOP_RWAIT; 377 wakeup((caddr_t)snp); 378 } 379 crit_exit(); 380 return (0); 381 } 382 crit_exit(); 383 } 384 if (n > s_tail) { 385 from = (caddr_t)(snp->snp_buf + snp->snp_base); 386 to = (caddr_t)(snp->snp_buf); 387 len = snp->snp_len; 388 bcopy(from, to, len); 389 snp->snp_base = 0; 390 } 391 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 392 bcopy(buf, to, n); 393 snp->snp_len += n; 394 395 if (snp->snp_flags & SNOOP_RWAIT) { 396 snp->snp_flags &= ~SNOOP_RWAIT; 397 wakeup((caddr_t)snp); 398 } 399 KNOTE(&snp->snp_kq.ki_note, 0); 400 401 return (n); 402 } 403 404 static int 405 snpopen(struct dev_open_args *ap) 406 { 407 cdev_t dev = ap->a_head.a_dev; 408 struct snoop *snp; 409 410 lwkt_gettoken(&tty_token); 411 if (dev->si_drv1 == NULL) { 412 #if 0 413 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL, 414 0600, "snp%d", minor(dev)); 415 #endif 416 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP, 417 M_WAITOK | M_ZERO); 418 } else { 419 lwkt_reltoken(&tty_token); 420 return (EBUSY); 421 } 422 423 /* 424 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 425 * all previous settings (especially SNOOP_OFLOW) will be cleared. 426 */ 427 snp->snp_flags = SNOOP_OPEN; 428 429 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 430 snp->snp_blen = SNOOP_MINLEN; 431 snp->snp_base = 0; 432 snp->snp_len = 0; 433 434 /* 435 * snp_tty == NULL is for inactive snoop devices. 436 */ 437 snp->snp_tty = NULL; 438 snp->snp_target = NULL; 439 440 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 441 lwkt_reltoken(&tty_token); 442 return (0); 443 } 444 445 /* 446 * NOTE: Must be called with tty_token held 447 */ 448 static int 449 snp_detach(struct snoop *snp) 450 { 451 struct tty *tp; 452 453 ASSERT_LWKT_TOKEN_HELD(&tty_token); 454 snp->snp_base = 0; 455 snp->snp_len = 0; 456 457 /* 458 * If line disc. changed we do not touch this pointer, SLIP/PPP will 459 * change it anyway. 460 */ 461 tp = snp->snp_tty; 462 if (tp == NULL) 463 goto detach_notty; 464 465 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 466 tp->t_line == snooplinedisc) { 467 tp->t_sc = NULL; 468 tp->t_state &= ~TS_SNOOP; 469 tp->t_line = snp->snp_olddisc; 470 } else 471 kprintf("Snoop: bad attached tty data.\n"); 472 473 snp->snp_tty = NULL; 474 snp->snp_target = NULL; 475 476 detach_notty: 477 KNOTE(&snp->snp_kq.ki_note, 0); 478 if ((snp->snp_flags & SNOOP_OPEN) == 0) 479 kfree(snp, M_SNP); 480 481 return (0); 482 } 483 484 static int 485 snpclose(struct dev_close_args *ap) 486 { 487 cdev_t dev = ap->a_head.a_dev; 488 struct snoop *snp; 489 int ret; 490 491 lwkt_gettoken(&tty_token); 492 snp = dev->si_drv1; 493 snp->snp_blen = 0; 494 LIST_REMOVE(snp, snp_list); 495 kfree(snp->snp_buf, M_SNP); 496 snp->snp_flags &= ~SNOOP_OPEN; 497 dev->si_drv1 = NULL; 498 if (dev->si_uminor >= SNP_PREALLOCATED_UNITS) { 499 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), dev->si_uminor); 500 destroy_dev(dev); 501 } 502 ret = snp_detach(snp); 503 lwkt_reltoken(&tty_token); 504 return ret; 505 } 506 507 /* 508 * NOTE: Must be called with tty_token held 509 */ 510 static int 511 snp_down(struct snoop *snp) 512 { 513 514 ASSERT_LWKT_TOKEN_HELD(&tty_token); 515 if (snp->snp_blen != SNOOP_MINLEN) { 516 kfree(snp->snp_buf, M_SNP); 517 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 518 snp->snp_blen = SNOOP_MINLEN; 519 } 520 snp->snp_flags |= SNOOP_DOWN; 521 522 return (snp_detach(snp)); 523 } 524 525 static int 526 snpioctl(struct dev_ioctl_args *ap) 527 { 528 cdev_t dev = ap->a_head.a_dev; 529 struct snoop *snp; 530 struct tty *tp, *tpo; 531 cdev_t tdev; 532 int ret; 533 534 lwkt_gettoken(&tty_token); 535 snp = dev->si_drv1; 536 switch (ap->a_cmd) { 537 case SNPSTTY: 538 tdev = udev2dev(*((udev_t *)ap->a_data), 0); 539 if (tdev == NULL) { 540 lwkt_reltoken(&tty_token); 541 ret = snp_down(snp); 542 return ret; 543 } 544 545 tp = snpdevtotty(tdev); 546 if (!tp) { 547 lwkt_reltoken(&tty_token); 548 return (EINVAL); 549 } 550 if (tp->t_state & TS_SNOOP) { 551 lwkt_reltoken(&tty_token); 552 return (EBUSY); 553 } 554 555 crit_enter(); 556 557 if (snp->snp_target == NULL) { 558 tpo = snp->snp_tty; 559 if (tpo) 560 tpo->t_state &= ~TS_SNOOP; 561 } 562 563 tp->t_sc = (caddr_t)snp; 564 tp->t_state |= TS_SNOOP; 565 snp->snp_olddisc = tp->t_line; 566 tp->t_line = snooplinedisc; 567 snp->snp_tty = tp; 568 snp->snp_target = tdev; 569 570 /* 571 * Clean overflow and down flags - 572 * we'll have a chance to get them in the future :))) 573 */ 574 snp->snp_flags &= ~SNOOP_OFLOW; 575 snp->snp_flags &= ~SNOOP_DOWN; 576 crit_exit(); 577 break; 578 579 case SNPGTTY: 580 /* 581 * We keep snp_target field specially to make 582 * SNPGTTY happy, else we can't know what is device 583 * major/minor for tty. 584 */ 585 *((cdev_t *)ap->a_data) = snp->snp_target; 586 break; 587 588 case FIOASYNC: 589 if (*(int *)ap->a_data) 590 snp->snp_flags |= SNOOP_ASYNC; 591 else 592 snp->snp_flags &= ~SNOOP_ASYNC; 593 break; 594 595 case FIONREAD: 596 crit_enter(); 597 if (snp->snp_tty != NULL) 598 *(int *)ap->a_data = snp->snp_len; 599 else 600 if (snp->snp_flags & SNOOP_DOWN) { 601 if (snp->snp_flags & SNOOP_OFLOW) 602 *(int *)ap->a_data = SNP_OFLOW; 603 else 604 *(int *)ap->a_data = SNP_TTYCLOSE; 605 } else { 606 *(int *)ap->a_data = SNP_DETACH; 607 } 608 crit_exit(); 609 break; 610 611 default: 612 lwkt_reltoken(&tty_token); 613 return (ENOTTY); 614 } 615 lwkt_reltoken(&tty_token); 616 return (0); 617 } 618 619 static struct filterops snpfiltops_rd = 620 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_rd }; 621 static struct filterops snpfiltops_wr = 622 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_wr }; 623 624 static int 625 snpkqfilter(struct dev_kqfilter_args *ap) 626 { 627 cdev_t dev = ap->a_head.a_dev; 628 struct snoop *snp = dev->si_drv1; 629 struct knote *kn = ap->a_kn; 630 struct klist *klist; 631 632 lwkt_gettoken(&tty_token); 633 ap->a_result = 0; 634 635 switch (kn->kn_filter) { 636 case EVFILT_READ: 637 kn->kn_fop = &snpfiltops_rd; 638 kn->kn_hook = (caddr_t)snp; 639 break; 640 case EVFILT_WRITE: 641 kn->kn_fop = &snpfiltops_wr; 642 kn->kn_hook = (caddr_t)snp; 643 break; 644 default: 645 ap->a_result = EOPNOTSUPP; 646 lwkt_reltoken(&tty_token); 647 return (0); 648 } 649 650 klist = &snp->snp_kq.ki_note; 651 knote_insert(klist, kn); 652 653 lwkt_reltoken(&tty_token); 654 return (0); 655 } 656 657 static void 658 snpfilter_detach(struct knote *kn) 659 { 660 struct snoop *snp = (struct snoop *)kn->kn_hook; 661 struct klist *klist; 662 663 klist = &snp->snp_kq.ki_note; 664 knote_insert(klist, kn); 665 } 666 667 static int 668 snpfilter_rd(struct knote *kn, long hint) 669 { 670 struct snoop *snp = (struct snoop *)kn->kn_hook; 671 int ready = 0; 672 673 lwkt_gettoken(&tty_token); 674 /* 675 * If snoop is down, we don't want to poll forever so we return 1. 676 * Caller should see if we down via FIONREAD ioctl(). The last should 677 * return -1 to indicate down state. 678 */ 679 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 680 ready = 1; 681 682 lwkt_reltoken(&tty_token); 683 return (ready); 684 } 685 686 static int 687 snpfilter_wr(struct knote *kn, long hint) 688 { 689 /* Writing is always OK */ 690 return (1); 691 } 692 693 static int 694 snpclone(struct dev_clone_args *ap) 695 { 696 int unit; 697 lwkt_gettoken(&tty_token); 698 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0); 699 ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600, 700 "snp%d", unit); 701 lwkt_reltoken(&tty_token); 702 return 0; 703 } 704 705 static int 706 snp_modevent(module_t mod, int type, void *data) 707 { 708 int i; 709 710 lwkt_gettoken(&tty_token); 711 switch (type) { 712 case MOD_LOAD: 713 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 714 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp), 715 snpclone, UID_ROOT, GID_WHEEL, 0600, "snp"); 716 717 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) { 718 make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, "snp%d", i); 719 devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i); 720 } 721 break; 722 case MOD_UNLOAD: 723 if (!LIST_EMPTY(&snp_sclist)) 724 return (EBUSY); 725 ldisc_deregister(snooplinedisc); 726 devfs_clone_handler_del("snp"); 727 dev_ops_remove_all(&snp_ops); 728 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp)); 729 break; 730 default: 731 break; 732 } 733 lwkt_reltoken(&tty_token); 734 return (0); 735 } 736 737 static moduledata_t snp_mod = { 738 "snp", 739 snp_modevent, 740 NULL 741 }; 742 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR); 743