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