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$ 16 */ 17 18 #include "snp.h" 19 20 #if NSNP > 0 21 22 #include "opt_compat.h" 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/filio.h> 26 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 27 #include <sys/ioctl_compat.h> 28 #endif 29 #include <sys/proc.h> 30 #include <sys/malloc.h> 31 #include <sys/tty.h> 32 #include <sys/conf.h> 33 #include <sys/poll.h> 34 #include <sys/kernel.h> 35 #include <sys/snoop.h> 36 #include <sys/vnode.h> 37 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_poll_t snppoll; 44 45 #define CDEV_MAJOR 53 46 static struct cdevsw snp_cdevsw = { 47 /* open */ snpopen, 48 /* close */ snpclose, 49 /* read */ snpread, 50 /* write */ snpwrite, 51 /* ioctl */ snpioctl, 52 /* stop */ nostop, 53 /* reset */ noreset, 54 /* devtotty */ nodevtotty, 55 /* poll */ snppoll, 56 /* mmap */ nommap, 57 /* strategy */ nostrategy, 58 /* name */ "snp", 59 /* parms */ noparms, 60 /* maj */ CDEV_MAJOR, 61 /* dump */ nodump, 62 /* psize */ nopsize, 63 /* flags */ 0, 64 /* maxio */ 0, 65 /* bmaj */ -1 66 }; 67 68 69 #ifndef MIN 70 #define MIN(a,b) (((a)<(b))?(a):(b)) 71 #endif 72 73 static struct snoop snoopsw[NSNP]; 74 75 static struct tty *snpdevtotty __P((dev_t dev)); 76 static int snp_detach __P((struct snoop *snp)); 77 78 static struct tty * 79 snpdevtotty (dev) 80 dev_t dev; 81 { 82 struct cdevsw *cdp; 83 84 cdp = devsw(dev); 85 if (cdp == NULL) 86 return (NULL); 87 return ((*cdp->d_devtotty)(dev)); 88 } 89 90 #define SNP_INPUT_BUF 5 /* This is even too much,the maximal 91 * interactive mode write is 3 bytes 92 * length for function keys... 93 */ 94 95 static int 96 snpwrite(dev, uio, flag) 97 dev_t dev; 98 struct uio *uio; 99 int flag; 100 { 101 int unit = minor(dev), len, i, error; 102 struct snoop *snp = &snoopsw[unit]; 103 struct tty *tp; 104 char c[SNP_INPUT_BUF]; 105 106 if (snp->snp_tty == NULL) 107 return (EIO); 108 109 tp = snp->snp_tty; 110 111 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 112 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) 113 goto tty_input; 114 115 printf("Snoop: attempt to write to bad tty.\n"); 116 return (EIO); 117 118 tty_input: 119 if (!(tp->t_state & TS_ISOPEN)) 120 return (EIO); 121 122 while (uio->uio_resid > 0) { 123 len = MIN(uio->uio_resid,SNP_INPUT_BUF); 124 if ((error = uiomove(c, len, uio)) != 0) 125 return (error); 126 for (i=0;i<len;i++) { 127 if (ttyinput(c[i] , tp)) 128 return (EIO); 129 } 130 } 131 return 0; 132 133 } 134 135 136 static int 137 snpread(dev, uio, flag) 138 dev_t dev; 139 struct uio *uio; 140 int flag; 141 { 142 int unit = minor(dev), s; 143 struct snoop *snp = &snoopsw[unit]; 144 int len, n, nblen, error = 0; 145 caddr_t from; 146 char *nbuf; 147 148 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 149 ("snoop buffer error")); 150 151 if (snp->snp_tty == NULL) 152 return (EIO); 153 154 snp->snp_flags &= ~SNOOP_RWAIT; 155 156 do { 157 if (snp->snp_len == 0) { 158 if (flag & IO_NDELAY) 159 return (EWOULDBLOCK); 160 snp->snp_flags |= SNOOP_RWAIT; 161 tsleep((caddr_t) snp, (PZERO + 1) | PCATCH, "snoopread", 0); 162 } 163 } while (snp->snp_len == 0); 164 165 n = snp->snp_len; 166 167 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 168 len = MIN(uio->uio_resid, snp->snp_len); 169 from = (caddr_t) (snp->snp_buf + snp->snp_base); 170 if (len == 0) 171 break; 172 173 error = uiomove(from, len, uio); 174 snp->snp_base += len; 175 snp->snp_len -= len; 176 } 177 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 178 snp->snp_flags &= ~SNOOP_OFLOW; 179 } 180 s = spltty(); 181 nblen = snp->snp_blen; 182 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 183 while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN)) 184 nblen = nblen / 2; 185 if ((nbuf = malloc(nblen, M_TTYS, M_NOWAIT)) != NULL) { 186 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 187 free(snp->snp_buf, M_TTYS); 188 snp->snp_buf = nbuf; 189 snp->snp_blen = nblen; 190 snp->snp_base = 0; 191 } 192 } 193 splx(s); 194 195 return error; 196 } 197 198 int 199 snpinc(struct snoop *snp, char c) 200 { 201 char buf[1]; 202 203 buf[0]=c; 204 return (snpin(snp,buf,1)); 205 } 206 207 208 int 209 snpin(snp, buf, n) 210 struct snoop *snp; 211 char *buf; 212 int n; 213 { 214 int s_free, s_tail; 215 int s, len, nblen; 216 caddr_t from, to; 217 char *nbuf; 218 219 KASSERT(n >= 0, ("negative snoop char count")); 220 221 if (n == 0) 222 return 0; 223 224 #ifdef DIAGNOSTIC 225 if (!(snp->snp_flags & SNOOP_OPEN)) { 226 printf("Snoop: data coming to closed device.\n"); 227 return 0; 228 } 229 #endif 230 if (snp->snp_flags & SNOOP_DOWN) { 231 printf("Snoop: more data to down interface.\n"); 232 return 0; 233 } 234 235 if (snp->snp_flags & SNOOP_OFLOW) { 236 printf("Snoop: buffer overflow.\n"); 237 /* 238 * On overflow we just repeat the standart close 239 * procedure...yes , this is waste of space but.. Then next 240 * read from device will fail if one would recall he is 241 * snooping and retry... 242 */ 243 244 return (snpdown(snp)); 245 } 246 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 247 s_free = snp->snp_blen - snp->snp_len; 248 249 250 if (n > s_free) { 251 s = spltty(); 252 nblen = snp->snp_blen; 253 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 254 nblen = snp->snp_blen * 2; 255 s_free = nblen - (snp->snp_len + snp->snp_base); 256 } 257 if ((n <= s_free) && (nbuf = malloc(nblen, M_TTYS, M_NOWAIT))) { 258 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 259 free(snp->snp_buf, M_TTYS); 260 snp->snp_buf = nbuf; 261 snp->snp_blen = nblen; 262 snp->snp_base = 0; 263 } else { 264 snp->snp_flags |= SNOOP_OFLOW; 265 if (snp->snp_flags & SNOOP_RWAIT) { 266 snp->snp_flags &= ~SNOOP_RWAIT; 267 wakeup((caddr_t) snp); 268 } 269 splx(s); 270 return 0; 271 } 272 splx(s); 273 } 274 if (n > s_tail) { 275 from = (caddr_t) (snp->snp_buf + snp->snp_base); 276 to = (caddr_t) (snp->snp_buf); 277 len = snp->snp_len; 278 bcopy(from, to, len); 279 snp->snp_base = 0; 280 } 281 to = (caddr_t) (snp->snp_buf + snp->snp_base + snp->snp_len); 282 bcopy(buf, to, n); 283 snp->snp_len += n; 284 285 if (snp->snp_flags & SNOOP_RWAIT) { 286 snp->snp_flags &= ~SNOOP_RWAIT; 287 wakeup((caddr_t) snp); 288 } 289 selwakeup(&snp->snp_sel); 290 snp->snp_sel.si_pid = 0; 291 292 return n; 293 } 294 295 static int 296 snpopen(dev, flag, mode, p) 297 dev_t dev; 298 int flag, mode; 299 struct proc *p; 300 { 301 struct snoop *snp; 302 register int unit, error; 303 304 if ((error = suser(p)) != 0) 305 return (error); 306 307 if ((unit = minor(dev)) >= NSNP) 308 return (ENXIO); 309 310 snp = &snoopsw[unit]; 311 312 if (snp->snp_flags & SNOOP_OPEN) 313 return (ENXIO); 314 315 /* 316 * We intentionally do not OR flags with SNOOP_OPEN,but set them so 317 * all previous settings (especially SNOOP_OFLOW) will be cleared. 318 */ 319 snp->snp_flags = SNOOP_OPEN; 320 321 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK); 322 snp->snp_blen = SNOOP_MINLEN; 323 snp->snp_base = 0; 324 snp->snp_len = 0; 325 326 /* 327 * snp_tty == NULL is for inactive snoop devices. 328 */ 329 snp->snp_tty = NULL; 330 snp->snp_target = NODEV; 331 return (0); 332 } 333 334 335 static int 336 snp_detach(snp) 337 struct snoop *snp; 338 { 339 struct tty *tp; 340 341 snp->snp_base = 0; 342 snp->snp_len = 0; 343 344 /* 345 * If line disc. changed we do not touch this pointer,SLIP/PPP will 346 * change it anyway. 347 */ 348 349 if (snp->snp_tty == NULL) 350 goto detach_notty; 351 352 tp = snp->snp_tty; 353 354 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 355 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) { 356 tp->t_sc = NULL; 357 tp->t_state &= ~TS_SNOOP; 358 } else 359 printf("Snoop: bad attached tty data.\n"); 360 361 snp->snp_tty = NULL; 362 snp->snp_target = NODEV; 363 364 detach_notty: 365 selwakeup(&snp->snp_sel); 366 snp->snp_sel.si_pid = 0; 367 368 return (0); 369 } 370 371 static int 372 snpclose(dev, flags, fmt, p) 373 dev_t dev; 374 int flags; 375 int fmt; 376 struct proc *p; 377 { 378 register int unit = minor(dev); 379 struct snoop *snp = &snoopsw[unit]; 380 381 snp->snp_blen = 0; 382 free(snp->snp_buf, M_TTYS); 383 snp->snp_flags &= ~SNOOP_OPEN; 384 385 return (snp_detach(snp)); 386 } 387 388 int 389 snpdown(snp) 390 struct snoop *snp; 391 { 392 snp->snp_blen = SNOOP_MINLEN; 393 free(snp->snp_buf, M_TTYS); 394 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK); 395 snp->snp_flags |= SNOOP_DOWN; 396 397 return (snp_detach(snp)); 398 } 399 400 401 static int 402 snpioctl(dev, cmd, data, flags, p) 403 dev_t dev; 404 u_long cmd; 405 caddr_t data; 406 int flags; 407 struct proc *p; 408 { 409 int unit = minor(dev), s; 410 dev_t tdev; 411 struct snoop *snp = &snoopsw[unit]; 412 struct tty *tp, *tpo; 413 414 switch (cmd) { 415 case SNPSTTY: 416 tdev = udev2dev(*((udev_t *) data), 0); 417 if (tdev == NODEV) 418 return (snpdown(snp)); 419 420 tp = snpdevtotty(tdev); 421 if (!tp) 422 return (EINVAL); 423 424 if ((tp->t_sc != (caddr_t) snp) && (tp->t_state & TS_SNOOP)) 425 return (EBUSY); 426 427 if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC)) 428 return (EBUSY); 429 430 s = spltty(); 431 432 if (snp->snp_target == NODEV) { 433 tpo = snp->snp_tty; 434 if (tpo) 435 tpo->t_state &= ~TS_SNOOP; 436 } 437 438 tp->t_sc = (caddr_t) snp; 439 tp->t_state |= TS_SNOOP; 440 snp->snp_tty = tp; 441 snp->snp_target = tdev; 442 443 /* 444 * Clean overflow and down flags - 445 * we'll have a chance to get them in the future :))) 446 */ 447 snp->snp_flags &= ~SNOOP_OFLOW; 448 snp->snp_flags &= ~SNOOP_DOWN; 449 splx(s); 450 break; 451 452 case SNPGTTY: 453 /* 454 * We keep snp_target field specially to make 455 * SNPGTTY happy,else we can't know what is device 456 * major/minor for tty. 457 */ 458 *((dev_t *) data) = snp->snp_target; 459 break; 460 461 case FIONBIO: 462 break; 463 464 case FIOASYNC: 465 if (*(int *) data) 466 snp->snp_flags |= SNOOP_ASYNC; 467 else 468 snp->snp_flags &= ~SNOOP_ASYNC; 469 break; 470 471 case FIONREAD: 472 s = spltty(); 473 if (snp->snp_tty != NULL) 474 *(int *) data = snp->snp_len; 475 else 476 if (snp->snp_flags & SNOOP_DOWN) { 477 if (snp->snp_flags & SNOOP_OFLOW) 478 *(int *) data = SNP_OFLOW; 479 else 480 *(int *) data = SNP_TTYCLOSE; 481 } else { 482 *(int *) data = SNP_DETACH; 483 } 484 splx(s); 485 break; 486 487 default: 488 return (ENOTTY); 489 } 490 return (0); 491 } 492 493 494 static int 495 snppoll(dev, events, p) 496 dev_t dev; 497 int events; 498 struct proc *p; 499 { 500 int unit = minor(dev); 501 struct snoop *snp = &snoopsw[unit]; 502 int revents = 0; 503 504 505 /* 506 * If snoop is down,we don't want to poll() forever so we return 1. 507 * Caller should see if we down via FIONREAD ioctl().The last should 508 * return -1 to indicate down state. 509 */ 510 if (events & (POLLIN | POLLRDNORM)) { 511 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 512 revents |= events & (POLLIN | POLLRDNORM); 513 else 514 selrecord(p, &snp->snp_sel); 515 } 516 return (revents); 517 } 518 519 static void snp_drvinit __P((void *unused)); 520 521 static void 522 snp_drvinit(unused) 523 void *unused; 524 { 525 int i; 526 527 cdevsw_add(&snp_cdevsw); 528 for ( i = 0 ; i < NSNP ; i++) 529 make_dev(&snp_cdevsw, i, 0, 0, 0600, "snp%d", i); 530 } 531 532 SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL) 533 534 535 #endif 536