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