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