ng_tty.c revision 164033
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 164033 2006-11-06 13:42:10Z rwatson $
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>
69164033Srwatson#include <sys/priv.h>
70140164Sglebius#include <sys/socket.h>
71140164Sglebius#include <sys/syslog.h>
7252419Sjulian#include <sys/tty.h>
7352441Sjulian#include <sys/ttycom.h>
7452419Sjulian
75140164Sglebius#include <net/if.h>
76140164Sglebius#include <net/if_var.h>
77140164Sglebius
7852419Sjulian#include <netgraph/ng_message.h>
7952419Sjulian#include <netgraph/netgraph.h>
8052419Sjulian#include <netgraph/ng_tty.h>
8152419Sjulian
8252419Sjulian/* Misc defs */
8352419Sjulian#define MAX_MBUFQ		3	/* Max number of queued mbufs */
8452419Sjulian#define NGT_HIWATER		400	/* High water mark on output */
8552419Sjulian
8652419Sjulian/* Per-node private info */
8752419Sjulianstruct ngt_sc {
8853913Sarchie	struct	tty *tp;		/* Terminal device */
8953913Sarchie	node_p	node;			/* Netgraph node */
9053913Sarchie	hook_p	hook;			/* Netgraph hook */
91140164Sglebius	struct	ifqueue outq;		/* Queue of outgoing data */
9253913Sarchie	struct	mbuf *m;		/* Incoming data buffer */
9353913Sarchie	short	hotchar;		/* Hotchar, or -1 if none */
9453913Sarchie	u_int	flags;			/* Flags */
95138420Sglebius	struct	callout	chand;		/* See man timeout(9) */
9652419Sjulian};
9752419Sjuliantypedef struct ngt_sc *sc_p;
9852419Sjulian
9952419Sjulian/* Flags */
10052419Sjulian#define FLG_DEBUG		0x0002
101140164Sglebius#define	FLG_DIE			0x0004
10252419Sjulian
10352419Sjulian/* Line discipline methods */
104130585Sphkstatic int	ngt_open(struct cdev *dev, struct tty *tp);
10552419Sjulianstatic int	ngt_close(struct tty *tp, int flag);
10652419Sjulianstatic int	ngt_read(struct tty *tp, struct uio *uio, int flag);
10752419Sjulianstatic int	ngt_write(struct tty *tp, struct uio *uio, int flag);
10852419Sjulianstatic int	ngt_tioctl(struct tty *tp,
10983366Sjulian		    u_long cmd, caddr_t data, int flag, struct thread *);
11052419Sjulianstatic int	ngt_input(int c, struct tty *tp);
11152419Sjulianstatic int	ngt_start(struct tty *tp);
11252419Sjulian
11352419Sjulian/* Netgraph methods */
11452752Sjulianstatic ng_constructor_t	ngt_constructor;
11552752Sjulianstatic ng_rcvmsg_t	ngt_rcvmsg;
11652752Sjulianstatic ng_shutdown_t	ngt_shutdown;
11752752Sjulianstatic ng_newhook_t	ngt_newhook;
11869922Sjulianstatic ng_connect_t	ngt_connect;
11952752Sjulianstatic ng_rcvdata_t	ngt_rcvdata;
12052752Sjulianstatic ng_disconnect_t	ngt_disconnect;
121140164Sglebiusstatic int		ngt_mod_event(module_t mod, int event, void *data);
12252419Sjulian
12352419Sjulian/* Other stuff */
124138420Sglebiusstatic void	ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2);
12552419Sjulian
12652419Sjulian#define ERROUT(x)		do { error = (x); goto done; } while (0)
12752419Sjulian
12852419Sjulian/* Line discipline descriptor */
12952419Sjulianstatic struct linesw ngt_disc = {
130140164Sglebius	.l_open =	ngt_open,
131140164Sglebius	.l_close =	ngt_close,
132140164Sglebius	.l_read =	ngt_read,
133140164Sglebius	.l_write =	ngt_write,
134140164Sglebius	.l_ioctl =	ngt_tioctl,
135140164Sglebius	.l_rint =	ngt_input,
136140164Sglebius	.l_start =	ngt_start,
137140164Sglebius	.l_modem =	ttymodem,
13852419Sjulian};
13952419Sjulian
14052419Sjulian/* Netgraph node type descriptor */
14152419Sjulianstatic struct ng_type typestruct = {
142129823Sjulian	.version =	NG_ABI_VERSION,
143129823Sjulian	.name =		NG_TTY_NODE_TYPE,
144129823Sjulian	.mod_event =	ngt_mod_event,
145129823Sjulian	.constructor =	ngt_constructor,
146129823Sjulian	.rcvmsg =	ngt_rcvmsg,
147129823Sjulian	.shutdown =	ngt_shutdown,
148129823Sjulian	.newhook =	ngt_newhook,
149129823Sjulian	.connect =	ngt_connect,
150129823Sjulian	.rcvdata =	ngt_rcvdata,
151129823Sjulian	.disconnect =	ngt_disconnect,
15252419Sjulian};
15352419SjulianNETGRAPH_INIT(tty, &typestruct);
15452419Sjulian
155132163Srwatson/*
156140164Sglebius * Locking:
157140164Sglebius *
158140164Sglebius * - node private data and tp->t_lsc is protected by mutex in struct
159140164Sglebius *   ifqueue, locking is done using IF_XXX() macros.
160140164Sglebius * - in all tty methods we should acquire node ifqueue mutex, when accessing
161140164Sglebius *   private data.
162140164Sglebius * - in _rcvdata() we should use locked versions of IF_{EN,DE}QUEUE() since
163140164Sglebius *   we may have multiple _rcvdata() threads.
164140164Sglebius * - when calling any of tty methods from netgraph methods, we should
165140164Sglebius *   acquire tty locking (now Giant).
166140164Sglebius *
167140164Sglebius * - ngt_unit is incremented atomically.
168132163Srwatson */
169140164Sglebius
170140164Sglebius#define	NGTLOCK(sc)	IF_LOCK(&sc->outq)
171140164Sglebius#define	NGTUNLOCK(sc)	IF_UNLOCK(&sc->outq)
172140164Sglebius
17352419Sjulianstatic int ngt_unit;
17452442Sjulianstatic int ngt_ldisc;
17552419Sjulian
17652419Sjulian/******************************************************************
17752419Sjulian		    LINE DISCIPLINE METHODS
17852419Sjulian******************************************************************/
17952419Sjulian
18052419Sjulian/*
18152419Sjulian * Set our line discipline on the tty.
182140164Sglebius * Called from device open routine or ttioctl()
18352419Sjulian */
18452419Sjulianstatic int
185130585Sphkngt_open(struct cdev *dev, struct tty *tp)
18652419Sjulian{
18783366Sjulian	struct thread *const td = curthread;	/* XXX */
18852419Sjulian	char name[sizeof(NG_TTY_NODE_TYPE) + 8];
18952419Sjulian	sc_p sc;
190140164Sglebius	int error;
19152419Sjulian
19252419Sjulian	/* Super-user only */
193164033Srwatson	error = priv_check(td, PRIV_NETGRAPH_TTY);
194164033Srwatson	if (error)
19552419Sjulian		return (error);
19652419Sjulian
19752419Sjulian	/* Initialize private struct */
198111119Simp	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
199140164Sglebius	if (sc == NULL)
200140164Sglebius		return (ENOMEM);
201140164Sglebius
20252419Sjulian	sc->tp = tp;
203140164Sglebius	sc->hotchar = tp->t_hotchar = NG_TTY_DFL_HOTCHAR;
204140164Sglebius	mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF);
205140164Sglebius	IFQ_SET_MAXLEN(&sc->outq, MAX_MBUFQ);
20652419Sjulian
207140164Sglebius	NGTLOCK(sc);
208140164Sglebius
20952419Sjulian	/* Setup netgraph node */
21052419Sjulian	error = ng_make_node_common(&typestruct, &sc->node);
21152419Sjulian	if (error) {
212140164Sglebius		NGTUNLOCK(sc);
21352419Sjulian		FREE(sc, M_NETGRAPH);
214140164Sglebius		return (error);
21552419Sjulian	}
21652419Sjulian
217140164Sglebius	atomic_add_int(&ngt_unit, 1);
218140164Sglebius	snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit);
219140164Sglebius
22052419Sjulian	/* Assign node its name */
22152419Sjulian	if ((error = ng_name_node(sc->node, name))) {
222140164Sglebius		sc->flags |= FLG_DIE;
223140164Sglebius		NGTUNLOCK(sc);
224140164Sglebius		NG_NODE_UNREF(sc->node);
22552419Sjulian		log(LOG_ERR, "%s: node name exists?\n", name);
226140164Sglebius		return (error);
22752419Sjulian	}
22852419Sjulian
22970700Sjulian	/* Set back pointers */
23070784Sjulian	NG_NODE_SET_PRIVATE(sc->node, sc);
231135406Sphk	tp->t_lsc = sc;
23270700Sjulian
233140164Sglebius	ng_callout_init(&sc->chand);
234140164Sglebius
23552419Sjulian	/*
23652419Sjulian	 * Pre-allocate cblocks to the an appropriate amount.
23752419Sjulian	 * I'm not sure what is appropriate.
23852419Sjulian	 */
23952419Sjulian	ttyflush(tp, FREAD | FWRITE);
24052419Sjulian	clist_alloc_cblocks(&tp->t_canq, 0, 0);
24152419Sjulian	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
24252419Sjulian	clist_alloc_cblocks(&tp->t_outq,
24352419Sjulian	    MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
24452419Sjulian
245140164Sglebius	NGTUNLOCK(sc);
246140164Sglebius
247140164Sglebius	return (0);
24852419Sjulian}
24952419Sjulian
25052419Sjulian/*
25152419Sjulian * Line specific close routine, called from device close routine
252140164Sglebius * and from ttioctl. This causes the node to be destroyed as well.
25352419Sjulian */
25452419Sjulianstatic int
25552419Sjulianngt_close(struct tty *tp, int flag)
25652419Sjulian{
257135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
25852419Sjulian
25952419Sjulian	ttyflush(tp, FREAD | FWRITE);
26052419Sjulian	clist_free_cblocks(&tp->t_outq);
26152419Sjulian	if (sc != NULL) {
262140164Sglebius		NGTLOCK(sc);
263140164Sglebius		if (callout_pending(&sc->chand))
264138420Sglebius			ng_uncallout(&sc->chand, sc->node);
265140164Sglebius		tp->t_lsc = NULL;
266140164Sglebius		sc->flags |= FLG_DIE;
267140164Sglebius		NGTUNLOCK(sc);
26870700Sjulian		ng_rmnode_self(sc->node);
26952419Sjulian	}
27052419Sjulian	return (0);
27152419Sjulian}
27252419Sjulian
27352419Sjulian/*
27452419Sjulian * Once the device has been turned into a node, we don't allow reading.
27552419Sjulian */
27652419Sjulianstatic int
27752419Sjulianngt_read(struct tty *tp, struct uio *uio, int flag)
27852419Sjulian{
27952419Sjulian	return (EIO);
28052419Sjulian}
28152419Sjulian
28252419Sjulian/*
28352419Sjulian * Once the device has been turned into a node, we don't allow writing.
28452419Sjulian */
28552419Sjulianstatic int
28652419Sjulianngt_write(struct tty *tp, struct uio *uio, int flag)
28752419Sjulian{
28852419Sjulian	return (EIO);
28952419Sjulian}
29052419Sjulian
29152419Sjulian/*
29252419Sjulian * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
29352419Sjulian */
29452419Sjulianstatic int
29583366Sjulianngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td)
29652419Sjulian{
297135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
29852419Sjulian
299140164Sglebius	if (sc == NULL)
300140164Sglebius		/* No node attached */
301140164Sglebius		return (0);
302140164Sglebius
30352419Sjulian	switch (cmd) {
30452419Sjulian	case NGIOCGINFO:
30552419Sjulian	    {
30652419Sjulian		struct nodeinfo *const ni = (struct nodeinfo *) data;
30752419Sjulian		const node_p node = sc->node;
30852419Sjulian
30952419Sjulian		bzero(ni, sizeof(*ni));
310140164Sglebius		NGTLOCK(sc);
31170784Sjulian		if (NG_NODE_HAS_NAME(node))
31270784Sjulian			strncpy(ni->name, NG_NODE_NAME(node), sizeof(ni->name) - 1);
31370784Sjulian		strncpy(ni->type, node->nd_type->name, sizeof(ni->type) - 1);
31470784Sjulian		ni->id = (u_int32_t) ng_node2ID(node);
31570784Sjulian		ni->hooks = NG_NODE_NUMHOOKS(node);
316140164Sglebius		NGTUNLOCK(sc);
31752419Sjulian		break;
31852419Sjulian	    }
31952419Sjulian	default:
320140164Sglebius		return (ENOIOCTL);
32152419Sjulian	}
322140164Sglebius
323140164Sglebius	return (0);
32452419Sjulian}
32552419Sjulian
32652419Sjulian/*
32752419Sjulian * Receive data coming from the device. We get one character at
32852419Sjulian * a time, which is kindof silly.
329140164Sglebius *
330140164Sglebius * Full locking of softc is not required, since we are the only
331140164Sglebius * user of sc->m.
33252419Sjulian */
33352419Sjulianstatic int
33452419Sjulianngt_input(int c, struct tty *tp)
33552419Sjulian{
336151085Stanimura	sc_p sc;
337151085Stanimura	node_p node;
33852419Sjulian	struct mbuf *m;
339140164Sglebius	int error = 0;
34052419Sjulian
341151085Stanimura	sc = (sc_p) tp->t_lsc;
342140164Sglebius	if (sc == NULL)
343140164Sglebius		/* No node attached */
34452419Sjulian		return (0);
34552419Sjulian
346151085Stanimura	node = sc->node;
347151085Stanimura
348140164Sglebius	if (tp != sc->tp)
349140164Sglebius		panic("ngt_input");
350140164Sglebius
35152419Sjulian	/* Check for error conditions */
35252419Sjulian	if ((tp->t_state & TS_CONNECTED) == 0) {
35352419Sjulian		if (sc->flags & FLG_DEBUG)
35470784Sjulian			log(LOG_DEBUG, "%s: no carrier\n", NG_NODE_NAME(node));
355140164Sglebius		return (0);
35652419Sjulian	}
35752419Sjulian	if (c & TTY_ERRORMASK) {
35852419Sjulian		/* framing error or overrun on this char */
35952419Sjulian		if (sc->flags & FLG_DEBUG)
36052419Sjulian			log(LOG_DEBUG, "%s: line error %x\n",
36170784Sjulian			    NG_NODE_NAME(node), c & TTY_ERRORMASK);
362140164Sglebius		return (0);
36352419Sjulian	}
36452419Sjulian	c &= TTY_CHARMASK;
36552419Sjulian
36652419Sjulian	/* Get a new header mbuf if we need one */
36752419Sjulian	if (!(m = sc->m)) {
368111119Simp		MGETHDR(m, M_DONTWAIT, MT_DATA);
36952419Sjulian		if (!m) {
37052419Sjulian			if (sc->flags & FLG_DEBUG)
37152419Sjulian				log(LOG_ERR,
37270784Sjulian				    "%s: can't get mbuf\n", NG_NODE_NAME(node));
373140164Sglebius			return (ENOBUFS);
37452419Sjulian		}
37553284Sarchie		m->m_len = m->m_pkthdr.len = 0;
37653284Sarchie		m->m_pkthdr.rcvif = NULL;
37752419Sjulian		sc->m = m;
37852419Sjulian	}
37952419Sjulian
38052419Sjulian	/* Add char to mbuf */
38152419Sjulian	*mtod(m, u_char *) = c;
38252419Sjulian	m->m_data++;
38352419Sjulian	m->m_len++;
38452419Sjulian	m->m_pkthdr.len++;
38552419Sjulian
38652419Sjulian	/* Ship off mbuf if it's time */
38752419Sjulian	if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
38852419Sjulian		m->m_data = m->m_pktdat;
38952419Sjulian		sc->m = NULL;
390140164Sglebius
391140164Sglebius		/*
392140164Sglebius		 * We have built our mbuf without checking that we actually
393140164Sglebius		 * have a hook to send it. This was done to avoid
394140164Sglebius		 * acquiring mutex on each character. Check now.
395140164Sglebius		 *
396140164Sglebius		 */
397140164Sglebius
398140164Sglebius		NGTLOCK(sc);
399140164Sglebius		if (sc->hook == NULL) {
400140164Sglebius			NGTUNLOCK(sc);
401140164Sglebius			m_freem(m);
402140164Sglebius			return (0);		/* XXX: original behavior */
403140164Sglebius		}
404140164Sglebius		NG_SEND_DATA_ONLY(error, sc->hook, m);	/* Will queue */
405140164Sglebius		NGTUNLOCK(sc);
40652419Sjulian	}
407140164Sglebius
40852419Sjulian	return (error);
40952419Sjulian}
41052419Sjulian
41152419Sjulian/*
41252419Sjulian * This is called when the device driver is ready for more output.
413140164Sglebius * Also called from ngt_rcv_data() when a new mbuf is available for output.
41452419Sjulian */
41552419Sjulianstatic int
41652419Sjulianngt_start(struct tty *tp)
41752419Sjulian{
418135406Sphk	const sc_p sc = (sc_p) tp->t_lsc;
41952419Sjulian
42052419Sjulian	while (tp->t_outq.c_cc < NGT_HIWATER) {	/* XXX 2.2 specific ? */
421140164Sglebius		struct mbuf *m;
42252419Sjulian
42352419Sjulian		/* Remove first mbuf from queue */
424140164Sglebius		IF_DEQUEUE(&sc->outq, m);
425140164Sglebius		if (m == NULL)
42652419Sjulian			break;
42752419Sjulian
42852419Sjulian		/* Send as much of it as possible */
429140164Sglebius		while (m != NULL) {
43052419Sjulian			int     sent;
43152419Sjulian
43252419Sjulian			sent = m->m_len
43352419Sjulian			    - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq);
43452419Sjulian			m->m_data += sent;
43552419Sjulian			m->m_len -= sent;
43652419Sjulian			if (m->m_len > 0)
43752419Sjulian				break;	/* device can't take no more */
43890227Sdillon			m = m_free(m);
43952419Sjulian		}
44052419Sjulian
44152419Sjulian		/* Put remainder of mbuf chain (if any) back on queue */
442140164Sglebius		if (m != NULL) {
443140164Sglebius			IF_PREPEND(&sc->outq, m);
44452419Sjulian			break;
44552419Sjulian		}
44652419Sjulian	}
44752419Sjulian
44852419Sjulian	/* Call output process whether or not there is any output. We are
44952419Sjulian	 * being called in lieu of ttstart and must do what it would. */
450151387Sphk	tt_oproc(tp);
45152419Sjulian
45252419Sjulian	/* This timeout is needed for operation on a pseudo-tty, because the
45352419Sjulian	 * pty code doesn't call pppstart after it has drained the t_outq. */
454140164Sglebius	/* XXX: outq not locked */
455140164Sglebius	if (!IFQ_IS_EMPTY(&sc->outq) && !callout_pending(&sc->chand))
456138420Sglebius		ng_callout(&sc->chand, sc->node, NULL, 1, ngt_timeout, NULL, 0);
457140164Sglebius
45852419Sjulian	return (0);
45952419Sjulian}
46052419Sjulian
46152419Sjulian/*
46252419Sjulian * We still have data to output to the device, so try sending more.
46352419Sjulian */
46452419Sjulianstatic void
465138420Sglebiusngt_timeout(node_p node, hook_p hook, void *arg1, int arg2)
46652419Sjulian{
467138420Sglebius	const sc_p sc = NG_NODE_PRIVATE(node);
46852419Sjulian
469140164Sglebius	mtx_lock(&Giant);
47052419Sjulian	ngt_start(sc->tp);
471140164Sglebius	mtx_unlock(&Giant);
47252419Sjulian}
47352419Sjulian
47452419Sjulian/******************************************************************
47552419Sjulian		    NETGRAPH NODE METHODS
47652419Sjulian******************************************************************/
47752419Sjulian
47852419Sjulian/*
47952419Sjulian * Initialize a new node of this type.
48052419Sjulian *
48152419Sjulian * We only allow nodes to be created as a result of setting
48252419Sjulian * the line discipline on a tty, so always return an error if not.
48352419Sjulian */
48452419Sjulianstatic int
48570700Sjulianngt_constructor(node_p node)
48652419Sjulian{
48770700Sjulian	return (EOPNOTSUPP);
48852419Sjulian}
48952419Sjulian
49052419Sjulian/*
49152419Sjulian * Add a new hook. There can only be one.
49252419Sjulian */
49352419Sjulianstatic int
49452419Sjulianngt_newhook(node_p node, hook_p hook, const char *name)
49552419Sjulian{
49670784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
49752419Sjulian
49852419Sjulian	if (strcmp(name, NG_TTY_HOOK))
49952419Sjulian		return (EINVAL);
500140164Sglebius
50152419Sjulian	if (sc->hook)
502140164Sglebius		return (EISCONN);
503140164Sglebius
504140164Sglebius	NGTLOCK(sc);
50552419Sjulian	sc->hook = hook;
506140164Sglebius	NGTUNLOCK(sc);
507140164Sglebius
508140164Sglebius	return (0);
50952419Sjulian}
51052419Sjulian
51152419Sjulian/*
512140164Sglebius * Set the hook into queueing mode (for outgoing packets),
513140164Sglebius * so that we wont deliver mbuf thru the whole graph holding
514140164Sglebius * tty locks.
51569922Sjulian */
51669922Sjulianstatic int
51769922Sjulianngt_connect(hook_p hook)
51869922Sjulian{
519140164Sglebius	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
520140164Sglebius	/*
521140164Sglebius	 * XXX: While ngt_start() is Giant-locked, queue incoming
522140164Sglebius	 * packets, too. Otherwise we acquire Giant holding some
523140164Sglebius	 * IP stack locks, e.g. divinp, and this makes WITNESS scream.
524140164Sglebius	 */
525140164Sglebius	NG_HOOK_FORCE_QUEUE(hook);
52669922Sjulian	return (0);
52769922Sjulian}
52869922Sjulian
52969922Sjulian/*
53052419Sjulian * Disconnect the hook
53152419Sjulian */
53252419Sjulianstatic int
53352419Sjulianngt_disconnect(hook_p hook)
53452419Sjulian{
53570784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
53652419Sjulian
53752419Sjulian	if (hook != sc->hook)
53887599Sobrien		panic(__func__);
539140164Sglebius
540140164Sglebius	NGTLOCK(sc);
54152419Sjulian	sc->hook = NULL;
542140164Sglebius	NGTUNLOCK(sc);
543140164Sglebius
54452419Sjulian	return (0);
54552419Sjulian}
54652419Sjulian
54752419Sjulian/*
54852419Sjulian * Remove this node. The does the netgraph portion of the shutdown.
54952419Sjulian * This should only be called indirectly from ngt_close().
550140164Sglebius *
551140164Sglebius * tp->t_lsc is already NULL, so we should be protected from
552140164Sglebius * tty calls now.
55352419Sjulian */
55452419Sjulianstatic int
55552419Sjulianngt_shutdown(node_p node)
55652419Sjulian{
55770784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
55852419Sjulian
559140164Sglebius	NGTLOCK(sc);
560140164Sglebius	if (!(sc->flags & FLG_DIE)) {
561140164Sglebius		NGTUNLOCK(sc);
56252419Sjulian		return (EOPNOTSUPP);
563140164Sglebius	}
564140164Sglebius	NGTUNLOCK(sc);
565140164Sglebius
566140164Sglebius	/* Free resources */
567140164Sglebius	_IF_DRAIN(&sc->outq);
568140164Sglebius	mtx_destroy(&(sc)->outq.ifq_mtx);
569140164Sglebius	m_freem(sc->m);
57070784Sjulian	NG_NODE_UNREF(sc->node);
57152419Sjulian	FREE(sc, M_NETGRAPH);
572140164Sglebius
57352419Sjulian	return (0);
57452419Sjulian}
57552419Sjulian
57652419Sjulian/*
57752419Sjulian * Receive incoming data from netgraph system. Put it on our
57852419Sjulian * output queue and start output if necessary.
57952419Sjulian */
58052419Sjulianstatic int
58170700Sjulianngt_rcvdata(hook_p hook, item_p item)
58252419Sjulian{
58370784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
58470700Sjulian	struct mbuf *m;
585140164Sglebius	int qlen;
58652419Sjulian
58752419Sjulian	if (hook != sc->hook)
58887599Sobrien		panic(__func__);
58970700Sjulian
59070700Sjulian	NGI_GET_M(item, m);
59170700Sjulian	NG_FREE_ITEM(item);
592140164Sglebius
593140164Sglebius	IF_LOCK(&sc->outq);
594140164Sglebius	if (_IF_QFULL(&sc->outq)) {
595140164Sglebius		_IF_DROP(&sc->outq);
596140164Sglebius		IF_UNLOCK(&sc->outq);
597140164Sglebius		NG_FREE_M(m);
598140164Sglebius		return (ENOBUFS);
599140164Sglebius	}
600140164Sglebius
601140164Sglebius	_IF_ENQUEUE(&sc->outq, m);
602140164Sglebius	qlen = sc->outq.ifq_len;
603140164Sglebius	IF_UNLOCK(&sc->outq);
604140164Sglebius
605140164Sglebius	/*
606140164Sglebius	 * If qlen > 1, then we should already have a scheduled callout.
607140164Sglebius	 */
608140164Sglebius	if (qlen == 1) {
609140164Sglebius		mtx_lock(&Giant);
61052419Sjulian		ngt_start(sc->tp);
611140164Sglebius		mtx_unlock(&Giant);
612140164Sglebius	}
613140164Sglebius
614140164Sglebius	return (0);
61552419Sjulian}
61652419Sjulian
61752419Sjulian/*
61852419Sjulian * Receive control message
61952419Sjulian */
62052419Sjulianstatic int
62170700Sjulianngt_rcvmsg(node_p node, item_p item, hook_p lasthook)
62252419Sjulian{
62370784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
624140164Sglebius	struct ng_mesg *msg, *resp = NULL;
62552419Sjulian	int error = 0;
62652419Sjulian
62770700Sjulian	NGI_GET_MSG(item, msg);
62852419Sjulian	switch (msg->header.typecookie) {
62952419Sjulian	case NGM_TTY_COOKIE:
63052419Sjulian		switch (msg->header.cmd) {
63152419Sjulian		case NGM_TTY_SET_HOTCHAR:
63252419Sjulian		    {
63352419Sjulian			int     hotchar;
63452419Sjulian
63552419Sjulian			if (msg->header.arglen != sizeof(int))
63652419Sjulian				ERROUT(EINVAL);
63752419Sjulian			hotchar = *((int *) msg->data);
63852419Sjulian			if (hotchar != (u_char) hotchar && hotchar != -1)
63952419Sjulian				ERROUT(EINVAL);
64052419Sjulian			sc->hotchar = hotchar;	/* race condition is OK */
64152419Sjulian			break;
64252419Sjulian		    }
64352419Sjulian		case NGM_TTY_GET_HOTCHAR:
64452419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
64552419Sjulian			if (!resp)
64652419Sjulian				ERROUT(ENOMEM);
64752419Sjulian			/* Race condition here is OK */
64852419Sjulian			*((int *) resp->data) = sc->hotchar;
64952419Sjulian			break;
65052419Sjulian		default:
65152419Sjulian			ERROUT(EINVAL);
65252419Sjulian		}
65352419Sjulian		break;
65452419Sjulian	default:
65552419Sjulian		ERROUT(EINVAL);
65652419Sjulian	}
65752419Sjuliandone:
65870700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
65970700Sjulian	NG_FREE_MSG(msg);
66052419Sjulian	return (error);
66152419Sjulian}
66252419Sjulian
66352419Sjulian/******************************************************************
66452419Sjulian		    	INITIALIZATION
66552419Sjulian******************************************************************/
66652419Sjulian
66752419Sjulian/*
66852419Sjulian * Handle loading and unloading for this node type
66952419Sjulian */
67052419Sjulianstatic int
67152419Sjulianngt_mod_event(module_t mod, int event, void *data)
67252419Sjulian{
673140164Sglebius	int error = 0;
67452419Sjulian
67552419Sjulian	switch (event) {
67652419Sjulian	case MOD_LOAD:
67752419Sjulian
67852419Sjulian		/* Register line discipline */
679140164Sglebius		mtx_lock(&Giant);
68052441Sjulian		if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
681140164Sglebius			mtx_unlock(&Giant);
68252419Sjulian			log(LOG_ERR, "%s: can't register line discipline",
68387599Sobrien			    __func__);
68452419Sjulian			return (EIO);
68552419Sjulian		}
686140164Sglebius		mtx_unlock(&Giant);
68752419Sjulian		break;
68852419Sjulian
68952419Sjulian	case MOD_UNLOAD:
69052419Sjulian
69152419Sjulian		/* Unregister line discipline */
692140164Sglebius		mtx_lock(&Giant);
69352419Sjulian		ldisc_deregister(ngt_ldisc);
694140164Sglebius		mtx_unlock(&Giant);
69552419Sjulian		break;
69652419Sjulian
69752419Sjulian	default:
69852419Sjulian		error = EOPNOTSUPP;
69952419Sjulian		break;
70052419Sjulian	}
70152419Sjulian	return (error);
70252419Sjulian}
703