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