xref: /dragonfly/sys/dev/misc/snp/snp.c (revision d600454b)
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: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $
16  * $DragonFly: src/sys/dev/misc/snp/snp.c,v 1.12 2005/12/11 01:54:08 swildner Exp $
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/filio.h>
22 #include <sys/malloc.h>
23 #include <sys/tty.h>
24 #include <sys/conf.h>
25 #include <sys/poll.h>
26 #include <sys/kernel.h>
27 #include <sys/queue.h>
28 #include <sys/snoop.h>
29 #include <sys/thread2.h>
30 #include <sys/vnode.h>
31 #include <sys/device.h>
32 
33 static	l_close_t	snplclose;
34 static	l_write_t	snplwrite;
35 static	d_open_t	snpopen;
36 static	d_close_t	snpclose;
37 static	d_read_t	snpread;
38 static	d_write_t	snpwrite;
39 static	d_ioctl_t	snpioctl;
40 static	d_poll_t	snppoll;
41 
42 #define CDEV_MAJOR 53
43 static struct cdevsw snp_cdevsw = {
44 	/* name */	"snp",
45 	/* maj */	CDEV_MAJOR,
46 	/* flags */	0,
47 	/* port */	NULL,
48 	/* clone */	NULL,
49 
50 	/* open */	snpopen,
51 	/* close */	snpclose,
52 	/* read */	snpread,
53 	/* write */	snpwrite,
54 	/* ioctl */	snpioctl,
55 	/* poll */	snppoll,
56 	/* mmap */	nommap,
57 	/* strategy */	nostrategy,
58 	/* dump */	nodump,
59 	/* psize */	nopsize
60 };
61 
62 static struct linesw snpdisc = {
63 	ttyopen,	snplclose,	ttread,		snplwrite,
64 	l_nullioctl,	ttyinput,	ttstart,	ttymodem
65 };
66 
67 /*
68  * This is the main snoop per-device structure.
69  */
70 struct snoop {
71 	LIST_ENTRY(snoop)	snp_list;	/* List glue. */
72 	dev_t			snp_target;	/* Target tty device. */
73 	struct tty		*snp_tty;	/* Target tty pointer. */
74 	u_long			 snp_len;	/* Possible length. */
75 	u_long			 snp_base;	/* Data base. */
76 	u_long			 snp_blen;	/* Used length. */
77 	caddr_t			 snp_buf;	/* Allocation pointer. */
78 	int			 snp_flags;	/* Flags. */
79 	struct selinfo		 snp_sel;	/* Select info. */
80 	int			 snp_olddisc;	/* Old line discipline. */
81 };
82 
83 /*
84  * Possible flags.
85  */
86 #define SNOOP_ASYNC		0x0002
87 #define SNOOP_OPEN		0x0004
88 #define SNOOP_RWAIT		0x0008
89 #define SNOOP_OFLOW		0x0010
90 #define SNOOP_DOWN		0x0020
91 
92 /*
93  * Other constants.
94  */
95 #define SNOOP_MINLEN		(4*1024)	/* This should be power of 2.
96 						 * 4K tested to be the minimum
97 						 * for which on normal tty
98 						 * usage there is no need to
99 						 * allocate more.
100 						 */
101 #define SNOOP_MAXLEN		(64*1024)	/* This one also,64K enough
102 						 * If we grow more,something
103 						 * really bad in this world..
104 						 */
105 
106 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
107 /*
108  * The number of the "snoop" line discipline.  This gets determined at
109  * module load time.
110  */
111 static int snooplinedisc;
112 
113 
114 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
115 
116 static struct tty	*snpdevtotty (dev_t dev);
117 static int		snp_detach (struct snoop *snp);
118 static int		snp_down (struct snoop *snp);
119 static int		snp_in (struct snoop *snp, char *buf, int n);
120 static int		snp_modevent (module_t mod, int what, void *arg);
121 
122 static int
123 snplclose(struct tty *tp, int flag)
124 {
125 	struct snoop *snp;
126 	int error;
127 
128 	snp = tp->t_sc;
129 	error = snp_down(snp);
130 	if (error != 0)
131 		return (error);
132 	error = ttylclose(tp, flag);
133 	return (error);
134 }
135 
136 static int
137 snplwrite(struct tty *tp, struct uio *uio, int flag)
138 {
139 	struct iovec iov;
140 	struct uio uio2;
141 	struct snoop *snp;
142 	int error, ilen;
143 	char *ibuf;
144 
145 	error = 0;
146 	ibuf = NULL;
147 	snp = tp->t_sc;
148 	while (uio->uio_resid > 0) {
149 		ilen = imin(512, uio->uio_resid);
150 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
151 		error = uiomove(ibuf, ilen, uio);
152 		if (error != 0)
153 			break;
154 		snp_in(snp, ibuf, ilen);
155 		/* Hackish, but probably the least of all evils. */
156 		iov.iov_base = ibuf;
157 		iov.iov_len = ilen;
158 		uio2.uio_iov = &iov;
159 		uio2.uio_iovcnt = 1;
160 		uio2.uio_offset = 0;
161 		uio2.uio_resid = ilen;
162 		uio2.uio_segflg = UIO_SYSSPACE;
163 		uio2.uio_rw = UIO_WRITE;
164 		uio2.uio_td = uio->uio_td;
165 		error = ttwrite(tp, &uio2, flag);
166 		if (error != 0)
167 			break;
168 		free(ibuf, M_SNP);
169 		ibuf = NULL;
170 	}
171 	if (ibuf != NULL)
172 		free(ibuf, M_SNP);
173 	return (error);
174 }
175 
176 static struct tty *
177 snpdevtotty(dev_t dev)
178 {
179 	if ((dev_dflags(dev) & D_TTY) == 0)
180 		return (NULL);
181 	return (dev->si_tty);
182 }
183 
184 #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
185 				 * interactive mode write is 3 bytes
186 				 * length for function keys...
187 				 */
188 
189 static int
190 snpwrite(dev_t dev, struct uio *uio, int flag)
191 {
192 	struct snoop *snp;
193 	struct tty *tp;
194 	int error, i, len;
195 	unsigned char c[SNP_INPUT_BUF];
196 
197 	snp = dev->si_drv1;
198 	tp = snp->snp_tty;
199 	if (tp == NULL)
200 		return (EIO);
201 	if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
202 	    tp->t_line == snooplinedisc)
203 		goto tty_input;
204 
205 	printf("Snoop: attempt to write to bad tty.\n");
206 	return (EIO);
207 
208 tty_input:
209 	if (!(tp->t_state & TS_ISOPEN))
210 		return (EIO);
211 
212 	while (uio->uio_resid > 0) {
213 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
214 		if ((error = uiomove(c, len, uio)) != 0)
215 			return (error);
216 		for (i=0; i < len; i++) {
217 			if (ttyinput(c[i], tp))
218 				return (EIO);
219 		}
220 	}
221 	return (0);
222 }
223 
224 
225 static int
226 snpread(dev_t dev, struct uio *uio, int flag)
227 {
228 	struct snoop *snp;
229 	int error, len, n, nblen;
230 	caddr_t from;
231 	char *nbuf;
232 
233 	snp = dev->si_drv1;
234 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
235 	    ("snoop buffer error"));
236 
237 	if (snp->snp_tty == NULL)
238 		return (EIO);
239 
240 	snp->snp_flags &= ~SNOOP_RWAIT;
241 
242 	do {
243 		if (snp->snp_len == 0) {
244 			if (flag & IO_NDELAY)
245 				return (EWOULDBLOCK);
246 			snp->snp_flags |= SNOOP_RWAIT;
247 			error = tsleep((caddr_t)snp, PCATCH, "snprd", 0);
248 			if (error != 0)
249 				return (error);
250 		}
251 	} while (snp->snp_len == 0);
252 
253 	n = snp->snp_len;
254 
255 	error = 0;
256 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
257 		len = min((unsigned)uio->uio_resid, snp->snp_len);
258 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
259 		if (len == 0)
260 			break;
261 
262 		error = uiomove(from, len, uio);
263 		snp->snp_base += len;
264 		snp->snp_len -= len;
265 	}
266 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
267 		snp->snp_flags &= ~SNOOP_OFLOW;
268 	}
269 	crit_enter();
270 	nblen = snp->snp_blen;
271 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
272 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
273 			nblen = nblen / 2;
274 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
275 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
276 			free(snp->snp_buf, M_SNP);
277 			snp->snp_buf = nbuf;
278 			snp->snp_blen = nblen;
279 			snp->snp_base = 0;
280 		}
281 	}
282 	crit_exit();
283 
284 	return (error);
285 }
286 
287 static int
288 snp_in(struct snoop *snp, char *buf, int n)
289 {
290 	int s_free, s_tail;
291 	int len, nblen;
292 	caddr_t from, to;
293 	char *nbuf;
294 
295 	KASSERT(n >= 0, ("negative snoop char count"));
296 
297 	if (n == 0)
298 		return (0);
299 
300 	if (snp->snp_flags & SNOOP_DOWN) {
301 		printf("Snoop: more data to down interface.\n");
302 		return (0);
303 	}
304 
305 	if (snp->snp_flags & SNOOP_OFLOW) {
306 		printf("Snoop: buffer overflow.\n");
307 		/*
308 		 * On overflow we just repeat the standart close
309 		 * procedure...yes , this is waste of space but.. Then next
310 		 * read from device will fail if one would recall he is
311 		 * snooping and retry...
312 		 */
313 
314 		return (snp_down(snp));
315 	}
316 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
317 	s_free = snp->snp_blen - snp->snp_len;
318 
319 
320 	if (n > s_free) {
321 		crit_enter();
322 		nblen = snp->snp_blen;
323 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
324 			nblen = snp->snp_blen * 2;
325 			s_free = nblen - (snp->snp_len + snp->snp_base);
326 		}
327 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
328 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
329 			free(snp->snp_buf, M_SNP);
330 			snp->snp_buf = nbuf;
331 			snp->snp_blen = nblen;
332 			snp->snp_base = 0;
333 		} else {
334 			snp->snp_flags |= SNOOP_OFLOW;
335 			if (snp->snp_flags & SNOOP_RWAIT) {
336 				snp->snp_flags &= ~SNOOP_RWAIT;
337 				wakeup((caddr_t)snp);
338 			}
339 			crit_exit();
340 			return (0);
341 		}
342 		crit_exit();
343 	}
344 	if (n > s_tail) {
345 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
346 		to = (caddr_t)(snp->snp_buf);
347 		len = snp->snp_len;
348 		bcopy(from, to, len);
349 		snp->snp_base = 0;
350 	}
351 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
352 	bcopy(buf, to, n);
353 	snp->snp_len += n;
354 
355 	if (snp->snp_flags & SNOOP_RWAIT) {
356 		snp->snp_flags &= ~SNOOP_RWAIT;
357 		wakeup((caddr_t)snp);
358 	}
359 	selwakeup(&snp->snp_sel);
360 	snp->snp_sel.si_pid = 0;
361 
362 	return (n);
363 }
364 
365 static int
366 snpopen(dev_t dev, int flag, int mode, d_thread_t *td)
367 {
368 	struct snoop *snp;
369 
370 	if (dev->si_drv1 == NULL) {
371 		make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
372 		    0600, "snp%d", minor(dev));
373 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
374 		    M_WAITOK | M_ZERO);
375 	} else {
376 		return (EBUSY);
377 	}
378 
379 	/*
380 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
381 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
382 	 */
383 	snp->snp_flags = SNOOP_OPEN;
384 
385 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
386 	snp->snp_blen = SNOOP_MINLEN;
387 	snp->snp_base = 0;
388 	snp->snp_len = 0;
389 
390 	/*
391 	 * snp_tty == NULL  is for inactive snoop devices.
392 	 */
393 	snp->snp_tty = NULL;
394 	snp->snp_target = NODEV;
395 
396 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
397 	return (0);
398 }
399 
400 
401 static int
402 snp_detach(struct snoop *snp)
403 {
404 	struct tty *tp;
405 
406 	snp->snp_base = 0;
407 	snp->snp_len = 0;
408 
409 	/*
410 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
411 	 * change it anyway.
412 	 */
413 	tp = snp->snp_tty;
414 	if (tp == NULL)
415 		goto detach_notty;
416 
417 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
418 	    tp->t_line == snooplinedisc) {
419 		tp->t_sc = NULL;
420 		tp->t_state &= ~TS_SNOOP;
421 		tp->t_line = snp->snp_olddisc;
422 	} else
423 		printf("Snoop: bad attached tty data.\n");
424 
425 	snp->snp_tty = NULL;
426 	snp->snp_target = NODEV;
427 
428 detach_notty:
429 	selwakeup(&snp->snp_sel);
430 	snp->snp_sel.si_pid = 0;
431 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
432 		free(snp, M_SNP);
433 
434 	return (0);
435 }
436 
437 static int
438 snpclose(dev_t dev, int flags, int fmt, d_thread_t *td)
439 {
440 	struct snoop *snp;
441 
442 	snp = dev->si_drv1;
443 	snp->snp_blen = 0;
444 	LIST_REMOVE(snp, snp_list);
445 	free(snp->snp_buf, M_SNP);
446 	snp->snp_flags &= ~SNOOP_OPEN;
447 	dev->si_drv1 = NULL;
448 
449 	return (snp_detach(snp));
450 }
451 
452 static int
453 snp_down(struct snoop *snp)
454 {
455 
456 	if (snp->snp_blen != SNOOP_MINLEN) {
457 		free(snp->snp_buf, M_SNP);
458 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
459 		snp->snp_blen = SNOOP_MINLEN;
460 	}
461 	snp->snp_flags |= SNOOP_DOWN;
462 
463 	return (snp_detach(snp));
464 }
465 
466 static int
467 snpioctl(dev_t dev, u_long cmd, caddr_t data, int flags, d_thread_t *td)
468 {
469 	struct snoop *snp;
470 	struct tty *tp, *tpo;
471 	dev_t tdev;
472 
473 	snp = dev->si_drv1;
474 	switch (cmd) {
475 	case SNPSTTY:
476 		tdev = udev2dev(*((udev_t *)data), 0);
477 		if (tdev == NODEV)
478 			return (snp_down(snp));
479 
480 		tp = snpdevtotty(tdev);
481 		if (!tp)
482 			return (EINVAL);
483 		if (tp->t_state & TS_SNOOP)
484 			return (EBUSY);
485 
486 		crit_enter();
487 
488 		if (snp->snp_target == NODEV) {
489 			tpo = snp->snp_tty;
490 			if (tpo)
491 				tpo->t_state &= ~TS_SNOOP;
492 		}
493 
494 		tp->t_sc = (caddr_t)snp;
495 		tp->t_state |= TS_SNOOP;
496 		snp->snp_olddisc = tp->t_line;
497 		tp->t_line = snooplinedisc;
498 		snp->snp_tty = tp;
499 		snp->snp_target = tdev;
500 
501 		/*
502 		 * Clean overflow and down flags -
503 		 * we'll have a chance to get them in the future :)))
504 		 */
505 		snp->snp_flags &= ~SNOOP_OFLOW;
506 		snp->snp_flags &= ~SNOOP_DOWN;
507 		crit_exit();
508 		break;
509 
510 	case SNPGTTY:
511 		/*
512 		 * We keep snp_target field specially to make
513 		 * SNPGTTY happy, else we can't know what is device
514 		 * major/minor for tty.
515 		 */
516 		*((dev_t *)data) = snp->snp_target;
517 		break;
518 
519 	case FIONBIO:
520 		break;
521 
522 	case FIOASYNC:
523 		if (*(int *)data)
524 			snp->snp_flags |= SNOOP_ASYNC;
525 		else
526 			snp->snp_flags &= ~SNOOP_ASYNC;
527 		break;
528 
529 	case FIONREAD:
530 		crit_enter();
531 		if (snp->snp_tty != NULL)
532 			*(int *)data = snp->snp_len;
533 		else
534 			if (snp->snp_flags & SNOOP_DOWN) {
535 				if (snp->snp_flags & SNOOP_OFLOW)
536 					*(int *)data = SNP_OFLOW;
537 				else
538 					*(int *)data = SNP_TTYCLOSE;
539 			} else {
540 				*(int *)data = SNP_DETACH;
541 			}
542 		crit_exit();
543 		break;
544 
545 	default:
546 		return (ENOTTY);
547 	}
548 	return (0);
549 }
550 
551 static int
552 snppoll(dev_t dev, int events, d_thread_t *td)
553 {
554 	struct snoop *snp;
555 	int revents;
556 
557 	snp = dev->si_drv1;
558 	revents = 0;
559 	/*
560 	 * If snoop is down, we don't want to poll() forever so we return 1.
561 	 * Caller should see if we down via FIONREAD ioctl().  The last should
562 	 * return -1 to indicate down state.
563 	 */
564 	if (events & (POLLIN | POLLRDNORM)) {
565 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
566 			revents |= events & (POLLIN | POLLRDNORM);
567 		else
568 			selrecord(td, &snp->snp_sel);
569 	}
570 	return (revents);
571 }
572 
573 static int
574 snp_modevent(module_t mod, int type, void *data)
575 {
576 
577 	switch (type) {
578 	case MOD_LOAD:
579 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
580 		cdevsw_add(&snp_cdevsw, 0, 0);
581 		break;
582 	case MOD_UNLOAD:
583 		if (!LIST_EMPTY(&snp_sclist))
584 			return (EBUSY);
585 		ldisc_deregister(snooplinedisc);
586 		cdevsw_remove(&snp_cdevsw, 0, 0);
587 		break;
588 	default:
589 		break;
590 	}
591 	return (0);
592 }
593 
594 static moduledata_t snp_mod = {
595         "snp",
596         snp_modevent,
597         NULL
598 };
599 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR);
600