xref: /freebsd/sys/dev/snp/snp.c (revision 542a8db549f73516381284ad071b8cc5f750939e)
1098ca2bdSWarner Losh /*-
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>
221ef0fc1dSPoul-Henning Kamp #include <sys/fcntl.h>
2371455815SBruce Evans #include <sys/filio.h>
24a1c995b6SPoul-Henning Kamp #include <sys/malloc.h>
25dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h>
26dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h>
27659ffb48SPeter Wemm #include <sys/poll.h>
28dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h>
29fe12f24bSPoul-Henning Kamp #include <sys/module.h>
30ac60b28dSDima Dorfman #include <sys/queue.h>
3187f6c662SJulian Elischer #include <sys/snoop.h>
321ef0fc1dSPoul-Henning Kamp #include <sys/uio.h>
3330ce1aadSOlivier Houchard #include <sys/file.h>
3430ce1aadSOlivier Houchard #include <sys/vnode.h>
3587f6c662SJulian Elischer 
36ddf5b796SDima Dorfman static	l_close_t	snplclose;
37f09f49f1SDima Dorfman static	l_write_t	snplwrite;
3887f6c662SJulian Elischer static	d_open_t	snpopen;
3987f6c662SJulian Elischer static	d_close_t	snpclose;
4087f6c662SJulian Elischer static	d_read_t	snpread;
4187f6c662SJulian Elischer static	d_write_t	snpwrite;
4287f6c662SJulian Elischer static	d_ioctl_t	snpioctl;
43659ffb48SPeter Wemm static	d_poll_t	snppoll;
4487f6c662SJulian Elischer 
454e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = {
46dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
4763a97efcSBruce Evans 	.d_flags =	D_PSEUDO | D_NEEDGIANT,
487ac40f5fSPoul-Henning Kamp 	.d_open =	snpopen,
497ac40f5fSPoul-Henning Kamp 	.d_close =	snpclose,
507ac40f5fSPoul-Henning Kamp 	.d_read =	snpread,
517ac40f5fSPoul-Henning Kamp 	.d_write =	snpwrite,
527ac40f5fSPoul-Henning Kamp 	.d_ioctl =	snpioctl,
537ac40f5fSPoul-Henning Kamp 	.d_poll =	snppoll,
547ac40f5fSPoul-Henning Kamp 	.d_name =	"snp",
554e2f199eSPoul-Henning Kamp };
5687f6c662SJulian Elischer 
57f09f49f1SDima Dorfman static struct linesw snpdisc = {
58672c05d4SPoul-Henning Kamp 	.l_open =	tty_open,
5966ea137dSRobert Watson 	.l_close =	snplclose,
6066ea137dSRobert Watson 	.l_read =	ttread,
6166ea137dSRobert Watson 	.l_write =	snplwrite,
6266ea137dSRobert Watson 	.l_ioctl =	l_nullioctl,
6366ea137dSRobert Watson 	.l_rint =	ttyinput,
6466ea137dSRobert Watson 	.l_start =	ttstart,
6566ea137dSRobert Watson 	.l_modem =	ttymodem
66f09f49f1SDima Dorfman };
6753ac6efbSJulian Elischer 
68101f105dSDima Dorfman /*
69101f105dSDima Dorfman  * This is the main snoop per-device structure.
70101f105dSDima Dorfman  */
71101f105dSDima Dorfman struct snoop {
72101f105dSDima Dorfman 	LIST_ENTRY(snoop)	snp_list;	/* List glue. */
73cdd530caSDima Dorfman 	int			snp_unit;	/* Device number. */
7489c9c53dSPoul-Henning Kamp 	struct cdev *snp_target;	/* Target tty device. */
75101f105dSDima Dorfman 	struct tty		*snp_tty;	/* Target tty pointer. */
76101f105dSDima Dorfman 	u_long			 snp_len;	/* Possible length. */
77101f105dSDima Dorfman 	u_long			 snp_base;	/* Data base. */
78101f105dSDima Dorfman 	u_long			 snp_blen;	/* Used length. */
79101f105dSDima Dorfman 	caddr_t			 snp_buf;	/* Allocation pointer. */
80101f105dSDima Dorfman 	int			 snp_flags;	/* Flags. */
81101f105dSDima Dorfman 	struct selinfo		 snp_sel;	/* Select info. */
82101f105dSDima Dorfman 	int			 snp_olddisc;	/* Old line discipline. */
83101f105dSDima Dorfman };
84101f105dSDima Dorfman 
85101f105dSDima Dorfman /*
86101f105dSDima Dorfman  * Possible flags.
87101f105dSDima Dorfman  */
88101f105dSDima Dorfman #define SNOOP_ASYNC		0x0002
89101f105dSDima Dorfman #define SNOOP_OPEN		0x0004
90101f105dSDima Dorfman #define SNOOP_RWAIT		0x0008
91101f105dSDima Dorfman #define SNOOP_OFLOW		0x0010
92101f105dSDima Dorfman #define SNOOP_DOWN		0x0020
93101f105dSDima Dorfman 
94101f105dSDima Dorfman /*
95101f105dSDima Dorfman  * Other constants.
96101f105dSDima Dorfman  */
97101f105dSDima Dorfman #define SNOOP_MINLEN		(4*1024)	/* This should be power of 2.
98101f105dSDima Dorfman 						 * 4K tested to be the minimum
99101f105dSDima Dorfman 						 * for which on normal tty
100101f105dSDima Dorfman 						 * usage there is no need to
101101f105dSDima Dorfman 						 * allocate more.
102101f105dSDima Dorfman 						 */
103101f105dSDima Dorfman #define SNOOP_MAXLEN		(64*1024)	/* This one also,64K enough
104101f105dSDima Dorfman 						 * If we grow more,something
105101f105dSDima Dorfman 						 * really bad in this world..
106101f105dSDima Dorfman 						 */
107101f105dSDima Dorfman 
108959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
10947eaa5f5SDima Dorfman /*
11047eaa5f5SDima Dorfman  * The number of the "snoop" line discipline.  This gets determined at
11147eaa5f5SDima Dorfman  * module load time.
11247eaa5f5SDima Dorfman  */
113f09f49f1SDima Dorfman static int snooplinedisc;
114ac60b28dSDima Dorfman 
115ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
116b0b03348SPoul-Henning Kamp static struct clonedevs	  *snpclones;
117ac60b28dSDima Dorfman 
11889c9c53dSPoul-Henning Kamp static struct tty	*snpdevtotty(struct cdev *dev);
1196a113b3dSRobert Watson static void		snp_clone(void *arg, struct ucred *cred, char *name,
12089c9c53dSPoul-Henning Kamp 			    int namelen, struct cdev **dev);
121542a8db5SKonstantin Belousov static void		snp_detach(void *arg);
122e51a25f8SAlfred Perlstein static int		snp_down(struct snoop *snp);
123e51a25f8SAlfred Perlstein static int		snp_in(struct snoop *snp, char *buf, int n);
124e51a25f8SAlfred Perlstein static int		snp_modevent(module_t mod, int what, void *arg);
125b5128f41SOlivier Houchard static struct snoop	*ttytosnp(struct tty *);
126b5128f41SOlivier Houchard 
127b5128f41SOlivier Houchard static struct snoop *
128b5128f41SOlivier Houchard ttytosnp(struct tty *tp)
129b5128f41SOlivier Houchard {
130b5128f41SOlivier Houchard 	struct snoop *snp;
131b5128f41SOlivier Houchard 
132b5128f41SOlivier Houchard 	LIST_FOREACH(snp, &snp_sclist, snp_list) {
133b5128f41SOlivier Houchard 		if (snp->snp_tty == tp)
134b5128f41SOlivier Houchard 			return (snp);
135b5128f41SOlivier Houchard 	}
136b5128f41SOlivier Houchard 	return (NULL);
137b5128f41SOlivier Houchard }
13847eaa5f5SDima Dorfman 
13947eaa5f5SDima Dorfman static int
1407409f6cdSCraig Rodrigues snplclose(struct tty *tp, int flag)
141ddf5b796SDima Dorfman {
142ddf5b796SDima Dorfman 	struct snoop *snp;
143ddf5b796SDima Dorfman 	int error;
144ddf5b796SDima Dorfman 
145b5128f41SOlivier Houchard 	snp = ttytosnp(tp);
146ddf5b796SDima Dorfman 	error = snp_down(snp);
147ddf5b796SDima Dorfman 	if (error != 0)
148ddf5b796SDima Dorfman 		return (error);
149ddf5b796SDima Dorfman 	error = ttylclose(tp, flag);
150ddf5b796SDima Dorfman 	return (error);
151ddf5b796SDima Dorfman }
152ddf5b796SDima Dorfman 
153ddf5b796SDima Dorfman static int
1547409f6cdSCraig Rodrigues snplwrite(struct tty *tp, struct uio *uio, int flag)
15547eaa5f5SDima Dorfman {
15647eaa5f5SDima Dorfman 	struct iovec iov;
15747eaa5f5SDima Dorfman 	struct uio uio2;
158f09f49f1SDima Dorfman 	struct snoop *snp;
159f09f49f1SDima Dorfman 	int error, ilen;
160e692c40cSDima Dorfman 	char *ibuf;
16147eaa5f5SDima Dorfman 
162e692c40cSDima Dorfman 	error = 0;
163e692c40cSDima Dorfman 	ibuf = NULL;
164b5128f41SOlivier Houchard 	snp = ttytosnp(tp);
16587826386SDima Dorfman 	while (uio->uio_resid > 0) {
166e692c40cSDima Dorfman 		ilen = imin(512, uio->uio_resid);
167a163d034SWarner Losh 		ibuf = malloc(ilen, M_SNP, M_WAITOK);
16847eaa5f5SDima Dorfman 		error = uiomove(ibuf, ilen, uio);
169f09f49f1SDima Dorfman 		if (error != 0)
170e692c40cSDima Dorfman 			break;
171f09f49f1SDima Dorfman 		snp_in(snp, ibuf, ilen);
172f09f49f1SDima Dorfman 		/* Hackish, but probably the least of all evils. */
17347eaa5f5SDima Dorfman 		iov.iov_base = ibuf;
17447eaa5f5SDima Dorfman 		iov.iov_len = ilen;
17547eaa5f5SDima Dorfman 		uio2.uio_iov = &iov;
17647eaa5f5SDima Dorfman 		uio2.uio_iovcnt = 1;
17747eaa5f5SDima Dorfman 		uio2.uio_offset = 0;
17847eaa5f5SDima Dorfman 		uio2.uio_resid = ilen;
17947eaa5f5SDima Dorfman 		uio2.uio_segflg = UIO_SYSSPACE;
18047eaa5f5SDima Dorfman 		uio2.uio_rw = UIO_WRITE;
181b40ce416SJulian Elischer 		uio2.uio_td = uio->uio_td;
18247eaa5f5SDima Dorfman 		error = ttwrite(tp, &uio2, flag);
183f09f49f1SDima Dorfman 		if (error != 0)
184e692c40cSDima Dorfman 			break;
185e692c40cSDima Dorfman 		free(ibuf, M_SNP);
186e692c40cSDima Dorfman 		ibuf = NULL;
18747eaa5f5SDima Dorfman 	}
188e692c40cSDima Dorfman 	if (ibuf != NULL)
189e692c40cSDima Dorfman 		free(ibuf, M_SNP);
190e692c40cSDima Dorfman 	return (error);
191ddf5b796SDima Dorfman }
19247eaa5f5SDima Dorfman 
19377f77631SPaul Traina static struct tty *
1947409f6cdSCraig Rodrigues snpdevtotty(struct cdev *dev)
19577f77631SPaul Traina {
196444f003cSBruce Evans 	struct cdevsw *cdp;
197969d098bSPoul-Henning Kamp 	struct tty *tp;
19877f77631SPaul Traina 
199969d098bSPoul-Henning Kamp 	cdp = dev_refthread(dev);
200969d098bSPoul-Henning Kamp 	if (cdp == NULL)
201444f003cSBruce Evans 		return (NULL);
202969d098bSPoul-Henning Kamp 	if (!(cdp->d_flags & D_TTY))
203969d098bSPoul-Henning Kamp 		tp = NULL;
204969d098bSPoul-Henning Kamp 	else
205969d098bSPoul-Henning Kamp 		tp = dev->si_tty;
206969d098bSPoul-Henning Kamp 	dev_relthread(dev);
207969d098bSPoul-Henning Kamp 	return (tp);
20877f77631SPaul Traina }
20977f77631SPaul Traina 
2100739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF	5	/* This is even too much, the maximal
2110739a0dcSUgen J.S. Antsilevich 				 * interactive mode write is 3 bytes
2120739a0dcSUgen J.S. Antsilevich 				 * length for function keys...
2130739a0dcSUgen J.S. Antsilevich 				 */
2140739a0dcSUgen J.S. Antsilevich 
21587f6c662SJulian Elischer static int
2167409f6cdSCraig Rodrigues snpwrite(struct cdev *dev, struct uio *uio, int flag)
2170739a0dcSUgen J.S. Antsilevich {
218f09f49f1SDima Dorfman 	struct snoop *snp;
2190739a0dcSUgen J.S. Antsilevich 	struct tty *tp;
220f09f49f1SDima Dorfman 	int error, i, len;
221f4c5dfbbSDima Dorfman 	unsigned char c[SNP_INPUT_BUF];
2220739a0dcSUgen J.S. Antsilevich 
223f09f49f1SDima Dorfman 	snp = dev->si_drv1;
2240739a0dcSUgen J.S. Antsilevich 	tp = snp->snp_tty;
225f09f49f1SDima Dorfman 	if (tp == NULL)
226f09f49f1SDima Dorfman 		return (EIO);
227b5128f41SOlivier Houchard 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc)
2280739a0dcSUgen J.S. Antsilevich 		goto tty_input;
2290739a0dcSUgen J.S. Antsilevich 
230cdd530caSDima Dorfman 	printf("snp%d: attempt to write to bad tty\n", snp->snp_unit);
2310739a0dcSUgen J.S. Antsilevich 	return (EIO);
2320739a0dcSUgen J.S. Antsilevich 
2330739a0dcSUgen J.S. Antsilevich tty_input:
2340739a0dcSUgen J.S. Antsilevich 	if (!(tp->t_state & TS_ISOPEN))
2350739a0dcSUgen J.S. Antsilevich 		return (EIO);
2360739a0dcSUgen J.S. Antsilevich 
2370739a0dcSUgen J.S. Antsilevich 	while (uio->uio_resid > 0) {
23887826386SDima Dorfman 		len = imin(uio->uio_resid, SNP_INPUT_BUF);
2390739a0dcSUgen J.S. Antsilevich 		if ((error = uiomove(c, len, uio)) != 0)
2400739a0dcSUgen J.S. Antsilevich 			return (error);
2410739a0dcSUgen J.S. Antsilevich 		for (i=0; i < len; i++) {
2420739a0dcSUgen J.S. Antsilevich 			if (ttyinput(c[i], tp))
2430739a0dcSUgen J.S. Antsilevich 				return (EIO);
2440739a0dcSUgen J.S. Antsilevich 		}
2450739a0dcSUgen J.S. Antsilevich 	}
246f09f49f1SDima Dorfman 	return (0);
2470739a0dcSUgen J.S. Antsilevich }
2480739a0dcSUgen J.S. Antsilevich 
2490739a0dcSUgen J.S. Antsilevich 
25087f6c662SJulian Elischer static int
2517409f6cdSCraig Rodrigues snpread(struct cdev *dev, struct uio *uio, int flag)
252dde8a05bSUgen J.S. Antsilevich {
253f09f49f1SDima Dorfman 	struct snoop *snp;
254f09f49f1SDima Dorfman 	int error, len, n, nblen, s;
255dde8a05bSUgen J.S. Antsilevich 	caddr_t from;
256dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
257dde8a05bSUgen J.S. Antsilevich 
258f09f49f1SDima Dorfman 	snp = dev->si_drv1;
2595526d2d9SEivind Eklund 	KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
2605526d2d9SEivind Eklund 	    ("snoop buffer error"));
26177f77631SPaul Traina 
2620739a0dcSUgen J.S. Antsilevich 	if (snp->snp_tty == NULL)
263dde8a05bSUgen J.S. Antsilevich 		return (EIO);
264dde8a05bSUgen J.S. Antsilevich 
265dde8a05bSUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_RWAIT;
266dde8a05bSUgen J.S. Antsilevich 
267dde8a05bSUgen J.S. Antsilevich 	do {
268dde8a05bSUgen J.S. Antsilevich 		if (snp->snp_len == 0) {
2691ef0fc1dSPoul-Henning Kamp 			if (flag & O_NONBLOCK)
270d96bc99dSBruce Evans 				return (EWOULDBLOCK);
271dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_RWAIT;
272521f364bSDag-Erling Smørgrav 			error = tsleep(snp, (PZERO + 1) | PCATCH,
2732c1a6f21SDima Dorfman 			    "snprd", 0);
2742c1a6f21SDima Dorfman 			if (error != 0)
2752c1a6f21SDima Dorfman 				return (error);
276dde8a05bSUgen J.S. Antsilevich 		}
277dde8a05bSUgen J.S. Antsilevich 	} while (snp->snp_len == 0);
278dde8a05bSUgen J.S. Antsilevich 
279019b4d63SUgen J.S. Antsilevich 	n = snp->snp_len;
280dde8a05bSUgen J.S. Antsilevich 
281f09f49f1SDima Dorfman 	error = 0;
282dde8a05bSUgen J.S. Antsilevich 	while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
28387826386SDima Dorfman 		len = min((unsigned)uio->uio_resid, snp->snp_len);
284dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
285dde8a05bSUgen J.S. Antsilevich 		if (len == 0)
286dde8a05bSUgen J.S. Antsilevich 			break;
287dde8a05bSUgen J.S. Antsilevich 
288dde8a05bSUgen J.S. Antsilevich 		error = uiomove(from, len, uio);
289dde8a05bSUgen J.S. Antsilevich 		snp->snp_base += len;
290dde8a05bSUgen J.S. Antsilevich 		snp->snp_len -= len;
291dde8a05bSUgen J.S. Antsilevich 	}
292019b4d63SUgen J.S. Antsilevich 	if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
293dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
294019b4d63SUgen J.S. Antsilevich 	}
295dde8a05bSUgen J.S. Antsilevich 	s = spltty();
296dde8a05bSUgen J.S. Antsilevich 	nblen = snp->snp_blen;
297dde8a05bSUgen J.S. Antsilevich 	if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
298f09f49f1SDima Dorfman 		while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
299dde8a05bSUgen J.S. Antsilevich 			nblen = nblen / 2;
30076e90dbcSBrian Feldman 		if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
301dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
30276e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
303dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
304dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
305dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
306dde8a05bSUgen J.S. Antsilevich 		}
307dde8a05bSUgen J.S. Antsilevich 	}
308dde8a05bSUgen J.S. Antsilevich 	splx(s);
309dde8a05bSUgen J.S. Antsilevich 
310f09f49f1SDima Dorfman 	return (error);
311dde8a05bSUgen J.S. Antsilevich }
312dde8a05bSUgen J.S. Antsilevich 
313f09f49f1SDima Dorfman static int
3147409f6cdSCraig Rodrigues snp_in(struct snoop *snp, char *buf, int n)
315dde8a05bSUgen J.S. Antsilevich {
316dde8a05bSUgen J.S. Antsilevich 	int s_free, s_tail;
317dde8a05bSUgen J.S. Antsilevich 	int s, len, nblen;
318dde8a05bSUgen J.S. Antsilevich 	caddr_t from, to;
319dde8a05bSUgen J.S. Antsilevich 	char *nbuf;
320dde8a05bSUgen J.S. Antsilevich 
321219cbf59SEivind Eklund 	KASSERT(n >= 0, ("negative snoop char count"));
322dde8a05bSUgen J.S. Antsilevich 
323dde8a05bSUgen J.S. Antsilevich 	if (n == 0)
324f09f49f1SDima Dorfman 		return (0);
325dde8a05bSUgen J.S. Antsilevich 
326019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_DOWN) {
327cdd530caSDima Dorfman 		printf("snp%d: more data to down interface\n", snp->snp_unit);
328f09f49f1SDima Dorfman 		return (0);
329019b4d63SUgen J.S. Antsilevich 	}
330964587caSUgen J.S. Antsilevich 
331019b4d63SUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_OFLOW) {
332cdd530caSDima Dorfman 		printf("snp%d: buffer overflow\n", snp->snp_unit);
333019b4d63SUgen J.S. Antsilevich 		/*
334019b4d63SUgen J.S. Antsilevich 		 * On overflow we just repeat the standart close
335019b4d63SUgen J.S. Antsilevich 		 * procedure...yes , this is waste of space but.. Then next
336019b4d63SUgen J.S. Antsilevich 		 * read from device will fail if one would recall he is
337019b4d63SUgen J.S. Antsilevich 		 * snooping and retry...
338019b4d63SUgen J.S. Antsilevich 		 */
339dde8a05bSUgen J.S. Antsilevich 
340f09f49f1SDima Dorfman 		return (snp_down(snp));
341019b4d63SUgen J.S. Antsilevich 	}
342019b4d63SUgen J.S. Antsilevich 	s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
343019b4d63SUgen J.S. Antsilevich 	s_free = snp->snp_blen - snp->snp_len;
344dde8a05bSUgen J.S. Antsilevich 
345dde8a05bSUgen J.S. Antsilevich 
346dde8a05bSUgen J.S. Antsilevich 	if (n > s_free) {
347dde8a05bSUgen J.S. Antsilevich 		s = spltty();
348dde8a05bSUgen J.S. Antsilevich 		nblen = snp->snp_blen;
349dde8a05bSUgen J.S. Antsilevich 		while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
350dde8a05bSUgen J.S. Antsilevich 			nblen = snp->snp_blen * 2;
351dde8a05bSUgen J.S. Antsilevich 			s_free = nblen - (snp->snp_len + snp->snp_base);
352dde8a05bSUgen J.S. Antsilevich 		}
35376e90dbcSBrian Feldman 		if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) {
354dde8a05bSUgen J.S. Antsilevich 			bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
35576e90dbcSBrian Feldman 			free(snp->snp_buf, M_SNP);
356dde8a05bSUgen J.S. Antsilevich 			snp->snp_buf = nbuf;
357dde8a05bSUgen J.S. Antsilevich 			snp->snp_blen = nblen;
358dde8a05bSUgen J.S. Antsilevich 			snp->snp_base = 0;
359dde8a05bSUgen J.S. Antsilevich 		} else {
360dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_OFLOW;
361dde8a05bSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_RWAIT) {
362dde8a05bSUgen J.S. Antsilevich 				snp->snp_flags &= ~SNOOP_RWAIT;
363521f364bSDag-Erling Smørgrav 				wakeup(snp);
364dde8a05bSUgen J.S. Antsilevich 			}
365dde8a05bSUgen J.S. Antsilevich 			splx(s);
366f09f49f1SDima Dorfman 			return (0);
367dde8a05bSUgen J.S. Antsilevich 		}
368dde8a05bSUgen J.S. Antsilevich 		splx(s);
369dde8a05bSUgen J.S. Antsilevich 	}
370dde8a05bSUgen J.S. Antsilevich 	if (n > s_tail) {
371dde8a05bSUgen J.S. Antsilevich 		from = (caddr_t)(snp->snp_buf + snp->snp_base);
372dde8a05bSUgen J.S. Antsilevich 		to = (caddr_t)(snp->snp_buf);
373dde8a05bSUgen J.S. Antsilevich 		len = snp->snp_len;
374dde8a05bSUgen J.S. Antsilevich 		bcopy(from, to, len);
375dde8a05bSUgen J.S. Antsilevich 		snp->snp_base = 0;
376dde8a05bSUgen J.S. Antsilevich 	}
377dde8a05bSUgen J.S. Antsilevich 	to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
378dde8a05bSUgen J.S. Antsilevich 	bcopy(buf, to, n);
379dde8a05bSUgen J.S. Antsilevich 	snp->snp_len += n;
380dde8a05bSUgen J.S. Antsilevich 
381dde8a05bSUgen J.S. Antsilevich 	if (snp->snp_flags & SNOOP_RWAIT) {
382dde8a05bSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_RWAIT;
383521f364bSDag-Erling Smørgrav 		wakeup(snp);
384dde8a05bSUgen J.S. Antsilevich 	}
385512824f8SSeigo Tanimura 	selwakeuppri(&snp->snp_sel, PZERO + 1);
386dde8a05bSUgen J.S. Antsilevich 
387f09f49f1SDima Dorfman 	return (n);
388dde8a05bSUgen J.S. Antsilevich }
389dde8a05bSUgen J.S. Antsilevich 
39087f6c662SJulian Elischer static int
3917409f6cdSCraig Rodrigues snpopen(struct cdev *dev, int flag, int mode, struct thread *td)
392dde8a05bSUgen J.S. Antsilevich {
393dde8a05bSUgen J.S. Antsilevich 	struct snoop *snp;
394dde8a05bSUgen J.S. Antsilevich 
39576e90dbcSBrian Feldman 	if (dev->si_drv1 == NULL) {
396b0b03348SPoul-Henning Kamp 		dev->si_flags &= ~SI_CHEAPCLONE;
397f09f49f1SDima Dorfman 		dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP,
398a163d034SWarner Losh 		    M_WAITOK | M_ZERO);
399cdd530caSDima Dorfman 		snp->snp_unit = dev2unit(dev);
40076e90dbcSBrian Feldman 	} else
40176e90dbcSBrian Feldman 		return (EBUSY);
40277f77631SPaul Traina 
403019b4d63SUgen J.S. Antsilevich 	/*
404019b4d63SUgen J.S. Antsilevich 	 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
405019b4d63SUgen J.S. Antsilevich 	 * all previous settings (especially SNOOP_OFLOW) will be cleared.
406019b4d63SUgen J.S. Antsilevich 	 */
407019b4d63SUgen J.S. Antsilevich 	snp->snp_flags = SNOOP_OPEN;
408dde8a05bSUgen J.S. Antsilevich 
409a163d034SWarner Losh 	snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
410dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = SNOOP_MINLEN;
411dde8a05bSUgen J.S. Antsilevich 	snp->snp_base = 0;
412dde8a05bSUgen J.S. Antsilevich 	snp->snp_len = 0;
413dde8a05bSUgen J.S. Antsilevich 
414dde8a05bSUgen J.S. Antsilevich 	/*
4150739a0dcSUgen J.S. Antsilevich 	 * snp_tty == NULL  is for inactive snoop devices.
416dde8a05bSUgen J.S. Antsilevich 	 */
4170739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
418f3732fd1SPoul-Henning Kamp 	snp->snp_target = NULL;
419ac60b28dSDima Dorfman 
420ac60b28dSDima Dorfman 	LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
421dde8a05bSUgen J.S. Antsilevich 	return (0);
422dde8a05bSUgen J.S. Antsilevich }
423dde8a05bSUgen J.S. Antsilevich 
424019b4d63SUgen J.S. Antsilevich 
425542a8db5SKonstantin Belousov static void
426542a8db5SKonstantin Belousov snp_detach(void *arg)
427019b4d63SUgen J.S. Antsilevich {
428542a8db5SKonstantin Belousov 	struct snoop *snp;
429019b4d63SUgen J.S. Antsilevich 	struct tty *tp;
430019b4d63SUgen J.S. Antsilevich 
431542a8db5SKonstantin Belousov 	snp = (struct snoop *)arg;
432019b4d63SUgen J.S. Antsilevich 	snp->snp_base = 0;
433019b4d63SUgen J.S. Antsilevich 	snp->snp_len = 0;
434019b4d63SUgen J.S. Antsilevich 
435019b4d63SUgen J.S. Antsilevich 	/*
436019b4d63SUgen J.S. Antsilevich 	 * If line disc. changed we do not touch this pointer, SLIP/PPP will
437019b4d63SUgen J.S. Antsilevich 	 * change it anyway.
438019b4d63SUgen J.S. Antsilevich 	 */
439f09f49f1SDima Dorfman 	tp = snp->snp_tty;
440f09f49f1SDima Dorfman 	if (tp == NULL)
441964587caSUgen J.S. Antsilevich 		goto detach_notty;
442019b4d63SUgen J.S. Antsilevich 
443b5128f41SOlivier Houchard 	if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) {
444019b4d63SUgen J.S. Antsilevich 		tp->t_state &= ~TS_SNOOP;
44547eaa5f5SDima Dorfman 		tp->t_line = snp->snp_olddisc;
446019b4d63SUgen J.S. Antsilevich 	} else
447cdd530caSDima Dorfman 		printf("snp%d: bad attached tty data\n", snp->snp_unit);
448019b4d63SUgen J.S. Antsilevich 
4490739a0dcSUgen J.S. Antsilevich 	snp->snp_tty = NULL;
450f3732fd1SPoul-Henning Kamp 	snp->snp_target = NULL;
451019b4d63SUgen J.S. Antsilevich 
452964587caSUgen J.S. Antsilevich detach_notty:
453512824f8SSeigo Tanimura 	selwakeuppri(&snp->snp_sel, PZERO + 1);
45476e90dbcSBrian Feldman 	if ((snp->snp_flags & SNOOP_OPEN) == 0)
45576e90dbcSBrian Feldman 		free(snp, M_SNP);
456019b4d63SUgen J.S. Antsilevich }
457019b4d63SUgen J.S. Antsilevich 
45887f6c662SJulian Elischer static int
4597409f6cdSCraig Rodrigues snpclose(struct cdev *dev, int flags, int fmt, struct thread *td)
460dde8a05bSUgen J.S. Antsilevich {
461f09f49f1SDima Dorfman 	struct snoop *snp;
462dde8a05bSUgen J.S. Antsilevich 
463f09f49f1SDima Dorfman 	snp = dev->si_drv1;
464dde8a05bSUgen J.S. Antsilevich 	snp->snp_blen = 0;
465ac60b28dSDima Dorfman 	LIST_REMOVE(snp, snp_list);
46676e90dbcSBrian Feldman 	free(snp->snp_buf, M_SNP);
467019b4d63SUgen J.S. Antsilevich 	snp->snp_flags &= ~SNOOP_OPEN;
46876e90dbcSBrian Feldman 	dev->si_drv1 = NULL;
469542a8db5SKonstantin Belousov 	destroy_dev_sched_cb(dev, snp_detach, snp);
470dde8a05bSUgen J.S. Antsilevich 
471542a8db5SKonstantin Belousov 	return (0);
472dde8a05bSUgen J.S. Antsilevich }
473dde8a05bSUgen J.S. Antsilevich 
474f09f49f1SDima Dorfman static int
4757409f6cdSCraig Rodrigues snp_down(struct snoop *snp)
476964587caSUgen J.S. Antsilevich {
47776e90dbcSBrian Feldman 
47876e90dbcSBrian Feldman 	if (snp->snp_blen != SNOOP_MINLEN) {
47976e90dbcSBrian Feldman 		free(snp->snp_buf, M_SNP);
480a163d034SWarner Losh 		snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
481964587caSUgen J.S. Antsilevich 		snp->snp_blen = SNOOP_MINLEN;
48276e90dbcSBrian Feldman 	}
483964587caSUgen J.S. Antsilevich 	snp->snp_flags |= SNOOP_DOWN;
484542a8db5SKonstantin Belousov 	snp_detach(snp);
485dde8a05bSUgen J.S. Antsilevich 
486542a8db5SKonstantin Belousov 	return (0);
487964587caSUgen J.S. Antsilevich }
488dde8a05bSUgen J.S. Antsilevich 
48987f6c662SJulian Elischer static int
4907409f6cdSCraig Rodrigues snpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
4917409f6cdSCraig Rodrigues     struct thread *td)
492dde8a05bSUgen J.S. Antsilevich {
493f09f49f1SDima Dorfman 	struct snoop *snp;
494dde8a05bSUgen J.S. Antsilevich 	struct tty *tp, *tpo;
49589c9c53dSPoul-Henning Kamp 	struct cdev *tdev;
49630ce1aadSOlivier Houchard 	struct file *fp;
49776e90dbcSBrian Feldman 	int s;
498dde8a05bSUgen J.S. Antsilevich 
499f09f49f1SDima Dorfman 	snp = dev->si_drv1;
500dde8a05bSUgen J.S. Antsilevich 	switch (cmd) {
501dde8a05bSUgen J.S. Antsilevich 	case SNPSTTY:
50230ce1aadSOlivier Houchard 		s = *(int *)data;
5036ed0b39dSRuslan Ermilov 		if (s < 0)
5046ed0b39dSRuslan Ermilov 			return (snp_down(snp));
5056ed0b39dSRuslan Ermilov 		if (fget(td, s, &fp) != 0)
50630ce1aadSOlivier Houchard 			return (EINVAL);
50730ce1aadSOlivier Houchard 		if (fp->f_type != DTYPE_VNODE ||
5086ed0b39dSRuslan Ermilov 		    fp->f_vnode->v_type != VCHR ||
5096ed0b39dSRuslan Ermilov 		    fp->f_vnode->v_rdev == NULL) {
51030ce1aadSOlivier Houchard 			fdrop(fp, td);
51130ce1aadSOlivier Houchard 			return (EINVAL);
51230ce1aadSOlivier Houchard 		}
51330ce1aadSOlivier Houchard 		tdev = fp->f_vnode->v_rdev;
51430ce1aadSOlivier Houchard 		fdrop(fp, td);
515964587caSUgen J.S. Antsilevich 
516444f003cSBruce Evans 		tp = snpdevtotty(tdev);
51777f77631SPaul Traina 		if (!tp)
518dde8a05bSUgen J.S. Antsilevich 			return (EINVAL);
5192c9fb909SDima Dorfman 		if (tp->t_state & TS_SNOOP)
5202c9fb909SDima Dorfman 			return (EBUSY);
521dde8a05bSUgen J.S. Antsilevich 
522dde8a05bSUgen J.S. Antsilevich 		s = spltty();
52377f77631SPaul Traina 
524f3732fd1SPoul-Henning Kamp 		if (snp->snp_target == NULL) {
5250739a0dcSUgen J.S. Antsilevich 			tpo = snp->snp_tty;
52677f77631SPaul Traina 			if (tpo)
527dde8a05bSUgen J.S. Antsilevich 				tpo->t_state &= ~TS_SNOOP;
528dde8a05bSUgen J.S. Antsilevich 		}
52977f77631SPaul Traina 
530dde8a05bSUgen J.S. Antsilevich 		tp->t_state |= TS_SNOOP;
53147eaa5f5SDima Dorfman 		snp->snp_olddisc = tp->t_line;
532f09f49f1SDima Dorfman 		tp->t_line = snooplinedisc;
5330739a0dcSUgen J.S. Antsilevich 		snp->snp_tty = tp;
53477f77631SPaul Traina 		snp->snp_target = tdev;
53577f77631SPaul Traina 
536964587caSUgen J.S. Antsilevich 		/*
537964587caSUgen J.S. Antsilevich 		 * Clean overflow and down flags -
538964587caSUgen J.S. Antsilevich 		 * we'll have a chance to get them in the future :)))
539964587caSUgen J.S. Antsilevich 		 */
540964587caSUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_OFLOW;
541019b4d63SUgen J.S. Antsilevich 		snp->snp_flags &= ~SNOOP_DOWN;
542dde8a05bSUgen J.S. Antsilevich 		splx(s);
543dde8a05bSUgen J.S. Antsilevich 		break;
54477f77631SPaul Traina 
545dde8a05bSUgen J.S. Antsilevich 	case SNPGTTY:
5460739a0dcSUgen J.S. Antsilevich 		/*
5470739a0dcSUgen J.S. Antsilevich 		 * We keep snp_target field specially to make
5480739a0dcSUgen J.S. Antsilevich 		 * SNPGTTY happy, else we can't know what is device
5490739a0dcSUgen J.S. Antsilevich 		 * major/minor for tty.
5500739a0dcSUgen J.S. Antsilevich 		 */
551f3732fd1SPoul-Henning Kamp 		*((dev_t *)data) = dev2udev(snp->snp_target);
552dde8a05bSUgen J.S. Antsilevich 		break;
553dde8a05bSUgen J.S. Antsilevich 
554dde8a05bSUgen J.S. Antsilevich 	case FIONBIO:
555dde8a05bSUgen J.S. Antsilevich 		break;
55677f77631SPaul Traina 
557dde8a05bSUgen J.S. Antsilevich 	case FIOASYNC:
558dde8a05bSUgen J.S. Antsilevich 		if (*(int *)data)
559dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags |= SNOOP_ASYNC;
560dde8a05bSUgen J.S. Antsilevich 		else
561dde8a05bSUgen J.S. Antsilevich 			snp->snp_flags &= ~SNOOP_ASYNC;
562dde8a05bSUgen J.S. Antsilevich 		break;
56377f77631SPaul Traina 
564dde8a05bSUgen J.S. Antsilevich 	case FIONREAD:
565dde8a05bSUgen J.S. Antsilevich 		s = spltty();
5660739a0dcSUgen J.S. Antsilevich 		if (snp->snp_tty != NULL)
567dde8a05bSUgen J.S. Antsilevich 			*(int *)data = snp->snp_len;
568dde8a05bSUgen J.S. Antsilevich 		else
569964587caSUgen J.S. Antsilevich 			if (snp->snp_flags & SNOOP_DOWN) {
570964587caSUgen J.S. Antsilevich 				if (snp->snp_flags & SNOOP_OFLOW)
571964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_OFLOW;
572964587caSUgen J.S. Antsilevich 				else
573964587caSUgen J.S. Antsilevich 					*(int *)data = SNP_TTYCLOSE;
574964587caSUgen J.S. Antsilevich 			} else {
575964587caSUgen J.S. Antsilevich 				*(int *)data = SNP_DETACH;
576964587caSUgen J.S. Antsilevich 			}
577dde8a05bSUgen J.S. Antsilevich 		splx(s);
578dde8a05bSUgen J.S. Antsilevich 		break;
57977f77631SPaul Traina 
580dde8a05bSUgen J.S. Antsilevich 	default:
581dde8a05bSUgen J.S. Antsilevich 		return (ENOTTY);
582dde8a05bSUgen J.S. Antsilevich 	}
583dde8a05bSUgen J.S. Antsilevich 	return (0);
584dde8a05bSUgen J.S. Antsilevich }
585dde8a05bSUgen J.S. Antsilevich 
58687f6c662SJulian Elischer static int
5877409f6cdSCraig Rodrigues snppoll(struct cdev *dev, int events, struct thread *td)
588dde8a05bSUgen J.S. Antsilevich {
589f09f49f1SDima Dorfman 	struct snoop *snp;
590f09f49f1SDima Dorfman 	int revents;
591dde8a05bSUgen J.S. Antsilevich 
592f09f49f1SDima Dorfman 	snp = dev->si_drv1;
593f09f49f1SDima Dorfman 	revents = 0;
594019b4d63SUgen J.S. Antsilevich 	/*
595659ffb48SPeter Wemm 	 * If snoop is down, we don't want to poll() forever so we return 1.
596019b4d63SUgen J.S. Antsilevich 	 * Caller should see if we down via FIONREAD ioctl().  The last should
597019b4d63SUgen J.S. Antsilevich 	 * return -1 to indicate down state.
598019b4d63SUgen J.S. Antsilevich 	 */
599dfd5dee1SPeter Wemm 	if (events & (POLLIN | POLLRDNORM)) {
600659ffb48SPeter Wemm 		if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
601659ffb48SPeter Wemm 			revents |= events & (POLLIN | POLLRDNORM);
602659ffb48SPeter Wemm 		else
603b40ce416SJulian Elischer 			selrecord(td, &snp->snp_sel);
604dfd5dee1SPeter Wemm 	}
605659ffb48SPeter Wemm 	return (revents);
606dde8a05bSUgen J.S. Antsilevich }
607dde8a05bSUgen J.S. Antsilevich 
60887f6c662SJulian Elischer static void
6097409f6cdSCraig Rodrigues snp_clone(void *arg, struct ucred *cred, char *name, int namelen,
6107409f6cdSCraig Rodrigues     struct cdev **dev)
61125c7870eSDima Dorfman {
612b0b03348SPoul-Henning Kamp 	int u, i;
61325c7870eSDima Dorfman 
614f3732fd1SPoul-Henning Kamp 	if (*dev != NULL)
61525c7870eSDima Dorfman 		return;
61625c7870eSDima Dorfman 	if (dev_stdclone(name, NULL, "snp", &u) != 1)
61725c7870eSDima Dorfman 		return;
618b0b03348SPoul-Henning Kamp 	i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0);
619b0b03348SPoul-Henning Kamp 	if (i)
6205977b8feSKonstantin Belousov 		*dev = make_dev_credf(MAKEDEV_REF, &snp_cdevsw, unit2minor(u),
6215977b8feSKonstantin Belousov 		     NULL, UID_ROOT, GID_WHEEL, 0600, "snp%d", u);
622f4f6abcbSPoul-Henning Kamp 	if (*dev != NULL) {
6232f7a3851SDima Dorfman 		(*dev)->si_flags |= SI_CHEAPCLONE;
62425c7870eSDima Dorfman 	}
625f4f6abcbSPoul-Henning Kamp }
62625c7870eSDima Dorfman 
62747eaa5f5SDima Dorfman static int
6287409f6cdSCraig Rodrigues snp_modevent(module_t mod, int type, void *data)
62953ac6efbSJulian Elischer {
630f09f49f1SDima Dorfman 	static eventhandler_tag eh_tag;
63153ac6efbSJulian Elischer 
63247eaa5f5SDima Dorfman 	switch (type) {
63347eaa5f5SDima Dorfman 	case MOD_LOAD:
634f09f49f1SDima Dorfman 		/* XXX error checking. */
6359397290eSPoul-Henning Kamp 		clone_setup(&snpclones);
63647eaa5f5SDima Dorfman 		eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000);
637f09f49f1SDima Dorfman 		snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
63847eaa5f5SDima Dorfman 		break;
63947eaa5f5SDima Dorfman 	case MOD_UNLOAD:
640ac60b28dSDima Dorfman 		if (!LIST_EMPTY(&snp_sclist))
641ac60b28dSDima Dorfman 			return (EBUSY);
64247eaa5f5SDima Dorfman 		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
6435977b8feSKonstantin Belousov 		drain_dev_clone_events();
644b0b03348SPoul-Henning Kamp 		clone_cleanup(&snpclones);
6455977b8feSKonstantin Belousov 		destroy_dev_drain(&snp_cdevsw);
646f09f49f1SDima Dorfman 		ldisc_deregister(snooplinedisc);
64747eaa5f5SDima Dorfman 		break;
64847eaa5f5SDima Dorfman 	default:
6493e019deaSPoul-Henning Kamp 		return (EOPNOTSUPP);
65047eaa5f5SDima Dorfman 		break;
65147eaa5f5SDima Dorfman 	}
652f09f49f1SDima Dorfman 	return (0);
6537198bf47SJulian Elischer }
65453ac6efbSJulian Elischer 
65547eaa5f5SDima Dorfman static moduledata_t snp_mod = {
65647eaa5f5SDima Dorfman         "snp",
65747eaa5f5SDima Dorfman         snp_modevent,
65847eaa5f5SDima Dorfman         NULL
65947eaa5f5SDima Dorfman };
660b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
661