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