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