ng_tty.c revision 151085
152419Sjulian/*
252419Sjulian * ng_tty.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
852419Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1952419Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3952419Sjulian *
4052419Sjulian * $FreeBSD: head/sys/netgraph/ng_tty.c 151085 2005-10-08 11:03:29Z tanimura $
4152752Sjulian * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $
4252419Sjulian */
4352419Sjulian
4452419Sjulian/*
4552419Sjulian * This file implements a terminal line discipline that is also a
4652419Sjulian * netgraph node. Installing this line discipline on a terminal device
4752419Sjulian * instantiates a new netgraph node of this type, which allows access
4852419Sjulian * to the device via the "hook" hook of the node.
4952419Sjulian *
5052419Sjulian * Once the line discipline is installed, you can find out the name
5152419Sjulian * of the corresponding netgraph node via a NGIOCGINFO ioctl().
5252419Sjulian *
5352419Sjulian * Incoming characters are delievered to the hook one at a time, each
5452419Sjulian * in its own mbuf. You may optionally define a ``hotchar,'' which causes
5552419Sjulian * incoming characters to be buffered up until either the hotchar is
5652419Sjulian * seen or the mbuf is full (MHLEN bytes). Then all buffered characters
5752419Sjulian * are immediately delivered.
5852419Sjulian */
5952419Sjulian
6052419Sjulian#include <sys/param.h>
6152419Sjulian#include <sys/systm.h>
62140164Sglebius#include <sys/conf.h>
63140164Sglebius#include <sys/errno.h>
64140164Sglebius#include <sys/fcntl.h>
65140164Sglebius#include <sys/ioccom.h>
6652419Sjulian#include <sys/kernel.h>
67140164Sglebius#include <sys/malloc.h>
6852419Sjulian#include <sys/mbuf.h>
69140164Sglebius#include <sys/socket.h>
70140164Sglebius#include <sys/syslog.h>
7152419Sjulian#include <sys/tty.h>
7252441Sjulian#include <sys/ttycom.h>
7352419Sjulian
74140164Sglebius#include <net/if.h>
75140164Sglebius#include <net/if_var.h>
76140164Sglebius
7752419Sjulian#include <netgraph/ng_message.h>
7852419Sjulian#include <netgraph/netgraph.h>
7952419Sjulian#include <netgraph/ng_tty.h>
8052419Sjulian
8152419Sjulian/* Misc defs */
8252419Sjulian#define MAX_MBUFQ		3	/* Max number of queued mbufs */
8352419Sjulian#define NGT_HIWATER		400	/* High water mark on output */
8452419Sjulian
8552419Sjulian/* Per-node private info */
8652419Sjulianstruct ngt_sc {
8753913Sarchie	struct	tty *tp;		/* Terminal device */
8853913Sarchie	node_p	node;			/* Netgraph node */
8953913Sarchie	hook_p	hook;			/* Netgraph hook */
90140164Sglebius	struct	ifqueue outq;		/* Queue of outgoing data */
9153913Sarchie	struct	mbuf *m;		/* Incoming data buffer */
9253913Sarchie	short	hotchar;		/* Hotchar, or -1 if none */
9353913Sarchie	u_int	flags;			/* Flags */
94138420Sglebius	struct	callout	chand;		/* See man timeout(9) */
9552419Sjulian};
9652419Sjuliantypedef struct ngt_sc *sc_p;
9752419Sjulian
9852419Sjulian/* Flags */
9952419Sjulian#define FLG_DEBUG		0x0002
100140164Sglebius#define	FLG_DIE			0x0004
10152419Sjulian
10252419Sjulian/* Line discipline methods */
103130585Sphkstatic int	ngt_open(struct cdev *dev, struct tty *tp);
10452419Sjulianstatic int	ngt_close(struct tty *tp, int flag);
10552419Sjulianstatic int	ngt_read(struct tty *tp, struct uio *uio, int flag);
10652419Sjulianstatic int	ngt_write(struct tty *tp, struct uio *uio, int flag);
10752419Sjulianstatic int	ngt_tioctl(struct tty *tp,
10883366Sjulian		    u_long cmd, caddr_t data, int flag, struct thread *);
10952419Sjulianstatic int	ngt_input(int c, struct tty *tp);
11052419Sjulianstatic int	ngt_start(struct tty *tp);
11152419Sjulian
11252419Sjulian/* Netgraph methods */
11352752Sjulianstatic ng_constructor_t	ngt_constructor;
11452752Sjulianstatic ng_rcvmsg_t	ngt_rcvmsg;
11552752Sjulianstatic ng_shutdown_t	ngt_shutdown;
11652752Sjulianstatic ng_newhook_t	ngt_newhook;
11769922Sjulianstatic ng_connect_t	ngt_connect;
11852752Sjulianstatic ng_rcvdata_t	ngt_rcvdata;
11952752Sjulianstatic ng_disconnect_t	ngt_disconnect;
120140164Sglebiusstatic int		ngt_mod_event(module_t mod, int event, void *data);
12152419Sjulian
12252419Sjulian/* Other stuff */
123138420Sglebiusstatic void	ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2);
12452419Sjulian
12552419Sjulian#define ERROUT(x)		do { error = (x); goto done; } while (0)
12652419Sjulian
12752419Sjulian/* Line discipline descriptor */
12852419Sjulianstatic struct linesw ngt_disc = {
129140164Sglebius	.l_open =	ngt_open,
130140164Sglebius	.l_close =	ngt_close,
131140164Sglebius	.l_read =	ngt_read,
132140164Sglebius	.l_write =	ngt_write,
133140164Sglebius	.l_ioctl =	ngt_tioctl,
134140164Sglebius	.l_rint =	ngt_input,
135140164Sglebius	.l_start =	ngt_start,
136140164Sglebius	.l_modem =	ttymodem,
13752419Sjulian};
13852419Sjulian
13952419Sjulian/* Netgraph node type descriptor */
14052419Sjulianstatic struct ng_type typestruct = {
141129823Sjulian	.version =	NG_ABI_VERSION,
142129823Sjulian	.name =		NG_TTY_NODE_TYPE,
143129823Sjulian	.mod_event =	ngt_mod_event,
144129823Sjulian	.constructor =	ngt_constructor,
145129823Sjulian	.rcvmsg =	ngt_rcvmsg,
146129823Sjulian	.shutdown =	ngt_shutdown,
147129823Sjulian	.newhook =	ngt_newhook,
148129823Sjulian	.connect =	ngt_connect,
149129823Sjulian	.rcvdata =	ngt_rcvdata,
150129823Sjulian	.disconnect =	ngt_disconnect,
15152419Sjulian};
15252419SjulianNETGRAPH_INIT(tty, &typestruct);
15352419Sjulian
154132163Srwatson/*
155140164Sglebius * Locking:
156140164Sglebius *
157140164Sglebius * - node private data and tp->t_lsc is protected by mutex in struct
158140164Sglebius *   ifqueue, locking is done using IF_XXX() macros.
159140164Sglebius * - in all tty methods we should acquire node ifqueue mutex, when accessing
160140164Sglebius *   private data.
161140164Sglebius * - in _rcvdata() we should use locked versions of IF_{EN,DE}QUEUE() since
162140164Sglebius *   we may have multiple _rcvdata() threads.
163140164Sglebius * - when calling any of tty methods from netgraph methods, we should
164140164Sglebius *   acquire tty locking (now Giant).
165140164Sglebius *
166140164Sglebius * - ngt_unit is incremented atomically.
167132163Srwatson */
168140164Sglebius
169140164Sglebius#define	NGTLOCK(sc)	IF_LOCK(&sc->outq)
170140164Sglebius#define	NGTUNLOCK(sc)	IF_UNLOCK(&sc->outq)
171140164Sglebius
17252419Sjulianstatic int ngt_unit;
17352442Sjulianstatic int ngt_ldisc;
17452419Sjulian
17552419Sjulian/******************************************************************
17652419Sjulian		    LINE DISCIPLINE METHODS
17752419Sjulian******************************************************************/
17852419Sjulian
17952419Sjulian/*
18052419Sjulian * Set our line discipline on the tty.
181140164Sglebius * Called from device open routine or ttioctl()
18252419Sjulian */
18352419Sjulianstatic int
184130585Sphkngt_open(struct cdev *dev, struct tty *tp)
18552419Sjulian{
18683366Sjulian	struct thread *const td = curthread;	/* XXX */
18752419Sjulian	char name[sizeof(NG_TTY_NODE_TYPE) + 8];
18852419Sjulian	sc_p sc;
189140164Sglebius	int error;
19052419Sjulian
19152419Sjulian	/* Super-user only */
19293593Sjhb	if ((error = suser(td)))
19352419Sjulian		return (error);
19452419Sjulian
19552419Sjulian	/* Initialize private struct */
196111119Simp	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
197140164Sglebius	if (sc == NULL)
198140164Sglebius		return (ENOMEM);
199140164Sglebius
20052419Sjulian	sc->tp = tp;
201140164Sglebius	sc->hotchar = tp->t_hotchar = NG_TTY_DFL_HOTCHAR;
202140164Sglebius	mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF);
203140164Sglebius	IFQ_SET_MAXLEN(&sc->outq, MAX_MBUFQ);
20452419Sjulian
205140164Sglebius	NGTLOCK(sc);
206140164Sglebius
20752419Sjulian	/* Setup netgraph node */
20852419Sjulian	error = ng_make_node_common(&typestruct, &sc->node);
20952419Sjulian	if (error) {
210140164Sglebius		NGTUNLOCK(sc);
21152419Sjulian		FREE(sc, M_NETGRAPH);
212140164Sglebius		return (error);
21352419Sjulian	}
21452419Sjulian
215140164Sglebius	atomic_add_int(&ngt_unit, 1);
216140164Sglebius	snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit);
217140164Sglebius
21852419Sjulian	/* Assign node its name */
21952419Sjulian	if ((error = ng_name_node(sc->node, name))) {
220140164Sglebius		sc->flags |= FLG_DIE;
221140164Sglebius		NGTUNLOCK(sc);
222140164Sglebius		NG_NODE_UNREF(sc->node);
22352419Sjulian		log(LOG_ERR, "%s: node name exists?\n", name);
224140164Sglebius		return (error);
22552419Sjulian	}
22652419Sjulian
22770700Sjulian	/* Set back pointers */
22870784Sjulian	NG_NODE_SET_PRIVATE(sc->node, sc);
229135406Sphk	tp->t_lsc = sc;
23070700Sjulian
231140164Sglebius	ng_callout_init(&sc->chand);
232140164Sglebius
23352419Sjulian	/*
23452419Sjulian	 * Pre-allocate cblocks to the an appropriate amount.
23552419Sjulian	 * I'm not sure what is appropriate.
23652419Sjulian	 */
23752419Sjulian	ttyflush(tp, FREAD | FWRITE);
23852419Sjulian	clist_alloc_cblocks(&tp->t_canq, 0, 0);
23952419Sjulian	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
24052419Sjulian	clist_alloc_cblocks(&tp->t_outq,
24152419Sjulian	    MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
24252419Sjulian
243140164Sglebius	NGTUNLOCK(sc);
244140164Sglebius
245140164Sglebius	return (0);
24652419Sjulian}
24752419Sjulian
24852419Sjulian/*
24952419Sjulian * Line specific close routine, called from device close routine
250140164Sglebius * and from ttioctl. This causes the node to be destroyed as well.
25152419Sjulian */
25252419Sjulianstatic int
25352419Sjulianngt_close(struct tty *tp, int flag)
25452419Sjulian{
255135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
25652419Sjulian
25752419Sjulian	ttyflush(tp, FREAD | FWRITE);
25852419Sjulian	clist_free_cblocks(&tp->t_outq);
25952419Sjulian	if (sc != NULL) {
260140164Sglebius		NGTLOCK(sc);
261140164Sglebius		if (callout_pending(&sc->chand))
262138420Sglebius			ng_uncallout(&sc->chand, sc->node);
263140164Sglebius		tp->t_lsc = NULL;
264140164Sglebius		sc->flags |= FLG_DIE;
265140164Sglebius		NGTUNLOCK(sc);
26670700Sjulian		ng_rmnode_self(sc->node);
26752419Sjulian	}
26852419Sjulian	return (0);
26952419Sjulian}
27052419Sjulian
27152419Sjulian/*
27252419Sjulian * Once the device has been turned into a node, we don't allow reading.
27352419Sjulian */
27452419Sjulianstatic int
27552419Sjulianngt_read(struct tty *tp, struct uio *uio, int flag)
27652419Sjulian{
27752419Sjulian	return (EIO);
27852419Sjulian}
27952419Sjulian
28052419Sjulian/*
28152419Sjulian * Once the device has been turned into a node, we don't allow writing.
28252419Sjulian */
28352419Sjulianstatic int
28452419Sjulianngt_write(struct tty *tp, struct uio *uio, int flag)
28552419Sjulian{
28652419Sjulian	return (EIO);
28752419Sjulian}
28852419Sjulian
28952419Sjulian/*
29052419Sjulian * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
29152419Sjulian */
29252419Sjulianstatic int
29383366Sjulianngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td)
29452419Sjulian{
295135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
29652419Sjulian
297140164Sglebius	if (sc == NULL)
298140164Sglebius		/* No node attached */
299140164Sglebius		return (0);
300140164Sglebius
30152419Sjulian	switch (cmd) {
30252419Sjulian	case NGIOCGINFO:
30352419Sjulian	    {
30452419Sjulian		struct nodeinfo *const ni = (struct nodeinfo *) data;
30552419Sjulian		const node_p node = sc->node;
30652419Sjulian
30752419Sjulian		bzero(ni, sizeof(*ni));
308140164Sglebius		NGTLOCK(sc);
30970784Sjulian		if (NG_NODE_HAS_NAME(node))
31070784Sjulian			strncpy(ni->name, NG_NODE_NAME(node), sizeof(ni->name) - 1);
31170784Sjulian		strncpy(ni->type, node->nd_type->name, sizeof(ni->type) - 1);
31270784Sjulian		ni->id = (u_int32_t) ng_node2ID(node);
31370784Sjulian		ni->hooks = NG_NODE_NUMHOOKS(node);
314140164Sglebius		NGTUNLOCK(sc);
31552419Sjulian		break;
31652419Sjulian	    }
31752419Sjulian	default:
318140164Sglebius		return (ENOIOCTL);
31952419Sjulian	}
320140164Sglebius
321140164Sglebius	return (0);
32252419Sjulian}
32352419Sjulian
32452419Sjulian/*
32552419Sjulian * Receive data coming from the device. We get one character at
32652419Sjulian * a time, which is kindof silly.
327140164Sglebius *
328140164Sglebius * Full locking of softc is not required, since we are the only
329140164Sglebius * user of sc->m.
33052419Sjulian */
33152419Sjulianstatic int
33252419Sjulianngt_input(int c, struct tty *tp)
33352419Sjulian{
334151085Stanimura	sc_p sc;
335151085Stanimura	node_p node;
33652419Sjulian	struct mbuf *m;
337140164Sglebius	int error = 0;
33852419Sjulian
339151085Stanimura	sc = (sc_p) tp->t_lsc;
340140164Sglebius	if (sc == NULL)
341140164Sglebius		/* No node attached */
34252419Sjulian		return (0);
34352419Sjulian
344151085Stanimura	node = sc->node;
345151085Stanimura
346140164Sglebius	if (tp != sc->tp)
347140164Sglebius		panic("ngt_input");
348140164Sglebius
34952419Sjulian	/* Check for error conditions */
35052419Sjulian	if ((tp->t_state & TS_CONNECTED) == 0) {
35152419Sjulian		if (sc->flags & FLG_DEBUG)
35270784Sjulian			log(LOG_DEBUG, "%s: no carrier\n", NG_NODE_NAME(node));
353140164Sglebius		return (0);
35452419Sjulian	}
35552419Sjulian	if (c & TTY_ERRORMASK) {
35652419Sjulian		/* framing error or overrun on this char */
35752419Sjulian		if (sc->flags & FLG_DEBUG)
35852419Sjulian			log(LOG_DEBUG, "%s: line error %x\n",
35970784Sjulian			    NG_NODE_NAME(node), c & TTY_ERRORMASK);
360140164Sglebius		return (0);
36152419Sjulian	}
36252419Sjulian	c &= TTY_CHARMASK;
36352419Sjulian
36452419Sjulian	/* Get a new header mbuf if we need one */
36552419Sjulian	if (!(m = sc->m)) {
366111119Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
36752419Sjulian		if (!m) {
36852419Sjulian			if (sc->flags & FLG_DEBUG)
36952419Sjulian				log(LOG_ERR,
37070784Sjulian				    "%s: can't get mbuf\n", NG_NODE_NAME(node));
371140164Sglebius			return (ENOBUFS);
37252419Sjulian		}
37353284Sarchie		m->m_len = m->m_pkthdr.len = 0;
37453284Sarchie		m->m_pkthdr.rcvif = NULL;
37552419Sjulian		sc->m = m;
37652419Sjulian	}
37752419Sjulian
37852419Sjulian	/* Add char to mbuf */
37952419Sjulian	*mtod(m, u_char *) = c;
38052419Sjulian	m->m_data++;
38152419Sjulian	m->m_len++;
38252419Sjulian	m->m_pkthdr.len++;
38352419Sjulian
38452419Sjulian	/* Ship off mbuf if it's time */
38552419Sjulian	if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
38652419Sjulian		m->m_data = m->m_pktdat;
38752419Sjulian		sc->m = NULL;
388140164Sglebius
389140164Sglebius		/*
390140164Sglebius		 * We have built our mbuf without checking that we actually
391140164Sglebius		 * have a hook to send it. This was done to avoid
392140164Sglebius		 * acquiring mutex on each character. Check now.
393140164Sglebius		 *
394140164Sglebius		 */
395140164Sglebius
396140164Sglebius		NGTLOCK(sc);
397140164Sglebius		if (sc->hook == NULL) {
398140164Sglebius			NGTUNLOCK(sc);
399140164Sglebius			m_freem(m);
400140164Sglebius			return (0);		/* XXX: original behavior */
401140164Sglebius		}
402140164Sglebius		NG_SEND_DATA_ONLY(error, sc->hook, m);	/* Will queue */
403140164Sglebius		NGTUNLOCK(sc);
40452419Sjulian	}
405140164Sglebius
40652419Sjulian	return (error);
40752419Sjulian}
40852419Sjulian
40952419Sjulian/*
41052419Sjulian * This is called when the device driver is ready for more output.
411140164Sglebius * Also called from ngt_rcv_data() when a new mbuf is available for output.
41252419Sjulian */
41352419Sjulianstatic int
41452419Sjulianngt_start(struct tty *tp)
41552419Sjulian{
416135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
41752419Sjulian
41852419Sjulian	while (tp->t_outq.c_cc < NGT_HIWATER) {	/* XXX 2.2 specific ? */
419140164Sglebius		struct mbuf *m;
42052419Sjulian
42152419Sjulian		/* Remove first mbuf from queue */
422140164Sglebius		IF_DEQUEUE(&sc->outq, m);
423140164Sglebius		if (m == NULL)
42452419Sjulian			break;
42552419Sjulian
42652419Sjulian		/* Send as much of it as possible */
427140164Sglebius		while (m != NULL) {
42852419Sjulian			int     sent;
42952419Sjulian
43052419Sjulian			sent = m->m_len
43152419Sjulian			    - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq);
43252419Sjulian			m->m_data += sent;
43352419Sjulian			m->m_len -= sent;
43452419Sjulian			if (m->m_len > 0)
43552419Sjulian				break;	/* device can't take no more */
43690227Sdillon			m = m_free(m);
43752419Sjulian		}
43852419Sjulian
43952419Sjulian		/* Put remainder of mbuf chain (if any) back on queue */
440140164Sglebius		if (m != NULL) {
441140164Sglebius			IF_PREPEND(&sc->outq, m);
44252419Sjulian			break;
44352419Sjulian		}
44452419Sjulian	}
44552419Sjulian
44652419Sjulian	/* Call output process whether or not there is any output. We are
44752419Sjulian	 * being called in lieu of ttstart and must do what it would. */
44852419Sjulian	if (tp->t_oproc != NULL)
44952419Sjulian		(*tp->t_oproc) (tp);
45052419Sjulian
45152419Sjulian	/* This timeout is needed for operation on a pseudo-tty, because the
45252419Sjulian	 * pty code doesn't call pppstart after it has drained the t_outq. */
453140164Sglebius	/* XXX: outq not locked */
454140164Sglebius	if (!IFQ_IS_EMPTY(&sc->outq) && !callout_pending(&sc->chand))
455138420Sglebius		ng_callout(&sc->chand, sc->node, NULL, 1, ngt_timeout, NULL, 0);
456140164Sglebius
45752419Sjulian	return (0);
45852419Sjulian}
45952419Sjulian
46052419Sjulian/*
46152419Sjulian * We still have data to output to the device, so try sending more.
46252419Sjulian */
46352419Sjulianstatic void
464138420Sglebiusngt_timeout(node_p node, hook_p hook, void *arg1, int arg2)
46552419Sjulian{
466138420Sglebius	const sc_p sc = NG_NODE_PRIVATE(node);
46752419Sjulian
468140164Sglebius	mtx_lock(&Giant);
46952419Sjulian	ngt_start(sc->tp);
470140164Sglebius	mtx_unlock(&Giant);
47152419Sjulian}
47252419Sjulian
47352419Sjulian/******************************************************************
47452419Sjulian		    NETGRAPH NODE METHODS
47552419Sjulian******************************************************************/
47652419Sjulian
47752419Sjulian/*
47852419Sjulian * Initialize a new node of this type.
47952419Sjulian *
48052419Sjulian * We only allow nodes to be created as a result of setting
48152419Sjulian * the line discipline on a tty, so always return an error if not.
48252419Sjulian */
48352419Sjulianstatic int
48470700Sjulianngt_constructor(node_p node)
48552419Sjulian{
48670700Sjulian	return (EOPNOTSUPP);
48752419Sjulian}
48852419Sjulian
48952419Sjulian/*
49052419Sjulian * Add a new hook. There can only be one.
49152419Sjulian */
49252419Sjulianstatic int
49352419Sjulianngt_newhook(node_p node, hook_p hook, const char *name)
49452419Sjulian{
49570784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
49652419Sjulian
49752419Sjulian	if (strcmp(name, NG_TTY_HOOK))
49852419Sjulian		return (EINVAL);
499140164Sglebius
50052419Sjulian	if (sc->hook)
501140164Sglebius		return (EISCONN);
502140164Sglebius
503140164Sglebius	NGTLOCK(sc);
50452419Sjulian	sc->hook = hook;
505140164Sglebius	NGTUNLOCK(sc);
506140164Sglebius
507140164Sglebius	return (0);
50852419Sjulian}
50952419Sjulian
51052419Sjulian/*
511140164Sglebius * Set the hook into queueing mode (for outgoing packets),
512140164Sglebius * so that we wont deliver mbuf thru the whole graph holding
513140164Sglebius * tty locks.
51469922Sjulian */
51569922Sjulianstatic int
51669922Sjulianngt_connect(hook_p hook)
51769922Sjulian{
518140164Sglebius	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
519140164Sglebius	/*
520140164Sglebius	 * XXX: While ngt_start() is Giant-locked, queue incoming
521140164Sglebius	 * packets, too. Otherwise we acquire Giant holding some
522140164Sglebius	 * IP stack locks, e.g. divinp, and this makes WITNESS scream.
523140164Sglebius	 */
524140164Sglebius	NG_HOOK_FORCE_QUEUE(hook);
52569922Sjulian	return (0);
52669922Sjulian}
52769922Sjulian
52869922Sjulian/*
52952419Sjulian * Disconnect the hook
53052419Sjulian */
53152419Sjulianstatic int
53252419Sjulianngt_disconnect(hook_p hook)
53352419Sjulian{
53470784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
53552419Sjulian
53652419Sjulian	if (hook != sc->hook)
53787599Sobrien		panic(__func__);
538140164Sglebius
539140164Sglebius	NGTLOCK(sc);
54052419Sjulian	sc->hook = NULL;
541140164Sglebius	NGTUNLOCK(sc);
542140164Sglebius
54352419Sjulian	return (0);
54452419Sjulian}
54552419Sjulian
54652419Sjulian/*
54752419Sjulian * Remove this node. The does the netgraph portion of the shutdown.
54852419Sjulian * This should only be called indirectly from ngt_close().
549140164Sglebius *
550140164Sglebius * tp->t_lsc is already NULL, so we should be protected from
551140164Sglebius * tty calls now.
55252419Sjulian */
55352419Sjulianstatic int
55452419Sjulianngt_shutdown(node_p node)
55552419Sjulian{
55670784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
55752419Sjulian
558140164Sglebius	NGTLOCK(sc);
559140164Sglebius	if (!(sc->flags & FLG_DIE)) {
560140164Sglebius		NGTUNLOCK(sc);
56152419Sjulian		return (EOPNOTSUPP);
562140164Sglebius	}
563140164Sglebius	NGTUNLOCK(sc);
564140164Sglebius
565140164Sglebius	/* Free resources */
566140164Sglebius	_IF_DRAIN(&sc->outq);
567140164Sglebius	mtx_destroy(&(sc)->outq.ifq_mtx);
568140164Sglebius	m_freem(sc->m);
56970784Sjulian	NG_NODE_UNREF(sc->node);
57052419Sjulian	FREE(sc, M_NETGRAPH);
571140164Sglebius
57252419Sjulian	return (0);
57352419Sjulian}
57452419Sjulian
57552419Sjulian/*
57652419Sjulian * Receive incoming data from netgraph system. Put it on our
57752419Sjulian * output queue and start output if necessary.
57852419Sjulian */
57952419Sjulianstatic int
58070700Sjulianngt_rcvdata(hook_p hook, item_p item)
58152419Sjulian{
58270784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
58370700Sjulian	struct mbuf *m;
584140164Sglebius	int qlen;
58552419Sjulian
58652419Sjulian	if (hook != sc->hook)
58787599Sobrien		panic(__func__);
58870700Sjulian
58970700Sjulian	NGI_GET_M(item, m);
59070700Sjulian	NG_FREE_ITEM(item);
591140164Sglebius
592140164Sglebius	IF_LOCK(&sc->outq);
593140164Sglebius	if (_IF_QFULL(&sc->outq)) {
594140164Sglebius		_IF_DROP(&sc->outq);
595140164Sglebius		IF_UNLOCK(&sc->outq);
596140164Sglebius		NG_FREE_M(m);
597140164Sglebius		return (ENOBUFS);
598140164Sglebius	}
599140164Sglebius
600140164Sglebius	_IF_ENQUEUE(&sc->outq, m);
601140164Sglebius	qlen = sc->outq.ifq_len;
602140164Sglebius	IF_UNLOCK(&sc->outq);
603140164Sglebius
604140164Sglebius	/*
605140164Sglebius	 * If qlen > 1, then we should already have a scheduled callout.
606140164Sglebius	 */
607140164Sglebius	if (qlen == 1) {
608140164Sglebius		mtx_lock(&Giant);
60952419Sjulian		ngt_start(sc->tp);
610140164Sglebius		mtx_unlock(&Giant);
611140164Sglebius	}
612140164Sglebius
613140164Sglebius	return (0);
61452419Sjulian}
61552419Sjulian
61652419Sjulian/*
61752419Sjulian * Receive control message
61852419Sjulian */
61952419Sjulianstatic int
62070700Sjulianngt_rcvmsg(node_p node, item_p item, hook_p lasthook)
62152419Sjulian{
62270784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
623140164Sglebius	struct ng_mesg *msg, *resp = NULL;
62452419Sjulian	int error = 0;
62552419Sjulian
62670700Sjulian	NGI_GET_MSG(item, msg);
62752419Sjulian	switch (msg->header.typecookie) {
62852419Sjulian	case NGM_TTY_COOKIE:
62952419Sjulian		switch (msg->header.cmd) {
63052419Sjulian		case NGM_TTY_SET_HOTCHAR:
63152419Sjulian		    {
63252419Sjulian			int     hotchar;
63352419Sjulian
63452419Sjulian			if (msg->header.arglen != sizeof(int))
63552419Sjulian				ERROUT(EINVAL);
63652419Sjulian			hotchar = *((int *) msg->data);
63752419Sjulian			if (hotchar != (u_char) hotchar && hotchar != -1)
63852419Sjulian				ERROUT(EINVAL);
63952419Sjulian			sc->hotchar = hotchar;	/* race condition is OK */
64052419Sjulian			break;
64152419Sjulian		    }
64252419Sjulian		case NGM_TTY_GET_HOTCHAR:
64352419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
64452419Sjulian			if (!resp)
64552419Sjulian				ERROUT(ENOMEM);
64652419Sjulian			/* Race condition here is OK */
64752419Sjulian			*((int *) resp->data) = sc->hotchar;
64852419Sjulian			break;
64952419Sjulian		default:
65052419Sjulian			ERROUT(EINVAL);
65152419Sjulian		}
65252419Sjulian		break;
65352419Sjulian	default:
65452419Sjulian		ERROUT(EINVAL);
65552419Sjulian	}
65652419Sjuliandone:
65770700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
65870700Sjulian	NG_FREE_MSG(msg);
65952419Sjulian	return (error);
66052419Sjulian}
66152419Sjulian
66252419Sjulian/******************************************************************
66352419Sjulian		    	INITIALIZATION
66452419Sjulian******************************************************************/
66552419Sjulian
66652419Sjulian/*
66752419Sjulian * Handle loading and unloading for this node type
66852419Sjulian */
66952419Sjulianstatic int
67052419Sjulianngt_mod_event(module_t mod, int event, void *data)
67152419Sjulian{
672140164Sglebius	int error = 0;
67352419Sjulian
67452419Sjulian	switch (event) {
67552419Sjulian	case MOD_LOAD:
67652419Sjulian
67752419Sjulian		/* Register line discipline */
678140164Sglebius		mtx_lock(&Giant);
67952441Sjulian		if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
680140164Sglebius			mtx_unlock(&Giant);
68152419Sjulian			log(LOG_ERR, "%s: can't register line discipline",
68287599Sobrien			    __func__);
68352419Sjulian			return (EIO);
68452419Sjulian		}
685140164Sglebius		mtx_unlock(&Giant);
68652419Sjulian		break;
68752419Sjulian
68852419Sjulian	case MOD_UNLOAD:
68952419Sjulian
69052419Sjulian		/* Unregister line discipline */
691140164Sglebius		mtx_lock(&Giant);
69252419Sjulian		ldisc_deregister(ngt_ldisc);
693140164Sglebius		mtx_unlock(&Giant);
69452419Sjulian		break;
69552419Sjulian
69652419Sjulian	default:
69752419Sjulian		error = EOPNOTSUPP;
69852419Sjulian		break;
69952419Sjulian	}
70052419Sjulian	return (error);
70152419Sjulian}
702