xref: /freebsd/sys/dev/snp/snp.c (revision 3a51f88a2721f4cf7aded2a63f20925f8d700a7c)
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 "opt_compat.h"
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/filio.h>
22 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
23 #include <sys/ioctl_compat.h>
24 #endif
25 #include <sys/malloc.h>
26 #include <sys/tty.h>
27 #include <sys/conf.h>
28 #include <sys/poll.h>
29 #include <sys/kernel.h>
30 #include <sys/snoop.h>
31 #include <sys/vnode.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 static struct tty	*snpdevtotty __P((dev_t dev));
65 static int		snp_detach __P((struct snoop *snp));
66 
67 static struct tty *
68 snpdevtotty (dev)
69 	dev_t		dev;
70 {
71 	struct cdevsw	*cdp;
72 
73 	cdp = devsw(dev);
74 	if (cdp && cdp->d_flags & D_TTY)
75 		return (dev->si_tty);
76 	return (NULL);
77 }
78 
79 #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
80 				 * interactive mode write is 3 bytes
81 				 * length for function keys...
82 				 */
83 
84 static	int
85 snpwrite(dev, uio, flag)
86 	dev_t           dev;
87 	struct uio     *uio;
88 	int             flag;
89 {
90 	int             len, i, error;
91 	struct snoop   *snp = dev->si_drv1;
92 	struct tty     *tp;
93 	char		c[SNP_INPUT_BUF];
94 
95 	if (snp->snp_tty == NULL)
96 		return (EIO);
97 
98 	tp = snp->snp_tty;
99 
100 	if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
101 	    (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC))
102 		goto tty_input;
103 
104 	printf("Snoop: attempt to write to bad tty.\n");
105 	return (EIO);
106 
107 tty_input:
108 	if (!(tp->t_state & TS_ISOPEN))
109 		return (EIO);
110 
111 	while (uio->uio_resid > 0) {
112 		len = MIN(uio->uio_resid, SNP_INPUT_BUF);
113 		if ((error = uiomove(c, len, uio)) != 0)
114 			return (error);
115 		for (i=0; i < len; i++) {
116 			if (ttyinput(c[i], tp))
117 				return (EIO);
118 		}
119 	}
120 	return 0;
121 
122 }
123 
124 
125 static	int
126 snpread(dev, uio, flag)
127 	dev_t           dev;
128 	struct uio     *uio;
129 	int             flag;
130 {
131 	struct snoop   *snp = dev->si_drv1;
132 	int             len, n, nblen, s, error = 0;
133 	caddr_t         from;
134 	char           *nbuf;
135 
136 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
137 	    ("snoop buffer error"));
138 
139 	if (snp->snp_tty == NULL)
140 		return (EIO);
141 
142 	snp->snp_flags &= ~SNOOP_RWAIT;
143 
144 	do {
145 		if (snp->snp_len == 0) {
146 			if (flag & IO_NDELAY)
147 				return (EWOULDBLOCK);
148 			snp->snp_flags |= SNOOP_RWAIT;
149 			tsleep((caddr_t)snp, (PZERO + 1) | PCATCH, "snprd", 0);
150 		}
151 	} while (snp->snp_len == 0);
152 
153 	n = snp->snp_len;
154 
155 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
156 		len = MIN(uio->uio_resid, snp->snp_len);
157 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
158 		if (len == 0)
159 			break;
160 
161 		error = uiomove(from, len, uio);
162 		snp->snp_base += len;
163 		snp->snp_len -= len;
164 	}
165 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
166 		snp->snp_flags &= ~SNOOP_OFLOW;
167 	}
168 	s = spltty();
169 	nblen = snp->snp_blen;
170 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
171 		while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN))
172 			nblen = nblen / 2;
173 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
174 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
175 			free(snp->snp_buf, M_SNP);
176 			snp->snp_buf = nbuf;
177 			snp->snp_blen = nblen;
178 			snp->snp_base = 0;
179 		}
180 	}
181 	splx(s);
182 
183 	return error;
184 }
185 
186 int
187 snpinc(struct snoop *snp, char c)
188 {
189         char    buf;
190 
191 	buf = c;
192         return (snpin(snp, &buf, 1));
193 }
194 
195 
196 int
197 snpin(snp, buf, n)
198 	struct snoop   *snp;
199 	char           *buf;
200 	int             n;
201 {
202 	int             s_free, s_tail;
203 	int             s, len, nblen;
204 	caddr_t         from, to;
205 	char           *nbuf;
206 
207 	KASSERT(n >= 0, ("negative snoop char count"));
208 
209 	if (n == 0)
210 		return 0;
211 
212 	if (snp->snp_flags & SNOOP_DOWN) {
213 		printf("Snoop: more data to down interface.\n");
214 		return 0;
215 	}
216 
217 	if (snp->snp_flags & SNOOP_OFLOW) {
218 		printf("Snoop: buffer overflow.\n");
219 		/*
220 		 * On overflow we just repeat the standart close
221 		 * procedure...yes , this is waste of space but.. Then next
222 		 * read from device will fail if one would recall he is
223 		 * snooping and retry...
224 		 */
225 
226 		return (snpdown(snp));
227 	}
228 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
229 	s_free = snp->snp_blen - snp->snp_len;
230 
231 
232 	if (n > s_free) {
233 		s = spltty();
234 		nblen = snp->snp_blen;
235 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
236 			nblen = snp->snp_blen * 2;
237 			s_free = nblen - (snp->snp_len + snp->snp_base);
238 		}
239 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
240 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
241 			free(snp->snp_buf, M_SNP);
242 			snp->snp_buf = nbuf;
243 			snp->snp_blen = nblen;
244 			snp->snp_base = 0;
245 		} else {
246 			snp->snp_flags |= SNOOP_OFLOW;
247 			if (snp->snp_flags & SNOOP_RWAIT) {
248 				snp->snp_flags &= ~SNOOP_RWAIT;
249 				wakeup((caddr_t)snp);
250 			}
251 			splx(s);
252 			return 0;
253 		}
254 		splx(s);
255 	}
256 	if (n > s_tail) {
257 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
258 		to = (caddr_t)(snp->snp_buf);
259 		len = snp->snp_len;
260 		bcopy(from, to, len);
261 		snp->snp_base = 0;
262 	}
263 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
264 	bcopy(buf, to, n);
265 	snp->snp_len += n;
266 
267 	if (snp->snp_flags & SNOOP_RWAIT) {
268 		snp->snp_flags &= ~SNOOP_RWAIT;
269 		wakeup((caddr_t)snp);
270 	}
271 	selwakeup(&snp->snp_sel);
272 	snp->snp_sel.si_pid = 0;
273 
274 	return n;
275 }
276 
277 static	int
278 snpopen(dev, flag, mode, p)
279 	dev_t           dev;
280 	int             flag, mode;
281 	struct proc    *p;
282 {
283 	struct snoop   *snp;
284 	int error;
285 
286 	if ((error = suser(p)) != 0)
287 		return (error);
288 
289 	if (dev->si_drv1 == NULL) {
290 		if (!(dev->si_flags & SI_NAMED))
291 			make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
292 			    0600, "snp%d", dev2unit(dev));
293 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, M_WAITOK|M_ZERO);
294 	} else
295 		return (EBUSY);
296 
297 	/*
298 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
299 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
300 	 */
301 	snp->snp_flags = SNOOP_OPEN;
302 
303 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
304 	snp->snp_blen = SNOOP_MINLEN;
305 	snp->snp_base = 0;
306 	snp->snp_len = 0;
307 
308 	/*
309 	 * snp_tty == NULL  is for inactive snoop devices.
310 	 */
311 	snp->snp_tty = NULL;
312 	snp->snp_target = NODEV;
313 	return (0);
314 }
315 
316 
317 static int
318 snp_detach(snp)
319 	struct snoop   *snp;
320 {
321 	struct tty     *tp;
322 
323 	snp->snp_base = 0;
324 	snp->snp_len = 0;
325 
326 	/*
327 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
328 	 * change it anyway.
329 	 */
330 
331 	if (snp->snp_tty == NULL)
332 		goto detach_notty;
333 
334 	tp = snp->snp_tty;
335 
336 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
337 	    (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) {
338 		tp->t_sc = NULL;
339 		tp->t_state &= ~TS_SNOOP;
340 	} else
341 		printf("Snoop: bad attached tty data.\n");
342 
343 	snp->snp_tty = NULL;
344 	snp->snp_target = NODEV;
345 
346 detach_notty:
347 	selwakeup(&snp->snp_sel);
348 	snp->snp_sel.si_pid = 0;
349 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
350 		free(snp, M_SNP);
351 
352 	return (0);
353 }
354 
355 static	int
356 snpclose(dev, flags, fmt, p)
357 	dev_t           dev;
358 	int             flags;
359 	int             fmt;
360 	struct proc    *p;
361 {
362 	struct snoop   *snp = dev->si_drv1;
363 
364 	snp->snp_blen = 0;
365 	free(snp->snp_buf, M_SNP);
366 	snp->snp_flags &= ~SNOOP_OPEN;
367 	dev->si_drv1 = NULL;
368 	destroy_dev(dev);
369 
370 	return (snp_detach(snp));
371 }
372 
373 int
374 snpdown(snp)
375 	struct snoop	*snp;
376 {
377 
378 	if (snp->snp_blen != SNOOP_MINLEN) {
379 		free(snp->snp_buf, M_SNP);
380 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
381 		snp->snp_blen = SNOOP_MINLEN;
382 	}
383 	snp->snp_flags |= SNOOP_DOWN;
384 
385 	return (snp_detach(snp));
386 }
387 
388 
389 static	int
390 snpioctl(dev, cmd, data, flags, p)
391 	dev_t           dev;
392 	u_long          cmd;
393 	caddr_t         data;
394 	int             flags;
395 	struct proc    *p;
396 {
397 	dev_t		tdev;
398 	struct snoop   *snp = dev->si_drv1;
399 	struct tty     *tp, *tpo;
400 	int s;
401 
402 	switch (cmd) {
403 	case SNPSTTY:
404 		tdev = udev2dev(*((udev_t *)data), 0);
405 		if (tdev == NODEV)
406 			return (snpdown(snp));
407 
408 		tp = snpdevtotty(tdev);
409 		if (!tp)
410 			return (EINVAL);
411 
412 		if ((tp->t_sc != (caddr_t)snp) && (tp->t_state & TS_SNOOP))
413 			return (EBUSY);
414 
415 		if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC))
416 			return (EBUSY);
417 
418 		s = spltty();
419 
420 		if (snp->snp_target == NODEV) {
421 			tpo = snp->snp_tty;
422 			if (tpo)
423 				tpo->t_state &= ~TS_SNOOP;
424 		}
425 
426 		tp->t_sc = (caddr_t)snp;
427 		tp->t_state |= TS_SNOOP;
428 		snp->snp_tty = tp;
429 		snp->snp_target = tdev;
430 
431 		/*
432 		 * Clean overflow and down flags -
433 		 * we'll have a chance to get them in the future :)))
434 		 */
435 		snp->snp_flags &= ~SNOOP_OFLOW;
436 		snp->snp_flags &= ~SNOOP_DOWN;
437 		splx(s);
438 		break;
439 
440 	case SNPGTTY:
441 		/*
442 		 * We keep snp_target field specially to make
443 		 * SNPGTTY happy, else we can't know what is device
444 		 * major/minor for tty.
445 		 */
446 		*((dev_t *)data) = snp->snp_target;
447 		break;
448 
449 	case FIONBIO:
450 		break;
451 
452 	case FIOASYNC:
453 		if (*(int *)data)
454 			snp->snp_flags |= SNOOP_ASYNC;
455 		else
456 			snp->snp_flags &= ~SNOOP_ASYNC;
457 		break;
458 
459 	case FIONREAD:
460 		s = spltty();
461 		if (snp->snp_tty != NULL)
462 			*(int *)data = snp->snp_len;
463 		else
464 			if (snp->snp_flags & SNOOP_DOWN) {
465 				if (snp->snp_flags & SNOOP_OFLOW)
466 					*(int *)data = SNP_OFLOW;
467 				else
468 					*(int *)data = SNP_TTYCLOSE;
469 			} else {
470 				*(int *)data = SNP_DETACH;
471 			}
472 		splx(s);
473 		break;
474 
475 	default:
476 		return (ENOTTY);
477 	}
478 	return (0);
479 }
480 
481 
482 static	int
483 snppoll(dev, events, p)
484 	dev_t           dev;
485 	int             events;
486 	struct proc    *p;
487 {
488 	struct snoop   *snp = dev->si_drv1;
489 	int		revents = 0;
490 
491 
492 	/*
493 	 * If snoop is down, we don't want to poll() forever so we return 1.
494 	 * Caller should see if we down via FIONREAD ioctl().  The last should
495 	 * return -1 to indicate down state.
496 	 */
497 	if (events & (POLLIN | POLLRDNORM)) {
498 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
499 			revents |= events & (POLLIN | POLLRDNORM);
500 		else
501 			selrecord(p, &snp->snp_sel);
502 	}
503 	return (revents);
504 }
505 
506 static void snp_drvinit __P((void *unused));
507 
508 static void
509 snp_clone(void *arg, char *name, int namelen, dev_t *dev)
510 {
511 	int u;
512 
513 	if (*dev != NODEV)
514 		return;
515 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
516 		return;
517 	*dev = make_dev(&snp_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, 0600,
518 	    "snp%d", u);
519 	return;
520 }
521 
522 static void
523 snp_drvinit(unused)
524 	void *unused;
525 {
526 
527 	EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
528 	cdevsw_add(&snp_cdevsw);
529 }
530 
531 SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL)
532