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