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