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