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