xref: /freebsd/sys/dev/snp/snp.c (revision 953a3198a35204535cc9d450f04da982a4fea59b)
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 	struct tty     *tp;
186 
187 
188 	if (n == 0)
189 		return 0;
190 
191 #ifdef DIAGNOSTIC
192 	if (n < 0)
193 		panic("bad snoop char count");
194 
195 	if (!(snp->snp_flags & SNOOP_OPEN)) {
196 		printf("Snoop: data coming to closed device.\n");
197 		return 0;
198 	}
199 #endif
200 	if (snp->snp_flags & SNOOP_DOWN) {
201 		printf("Snoop: more data to down interface.\n");
202 		return 0;
203 	}
204 
205 	if (snp->snp_flags & SNOOP_OFLOW) {
206 		printf("Snoop: buffer overflow.\n");
207 		/*
208 		 * On overflow we just repeat the standart close
209 		 * procedure...yes , this is waste of space but.. Then next
210 		 * read from device will fail if one would recall he is
211 		 * snooping and retry...
212 		 */
213 
214 		return (snpdown(snp));
215 	}
216 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
217 	s_free = snp->snp_blen - snp->snp_len;
218 
219 
220 	if (n > s_free) {
221 		s = spltty();
222 		nblen = snp->snp_blen;
223 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
224 			nblen = snp->snp_blen * 2;
225 			s_free = nblen - (snp->snp_len + snp->snp_base);
226 		}
227 		if ((n <= s_free) && (nbuf = malloc(nblen, M_TTYS, M_NOWAIT))) {
228 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
229 			free(snp->snp_buf, M_TTYS);
230 			snp->snp_buf = nbuf;
231 			snp->snp_blen = nblen;
232 			snp->snp_base = 0;
233 		} else {
234 			snp->snp_flags |= SNOOP_OFLOW;
235 			if (snp->snp_flags & SNOOP_RWAIT) {
236 				snp->snp_flags &= ~SNOOP_RWAIT;
237 				wakeup((caddr_t) snp);
238 			}
239 			splx(s);
240 			return 0;
241 		}
242 		splx(s);
243 	}
244 	if (n > s_tail) {
245 		from = (caddr_t) (snp->snp_buf + snp->snp_base);
246 		to = (caddr_t) (snp->snp_buf);
247 		len = snp->snp_len;
248 		bcopy(from, to, len);
249 		snp->snp_base = 0;
250 	}
251 	to = (caddr_t) (snp->snp_buf + snp->snp_base + snp->snp_len);
252 	bcopy(buf, to, n);
253 	snp->snp_len += n;
254 
255 	if (snp->snp_flags & SNOOP_RWAIT) {
256 		snp->snp_flags &= ~SNOOP_RWAIT;
257 		wakeup((caddr_t) snp);
258 	}
259 	selwakeup(&snp->snp_sel);
260 	snp->snp_sel.si_pid = 0;
261 
262 	return n;
263 }
264 
265 int
266 snpopen(dev, flag, mode, p)
267 	dev_t           dev;
268 	int             flag, mode;
269 	struct proc    *p;
270 {
271 	struct snoop   *snp;
272 	register int    unit, error;
273 
274 	if (error = suser(p->p_ucred, &p->p_acflag))
275 		return (error);
276 
277 	if ((unit = minor(dev)) >= NSNP)
278 		return (ENXIO);
279 
280 	snp = &snoopsw[unit];
281 
282 	if (snp->snp_flags & SNOOP_OPEN)
283 		return (ENXIO);
284 
285 	/*
286 	 * We intentionally do not OR flags with SNOOP_OPEN,but set them so
287 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
288 	 */
289 	snp->snp_flags = SNOOP_OPEN;
290 
291 	snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
292 	snp->snp_blen = SNOOP_MINLEN;
293 	snp->snp_base = 0;
294 	snp->snp_len = 0;
295 
296 	/*
297 	 * snp_tty == NULL  is for inactive snoop devices.
298 	 */
299 	snp->snp_tty = NULL;
300 	snp->snp_target = -1;
301 	return (0);
302 }
303 
304 
305 int
306 snp_detach(snp)
307 	struct snoop   *snp;
308 {
309 	struct tty     *tp;
310 
311 	snp->snp_base = 0;
312 	snp->snp_len = 0;
313 
314 	/*
315 	 * If line disc. changed we do not touch this pointer,SLIP/PPP will
316 	 * change it anyway.
317 	 */
318 
319 	if (snp->snp_tty == NULL)
320 		goto detach_notty;
321 
322 	tp = snp->snp_tty;
323 
324 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
325 	    (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) {
326 		tp->t_sc = NULL;
327 		tp->t_state &= ~TS_SNOOP;
328 	} else
329 		printf("Snoop: bad attached tty data.\n");
330 
331 	snp->snp_tty = NULL;
332 	snp->snp_target = -1;
333 
334 detach_notty:
335 	selwakeup(&snp->snp_sel);
336 	snp->snp_sel.si_pid = 0;
337 
338 	return (0);
339 }
340 
341 int
342 snpclose(dev, flags, fmt, p)
343 	dev_t           dev;
344 	int             flags;
345 	int             fmt;
346 	struct proc    *p;
347 {
348 	register int    unit = minor(dev);
349 	struct snoop   *snp = &snoopsw[unit];
350 
351 	snp->snp_blen = 0;
352 	free(snp->snp_buf, M_TTYS);
353 	snp->snp_flags &= ~SNOOP_OPEN;
354 
355 	return (snp_detach(snp));
356 }
357 
358 int
359 snpdown(snp)
360 	struct snoop	*snp;
361 {
362 	snp->snp_blen = SNOOP_MINLEN;
363 	free(snp->snp_buf, M_TTYS);
364 	snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
365 	snp->snp_flags |= SNOOP_DOWN;
366 
367 	return (snp_detach(snp));
368 }
369 
370 
371 int
372 snpioctl(dev, cmd, data, flags, p)
373 	dev_t           dev;
374 	int             cmd;
375 	caddr_t         data;
376 	int             flags;
377 	struct proc    *p;
378 {
379 	int             unit = minor(dev), s;
380 	dev_t		tdev;
381 	struct snoop   *snp = &snoopsw[unit];
382 	struct tty     *tp, *tpo;
383 
384 	switch (cmd) {
385 	case SNPSTTY:
386 		tdev = *((dev_t *) data);
387 		if (tdev == -1)
388 			return (snpdown(snp));
389 
390 		tp = devtotty(tdev);
391 		if (!tp)
392 			return (EINVAL);
393 
394 		if ((tp->t_sc != (caddr_t) snp) && (tp->t_state & TS_SNOOP))
395 			return (EBUSY);
396 
397 		if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC))
398 			return (EBUSY);
399 
400 		s = spltty();
401 
402 		if (snp->snp_target == -1) {
403 			tpo = snp->snp_tty;
404 			if (tpo)
405 				tpo->t_state &= ~TS_SNOOP;
406 		}
407 
408 		tp->t_sc = (caddr_t) snp;
409 		tp->t_state |= TS_SNOOP;
410 		snp->snp_tty = tp;
411 		snp->snp_target = tdev;
412 
413 		/*
414 		 * Clean overflow and down flags -
415 		 * we'll have a chance to get them in the future :)))
416 		 */
417 		snp->snp_flags &= ~SNOOP_OFLOW;
418 		snp->snp_flags &= ~SNOOP_DOWN;
419 		splx(s);
420 		break;
421 
422 	case SNPGTTY:
423 		/*
424 		 * We keep snp_target field specially to make
425 		 * SNPGTTY happy,else we can't know what is device
426 		 * major/minor for tty.
427 		 */
428 		*((dev_t *) data) = snp->snp_target;
429 		break;
430 
431 	case FIONBIO:
432 		if (*(int *) data)
433 			snp->snp_flags |= SNOOP_NBIO;
434 		else
435 			snp->snp_flags &= ~SNOOP_NBIO;
436 		break;
437 
438 	case FIOASYNC:
439 		if (*(int *) data)
440 			snp->snp_flags |= SNOOP_ASYNC;
441 		else
442 			snp->snp_flags &= ~SNOOP_ASYNC;
443 		break;
444 
445 	case FIONREAD:
446 		s = spltty();
447 		if (snp->snp_tty != NULL)
448 			*(int *) data = snp->snp_len;
449 		else
450 			if (snp->snp_flags & SNOOP_DOWN) {
451 				if (snp->snp_flags & SNOOP_OFLOW)
452 					*(int *) data = SNP_OFLOW;
453 				else
454 					*(int *) data = SNP_TTYCLOSE;
455 			} else {
456 				*(int *) data = SNP_DETACH;
457 			}
458 		splx(s);
459 		break;
460 
461 	default:
462 		return (ENOTTY);
463 	}
464 	return (0);
465 }
466 
467 
468 int
469 snpselect(dev, rw, p)
470 	dev_t           dev;
471 	int             rw;
472 	struct proc    *p;
473 {
474 	int             unit = minor(dev), s;
475 	struct snoop   *snp = &snoopsw[unit];
476 
477 	if (rw != FREAD)
478 		return 1;
479 
480 	if (snp->snp_len > 0)
481 		return 1;
482 
483 	/*
484 	 * If snoop is down,we don't want to select() forever so we return 1.
485 	 * Caller should see if we down via FIONREAD ioctl().The last should
486 	 * return -1 to indicate down state.
487 	 */
488 	if (snp->snp_flags & SNOOP_DOWN)
489 		return 1;
490 
491 	selrecord(p, &snp->snp_sel);
492 	return 0;
493 }
494 
495 #endif
496