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