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/uio.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_DEFINE_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 static struct dev_ops snp_ops = { 59 { "snp", 0, 0 }, 60 .d_open = snpopen, 61 .d_close = snpclose, 62 .d_read = snpread, 63 .d_write = snpwrite, 64 .d_ioctl = snpioctl, 65 .d_kqfilter = snpkqfilter 66 }; 67 68 static struct linesw snpdisc = { 69 ttyopen, snplclose, ttread, snplwrite, 70 l_nullioctl, ttyinput, ttstart, ttymodem 71 }; 72 73 /* 74 * This is the main snoop per-device structure. 75 */ 76 struct snoop { 77 LIST_ENTRY(snoop) snp_list; /* List glue. */ 78 cdev_t snp_target; /* Target tty device. */ 79 struct tty *snp_tty; /* Target tty pointer. */ 80 u_long snp_len; /* Possible length. */ 81 u_long snp_base; /* Data base. */ 82 u_long snp_blen; /* Used length. */ 83 caddr_t snp_buf; /* Allocation pointer. */ 84 int snp_flags; /* Flags. */ 85 struct kqinfo snp_kq; /* Kqueue info. */ 86 int snp_olddisc; /* Old line discipline. */ 87 }; 88 89 /* 90 * Possible flags. 91 */ 92 #define SNOOP_ASYNC 0x0002 93 #define SNOOP_OPEN 0x0004 94 #define SNOOP_RWAIT 0x0008 95 #define SNOOP_OFLOW 0x0010 96 #define SNOOP_DOWN 0x0020 97 98 /* 99 * Other constants. 100 */ 101 #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 102 * 4K tested to be the minimum 103 * for which on normal tty 104 * usage there is no need to 105 * allocate more. 106 */ 107 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 108 * If we grow more,something 109 * really bad in this world.. 110 */ 111 112 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 113 /* 114 * The number of the "snoop" line discipline. This gets determined at 115 * module load time. 116 */ 117 static int snooplinedisc; 118 119 120 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 121 static struct lwkt_token snp_token = LWKT_TOKEN_INITIALIZER(snp_token); 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(&snp_token); 136 snp = tp->t_sc; 137 error = snp_down(snp); 138 if (error != 0) { 139 lwkt_reltoken(&snp_token); 140 return (error); 141 } 142 lwkt_gettoken(&tp->t_token); 143 error = ttylclose(tp, flag); 144 lwkt_reltoken(&tp->t_token); 145 lwkt_reltoken(&snp_token); 146 147 return (error); 148 } 149 150 static int 151 snplwrite(struct tty *tp, struct uio *uio, int flag) 152 { 153 struct iovec iov; 154 struct uio uio2; 155 struct snoop *snp; 156 int error, ilen; 157 char *ibuf; 158 159 lwkt_gettoken(&tp->t_token); 160 error = 0; 161 ibuf = NULL; 162 snp = tp->t_sc; 163 while (uio->uio_resid > 0) { 164 ilen = (int)szmin(512, uio->uio_resid); 165 ibuf = kmalloc(ilen, M_SNP, M_WAITOK); 166 error = uiomove(ibuf, (size_t)ilen, uio); 167 if (error != 0) 168 break; 169 snp_in(snp, ibuf, ilen); 170 /* Hackish, but probably the least of all evils. */ 171 iov.iov_base = ibuf; 172 iov.iov_len = ilen; 173 uio2.uio_iov = &iov; 174 uio2.uio_iovcnt = 1; 175 uio2.uio_offset = 0; 176 uio2.uio_resid = ilen; 177 uio2.uio_segflg = UIO_SYSSPACE; 178 uio2.uio_rw = UIO_WRITE; 179 uio2.uio_td = uio->uio_td; 180 error = ttwrite(tp, &uio2, flag); 181 if (error != 0) 182 break; 183 kfree(ibuf, M_SNP); 184 ibuf = NULL; 185 } 186 if (ibuf != NULL) 187 kfree(ibuf, M_SNP); 188 lwkt_reltoken(&tp->t_token); 189 return (error); 190 } 191 192 static struct tty * 193 snpdevtotty(cdev_t dev) 194 { 195 if ((dev_dflags(dev) & D_TTY) == 0) 196 return (NULL); 197 return (dev->si_tty); 198 } 199 200 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 201 * interactive mode write is 3 bytes 202 * length for function keys... 203 */ 204 205 static int 206 snpwrite(struct dev_write_args *ap) 207 { 208 cdev_t dev = ap->a_head.a_dev; 209 struct uio *uio = ap->a_uio; 210 struct snoop *snp; 211 struct tty *tp; 212 int error, i, len; 213 unsigned char c[SNP_INPUT_BUF]; 214 215 snp = dev->si_drv1; 216 tp = snp->snp_tty; 217 if (tp == NULL) 218 return (EIO); 219 lwkt_gettoken(&tp->t_token); 220 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 221 tp->t_line == snooplinedisc) 222 goto tty_input; 223 224 kprintf("Snoop: attempt to write to bad tty.\n"); 225 lwkt_reltoken(&tp->t_token); 226 return (EIO); 227 228 tty_input: 229 if (!(tp->t_state & TS_ISOPEN)) { 230 lwkt_reltoken(&tp->t_token); 231 return (EIO); 232 } 233 234 while (uio->uio_resid > 0) { 235 len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF); 236 if ((error = uiomove(c, (size_t)len, uio)) != 0) { 237 lwkt_reltoken(&tp->t_token); 238 return (error); 239 } 240 for (i=0; i < len; i++) { 241 if (ttyinput(c[i], tp)) { 242 lwkt_reltoken(&tp->t_token); 243 return (EIO); 244 } 245 } 246 } 247 lwkt_reltoken(&tp->t_token); 248 return (0); 249 } 250 251 252 static int 253 snpread(struct dev_read_args *ap) 254 { 255 cdev_t dev = ap->a_head.a_dev; 256 struct uio *uio = ap->a_uio; 257 struct snoop *snp; 258 struct tty *tp; 259 int error, len, n, nblen; 260 caddr_t from; 261 char *nbuf; 262 263 snp = dev->si_drv1; 264 tp = snp->snp_tty; 265 lwkt_gettoken(&tp->t_token); 266 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 267 ("snoop buffer error")); 268 269 if (snp->snp_tty == NULL) { 270 lwkt_reltoken(&tp->t_token); 271 return (EIO); 272 } 273 274 snp->snp_flags &= ~SNOOP_RWAIT; 275 276 do { 277 if (snp->snp_len == 0) { 278 if (ap->a_ioflag & IO_NDELAY) { 279 lwkt_reltoken(&tp->t_token); 280 return (EWOULDBLOCK); 281 } 282 snp->snp_flags |= SNOOP_RWAIT; 283 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0); 284 if (error != 0) { 285 lwkt_reltoken(&tp->t_token); 286 return (error); 287 } 288 } 289 } while (snp->snp_len == 0); 290 291 n = snp->snp_len; 292 293 error = 0; 294 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 295 len = (int)szmin(uio->uio_resid, snp->snp_len); 296 from = (caddr_t)(snp->snp_buf + snp->snp_base); 297 if (len == 0) 298 break; 299 300 error = uiomove(from, (size_t)len, uio); 301 snp->snp_base += len; 302 snp->snp_len -= len; 303 } 304 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 305 snp->snp_flags &= ~SNOOP_OFLOW; 306 } 307 nblen = snp->snp_blen; 308 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 309 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 310 nblen = nblen / 2; 311 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 312 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 313 kfree(snp->snp_buf, M_SNP); 314 snp->snp_buf = nbuf; 315 snp->snp_blen = nblen; 316 snp->snp_base = 0; 317 } 318 } 319 lwkt_reltoken(&tp->t_token); 320 321 return (error); 322 } 323 324 /* 325 * NOTE: Must be called with tp->t_token held 326 */ 327 static int 328 snp_in(struct snoop *snp, char *buf, int n) 329 { 330 int s_free, s_tail; 331 int len, nblen; 332 caddr_t from, to; 333 char *nbuf; 334 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 nblen = snp->snp_blen; 362 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 363 nblen = snp->snp_blen * 2; 364 s_free = nblen - (snp->snp_len + snp->snp_base); 365 } 366 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) { 367 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 368 kfree(snp->snp_buf, M_SNP); 369 snp->snp_buf = nbuf; 370 snp->snp_blen = nblen; 371 snp->snp_base = 0; 372 } else { 373 snp->snp_flags |= SNOOP_OFLOW; 374 if (snp->snp_flags & SNOOP_RWAIT) { 375 snp->snp_flags &= ~SNOOP_RWAIT; 376 wakeup((caddr_t)snp); 377 } 378 return (0); 379 } 380 } 381 if (n > s_tail) { 382 from = (caddr_t)(snp->snp_buf + snp->snp_base); 383 to = (caddr_t)(snp->snp_buf); 384 len = snp->snp_len; 385 bcopy(from, to, len); 386 snp->snp_base = 0; 387 } 388 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 389 bcopy(buf, to, n); 390 snp->snp_len += n; 391 392 if (snp->snp_flags & SNOOP_RWAIT) { 393 snp->snp_flags &= ~SNOOP_RWAIT; 394 wakeup((caddr_t)snp); 395 } 396 KNOTE(&snp->snp_kq.ki_note, 0); 397 398 return (n); 399 } 400 401 static int 402 snpopen(struct dev_open_args *ap) 403 { 404 cdev_t dev = ap->a_head.a_dev; 405 struct snoop *snp; 406 407 lwkt_gettoken(&snp_token); 408 if (dev->si_drv1 == NULL) { 409 #if 0 410 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL, 411 0600, "snp%d", minor(dev)); 412 #endif 413 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP, 414 M_WAITOK | M_ZERO); 415 } else { 416 lwkt_reltoken(&snp_token); 417 return (EBUSY); 418 } 419 420 /* 421 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 422 * all previous settings (especially SNOOP_OFLOW) will be cleared. 423 */ 424 snp->snp_flags = SNOOP_OPEN; 425 426 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 427 snp->snp_blen = SNOOP_MINLEN; 428 snp->snp_base = 0; 429 snp->snp_len = 0; 430 431 /* 432 * snp_tty == NULL is for inactive snoop devices. 433 */ 434 snp->snp_tty = NULL; 435 snp->snp_target = NULL; 436 437 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 438 lwkt_reltoken(&snp_token); 439 return (0); 440 } 441 442 /* 443 * NOTE: Must be called with snp_token held 444 */ 445 static int 446 snp_detach(struct snoop *snp) 447 { 448 struct tty *tp; 449 450 ASSERT_LWKT_TOKEN_HELD(&snp_token); 451 snp->snp_base = 0; 452 snp->snp_len = 0; 453 454 /* 455 * If line disc. changed we do not touch this pointer, SLIP/PPP will 456 * change it anyway. 457 */ 458 tp = snp->snp_tty; 459 if (tp == NULL) 460 goto detach_notty; 461 462 lwkt_gettoken(&tp->t_token); 463 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 464 tp->t_line == snooplinedisc) { 465 tp->t_sc = NULL; 466 tp->t_state &= ~TS_SNOOP; 467 tp->t_line = snp->snp_olddisc; 468 } else { 469 kprintf("Snoop: bad attached tty data.\n"); 470 } 471 lwkt_reltoken(&tp->t_token); 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 unit; 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 unit = dev->si_uminor; 500 if (unit >= SNP_PREALLOCATED_UNITS) { 501 destroy_dev(dev); 502 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), unit); 503 } 504 ret = snp_detach(snp); 505 lwkt_reltoken(&snp_token); 506 507 return ret; 508 } 509 510 /* 511 * NOTE: Must be called with snp_token held 512 */ 513 static int 514 snp_down(struct snoop *snp) 515 { 516 ASSERT_LWKT_TOKEN_HELD(&snp_token); 517 if (snp->snp_blen != SNOOP_MINLEN) { 518 kfree(snp->snp_buf, M_SNP); 519 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 520 snp->snp_blen = SNOOP_MINLEN; 521 } 522 snp->snp_flags |= SNOOP_DOWN; 523 524 return (snp_detach(snp)); 525 } 526 527 static int 528 snpioctl(struct dev_ioctl_args *ap) 529 { 530 cdev_t dev = ap->a_head.a_dev; 531 struct snoop *snp; 532 struct tty *tp, *tpo; 533 cdev_t tdev; 534 int ret; 535 536 lwkt_gettoken(&snp_token); 537 snp = dev->si_drv1; 538 539 switch (ap->a_cmd) { 540 case SNPSTTY: 541 tdev = dev_from_devid(*((dev_t *)ap->a_data), 0); 542 if (tdev == NULL) { 543 lwkt_reltoken(&snp_token); 544 ret = snp_down(snp); 545 return ret; 546 } 547 548 tp = snpdevtotty(tdev); 549 if (!tp) { 550 lwkt_reltoken(&snp_token); 551 return (EINVAL); 552 } 553 lwkt_gettoken(&tp->t_token); 554 if (tp->t_state & TS_SNOOP) { 555 lwkt_reltoken(&tp->t_token); 556 lwkt_reltoken(&snp_token); 557 return (EBUSY); 558 } 559 560 if (snp->snp_target == NULL) { 561 tpo = snp->snp_tty; 562 if (tpo) 563 tpo->t_state &= ~TS_SNOOP; 564 } 565 566 tp->t_sc = (caddr_t)snp; 567 tp->t_state |= TS_SNOOP; 568 snp->snp_olddisc = tp->t_line; 569 tp->t_line = snooplinedisc; 570 snp->snp_tty = tp; 571 snp->snp_target = tdev; 572 573 /* 574 * Clean overflow and down flags - 575 * we'll have a chance to get them in the future :))) 576 */ 577 snp->snp_flags &= ~SNOOP_OFLOW; 578 snp->snp_flags &= ~SNOOP_DOWN; 579 lwkt_reltoken(&tp->t_token); 580 break; 581 582 case SNPGTTY: 583 /* 584 * We keep snp_target field specially to make 585 * SNPGTTY happy, else we can't know what is device 586 * major/minor for tty. 587 */ 588 *((cdev_t *)ap->a_data) = snp->snp_target; 589 break; 590 591 case FIOASYNC: 592 if (*(int *)ap->a_data) 593 snp->snp_flags |= SNOOP_ASYNC; 594 else 595 snp->snp_flags &= ~SNOOP_ASYNC; 596 break; 597 598 case FIONREAD: 599 if (snp->snp_tty != NULL) { 600 *(int *)ap->a_data = snp->snp_len; 601 } else { 602 if (snp->snp_flags & SNOOP_DOWN) { 603 if (snp->snp_flags & SNOOP_OFLOW) 604 *(int *)ap->a_data = SNP_OFLOW; 605 else 606 *(int *)ap->a_data = SNP_TTYCLOSE; 607 } else { 608 *(int *)ap->a_data = SNP_DETACH; 609 } 610 } 611 break; 612 613 default: 614 lwkt_reltoken(&snp_token); 615 return (ENOTTY); 616 } 617 lwkt_reltoken(&snp_token); 618 619 return (0); 620 } 621 622 static struct filterops snpfiltops_rd = 623 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_rd }; 624 static struct filterops snpfiltops_wr = 625 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_wr }; 626 627 static int 628 snpkqfilter(struct dev_kqfilter_args *ap) 629 { 630 cdev_t dev = ap->a_head.a_dev; 631 struct snoop *snp = dev->si_drv1; 632 struct knote *kn = ap->a_kn; 633 struct klist *klist; 634 struct tty *tp = snp->snp_tty; 635 636 lwkt_gettoken(&tp->t_token); 637 ap->a_result = 0; 638 639 switch (kn->kn_filter) { 640 case EVFILT_READ: 641 kn->kn_fop = &snpfiltops_rd; 642 kn->kn_hook = (caddr_t)snp; 643 break; 644 case EVFILT_WRITE: 645 kn->kn_fop = &snpfiltops_wr; 646 kn->kn_hook = (caddr_t)snp; 647 break; 648 default: 649 ap->a_result = EOPNOTSUPP; 650 lwkt_reltoken(&tp->t_token); 651 return (0); 652 } 653 654 klist = &snp->snp_kq.ki_note; 655 knote_insert(klist, kn); 656 lwkt_reltoken(&tp->t_token); 657 658 return (0); 659 } 660 661 static void 662 snpfilter_detach(struct knote *kn) 663 { 664 struct snoop *snp = (struct snoop *)kn->kn_hook; 665 struct klist *klist; 666 667 klist = &snp->snp_kq.ki_note; 668 knote_remove(klist, kn); 669 } 670 671 static int 672 snpfilter_rd(struct knote *kn, long hint) 673 { 674 struct snoop *snp = (struct snoop *)kn->kn_hook; 675 struct tty *tp = snp->snp_tty; 676 int ready = 0; 677 678 lwkt_gettoken(&tp->t_token); 679 /* 680 * If snoop is down, we don't want to poll forever so we return 1. 681 * Caller should see if we down via FIONREAD ioctl(). The last should 682 * return -1 to indicate down state. 683 */ 684 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 685 ready = 1; 686 lwkt_reltoken(&tp->t_token); 687 688 return (ready); 689 } 690 691 static int 692 snpfilter_wr(struct knote *kn, long hint) 693 { 694 /* Writing is always OK */ 695 return (1); 696 } 697 698 static int 699 snpclone(struct dev_clone_args *ap) 700 { 701 int unit; 702 703 lwkt_gettoken(&snp_token); 704 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0); 705 ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600, 706 "snp%d", unit); 707 lwkt_reltoken(&snp_token); 708 709 return 0; 710 } 711 712 static int 713 snp_modevent(module_t mod, int type, void *data) 714 { 715 int i; 716 717 lwkt_gettoken(&snp_token); 718 719 switch (type) { 720 case MOD_LOAD: 721 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 722 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp), 723 snpclone, UID_ROOT, GID_WHEEL, 0600, "snp"); 724 725 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) { 726 make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, 727 "snp%d", i); 728 devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i); 729 } 730 break; 731 case MOD_UNLOAD: 732 if (!LIST_EMPTY(&snp_sclist)) { 733 lwkt_reltoken(&snp_token); 734 return (EBUSY); 735 } 736 ldisc_deregister(snooplinedisc); 737 devfs_clone_handler_del("snp"); 738 dev_ops_remove_all(&snp_ops); 739 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp)); 740 break; 741 default: 742 break; 743 } 744 lwkt_reltoken(&snp_token); 745 746 return (0); 747 } 748 749 static moduledata_t snp_mod = { 750 "snp", 751 snp_modevent, 752 NULL 753 }; 754 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 755