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