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