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