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