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