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