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