xref: /freebsd/sys/dev/snp/snp.c (revision 101f105db2aec18214a7e93a8de52b28f1cddc03)
1dde8a05bSUgen J.S. Antsilevich /*
2dde8a05bSUgen J.S. Antsilevich  * Copyright (c) 1995 Ugen J.S.Antsilevich
3dde8a05bSUgen J.S. Antsilevich  *
4dde8a05bSUgen J.S. Antsilevich  * Redistribution and use in source forms, with and without modification,
5dde8a05bSUgen J.S. Antsilevich  * are permitted provided that this entire comment appears intact.
6dde8a05bSUgen J.S. Antsilevich  *
7dde8a05bSUgen J.S. Antsilevich  * Redistribution in binary form may occur without any restrictions.
8dde8a05bSUgen J.S. Antsilevich  * Obviously, it would be nice if you gave credit where credit is due
9dde8a05bSUgen J.S. Antsilevich  * but requiring it would be too onerous.
10dde8a05bSUgen J.S. Antsilevich  *
11dde8a05bSUgen J.S. Antsilevich  * This software is provided ``AS IS'' without any warranties of any kind.
12dde8a05bSUgen J.S. Antsilevich  *
13dde8a05bSUgen J.S. Antsilevich  * Snoop stuff.
140625ba2fSGary Palmer  *
15c3aac50fSPeter Wemm  * $FreeBSD$
16dde8a05bSUgen J.S. Antsilevich  */
17dde8a05bSUgen J.S. Antsilevich 
18dde8a05bSUgen J.S. Antsilevich #include <sys/param.h>
19dde8a05bSUgen J.S. Antsilevich #include <sys/systm.h>
2071455815SBruce Evans #include <sys/filio.h>
21a1c995b6SPoul-Henning Kamp #include <sys/malloc.h>
22dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h>
23dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h>
24659ffb48SPeter Wemm #include <sys/poll.h>
25dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h>
26ac60b28dSDima Dorfman #include <sys/queue.h>
2787f6c662SJulian Elischer #include <sys/snoop.h>
28d96bc99dSBruce Evans #include <sys/vnode.h>
2987f6c662SJulian Elischer 
30ddf5b796SDima Dorfman static	l_close_t	snplclose;
31f09f49f1SDima Dorfman static	l_write_t	snplwrite;
3287f6c662SJulian Elischer static	d_open_t	snpopen;
3387f6c662SJulian Elischer static	d_close_t	snpclose;
3487f6c662SJulian Elischer static	d_read_t	snpread;
3587f6c662SJulian Elischer static	d_write_t	snpwrite;
3687f6c662SJulian Elischer static	d_ioctl_t	snpioctl;
37659ffb48SPeter Wemm static	d_poll_t	snppoll;
3887f6c662SJulian Elischer 
3953ac6efbSJulian Elischer #define CDEV_MAJOR 53
404e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = {
414e2f199eSPoul-Henning Kamp 	/* open */	snpopen,
424e2f199eSPoul-Henning Kamp 	/* close */	snpclose,
434e2f199eSPoul-Henning Kamp 	/* read */	snpread,
444e2f199eSPoul-Henning Kamp 	/* write */	snpwrite,
454e2f199eSPoul-Henning Kamp 	/* ioctl */	snpioctl,
464e2f199eSPoul-Henning Kamp 	/* poll */	snppoll,
474e2f199eSPoul-Henning Kamp 	/* mmap */	nommap,
484e2f199eSPoul-Henning Kamp 	/* strategy */	nostrategy,
494e2f199eSPoul-Henning Kamp 	/* name */	"snp",
504e2f199eSPoul-Henning Kamp 	/* maj */	CDEV_MAJOR,
514e2f199eSPoul-Henning Kamp 	/* dump */	nodump,
524e2f199eSPoul-Henning Kamp 	/* psize */	nopsize,
534e2f199eSPoul-Henning Kamp 	/* flags */	0,
544e2f199eSPoul-Henning Kamp };
5587f6c662SJulian Elischer 
56f09f49f1SDima Dorfman static struct linesw snpdisc = {
57ddf5b796SDima Dorfman 	ttyopen,	snplclose,	ttread,		snplwrite,
58f09f49f1SDima Dorfman 	l_nullioctl,	ttyinput,	ttstart,	ttymodem
59f09f49f1SDima Dorfman };
6053ac6efbSJulian Elischer 
61101f105dSDima Dorfman /*
62101f105dSDima Dorfman  * This is the main snoop per-device structure.
63101f105dSDima Dorfman  */
64101f105dSDima Dorfman struct snoop {
65101f105dSDima Dorfman 	LIST_ENTRY(snoop)	snp_list;	/* List glue. */
66101f105dSDima Dorfman 	dev_t			snp_target;	/* Target tty device. */
67101f105dSDima Dorfman 	struct tty		*snp_tty;	/* Target tty pointer. */
68101f105dSDima Dorfman 	u_long			 snp_len;	/* Possible length. */
69101f105dSDima Dorfman 	u_long			 snp_base;	/* Data base. */
70101f105dSDima Dorfman 	u_long			 snp_blen;	/* Used length. */
71101f105dSDima Dorfman 	caddr_t			 snp_buf;	/* Allocation pointer. */
72101f105dSDima Dorfman 	int			 snp_flags;	/* Flags. */
73101f105dSDima Dorfman 	struct selinfo		 snp_sel;	/* Select info. */
74101f105dSDima Dorfman 	int			 snp_olddisc;	/* Old line discipline. */
75101f105dSDima Dorfman };
76101f105dSDima Dorfman 
77101f105dSDima Dorfman /*
78101f105dSDima Dorfman  * Possible flags.
79101f105dSDima Dorfman  */
80101f105dSDima Dorfman #define SNOOP_ASYNC		0x0002
81101f105dSDima Dorfman #define SNOOP_OPEN		0x0004
82101f105dSDima Dorfman #define SNOOP_RWAIT		0x0008
83101f105dSDima Dorfman #define SNOOP_OFLOW		0x0010
84101f105dSDima Dorfman #define SNOOP_DOWN		0x0020
85101f105dSDima Dorfman 
86101f105dSDima Dorfman /*
87101f105dSDima Dorfman  * Other constants.
88101f105dSDima Dorfman  */
89101f105dSDima Dorfman #define SNOOP_MINLEN		(4*1024)	/* This should be power of 2.
90101f105dSDima Dorfman 						 * 4K tested to be the minimum
91101f105dSDima Dorfman 						 * for which on normal tty
92101f105dSDima Dorfman 						 * usage there is no need to
93101f105dSDima Dorfman 						 * allocate more.
94101f105dSDima Dorfman 						 */
95101f105dSDima Dorfman #define SNOOP_MAXLEN		(64*1024)	/* This one also,64K enough
96101f105dSDima Dorfman 						 * If we grow more,something
97101f105dSDima Dorfman 						 * really bad in this world..
98101f105dSDima Dorfman 						 */
99101f105dSDima Dorfman 
100959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
10147eaa5f5SDima Dorfman /*
10247eaa5f5SDima Dorfman  * The number of the "snoop" line discipline.  This gets determined at
10347eaa5f5SDima Dorfman  * module load time.
10447eaa5f5SDima Dorfman  */
105f09f49f1SDima Dorfman static int snooplinedisc;
106f09f49f1SDima Dorfman 
107ac60b28dSDima Dorfman 
108ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
109ac60b28dSDima Dorfman 
110f09f49f1SDima Dorfman static struct tty	*snpdevtotty __P((dev_t dev));
111f09f49f1SDima Dorfman static void		snp_clone __P((void *arg, char *name,
112f09f49f1SDima Dorfman 			    int namelen, dev_t *dev));
113f09f49f1SDima Dorfman static int		snp_detach __P((struct snoop *snp));
114f09f49f1SDima Dorfman static int		snp_down __P((struct snoop *snp));
115f09f49f1SDima Dorfman static int		snp_in __P((struct snoop *snp, char *buf, int n));
116f09f49f1SDima Dorfman static int		snp_modevent __P((module_t mod, int what, void *arg));
11747eaa5f5SDima Dorfman 
11847eaa5f5SDima Dorfman static int
119ddf5b796SDima Dorfman snplclose(tp, flag)
120ddf5b796SDima Dorfman 	struct tty *tp;
121ddf5b796SDima Dorfman 	int flag;
122ddf5b796SDima Dorfman {
123ddf5b796SDima Dorfman 	struct snoop *snp;
124ddf5b796SDima Dorfman 	int error;
125ddf5b796SDima Dorfman 
126ddf5b796SDima Dorfman 	snp = tp->t_sc;
127ddf5b796SDima Dorfman 	error = snp_down(snp);
128ddf5b796SDima Dorfman 	if (error != 0)
129ddf5b796SDima Dorfman 		return (error);
130ddf5b796SDima Dorfman 	error = ttylclose(tp, flag);
131ddf5b796SDima Dorfman 	return (error);
132ddf5b796SDima Dorfman }
133ddf5b796SDima Dorfman 
134ddf5b796SDima Dorfman static int
135f09f49f1SDima Dorfman snplwrite(tp, uio, flag)
136f09f49f1SDima Dorfman 	struct tty *tp;
137f09f49f1SDima Dorfman 	struct uio *uio;
138f09f49f1SDima Dorfman 	int flag;
13947eaa5f5SDima Dorfman {
14047eaa5f5SDima Dorfman 	struct iovec iov;
14147eaa5f5SDima Dorfman 	struct uio uio2;
142f09f49f1SDima Dorfman 	struct snoop *snp;
143f09f49f1SDima Dorfman 	int error, ilen;
144f09f49f1SDima Dorfman 	char ibuf[512];
14547eaa5f5SDima Dorfman 
146f09f49f1SDima Dorfman 	snp = tp->t_sc;
14787826386SDima Dorfman 	while (uio->uio_resid > 0) {
14887826386SDima Dorfman 		ilen = imin(sizeof(ibuf), uio->uio_resid);
14947eaa5f5SDima Dorfman 		error = uiomove(ibuf, ilen, uio);
150f09f49f1SDima Dorfman 		if (error != 0)
151ddf5b796SDima Dorfman 			return (error);
152f09f49f1SDima Dorfman 		snp_in(snp, ibuf, ilen);
153f09f49f1SDima Dorfman 		/* Hackish, but probably the least of all evils. */
15447eaa5f5SDima Dorfman 		iov.iov_base = ibuf;
15547eaa5f5SDima Dorfman 		iov.iov_len = ilen;
15647eaa5f5SDima Dorfman 		uio2.uio_iov = &iov;
15747eaa5f5SDima Dorfman 		uio2.uio_iovcnt = 1;
15847eaa5f5SDima Dorfman 		uio2.uio_offset = 0;
15947eaa5f5SDima Dorfman 		uio2.uio_resid = ilen;
16047eaa5f5SDima Dorfman 		uio2.uio_segflg = UIO_SYSSPACE;
16147eaa5f5SDima Dorfman 		uio2.uio_rw = UIO_WRITE;
16247eaa5f5SDima Dorfman 		uio2.uio_procp = uio->uio_procp;
16347eaa5f5SDima Dorfman 		error = ttwrite(tp, &uio2, flag);
164f09f49f1SDima Dorfman 		if (error != 0)
16547eaa5f5SDima Dorfman 			return (error);
16647eaa5f5SDima Dorfman 	}
167ddf5b796SDima Dorfman 	return (0);
168ddf5b796SDima Dorfman }
16947eaa5f5SDima Dorfman 
17077f77631SPaul Traina static struct tty *
171444f003cSBruce Evans snpdevtotty(dev)
17277f77631SPaul Traina 	dev_t dev;
17377f77631SPaul Traina {
174444f003cSBruce Evans 	struct cdevsw *cdp;
17577f77631SPaul Traina 
1764be2eb8cSPoul-Henning Kamp 	cdp = devsw(dev);
17787826386SDima Dorfman 	if (cdp == NULL || (cdp->d_flags & D_TTY) == 0)
178444f003cSBruce Evans 		return (NULL);
17987826386SDima Dorfman 	return (dev->si_tty);
18077f77631SPaul Traina }
18177f77631SPaul Traina 
1820739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
1830739a0dcSUgen J.S. Antsilevich 				 * interactive mode write is 3 bytes
1840739a0dcSUgen J.S. Antsilevich 				 * length for function keys...
1850739a0dcSUgen J.S. Antsilevich 				 */
1860739a0dcSUgen J.S. Antsilevich 
18787f6c662SJulian Elischer static int
1880739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag)
1890739a0dcSUgen J.S. Antsilevich 	dev_t dev;
1900739a0dcSUgen J.S. Antsilevich 	struct uio *uio;
1910739a0dcSUgen J.S. Antsilevich 	int flag;
1920739a0dcSUgen J.S. Antsilevich {
193f09f49f1SDima Dorfman 	struct snoop *snp;
1940739a0dcSUgen J.S. Antsilevich 	struct tty *tp;
195f09f49f1SDima Dorfman 	int error, i, len;
1960739a0dcSUgen J.S. Antsilevich 	char c[SNP_INPUT_BUF];
1970739a0dcSUgen J.S. Antsilevich 
198f09f49f1SDima Dorfman 	snp = dev->si_drv1;
1990739a0dcSUgen J.S. Antsilevich 	tp = snp->snp_tty;
200f09f49f1SDima Dorfman 	if (tp == NULL)
201f09f49f1SDima Dorfman 		return (EIO);
2020739a0dcSUgen J.S. Antsilevich 	if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
203f09f49f1SDima Dorfman 	    tp->t_line == snooplinedisc)
2040739a0dcSUgen J.S. Antsilevich 		goto tty_input;
2050739a0dcSUgen J.S. Antsilevich 
2060739a0dcSUgen J.S. Antsilevich 	printf("Snoop: attempt to write to bad tty.\n");
2070739a0dcSUgen J.S. Antsilevich 	return (EIO);
2080739a0dcSUgen J.S. Antsilevich 
2090739a0dcSUgen J.S. Antsilevich tty_input:
2100739a0dcSUgen J.S. Antsilevich 	if (!(tp->t_state & TS_ISOPEN))
2110739a0dcSUgen J.S. Antsilevich 		return (EIO);
2120739a0dcSUgen J.S. Antsilevich 
2130739a0dcSUgen J.S. Antsilevich 	while (uio->uio_resid > 0) {
21487826386SDima Dorfman 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
2150739a0dcSUgen J.S. Antsilevich 		if ((error = uiomove(c, len, uio)) != 0)
2160739a0dcSUgen J.S. Antsilevich 			return (error);
2170739a0dcSUgen J.S. Antsilevich 		for (i=0; i < len; i++) {
2180739a0dcSUgen J.S. Antsilevich 			if (ttyinput(c[i], tp))
2190739a0dcSUgen J.S. Antsilevich 				return (EIO);
2200739a0dcSUgen J.S. Antsilevich 		}
2210739a0dcSUgen J.S. Antsilevich 	}
222f09f49f1SDima Dorfman 	return (0);
2230739a0dcSUgen J.S. Antsilevich }
2240739a0dcSUgen J.S. Antsilevich 
2250739a0dcSUgen J.S. Antsilevich 
22687f6c662SJulian Elischer static int
227dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag)
228dde8a05bSUgen J.S. Antsilevich 	dev_t dev;
229dde8a05bSUgen J.S. Antsilevich 	struct uio *uio;
230dde8a05bSUgen J.S. Antsilevich 	int flag;
231dde8a05bSUgen J.S. Antsilevich {
232f09f49f1SDima Dorfman 	struct snoop *snp;
233f09f49f1SDima Dorfman 	int error, len, n, nblen, s;
234dde8a05bSUgen J.S. Antsilevich 	caddr_t from;
235dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
236dde8a05bSUgen J.S. Antsilevich 
237f09f49f1SDima Dorfman 	snp = dev->si_drv1;
2385526d2d9SEivind Eklund 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
2395526d2d9SEivind Eklund 	    ("snoop buffer error"));
24077f77631SPaul Traina 
2410739a0dcSUgen J.S. Antsilevich 	if (snp->snp_tty == NULL)
242dde8a05bSUgen J.S. Antsilevich 		return (EIO);
243dde8a05bSUgen J.S. Antsilevich 
244dde8a05bSUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_RWAIT;
245dde8a05bSUgen J.S. Antsilevich 
246dde8a05bSUgen J.S. Antsilevich 	do {
247dde8a05bSUgen J.S. Antsilevich 		if (snp->snp_len == 0) {
248d96bc99dSBruce Evans 			if (flag & IO_NDELAY)
249d96bc99dSBruce Evans 				return (EWOULDBLOCK);
250dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_RWAIT;
25176e90dbcSBrian Feldman 			tsleep((caddr_t)snp, (PZERO + 1) | PCATCH, "snprd", 0);
252dde8a05bSUgen J.S. Antsilevich 		}
253dde8a05bSUgen J.S. Antsilevich 	} while (snp->snp_len == 0);
254dde8a05bSUgen J.S. Antsilevich 
255019b4d63SUgen J.S. Antsilevich 	n = snp->snp_len;
256dde8a05bSUgen J.S. Antsilevich 
257f09f49f1SDima Dorfman 	error = 0;
258dde8a05bSUgen J.S. Antsilevich 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
25987826386SDima Dorfman 		len = min((unsigned)uio->uio_resid, snp->snp_len);
260dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
261dde8a05bSUgen J.S. Antsilevich 		if (len == 0)
262dde8a05bSUgen J.S. Antsilevich 			break;
263dde8a05bSUgen J.S. Antsilevich 
264dde8a05bSUgen J.S. Antsilevich 		error = uiomove(from, len, uio);
265dde8a05bSUgen J.S. Antsilevich 		snp->snp_base += len;
266dde8a05bSUgen J.S. Antsilevich 		snp->snp_len -= len;
267dde8a05bSUgen J.S. Antsilevich 	}
268019b4d63SUgen J.S. Antsilevich 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
269dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
270019b4d63SUgen J.S. Antsilevich 	}
271dde8a05bSUgen J.S. Antsilevich 	s = spltty();
272dde8a05bSUgen J.S. Antsilevich 	nblen = snp->snp_blen;
273dde8a05bSUgen J.S. Antsilevich 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
274f09f49f1SDima Dorfman 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
275dde8a05bSUgen J.S. Antsilevich 			nblen = nblen / 2;
27676e90dbcSBrian Feldman 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
277dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
27876e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
279dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
280dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
281dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
282dde8a05bSUgen J.S. Antsilevich 		}
283dde8a05bSUgen J.S. Antsilevich 	}
284dde8a05bSUgen J.S. Antsilevich 	splx(s);
285dde8a05bSUgen J.S. Antsilevich 
286f09f49f1SDima Dorfman 	return (error);
287dde8a05bSUgen J.S. Antsilevich }
288dde8a05bSUgen J.S. Antsilevich 
289f09f49f1SDima Dorfman static int
290f09f49f1SDima Dorfman snp_in(snp, buf, n)
291dde8a05bSUgen J.S. Antsilevich 	struct snoop *snp;
292dde8a05bSUgen J.S. Antsilevich 	char *buf;
293dde8a05bSUgen J.S. Antsilevich 	int n;
294dde8a05bSUgen J.S. Antsilevich {
295dde8a05bSUgen J.S. Antsilevich 	int s_free, s_tail;
296dde8a05bSUgen J.S. Antsilevich 	int s, len, nblen;
297dde8a05bSUgen J.S. Antsilevich 	caddr_t from, to;
298dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
299dde8a05bSUgen J.S. Antsilevich 
300219cbf59SEivind Eklund 	KASSERT(n >= 0, ("negative snoop char count"));
301dde8a05bSUgen J.S. Antsilevich 
302dde8a05bSUgen J.S. Antsilevich 	if (n == 0)
303f09f49f1SDima Dorfman 		return (0);
304dde8a05bSUgen J.S. Antsilevich 
305019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_DOWN) {
306019b4d63SUgen J.S. Antsilevich 		printf("Snoop: more data to down interface.\n");
307f09f49f1SDima Dorfman 		return (0);
308019b4d63SUgen J.S. Antsilevich 	}
309964587caSUgen J.S. Antsilevich 
310019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_OFLOW) {
311019b4d63SUgen J.S. Antsilevich 		printf("Snoop: buffer overflow.\n");
312019b4d63SUgen J.S. Antsilevich 		/*
313019b4d63SUgen J.S. Antsilevich 		 * On overflow we just repeat the standart close
314019b4d63SUgen J.S. Antsilevich 		 * procedure...yes , this is waste of space but.. Then next
315019b4d63SUgen J.S. Antsilevich 		 * read from device will fail if one would recall he is
316019b4d63SUgen J.S. Antsilevich 		 * snooping and retry...
317019b4d63SUgen J.S. Antsilevich 		 */
318dde8a05bSUgen J.S. Antsilevich 
319f09f49f1SDima Dorfman 		return (snp_down(snp));
320019b4d63SUgen J.S. Antsilevich 	}
321019b4d63SUgen J.S. Antsilevich 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
322019b4d63SUgen J.S. Antsilevich 	s_free = snp->snp_blen - snp->snp_len;
323dde8a05bSUgen J.S. Antsilevich 
324dde8a05bSUgen J.S. Antsilevich 
325dde8a05bSUgen J.S. Antsilevich 	if (n > s_free) {
326dde8a05bSUgen J.S. Antsilevich 		s = spltty();
327dde8a05bSUgen J.S. Antsilevich 		nblen = snp->snp_blen;
328dde8a05bSUgen J.S. Antsilevich 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
329dde8a05bSUgen J.S. Antsilevich 			nblen = snp->snp_blen * 2;
330dde8a05bSUgen J.S. Antsilevich 			s_free = nblen - (snp->snp_len + snp->snp_base);
331dde8a05bSUgen J.S. Antsilevich 		}
33276e90dbcSBrian Feldman 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
333dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
33476e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
335dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
336dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
337dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
338dde8a05bSUgen J.S. Antsilevich 		} else {
339dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_OFLOW;
340dde8a05bSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_RWAIT) {
341dde8a05bSUgen J.S. Antsilevich 				snp->snp_flags &= ~SNOOP_RWAIT;
342dde8a05bSUgen J.S. Antsilevich 				wakeup((caddr_t)snp);
343dde8a05bSUgen J.S. Antsilevich 			}
344dde8a05bSUgen J.S. Antsilevich 			splx(s);
345f09f49f1SDima Dorfman 			return (0);
346dde8a05bSUgen J.S. Antsilevich 		}
347dde8a05bSUgen J.S. Antsilevich 		splx(s);
348dde8a05bSUgen J.S. Antsilevich 	}
349dde8a05bSUgen J.S. Antsilevich 	if (n > s_tail) {
350dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
351dde8a05bSUgen J.S. Antsilevich 		to = (caddr_t)(snp->snp_buf);
352dde8a05bSUgen J.S. Antsilevich 		len = snp->snp_len;
353dde8a05bSUgen J.S. Antsilevich 		bcopy(from, to, len);
354dde8a05bSUgen J.S. Antsilevich 		snp->snp_base = 0;
355dde8a05bSUgen J.S. Antsilevich 	}
356dde8a05bSUgen J.S. Antsilevich 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
357dde8a05bSUgen J.S. Antsilevich 	bcopy(buf, to, n);
358dde8a05bSUgen J.S. Antsilevich 	snp->snp_len += n;
359dde8a05bSUgen J.S. Antsilevich 
360dde8a05bSUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_RWAIT) {
361dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_RWAIT;
362dde8a05bSUgen J.S. Antsilevich 		wakeup((caddr_t)snp);
363dde8a05bSUgen J.S. Antsilevich 	}
364dde8a05bSUgen J.S. Antsilevich 	selwakeup(&snp->snp_sel);
365dde8a05bSUgen J.S. Antsilevich 	snp->snp_sel.si_pid = 0;
366dde8a05bSUgen J.S. Antsilevich 
367f09f49f1SDima Dorfman 	return (n);
368dde8a05bSUgen J.S. Antsilevich }
369dde8a05bSUgen J.S. Antsilevich 
37087f6c662SJulian Elischer static int
371dde8a05bSUgen J.S. Antsilevich snpopen(dev, flag, mode, p)
372dde8a05bSUgen J.S. Antsilevich 	dev_t dev;
373dde8a05bSUgen J.S. Antsilevich 	int flag, mode;
374dde8a05bSUgen J.S. Antsilevich 	struct proc *p;
375dde8a05bSUgen J.S. Antsilevich {
376dde8a05bSUgen J.S. Antsilevich 	struct snoop *snp;
37776e90dbcSBrian Feldman 	int error;
378dde8a05bSUgen J.S. Antsilevich 
379f711d546SPoul-Henning Kamp 	if ((error = suser(p)) != 0)
380dde8a05bSUgen J.S. Antsilevich 		return (error);
381dde8a05bSUgen J.S. Antsilevich 
38276e90dbcSBrian Feldman 	if (dev->si_drv1 == NULL) {
38325c7870eSDima Dorfman 		if (!(dev->si_flags & SI_NAMED))
38425c7870eSDima Dorfman 			make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
38525c7870eSDima Dorfman 			    0600, "snp%d", dev2unit(dev));
386f09f49f1SDima Dorfman 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
387f09f49f1SDima Dorfman 		    M_WAITOK | M_ZERO);
38876e90dbcSBrian Feldman 	} else
38976e90dbcSBrian Feldman 		return (EBUSY);
39077f77631SPaul Traina 
391019b4d63SUgen J.S. Antsilevich 	/*
392019b4d63SUgen J.S. Antsilevich 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
393019b4d63SUgen J.S. Antsilevich 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
394019b4d63SUgen J.S. Antsilevich 	 */
395019b4d63SUgen J.S. Antsilevich 	snp->snp_flags = SNOOP_OPEN;
396dde8a05bSUgen J.S. Antsilevich 
39776e90dbcSBrian Feldman 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
398dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = SNOOP_MINLEN;
399dde8a05bSUgen J.S. Antsilevich 	snp->snp_base = 0;
400dde8a05bSUgen J.S. Antsilevich 	snp->snp_len = 0;
401dde8a05bSUgen J.S. Antsilevich 
402dde8a05bSUgen J.S. Antsilevich 	/*
4030739a0dcSUgen J.S. Antsilevich 	 * snp_tty == NULL  is for inactive snoop devices.
404dde8a05bSUgen J.S. Antsilevich 	 */
4050739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
406784fc12fSPoul-Henning Kamp 	snp->snp_target = NODEV;
407ac60b28dSDima Dorfman 
408ac60b28dSDima Dorfman 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
409dde8a05bSUgen J.S. Antsilevich 	return (0);
410dde8a05bSUgen J.S. Antsilevich }
411dde8a05bSUgen J.S. Antsilevich 
412019b4d63SUgen J.S. Antsilevich 
41387b6de2bSPoul-Henning Kamp static int
414019b4d63SUgen J.S. Antsilevich snp_detach(snp)
415019b4d63SUgen J.S. Antsilevich 	struct snoop *snp;
416019b4d63SUgen J.S. Antsilevich {
417019b4d63SUgen J.S. Antsilevich 	struct tty *tp;
418019b4d63SUgen J.S. Antsilevich 
419019b4d63SUgen J.S. Antsilevich 	snp->snp_base = 0;
420019b4d63SUgen J.S. Antsilevich 	snp->snp_len = 0;
421019b4d63SUgen J.S. Antsilevich 
422019b4d63SUgen J.S. Antsilevich 	/*
423019b4d63SUgen J.S. Antsilevich 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
424019b4d63SUgen J.S. Antsilevich 	 * change it anyway.
425019b4d63SUgen J.S. Antsilevich 	 */
426f09f49f1SDima Dorfman 	tp = snp->snp_tty;
427f09f49f1SDima Dorfman 	if (tp == NULL)
428964587caSUgen J.S. Antsilevich 		goto detach_notty;
429019b4d63SUgen J.S. Antsilevich 
43077f77631SPaul Traina 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
431f09f49f1SDima Dorfman 	    tp->t_line == snooplinedisc) {
432019b4d63SUgen J.S. Antsilevich 		tp->t_sc = NULL;
433019b4d63SUgen J.S. Antsilevich 		tp->t_state &= ~TS_SNOOP;
43447eaa5f5SDima Dorfman 		tp->t_line = snp->snp_olddisc;
435019b4d63SUgen J.S. Antsilevich 	} else
436019b4d63SUgen J.S. Antsilevich 		printf("Snoop: bad attached tty data.\n");
437019b4d63SUgen J.S. Antsilevich 
4380739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
439784fc12fSPoul-Henning Kamp 	snp->snp_target = NODEV;
440019b4d63SUgen J.S. Antsilevich 
441964587caSUgen J.S. Antsilevich detach_notty:
442019b4d63SUgen J.S. Antsilevich 	selwakeup(&snp->snp_sel);
443019b4d63SUgen J.S. Antsilevich 	snp->snp_sel.si_pid = 0;
44476e90dbcSBrian Feldman 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
44576e90dbcSBrian Feldman 		free(snp, M_SNP);
446019b4d63SUgen J.S. Antsilevich 
447019b4d63SUgen J.S. Antsilevich 	return (0);
448019b4d63SUgen J.S. Antsilevich }
449019b4d63SUgen J.S. Antsilevich 
45087f6c662SJulian Elischer static int
45160039670SBruce Evans snpclose(dev, flags, fmt, p)
452dde8a05bSUgen J.S. Antsilevich 	dev_t dev;
45360039670SBruce Evans 	int flags;
45460039670SBruce Evans 	int fmt;
45560039670SBruce Evans 	struct proc *p;
456dde8a05bSUgen J.S. Antsilevich {
457f09f49f1SDima Dorfman 	struct snoop *snp;
458dde8a05bSUgen J.S. Antsilevich 
459f09f49f1SDima Dorfman 	snp = dev->si_drv1;
460dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = 0;
461ac60b28dSDima Dorfman 	LIST_REMOVE(snp, snp_list);
46276e90dbcSBrian Feldman 	free(snp->snp_buf, M_SNP);
463019b4d63SUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_OPEN;
46476e90dbcSBrian Feldman 	dev->si_drv1 = NULL;
46525c7870eSDima Dorfman 	destroy_dev(dev);
466dde8a05bSUgen J.S. Antsilevich 
467019b4d63SUgen J.S. Antsilevich 	return (snp_detach(snp));
468dde8a05bSUgen J.S. Antsilevich }
469dde8a05bSUgen J.S. Antsilevich 
470f09f49f1SDima Dorfman static int
471f09f49f1SDima Dorfman snp_down(snp)
472964587caSUgen J.S. Antsilevich 	struct snoop *snp;
473964587caSUgen J.S. Antsilevich {
47476e90dbcSBrian Feldman 
47576e90dbcSBrian Feldman 	if (snp->snp_blen != SNOOP_MINLEN) {
47676e90dbcSBrian Feldman 		free(snp->snp_buf, M_SNP);
47776e90dbcSBrian Feldman 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
478964587caSUgen J.S. Antsilevich 		snp->snp_blen = SNOOP_MINLEN;
47976e90dbcSBrian Feldman 	}
480964587caSUgen J.S. Antsilevich 	snp->snp_flags |= SNOOP_DOWN;
481dde8a05bSUgen J.S. Antsilevich 
482964587caSUgen J.S. Antsilevich 	return (snp_detach(snp));
483964587caSUgen J.S. Antsilevich }
484dde8a05bSUgen J.S. Antsilevich 
48587f6c662SJulian Elischer static int
48660039670SBruce Evans snpioctl(dev, cmd, data, flags, p)
487dde8a05bSUgen J.S. Antsilevich 	dev_t dev;
488ecbb00a2SDoug Rabson 	u_long cmd;
489dde8a05bSUgen J.S. Antsilevich 	caddr_t data;
49060039670SBruce Evans 	int flags;
49160039670SBruce Evans 	struct proc *p;
492dde8a05bSUgen J.S. Antsilevich {
493f09f49f1SDima Dorfman 	struct snoop *snp;
494dde8a05bSUgen J.S. Antsilevich 	struct tty *tp, *tpo;
495f09f49f1SDima Dorfman 	dev_t tdev;
49676e90dbcSBrian Feldman 	int s;
497dde8a05bSUgen J.S. Antsilevich 
498f09f49f1SDima Dorfman 	snp = dev->si_drv1;
499dde8a05bSUgen J.S. Antsilevich 	switch (cmd) {
500dde8a05bSUgen J.S. Antsilevich 	case SNPSTTY:
501212dfe6fSBruce Evans 		tdev = udev2dev(*((udev_t *)data), 0);
502784fc12fSPoul-Henning Kamp 		if (tdev == NODEV)
503f09f49f1SDima Dorfman 			return (snp_down(snp));
504964587caSUgen J.S. Antsilevich 
505444f003cSBruce Evans 		tp = snpdevtotty(tdev);
50677f77631SPaul Traina 		if (!tp)
507dde8a05bSUgen J.S. Antsilevich 			return (EINVAL);
508dde8a05bSUgen J.S. Antsilevich 
509dde8a05bSUgen J.S. Antsilevich 		s = spltty();
51077f77631SPaul Traina 
511784fc12fSPoul-Henning Kamp 		if (snp->snp_target == NODEV) {
5120739a0dcSUgen J.S. Antsilevich 			tpo = snp->snp_tty;
51377f77631SPaul Traina 			if (tpo)
514dde8a05bSUgen J.S. Antsilevich 				tpo->t_state &= ~TS_SNOOP;
515dde8a05bSUgen J.S. Antsilevich 		}
51677f77631SPaul Traina 
517dde8a05bSUgen J.S. Antsilevich 		tp->t_sc = (caddr_t)snp;
518dde8a05bSUgen J.S. Antsilevich 		tp->t_state |= TS_SNOOP;
51947eaa5f5SDima Dorfman 		snp->snp_olddisc = tp->t_line;
520f09f49f1SDima Dorfman 		tp->t_line = snooplinedisc;
5210739a0dcSUgen J.S. Antsilevich 		snp->snp_tty = tp;
52277f77631SPaul Traina 		snp->snp_target = tdev;
52377f77631SPaul Traina 
524964587caSUgen J.S. Antsilevich 		/*
525964587caSUgen J.S. Antsilevich 		 * Clean overflow and down flags -
526964587caSUgen J.S. Antsilevich 		 * we'll have a chance to get them in the future :)))
527964587caSUgen J.S. Antsilevich 		 */
528964587caSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
529019b4d63SUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_DOWN;
530dde8a05bSUgen J.S. Antsilevich 		splx(s);
531dde8a05bSUgen J.S. Antsilevich 		break;
53277f77631SPaul Traina 
533dde8a05bSUgen J.S. Antsilevich 	case SNPGTTY:
5340739a0dcSUgen J.S. Antsilevich 		/*
5350739a0dcSUgen J.S. Antsilevich 		 * We keep snp_target field specially to make
5360739a0dcSUgen J.S. Antsilevich 		 * SNPGTTY happy, else we can't know what is device
5370739a0dcSUgen J.S. Antsilevich 		 * major/minor for tty.
5380739a0dcSUgen J.S. Antsilevich 		 */
53977f77631SPaul Traina 		*((dev_t *)data) = snp->snp_target;
540dde8a05bSUgen J.S. Antsilevich 		break;
541dde8a05bSUgen J.S. Antsilevich 
542dde8a05bSUgen J.S. Antsilevich 	case FIONBIO:
543dde8a05bSUgen J.S. Antsilevich 		break;
54477f77631SPaul Traina 
545dde8a05bSUgen J.S. Antsilevich 	case FIOASYNC:
546dde8a05bSUgen J.S. Antsilevich 		if (*(int *)data)
547dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_ASYNC;
548dde8a05bSUgen J.S. Antsilevich 		else
549dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags &= ~SNOOP_ASYNC;
550dde8a05bSUgen J.S. Antsilevich 		break;
55177f77631SPaul Traina 
552dde8a05bSUgen J.S. Antsilevich 	case FIONREAD:
553dde8a05bSUgen J.S. Antsilevich 		s = spltty();
5540739a0dcSUgen J.S. Antsilevich 		if (snp->snp_tty != NULL)
555dde8a05bSUgen J.S. Antsilevich 			*(int *)data = snp->snp_len;
556dde8a05bSUgen J.S. Antsilevich 		else
557964587caSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_DOWN) {
558964587caSUgen J.S. Antsilevich 				if (snp->snp_flags & SNOOP_OFLOW)
559964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_OFLOW;
560964587caSUgen J.S. Antsilevich 				else
561964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_TTYCLOSE;
562964587caSUgen J.S. Antsilevich 			} else {
563964587caSUgen J.S. Antsilevich 				*(int *)data = SNP_DETACH;
564964587caSUgen J.S. Antsilevich 			}
565dde8a05bSUgen J.S. Antsilevich 		splx(s);
566dde8a05bSUgen J.S. Antsilevich 		break;
56777f77631SPaul Traina 
568dde8a05bSUgen J.S. Antsilevich 	default:
569dde8a05bSUgen J.S. Antsilevich 		return (ENOTTY);
570dde8a05bSUgen J.S. Antsilevich 	}
571dde8a05bSUgen J.S. Antsilevich 	return (0);
572dde8a05bSUgen J.S. Antsilevich }
573dde8a05bSUgen J.S. Antsilevich 
57487f6c662SJulian Elischer static int
575659ffb48SPeter Wemm snppoll(dev, events, p)
576dde8a05bSUgen J.S. Antsilevich 	dev_t dev;
577659ffb48SPeter Wemm 	int events;
578dde8a05bSUgen J.S. Antsilevich 	struct proc *p;
579dde8a05bSUgen J.S. Antsilevich {
580f09f49f1SDima Dorfman 	struct snoop *snp;
581f09f49f1SDima Dorfman 	int revents;
582dde8a05bSUgen J.S. Antsilevich 
583f09f49f1SDima Dorfman 	snp = dev->si_drv1;
584f09f49f1SDima Dorfman 	revents = 0;
585019b4d63SUgen J.S. Antsilevich 	/*
586659ffb48SPeter Wemm 	 * If snoop is down, we don't want to poll() forever so we return 1.
587019b4d63SUgen J.S. Antsilevich 	 * Caller should see if we down via FIONREAD ioctl().  The last should
588019b4d63SUgen J.S. Antsilevich 	 * return -1 to indicate down state.
589019b4d63SUgen J.S. Antsilevich 	 */
590dfd5dee1SPeter Wemm 	if (events & (POLLIN | POLLRDNORM)) {
591659ffb48SPeter Wemm 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
592659ffb48SPeter Wemm 			revents |= events & (POLLIN | POLLRDNORM);
593659ffb48SPeter Wemm 		else
594dde8a05bSUgen J.S. Antsilevich 			selrecord(p, &snp->snp_sel);
595dfd5dee1SPeter Wemm 	}
596659ffb48SPeter Wemm 	return (revents);
597dde8a05bSUgen J.S. Antsilevich }
598dde8a05bSUgen J.S. Antsilevich 
59987f6c662SJulian Elischer static void
600f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev)
601f09f49f1SDima Dorfman 	void *arg;
602f09f49f1SDima Dorfman 	char *name;
603f09f49f1SDima Dorfman 	int namelen;
604f09f49f1SDima Dorfman 	dev_t *dev;
60525c7870eSDima Dorfman {
60625c7870eSDima Dorfman 	int u;
60725c7870eSDima Dorfman 
60825c7870eSDima Dorfman 	if (*dev != NODEV)
60925c7870eSDima Dorfman 		return;
61025c7870eSDima Dorfman 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
61125c7870eSDima Dorfman 		return;
61225c7870eSDima Dorfman 	*dev = make_dev(&snp_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, 0600,
61325c7870eSDima Dorfman 	    "snp%d", u);
61425c7870eSDima Dorfman }
61525c7870eSDima Dorfman 
61647eaa5f5SDima Dorfman static int
617f09f49f1SDima Dorfman snp_modevent(mod, type, data)
618f09f49f1SDima Dorfman 	module_t mod;
619f09f49f1SDima Dorfman 	int type;
620f09f49f1SDima Dorfman 	void *data;
62153ac6efbSJulian Elischer {
622f09f49f1SDima Dorfman 	static eventhandler_tag eh_tag;
62353ac6efbSJulian Elischer 
62447eaa5f5SDima Dorfman 	switch (type) {
62547eaa5f5SDima Dorfman 	case MOD_LOAD:
626f09f49f1SDima Dorfman 		/* XXX error checking. */
62747eaa5f5SDima Dorfman 		eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
628f09f49f1SDima Dorfman 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
62976e90dbcSBrian Feldman 		cdevsw_add(&snp_cdevsw);
63047eaa5f5SDima Dorfman 		break;
63147eaa5f5SDima Dorfman 	case MOD_UNLOAD:
632ac60b28dSDima Dorfman 		if (!LIST_EMPTY(&snp_sclist))
633ac60b28dSDima Dorfman 			return (EBUSY);
63447eaa5f5SDima Dorfman 		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
635f09f49f1SDima Dorfman 		ldisc_deregister(snooplinedisc);
63647eaa5f5SDima Dorfman 		cdevsw_remove(&snp_cdevsw);
63747eaa5f5SDima Dorfman 		break;
63847eaa5f5SDima Dorfman 	default:
63947eaa5f5SDima Dorfman 		break;
64047eaa5f5SDima Dorfman 	}
641f09f49f1SDima Dorfman 	return (0);
6427198bf47SJulian Elischer }
64353ac6efbSJulian Elischer 
64447eaa5f5SDima Dorfman static moduledata_t snp_mod = {
64547eaa5f5SDima Dorfman         "snp",
64647eaa5f5SDima Dorfman         snp_modevent,
64747eaa5f5SDima Dorfman         NULL
64847eaa5f5SDima Dorfman };
64947eaa5f5SDima Dorfman DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR);
650