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 #include "opt_compat.h" 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/filio.h> 23 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 24 #include <sys/ioctl_compat.h> 25 #endif 26 #include <sys/proc.h> 27 #include <sys/malloc.h> 28 #include <sys/tty.h> 29 #include <sys/conf.h> 30 #include <sys/poll.h> 31 #include <sys/kernel.h> 32 #include <sys/snoop.h> 33 #include <sys/vnode.h> 34 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 cdevsw snp_cdevsw = { 44 /* open */ snpopen, 45 /* close */ snpclose, 46 /* read */ snpread, 47 /* write */ snpwrite, 48 /* ioctl */ snpioctl, 49 /* poll */ snppoll, 50 /* mmap */ nommap, 51 /* strategy */ nostrategy, 52 /* name */ "snp", 53 /* maj */ CDEV_MAJOR, 54 /* dump */ nodump, 55 /* psize */ nopsize, 56 /* flags */ 0, 57 /* bmaj */ -1 58 }; 59 60 61 #ifndef MIN 62 #define MIN(a,b) (((a)<(b))?(a):(b)) 63 #endif 64 65 MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 66 67 static struct tty *snpdevtotty __P((dev_t dev)); 68 static int snp_detach __P((struct snoop *snp)); 69 70 static struct tty * 71 snpdevtotty (dev) 72 dev_t dev; 73 { 74 struct cdevsw *cdp; 75 76 cdp = devsw(dev); 77 if (cdp && cdp->d_flags & D_TTY) 78 return (dev->si_tty); 79 return (NULL); 80 } 81 82 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 83 * interactive mode write is 3 bytes 84 * length for function keys... 85 */ 86 87 static int 88 snpwrite(dev, uio, flag) 89 dev_t dev; 90 struct uio *uio; 91 int flag; 92 { 93 int len, i, error; 94 struct snoop *snp = dev->si_drv1; 95 struct tty *tp; 96 char c[SNP_INPUT_BUF]; 97 98 if (snp->snp_tty == NULL) 99 return (EIO); 100 101 tp = snp->snp_tty; 102 103 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 104 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) 105 goto tty_input; 106 107 printf("Snoop: attempt to write to bad tty.\n"); 108 return (EIO); 109 110 tty_input: 111 if (!(tp->t_state & TS_ISOPEN)) 112 return (EIO); 113 114 while (uio->uio_resid > 0) { 115 len = MIN(uio->uio_resid, SNP_INPUT_BUF); 116 if ((error = uiomove(c, len, uio)) != 0) 117 return (error); 118 for (i=0; i < len; i++) { 119 if (ttyinput(c[i], tp)) 120 return (EIO); 121 } 122 } 123 return 0; 124 125 } 126 127 128 static int 129 snpread(dev, uio, flag) 130 dev_t dev; 131 struct uio *uio; 132 int flag; 133 { 134 struct snoop *snp = dev->si_drv1; 135 int len, n, nblen, s, error = 0; 136 caddr_t from; 137 char *nbuf; 138 139 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 140 ("snoop buffer error")); 141 142 if (snp->snp_tty == NULL) 143 return (EIO); 144 145 snp->snp_flags &= ~SNOOP_RWAIT; 146 147 do { 148 if (snp->snp_len == 0) { 149 if (flag & IO_NDELAY) 150 return (EWOULDBLOCK); 151 snp->snp_flags |= SNOOP_RWAIT; 152 tsleep((caddr_t)snp, (PZERO + 1) | PCATCH, "snprd", 0); 153 } 154 } while (snp->snp_len == 0); 155 156 n = snp->snp_len; 157 158 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 159 len = MIN(uio->uio_resid, snp->snp_len); 160 from = (caddr_t)(snp->snp_buf + snp->snp_base); 161 if (len == 0) 162 break; 163 164 error = uiomove(from, len, uio); 165 snp->snp_base += len; 166 snp->snp_len -= len; 167 } 168 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 169 snp->snp_flags &= ~SNOOP_OFLOW; 170 } 171 s = spltty(); 172 nblen = snp->snp_blen; 173 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 174 while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN)) 175 nblen = nblen / 2; 176 if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 177 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 178 free(snp->snp_buf, M_SNP); 179 snp->snp_buf = nbuf; 180 snp->snp_blen = nblen; 181 snp->snp_base = 0; 182 } 183 } 184 splx(s); 185 186 return error; 187 } 188 189 int 190 snpinc(struct snoop *snp, char c) 191 { 192 char buf; 193 194 buf = c; 195 return (snpin(snp, &buf, 1)); 196 } 197 198 199 int 200 snpin(snp, buf, n) 201 struct snoop *snp; 202 char *buf; 203 int n; 204 { 205 int s_free, s_tail; 206 int s, len, nblen; 207 caddr_t from, to; 208 char *nbuf; 209 210 KASSERT(n >= 0, ("negative snoop char count")); 211 212 if (n == 0) 213 return 0; 214 215 if (snp->snp_flags & SNOOP_DOWN) { 216 printf("Snoop: more data to down interface.\n"); 217 return 0; 218 } 219 220 if (snp->snp_flags & SNOOP_OFLOW) { 221 printf("Snoop: buffer overflow.\n"); 222 /* 223 * On overflow we just repeat the standart close 224 * procedure...yes , this is waste of space but.. Then next 225 * read from device will fail if one would recall he is 226 * snooping and retry... 227 */ 228 229 return (snpdown(snp)); 230 } 231 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 232 s_free = snp->snp_blen - snp->snp_len; 233 234 235 if (n > s_free) { 236 s = spltty(); 237 nblen = snp->snp_blen; 238 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 239 nblen = snp->snp_blen * 2; 240 s_free = nblen - (snp->snp_len + snp->snp_base); 241 } 242 if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 243 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 244 free(snp->snp_buf, M_SNP); 245 snp->snp_buf = nbuf; 246 snp->snp_blen = nblen; 247 snp->snp_base = 0; 248 } else { 249 snp->snp_flags |= SNOOP_OFLOW; 250 if (snp->snp_flags & SNOOP_RWAIT) { 251 snp->snp_flags &= ~SNOOP_RWAIT; 252 wakeup((caddr_t)snp); 253 } 254 splx(s); 255 return 0; 256 } 257 splx(s); 258 } 259 if (n > s_tail) { 260 from = (caddr_t)(snp->snp_buf + snp->snp_base); 261 to = (caddr_t)(snp->snp_buf); 262 len = snp->snp_len; 263 bcopy(from, to, len); 264 snp->snp_base = 0; 265 } 266 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 267 bcopy(buf, to, n); 268 snp->snp_len += n; 269 270 if (snp->snp_flags & SNOOP_RWAIT) { 271 snp->snp_flags &= ~SNOOP_RWAIT; 272 wakeup((caddr_t)snp); 273 } 274 selwakeup(&snp->snp_sel); 275 snp->snp_sel.si_pid = 0; 276 277 return n; 278 } 279 280 static int 281 snpopen(dev, flag, mode, p) 282 dev_t dev; 283 int flag, mode; 284 struct proc *p; 285 { 286 struct snoop *snp; 287 int error; 288 289 if ((error = suser(p)) != 0) 290 return (error); 291 292 if (dev->si_drv1 == NULL) { 293 int mynor = minor(dev); 294 295 dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, M_WAITOK); 296 bzero(snp, sizeof(*snp)); 297 make_dev(&snp_cdevsw, mynor, 0, 0, 0600, "snp%d", mynor); 298 } else 299 return (EBUSY); 300 301 /* 302 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 303 * all previous settings (especially SNOOP_OFLOW) will be cleared. 304 */ 305 snp->snp_flags = SNOOP_OPEN; 306 307 snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 308 snp->snp_blen = SNOOP_MINLEN; 309 snp->snp_base = 0; 310 snp->snp_len = 0; 311 312 /* 313 * snp_tty == NULL is for inactive snoop devices. 314 */ 315 snp->snp_tty = NULL; 316 snp->snp_target = NODEV; 317 return (0); 318 } 319 320 321 static int 322 snp_detach(snp) 323 struct snoop *snp; 324 { 325 struct tty *tp; 326 327 snp->snp_base = 0; 328 snp->snp_len = 0; 329 330 /* 331 * If line disc. changed we do not touch this pointer, SLIP/PPP will 332 * change it anyway. 333 */ 334 335 if (snp->snp_tty == NULL) 336 goto detach_notty; 337 338 tp = snp->snp_tty; 339 340 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 341 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) { 342 tp->t_sc = NULL; 343 tp->t_state &= ~TS_SNOOP; 344 } else 345 printf("Snoop: bad attached tty data.\n"); 346 347 snp->snp_tty = NULL; 348 snp->snp_target = NODEV; 349 350 detach_notty: 351 selwakeup(&snp->snp_sel); 352 snp->snp_sel.si_pid = 0; 353 if ((snp->snp_flags & SNOOP_OPEN) == 0) 354 free(snp, M_SNP); 355 356 return (0); 357 } 358 359 static int 360 snpclose(dev, flags, fmt, p) 361 dev_t dev; 362 int flags; 363 int fmt; 364 struct proc *p; 365 { 366 struct snoop *snp = dev->si_drv1; 367 368 snp->snp_blen = 0; 369 free(snp->snp_buf, M_SNP); 370 snp->snp_flags &= ~SNOOP_OPEN; 371 dev->si_drv1 = NULL; 372 373 return (snp_detach(snp)); 374 } 375 376 int 377 snpdown(snp) 378 struct snoop *snp; 379 { 380 381 if (snp->snp_blen != SNOOP_MINLEN) { 382 free(snp->snp_buf, M_SNP); 383 snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 384 snp->snp_blen = SNOOP_MINLEN; 385 } 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 dev_t tdev; 401 struct snoop *snp = dev->si_drv1; 402 struct tty *tp, *tpo; 403 int s; 404 405 switch (cmd) { 406 case SNPSTTY: 407 tdev = udev2dev(*((udev_t *)data), 0); 408 if (tdev == NODEV) 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 == NODEV) { 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 struct snoop *snp = dev->si_drv1; 492 int revents = 0; 493 494 495 /* 496 * If snoop is down, we don't want to poll() forever so we return 1. 497 * Caller should see if we down via FIONREAD ioctl(). The last should 498 * return -1 to indicate down state. 499 */ 500 if (events & (POLLIN | POLLRDNORM)) { 501 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 502 revents |= events & (POLLIN | POLLRDNORM); 503 else 504 selrecord(p, &snp->snp_sel); 505 } 506 return (revents); 507 } 508 509 static void snp_drvinit __P((void *unused)); 510 511 static void 512 snp_drvinit(unused) 513 void *unused; 514 { 515 516 cdevsw_add(&snp_cdevsw); 517 } 518 519 SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL) 520