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