ng_tty.c revision 151387
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 151387 2005-10-16 20:44:18Z phk $
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. */
448151387Sphk	tt_oproc(tp);
44952419Sjulian
45052419Sjulian	/* This timeout is needed for operation on a pseudo-tty, because the
45152419Sjulian	 * pty code doesn't call pppstart after it has drained the t_outq. */
452140164Sglebius	/* XXX: outq not locked */
453140164Sglebius	if (!IFQ_IS_EMPTY(&sc->outq) && !callout_pending(&sc->chand))
454138420Sglebius		ng_callout(&sc->chand, sc->node, NULL, 1, ngt_timeout, NULL, 0);
455140164Sglebius
45652419Sjulian	return (0);
45752419Sjulian}
45852419Sjulian
45952419Sjulian/*
46052419Sjulian * We still have data to output to the device, so try sending more.
46152419Sjulian */
46252419Sjulianstatic void
463138420Sglebiusngt_timeout(node_p node, hook_p hook, void *arg1, int arg2)
46452419Sjulian{
465138420Sglebius	const sc_p sc = NG_NODE_PRIVATE(node);
46652419Sjulian
467140164Sglebius	mtx_lock(&Giant);
46852419Sjulian	ngt_start(sc->tp);
469140164Sglebius	mtx_unlock(&Giant);
47052419Sjulian}
47152419Sjulian
47252419Sjulian/******************************************************************
47352419Sjulian		    NETGRAPH NODE METHODS
47452419Sjulian******************************************************************/
47552419Sjulian
47652419Sjulian/*
47752419Sjulian * Initialize a new node of this type.
47852419Sjulian *
47952419Sjulian * We only allow nodes to be created as a result of setting
48052419Sjulian * the line discipline on a tty, so always return an error if not.
48152419Sjulian */
48252419Sjulianstatic int
48370700Sjulianngt_constructor(node_p node)
48452419Sjulian{
48570700Sjulian	return (EOPNOTSUPP);
48652419Sjulian}
48752419Sjulian
48852419Sjulian/*
48952419Sjulian * Add a new hook. There can only be one.
49052419Sjulian */
49152419Sjulianstatic int
49252419Sjulianngt_newhook(node_p node, hook_p hook, const char *name)
49352419Sjulian{
49470784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
49552419Sjulian
49652419Sjulian	if (strcmp(name, NG_TTY_HOOK))
49752419Sjulian		return (EINVAL);
498140164Sglebius
49952419Sjulian	if (sc->hook)
500140164Sglebius		return (EISCONN);
501140164Sglebius
502140164Sglebius	NGTLOCK(sc);
50352419Sjulian	sc->hook = hook;
504140164Sglebius	NGTUNLOCK(sc);
505140164Sglebius
506140164Sglebius	return (0);
50752419Sjulian}
50852419Sjulian
50952419Sjulian/*
510140164Sglebius * Set the hook into queueing mode (for outgoing packets),
511140164Sglebius * so that we wont deliver mbuf thru the whole graph holding
512140164Sglebius * tty locks.
51369922Sjulian */
51469922Sjulianstatic int
51569922Sjulianngt_connect(hook_p hook)
51669922Sjulian{
517140164Sglebius	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
518140164Sglebius	/*
519140164Sglebius	 * XXX: While ngt_start() is Giant-locked, queue incoming
520140164Sglebius	 * packets, too. Otherwise we acquire Giant holding some
521140164Sglebius	 * IP stack locks, e.g. divinp, and this makes WITNESS scream.
522140164Sglebius	 */
523140164Sglebius	NG_HOOK_FORCE_QUEUE(hook);
52469922Sjulian	return (0);
52569922Sjulian}
52669922Sjulian
52769922Sjulian/*
52852419Sjulian * Disconnect the hook
52952419Sjulian */
53052419Sjulianstatic int
53152419Sjulianngt_disconnect(hook_p hook)
53252419Sjulian{
53370784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
53452419Sjulian
53552419Sjulian	if (hook != sc->hook)
53687599Sobrien		panic(__func__);
537140164Sglebius
538140164Sglebius	NGTLOCK(sc);
53952419Sjulian	sc->hook = NULL;
540140164Sglebius	NGTUNLOCK(sc);
541140164Sglebius
54252419Sjulian	return (0);
54352419Sjulian}
54452419Sjulian
54552419Sjulian/*
54652419Sjulian * Remove this node. The does the netgraph portion of the shutdown.
54752419Sjulian * This should only be called indirectly from ngt_close().
548140164Sglebius *
549140164Sglebius * tp->t_lsc is already NULL, so we should be protected from
550140164Sglebius * tty calls now.
55152419Sjulian */
55252419Sjulianstatic int
55352419Sjulianngt_shutdown(node_p node)
55452419Sjulian{
55570784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
55652419Sjulian
557140164Sglebius	NGTLOCK(sc);
558140164Sglebius	if (!(sc->flags & FLG_DIE)) {
559140164Sglebius		NGTUNLOCK(sc);
56052419Sjulian		return (EOPNOTSUPP);
561140164Sglebius	}
562140164Sglebius	NGTUNLOCK(sc);
563140164Sglebius
564140164Sglebius	/* Free resources */
565140164Sglebius	_IF_DRAIN(&sc->outq);
566140164Sglebius	mtx_destroy(&(sc)->outq.ifq_mtx);
567140164Sglebius	m_freem(sc->m);
56870784Sjulian	NG_NODE_UNREF(sc->node);
56952419Sjulian	FREE(sc, M_NETGRAPH);
570140164Sglebius
57152419Sjulian	return (0);
57252419Sjulian}
57352419Sjulian
57452419Sjulian/*
57552419Sjulian * Receive incoming data from netgraph system. Put it on our
57652419Sjulian * output queue and start output if necessary.
57752419Sjulian */
57852419Sjulianstatic int
57970700Sjulianngt_rcvdata(hook_p hook, item_p item)
58052419Sjulian{
58170784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
58270700Sjulian	struct mbuf *m;
583140164Sglebius	int qlen;
58452419Sjulian
58552419Sjulian	if (hook != sc->hook)
58687599Sobrien		panic(__func__);
58770700Sjulian
58870700Sjulian	NGI_GET_M(item, m);
58970700Sjulian	NG_FREE_ITEM(item);
590140164Sglebius
591140164Sglebius	IF_LOCK(&sc->outq);
592140164Sglebius	if (_IF_QFULL(&sc->outq)) {
593140164Sglebius		_IF_DROP(&sc->outq);
594140164Sglebius		IF_UNLOCK(&sc->outq);
595140164Sglebius		NG_FREE_M(m);
596140164Sglebius		return (ENOBUFS);
597140164Sglebius	}
598140164Sglebius
599140164Sglebius	_IF_ENQUEUE(&sc->outq, m);
600140164Sglebius	qlen = sc->outq.ifq_len;
601140164Sglebius	IF_UNLOCK(&sc->outq);
602140164Sglebius
603140164Sglebius	/*
604140164Sglebius	 * If qlen > 1, then we should already have a scheduled callout.
605140164Sglebius	 */
606140164Sglebius	if (qlen == 1) {
607140164Sglebius		mtx_lock(&Giant);
60852419Sjulian		ngt_start(sc->tp);
609140164Sglebius		mtx_unlock(&Giant);
610140164Sglebius	}
611140164Sglebius
612140164Sglebius	return (0);
61352419Sjulian}
61452419Sjulian
61552419Sjulian/*
61652419Sjulian * Receive control message
61752419Sjulian */
61852419Sjulianstatic int
61970700Sjulianngt_rcvmsg(node_p node, item_p item, hook_p lasthook)
62052419Sjulian{
62170784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
622140164Sglebius	struct ng_mesg *msg, *resp = NULL;
62352419Sjulian	int error = 0;
62452419Sjulian
62570700Sjulian	NGI_GET_MSG(item, msg);
62652419Sjulian	switch (msg->header.typecookie) {
62752419Sjulian	case NGM_TTY_COOKIE:
62852419Sjulian		switch (msg->header.cmd) {
62952419Sjulian		case NGM_TTY_SET_HOTCHAR:
63052419Sjulian		    {
63152419Sjulian			int     hotchar;
63252419Sjulian
63352419Sjulian			if (msg->header.arglen != sizeof(int))
63452419Sjulian				ERROUT(EINVAL);
63552419Sjulian			hotchar = *((int *) msg->data);
63652419Sjulian			if (hotchar != (u_char) hotchar && hotchar != -1)
63752419Sjulian				ERROUT(EINVAL);
63852419Sjulian			sc->hotchar = hotchar;	/* race condition is OK */
63952419Sjulian			break;
64052419Sjulian		    }
64152419Sjulian		case NGM_TTY_GET_HOTCHAR:
64252419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
64352419Sjulian			if (!resp)
64452419Sjulian				ERROUT(ENOMEM);
64552419Sjulian			/* Race condition here is OK */
64652419Sjulian			*((int *) resp->data) = sc->hotchar;
64752419Sjulian			break;
64852419Sjulian		default:
64952419Sjulian			ERROUT(EINVAL);
65052419Sjulian		}
65152419Sjulian		break;
65252419Sjulian	default:
65352419Sjulian		ERROUT(EINVAL);
65452419Sjulian	}
65552419Sjuliandone:
65670700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
65770700Sjulian	NG_FREE_MSG(msg);
65852419Sjulian	return (error);
65952419Sjulian}
66052419Sjulian
66152419Sjulian/******************************************************************
66252419Sjulian		    	INITIALIZATION
66352419Sjulian******************************************************************/
66452419Sjulian
66552419Sjulian/*
66652419Sjulian * Handle loading and unloading for this node type
66752419Sjulian */
66852419Sjulianstatic int
66952419Sjulianngt_mod_event(module_t mod, int event, void *data)
67052419Sjulian{
671140164Sglebius	int error = 0;
67252419Sjulian
67352419Sjulian	switch (event) {
67452419Sjulian	case MOD_LOAD:
67552419Sjulian
67652419Sjulian		/* Register line discipline */
677140164Sglebius		mtx_lock(&Giant);
67852441Sjulian		if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
679140164Sglebius			mtx_unlock(&Giant);
68052419Sjulian			log(LOG_ERR, "%s: can't register line discipline",
68187599Sobrien			    __func__);
68252419Sjulian			return (EIO);
68352419Sjulian		}
684140164Sglebius		mtx_unlock(&Giant);
68552419Sjulian		break;
68652419Sjulian
68752419Sjulian	case MOD_UNLOAD:
68852419Sjulian
68952419Sjulian		/* Unregister line discipline */
690140164Sglebius		mtx_lock(&Giant);
69152419Sjulian		ldisc_deregister(ngt_ldisc);
692140164Sglebius		mtx_unlock(&Giant);
69352419Sjulian		break;
69452419Sjulian
69552419Sjulian	default:
69652419Sjulian		error = EOPNOTSUPP;
69752419Sjulian		break;
69852419Sjulian	}
69952419Sjulian	return (error);
70052419Sjulian}
701