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