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