ng_tty.c revision 213794
117680Spst/*
217680Spst * ng_tty.c
317680Spst */
417680Spst
517680Spst/*-
617680Spst * Copyright (c) 1996-1999 Whistle Communications, Inc.
717680Spst * All rights reserved.
817680Spst *
917680Spst * Subject to the following obligations and disclaimer of warranty, use and
1017680Spst * redistribution of this software, in source or object code forms, with or
1117680Spst * without modifications are expressly permitted by Whistle Communications;
1217680Spst * provided, however, that:
1317680Spst * 1. Any and all reproductions of the source or object code must include the
1417680Spst *    copyright notice above and the following disclaimer of warranties; and
1517680Spst * 2. No rights are granted, in any manner or form, to use Whistle
1617680Spst *    Communications, Inc. trademarks, including the mark "WHISTLE
1717680Spst *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1817680Spst *    such appears in the above copyright notice or in the software.
1917680Spst *
2057278Sfenner * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2157278Sfenner * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2217680Spst * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2317680Spst * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2417680Spst * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25127675Sbms * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26190207Srpaulo * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2717680Spst * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2817680Spst * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2956893Sfenner * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3056893Sfenner * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3156893Sfenner * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3256893Sfenner * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33127675Sbms * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34235530Sdelphij * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35235530Sdelphij * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36235530Sdelphij * OF SUCH DAMAGE.
37235530Sdelphij *
38146778Ssam * Author: Archie Cobbs <archie@freebsd.org>
39235530Sdelphij *
40235530Sdelphij * Updated by Andrew Thompson <thompsa@FreeBSD.org> for MPSAFE TTY.
4117680Spst *
42235530Sdelphij * $FreeBSD: head/sys/netgraph/ng_tty.c 213794 2010-10-13 17:21:21Z rpaulo $
43235530Sdelphij * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $
44146778Ssam */
45146778Ssam
46146778Ssam/*
47146778Ssam * This file implements TTY hooks to link in to the netgraph system.  The node
48146778Ssam * is created and then passed the callers opened TTY file descriptor number to
49146778Ssam * NGM_TTY_SET_TTY, this will hook the tty via ttyhook_register().
50146778Ssam *
51146778Ssam * Incoming data is delivered directly to ng_tty via the TTY bypass hook as a
52146778Ssam * buffer pointer and length, this is converted to a mbuf and passed to the
53146778Ssam * peer.
54146778Ssam *
55146778Ssam * If the TTY device does not support bypass then incoming characters are
56146778Ssam * delivered to the hook one at a time, each in its own mbuf. You may
57146778Ssam * optionally define a ``hotchar,'' which causes incoming characters to be
58146778Ssam * buffered up until either the hotchar is seen or the mbuf is full (MHLEN
59146778Ssam * bytes). Then all buffered characters are immediately delivered.
60146778Ssam */
61146778Ssam
62146778Ssam#include <sys/param.h>
63146778Ssam#include <sys/systm.h>
64146778Ssam#include <sys/conf.h>
65146778Ssam#include <sys/errno.h>
66146778Ssam#include <sys/fcntl.h>
67146778Ssam#include <sys/ioccom.h>
68146778Ssam#include <sys/kernel.h>
69146778Ssam#include <sys/malloc.h>
70146778Ssam#include <sys/mbuf.h>
71146778Ssam#include <sys/priv.h>
72146778Ssam#include <sys/socket.h>
73146778Ssam#include <sys/syslog.h>
74146778Ssam#include <sys/tty.h>
75146778Ssam#include <sys/ttycom.h>
76146778Ssam#include <sys/proc.h>
77146778Ssam
78146778Ssam#include <net/if.h>
79146778Ssam#include <net/if_var.h>
80146778Ssam
81146778Ssam#include <netgraph/ng_message.h>
82146778Ssam#include <netgraph/netgraph.h>
83146778Ssam#include <netgraph/ng_tty.h>
84146778Ssam
85146778Ssam/* Per-node private info */
86146778Ssamstruct ngt_softc {
87146778Ssam	struct tty	*tp;		/* Terminal device */
88146778Ssam	node_p		node;		/* Netgraph node */
89146778Ssam	hook_p		hook;		/* Netgraph hook */
90146778Ssam	struct ifqueue	outq;		/* Queue of outgoing data */
91146778Ssam	size_t		outqlen;	/* Number of bytes in outq */
92172686Smlaier	struct mbuf	*m;		/* Incoming non-bypass data buffer */
93172686Smlaier	short		hotchar;	/* Hotchar, or -1 if none */
94172686Smlaier	u_int		flags;		/* Flags */
95146778Ssam};
96172686Smlaiertypedef struct ngt_softc *sc_p;
97172686Smlaier
98172686Smlaier/* Flags */
99172686Smlaier#define FLG_DEBUG		0x0002
100172686Smlaier
101172686Smlaier/* Netgraph methods */
10256893Sfennerstatic ng_constructor_t		ngt_constructor;
10356893Sfennerstatic ng_rcvmsg_t		ngt_rcvmsg;
10456893Sfennerstatic ng_shutdown_t		ngt_shutdown;
10556893Sfennerstatic ng_newhook_t		ngt_newhook;
10656893Sfennerstatic ng_connect_t		ngt_connect;
10756893Sfennerstatic ng_rcvdata_t		ngt_rcvdata;
10856893Sfennerstatic ng_disconnect_t		ngt_disconnect;
10957278Sfenner
11057278Sfenner#define ERROUT(x)		do { error = (x); goto done; } while (0)
11156893Sfenner
11256893Sfennerstatic th_getc_inject_t		ngt_getc_inject;
11356893Sfennerstatic th_getc_poll_t		ngt_getc_poll;
11456893Sfennerstatic th_rint_t		ngt_rint;
11557278Sfennerstatic th_rint_bypass_t		ngt_rint_bypass;
11657278Sfennerstatic th_rint_poll_t		ngt_rint_poll;
11756893Sfenner
11856893Sfennerstatic struct ttyhook ngt_hook = {
11956893Sfenner	.th_getc_inject = ngt_getc_inject,
12056893Sfenner	.th_getc_poll = ngt_getc_poll,
121214478Srpaulo	.th_rint = ngt_rint,
12256893Sfenner	.th_rint_bypass = ngt_rint_bypass,
12356893Sfenner	.th_rint_poll = ngt_rint_poll,
12456893Sfenner};
12556893Sfenner
12698527Sfenner/* Netgraph node type descriptor */
12798527Sfennerstatic struct ng_type typestruct = {
12856893Sfenner	.version =	NG_ABI_VERSION,
12998527Sfenner	.name =		NG_TTY_NODE_TYPE,
13098527Sfenner	.constructor =	ngt_constructor,
13198527Sfenner	.rcvmsg =	ngt_rcvmsg,
13298527Sfenner	.shutdown =	ngt_shutdown,
13356893Sfenner	.newhook =	ngt_newhook,
13498527Sfenner	.connect =	ngt_connect,
13598527Sfenner	.rcvdata =	ngt_rcvdata,
13698527Sfenner	.disconnect =	ngt_disconnect,
13798527Sfenner};
13898527SfennerNETGRAPH_INIT(tty, &typestruct);
13998527Sfenner
14098527Sfenner#define	NGTLOCK(sc)	IF_LOCK(&sc->outq)
14198527Sfenner#define	NGTUNLOCK(sc)	IF_UNLOCK(&sc->outq)
14298527Sfenner
14398527Sfenner/******************************************************************
14498527Sfenner		    NETGRAPH NODE METHODS
14598527Sfenner******************************************************************/
14656893Sfenner
14756893Sfenner/*
14856893Sfenner * Initialize a new node of this type.
14998527Sfenner *
15098527Sfenner * We only allow nodes to be created as a result of setting
15156893Sfenner * the line discipline on a tty, so always return an error if not.
152162021Ssam */
15398527Sfennerstatic int
15498527Sfennerngt_constructor(node_p node)
15598527Sfenner{
15698527Sfenner	sc_p sc;
15798527Sfenner
15898527Sfenner	/* Allocate private structure */
15998527Sfenner	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
16098527Sfenner	if (sc == NULL)
16198527Sfenner		return (ENOMEM);
16298527Sfenner
16398527Sfenner	NG_NODE_SET_PRIVATE(node, sc);
16498527Sfenner	sc->node = node;
16556893Sfenner
16698527Sfenner	mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF);
16798527Sfenner	IFQ_SET_MAXLEN(&sc->outq, ifqmaxlen);
16898527Sfenner
16998527Sfenner	return (0);
17098527Sfenner}
17198527Sfenner
17298527Sfenner/*
173147904Ssam * Add a new hook. There can only be one.
174147904Ssam */
175147904Ssamstatic int
176147904Ssamngt_newhook(node_p node, hook_p hook, const char *name)
177162021Ssam{
17898527Sfenner	const sc_p sc = NG_NODE_PRIVATE(node);
179162021Ssam
18098527Sfenner	if (strcmp(name, NG_TTY_HOOK))
18198527Sfenner		return (EINVAL);
18298527Sfenner
18398527Sfenner	if (sc->hook)
18498527Sfenner		return (EISCONN);
18598527Sfenner
18698527Sfenner	NGTLOCK(sc);
18798527Sfenner	sc->hook = hook;
18898527Sfenner	NGTUNLOCK(sc);
189127675Sbms
19098527Sfenner	return (0);
19198527Sfenner}
19298527Sfenner
19398527Sfenner/*
19498527Sfenner * Set the hook into queueing mode (for outgoing packets),
19598527Sfenner * so that we wont deliver mbuf thru the whole graph holding
19698527Sfenner * tty locks.
19756893Sfenner */
19856893Sfennerstatic int
19956893Sfennerngt_connect(hook_p hook)
20098527Sfenner{
20198527Sfenner	NG_HOOK_FORCE_QUEUE(hook);
20298527Sfenner	return (0);
20398527Sfenner}
20456893Sfenner
20598527Sfenner/*
20656893Sfenner * Disconnect the hook
20798527Sfenner */
20898527Sfennerstatic int
20956893Sfennerngt_disconnect(hook_p hook)
21056893Sfenner{
21117680Spst	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
21256893Sfenner
21317680Spst	if (hook != sc->hook)
21498527Sfenner		panic("%s", __func__);
21598527Sfenner
21617680Spst	NGTLOCK(sc);
21798527Sfenner	sc->hook = NULL;
21898527Sfenner	NGTUNLOCK(sc);
21998527Sfenner
22017680Spst	return (0);
221127675Sbms}
22298527Sfenner
22317680Spst/*
22498527Sfenner * Remove this node. The does the netgraph portion of the shutdown.
22598527Sfenner */
22698527Sfennerstatic int
22798527Sfennerngt_shutdown(node_p node)
22898527Sfenner{
22998527Sfenner	const sc_p sc = NG_NODE_PRIVATE(node);
23098527Sfenner	struct tty *tp;
23156893Sfenner
23298527Sfenner	tp = sc->tp;
23398527Sfenner	if (tp != NULL) {
23456893Sfenner		tty_lock(tp);
23598527Sfenner		ttyhook_unregister(tp);
23698527Sfenner	}
23756893Sfenner	/* Free resources */
23898527Sfenner	IF_DRAIN(&sc->outq);
23998527Sfenner	mtx_destroy(&(sc)->outq.ifq_mtx);
24056893Sfenner	NG_NODE_UNREF(sc->node);
24198527Sfenner	free(sc, M_NETGRAPH);
24256893Sfenner
24398527Sfenner	return (0);
24498527Sfenner}
24598527Sfenner
24698527Sfenner/*
24798527Sfenner * Receive control message
24898527Sfenner */
24998527Sfennerstatic int
25017680Spstngt_rcvmsg(node_p node, item_p item, hook_p lasthook)
25198527Sfenner{
25298527Sfenner	struct proc *p;
25398527Sfenner	const sc_p sc = NG_NODE_PRIVATE(node);
25498527Sfenner	struct ng_mesg *msg, *resp = NULL;
25598527Sfenner	int error = 0;
25698527Sfenner
25798527Sfenner	NGI_GET_MSG(item, msg);
25898527Sfenner	switch (msg->header.typecookie) {
259162021Ssam	case NGM_TTY_COOKIE:
26098527Sfenner		switch (msg->header.cmd) {
26198527Sfenner		case NGM_TTY_SET_TTY:
26298527Sfenner			if (sc->tp != NULL)
26398527Sfenner				return (EBUSY);
26498527Sfenner
26598527Sfenner			p = pfind(((int *)msg->data)[0]);
26698527Sfenner			if (p == NULL || (p->p_flag & P_WEXIT))
26798527Sfenner				return (ESRCH);
26898527Sfenner			_PHOLD(p);
26998527Sfenner			PROC_UNLOCK(p);
27098527Sfenner			error = ttyhook_register(&sc->tp, p, ((int *)msg->data)[1],
27198527Sfenner			    &ngt_hook, sc);
27298527Sfenner			PRELE(p);
27356893Sfenner			if (error != 0)
27498527Sfenner				return (error);
27598527Sfenner			break;
27698527Sfenner		case NGM_TTY_SET_HOTCHAR:
27798527Sfenner		    {
27898527Sfenner			int     hotchar;
27998527Sfenner
28098527Sfenner			if (msg->header.arglen != sizeof(int))
28198527Sfenner				ERROUT(EINVAL);
282162021Ssam			hotchar = *((int *) msg->data);
28398527Sfenner			if (hotchar != (u_char) hotchar && hotchar != -1)
28498527Sfenner				ERROUT(EINVAL);
28556893Sfenner			sc->hotchar = hotchar;	/* race condition is OK */
28656893Sfenner			break;
28798527Sfenner		    }
28898527Sfenner		case NGM_TTY_GET_HOTCHAR:
28998527Sfenner			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
29056893Sfenner			if (!resp)
29156893Sfenner				ERROUT(ENOMEM);
29298527Sfenner			/* Race condition here is OK */
29398527Sfenner			*((int *) resp->data) = sc->hotchar;
29498527Sfenner			break;
29598527Sfenner		default:
29698527Sfenner			ERROUT(EINVAL);
29798527Sfenner		}
29898527Sfenner		break;
29998527Sfenner	default:
30098527Sfenner		ERROUT(EINVAL);
30198527Sfenner	}
30298527Sfennerdone:
30398527Sfenner	NG_RESPOND_MSG(error, node, item, resp);
30498527Sfenner	NG_FREE_MSG(msg);
30598527Sfenner	return (error);
30698527Sfenner}
30798527Sfenner
30898527Sfenner/*
30956893Sfenner * Receive incoming data from netgraph system. Put it on our
31098527Sfenner * output queue and start output if necessary.
31198527Sfenner */
31298527Sfennerstatic int
31317680Spstngt_rcvdata(hook_p hook, item_p item)
31456893Sfenner{
31598527Sfenner	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
31698527Sfenner	struct tty *tp = sc->tp;
31717680Spst	struct mbuf *m;
31856893Sfenner
31956893Sfenner	if (hook != sc->hook)
32056893Sfenner		panic("%s", __func__);
32198527Sfenner
32298527Sfenner	NGI_GET_M(item, m);
32398527Sfenner	NG_FREE_ITEM(item);
32456893Sfenner
32556893Sfenner	if (tp == NULL) {
32656893Sfenner		NG_FREE_M(m);
32756893Sfenner		return (ENXIO);
32898527Sfenner	}
32998527Sfenner
33098527Sfenner	IF_LOCK(&sc->outq);
33156893Sfenner	if (_IF_QFULL(&sc->outq)) {
33298527Sfenner		_IF_DROP(&sc->outq);
33398527Sfenner		IF_UNLOCK(&sc->outq);
33498527Sfenner		NG_FREE_M(m);
33598527Sfenner		return (ENOBUFS);
33698527Sfenner	}
33798527Sfenner
33898527Sfenner	_IF_ENQUEUE(&sc->outq, m);
33998527Sfenner	sc->outqlen += m->m_pkthdr.len;
34098527Sfenner	IF_UNLOCK(&sc->outq);
34198527Sfenner
34298527Sfenner	/* notify the TTY that data is ready */
34398527Sfenner	tty_lock(tp);
34498527Sfenner	if (!tty_gone(tp))
34598527Sfenner		ttydevsw_outwakeup(tp);
34656893Sfenner	tty_unlock(tp);
34798527Sfenner
34898527Sfenner	return (0);
34956893Sfenner}
35098527Sfenner
35198527Sfennerstatic size_t
35298527Sfennerngt_getc_inject(struct tty *tp, void *buf, size_t len)
35398527Sfenner{
35498527Sfenner	sc_p sc = ttyhook_softc(tp);
35598527Sfenner	size_t total = 0;
35698527Sfenner	int length;
35756893Sfenner
35898527Sfenner	while (len) {
35956893Sfenner		struct mbuf *m;
36098527Sfenner
36156893Sfenner		/* Remove first mbuf from queue */
36298527Sfenner		IF_DEQUEUE(&sc->outq, m);
36356893Sfenner		if (m == NULL)
36498527Sfenner			break;
36598527Sfenner
36698527Sfenner		/* Send as much of it as possible */
36798527Sfenner		while (m != NULL) {
36898527Sfenner			length = min(m->m_len, len);
36998527Sfenner			memcpy((char *)buf + total, mtod(m, char *), length);
37098527Sfenner
37198527Sfenner			m->m_data += length;
37298527Sfenner			m->m_len -= length;
37398527Sfenner			total += length;
37498527Sfenner			len -= length;
37598527Sfenner
37698527Sfenner			if (m->m_len > 0)
37798527Sfenner				break;	/* device can't take any more */
37856893Sfenner			m = m_free(m);
37998527Sfenner		}
38098527Sfenner
38198527Sfenner		/* Put remainder of mbuf chain (if any) back on queue */
38298527Sfenner		if (m != NULL) {
38398527Sfenner			IF_PREPEND(&sc->outq, m);
38498527Sfenner			break;
38598527Sfenner		}
38698527Sfenner	}
38798527Sfenner	IF_LOCK(&sc->outq);
38898527Sfenner	sc->outqlen -= total;
38998527Sfenner	IF_UNLOCK(&sc->outq);
39098527Sfenner	MPASS(sc->outqlen >= 0);
39198527Sfenner
39298527Sfenner	return (total);
39398527Sfenner}
39498527Sfenner
39598527Sfennerstatic size_t
39698527Sfennerngt_getc_poll(struct tty *tp)
39798527Sfenner{
39898527Sfenner	sc_p sc = ttyhook_softc(tp);
39998527Sfenner
40098527Sfenner	return (sc->outqlen);
40198527Sfenner}
402235530Sdelphij
403235530Sdelphij/*
404235530Sdelphij * Optimised TTY input.
405235530Sdelphij *
406235530Sdelphij * We get a buffer pointer to hopefully a complete data frame. Do not check for
407235530Sdelphij * the hotchar, just pass it on.
40898527Sfenner */
40998527Sfennerstatic size_t
41098527Sfennerngt_rint_bypass(struct tty *tp, const void *buf, size_t len)
41156893Sfenner{
41298527Sfenner	sc_p sc = ttyhook_softc(tp);
41356893Sfenner	node_p node = sc->node;
41456893Sfenner	struct mbuf *m, *mb;
41598527Sfenner	size_t total = 0;
41698527Sfenner	int error = 0, length;
41756893Sfenner
41856893Sfenner	tty_lock_assert(tp, MA_OWNED);
41956893Sfenner
420214478Srpaulo	if (sc->hook == NULL)
42156893Sfenner		return (0);
42256893Sfenner
42356893Sfenner	m = m_getm2(NULL, len, M_DONTWAIT, MT_DATA, M_PKTHDR);
42456893Sfenner	if (m == NULL) {
42556893Sfenner		if (sc->flags & FLG_DEBUG)
42656893Sfenner			log(LOG_ERR,
42756893Sfenner			    "%s: can't get mbuf\n", NG_NODE_NAME(node));
42856893Sfenner		return (0);
42956893Sfenner	}
43056893Sfenner	m->m_pkthdr.rcvif = NULL;
43156893Sfenner
43275118Sfenner	for (mb = m; mb != NULL; mb = mb->m_next) {
433146778Ssam		length = min(M_TRAILINGSPACE(mb), len - total);
434146778Ssam
435172686Smlaier		memcpy(mtod(m, char *), (const char *)buf + total, length);
436146778Ssam		mb->m_len = length;
437146778Ssam		total += length;
438146778Ssam		m->m_pkthdr.len += length;
439146778Ssam	}
440146778Ssam	if (sc->m != NULL) {
441172686Smlaier		/*
442146778Ssam		 * Odd, we have changed from non-bypass to bypass. It is
443146778Ssam		 * unlikely but not impossible, flush the data first.
444146778Ssam		 */
445214478Srpaulo		sc->m->m_data = sc->m->m_pktdat;
446146778Ssam		NG_SEND_DATA_ONLY(error, sc->hook, sc->m);
447146778Ssam		sc->m = NULL;
44898527Sfenner	}
449172686Smlaier	NG_SEND_DATA_ONLY(error, sc->hook, m);
450146778Ssam
451146778Ssam	return (total);
45256893Sfenner}
45356893Sfenner
45456893Sfenner/*
45556893Sfenner * Receive data coming from the device one char at a time, when it is not in
45656893Sfenner * bypass mode.
45756893Sfenner */
45856893Sfennerstatic int
45956893Sfennerngt_rint(struct tty *tp, char c, int flags)
46056893Sfenner{
46156893Sfenner	sc_p sc = ttyhook_softc(tp);
46256893Sfenner	node_p node = sc->node;
46356893Sfenner	struct mbuf *m;
46456893Sfenner	int error = 0;
46556893Sfenner
46656893Sfenner	tty_lock_assert(tp, MA_OWNED);
46756893Sfenner
46856893Sfenner	if (sc->hook == NULL)
46956893Sfenner		return (0);
47056893Sfenner
47156893Sfenner	if (flags != 0) {
47256893Sfenner		/* framing error or overrun on this char */
47356893Sfenner		if (sc->flags & FLG_DEBUG)
47456893Sfenner			log(LOG_DEBUG, "%s: line error %x\n",
47556893Sfenner			    NG_NODE_NAME(node), flags);
47656893Sfenner		return (0);
47756893Sfenner	}
47856893Sfenner
47956893Sfenner	/* Get a new header mbuf if we need one */
48056893Sfenner	if (!(m = sc->m)) {
48156893Sfenner		MGETHDR(m, M_DONTWAIT, MT_DATA);
48256893Sfenner		if (!m) {
48356893Sfenner			if (sc->flags & FLG_DEBUG)
48456893Sfenner				log(LOG_ERR,
48556893Sfenner				    "%s: can't get mbuf\n", NG_NODE_NAME(node));
48656893Sfenner			return (ENOBUFS);
48756893Sfenner		}
48856893Sfenner		m->m_len = m->m_pkthdr.len = 0;
48956893Sfenner		m->m_pkthdr.rcvif = NULL;
49056893Sfenner		sc->m = m;
49156893Sfenner	}
49256893Sfenner
49356893Sfenner	/* Add char to mbuf */
49456893Sfenner	*mtod(m, u_char *) = c;
49556893Sfenner	m->m_data++;
49656893Sfenner	m->m_len++;
49756893Sfenner	m->m_pkthdr.len++;
49856893Sfenner
49956893Sfenner	/* Ship off mbuf if it's time */
50056893Sfenner	if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
50156893Sfenner		m->m_data = m->m_pktdat;
50256893Sfenner		sc->m = NULL;
50356893Sfenner		NG_SEND_DATA_ONLY(error, sc->hook, m);	/* Will queue */
50456893Sfenner	}
50556893Sfenner
50656893Sfenner	return (error);
50756893Sfenner}
50856893Sfenner
50956893Sfennerstatic size_t
51056893Sfennerngt_rint_poll(struct tty *tp)
51156893Sfenner{
51256893Sfenner	/* We can always accept input */
51356893Sfenner	return (1);
51456893Sfenner}
51556893Sfenner
51656893Sfenner