xref: /freebsd/sys/dev/snp/snp.c (revision 3e019deaed5ad0687ea53ed5b5ba3336dc0be3c4)
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  *
15dde8a05bSUgen J.S. Antsilevich  */
16dde8a05bSUgen J.S. Antsilevich 
17945ff31aSDavid E. O'Brien #include <sys/cdefs.h>
18945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$");
19945ff31aSDavid E. O'Brien 
20dde8a05bSUgen J.S. Antsilevich #include <sys/param.h>
21dde8a05bSUgen J.S. Antsilevich #include <sys/systm.h>
2271455815SBruce Evans #include <sys/filio.h>
23a1c995b6SPoul-Henning Kamp #include <sys/malloc.h>
24dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h>
25dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h>
26659ffb48SPeter Wemm #include <sys/poll.h>
27dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h>
28fe12f24bSPoul-Henning Kamp #include <sys/module.h>
29ac60b28dSDima Dorfman #include <sys/queue.h>
3087f6c662SJulian Elischer #include <sys/snoop.h>
31d96bc99dSBruce Evans #include <sys/vnode.h>
3287f6c662SJulian Elischer 
33ddf5b796SDima Dorfman static	l_close_t	snplclose;
34f09f49f1SDima Dorfman static	l_write_t	snplwrite;
3587f6c662SJulian Elischer static	d_open_t	snpopen;
3687f6c662SJulian Elischer static	d_close_t	snpclose;
3787f6c662SJulian Elischer static	d_read_t	snpread;
3887f6c662SJulian Elischer static	d_write_t	snpwrite;
3987f6c662SJulian Elischer static	d_ioctl_t	snpioctl;
40659ffb48SPeter Wemm static	d_poll_t	snppoll;
4187f6c662SJulian Elischer 
424e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = {
43dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
4463a97efcSBruce Evans 	.d_flags =	D_PSEUDO | D_NEEDGIANT,
457ac40f5fSPoul-Henning Kamp 	.d_open =	snpopen,
467ac40f5fSPoul-Henning Kamp 	.d_close =	snpclose,
477ac40f5fSPoul-Henning Kamp 	.d_read =	snpread,
487ac40f5fSPoul-Henning Kamp 	.d_write =	snpwrite,
497ac40f5fSPoul-Henning Kamp 	.d_ioctl =	snpioctl,
507ac40f5fSPoul-Henning Kamp 	.d_poll =	snppoll,
517ac40f5fSPoul-Henning Kamp 	.d_name =	"snp",
524e2f199eSPoul-Henning Kamp };
5387f6c662SJulian Elischer 
54f09f49f1SDima Dorfman static struct linesw snpdisc = {
5566ea137dSRobert Watson 	.l_open =	ttyopen,
5666ea137dSRobert Watson 	.l_close =	snplclose,
5766ea137dSRobert Watson 	.l_read =	ttread,
5866ea137dSRobert Watson 	.l_write =	snplwrite,
5966ea137dSRobert Watson 	.l_ioctl =	l_nullioctl,
6066ea137dSRobert Watson 	.l_rint =	ttyinput,
6166ea137dSRobert Watson 	.l_start =	ttstart,
6266ea137dSRobert Watson 	.l_modem =	ttymodem
63f09f49f1SDima Dorfman };
6453ac6efbSJulian Elischer 
65101f105dSDima Dorfman /*
66101f105dSDima Dorfman  * This is the main snoop per-device structure.
67101f105dSDima Dorfman  */
68101f105dSDima Dorfman struct snoop {
69101f105dSDima Dorfman 	LIST_ENTRY(snoop)	snp_list;	/* List glue. */
70cdd530caSDima Dorfman 	int			snp_unit;	/* Device number. */
7189c9c53dSPoul-Henning Kamp 	struct cdev *snp_target;	/* Target tty device. */
72101f105dSDima Dorfman 	struct tty		*snp_tty;	/* Target tty pointer. */
73101f105dSDima Dorfman 	u_long			 snp_len;	/* Possible length. */
74101f105dSDima Dorfman 	u_long			 snp_base;	/* Data base. */
75101f105dSDima Dorfman 	u_long			 snp_blen;	/* Used length. */
76101f105dSDima Dorfman 	caddr_t			 snp_buf;	/* Allocation pointer. */
77101f105dSDima Dorfman 	int			 snp_flags;	/* Flags. */
78101f105dSDima Dorfman 	struct selinfo		 snp_sel;	/* Select info. */
79101f105dSDima Dorfman 	int			 snp_olddisc;	/* Old line discipline. */
80101f105dSDima Dorfman };
81101f105dSDima Dorfman 
82101f105dSDima Dorfman /*
83101f105dSDima Dorfman  * Possible flags.
84101f105dSDima Dorfman  */
85101f105dSDima Dorfman #define SNOOP_ASYNC		0x0002
86101f105dSDima Dorfman #define SNOOP_OPEN		0x0004
87101f105dSDima Dorfman #define SNOOP_RWAIT		0x0008
88101f105dSDima Dorfman #define SNOOP_OFLOW		0x0010
89101f105dSDima Dorfman #define SNOOP_DOWN		0x0020
90101f105dSDima Dorfman 
91101f105dSDima Dorfman /*
92101f105dSDima Dorfman  * Other constants.
93101f105dSDima Dorfman  */
94101f105dSDima Dorfman #define SNOOP_MINLEN		(4*1024)	/* This should be power of 2.
95101f105dSDima Dorfman 						 * 4K tested to be the minimum
96101f105dSDima Dorfman 						 * for which on normal tty
97101f105dSDima Dorfman 						 * usage there is no need to
98101f105dSDima Dorfman 						 * allocate more.
99101f105dSDima Dorfman 						 */
100101f105dSDima Dorfman #define SNOOP_MAXLEN		(64*1024)	/* This one also,64K enough
101101f105dSDima Dorfman 						 * If we grow more,something
102101f105dSDima Dorfman 						 * really bad in this world..
103101f105dSDima Dorfman 						 */
104101f105dSDima Dorfman 
105959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
10647eaa5f5SDima Dorfman /*
10747eaa5f5SDima Dorfman  * The number of the "snoop" line discipline.  This gets determined at
10847eaa5f5SDima Dorfman  * module load time.
10947eaa5f5SDima Dorfman  */
110f09f49f1SDima Dorfman static int snooplinedisc;
111ac60b28dSDima Dorfman 
112ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
113b0b03348SPoul-Henning Kamp static struct clonedevs	  *snpclones;
114ac60b28dSDima Dorfman 
11589c9c53dSPoul-Henning Kamp static struct tty	*snpdevtotty(struct cdev *dev);
116e51a25f8SAlfred Perlstein static void		snp_clone(void *arg, char *name,
11789c9c53dSPoul-Henning Kamp 			    int namelen, struct cdev **dev);
118e51a25f8SAlfred Perlstein static int		snp_detach(struct snoop *snp);
119e51a25f8SAlfred Perlstein static int		snp_down(struct snoop *snp);
120e51a25f8SAlfred Perlstein static int		snp_in(struct snoop *snp, char *buf, int n);
121e51a25f8SAlfred Perlstein static int		snp_modevent(module_t mod, int what, void *arg);
12247eaa5f5SDima Dorfman 
12347eaa5f5SDima Dorfman static int
124ddf5b796SDima Dorfman snplclose(tp, flag)
125ddf5b796SDima Dorfman 	struct tty *tp;
126ddf5b796SDima Dorfman 	int flag;
127ddf5b796SDima Dorfman {
128ddf5b796SDima Dorfman 	struct snoop *snp;
129ddf5b796SDima Dorfman 	int error;
130ddf5b796SDima Dorfman 
131ddf5b796SDima Dorfman 	snp = tp->t_sc;
132ddf5b796SDima Dorfman 	error = snp_down(snp);
133ddf5b796SDima Dorfman 	if (error != 0)
134ddf5b796SDima Dorfman 		return (error);
135ddf5b796SDima Dorfman 	error = ttylclose(tp, flag);
136ddf5b796SDima Dorfman 	return (error);
137ddf5b796SDima Dorfman }
138ddf5b796SDima Dorfman 
139ddf5b796SDima Dorfman static int
140f09f49f1SDima Dorfman snplwrite(tp, uio, flag)
141f09f49f1SDima Dorfman 	struct tty *tp;
142f09f49f1SDima Dorfman 	struct uio *uio;
143f09f49f1SDima Dorfman 	int flag;
14447eaa5f5SDima Dorfman {
14547eaa5f5SDima Dorfman 	struct iovec iov;
14647eaa5f5SDima Dorfman 	struct uio uio2;
147f09f49f1SDima Dorfman 	struct snoop *snp;
148f09f49f1SDima Dorfman 	int error, ilen;
149e692c40cSDima Dorfman 	char *ibuf;
15047eaa5f5SDima Dorfman 
151e692c40cSDima Dorfman 	error = 0;
152e692c40cSDima Dorfman 	ibuf = NULL;
153f09f49f1SDima Dorfman 	snp = tp->t_sc;
15487826386SDima Dorfman 	while (uio->uio_resid > 0) {
155e692c40cSDima Dorfman 		ilen = imin(512, uio->uio_resid);
156a163d034SWarner Losh 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
15747eaa5f5SDima Dorfman 		error = uiomove(ibuf, ilen, uio);
158f09f49f1SDima Dorfman 		if (error != 0)
159e692c40cSDima Dorfman 			break;
160f09f49f1SDima Dorfman 		snp_in(snp, ibuf, ilen);
161f09f49f1SDima Dorfman 		/* Hackish, but probably the least of all evils. */
16247eaa5f5SDima Dorfman 		iov.iov_base = ibuf;
16347eaa5f5SDima Dorfman 		iov.iov_len = ilen;
16447eaa5f5SDima Dorfman 		uio2.uio_iov = &iov;
16547eaa5f5SDima Dorfman 		uio2.uio_iovcnt = 1;
16647eaa5f5SDima Dorfman 		uio2.uio_offset = 0;
16747eaa5f5SDima Dorfman 		uio2.uio_resid = ilen;
16847eaa5f5SDima Dorfman 		uio2.uio_segflg = UIO_SYSSPACE;
16947eaa5f5SDima Dorfman 		uio2.uio_rw = UIO_WRITE;
170b40ce416SJulian Elischer 		uio2.uio_td = uio->uio_td;
17147eaa5f5SDima Dorfman 		error = ttwrite(tp, &uio2, flag);
172f09f49f1SDima Dorfman 		if (error != 0)
173e692c40cSDima Dorfman 			break;
174e692c40cSDima Dorfman 		free(ibuf, M_SNP);
175e692c40cSDima Dorfman 		ibuf = NULL;
17647eaa5f5SDima Dorfman 	}
177e692c40cSDima Dorfman 	if (ibuf != NULL)
178e692c40cSDima Dorfman 		free(ibuf, M_SNP);
179e692c40cSDima Dorfman 	return (error);
180ddf5b796SDima Dorfman }
18147eaa5f5SDima Dorfman 
18277f77631SPaul Traina static struct tty *
183444f003cSBruce Evans snpdevtotty(dev)
18489c9c53dSPoul-Henning Kamp 	struct cdev *dev;
18577f77631SPaul Traina {
186444f003cSBruce Evans 	struct cdevsw *cdp;
18777f77631SPaul Traina 
1884be2eb8cSPoul-Henning Kamp 	cdp = devsw(dev);
18987826386SDima Dorfman 	if (cdp == NULL || (cdp->d_flags & D_TTY) == 0)
190444f003cSBruce Evans 		return (NULL);
19187826386SDima Dorfman 	return (dev->si_tty);
19277f77631SPaul Traina }
19377f77631SPaul Traina 
1940739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
1950739a0dcSUgen J.S. Antsilevich 				 * interactive mode write is 3 bytes
1960739a0dcSUgen J.S. Antsilevich 				 * length for function keys...
1970739a0dcSUgen J.S. Antsilevich 				 */
1980739a0dcSUgen J.S. Antsilevich 
19987f6c662SJulian Elischer static int
2000739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag)
20189c9c53dSPoul-Henning Kamp 	struct cdev *dev;
2020739a0dcSUgen J.S. Antsilevich 	struct uio *uio;
2030739a0dcSUgen J.S. Antsilevich 	int flag;
2040739a0dcSUgen J.S. Antsilevich {
205f09f49f1SDima Dorfman 	struct snoop *snp;
2060739a0dcSUgen J.S. Antsilevich 	struct tty *tp;
207f09f49f1SDima Dorfman 	int error, i, len;
208f4c5dfbbSDima Dorfman 	unsigned char c[SNP_INPUT_BUF];
2090739a0dcSUgen J.S. Antsilevich 
210f09f49f1SDima Dorfman 	snp = dev->si_drv1;
2110739a0dcSUgen J.S. Antsilevich 	tp = snp->snp_tty;
212f09f49f1SDima Dorfman 	if (tp == NULL)
213f09f49f1SDima Dorfman 		return (EIO);
2140739a0dcSUgen J.S. Antsilevich 	if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
215f09f49f1SDima Dorfman 	    tp->t_line == snooplinedisc)
2160739a0dcSUgen J.S. Antsilevich 		goto tty_input;
2170739a0dcSUgen J.S. Antsilevich 
218cdd530caSDima Dorfman 	printf("snp%d: attempt to write to bad tty\n", snp->snp_unit);
2190739a0dcSUgen J.S. Antsilevich 	return (EIO);
2200739a0dcSUgen J.S. Antsilevich 
2210739a0dcSUgen J.S. Antsilevich tty_input:
2220739a0dcSUgen J.S. Antsilevich 	if (!(tp->t_state & TS_ISOPEN))
2230739a0dcSUgen J.S. Antsilevich 		return (EIO);
2240739a0dcSUgen J.S. Antsilevich 
2250739a0dcSUgen J.S. Antsilevich 	while (uio->uio_resid > 0) {
22687826386SDima Dorfman 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
2270739a0dcSUgen J.S. Antsilevich 		if ((error = uiomove(c, len, uio)) != 0)
2280739a0dcSUgen J.S. Antsilevich 			return (error);
2290739a0dcSUgen J.S. Antsilevich 		for (i=0; i < len; i++) {
2300739a0dcSUgen J.S. Antsilevich 			if (ttyinput(c[i], tp))
2310739a0dcSUgen J.S. Antsilevich 				return (EIO);
2320739a0dcSUgen J.S. Antsilevich 		}
2330739a0dcSUgen J.S. Antsilevich 	}
234f09f49f1SDima Dorfman 	return (0);
2350739a0dcSUgen J.S. Antsilevich }
2360739a0dcSUgen J.S. Antsilevich 
2370739a0dcSUgen J.S. Antsilevich 
23887f6c662SJulian Elischer static int
239dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag)
24089c9c53dSPoul-Henning Kamp 	struct cdev *dev;
241dde8a05bSUgen J.S. Antsilevich 	struct uio *uio;
242dde8a05bSUgen J.S. Antsilevich 	int flag;
243dde8a05bSUgen J.S. Antsilevich {
244f09f49f1SDima Dorfman 	struct snoop *snp;
245f09f49f1SDima Dorfman 	int error, len, n, nblen, s;
246dde8a05bSUgen J.S. Antsilevich 	caddr_t from;
247dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
248dde8a05bSUgen J.S. Antsilevich 
249f09f49f1SDima Dorfman 	snp = dev->si_drv1;
2505526d2d9SEivind Eklund 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
2515526d2d9SEivind Eklund 	    ("snoop buffer error"));
25277f77631SPaul Traina 
2530739a0dcSUgen J.S. Antsilevich 	if (snp->snp_tty == NULL)
254dde8a05bSUgen J.S. Antsilevich 		return (EIO);
255dde8a05bSUgen J.S. Antsilevich 
256dde8a05bSUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_RWAIT;
257dde8a05bSUgen J.S. Antsilevich 
258dde8a05bSUgen J.S. Antsilevich 	do {
259dde8a05bSUgen J.S. Antsilevich 		if (snp->snp_len == 0) {
260d96bc99dSBruce Evans 			if (flag & IO_NDELAY)
261d96bc99dSBruce Evans 				return (EWOULDBLOCK);
262dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_RWAIT;
263521f364bSDag-Erling Smørgrav 			error = tsleep(snp, (PZERO + 1) | PCATCH,
2642c1a6f21SDima Dorfman 			    "snprd", 0);
2652c1a6f21SDima Dorfman 			if (error != 0)
2662c1a6f21SDima Dorfman 				return (error);
267dde8a05bSUgen J.S. Antsilevich 		}
268dde8a05bSUgen J.S. Antsilevich 	} while (snp->snp_len == 0);
269dde8a05bSUgen J.S. Antsilevich 
270019b4d63SUgen J.S. Antsilevich 	n = snp->snp_len;
271dde8a05bSUgen J.S. Antsilevich 
272f09f49f1SDima Dorfman 	error = 0;
273dde8a05bSUgen J.S. Antsilevich 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
27487826386SDima Dorfman 		len = min((unsigned)uio->uio_resid, snp->snp_len);
275dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
276dde8a05bSUgen J.S. Antsilevich 		if (len == 0)
277dde8a05bSUgen J.S. Antsilevich 			break;
278dde8a05bSUgen J.S. Antsilevich 
279dde8a05bSUgen J.S. Antsilevich 		error = uiomove(from, len, uio);
280dde8a05bSUgen J.S. Antsilevich 		snp->snp_base += len;
281dde8a05bSUgen J.S. Antsilevich 		snp->snp_len -= len;
282dde8a05bSUgen J.S. Antsilevich 	}
283019b4d63SUgen J.S. Antsilevich 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
284dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
285019b4d63SUgen J.S. Antsilevich 	}
286dde8a05bSUgen J.S. Antsilevich 	s = spltty();
287dde8a05bSUgen J.S. Antsilevich 	nblen = snp->snp_blen;
288dde8a05bSUgen J.S. Antsilevich 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
289f09f49f1SDima Dorfman 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
290dde8a05bSUgen J.S. Antsilevich 			nblen = nblen / 2;
29176e90dbcSBrian Feldman 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
292dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
29376e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
294dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
295dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
296dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
297dde8a05bSUgen J.S. Antsilevich 		}
298dde8a05bSUgen J.S. Antsilevich 	}
299dde8a05bSUgen J.S. Antsilevich 	splx(s);
300dde8a05bSUgen J.S. Antsilevich 
301f09f49f1SDima Dorfman 	return (error);
302dde8a05bSUgen J.S. Antsilevich }
303dde8a05bSUgen J.S. Antsilevich 
304f09f49f1SDima Dorfman static int
305f09f49f1SDima Dorfman snp_in(snp, buf, n)
306dde8a05bSUgen J.S. Antsilevich 	struct snoop *snp;
307dde8a05bSUgen J.S. Antsilevich 	char *buf;
308dde8a05bSUgen J.S. Antsilevich 	int n;
309dde8a05bSUgen J.S. Antsilevich {
310dde8a05bSUgen J.S. Antsilevich 	int s_free, s_tail;
311dde8a05bSUgen J.S. Antsilevich 	int s, len, nblen;
312dde8a05bSUgen J.S. Antsilevich 	caddr_t from, to;
313dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
314dde8a05bSUgen J.S. Antsilevich 
315219cbf59SEivind Eklund 	KASSERT(n >= 0, ("negative snoop char count"));
316dde8a05bSUgen J.S. Antsilevich 
317dde8a05bSUgen J.S. Antsilevich 	if (n == 0)
318f09f49f1SDima Dorfman 		return (0);
319dde8a05bSUgen J.S. Antsilevich 
320019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_DOWN) {
321cdd530caSDima Dorfman 		printf("snp%d: more data to down interface\n", snp->snp_unit);
322f09f49f1SDima Dorfman 		return (0);
323019b4d63SUgen J.S. Antsilevich 	}
324964587caSUgen J.S. Antsilevich 
325019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_OFLOW) {
326cdd530caSDima Dorfman 		printf("snp%d: buffer overflow\n", snp->snp_unit);
327019b4d63SUgen J.S. Antsilevich 		/*
328019b4d63SUgen J.S. Antsilevich 		 * On overflow we just repeat the standart close
329019b4d63SUgen J.S. Antsilevich 		 * procedure...yes , this is waste of space but.. Then next
330019b4d63SUgen J.S. Antsilevich 		 * read from device will fail if one would recall he is
331019b4d63SUgen J.S. Antsilevich 		 * snooping and retry...
332019b4d63SUgen J.S. Antsilevich 		 */
333dde8a05bSUgen J.S. Antsilevich 
334f09f49f1SDima Dorfman 		return (snp_down(snp));
335019b4d63SUgen J.S. Antsilevich 	}
336019b4d63SUgen J.S. Antsilevich 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
337019b4d63SUgen J.S. Antsilevich 	s_free = snp->snp_blen - snp->snp_len;
338dde8a05bSUgen J.S. Antsilevich 
339dde8a05bSUgen J.S. Antsilevich 
340dde8a05bSUgen J.S. Antsilevich 	if (n > s_free) {
341dde8a05bSUgen J.S. Antsilevich 		s = spltty();
342dde8a05bSUgen J.S. Antsilevich 		nblen = snp->snp_blen;
343dde8a05bSUgen J.S. Antsilevich 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
344dde8a05bSUgen J.S. Antsilevich 			nblen = snp->snp_blen * 2;
345dde8a05bSUgen J.S. Antsilevich 			s_free = nblen - (snp->snp_len + snp->snp_base);
346dde8a05bSUgen J.S. Antsilevich 		}
34776e90dbcSBrian Feldman 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
348dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
34976e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
350dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
351dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
352dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
353dde8a05bSUgen J.S. Antsilevich 		} else {
354dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_OFLOW;
355dde8a05bSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_RWAIT) {
356dde8a05bSUgen J.S. Antsilevich 				snp->snp_flags &= ~SNOOP_RWAIT;
357521f364bSDag-Erling Smørgrav 				wakeup(snp);
358dde8a05bSUgen J.S. Antsilevich 			}
359dde8a05bSUgen J.S. Antsilevich 			splx(s);
360f09f49f1SDima Dorfman 			return (0);
361dde8a05bSUgen J.S. Antsilevich 		}
362dde8a05bSUgen J.S. Antsilevich 		splx(s);
363dde8a05bSUgen J.S. Antsilevich 	}
364dde8a05bSUgen J.S. Antsilevich 	if (n > s_tail) {
365dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
366dde8a05bSUgen J.S. Antsilevich 		to = (caddr_t)(snp->snp_buf);
367dde8a05bSUgen J.S. Antsilevich 		len = snp->snp_len;
368dde8a05bSUgen J.S. Antsilevich 		bcopy(from, to, len);
369dde8a05bSUgen J.S. Antsilevich 		snp->snp_base = 0;
370dde8a05bSUgen J.S. Antsilevich 	}
371dde8a05bSUgen J.S. Antsilevich 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
372dde8a05bSUgen J.S. Antsilevich 	bcopy(buf, to, n);
373dde8a05bSUgen J.S. Antsilevich 	snp->snp_len += n;
374dde8a05bSUgen J.S. Antsilevich 
375dde8a05bSUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_RWAIT) {
376dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_RWAIT;
377521f364bSDag-Erling Smørgrav 		wakeup(snp);
378dde8a05bSUgen J.S. Antsilevich 	}
379512824f8SSeigo Tanimura 	selwakeuppri(&snp->snp_sel, PZERO + 1);
380dde8a05bSUgen J.S. Antsilevich 
381f09f49f1SDima Dorfman 	return (n);
382dde8a05bSUgen J.S. Antsilevich }
383dde8a05bSUgen J.S. Antsilevich 
38487f6c662SJulian Elischer static int
385b40ce416SJulian Elischer snpopen(dev, flag, mode, td)
38689c9c53dSPoul-Henning Kamp 	struct cdev *dev;
387dde8a05bSUgen J.S. Antsilevich 	int flag, mode;
388b40ce416SJulian Elischer 	struct thread *td;
389dde8a05bSUgen J.S. Antsilevich {
390dde8a05bSUgen J.S. Antsilevich 	struct snoop *snp;
391dde8a05bSUgen J.S. Antsilevich 
39276e90dbcSBrian Feldman 	if (dev->si_drv1 == NULL) {
393b0b03348SPoul-Henning Kamp 		dev->si_flags &= ~SI_CHEAPCLONE;
394f09f49f1SDima Dorfman 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
395a163d034SWarner Losh 		    M_WAITOK | M_ZERO);
396cdd530caSDima Dorfman 		snp->snp_unit = dev2unit(dev);
39776e90dbcSBrian Feldman 	} else
39876e90dbcSBrian Feldman 		return (EBUSY);
39977f77631SPaul Traina 
400019b4d63SUgen J.S. Antsilevich 	/*
401019b4d63SUgen J.S. Antsilevich 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
402019b4d63SUgen J.S. Antsilevich 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
403019b4d63SUgen J.S. Antsilevich 	 */
404019b4d63SUgen J.S. Antsilevich 	snp->snp_flags = SNOOP_OPEN;
405dde8a05bSUgen J.S. Antsilevich 
406a163d034SWarner Losh 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
407dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = SNOOP_MINLEN;
408dde8a05bSUgen J.S. Antsilevich 	snp->snp_base = 0;
409dde8a05bSUgen J.S. Antsilevich 	snp->snp_len = 0;
410dde8a05bSUgen J.S. Antsilevich 
411dde8a05bSUgen J.S. Antsilevich 	/*
4120739a0dcSUgen J.S. Antsilevich 	 * snp_tty == NULL  is for inactive snoop devices.
413dde8a05bSUgen J.S. Antsilevich 	 */
4140739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
415f3732fd1SPoul-Henning Kamp 	snp->snp_target = NULL;
416ac60b28dSDima Dorfman 
417ac60b28dSDima Dorfman 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
418dde8a05bSUgen J.S. Antsilevich 	return (0);
419dde8a05bSUgen J.S. Antsilevich }
420dde8a05bSUgen J.S. Antsilevich 
421019b4d63SUgen J.S. Antsilevich 
42287b6de2bSPoul-Henning Kamp static int
423019b4d63SUgen J.S. Antsilevich snp_detach(snp)
424019b4d63SUgen J.S. Antsilevich 	struct snoop *snp;
425019b4d63SUgen J.S. Antsilevich {
426019b4d63SUgen J.S. Antsilevich 	struct tty *tp;
427019b4d63SUgen J.S. Antsilevich 
428019b4d63SUgen J.S. Antsilevich 	snp->snp_base = 0;
429019b4d63SUgen J.S. Antsilevich 	snp->snp_len = 0;
430019b4d63SUgen J.S. Antsilevich 
431019b4d63SUgen J.S. Antsilevich 	/*
432019b4d63SUgen J.S. Antsilevich 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
433019b4d63SUgen J.S. Antsilevich 	 * change it anyway.
434019b4d63SUgen J.S. Antsilevich 	 */
435f09f49f1SDima Dorfman 	tp = snp->snp_tty;
436f09f49f1SDima Dorfman 	if (tp == NULL)
437964587caSUgen J.S. Antsilevich 		goto detach_notty;
438019b4d63SUgen J.S. Antsilevich 
43977f77631SPaul Traina 	if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
440f09f49f1SDima Dorfman 	    tp->t_line == snooplinedisc) {
441019b4d63SUgen J.S. Antsilevich 		tp->t_sc = NULL;
442019b4d63SUgen J.S. Antsilevich 		tp->t_state &= ~TS_SNOOP;
44347eaa5f5SDima Dorfman 		tp->t_line = snp->snp_olddisc;
444019b4d63SUgen J.S. Antsilevich 	} else
445cdd530caSDima Dorfman 		printf("snp%d: bad attached tty data\n", snp->snp_unit);
446019b4d63SUgen J.S. Antsilevich 
4470739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
448f3732fd1SPoul-Henning Kamp 	snp->snp_target = NULL;
449019b4d63SUgen J.S. Antsilevich 
450964587caSUgen J.S. Antsilevich detach_notty:
451512824f8SSeigo Tanimura 	selwakeuppri(&snp->snp_sel, PZERO + 1);
45276e90dbcSBrian Feldman 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
45376e90dbcSBrian Feldman 		free(snp, M_SNP);
454019b4d63SUgen J.S. Antsilevich 
455019b4d63SUgen J.S. Antsilevich 	return (0);
456019b4d63SUgen J.S. Antsilevich }
457019b4d63SUgen J.S. Antsilevich 
45887f6c662SJulian Elischer static int
459b40ce416SJulian Elischer snpclose(dev, flags, fmt, td)
46089c9c53dSPoul-Henning Kamp 	struct cdev *dev;
46160039670SBruce Evans 	int flags;
46260039670SBruce Evans 	int fmt;
463b40ce416SJulian Elischer 	struct thread *td;
464dde8a05bSUgen J.S. Antsilevich {
465f09f49f1SDima Dorfman 	struct snoop *snp;
466dde8a05bSUgen J.S. Antsilevich 
467f09f49f1SDima Dorfman 	snp = dev->si_drv1;
468dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = 0;
469ac60b28dSDima Dorfman 	LIST_REMOVE(snp, snp_list);
47076e90dbcSBrian Feldman 	free(snp->snp_buf, M_SNP);
471019b4d63SUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_OPEN;
47276e90dbcSBrian Feldman 	dev->si_drv1 = NULL;
473b0b03348SPoul-Henning Kamp 	destroy_dev(dev);
474dde8a05bSUgen J.S. Antsilevich 
475019b4d63SUgen J.S. Antsilevich 	return (snp_detach(snp));
476dde8a05bSUgen J.S. Antsilevich }
477dde8a05bSUgen J.S. Antsilevich 
478f09f49f1SDima Dorfman static int
479f09f49f1SDima Dorfman snp_down(snp)
480964587caSUgen J.S. Antsilevich 	struct snoop *snp;
481964587caSUgen J.S. Antsilevich {
48276e90dbcSBrian Feldman 
48376e90dbcSBrian Feldman 	if (snp->snp_blen != SNOOP_MINLEN) {
48476e90dbcSBrian Feldman 		free(snp->snp_buf, M_SNP);
485a163d034SWarner Losh 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
486964587caSUgen J.S. Antsilevich 		snp->snp_blen = SNOOP_MINLEN;
48776e90dbcSBrian Feldman 	}
488964587caSUgen J.S. Antsilevich 	snp->snp_flags |= SNOOP_DOWN;
489dde8a05bSUgen J.S. Antsilevich 
490964587caSUgen J.S. Antsilevich 	return (snp_detach(snp));
491964587caSUgen J.S. Antsilevich }
492dde8a05bSUgen J.S. Antsilevich 
49387f6c662SJulian Elischer static int
494b40ce416SJulian Elischer snpioctl(dev, cmd, data, flags, td)
49589c9c53dSPoul-Henning Kamp 	struct cdev *dev;
496ecbb00a2SDoug Rabson 	u_long cmd;
497dde8a05bSUgen J.S. Antsilevich 	caddr_t data;
49860039670SBruce Evans 	int flags;
499b40ce416SJulian Elischer 	struct thread *td;
500dde8a05bSUgen J.S. Antsilevich {
501f09f49f1SDima Dorfman 	struct snoop *snp;
502dde8a05bSUgen J.S. Antsilevich 	struct tty *tp, *tpo;
50389c9c53dSPoul-Henning Kamp 	struct cdev *tdev;
50476e90dbcSBrian Feldman 	int s;
505dde8a05bSUgen J.S. Antsilevich 
506f09f49f1SDima Dorfman 	snp = dev->si_drv1;
507dde8a05bSUgen J.S. Antsilevich 	switch (cmd) {
508dde8a05bSUgen J.S. Antsilevich 	case SNPSTTY:
509f3732fd1SPoul-Henning Kamp 		tdev = findcdev(*((dev_t *)data));
510f3732fd1SPoul-Henning Kamp 		if (tdev == NULL)
511f09f49f1SDima Dorfman 			return (snp_down(snp));
512964587caSUgen J.S. Antsilevich 
513444f003cSBruce Evans 		tp = snpdevtotty(tdev);
51477f77631SPaul Traina 		if (!tp)
515dde8a05bSUgen J.S. Antsilevich 			return (EINVAL);
5162c9fb909SDima Dorfman 		if (tp->t_state & TS_SNOOP)
5172c9fb909SDima Dorfman 			return (EBUSY);
518dde8a05bSUgen J.S. Antsilevich 
519dde8a05bSUgen J.S. Antsilevich 		s = spltty();
52077f77631SPaul Traina 
521f3732fd1SPoul-Henning Kamp 		if (snp->snp_target == NULL) {
5220739a0dcSUgen J.S. Antsilevich 			tpo = snp->snp_tty;
52377f77631SPaul Traina 			if (tpo)
524dde8a05bSUgen J.S. Antsilevich 				tpo->t_state &= ~TS_SNOOP;
525dde8a05bSUgen J.S. Antsilevich 		}
52677f77631SPaul Traina 
527dde8a05bSUgen J.S. Antsilevich 		tp->t_sc = (caddr_t)snp;
528dde8a05bSUgen J.S. Antsilevich 		tp->t_state |= TS_SNOOP;
52947eaa5f5SDima Dorfman 		snp->snp_olddisc = tp->t_line;
530f09f49f1SDima Dorfman 		tp->t_line = snooplinedisc;
5310739a0dcSUgen J.S. Antsilevich 		snp->snp_tty = tp;
53277f77631SPaul Traina 		snp->snp_target = tdev;
53377f77631SPaul Traina 
534964587caSUgen J.S. Antsilevich 		/*
535964587caSUgen J.S. Antsilevich 		 * Clean overflow and down flags -
536964587caSUgen J.S. Antsilevich 		 * we'll have a chance to get them in the future :)))
537964587caSUgen J.S. Antsilevich 		 */
538964587caSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
539019b4d63SUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_DOWN;
540dde8a05bSUgen J.S. Antsilevich 		splx(s);
541dde8a05bSUgen J.S. Antsilevich 		break;
54277f77631SPaul Traina 
543dde8a05bSUgen J.S. Antsilevich 	case SNPGTTY:
5440739a0dcSUgen J.S. Antsilevich 		/*
5450739a0dcSUgen J.S. Antsilevich 		 * We keep snp_target field specially to make
5460739a0dcSUgen J.S. Antsilevich 		 * SNPGTTY happy, else we can't know what is device
5470739a0dcSUgen J.S. Antsilevich 		 * major/minor for tty.
5480739a0dcSUgen J.S. Antsilevich 		 */
549f3732fd1SPoul-Henning Kamp 		*((dev_t *)data) = dev2udev(snp->snp_target);
550dde8a05bSUgen J.S. Antsilevich 		break;
551dde8a05bSUgen J.S. Antsilevich 
552dde8a05bSUgen J.S. Antsilevich 	case FIONBIO:
553dde8a05bSUgen J.S. Antsilevich 		break;
55477f77631SPaul Traina 
555dde8a05bSUgen J.S. Antsilevich 	case FIOASYNC:
556dde8a05bSUgen J.S. Antsilevich 		if (*(int *)data)
557dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_ASYNC;
558dde8a05bSUgen J.S. Antsilevich 		else
559dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags &= ~SNOOP_ASYNC;
560dde8a05bSUgen J.S. Antsilevich 		break;
56177f77631SPaul Traina 
562dde8a05bSUgen J.S. Antsilevich 	case FIONREAD:
563dde8a05bSUgen J.S. Antsilevich 		s = spltty();
5640739a0dcSUgen J.S. Antsilevich 		if (snp->snp_tty != NULL)
565dde8a05bSUgen J.S. Antsilevich 			*(int *)data = snp->snp_len;
566dde8a05bSUgen J.S. Antsilevich 		else
567964587caSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_DOWN) {
568964587caSUgen J.S. Antsilevich 				if (snp->snp_flags & SNOOP_OFLOW)
569964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_OFLOW;
570964587caSUgen J.S. Antsilevich 				else
571964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_TTYCLOSE;
572964587caSUgen J.S. Antsilevich 			} else {
573964587caSUgen J.S. Antsilevich 				*(int *)data = SNP_DETACH;
574964587caSUgen J.S. Antsilevich 			}
575dde8a05bSUgen J.S. Antsilevich 		splx(s);
576dde8a05bSUgen J.S. Antsilevich 		break;
57777f77631SPaul Traina 
578dde8a05bSUgen J.S. Antsilevich 	default:
579dde8a05bSUgen J.S. Antsilevich 		return (ENOTTY);
580dde8a05bSUgen J.S. Antsilevich 	}
581dde8a05bSUgen J.S. Antsilevich 	return (0);
582dde8a05bSUgen J.S. Antsilevich }
583dde8a05bSUgen J.S. Antsilevich 
58487f6c662SJulian Elischer static int
585b40ce416SJulian Elischer snppoll(dev, events, td)
58689c9c53dSPoul-Henning Kamp 	struct cdev *dev;
587659ffb48SPeter Wemm 	int events;
588b40ce416SJulian Elischer 	struct thread *td;
589dde8a05bSUgen J.S. Antsilevich {
590f09f49f1SDima Dorfman 	struct snoop *snp;
591f09f49f1SDima Dorfman 	int revents;
592dde8a05bSUgen J.S. Antsilevich 
593f09f49f1SDima Dorfman 	snp = dev->si_drv1;
594f09f49f1SDima Dorfman 	revents = 0;
595019b4d63SUgen J.S. Antsilevich 	/*
596659ffb48SPeter Wemm 	 * If snoop is down, we don't want to poll() forever so we return 1.
597019b4d63SUgen J.S. Antsilevich 	 * Caller should see if we down via FIONREAD ioctl().  The last should
598019b4d63SUgen J.S. Antsilevich 	 * return -1 to indicate down state.
599019b4d63SUgen J.S. Antsilevich 	 */
600dfd5dee1SPeter Wemm 	if (events & (POLLIN | POLLRDNORM)) {
601659ffb48SPeter Wemm 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
602659ffb48SPeter Wemm 			revents |= events & (POLLIN | POLLRDNORM);
603659ffb48SPeter Wemm 		else
604b40ce416SJulian Elischer 			selrecord(td, &snp->snp_sel);
605dfd5dee1SPeter Wemm 	}
606659ffb48SPeter Wemm 	return (revents);
607dde8a05bSUgen J.S. Antsilevich }
608dde8a05bSUgen J.S. Antsilevich 
60987f6c662SJulian Elischer static void
610f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev)
611f09f49f1SDima Dorfman 	void *arg;
612f09f49f1SDima Dorfman 	char *name;
613f09f49f1SDima Dorfman 	int namelen;
61489c9c53dSPoul-Henning Kamp 	struct cdev **dev;
61525c7870eSDima Dorfman {
616b0b03348SPoul-Henning Kamp 	int u, i;
61725c7870eSDima Dorfman 
618f3732fd1SPoul-Henning Kamp 	if (*dev != NULL)
61925c7870eSDima Dorfman 		return;
62025c7870eSDima Dorfman 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
62125c7870eSDima Dorfman 		return;
622b0b03348SPoul-Henning Kamp 	i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0);
623b0b03348SPoul-Henning Kamp 	if (i)
624b0b03348SPoul-Henning Kamp 		*dev = make_dev(&snp_cdevsw, unit2minor(u),
625b0b03348SPoul-Henning Kamp 		     UID_ROOT, GID_WHEEL, 0600, "snp%d", u);
626b0b03348SPoul-Henning Kamp 	if (*dev != NULL)
6272f7a3851SDima Dorfman 		(*dev)->si_flags |= SI_CHEAPCLONE;
62825c7870eSDima Dorfman }
62925c7870eSDima Dorfman 
63047eaa5f5SDima Dorfman static int
631f09f49f1SDima Dorfman snp_modevent(mod, type, data)
632f09f49f1SDima Dorfman 	module_t mod;
633f09f49f1SDima Dorfman 	int type;
634f09f49f1SDima Dorfman 	void *data;
63553ac6efbSJulian Elischer {
636f09f49f1SDima Dorfman 	static eventhandler_tag eh_tag;
63753ac6efbSJulian Elischer 
63847eaa5f5SDima Dorfman 	switch (type) {
63947eaa5f5SDima Dorfman 	case MOD_LOAD:
640f09f49f1SDima Dorfman 		/* XXX error checking. */
6419397290eSPoul-Henning Kamp 		clone_setup(&snpclones);
64247eaa5f5SDima Dorfman 		eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
643f09f49f1SDima Dorfman 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
64447eaa5f5SDima Dorfman 		break;
64547eaa5f5SDima Dorfman 	case MOD_UNLOAD:
646ac60b28dSDima Dorfman 		if (!LIST_EMPTY(&snp_sclist))
647ac60b28dSDima Dorfman 			return (EBUSY);
64847eaa5f5SDima Dorfman 		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
649b0b03348SPoul-Henning Kamp 		clone_cleanup(&snpclones);
650f09f49f1SDima Dorfman 		ldisc_deregister(snooplinedisc);
65147eaa5f5SDima Dorfman 		break;
65247eaa5f5SDima Dorfman 	default:
6533e019deaSPoul-Henning Kamp 		return (EOPNOTSUPP);
65447eaa5f5SDima Dorfman 		break;
65547eaa5f5SDima Dorfman 	}
656f09f49f1SDima Dorfman 	return (0);
6577198bf47SJulian Elischer }
65853ac6efbSJulian Elischer 
65947eaa5f5SDima Dorfman static moduledata_t snp_mod = {
66047eaa5f5SDima Dorfman         "snp",
66147eaa5f5SDima Dorfman         snp_modevent,
66247eaa5f5SDima Dorfman         NULL
66347eaa5f5SDima Dorfman };
664b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
665