ng_tty.c revision 52442
152419Sjulian
252419Sjulian/*
352419Sjulian * ng_tty.c
452419Sjulian *
552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
652419Sjulian * All rights reserved.
752419Sjulian *
852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
952419Sjulian * redistribution of this software, in source or object code forms, with or
1052419Sjulian * without modifications are expressly permitted by Whistle Communications;
1152419Sjulian * provided, however, that:
1252419Sjulian * 1. Any and all reproductions of the source or object code must include the
1352419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1552419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1652419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1752419Sjulian *    such appears in the above copyright notice or in the software.
1852419Sjulian *
1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3552419Sjulian * OF SUCH DAMAGE.
3652419Sjulian *
3752419Sjulian * Author: Archie Cobbs <archie@whistle.com>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_tty.c 52442 1999-10-23 04:52:54Z julian $
4052419Sjulian * $Whistle: ng_tty.c,v 1.18 1999/01/28 23:54:54 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This file implements a terminal line discipline that is also a
4552419Sjulian * netgraph node. Installing this line discipline on a terminal device
4652419Sjulian * instantiates a new netgraph node of this type, which allows access
4752419Sjulian * to the device via the "hook" hook of the node.
4852419Sjulian *
4952419Sjulian * Once the line discipline is installed, you can find out the name
5052419Sjulian * of the corresponding netgraph node via a NGIOCGINFO ioctl().
5152419Sjulian *
5252419Sjulian * Incoming characters are delievered to the hook one at a time, each
5352419Sjulian * in its own mbuf. You may optionally define a ``hotchar,'' which causes
5452419Sjulian * incoming characters to be buffered up until either the hotchar is
5552419Sjulian * seen or the mbuf is full (MHLEN bytes). Then all buffered characters
5652419Sjulian * are immediately delivered.
5752419Sjulian *
5852419Sjulian * NOTE: This node operates at spltty().
5952419Sjulian */
6052419Sjulian
6152419Sjulian#include <sys/param.h>
6252419Sjulian#include <sys/systm.h>
6352419Sjulian#include <sys/kernel.h>
6452419Sjulian#include <sys/conf.h>
6552419Sjulian#include <sys/proc.h>
6652419Sjulian#include <sys/mbuf.h>
6752419Sjulian#include <sys/malloc.h>
6852419Sjulian#include <sys/socket.h>
6952419Sjulian#include <sys/fcntl.h>
7052419Sjulian#include <sys/file.h>
7152419Sjulian#include <sys/tty.h>
7252441Sjulian#include <sys/ttycom.h>
7352419Sjulian#include <sys/syslog.h>
7452419Sjulian#include <sys/errno.h>
7552419Sjulian#include <sys/ioccom.h>
7652419Sjulian
7752419Sjulian#include <netgraph/ng_message.h>
7852419Sjulian#include <netgraph/netgraph.h>
7952419Sjulian#include <netgraph/ng_tty.h>
8052419Sjulian
8152419Sjulian#ifdef __i386__			/* fiddle with the spl locking */
8252419Sjulian#include <machine/ipl.h>
8352419Sjulian#include <i386/isa/intr_machdep.h>
8452419Sjulian#endif
8552419Sjulian
8652419Sjulian/* Misc defs */
8752419Sjulian#define MAX_MBUFQ		3	/* Max number of queued mbufs */
8852419Sjulian#define NGT_HIWATER		400	/* High water mark on output */
8952419Sjulian
9052419Sjulian/* Per-node private info */
9152419Sjulianstruct ngt_sc {
9252419Sjulian	struct tty *tp;		/* Terminal device */
9352419Sjulian	node_p  node;		/* Netgraph node */
9452419Sjulian	hook_p  hook;		/* Netgraph hook */
9552419Sjulian	struct mbuf *m;		/* Incoming data buffer */
9652419Sjulian	struct mbuf *qhead, **qtail;	/* Queue of outgoing mbuf's */
9752419Sjulian	short   qlen;		/* Length of queue */
9852419Sjulian	short   hotchar;	/* Hotchar, or -1 if none */
9952419Sjulian	u_int   flags;		/* Flags */
10052419Sjulian	struct callout_handle chand;	/* See man timeout(9) */
10152419Sjulian};
10252419Sjuliantypedef struct ngt_sc *sc_p;
10352419Sjulian
10452419Sjulian/* Flags */
10552419Sjulian#define FLG_TIMEOUT		0x0001	/* A timeout is pending */
10652419Sjulian#define FLG_DEBUG		0x0002
10752419Sjulian
10852419Sjulian/* Debugging */
10952419Sjulian#ifdef DIAGNOSTICS
11052419Sjulian#define QUEUECHECK(sc)							\
11152419Sjulian    do {								\
11252419Sjulian      struct mbuf	**mp;						\
11352419Sjulian      int		k;						\
11452419Sjulian									\
11552419Sjulian      for (k = 0, mp = &sc->qhead;					\
11652419Sjulian	k <= MAX_MBUFQ && *mp;						\
11752419Sjulian	k++, mp = &(*mp)->m_nextpkt);					\
11852419Sjulian      if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail)	\
11952419Sjulian	panic(__FUNCTION__ ": queue");					\
12052419Sjulian    } while (0)
12152419Sjulian#else
12252419Sjulian#define QUEUECHECK(sc)	do {} while (0)
12352419Sjulian#endif
12452419Sjulian
12552419Sjulian/* Line discipline methods */
12652419Sjulianstatic int	ngt_open(dev_t dev, struct tty *tp);
12752419Sjulianstatic int	ngt_close(struct tty *tp, int flag);
12852419Sjulianstatic int	ngt_read(struct tty *tp, struct uio *uio, int flag);
12952419Sjulianstatic int	ngt_write(struct tty *tp, struct uio *uio, int flag);
13052419Sjulianstatic int	ngt_tioctl(struct tty *tp,
13152419Sjulian		    u_long cmd, caddr_t data, int flag, struct proc *);
13252419Sjulianstatic int	ngt_input(int c, struct tty *tp);
13352419Sjulianstatic int	ngt_start(struct tty *tp);
13452419Sjulian
13552419Sjulian/* Netgraph methods */
13652419Sjulianstatic int	ngt_constructor(node_p *node);
13752419Sjulianstatic int	ngt_rcvmsg(node_p node, struct ng_mesg *msg,
13852419Sjulian		    const char *retaddr, struct ng_mesg **resp);
13952419Sjulianstatic int	ngt_shutdown(node_p node);
14052419Sjulianstatic int	ngt_newhook(node_p node, hook_p hook, const char *name);
14152419Sjulianstatic int	ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
14252419Sjulianstatic int	ngt_disconnect(hook_p hook);
14352419Sjulianstatic int	ngt_mod_event(module_t mod, int event, void *data);
14452419Sjulian
14552419Sjulian/* Other stuff */
14652419Sjulianstatic void	ngt_timeout(void *arg);
14752419Sjulian
14852419Sjulian#define ERROUT(x)		do { error = (x); goto done; } while (0)
14952419Sjulian
15052419Sjulian/* Line discipline descriptor */
15152419Sjulianstatic struct linesw ngt_disc = {
15252419Sjulian	ngt_open,
15352419Sjulian	ngt_close,
15452419Sjulian	ngt_read,
15552419Sjulian	ngt_write,
15652419Sjulian	ngt_tioctl,
15752419Sjulian	ngt_input,
15852419Sjulian	ngt_start,
15952419Sjulian	ttymodem,
16052419Sjulian	NG_TTY_DFL_HOTCHAR	/* XXX can't change this in serial driver */
16152419Sjulian};
16252419Sjulian
16352419Sjulian/* Netgraph node type descriptor */
16452419Sjulianstatic struct ng_type typestruct = {
16552419Sjulian	NG_VERSION,
16652419Sjulian	NG_TTY_NODE_TYPE,
16752419Sjulian	ngt_mod_event,
16852419Sjulian	ngt_constructor,
16952419Sjulian	ngt_rcvmsg,
17052419Sjulian	ngt_shutdown,
17152419Sjulian	ngt_newhook,
17252419Sjulian	NULL,
17352419Sjulian	NULL,
17452419Sjulian	ngt_rcvdata,
17552419Sjulian	ngt_rcvdata,
17652419Sjulian	ngt_disconnect,
17752419Sjulian};
17852419SjulianNETGRAPH_INIT(tty, &typestruct);
17952419Sjulian
18052419Sjulianstatic int ngt_unit;
18152419Sjulianstatic int ngt_nodeop_ok;	/* OK to create/remove node */
18252442Sjulianstatic int ngt_ldisc;
18352419Sjulian
18452419Sjulian/******************************************************************
18552419Sjulian		    LINE DISCIPLINE METHODS
18652419Sjulian******************************************************************/
18752419Sjulian
18852419Sjulian/*
18952419Sjulian * Set our line discipline on the tty.
19052419Sjulian * Called from device open routine or ttioctl() at >= splsofttty()
19152419Sjulian */
19252419Sjulianstatic int
19352419Sjulianngt_open(dev_t dev, struct tty *tp)
19452419Sjulian{
19552419Sjulian	struct proc *const p = curproc;	/* XXX */
19652419Sjulian	char name[sizeof(NG_TTY_NODE_TYPE) + 8];
19752419Sjulian	sc_p sc;
19852419Sjulian	int s, error;
19952419Sjulian
20052419Sjulian	/* Super-user only */
20152419Sjulian	if ((error = suser(p)))
20252419Sjulian		return (error);
20352419Sjulian	s = splnet();
20452419Sjulian	(void) spltty();	/* XXX is this necessary? */
20552419Sjulian
20652419Sjulian	/* Already installed? */
20752441Sjulian	if (tp->t_line == NETGRAPHDISC) {
20852419Sjulian		sc = (sc_p) tp->t_sc;
20952419Sjulian		if (sc != NULL && sc->tp == tp)
21052419Sjulian			goto done;
21152419Sjulian	}
21252419Sjulian
21352419Sjulian	/* Initialize private struct */
21452419Sjulian	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK);
21552419Sjulian	if (sc == NULL) {
21652419Sjulian		error = ENOMEM;
21752419Sjulian		goto done;
21852419Sjulian	}
21952419Sjulian	bzero(sc, sizeof(*sc));
22052419Sjulian	sc->tp = tp;
22152419Sjulian	sc->hotchar = NG_TTY_DFL_HOTCHAR;
22252419Sjulian	sc->qtail = &sc->qhead;
22352419Sjulian	QUEUECHECK(sc);
22452419Sjulian	callout_handle_init(&sc->chand);
22552419Sjulian
22652419Sjulian	/* Setup netgraph node */
22752419Sjulian	ngt_nodeop_ok = 1;
22852419Sjulian	error = ng_make_node_common(&typestruct, &sc->node);
22952419Sjulian	ngt_nodeop_ok = 0;
23052419Sjulian	if (error) {
23152419Sjulian		FREE(sc, M_NETGRAPH);
23252419Sjulian		goto done;
23352419Sjulian	}
23452419Sjulian	sprintf(name, "%s%d", typestruct.name, ngt_unit++);
23552419Sjulian
23652419Sjulian	/* Set back pointers */
23752419Sjulian	sc->node->private = sc;
23852419Sjulian	tp->t_sc = (caddr_t) sc;
23952419Sjulian
24052419Sjulian	/* Assign node its name */
24152419Sjulian	if ((error = ng_name_node(sc->node, name))) {
24252419Sjulian		log(LOG_ERR, "%s: node name exists?\n", name);
24352419Sjulian		ngt_nodeop_ok = 1;
24452419Sjulian		ng_rmnode(sc->node);
24552419Sjulian		ngt_nodeop_ok = 0;
24652419Sjulian		goto done;
24752419Sjulian	}
24852419Sjulian
24952419Sjulian	/*
25052419Sjulian	 * Pre-allocate cblocks to the an appropriate amount.
25152419Sjulian	 * I'm not sure what is appropriate.
25252419Sjulian	 */
25352419Sjulian	ttyflush(tp, FREAD | FWRITE);
25452419Sjulian	clist_alloc_cblocks(&tp->t_canq, 0, 0);
25552419Sjulian	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
25652419Sjulian	clist_alloc_cblocks(&tp->t_outq,
25752419Sjulian	    MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
25852419Sjulian
25952419Sjuliandone:
26052419Sjulian	/* Done */
26152419Sjulian	splx(s);
26252419Sjulian	return (error);
26352419Sjulian}
26452419Sjulian
26552419Sjulian/*
26652419Sjulian * Line specific close routine, called from device close routine
26752419Sjulian * and from ttioctl at >= splsofttty(). This causes the node to
26852419Sjulian * be destroyed as well.
26952419Sjulian */
27052419Sjulianstatic int
27152419Sjulianngt_close(struct tty *tp, int flag)
27252419Sjulian{
27352419Sjulian	const sc_p sc = (sc_p) tp->t_sc;
27452419Sjulian	int s;
27552419Sjulian
27652419Sjulian	s = spltty();
27752419Sjulian	ttyflush(tp, FREAD | FWRITE);
27852419Sjulian	clist_free_cblocks(&tp->t_outq);
27952419Sjulian	tp->t_line = 0;
28052419Sjulian	if (sc != NULL) {
28152419Sjulian		if (sc->flags & FLG_TIMEOUT) {
28252419Sjulian			untimeout(ngt_timeout, sc, sc->chand);
28352419Sjulian			sc->flags &= ~FLG_TIMEOUT;
28452419Sjulian		}
28552419Sjulian		ngt_nodeop_ok = 1;
28652419Sjulian		ng_rmnode(sc->node);
28752419Sjulian		ngt_nodeop_ok = 0;
28852419Sjulian		tp->t_sc = NULL;
28952419Sjulian	}
29052419Sjulian	splx(s);
29152419Sjulian	return (0);
29252419Sjulian}
29352419Sjulian
29452419Sjulian/*
29552419Sjulian * Once the device has been turned into a node, we don't allow reading.
29652419Sjulian */
29752419Sjulianstatic int
29852419Sjulianngt_read(struct tty *tp, struct uio *uio, int flag)
29952419Sjulian{
30052419Sjulian	return (EIO);
30152419Sjulian}
30252419Sjulian
30352419Sjulian/*
30452419Sjulian * Once the device has been turned into a node, we don't allow writing.
30552419Sjulian */
30652419Sjulianstatic int
30752419Sjulianngt_write(struct tty *tp, struct uio *uio, int flag)
30852419Sjulian{
30952419Sjulian	return (EIO);
31052419Sjulian}
31152419Sjulian
31252419Sjulian/*
31352419Sjulian * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
31452419Sjulian */
31552419Sjulianstatic int
31652419Sjulianngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
31752419Sjulian{
31852419Sjulian	const sc_p sc = (sc_p) tp->t_sc;
31952419Sjulian	int s, error = 0;
32052419Sjulian
32152419Sjulian	s = spltty();
32252419Sjulian	switch (cmd) {
32352419Sjulian	case NGIOCGINFO:
32452419Sjulian	    {
32552419Sjulian		struct nodeinfo *const ni = (struct nodeinfo *) data;
32652419Sjulian		const node_p node = sc->node;
32752419Sjulian
32852419Sjulian		bzero(ni, sizeof(*ni));
32952419Sjulian		if (node->name)
33052419Sjulian			strncpy(ni->name, node->name, sizeof(ni->name) - 1);
33152419Sjulian		strncpy(ni->type, node->type->name, sizeof(ni->type) - 1);
33252419Sjulian		ni->id = (u_int32_t) node;
33352419Sjulian		ni->hooks = node->numhooks;
33452419Sjulian		break;
33552419Sjulian	    }
33652419Sjulian	default:
33752419Sjulian		ERROUT(ENOIOCTL);
33852419Sjulian	}
33952419Sjuliandone:
34052419Sjulian	splx(s);
34152419Sjulian	return (error);
34252419Sjulian}
34352419Sjulian
34452419Sjulian/*
34552419Sjulian * Receive data coming from the device. We get one character at
34652419Sjulian * a time, which is kindof silly.
34752419Sjulian * Only guaranteed to be at splsofttty() or spltty().
34852419Sjulian */
34952419Sjulianstatic int
35052419Sjulianngt_input(int c, struct tty *tp)
35152419Sjulian{
35252419Sjulian	const sc_p sc = (sc_p) tp->t_sc;
35352419Sjulian	const node_p node = sc->node;
35452419Sjulian	struct mbuf *m;
35552419Sjulian	int s, error = 0;
35652419Sjulian
35752419Sjulian	if (!sc || tp != sc->tp)
35852419Sjulian		return (0);
35952419Sjulian	s = spltty();
36052419Sjulian	if (!sc->hook)
36152419Sjulian		ERROUT(0);
36252419Sjulian
36352419Sjulian	/* Check for error conditions */
36452419Sjulian	if ((tp->t_state & TS_CONNECTED) == 0) {
36552419Sjulian		if (sc->flags & FLG_DEBUG)
36652419Sjulian			log(LOG_DEBUG, "%s: no carrier\n", node->name);
36752419Sjulian		ERROUT(0);
36852419Sjulian	}
36952419Sjulian	if (c & TTY_ERRORMASK) {
37052419Sjulian		/* framing error or overrun on this char */
37152419Sjulian		if (sc->flags & FLG_DEBUG)
37252419Sjulian			log(LOG_DEBUG, "%s: line error %x\n",
37352419Sjulian			    node->name, c & TTY_ERRORMASK);
37452419Sjulian		ERROUT(0);
37552419Sjulian	}
37652419Sjulian	c &= TTY_CHARMASK;
37752419Sjulian
37852419Sjulian	/* Get a new header mbuf if we need one */
37952419Sjulian	if (!(m = sc->m)) {
38052419Sjulian		MGETHDR(m, M_DONTWAIT, MT_DATA);
38152419Sjulian		if (!m) {
38252419Sjulian			if (sc->flags & FLG_DEBUG)
38352419Sjulian				log(LOG_ERR,
38452419Sjulian				    "%s: can't get mbuf\n", node->name);
38552419Sjulian			ERROUT(ENOBUFS);
38652419Sjulian		}
38752419Sjulian		m->m_len = 0;
38852419Sjulian		m->m_pkthdr.len = 0;
38952419Sjulian		sc->m = m;
39052419Sjulian	}
39152419Sjulian
39252419Sjulian	/* Add char to mbuf */
39352419Sjulian	*mtod(m, u_char *) = c;
39452419Sjulian	m->m_data++;
39552419Sjulian	m->m_len++;
39652419Sjulian	m->m_pkthdr.len++;
39752419Sjulian
39852419Sjulian	/* Ship off mbuf if it's time */
39952419Sjulian	if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
40052419Sjulian		m->m_data = m->m_pktdat;
40152419Sjulian		error = ng_queue_data(sc->hook, m, NULL);
40252419Sjulian		sc->m = NULL;
40352419Sjulian	}
40452419Sjuliandone:
40552419Sjulian	splx(s);
40652419Sjulian	return (error);
40752419Sjulian}
40852419Sjulian
40952419Sjulian/*
41052419Sjulian * This is called when the device driver is ready for more output.
41152419Sjulian * Called from tty system at splsofttty() or spltty().
41252419Sjulian * Also call from ngt_rcv_data() when a new mbuf is available for output.
41352419Sjulian */
41452419Sjulianstatic int
41552419Sjulianngt_start(struct tty *tp)
41652419Sjulian{
41752419Sjulian	const sc_p sc = (sc_p) tp->t_sc;
41852419Sjulian	int s;
41952419Sjulian
42052419Sjulian	s = spltty();
42152419Sjulian	while (tp->t_outq.c_cc < NGT_HIWATER) {	/* XXX 2.2 specific ? */
42252419Sjulian		struct mbuf *m = sc->qhead;
42352419Sjulian
42452419Sjulian		/* Remove first mbuf from queue */
42552419Sjulian		if (!m)
42652419Sjulian			break;
42752419Sjulian		if ((sc->qhead = m->m_nextpkt) == NULL)
42852419Sjulian			sc->qtail = &sc->qhead;
42952419Sjulian		sc->qlen--;
43052419Sjulian		QUEUECHECK(sc);
43152419Sjulian
43252419Sjulian		/* Send as much of it as possible */
43352419Sjulian		while (m) {
43452419Sjulian			struct mbuf *m2;
43552419Sjulian			int     sent;
43652419Sjulian
43752419Sjulian			sent = m->m_len
43852419Sjulian			    - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq);
43952419Sjulian			m->m_data += sent;
44052419Sjulian			m->m_len -= sent;
44152419Sjulian			if (m->m_len > 0)
44252419Sjulian				break;	/* device can't take no more */
44352419Sjulian			MFREE(m, m2);
44452419Sjulian			m = m2;
44552419Sjulian		}
44652419Sjulian
44752419Sjulian		/* Put remainder of mbuf chain (if any) back on queue */
44852419Sjulian		if (m) {
44952419Sjulian			m->m_nextpkt = sc->qhead;
45052419Sjulian			sc->qhead = m;
45152419Sjulian			if (sc->qtail == &sc->qhead)
45252419Sjulian				sc->qtail = &m->m_nextpkt;
45352419Sjulian			sc->qlen++;
45452419Sjulian			QUEUECHECK(sc);
45552419Sjulian			break;
45652419Sjulian		}
45752419Sjulian	}
45852419Sjulian
45952419Sjulian	/* Call output process whether or not there is any output. We are
46052419Sjulian	 * being called in lieu of ttstart and must do what it would. */
46152419Sjulian	if (tp->t_oproc != NULL)
46252419Sjulian		(*tp->t_oproc) (tp);
46352419Sjulian
46452419Sjulian	/* This timeout is needed for operation on a pseudo-tty, because the
46552419Sjulian	 * pty code doesn't call pppstart after it has drained the t_outq. */
46652419Sjulian	if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) {
46752419Sjulian		sc->chand = timeout(ngt_timeout, sc, 1);
46852419Sjulian		sc->flags |= FLG_TIMEOUT;
46952419Sjulian	}
47052419Sjulian	splx(s);
47152419Sjulian	return (0);
47252419Sjulian}
47352419Sjulian
47452419Sjulian/*
47552419Sjulian * We still have data to output to the device, so try sending more.
47652419Sjulian */
47752419Sjulianstatic void
47852419Sjulianngt_timeout(void *arg)
47952419Sjulian{
48052419Sjulian	const sc_p sc = (sc_p) arg;
48152419Sjulian	int s;
48252419Sjulian
48352419Sjulian	s = spltty();
48452419Sjulian	sc->flags &= ~FLG_TIMEOUT;
48552419Sjulian	ngt_start(sc->tp);
48652419Sjulian	splx(s);
48752419Sjulian}
48852419Sjulian
48952419Sjulian/******************************************************************
49052419Sjulian		    NETGRAPH NODE METHODS
49152419Sjulian******************************************************************/
49252419Sjulian
49352419Sjulian/*
49452419Sjulian * Initialize a new node of this type.
49552419Sjulian *
49652419Sjulian * We only allow nodes to be created as a result of setting
49752419Sjulian * the line discipline on a tty, so always return an error if not.
49852419Sjulian */
49952419Sjulianstatic int
50052419Sjulianngt_constructor(node_p *nodep)
50152419Sjulian{
50252419Sjulian	if (!ngt_nodeop_ok)
50352419Sjulian		return (EOPNOTSUPP);
50452419Sjulian	return (ng_make_node_common(&typestruct, nodep));
50552419Sjulian}
50652419Sjulian
50752419Sjulian/*
50852419Sjulian * Add a new hook. There can only be one.
50952419Sjulian */
51052419Sjulianstatic int
51152419Sjulianngt_newhook(node_p node, hook_p hook, const char *name)
51252419Sjulian{
51352419Sjulian	const sc_p sc = node->private;
51452419Sjulian	int s, error = 0;
51552419Sjulian
51652419Sjulian	if (strcmp(name, NG_TTY_HOOK))
51752419Sjulian		return (EINVAL);
51852419Sjulian	s = spltty();
51952419Sjulian	if (sc->hook)
52052419Sjulian		ERROUT(EISCONN);
52152419Sjulian	sc->hook = hook;
52252419Sjuliandone:
52352419Sjulian	splx(s);
52452419Sjulian	return (error);
52552419Sjulian}
52652419Sjulian
52752419Sjulian/*
52852419Sjulian * Disconnect the hook
52952419Sjulian */
53052419Sjulianstatic int
53152419Sjulianngt_disconnect(hook_p hook)
53252419Sjulian{
53352419Sjulian	const sc_p sc = hook->node->private;
53452419Sjulian	int s;
53552419Sjulian
53652419Sjulian	s = spltty();
53752419Sjulian	if (hook != sc->hook)
53852419Sjulian		panic(__FUNCTION__);
53952419Sjulian	sc->hook = NULL;
54052419Sjulian	m_freem(sc->m);
54152419Sjulian	sc->m = NULL;
54252419Sjulian	splx(s);
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().
54952419Sjulian */
55052419Sjulianstatic int
55152419Sjulianngt_shutdown(node_p node)
55252419Sjulian{
55352419Sjulian	const sc_p sc = node->private;
55452419Sjulian
55552419Sjulian	if (!ngt_nodeop_ok)
55652419Sjulian		return (EOPNOTSUPP);
55752419Sjulian	ng_unname(node);
55852419Sjulian	ng_cutlinks(node);
55952419Sjulian	node->private = NULL;
56052419Sjulian	ng_unref(sc->node);
56152419Sjulian	m_freem(sc->qhead);
56252419Sjulian	m_freem(sc->m);
56352419Sjulian	bzero(sc, sizeof(*sc));
56452419Sjulian	FREE(sc, M_NETGRAPH);
56552419Sjulian	return (0);
56652419Sjulian}
56752419Sjulian
56852419Sjulian/*
56952419Sjulian * Receive incoming data from netgraph system. Put it on our
57052419Sjulian * output queue and start output if necessary.
57152419Sjulian */
57252419Sjulianstatic int
57352419Sjulianngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
57452419Sjulian{
57552419Sjulian	const sc_p sc = hook->node->private;
57652419Sjulian	int s, error = 0;
57752419Sjulian
57852419Sjulian	if (hook != sc->hook)
57952419Sjulian		panic(__FUNCTION__);
58052419Sjulian	NG_FREE_META(meta);
58152419Sjulian	s = spltty();
58252419Sjulian	if (sc->qlen >= MAX_MBUFQ)
58352419Sjulian		ERROUT(ENOBUFS);
58452419Sjulian	m->m_nextpkt = NULL;
58552419Sjulian	*sc->qtail = m;
58652419Sjulian	sc->qtail = &m->m_nextpkt;
58752419Sjulian	sc->qlen++;
58852419Sjulian	QUEUECHECK(sc);
58952419Sjulian	m = NULL;
59052419Sjulian	if (sc->qlen == 1)
59152419Sjulian		ngt_start(sc->tp);
59252419Sjuliandone:
59352419Sjulian	splx(s);
59452419Sjulian	if (m)
59552419Sjulian		m_freem(m);
59652419Sjulian	return (error);
59752419Sjulian}
59852419Sjulian
59952419Sjulian/*
60052419Sjulian * Receive control message
60152419Sjulian */
60252419Sjulianstatic int
60352419Sjulianngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
60452419Sjulian	   struct ng_mesg **rptr)
60552419Sjulian{
60652419Sjulian	const sc_p sc = (sc_p) node->private;
60752419Sjulian	struct ng_mesg *resp = NULL;
60852419Sjulian	int error = 0;
60952419Sjulian
61052419Sjulian	switch (msg->header.typecookie) {
61152419Sjulian	case NGM_TTY_COOKIE:
61252419Sjulian		switch (msg->header.cmd) {
61352419Sjulian		case NGM_TTY_SET_HOTCHAR:
61452419Sjulian		    {
61552419Sjulian			int     hotchar;
61652419Sjulian
61752419Sjulian			if (msg->header.arglen != sizeof(int))
61852419Sjulian				ERROUT(EINVAL);
61952419Sjulian			hotchar = *((int *) msg->data);
62052419Sjulian			if (hotchar != (u_char) hotchar && hotchar != -1)
62152419Sjulian				ERROUT(EINVAL);
62252419Sjulian			sc->hotchar = hotchar;	/* race condition is OK */
62352419Sjulian			break;
62452419Sjulian		    }
62552419Sjulian		case NGM_TTY_GET_HOTCHAR:
62652419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
62752419Sjulian			if (!resp)
62852419Sjulian				ERROUT(ENOMEM);
62952419Sjulian			/* Race condition here is OK */
63052419Sjulian			*((int *) resp->data) = sc->hotchar;
63152419Sjulian			break;
63252419Sjulian		default:
63352419Sjulian			ERROUT(EINVAL);
63452419Sjulian		}
63552419Sjulian		break;
63652419Sjulian	default:
63752419Sjulian		ERROUT(EINVAL);
63852419Sjulian	}
63952419Sjulian	if (rptr)
64052419Sjulian		*rptr = resp;
64152419Sjulian	else if (resp)
64252419Sjulian		FREE(resp, M_NETGRAPH);
64352419Sjulian
64452419Sjuliandone:
64552419Sjulian	FREE(msg, M_NETGRAPH);
64652419Sjulian	return (error);
64752419Sjulian}
64852419Sjulian
64952419Sjulian/******************************************************************
65052419Sjulian		    	INITIALIZATION
65152419Sjulian******************************************************************/
65252419Sjulian
65352419Sjulian/*
65452419Sjulian * Handle loading and unloading for this node type
65552419Sjulian */
65652419Sjulianstatic int
65752419Sjulianngt_mod_event(module_t mod, int event, void *data)
65852419Sjulian{
65952442Sjulian	/* struct ng_type *const type = data;*/
66052419Sjulian	int s, error = 0;
66152419Sjulian
66252419Sjulian	switch (event) {
66352419Sjulian	case MOD_LOAD:
66452419Sjulian#ifdef __i386__
66552419Sjulian		/* Insure the soft net "engine" can't run during spltty code */
66652419Sjulian		s = splhigh();
66752419Sjulian		tty_imask |= softnet_imask; /* spltty() block spl[soft]net() */
66852419Sjulian		net_imask |= softtty_imask; /* splimp() block splsofttty() */
66952419Sjulian		net_imask |= tty_imask;	    /* splimp() block spltty() */
67052419Sjulian		update_intr_masks();
67152419Sjulian		splx(s);
67252419Sjulian
67352419Sjulian		if (bootverbose)
67452419Sjulian			log(LOG_DEBUG, "new masks: bio %x, tty %x, net %x\n",
67552419Sjulian			    bio_imask, tty_imask, net_imask);
67652419Sjulian#endif
67752419Sjulian
67852419Sjulian		/* Register line discipline */
67952419Sjulian		s = spltty();
68052441Sjulian		if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
68152419Sjulian			splx(s);
68252419Sjulian			log(LOG_ERR, "%s: can't register line discipline",
68352419Sjulian			    __FUNCTION__);
68452419Sjulian			return (EIO);
68552419Sjulian		}
68652419Sjulian		splx(s);
68752419Sjulian		break;
68852419Sjulian
68952419Sjulian	case MOD_UNLOAD:
69052419Sjulian
69152419Sjulian		/* Unregister line discipline */
69252419Sjulian		s = spltty();
69352419Sjulian		ldisc_deregister(ngt_ldisc);
69452419Sjulian		splx(s);
69552419Sjulian		break;
69652419Sjulian
69752419Sjulian	default:
69852419Sjulian		error = EOPNOTSUPP;
69952419Sjulian		break;
70052419Sjulian	}
70152419Sjulian	return (error);
70252419Sjulian}
70352419Sjulian
704