ng_tty.c revision 139823
1
2/*
3 * ng_tty.c
4 */
5
6/*-
7 * Copyright (c) 1996-1999 Whistle Communications, Inc.
8 * All rights reserved.
9 *
10 * Subject to the following obligations and disclaimer of warranty, use and
11 * redistribution of this software, in source or object code forms, with or
12 * without modifications are expressly permitted by Whistle Communications;
13 * provided, however, that:
14 * 1. Any and all reproductions of the source or object code must include the
15 *    copyright notice above and the following disclaimer of warranties; and
16 * 2. No rights are granted, in any manner or form, to use Whistle
17 *    Communications, Inc. trademarks, including the mark "WHISTLE
18 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
19 *    such appears in the above copyright notice or in the software.
20 *
21 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
22 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
23 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
24 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
26 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
27 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
28 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
29 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
30 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
31 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
32 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
37 * OF SUCH DAMAGE.
38 *
39 * Author: Archie Cobbs <archie@freebsd.org>
40 *
41 * $FreeBSD: head/sys/netgraph/ng_tty.c 139823 2005-01-07 01:45:51Z imp $
42 * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $
43 */
44
45/*
46 * This file implements a terminal line discipline that is also a
47 * netgraph node. Installing this line discipline on a terminal device
48 * instantiates a new netgraph node of this type, which allows access
49 * to the device via the "hook" hook of the node.
50 *
51 * Once the line discipline is installed, you can find out the name
52 * of the corresponding netgraph node via a NGIOCGINFO ioctl().
53 *
54 * Incoming characters are delievered to the hook one at a time, each
55 * in its own mbuf. You may optionally define a ``hotchar,'' which causes
56 * incoming characters to be buffered up until either the hotchar is
57 * seen or the mbuf is full (MHLEN bytes). Then all buffered characters
58 * are immediately delivered.
59 *
60 * NOTE: This node operates at spltty().
61 */
62
63#include <sys/param.h>
64#include <sys/systm.h>
65#include <sys/kernel.h>
66#include <sys/conf.h>
67#include <sys/mbuf.h>
68#include <sys/malloc.h>
69#include <sys/fcntl.h>
70#include <sys/tty.h>
71#include <sys/ttycom.h>
72#include <sys/syslog.h>
73#include <sys/errno.h>
74#include <sys/ioccom.h>
75
76#include <netgraph/ng_message.h>
77#include <netgraph/netgraph.h>
78#include <netgraph/ng_tty.h>
79
80NET_NEEDS_GIANT("ng_tty");
81
82/* Misc defs */
83#define MAX_MBUFQ		3	/* Max number of queued mbufs */
84#define NGT_HIWATER		400	/* High water mark on output */
85
86/* Per-node private info */
87struct ngt_sc {
88	struct	tty *tp;		/* Terminal device */
89	node_p	node;			/* Netgraph node */
90	hook_p	hook;			/* Netgraph hook */
91	struct	mbuf *m;		/* Incoming data buffer */
92	struct	mbuf *qhead, **qtail;	/* Queue of outgoing mbuf's */
93	short	qlen;			/* Length of queue */
94	short	hotchar;		/* Hotchar, or -1 if none */
95	u_int	flags;			/* Flags */
96	struct	callout	chand;		/* See man timeout(9) */
97};
98typedef struct ngt_sc *sc_p;
99
100/* Flags */
101#define FLG_TIMEOUT		0x0001	/* A timeout is pending */
102#define FLG_DEBUG		0x0002
103
104/* Debugging */
105#ifdef INVARIANTS
106#define QUEUECHECK(sc)							\
107    do {								\
108      struct mbuf	**mp;						\
109      int		k;						\
110									\
111      for (k = 0, mp = &sc->qhead;					\
112	k <= MAX_MBUFQ && *mp;						\
113	k++, mp = &(*mp)->m_nextpkt);					\
114      if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail)	\
115	panic("%s: queue", __func__);					\
116    } while (0)
117#else
118#define QUEUECHECK(sc)	do {} while (0)
119#endif
120
121/* Line discipline methods */
122static int	ngt_open(struct cdev *dev, struct tty *tp);
123static int	ngt_close(struct tty *tp, int flag);
124static int	ngt_read(struct tty *tp, struct uio *uio, int flag);
125static int	ngt_write(struct tty *tp, struct uio *uio, int flag);
126static int	ngt_tioctl(struct tty *tp,
127		    u_long cmd, caddr_t data, int flag, struct thread *);
128static int	ngt_input(int c, struct tty *tp);
129static int	ngt_start(struct tty *tp);
130
131/* Netgraph methods */
132static ng_constructor_t	ngt_constructor;
133static ng_rcvmsg_t	ngt_rcvmsg;
134static ng_shutdown_t	ngt_shutdown;
135static ng_newhook_t	ngt_newhook;
136static ng_connect_t	ngt_connect;
137static ng_rcvdata_t	ngt_rcvdata;
138static ng_disconnect_t	ngt_disconnect;
139static int	ngt_mod_event(module_t mod, int event, void *data);
140
141/* Other stuff */
142static void	ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2);
143
144#define ERROUT(x)		do { error = (x); goto done; } while (0)
145
146/* Line discipline descriptor */
147static struct linesw ngt_disc = {
148	ngt_open,
149	ngt_close,
150	ngt_read,
151	ngt_write,
152	ngt_tioctl,
153	ngt_input,
154	ngt_start,
155	ttymodem
156};
157
158/* Netgraph node type descriptor */
159static struct ng_type typestruct = {
160	.version =	NG_ABI_VERSION,
161	.name =		NG_TTY_NODE_TYPE,
162	.mod_event =	ngt_mod_event,
163	.constructor =	ngt_constructor,
164	.rcvmsg =	ngt_rcvmsg,
165	.shutdown =	ngt_shutdown,
166	.newhook =	ngt_newhook,
167	.connect =	ngt_connect,
168	.rcvdata =	ngt_rcvdata,
169	.disconnect =	ngt_disconnect,
170};
171NETGRAPH_INIT(tty, &typestruct);
172
173/*
174 * XXXRW: ngt_unit is protected by ng_tty_mtx.  ngt_ldisc is constant once
175 * ng_tty is initialized.  ngt_nodeop_ok is untouched, and might want to be a
176 * sleep lock in the future?
177 */
178static int ngt_unit;
179static int ngt_nodeop_ok;	/* OK to create/remove node */
180static int ngt_ldisc;
181
182static struct mtx	ng_tty_mtx;
183MTX_SYSINIT(ng_tty, &ng_tty_mtx, "ng_tty", MTX_DEF);
184
185/******************************************************************
186		    LINE DISCIPLINE METHODS
187******************************************************************/
188
189/*
190 * Set our line discipline on the tty.
191 * Called from device open routine or ttioctl() at >= splsofttty()
192 */
193static int
194ngt_open(struct cdev *dev, struct tty *tp)
195{
196	struct thread *const td = curthread;	/* XXX */
197	char name[sizeof(NG_TTY_NODE_TYPE) + 8];
198	sc_p sc;
199	int s, error;
200
201	/* Super-user only */
202	if ((error = suser(td)))
203		return (error);
204	s = splnet();
205	(void) spltty();	/* XXX is this necessary? */
206
207	tp->t_hotchar = NG_TTY_DFL_HOTCHAR;
208
209	/* Initialize private struct */
210	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
211	if (sc == NULL) {
212		error = ENOMEM;
213		goto done;
214	}
215	sc->tp = tp;
216	sc->hotchar = NG_TTY_DFL_HOTCHAR;
217	sc->qtail = &sc->qhead;
218	QUEUECHECK(sc);
219	ng_callout_init(&sc->chand);
220
221	/* Setup netgraph node */
222	ngt_nodeop_ok = 1;
223	error = ng_make_node_common(&typestruct, &sc->node);
224	ngt_nodeop_ok = 0;
225	if (error) {
226		FREE(sc, M_NETGRAPH);
227		goto done;
228	}
229	mtx_lock(&ng_tty_mtx);
230	snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit++);
231	mtx_unlock(&ng_tty_mtx);
232
233	/* Assign node its name */
234	if ((error = ng_name_node(sc->node, name))) {
235		log(LOG_ERR, "%s: node name exists?\n", name);
236		ngt_nodeop_ok = 1;
237		NG_NODE_UNREF(sc->node);
238		ngt_nodeop_ok = 0;
239		goto done;
240	}
241
242	/* Set back pointers */
243	NG_NODE_SET_PRIVATE(sc->node, sc);
244	tp->t_lsc = sc;
245
246	/*
247	 * Pre-allocate cblocks to the an appropriate amount.
248	 * I'm not sure what is appropriate.
249	 */
250	ttyflush(tp, FREAD | FWRITE);
251	clist_alloc_cblocks(&tp->t_canq, 0, 0);
252	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
253	clist_alloc_cblocks(&tp->t_outq,
254	    MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
255
256done:
257	/* Done */
258	splx(s);
259	return (error);
260}
261
262/*
263 * Line specific close routine, called from device close routine
264 * and from ttioctl at >= splsofttty(). This causes the node to
265 * be destroyed as well.
266 */
267static int
268ngt_close(struct tty *tp, int flag)
269{
270	const sc_p sc = (sc_p) tp->t_lsc;
271	int s;
272
273	s = spltty();
274	ttyflush(tp, FREAD | FWRITE);
275	clist_free_cblocks(&tp->t_outq);
276	if (sc != NULL) {
277		if (sc->flags & FLG_TIMEOUT) {
278			ng_uncallout(&sc->chand, sc->node);
279			sc->flags &= ~FLG_TIMEOUT;
280		}
281		ngt_nodeop_ok = 1;
282		ng_rmnode_self(sc->node);
283		ngt_nodeop_ok = 0;
284		tp->t_lsc = NULL;
285	}
286	splx(s);
287	return (0);
288}
289
290/*
291 * Once the device has been turned into a node, we don't allow reading.
292 */
293static int
294ngt_read(struct tty *tp, struct uio *uio, int flag)
295{
296	return (EIO);
297}
298
299/*
300 * Once the device has been turned into a node, we don't allow writing.
301 */
302static int
303ngt_write(struct tty *tp, struct uio *uio, int flag)
304{
305	return (EIO);
306}
307
308/*
309 * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
310 */
311static int
312ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td)
313{
314	const sc_p sc = (sc_p) tp->t_lsc;
315	int s, error = 0;
316
317	s = spltty();
318	switch (cmd) {
319	case NGIOCGINFO:
320	    {
321		struct nodeinfo *const ni = (struct nodeinfo *) data;
322		const node_p node = sc->node;
323
324		bzero(ni, sizeof(*ni));
325		if (NG_NODE_HAS_NAME(node))
326			strncpy(ni->name, NG_NODE_NAME(node), sizeof(ni->name) - 1);
327		strncpy(ni->type, node->nd_type->name, sizeof(ni->type) - 1);
328		ni->id = (u_int32_t) ng_node2ID(node);
329		ni->hooks = NG_NODE_NUMHOOKS(node);
330		break;
331	    }
332	default:
333		ERROUT(ENOIOCTL);
334	}
335done:
336	splx(s);
337	return (error);
338}
339
340/*
341 * Receive data coming from the device. We get one character at
342 * a time, which is kindof silly.
343 * Only guaranteed to be at splsofttty() or spltty().
344 */
345static int
346ngt_input(int c, struct tty *tp)
347{
348	const sc_p sc = (sc_p) tp->t_lsc;
349	const node_p node = sc->node;
350	struct mbuf *m;
351	int s, error = 0;
352
353	if (!sc || tp != sc->tp)
354		return (0);
355	s = spltty();
356	if (!sc->hook)
357		ERROUT(0);
358
359	/* Check for error conditions */
360	if ((tp->t_state & TS_CONNECTED) == 0) {
361		if (sc->flags & FLG_DEBUG)
362			log(LOG_DEBUG, "%s: no carrier\n", NG_NODE_NAME(node));
363		ERROUT(0);
364	}
365	if (c & TTY_ERRORMASK) {
366		/* framing error or overrun on this char */
367		if (sc->flags & FLG_DEBUG)
368			log(LOG_DEBUG, "%s: line error %x\n",
369			    NG_NODE_NAME(node), c & TTY_ERRORMASK);
370		ERROUT(0);
371	}
372	c &= TTY_CHARMASK;
373
374	/* Get a new header mbuf if we need one */
375	if (!(m = sc->m)) {
376		MGETHDR(m, M_DONTWAIT, MT_DATA);
377		if (!m) {
378			if (sc->flags & FLG_DEBUG)
379				log(LOG_ERR,
380				    "%s: can't get mbuf\n", NG_NODE_NAME(node));
381			ERROUT(ENOBUFS);
382		}
383		m->m_len = m->m_pkthdr.len = 0;
384		m->m_pkthdr.rcvif = NULL;
385		sc->m = m;
386	}
387
388	/* Add char to mbuf */
389	*mtod(m, u_char *) = c;
390	m->m_data++;
391	m->m_len++;
392	m->m_pkthdr.len++;
393
394	/* Ship off mbuf if it's time */
395	if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
396		m->m_data = m->m_pktdat;
397		NG_SEND_DATA_ONLY(error, sc->hook, m);
398		sc->m = NULL;
399	}
400done:
401	splx(s);
402	return (error);
403}
404
405/*
406 * This is called when the device driver is ready for more output.
407 * Called from tty system at splsofttty() or spltty().
408 * Also call from ngt_rcv_data() when a new mbuf is available for output.
409 */
410static int
411ngt_start(struct tty *tp)
412{
413	const sc_p sc = (sc_p) tp->t_lsc;
414	int s;
415
416	s = spltty();
417	while (tp->t_outq.c_cc < NGT_HIWATER) {	/* XXX 2.2 specific ? */
418		struct mbuf *m = sc->qhead;
419
420		/* Remove first mbuf from queue */
421		if (!m)
422			break;
423		if ((sc->qhead = m->m_nextpkt) == NULL)
424			sc->qtail = &sc->qhead;
425		sc->qlen--;
426		QUEUECHECK(sc);
427
428		/* Send as much of it as possible */
429		while (m) {
430			int     sent;
431
432			sent = m->m_len
433			    - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq);
434			m->m_data += sent;
435			m->m_len -= sent;
436			if (m->m_len > 0)
437				break;	/* device can't take no more */
438			m = m_free(m);
439		}
440
441		/* Put remainder of mbuf chain (if any) back on queue */
442		if (m) {
443			m->m_nextpkt = sc->qhead;
444			sc->qhead = m;
445			if (sc->qtail == &sc->qhead)
446				sc->qtail = &m->m_nextpkt;
447			sc->qlen++;
448			QUEUECHECK(sc);
449			break;
450		}
451	}
452
453	/* Call output process whether or not there is any output. We are
454	 * being called in lieu of ttstart and must do what it would. */
455	if (tp->t_oproc != NULL)
456		(*tp->t_oproc) (tp);
457
458	/* This timeout is needed for operation on a pseudo-tty, because the
459	 * pty code doesn't call pppstart after it has drained the t_outq. */
460	if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) {
461		ng_callout(&sc->chand, sc->node, NULL, 1, ngt_timeout, NULL, 0);
462		sc->flags |= FLG_TIMEOUT;
463	}
464	splx(s);
465	return (0);
466}
467
468/*
469 * We still have data to output to the device, so try sending more.
470 */
471static void
472ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2)
473{
474	const sc_p sc = NG_NODE_PRIVATE(node);
475	int s;
476
477	s = spltty();
478	sc->flags &= ~FLG_TIMEOUT;
479	ngt_start(sc->tp);
480	splx(s);
481}
482
483/******************************************************************
484		    NETGRAPH NODE METHODS
485******************************************************************/
486
487/*
488 * Initialize a new node of this type.
489 *
490 * We only allow nodes to be created as a result of setting
491 * the line discipline on a tty, so always return an error if not.
492 */
493static int
494ngt_constructor(node_p node)
495{
496	return (EOPNOTSUPP);
497}
498
499/*
500 * Add a new hook. There can only be one.
501 */
502static int
503ngt_newhook(node_p node, hook_p hook, const char *name)
504{
505	const sc_p sc = NG_NODE_PRIVATE(node);
506	int s, error = 0;
507
508	if (strcmp(name, NG_TTY_HOOK))
509		return (EINVAL);
510	s = spltty();
511	if (sc->hook)
512		ERROUT(EISCONN);
513	sc->hook = hook;
514done:
515	splx(s);
516	return (error);
517}
518
519/*
520 * Set the hooks into queueing mode (for outgoing packets)
521 * Force single client at a time.
522 */
523static int
524ngt_connect(hook_p hook)
525{
526	/*NG_HOOK_FORCE_WRITER(hook);
527	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));*/
528	return (0);
529}
530
531/*
532 * Disconnect the hook
533 */
534static int
535ngt_disconnect(hook_p hook)
536{
537	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
538	int s;
539
540	s = spltty();
541	if (hook != sc->hook)
542		panic(__func__);
543	sc->hook = NULL;
544	m_freem(sc->m);
545	sc->m = NULL;
546	splx(s);
547	return (0);
548}
549
550/*
551 * Remove this node. The does the netgraph portion of the shutdown.
552 * This should only be called indirectly from ngt_close().
553 */
554static int
555ngt_shutdown(node_p node)
556{
557	const sc_p sc = NG_NODE_PRIVATE(node);
558
559	if (!ngt_nodeop_ok)
560		return (EOPNOTSUPP);
561	NG_NODE_SET_PRIVATE(node, NULL);
562	NG_NODE_UNREF(sc->node);
563	m_freem(sc->qhead);
564	m_freem(sc->m);
565	bzero(sc, sizeof(*sc));
566	FREE(sc, M_NETGRAPH);
567	return (0);
568}
569
570/*
571 * Receive incoming data from netgraph system. Put it on our
572 * output queue and start output if necessary.
573 */
574static int
575ngt_rcvdata(hook_p hook, item_p item)
576{
577	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
578	int s, error = 0;
579	struct mbuf *m;
580
581	if (hook != sc->hook)
582		panic(__func__);
583
584	NGI_GET_M(item, m);
585	NG_FREE_ITEM(item);
586	s = spltty();
587	if (sc->qlen >= MAX_MBUFQ)
588		ERROUT(ENOBUFS);
589	m->m_nextpkt = NULL;
590	*sc->qtail = m;
591	sc->qtail = &m->m_nextpkt;
592	sc->qlen++;
593	QUEUECHECK(sc);
594	m = NULL;
595	if (sc->qlen == 1)
596		ngt_start(sc->tp);
597done:
598	splx(s);
599	if (m)
600		m_freem(m);
601	return (error);
602}
603
604/*
605 * Receive control message
606 */
607static int
608ngt_rcvmsg(node_p node, item_p item, hook_p lasthook)
609{
610	const sc_p sc = NG_NODE_PRIVATE(node);
611	struct ng_mesg *resp = NULL;
612	int error = 0;
613	struct ng_mesg *msg;
614
615	NGI_GET_MSG(item, msg);
616	switch (msg->header.typecookie) {
617	case NGM_TTY_COOKIE:
618		switch (msg->header.cmd) {
619		case NGM_TTY_SET_HOTCHAR:
620		    {
621			int     hotchar;
622
623			if (msg->header.arglen != sizeof(int))
624				ERROUT(EINVAL);
625			hotchar = *((int *) msg->data);
626			if (hotchar != (u_char) hotchar && hotchar != -1)
627				ERROUT(EINVAL);
628			sc->hotchar = hotchar;	/* race condition is OK */
629			break;
630		    }
631		case NGM_TTY_GET_HOTCHAR:
632			NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
633			if (!resp)
634				ERROUT(ENOMEM);
635			/* Race condition here is OK */
636			*((int *) resp->data) = sc->hotchar;
637			break;
638		default:
639			ERROUT(EINVAL);
640		}
641		break;
642	default:
643		ERROUT(EINVAL);
644	}
645done:
646	NG_RESPOND_MSG(error, node, item, resp);
647	NG_FREE_MSG(msg);
648	return (error);
649}
650
651/******************************************************************
652		    	INITIALIZATION
653******************************************************************/
654
655/*
656 * Handle loading and unloading for this node type
657 */
658static int
659ngt_mod_event(module_t mod, int event, void *data)
660{
661	/* struct ng_type *const type = data;*/
662	int s, error = 0;
663
664	switch (event) {
665	case MOD_LOAD:
666
667		/* Register line discipline */
668		s = spltty();
669		if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
670			splx(s);
671			log(LOG_ERR, "%s: can't register line discipline",
672			    __func__);
673			return (EIO);
674		}
675		splx(s);
676		break;
677
678	case MOD_UNLOAD:
679
680		/* Unregister line discipline */
681		s = spltty();
682		ldisc_deregister(ngt_ldisc);
683		splx(s);
684		break;
685
686	default:
687		error = EOPNOTSUPP;
688		break;
689	}
690	return (error);
691}
692
693