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 lwkt_gettoken(&tp->t_token); 217 if (tp == NULL) { 218 lwkt_reltoken(&tp->t_token); 219 return (EIO); 220 } 221 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 222 tp->t_line == snooplinedisc) 223 goto tty_input; 224 225 kprintf("Snoop: attempt to write to bad tty.\n"); 226 lwkt_reltoken(&tp->t_token); 227 return (EIO); 228 229 tty_input: 230 if (!(tp->t_state & TS_ISOPEN)) { 231 lwkt_reltoken(&tp->t_token); 232 return (EIO); 233 } 234 235 while (uio->uio_resid > 0) { 236 len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF); 237 if ((error = uiomove(c, (size_t)len, uio)) != 0) { 238 lwkt_reltoken(&tp->t_token); 239 return (error); 240 } 241 for (i=0; i < len; i++) { 242 if (ttyinput(c[i], tp)) { 243 lwkt_reltoken(&tp->t_token); 244 return (EIO); 245 } 246 } 247 } 248 lwkt_reltoken(&tp->t_token); 249 return (0); 250 } 251 252 253 static int 254 snpread(struct dev_read_args *ap) 255 { 256 cdev_t dev = ap->a_head.a_dev; 257 struct uio *uio = ap->a_uio; 258 struct snoop *snp; 259 struct tty *tp; 260 int error, len, n, nblen; 261 caddr_t from; 262 char *nbuf; 263 264 snp = dev->si_drv1; 265 tp = snp->snp_tty; 266 lwkt_gettoken(&tp->t_token); 267 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 268 ("snoop buffer error")); 269 270 if (snp->snp_tty == NULL) { 271 lwkt_reltoken(&tp->t_token); 272 return (EIO); 273 } 274 275 snp->snp_flags &= ~SNOOP_RWAIT; 276 277 do { 278 if (snp->snp_len == 0) { 279 if (ap->a_ioflag & IO_NDELAY) { 280 lwkt_reltoken(&tp->t_token); 281 return (EWOULDBLOCK); 282 } 283 snp->snp_flags |= SNOOP_RWAIT; 284 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0); 285 if (error != 0) { 286 lwkt_reltoken(&tp->t_token); 287 return (error); 288 } 289 } 290 } while (snp->snp_len == 0); 291 292 n = snp->snp_len; 293 294 error = 0; 295 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 296 len = (int)szmin(uio->uio_resid, snp->snp_len); 297 from = (caddr_t)(snp->snp_buf + snp->snp_base); 298 if (len == 0) 299 break; 300 301 error = uiomove(from, (size_t)len, uio); 302 snp->snp_base += len; 303 snp->snp_len -= len; 304 } 305 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 306 snp->snp_flags &= ~SNOOP_OFLOW; 307 } 308 nblen = snp->snp_blen; 309 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 310 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 311 nblen = nblen / 2; 312 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 313 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 314 kfree(snp->snp_buf, M_SNP); 315 snp->snp_buf = nbuf; 316 snp->snp_blen = nblen; 317 snp->snp_base = 0; 318 } 319 } 320 lwkt_reltoken(&tp->t_token); 321 322 return (error); 323 } 324 325 /* 326 * NOTE: Must be called with tp->t_token held 327 */ 328 static int 329 snp_in(struct snoop *snp, char *buf, int n) 330 { 331 int s_free, s_tail; 332 int len, nblen; 333 caddr_t from, to; 334 char *nbuf; 335 336 KASSERT(n >= 0, ("negative snoop char count")); 337 338 if (n == 0) 339 return (0); 340 341 if (snp->snp_flags & SNOOP_DOWN) { 342 kprintf("Snoop: more data to down interface.\n"); 343 return (0); 344 } 345 346 if (snp->snp_flags & SNOOP_OFLOW) { 347 kprintf("Snoop: buffer overflow.\n"); 348 /* 349 * On overflow we just repeat the standart close 350 * procedure...yes , this is waste of space but.. Then next 351 * read from device will fail if one would recall he is 352 * snooping and retry... 353 */ 354 355 return (snp_down(snp)); 356 } 357 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 358 s_free = snp->snp_blen - snp->snp_len; 359 360 361 if (n > s_free) { 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 return (0); 380 } 381 } 382 if (n > s_tail) { 383 from = (caddr_t)(snp->snp_buf + snp->snp_base); 384 to = (caddr_t)(snp->snp_buf); 385 len = snp->snp_len; 386 bcopy(from, to, len); 387 snp->snp_base = 0; 388 } 389 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 390 bcopy(buf, to, n); 391 snp->snp_len += n; 392 393 if (snp->snp_flags & SNOOP_RWAIT) { 394 snp->snp_flags &= ~SNOOP_RWAIT; 395 wakeup((caddr_t)snp); 396 } 397 KNOTE(&snp->snp_kq.ki_note, 0); 398 399 return (n); 400 } 401 402 static int 403 snpopen(struct dev_open_args *ap) 404 { 405 cdev_t dev = ap->a_head.a_dev; 406 struct snoop *snp; 407 408 lwkt_gettoken(&snp_token); 409 if (dev->si_drv1 == NULL) { 410 #if 0 411 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL, 412 0600, "snp%d", minor(dev)); 413 #endif 414 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP, 415 M_WAITOK | M_ZERO); 416 } else { 417 lwkt_reltoken(&snp_token); 418 return (EBUSY); 419 } 420 421 /* 422 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 423 * all previous settings (especially SNOOP_OFLOW) will be cleared. 424 */ 425 snp->snp_flags = SNOOP_OPEN; 426 427 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 428 snp->snp_blen = SNOOP_MINLEN; 429 snp->snp_base = 0; 430 snp->snp_len = 0; 431 432 /* 433 * snp_tty == NULL is for inactive snoop devices. 434 */ 435 snp->snp_tty = NULL; 436 snp->snp_target = NULL; 437 438 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 439 lwkt_reltoken(&snp_token); 440 return (0); 441 } 442 443 /* 444 * NOTE: Must be called with snp_token held 445 */ 446 static int 447 snp_detach(struct snoop *snp) 448 { 449 struct tty *tp; 450 451 ASSERT_LWKT_TOKEN_HELD(&snp_token); 452 snp->snp_base = 0; 453 snp->snp_len = 0; 454 455 /* 456 * If line disc. changed we do not touch this pointer, SLIP/PPP will 457 * change it anyway. 458 */ 459 tp = snp->snp_tty; 460 if (tp == NULL) 461 goto detach_notty; 462 463 lwkt_gettoken(&tp->t_token); 464 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 465 tp->t_line == snooplinedisc) { 466 tp->t_sc = NULL; 467 tp->t_state &= ~TS_SNOOP; 468 tp->t_line = snp->snp_olddisc; 469 } else { 470 kprintf("Snoop: bad attached tty data.\n"); 471 } 472 lwkt_reltoken(&tp->t_token); 473 474 snp->snp_tty = NULL; 475 snp->snp_target = NULL; 476 477 detach_notty: 478 KNOTE(&snp->snp_kq.ki_note, 0); 479 if ((snp->snp_flags & SNOOP_OPEN) == 0) 480 kfree(snp, M_SNP); 481 482 return (0); 483 } 484 485 static int 486 snpclose(struct dev_close_args *ap) 487 { 488 cdev_t dev = ap->a_head.a_dev; 489 struct snoop *snp; 490 int ret; 491 492 lwkt_gettoken(&snp_token); 493 snp = dev->si_drv1; 494 snp->snp_blen = 0; 495 LIST_REMOVE(snp, snp_list); 496 kfree(snp->snp_buf, M_SNP); 497 snp->snp_flags &= ~SNOOP_OPEN; 498 dev->si_drv1 = NULL; 499 if (dev->si_uminor >= SNP_PREALLOCATED_UNITS) { 500 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), dev->si_uminor); 501 destroy_dev(dev); 502 } 503 ret = snp_detach(snp); 504 lwkt_reltoken(&snp_token); 505 return ret; 506 } 507 508 /* 509 * NOTE: Must be called with snp_token held 510 */ 511 static int 512 snp_down(struct snoop *snp) 513 { 514 ASSERT_LWKT_TOKEN_HELD(&snp_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(&snp_token); 535 snp = dev->si_drv1; 536 537 switch (ap->a_cmd) { 538 case SNPSTTY: 539 tdev = udev2dev(*((udev_t *)ap->a_data), 0); 540 if (tdev == NULL) { 541 lwkt_reltoken(&snp_token); 542 ret = snp_down(snp); 543 return ret; 544 } 545 546 tp = snpdevtotty(tdev); 547 if (!tp) { 548 lwkt_reltoken(&snp_token); 549 return (EINVAL); 550 } 551 lwkt_gettoken(&tp->t_token); 552 if (tp->t_state & TS_SNOOP) { 553 lwkt_reltoken(&tp->t_token); 554 lwkt_reltoken(&snp_token); 555 return (EBUSY); 556 } 557 558 if (snp->snp_target == NULL) { 559 tpo = snp->snp_tty; 560 if (tpo) 561 tpo->t_state &= ~TS_SNOOP; 562 } 563 564 tp->t_sc = (caddr_t)snp; 565 tp->t_state |= TS_SNOOP; 566 snp->snp_olddisc = tp->t_line; 567 tp->t_line = snooplinedisc; 568 snp->snp_tty = tp; 569 snp->snp_target = tdev; 570 571 /* 572 * Clean overflow and down flags - 573 * we'll have a chance to get them in the future :))) 574 */ 575 snp->snp_flags &= ~SNOOP_OFLOW; 576 snp->snp_flags &= ~SNOOP_DOWN; 577 lwkt_reltoken(&tp->t_token); 578 break; 579 580 case SNPGTTY: 581 /* 582 * We keep snp_target field specially to make 583 * SNPGTTY happy, else we can't know what is device 584 * major/minor for tty. 585 */ 586 *((cdev_t *)ap->a_data) = snp->snp_target; 587 break; 588 589 case FIOASYNC: 590 if (*(int *)ap->a_data) 591 snp->snp_flags |= SNOOP_ASYNC; 592 else 593 snp->snp_flags &= ~SNOOP_ASYNC; 594 break; 595 596 case FIONREAD: 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 } 609 break; 610 611 default: 612 lwkt_reltoken(&snp_token); 613 return (ENOTTY); 614 } 615 lwkt_reltoken(&snp_token); 616 617 return (0); 618 } 619 620 static struct filterops snpfiltops_rd = 621 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_rd }; 622 static struct filterops snpfiltops_wr = 623 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_wr }; 624 625 static int 626 snpkqfilter(struct dev_kqfilter_args *ap) 627 { 628 cdev_t dev = ap->a_head.a_dev; 629 struct snoop *snp = dev->si_drv1; 630 struct knote *kn = ap->a_kn; 631 struct klist *klist; 632 struct tty *tp = snp->snp_tty; 633 634 lwkt_gettoken(&tp->t_token); 635 ap->a_result = 0; 636 637 switch (kn->kn_filter) { 638 case EVFILT_READ: 639 kn->kn_fop = &snpfiltops_rd; 640 kn->kn_hook = (caddr_t)snp; 641 break; 642 case EVFILT_WRITE: 643 kn->kn_fop = &snpfiltops_wr; 644 kn->kn_hook = (caddr_t)snp; 645 break; 646 default: 647 ap->a_result = EOPNOTSUPP; 648 lwkt_reltoken(&tp->t_token); 649 return (0); 650 } 651 652 klist = &snp->snp_kq.ki_note; 653 knote_insert(klist, kn); 654 lwkt_reltoken(&tp->t_token); 655 656 return (0); 657 } 658 659 static void 660 snpfilter_detach(struct knote *kn) 661 { 662 struct snoop *snp = (struct snoop *)kn->kn_hook; 663 struct klist *klist; 664 665 klist = &snp->snp_kq.ki_note; 666 knote_remove(klist, kn); 667 } 668 669 static int 670 snpfilter_rd(struct knote *kn, long hint) 671 { 672 struct snoop *snp = (struct snoop *)kn->kn_hook; 673 struct tty *tp = snp->snp_tty; 674 int ready = 0; 675 676 lwkt_gettoken(&tp->t_token); 677 /* 678 * If snoop is down, we don't want to poll forever so we return 1. 679 * Caller should see if we down via FIONREAD ioctl(). The last should 680 * return -1 to indicate down state. 681 */ 682 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 683 ready = 1; 684 lwkt_reltoken(&tp->t_token); 685 686 return (ready); 687 } 688 689 static int 690 snpfilter_wr(struct knote *kn, long hint) 691 { 692 /* Writing is always OK */ 693 return (1); 694 } 695 696 static int 697 snpclone(struct dev_clone_args *ap) 698 { 699 int unit; 700 701 lwkt_gettoken(&snp_token); 702 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0); 703 ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600, 704 "snp%d", unit); 705 lwkt_reltoken(&snp_token); 706 707 return 0; 708 } 709 710 static int 711 snp_modevent(module_t mod, int type, void *data) 712 { 713 int i; 714 715 lwkt_gettoken(&snp_token); 716 717 switch (type) { 718 case MOD_LOAD: 719 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 720 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp), 721 snpclone, UID_ROOT, GID_WHEEL, 0600, "snp"); 722 723 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) { 724 make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, "snp%d", i); 725 devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i); 726 } 727 break; 728 case MOD_UNLOAD: 729 if (!LIST_EMPTY(&snp_sclist)) { 730 lwkt_reltoken(&snp_token); 731 return (EBUSY); 732 } 733 ldisc_deregister(snooplinedisc); 734 devfs_clone_handler_del("snp"); 735 dev_ops_remove_all(&snp_ops); 736 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp)); 737 break; 738 default: 739 break; 740 } 741 lwkt_reltoken(&snp_token); 742 743 return (0); 744 } 745 746 static moduledata_t snp_mod = { 747 "snp", 748 snp_modevent, 749 NULL 750 }; 751 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 752