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