xref: /freebsd/sys/dev/snp/snp.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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 
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/fcntl.h>
23 #include <sys/filio.h>
24 #include <sys/malloc.h>
25 #include <sys/tty.h>
26 #include <sys/conf.h>
27 #include <sys/poll.h>
28 #include <sys/kernel.h>
29 #include <sys/module.h>
30 #include <sys/queue.h>
31 #include <sys/snoop.h>
32 #include <sys/uio.h>
33 
34 static	l_close_t	snplclose;
35 static	l_write_t	snplwrite;
36 static	d_open_t	snpopen;
37 static	d_close_t	snpclose;
38 static	d_read_t	snpread;
39 static	d_write_t	snpwrite;
40 static	d_ioctl_t	snpioctl;
41 static	d_poll_t	snppoll;
42 
43 static struct cdevsw snp_cdevsw = {
44 	.d_version =	D_VERSION,
45 	.d_flags =	D_PSEUDO | D_NEEDGIANT,
46 	.d_open =	snpopen,
47 	.d_close =	snpclose,
48 	.d_read =	snpread,
49 	.d_write =	snpwrite,
50 	.d_ioctl =	snpioctl,
51 	.d_poll =	snppoll,
52 	.d_name =	"snp",
53 };
54 
55 static struct linesw snpdisc = {
56 	.l_open =	tty_open,
57 	.l_close =	snplclose,
58 	.l_read =	ttread,
59 	.l_write =	snplwrite,
60 	.l_ioctl =	l_nullioctl,
61 	.l_rint =	ttyinput,
62 	.l_start =	ttstart,
63 	.l_modem =	ttymodem
64 };
65 
66 /*
67  * This is the main snoop per-device structure.
68  */
69 struct snoop {
70 	LIST_ENTRY(snoop)	snp_list;	/* List glue. */
71 	int			snp_unit;	/* Device number. */
72 	struct cdev *snp_target;	/* Target tty device. */
73 	struct tty		*snp_tty;	/* Target tty pointer. */
74 	u_long			 snp_len;	/* Possible length. */
75 	u_long			 snp_base;	/* Data base. */
76 	u_long			 snp_blen;	/* Used length. */
77 	caddr_t			 snp_buf;	/* Allocation pointer. */
78 	int			 snp_flags;	/* Flags. */
79 	struct selinfo		 snp_sel;	/* Select info. */
80 	int			 snp_olddisc;	/* Old line discipline. */
81 };
82 
83 /*
84  * Possible flags.
85  */
86 #define SNOOP_ASYNC		0x0002
87 #define SNOOP_OPEN		0x0004
88 #define SNOOP_RWAIT		0x0008
89 #define SNOOP_OFLOW		0x0010
90 #define SNOOP_DOWN		0x0020
91 
92 /*
93  * Other constants.
94  */
95 #define SNOOP_MINLEN		(4*1024)	/* This should be power of 2.
96 						 * 4K tested to be the minimum
97 						 * for which on normal tty
98 						 * usage there is no need to
99 						 * allocate more.
100 						 */
101 #define SNOOP_MAXLEN		(64*1024)	/* This one also,64K enough
102 						 * If we grow more,something
103 						 * really bad in this world..
104 						 */
105 
106 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
107 /*
108  * The number of the "snoop" line discipline.  This gets determined at
109  * module load time.
110  */
111 static int snooplinedisc;
112 
113 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
114 static struct clonedevs	  *snpclones;
115 
116 static struct tty	*snpdevtotty(struct cdev *dev);
117 static void		snp_clone(void *arg, char *name,
118 			    int namelen, struct cdev **dev);
119 static int		snp_detach(struct snoop *snp);
120 static int		snp_down(struct snoop *snp);
121 static int		snp_in(struct snoop *snp, char *buf, int n);
122 static int		snp_modevent(module_t mod, int what, void *arg);
123 static struct snoop	*ttytosnp(struct tty *);
124 
125 static struct snoop *
126 ttytosnp(struct tty *tp)
127 {
128 	struct snoop *snp;
129 
130 	LIST_FOREACH(snp, &snp_sclist, snp_list) {
131 		if (snp->snp_tty == tp)
132 			return (snp);
133 	}
134 	return (NULL);
135 }
136 
137 static int
138 snplclose(tp, flag)
139 	struct tty *tp;
140 	int flag;
141 {
142 	struct snoop *snp;
143 	int error;
144 
145 	snp = ttytosnp(tp);
146 	error = snp_down(snp);
147 	if (error != 0)
148 		return (error);
149 	error = ttylclose(tp, flag);
150 	return (error);
151 }
152 
153 static int
154 snplwrite(tp, uio, flag)
155 	struct tty *tp;
156 	struct uio *uio;
157 	int flag;
158 {
159 	struct iovec iov;
160 	struct uio uio2;
161 	struct snoop *snp;
162 	int error, ilen;
163 	char *ibuf;
164 
165 	error = 0;
166 	ibuf = NULL;
167 	snp = ttytosnp(tp);
168 	while (uio->uio_resid > 0) {
169 		ilen = imin(512, uio->uio_resid);
170 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
171 		error = uiomove(ibuf, ilen, uio);
172 		if (error != 0)
173 			break;
174 		snp_in(snp, ibuf, ilen);
175 		/* Hackish, but probably the least of all evils. */
176 		iov.iov_base = ibuf;
177 		iov.iov_len = ilen;
178 		uio2.uio_iov = &iov;
179 		uio2.uio_iovcnt = 1;
180 		uio2.uio_offset = 0;
181 		uio2.uio_resid = ilen;
182 		uio2.uio_segflg = UIO_SYSSPACE;
183 		uio2.uio_rw = UIO_WRITE;
184 		uio2.uio_td = uio->uio_td;
185 		error = ttwrite(tp, &uio2, flag);
186 		if (error != 0)
187 			break;
188 		free(ibuf, M_SNP);
189 		ibuf = NULL;
190 	}
191 	if (ibuf != NULL)
192 		free(ibuf, M_SNP);
193 	return (error);
194 }
195 
196 static struct tty *
197 snpdevtotty(dev)
198 	struct cdev *dev;
199 {
200 	struct cdevsw *cdp;
201 	struct tty *tp;
202 
203 	cdp = dev_refthread(dev);
204 	if (cdp == NULL)
205 		return (NULL);
206 	if (!(cdp->d_flags & D_TTY))
207 		tp = NULL;
208 	else
209 		tp = dev->si_tty;
210 	dev_relthread(dev);
211 	return (tp);
212 }
213 
214 #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
215 				 * interactive mode write is 3 bytes
216 				 * length for function keys...
217 				 */
218 
219 static int
220 snpwrite(dev, uio, flag)
221 	struct cdev *dev;
222 	struct uio *uio;
223 	int flag;
224 {
225 	struct snoop *snp;
226 	struct tty *tp;
227 	int error, i, len;
228 	unsigned char c[SNP_INPUT_BUF];
229 
230 	snp = dev->si_drv1;
231 	tp = snp->snp_tty;
232 	if (tp == NULL)
233 		return (EIO);
234 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc)
235 		goto tty_input;
236 
237 	printf("snp%d: attempt to write to bad tty\n", snp->snp_unit);
238 	return (EIO);
239 
240 tty_input:
241 	if (!(tp->t_state & TS_ISOPEN))
242 		return (EIO);
243 
244 	while (uio->uio_resid > 0) {
245 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
246 		if ((error = uiomove(c, len, uio)) != 0)
247 			return (error);
248 		for (i=0; i < len; i++) {
249 			if (ttyinput(c[i], tp))
250 				return (EIO);
251 		}
252 	}
253 	return (0);
254 }
255 
256 
257 static int
258 snpread(dev, uio, flag)
259 	struct cdev *dev;
260 	struct uio *uio;
261 	int flag;
262 {
263 	struct snoop *snp;
264 	int error, len, n, nblen, s;
265 	caddr_t from;
266 	char *nbuf;
267 
268 	snp = dev->si_drv1;
269 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
270 	    ("snoop buffer error"));
271 
272 	if (snp->snp_tty == NULL)
273 		return (EIO);
274 
275 	snp->snp_flags &= ~SNOOP_RWAIT;
276 
277 	do {
278 		if (snp->snp_len == 0) {
279 			if (flag & O_NONBLOCK)
280 				return (EWOULDBLOCK);
281 			snp->snp_flags |= SNOOP_RWAIT;
282 			error = tsleep(snp, (PZERO + 1) | PCATCH,
283 			    "snprd", 0);
284 			if (error != 0)
285 				return (error);
286 		}
287 	} while (snp->snp_len == 0);
288 
289 	n = snp->snp_len;
290 
291 	error = 0;
292 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
293 		len = min((unsigned)uio->uio_resid, snp->snp_len);
294 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
295 		if (len == 0)
296 			break;
297 
298 		error = uiomove(from, len, uio);
299 		snp->snp_base += len;
300 		snp->snp_len -= len;
301 	}
302 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
303 		snp->snp_flags &= ~SNOOP_OFLOW;
304 	}
305 	s = spltty();
306 	nblen = snp->snp_blen;
307 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
308 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
309 			nblen = nblen / 2;
310 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
311 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
312 			free(snp->snp_buf, M_SNP);
313 			snp->snp_buf = nbuf;
314 			snp->snp_blen = nblen;
315 			snp->snp_base = 0;
316 		}
317 	}
318 	splx(s);
319 
320 	return (error);
321 }
322 
323 static int
324 snp_in(snp, buf, n)
325 	struct snoop *snp;
326 	char *buf;
327 	int n;
328 {
329 	int s_free, s_tail;
330 	int s, len, nblen;
331 	caddr_t from, to;
332 	char *nbuf;
333 
334 	KASSERT(n >= 0, ("negative snoop char count"));
335 
336 	if (n == 0)
337 		return (0);
338 
339 	if (snp->snp_flags & SNOOP_DOWN) {
340 		printf("snp%d: more data to down interface\n", snp->snp_unit);
341 		return (0);
342 	}
343 
344 	if (snp->snp_flags & SNOOP_OFLOW) {
345 		printf("snp%d: buffer overflow\n", snp->snp_unit);
346 		/*
347 		 * On overflow we just repeat the standart close
348 		 * procedure...yes , this is waste of space but.. Then next
349 		 * read from device will fail if one would recall he is
350 		 * snooping and retry...
351 		 */
352 
353 		return (snp_down(snp));
354 	}
355 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
356 	s_free = snp->snp_blen - snp->snp_len;
357 
358 
359 	if (n > s_free) {
360 		s = spltty();
361 		nblen = snp->snp_blen;
362 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
363 			nblen = snp->snp_blen * 2;
364 			s_free = nblen - (snp->snp_len + snp->snp_base);
365 		}
366 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
367 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
368 			free(snp->snp_buf, M_SNP);
369 			snp->snp_buf = nbuf;
370 			snp->snp_blen = nblen;
371 			snp->snp_base = 0;
372 		} else {
373 			snp->snp_flags |= SNOOP_OFLOW;
374 			if (snp->snp_flags & SNOOP_RWAIT) {
375 				snp->snp_flags &= ~SNOOP_RWAIT;
376 				wakeup(snp);
377 			}
378 			splx(s);
379 			return (0);
380 		}
381 		splx(s);
382 	}
383 	if (n > s_tail) {
384 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
385 		to = (caddr_t)(snp->snp_buf);
386 		len = snp->snp_len;
387 		bcopy(from, to, len);
388 		snp->snp_base = 0;
389 	}
390 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
391 	bcopy(buf, to, n);
392 	snp->snp_len += n;
393 
394 	if (snp->snp_flags & SNOOP_RWAIT) {
395 		snp->snp_flags &= ~SNOOP_RWAIT;
396 		wakeup(snp);
397 	}
398 	selwakeuppri(&snp->snp_sel, PZERO + 1);
399 
400 	return (n);
401 }
402 
403 static int
404 snpopen(dev, flag, mode, td)
405 	struct cdev *dev;
406 	int flag, mode;
407 	struct thread *td;
408 {
409 	struct snoop *snp;
410 
411 	if (dev->si_drv1 == NULL) {
412 		dev->si_flags &= ~SI_CHEAPCLONE;
413 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
414 		    M_WAITOK | M_ZERO);
415 		snp->snp_unit = dev2unit(dev);
416 	} else
417 		return (EBUSY);
418 
419 	/*
420 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
421 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
422 	 */
423 	snp->snp_flags = SNOOP_OPEN;
424 
425 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
426 	snp->snp_blen = SNOOP_MINLEN;
427 	snp->snp_base = 0;
428 	snp->snp_len = 0;
429 
430 	/*
431 	 * snp_tty == NULL  is for inactive snoop devices.
432 	 */
433 	snp->snp_tty = NULL;
434 	snp->snp_target = NULL;
435 
436 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
437 	return (0);
438 }
439 
440 
441 static int
442 snp_detach(snp)
443 	struct snoop *snp;
444 {
445 	struct tty *tp;
446 
447 	snp->snp_base = 0;
448 	snp->snp_len = 0;
449 
450 	/*
451 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
452 	 * change it anyway.
453 	 */
454 	tp = snp->snp_tty;
455 	if (tp == NULL)
456 		goto detach_notty;
457 
458 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) {
459 		tp->t_state &= ~TS_SNOOP;
460 		tp->t_line = snp->snp_olddisc;
461 	} else
462 		printf("snp%d: bad attached tty data\n", snp->snp_unit);
463 
464 	snp->snp_tty = NULL;
465 	snp->snp_target = NULL;
466 
467 detach_notty:
468 	selwakeuppri(&snp->snp_sel, PZERO + 1);
469 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
470 		free(snp, M_SNP);
471 
472 	return (0);
473 }
474 
475 static int
476 snpclose(dev, flags, fmt, td)
477 	struct cdev *dev;
478 	int flags;
479 	int fmt;
480 	struct thread *td;
481 {
482 	struct snoop *snp;
483 
484 	snp = dev->si_drv1;
485 	snp->snp_blen = 0;
486 	LIST_REMOVE(snp, snp_list);
487 	free(snp->snp_buf, M_SNP);
488 	snp->snp_flags &= ~SNOOP_OPEN;
489 	dev->si_drv1 = NULL;
490 	destroy_dev(dev);
491 
492 	return (snp_detach(snp));
493 }
494 
495 static int
496 snp_down(snp)
497 	struct snoop *snp;
498 {
499 
500 	if (snp->snp_blen != SNOOP_MINLEN) {
501 		free(snp->snp_buf, M_SNP);
502 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
503 		snp->snp_blen = SNOOP_MINLEN;
504 	}
505 	snp->snp_flags |= SNOOP_DOWN;
506 
507 	return (snp_detach(snp));
508 }
509 
510 static int
511 snpioctl(dev, cmd, data, flags, td)
512 	struct cdev *dev;
513 	u_long cmd;
514 	caddr_t data;
515 	int flags;
516 	struct thread *td;
517 {
518 	struct snoop *snp;
519 	struct tty *tp, *tpo;
520 	struct cdev *tdev;
521 	int s;
522 
523 	snp = dev->si_drv1;
524 	switch (cmd) {
525 	case SNPSTTY:
526 		tdev = findcdev(*((dev_t *)data));
527 		if (tdev == NULL)
528 			return (snp_down(snp));
529 
530 		tp = snpdevtotty(tdev);
531 		if (!tp)
532 			return (EINVAL);
533 		if (tp->t_state & TS_SNOOP)
534 			return (EBUSY);
535 
536 		s = spltty();
537 
538 		if (snp->snp_target == NULL) {
539 			tpo = snp->snp_tty;
540 			if (tpo)
541 				tpo->t_state &= ~TS_SNOOP;
542 		}
543 
544 		tp->t_state |= TS_SNOOP;
545 		snp->snp_olddisc = tp->t_line;
546 		tp->t_line = snooplinedisc;
547 		snp->snp_tty = tp;
548 		snp->snp_target = tdev;
549 
550 		/*
551 		 * Clean overflow and down flags -
552 		 * we'll have a chance to get them in the future :)))
553 		 */
554 		snp->snp_flags &= ~SNOOP_OFLOW;
555 		snp->snp_flags &= ~SNOOP_DOWN;
556 		splx(s);
557 		break;
558 
559 	case SNPGTTY:
560 		/*
561 		 * We keep snp_target field specially to make
562 		 * SNPGTTY happy, else we can't know what is device
563 		 * major/minor for tty.
564 		 */
565 		*((dev_t *)data) = dev2udev(snp->snp_target);
566 		break;
567 
568 	case FIONBIO:
569 		break;
570 
571 	case FIOASYNC:
572 		if (*(int *)data)
573 			snp->snp_flags |= SNOOP_ASYNC;
574 		else
575 			snp->snp_flags &= ~SNOOP_ASYNC;
576 		break;
577 
578 	case FIONREAD:
579 		s = spltty();
580 		if (snp->snp_tty != NULL)
581 			*(int *)data = snp->snp_len;
582 		else
583 			if (snp->snp_flags & SNOOP_DOWN) {
584 				if (snp->snp_flags & SNOOP_OFLOW)
585 					*(int *)data = SNP_OFLOW;
586 				else
587 					*(int *)data = SNP_TTYCLOSE;
588 			} else {
589 				*(int *)data = SNP_DETACH;
590 			}
591 		splx(s);
592 		break;
593 
594 	default:
595 		return (ENOTTY);
596 	}
597 	return (0);
598 }
599 
600 static int
601 snppoll(dev, events, td)
602 	struct cdev *dev;
603 	int events;
604 	struct thread *td;
605 {
606 	struct snoop *snp;
607 	int revents;
608 
609 	snp = dev->si_drv1;
610 	revents = 0;
611 	/*
612 	 * If snoop is down, we don't want to poll() forever so we return 1.
613 	 * Caller should see if we down via FIONREAD ioctl().  The last should
614 	 * return -1 to indicate down state.
615 	 */
616 	if (events & (POLLIN | POLLRDNORM)) {
617 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
618 			revents |= events & (POLLIN | POLLRDNORM);
619 		else
620 			selrecord(td, &snp->snp_sel);
621 	}
622 	return (revents);
623 }
624 
625 static void
626 snp_clone(arg, name, namelen, dev)
627 	void *arg;
628 	char *name;
629 	int namelen;
630 	struct cdev **dev;
631 {
632 	int u, i;
633 
634 	if (*dev != NULL)
635 		return;
636 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
637 		return;
638 	i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0);
639 	if (i)
640 		*dev = make_dev(&snp_cdevsw, unit2minor(u),
641 		     UID_ROOT, GID_WHEEL, 0600, "snp%d", u);
642 	if (*dev != NULL)
643 		(*dev)->si_flags |= SI_CHEAPCLONE;
644 }
645 
646 static int
647 snp_modevent(mod, type, data)
648 	module_t mod;
649 	int type;
650 	void *data;
651 {
652 	static eventhandler_tag eh_tag;
653 
654 	switch (type) {
655 	case MOD_LOAD:
656 		/* XXX error checking. */
657 		clone_setup(&snpclones);
658 		eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
659 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
660 		break;
661 	case MOD_UNLOAD:
662 		if (!LIST_EMPTY(&snp_sclist))
663 			return (EBUSY);
664 		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
665 		clone_cleanup(&snpclones);
666 		ldisc_deregister(snooplinedisc);
667 		break;
668 	default:
669 		return (EOPNOTSUPP);
670 		break;
671 	}
672 	return (0);
673 }
674 
675 static moduledata_t snp_mod = {
676         "snp",
677         snp_modevent,
678         NULL
679 };
680 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
681