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