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