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