xref: /dragonfly/sys/dev/misc/snp/snp.c (revision 9bb2a92d)
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.8 2003/08/27 10:35:18 rob 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/vnode.h>
30 #include <sys/device.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 	/* name */	"snp",
44 	/* maj */	CDEV_MAJOR,
45 	/* flags */	0,
46 	/* port */	NULL,
47 	/* autoq */	0,
48 
49 	/* open */	snpopen,
50 	/* close */	snpclose,
51 	/* read */	snpread,
52 	/* write */	snpwrite,
53 	/* ioctl */	snpioctl,
54 	/* poll */	snppoll,
55 	/* mmap */	nommap,
56 	/* strategy */	nostrategy,
57 	/* dump */	nodump,
58 	/* psize */	nopsize
59 };
60 
61 static struct linesw snpdisc = {
62 	ttyopen,	snplclose,	ttread,		snplwrite,
63 	l_nullioctl,	ttyinput,	ttstart,	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 	dev_t			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 
113 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
114 
115 static struct tty	*snpdevtotty (dev_t dev);
116 static int		snp_detach (struct snoop *snp);
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 
121 static int
122 snplclose(tp, flag)
123 	struct tty *tp;
124 	int flag;
125 {
126 	struct snoop *snp;
127 	int error;
128 
129 	snp = tp->t_sc;
130 	error = snp_down(snp);
131 	if (error != 0)
132 		return (error);
133 	error = ttylclose(tp, flag);
134 	return (error);
135 }
136 
137 static int
138 snplwrite(tp, uio, flag)
139 	struct tty *tp;
140 	struct uio *uio;
141 	int flag;
142 {
143 	struct iovec iov;
144 	struct uio uio2;
145 	struct snoop *snp;
146 	int error, ilen;
147 	char *ibuf;
148 
149 	error = 0;
150 	ibuf = NULL;
151 	snp = tp->t_sc;
152 	while (uio->uio_resid > 0) {
153 		ilen = imin(512, uio->uio_resid);
154 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
155 		error = uiomove(ibuf, ilen, uio);
156 		if (error != 0)
157 			break;
158 		snp_in(snp, ibuf, ilen);
159 		/* Hackish, but probably the least of all evils. */
160 		iov.iov_base = ibuf;
161 		iov.iov_len = ilen;
162 		uio2.uio_iov = &iov;
163 		uio2.uio_iovcnt = 1;
164 		uio2.uio_offset = 0;
165 		uio2.uio_resid = ilen;
166 		uio2.uio_segflg = UIO_SYSSPACE;
167 		uio2.uio_rw = UIO_WRITE;
168 		uio2.uio_td = uio->uio_td;
169 		error = ttwrite(tp, &uio2, flag);
170 		if (error != 0)
171 			break;
172 		free(ibuf, M_SNP);
173 		ibuf = NULL;
174 	}
175 	if (ibuf != NULL)
176 		free(ibuf, M_SNP);
177 	return (error);
178 }
179 
180 static struct tty *
181 snpdevtotty(dev)
182 	dev_t dev;
183 {
184 	if ((dev_dflags(dev) & D_TTY) == 0)
185 		return (NULL);
186 	return (dev->si_tty);
187 }
188 
189 #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
190 				 * interactive mode write is 3 bytes
191 				 * length for function keys...
192 				 */
193 
194 static int
195 snpwrite(dev, uio, flag)
196 	dev_t dev;
197 	struct uio *uio;
198 	int flag;
199 {
200 	struct snoop *snp;
201 	struct tty *tp;
202 	int error, i, len;
203 	unsigned char c[SNP_INPUT_BUF];
204 
205 	snp = dev->si_drv1;
206 	tp = snp->snp_tty;
207 	if (tp == NULL)
208 		return (EIO);
209 	if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
210 	    tp->t_line == snooplinedisc)
211 		goto tty_input;
212 
213 	printf("Snoop: attempt to write to bad tty.\n");
214 	return (EIO);
215 
216 tty_input:
217 	if (!(tp->t_state & TS_ISOPEN))
218 		return (EIO);
219 
220 	while (uio->uio_resid > 0) {
221 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
222 		if ((error = uiomove(c, len, uio)) != 0)
223 			return (error);
224 		for (i=0; i < len; i++) {
225 			if (ttyinput(c[i], tp))
226 				return (EIO);
227 		}
228 	}
229 	return (0);
230 }
231 
232 
233 static int
234 snpread(dev, uio, flag)
235 	dev_t dev;
236 	struct uio *uio;
237 	int flag;
238 {
239 	struct snoop *snp;
240 	int error, len, n, nblen, s;
241 	caddr_t from;
242 	char *nbuf;
243 
244 	snp = dev->si_drv1;
245 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
246 	    ("snoop buffer error"));
247 
248 	if (snp->snp_tty == NULL)
249 		return (EIO);
250 
251 	snp->snp_flags &= ~SNOOP_RWAIT;
252 
253 	do {
254 		if (snp->snp_len == 0) {
255 			if (flag & IO_NDELAY)
256 				return (EWOULDBLOCK);
257 			snp->snp_flags |= SNOOP_RWAIT;
258 			error = tsleep((caddr_t)snp, PCATCH, "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("Snoop: more data to down interface.\n");
316 		return (0);
317 	}
318 
319 	if (snp->snp_flags & SNOOP_OFLOW) {
320 		printf("Snoop: buffer overflow.\n");
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((caddr_t)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((caddr_t)snp);
372 	}
373 	selwakeup(&snp->snp_sel);
374 	snp->snp_sel.si_pid = 0;
375 
376 	return (n);
377 }
378 
379 static int
380 snpopen(dev_t dev, int flag, int mode, d_thread_t *td)
381 {
382 	struct snoop *snp;
383 
384 	if (dev->si_drv1 == NULL) {
385 		make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
386 		    0600, "snp%d", minor(dev));
387 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
388 		    M_WAITOK | M_ZERO);
389 	} else
390 		return (EBUSY);
391 
392 	/*
393 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
394 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
395 	 */
396 	snp->snp_flags = SNOOP_OPEN;
397 
398 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
399 	snp->snp_blen = SNOOP_MINLEN;
400 	snp->snp_base = 0;
401 	snp->snp_len = 0;
402 
403 	/*
404 	 * snp_tty == NULL  is for inactive snoop devices.
405 	 */
406 	snp->snp_tty = NULL;
407 	snp->snp_target = NODEV;
408 
409 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
410 	return (0);
411 }
412 
413 
414 static int
415 snp_detach(snp)
416 	struct snoop *snp;
417 {
418 	struct tty *tp;
419 
420 	snp->snp_base = 0;
421 	snp->snp_len = 0;
422 
423 	/*
424 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
425 	 * change it anyway.
426 	 */
427 	tp = snp->snp_tty;
428 	if (tp == NULL)
429 		goto detach_notty;
430 
431 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
432 	    tp->t_line == snooplinedisc) {
433 		tp->t_sc = NULL;
434 		tp->t_state &= ~TS_SNOOP;
435 		tp->t_line = snp->snp_olddisc;
436 	} else
437 		printf("Snoop: bad attached tty data.\n");
438 
439 	snp->snp_tty = NULL;
440 	snp->snp_target = NODEV;
441 
442 detach_notty:
443 	selwakeup(&snp->snp_sel);
444 	snp->snp_sel.si_pid = 0;
445 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
446 		free(snp, M_SNP);
447 
448 	return (0);
449 }
450 
451 static int
452 snpclose(dev_t dev, int flags, int fmt, d_thread_t *td)
453 {
454 	struct snoop *snp;
455 
456 	snp = dev->si_drv1;
457 	snp->snp_blen = 0;
458 	LIST_REMOVE(snp, snp_list);
459 	free(snp->snp_buf, M_SNP);
460 	snp->snp_flags &= ~SNOOP_OPEN;
461 	dev->si_drv1 = NULL;
462 
463 	return (snp_detach(snp));
464 }
465 
466 static int
467 snp_down(snp)
468 	struct snoop *snp;
469 {
470 
471 	if (snp->snp_blen != SNOOP_MINLEN) {
472 		free(snp->snp_buf, M_SNP);
473 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
474 		snp->snp_blen = SNOOP_MINLEN;
475 	}
476 	snp->snp_flags |= SNOOP_DOWN;
477 
478 	return (snp_detach(snp));
479 }
480 
481 static int
482 snpioctl(dev_t dev, u_long cmd, caddr_t data, int flags, d_thread_t *td)
483 {
484 	struct snoop *snp;
485 	struct tty *tp, *tpo;
486 	dev_t tdev;
487 	int s;
488 
489 	snp = dev->si_drv1;
490 	switch (cmd) {
491 	case SNPSTTY:
492 		tdev = udev2dev(*((udev_t *)data), 0);
493 		if (tdev == NODEV)
494 			return (snp_down(snp));
495 
496 		tp = snpdevtotty(tdev);
497 		if (!tp)
498 			return (EINVAL);
499 		if (tp->t_state & TS_SNOOP)
500 			return (EBUSY);
501 
502 		s = spltty();
503 
504 		if (snp->snp_target == NODEV) {
505 			tpo = snp->snp_tty;
506 			if (tpo)
507 				tpo->t_state &= ~TS_SNOOP;
508 		}
509 
510 		tp->t_sc = (caddr_t)snp;
511 		tp->t_state |= TS_SNOOP;
512 		snp->snp_olddisc = tp->t_line;
513 		tp->t_line = snooplinedisc;
514 		snp->snp_tty = tp;
515 		snp->snp_target = tdev;
516 
517 		/*
518 		 * Clean overflow and down flags -
519 		 * we'll have a chance to get them in the future :)))
520 		 */
521 		snp->snp_flags &= ~SNOOP_OFLOW;
522 		snp->snp_flags &= ~SNOOP_DOWN;
523 		splx(s);
524 		break;
525 
526 	case SNPGTTY:
527 		/*
528 		 * We keep snp_target field specially to make
529 		 * SNPGTTY happy, else we can't know what is device
530 		 * major/minor for tty.
531 		 */
532 		*((dev_t *)data) = snp->snp_target;
533 		break;
534 
535 	case FIONBIO:
536 		break;
537 
538 	case FIOASYNC:
539 		if (*(int *)data)
540 			snp->snp_flags |= SNOOP_ASYNC;
541 		else
542 			snp->snp_flags &= ~SNOOP_ASYNC;
543 		break;
544 
545 	case FIONREAD:
546 		s = spltty();
547 		if (snp->snp_tty != NULL)
548 			*(int *)data = snp->snp_len;
549 		else
550 			if (snp->snp_flags & SNOOP_DOWN) {
551 				if (snp->snp_flags & SNOOP_OFLOW)
552 					*(int *)data = SNP_OFLOW;
553 				else
554 					*(int *)data = SNP_TTYCLOSE;
555 			} else {
556 				*(int *)data = SNP_DETACH;
557 			}
558 		splx(s);
559 		break;
560 
561 	default:
562 		return (ENOTTY);
563 	}
564 	return (0);
565 }
566 
567 static int
568 snppoll(dev_t dev, int events, d_thread_t *td)
569 {
570 	struct snoop *snp;
571 	int revents;
572 
573 	snp = dev->si_drv1;
574 	revents = 0;
575 	/*
576 	 * If snoop is down, we don't want to poll() forever so we return 1.
577 	 * Caller should see if we down via FIONREAD ioctl().  The last should
578 	 * return -1 to indicate down state.
579 	 */
580 	if (events & (POLLIN | POLLRDNORM)) {
581 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
582 			revents |= events & (POLLIN | POLLRDNORM);
583 		else
584 			selrecord(td, &snp->snp_sel);
585 	}
586 	return (revents);
587 }
588 
589 static int
590 snp_modevent(mod, type, data)
591 	module_t mod;
592 	int type;
593 	void *data;
594 {
595 
596 	switch (type) {
597 	case MOD_LOAD:
598 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
599 		cdevsw_add(&snp_cdevsw);
600 		break;
601 	case MOD_UNLOAD:
602 		if (!LIST_EMPTY(&snp_sclist))
603 			return (EBUSY);
604 		ldisc_deregister(snooplinedisc);
605 		cdevsw_remove(&snp_cdevsw);
606 		break;
607 	default:
608 		break;
609 	}
610 	return (0);
611 }
612 
613 static moduledata_t snp_mod = {
614         "snp",
615         snp_modevent,
616         NULL
617 };
618 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR);
619