1/*
2 * ng_h4.c
3 */
4
5/*-
6 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: ng_h4.c,v 1.10 2005/10/31 17:57:43 max Exp $
31 * $FreeBSD$
32 *
33 * Based on:
34 * ---------
35 *
36 * FreeBSD: src/sys/netgraph/ng_tty.c
37 * Author: Archie Cobbs <archie@freebsd.org>
38 *
39 */
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/conf.h>
45#include <sys/endian.h>
46#include <sys/errno.h>
47#include <sys/fcntl.h>
48#include <sys/ioccom.h>
49#include <sys/malloc.h>
50#include <sys/mbuf.h>
51#include <sys/priv.h>
52#include <sys/socket.h>
53#include <sys/tty.h>
54#include <sys/ttycom.h>
55#include <net/if.h>
56#include <net/if_var.h>
57#include <netgraph/ng_message.h>
58#include <netgraph/netgraph.h>
59#include <netgraph/ng_parse.h>
60#include <netgraph/bluetooth/include/ng_bluetooth.h>
61#include <netgraph/bluetooth/include/ng_hci.h>
62#include <netgraph/bluetooth/include/ng_h4.h>
63#include <netgraph/bluetooth/drivers/h4/ng_h4_var.h>
64#include <netgraph/bluetooth/drivers/h4/ng_h4_prse.h>
65
66/*****************************************************************************
67 *****************************************************************************
68 ** This node implements a Bluetooth HCI UART transport layer as per chapter
69 ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line
70 ** discipline that is also a netgraph node. Installing this line discipline
71 ** on a terminal device instantiates a new netgraph node of this type, which
72 ** allows access to the device via the "hook" hook of the node.
73 **
74 ** Once the line discipline is installed, you can find out the name of the
75 ** corresponding netgraph node via a NGIOCGINFO ioctl().
76 *****************************************************************************
77 *****************************************************************************/
78
79/* MALLOC define */
80#ifndef NG_SEPARATE_MALLOC
81MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node");
82#else
83#define M_NETGRAPH_H4 M_NETGRAPH
84#endif /* NG_SEPARATE_MALLOC */
85
86/* Line discipline methods */
87static int	ng_h4_open	(struct cdev *, struct tty *);
88static int	ng_h4_close	(struct tty *, int);
89static int	ng_h4_read	(struct tty *, struct uio *, int);
90static int	ng_h4_write	(struct tty *, struct uio *, int);
91static int	ng_h4_input	(int, struct tty *);
92static int	ng_h4_start	(struct tty *);
93static int	ng_h4_ioctl	(struct tty *, u_long, caddr_t,
94					int, struct thread *);
95
96/* Line discipline descriptor */
97static struct linesw		ng_h4_disc = {
98	ng_h4_open,		/* open */
99	ng_h4_close,		/* close */
100	ng_h4_read,		/* read */
101	ng_h4_write,		/* write */
102	ng_h4_ioctl,		/* ioctl */
103	ng_h4_input,		/* input */
104	ng_h4_start,		/* start */
105	ttymodem		/* modem */
106};
107
108/* Netgraph methods */
109static ng_constructor_t		ng_h4_constructor;
110static ng_rcvmsg_t		ng_h4_rcvmsg;
111static ng_shutdown_t		ng_h4_shutdown;
112static ng_newhook_t		ng_h4_newhook;
113static ng_connect_t		ng_h4_connect;
114static ng_rcvdata_t		ng_h4_rcvdata;
115static ng_disconnect_t		ng_h4_disconnect;
116
117/* Other stuff */
118static void	ng_h4_process_timeout	(node_p, hook_p, void *, int);
119static int	ng_h4_mod_event		(module_t, int, void *);
120
121/* Netgraph node type descriptor */
122static struct ng_type		typestruct = {
123	.version =	NG_ABI_VERSION,
124	.name =		NG_H4_NODE_TYPE,
125	.mod_event =	ng_h4_mod_event,
126	.constructor =	ng_h4_constructor,
127	.rcvmsg =	ng_h4_rcvmsg,
128	.shutdown =	ng_h4_shutdown,
129	.newhook =	ng_h4_newhook,
130	.connect =	ng_h4_connect,
131	.rcvdata =	ng_h4_rcvdata,
132	.disconnect =	ng_h4_disconnect,
133	.cmdlist =	ng_h4_cmdlist
134};
135NETGRAPH_INIT(h4, &typestruct);
136MODULE_VERSION(ng_h4, NG_BLUETOOTH_VERSION);
137
138static int	ng_h4_node = 0;
139
140/*****************************************************************************
141 *****************************************************************************
142 **			    Line discipline methods
143 *****************************************************************************
144 *****************************************************************************/
145
146/*
147 * Set our line discipline on the tty.
148 */
149
150static int
151ng_h4_open(struct cdev *dev, struct tty *tp)
152{
153	struct thread	*td = curthread;
154	char		 name[NG_NODESIZ];
155	ng_h4_info_p	 sc = NULL;
156	int		 error;
157
158	/* Super-user only */
159	error = priv_check(td, PRIV_NETGRAPH_TTY); /* XXX */
160	if (error != 0)
161		return (error);
162
163	/* Initialize private struct */
164	sc = malloc(sizeof(*sc), M_NETGRAPH_H4, M_NOWAIT|M_ZERO);
165	if (sc == NULL)
166		return (ENOMEM);
167
168	sc->tp = tp;
169	sc->debug = NG_H4_WARN_LEVEL;
170
171	sc->state = NG_H4_W4_PKT_IND;
172	sc->want = 1;
173	sc->got = 0;
174
175	mtx_init(&sc->outq.ifq_mtx, "ng_h4 node+queue", NULL, MTX_DEF);
176	IFQ_SET_MAXLEN(&sc->outq, NG_H4_DEFAULTQLEN);
177	ng_callout_init(&sc->timo);
178
179	NG_H4_LOCK(sc);
180
181	/* Setup netgraph node */
182	error = ng_make_node_common(&typestruct, &sc->node);
183	if (error != 0) {
184		NG_H4_UNLOCK(sc);
185
186		printf("%s: Unable to create new node!\n", __func__);
187
188		mtx_destroy(&sc->outq.ifq_mtx);
189		bzero(sc, sizeof(*sc));
190		free(sc, M_NETGRAPH_H4);
191
192		return (error);
193	}
194
195	/* Assign node its name */
196	snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
197
198	error = ng_name_node(sc->node, name);
199	if (error != 0) {
200		NG_H4_UNLOCK(sc);
201
202		printf("%s: %s - node name exists?\n", __func__, name);
203
204		NG_NODE_UNREF(sc->node);
205		mtx_destroy(&sc->outq.ifq_mtx);
206		bzero(sc, sizeof(*sc));
207		free(sc, M_NETGRAPH_H4);
208
209		return (error);
210	}
211
212	/* Set back pointers */
213	NG_NODE_SET_PRIVATE(sc->node, sc);
214	tp->t_lsc = (caddr_t) sc;
215
216	/* The node has to be a WRITER because data can change node status */
217	NG_NODE_FORCE_WRITER(sc->node);
218
219	/*
220	 * Pre-allocate cblocks to the an appropriate amount.
221	 * I'm not sure what is appropriate.
222	 */
223
224	ttyflush(tp, FREAD | FWRITE);
225	clist_alloc_cblocks(&tp->t_canq, 0, 0);
226	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
227	clist_alloc_cblocks(&tp->t_outq,
228		MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER);
229
230	NG_H4_UNLOCK(sc);
231
232	return (error);
233} /* ng_h4_open */
234
235/*
236 * Line specific close routine, called from device close routine
237 * and from ttioctl. This causes the node to be destroyed as well.
238 */
239
240static int
241ng_h4_close(struct tty *tp, int flag)
242{
243	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
244
245	ttyflush(tp, FREAD | FWRITE);
246	clist_free_cblocks(&tp->t_outq);
247
248	if (sc != NULL) {
249		NG_H4_LOCK(sc);
250
251		if (callout_pending(&sc->timo))
252			ng_uncallout(&sc->timo, sc->node);
253
254		tp->t_lsc = NULL;
255		sc->dying = 1;
256
257		NG_H4_UNLOCK(sc);
258
259		ng_rmnode_self(sc->node);
260	}
261
262	return (0);
263} /* ng_h4_close */
264
265/*
266 * Once the device has been turned into a node, we don't allow reading.
267 */
268
269static int
270ng_h4_read(struct tty *tp, struct uio *uio, int flag)
271{
272	return (EIO);
273} /* ng_h4_read */
274
275/*
276 * Once the device has been turned into a node, we don't allow writing.
277 */
278
279static int
280ng_h4_write(struct tty *tp, struct uio *uio, int flag)
281{
282	return (EIO);
283} /* ng_h4_write */
284
285/*
286 * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
287 */
288
289static int
290ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
291		struct thread *td)
292{
293	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
294	int		error = 0;
295
296	if (sc == NULL)
297		return (ENXIO);
298
299	NG_H4_LOCK(sc);
300
301	switch (cmd) {
302	case NGIOCGINFO:
303#undef	NI
304#define NI(x)	((struct nodeinfo *)(x))
305
306		bzero(data, sizeof(*NI(data)));
307
308		if (NG_NODE_HAS_NAME(sc->node))
309			strncpy(NI(data)->name, NG_NODE_NAME(sc->node),
310				sizeof(NI(data)->name) - 1);
311
312		strncpy(NI(data)->type, sc->node->nd_type->name,
313			sizeof(NI(data)->type) - 1);
314
315		NI(data)->id = (u_int32_t) ng_node2ID(sc->node);
316		NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node);
317		break;
318
319	default:
320		error = ENOIOCTL;
321		break;
322	}
323
324	NG_H4_UNLOCK(sc);
325
326	return (error);
327} /* ng_h4_ioctl */
328
329/*
330 * Receive data coming from the device. We get one character at a time, which
331 * is kindof silly.
332 */
333
334static int
335ng_h4_input(int c, struct tty *tp)
336{
337	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
338
339	if (sc == NULL || tp != sc->tp ||
340	    sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
341		return (0);
342
343	NG_H4_LOCK(sc);
344
345	/* Check for error conditions */
346	if ((tp->t_state & TS_CONNECTED) == 0) {
347		NG_H4_INFO("%s: %s - no carrier\n", __func__,
348			NG_NODE_NAME(sc->node));
349
350		sc->state = NG_H4_W4_PKT_IND;
351		sc->want = 1;
352		sc->got = 0;
353
354		NG_H4_UNLOCK(sc);
355
356		return (0); /* XXX Loss of synchronization here! */
357	}
358
359	/* Check for framing error or overrun on this char */
360	if (c & TTY_ERRORMASK) {
361		NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__,
362			NG_NODE_NAME(sc->node), c & TTY_ERRORMASK,
363			c & TTY_CHARMASK);
364
365		NG_H4_STAT_IERROR(sc->stat);
366
367		sc->state = NG_H4_W4_PKT_IND;
368		sc->want = 1;
369		sc->got = 0;
370
371		NG_H4_UNLOCK(sc);
372
373		return (0); /* XXX Loss of synchronization here! */
374	}
375
376	NG_H4_STAT_BYTES_RECV(sc->stat, 1);
377
378	/* Append char to mbuf */
379	if (sc->got >= sizeof(sc->ibuf)) {
380		NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n",
381			__func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK,
382			sc->got);
383
384		NG_H4_STAT_IERROR(sc->stat);
385
386		sc->state = NG_H4_W4_PKT_IND;
387		sc->want = 1;
388		sc->got = 0;
389
390		NG_H4_UNLOCK(sc);
391
392		return (0); /* XXX Loss of synchronization here! */
393	}
394
395	sc->ibuf[sc->got ++] = (c & TTY_CHARMASK);
396
397	NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__,
398		NG_NODE_NAME(sc->node), c, sc->want, sc->got);
399
400	if (sc->got < sc->want) {
401		NG_H4_UNLOCK(sc);
402
403		return (0); /* Wait for more */
404	}
405
406	switch (sc->state) {
407	/* Got packet indicator */
408	case NG_H4_W4_PKT_IND:
409		NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__,
410			NG_NODE_NAME(sc->node), sc->ibuf[0]);
411
412		sc->state = NG_H4_W4_PKT_HDR;
413
414		/*
415		 * Since packet indicator included in the packet header
416		 * just set sc->want to sizeof(packet header).
417		 */
418
419		switch (sc->ibuf[0]) {
420		case NG_HCI_ACL_DATA_PKT:
421			sc->want = sizeof(ng_hci_acldata_pkt_t);
422			break;
423
424		case NG_HCI_SCO_DATA_PKT:
425			sc->want = sizeof(ng_hci_scodata_pkt_t);
426			break;
427
428		case NG_HCI_EVENT_PKT:
429			sc->want = sizeof(ng_hci_event_pkt_t);
430			break;
431
432		default:
433			NG_H4_WARN("%s: %s - ignoring unknown packet " \
434				"type=%#x\n", __func__, NG_NODE_NAME(sc->node),
435				sc->ibuf[0]);
436
437			NG_H4_STAT_IERROR(sc->stat);
438
439			sc->state = NG_H4_W4_PKT_IND;
440			sc->want = 1;
441			sc->got = 0;
442			break;
443		}
444		break;
445
446	/* Got packet header */
447	case NG_H4_W4_PKT_HDR:
448		sc->state = NG_H4_W4_PKT_DATA;
449
450		switch (sc->ibuf[0]) {
451		case NG_HCI_ACL_DATA_PKT:
452			c = le16toh(((ng_hci_acldata_pkt_t *)
453				(sc->ibuf))->length);
454			break;
455
456		case NG_HCI_SCO_DATA_PKT:
457			c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length;
458			break;
459
460		case NG_HCI_EVENT_PKT:
461			c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length;
462			break;
463
464		default:
465			KASSERT((0), ("Invalid packet type=%#x\n",
466				sc->ibuf[0]));
467			break;
468		}
469
470		NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \
471			"packet size=%d, payload size=%d\n", __func__,
472			NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c);
473
474		if (c > 0) {
475			sc->want += c;
476
477			/*
478			 * Try to prevent possible buffer overrun
479			 *
480			 * XXX I'm *really* confused here. It turns out
481			 * that Xircom card sends us packets with length
482			 * greater then 512 bytes! This is greater then
483			 * our old receive buffer (ibuf) size. In the same
484			 * time the card demands from us *not* to send
485			 * packets greater then 192 bytes. Weird! How the
486			 * hell i should know how big *receive* buffer
487			 * should be? For now increase receiving buffer
488			 * size to 1K and add the following check.
489			 */
490
491			if (sc->want >= sizeof(sc->ibuf)) {
492				int	b;
493
494				NG_H4_ALERT("%s: %s - packet too big for " \
495					"buffer, type=%#x, got=%d, want=%d, " \
496					"length=%d\n", __func__,
497					NG_NODE_NAME(sc->node), sc->ibuf[0],
498					sc->got, sc->want, c);
499
500				NG_H4_ALERT("Packet header:\n");
501				for (b = 0; b < sc->got; b++)
502					NG_H4_ALERT("%#x ", sc->ibuf[b]);
503				NG_H4_ALERT("\n");
504
505				/* Reset state */
506				NG_H4_STAT_IERROR(sc->stat);
507
508				sc->state = NG_H4_W4_PKT_IND;
509				sc->want = 1;
510				sc->got = 0;
511			}
512
513			break;
514		}
515
516		/* else FALLTHROUGH and deliver frame */
517		/* XXX Is this true? Should we deliver empty frame? */
518
519	/* Got packet data */
520	case NG_H4_W4_PKT_DATA:
521		NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \
522			"packet size=%d\n", __func__,
523			NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got);
524
525		if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
526			struct mbuf	*m = NULL;
527
528			MGETHDR(m, M_DONTWAIT, MT_DATA);
529			if (m != NULL) {
530				m->m_pkthdr.len = 0;
531
532				/* XXX m_copyback() is stupid */
533				m->m_len = min(MHLEN, sc->got);
534
535				m_copyback(m, 0, sc->got, sc->ibuf);
536				NG_SEND_DATA_ONLY(c, sc->hook, m);
537			} else {
538				NG_H4_ERR("%s: %s - could not get mbuf\n",
539					__func__, NG_NODE_NAME(sc->node));
540
541				NG_H4_STAT_IERROR(sc->stat);
542			}
543		}
544
545		sc->state = NG_H4_W4_PKT_IND;
546		sc->want = 1;
547		sc->got = 0;
548
549		NG_H4_STAT_PCKTS_RECV(sc->stat);
550		break;
551
552	default:
553		KASSERT((0), ("Invalid H4 node state=%d", sc->state));
554		break;
555	}
556
557	NG_H4_UNLOCK(sc);
558
559	return (0);
560} /* ng_h4_input */
561
562/*
563 * This is called when the device driver is ready for more output. Called from
564 * tty system.
565 */
566
567static int
568ng_h4_start(struct tty *tp)
569{
570	ng_h4_info_p	 sc = (ng_h4_info_p) tp->t_lsc;
571	struct mbuf	*m = NULL;
572	int		 size;
573
574	if (sc == NULL || tp != sc->tp ||
575	    sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
576		return (0);
577
578#if 0
579	while (tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */
580#else
581	while (1) {
582#endif
583		/* Remove first mbuf from queue */
584		IF_DEQUEUE(&sc->outq, m);
585		if (m == NULL)
586			break;
587
588		/* Send as much of it as possible */
589		while (m != NULL) {
590			size = m->m_len - b_to_q(mtod(m, u_char *),
591					m->m_len, &tp->t_outq);
592
593			NG_H4_LOCK(sc);
594			NG_H4_STAT_BYTES_SENT(sc->stat, size);
595			NG_H4_UNLOCK(sc);
596
597			m->m_data += size;
598			m->m_len -= size;
599			if (m->m_len > 0)
600				break;	/* device can't take no more */
601
602			m = m_free(m);
603		}
604
605		/* Put remainder of mbuf chain (if any) back on queue */
606		if (m != NULL) {
607			IF_PREPEND(&sc->outq, m);
608			break;
609		}
610
611		/* Full packet has been sent */
612		NG_H4_LOCK(sc);
613		NG_H4_STAT_PCKTS_SENT(sc->stat);
614		NG_H4_UNLOCK(sc);
615	}
616
617	/*
618	 * Call output process whether or not there is any output. We are
619	 * being called in lieu of ttstart and must do what it would.
620	 */
621
622	tt_oproc(sc->tp);
623
624	/*
625	 * This timeout is needed for operation on a pseudo-tty, because the
626	 * pty code doesn't call pppstart after it has drained the t_outq.
627	 */
628
629	NG_H4_LOCK(sc);
630
631	if (!IFQ_IS_EMPTY(&sc->outq) && !callout_pending(&sc->timo))
632		ng_callout(&sc->timo, sc->node, NULL, 1,
633			ng_h4_process_timeout, NULL, 0);
634
635	NG_H4_UNLOCK(sc);
636
637	return (0);
638} /* ng_h4_start */
639
640/*****************************************************************************
641 *****************************************************************************
642 **			    Netgraph node methods
643 *****************************************************************************
644 *****************************************************************************/
645
646/*
647 * Initialize a new node of this type. We only allow nodes to be created as
648 * a result of setting the line discipline on a tty, so always return an error
649 * if not.
650 */
651
652static int
653ng_h4_constructor(node_p node)
654{
655	return (EOPNOTSUPP);
656} /* ng_h4_constructor */
657
658/*
659 * Add a new hook. There can only be one.
660 */
661
662static int
663ng_h4_newhook(node_p node, hook_p hook, const char *name)
664{
665	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
666
667	if (strcmp(name, NG_H4_HOOK) != 0)
668		return (EINVAL);
669
670	NG_H4_LOCK(sc);
671
672	if (sc->hook != NULL) {
673		NG_H4_UNLOCK(sc);
674		return (EISCONN);
675	}
676	sc->hook = hook;
677
678	NG_H4_UNLOCK(sc);
679
680	return (0);
681} /* ng_h4_newhook */
682
683/*
684 * Connect hook. Just say yes.
685 */
686
687static int
688ng_h4_connect(hook_p hook)
689{
690	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
691
692	if (hook != sc->hook)
693		panic("%s: hook != sc->hook\n", __func__);
694
695	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
696	NG_HOOK_FORCE_QUEUE(hook);
697
698	return (0);
699} /* ng_h4_connect */
700
701/*
702 * Disconnect the hook
703 */
704
705static int
706ng_h4_disconnect(hook_p hook)
707{
708	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
709
710	/*
711	 * We need to check for sc != NULL because we can be called from
712	 * ng_h4_close() via ng_rmnode_self()
713	 */
714
715	if (sc != NULL) {
716		if (hook != sc->hook)
717			panic("%s: hook != sc->hook\n", __func__);
718
719		NG_H4_LOCK(sc);
720
721		/* XXX do we have to untimeout and drain out queue? */
722		if (callout_pending(&sc->timo))
723			ng_uncallout(&sc->timo, sc->node);
724
725		_IF_DRAIN(&sc->outq);
726
727		sc->state = NG_H4_W4_PKT_IND;
728		sc->want = 1;
729		sc->got = 0;
730
731		sc->hook = NULL;
732
733		NG_H4_UNLOCK(sc);
734	}
735
736	return (0);
737} /* ng_h4_disconnect */
738
739/*
740 * Remove this node. The does the netgraph portion of the shutdown.
741 * This should only be called indirectly from ng_h4_close().
742 */
743
744static int
745ng_h4_shutdown(node_p node)
746{
747	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
748
749	NG_H4_LOCK(sc);
750
751	if (!sc->dying) {
752		NG_H4_UNLOCK(sc);
753
754		NG_NODE_REVIVE(node);	/* we will persist */
755
756		return (EOPNOTSUPP);
757	}
758
759	NG_H4_UNLOCK(sc);
760
761	NG_NODE_SET_PRIVATE(node, NULL);
762
763	_IF_DRAIN(&sc->outq);
764
765	NG_NODE_UNREF(node);
766	mtx_destroy(&sc->outq.ifq_mtx);
767	bzero(sc, sizeof(*sc));
768	free(sc, M_NETGRAPH_H4);
769
770	return (0);
771} /* ng_h4_shutdown */
772
773/*
774 * Receive incoming data from Netgraph system. Put it on our
775 * output queue and start output if necessary.
776 */
777
778static int
779ng_h4_rcvdata(hook_p hook, item_p item)
780{
781	ng_h4_info_p	 sc = (ng_h4_info_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
782	struct mbuf	*m = NULL;
783	int		 qlen;
784
785	if (sc == NULL)
786		return (EHOSTDOWN);
787
788	if (hook != sc->hook)
789		panic("%s: hook != sc->hook\n", __func__);
790
791	NGI_GET_M(item, m);
792	NG_FREE_ITEM(item);
793
794	NG_H4_LOCK(sc);
795
796	if (_IF_QFULL(&sc->outq)) {
797		NG_H4_ERR("%s: %s - dropping mbuf, len=%d\n", __func__,
798			NG_NODE_NAME(sc->node), m->m_pkthdr.len);
799
800		NG_H4_STAT_OERROR(sc->stat);
801		_IF_DROP(&sc->outq);
802
803		NG_H4_UNLOCK(sc);
804
805		NG_FREE_M(m);
806
807		return (ENOBUFS);
808	}
809
810	NG_H4_INFO("%s: %s - queue mbuf, len=%d\n", __func__,
811		NG_NODE_NAME(sc->node), m->m_pkthdr.len);
812
813	_IF_ENQUEUE(&sc->outq, m);
814	qlen = _IF_QLEN(&sc->outq);
815
816	NG_H4_UNLOCK(sc);
817
818	/*
819	 * If qlen > 1, then we should already have a scheduled callout
820	 */
821
822	if (qlen == 1) {
823		mtx_lock(&Giant);
824		ng_h4_start(sc->tp);
825		mtx_unlock(&Giant);
826	}
827
828	return (0);
829} /* ng_h4_rcvdata */
830
831/*
832 * Receive control message
833 */
834
835static int
836ng_h4_rcvmsg(node_p node, item_p item, hook_p lasthook)
837{
838	ng_h4_info_p	 sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
839	struct ng_mesg	*msg = NULL, *resp = NULL;
840	int		 error = 0;
841
842	if (sc == NULL)
843		return (EHOSTDOWN);
844
845	NGI_GET_MSG(item, msg);
846	NG_H4_LOCK(sc);
847
848	switch (msg->header.typecookie) {
849	case NGM_GENERIC_COOKIE:
850		switch (msg->header.cmd) {
851		case NGM_TEXT_STATUS:
852			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
853			if (resp == NULL)
854				error = ENOMEM;
855			else
856				snprintf(resp->data, NG_TEXTRESPONSE,
857					"Hook: %s\n"   \
858					"Debug: %d\n"  \
859					"State: %d\n"  \
860					"Queue: [have:%d,max:%d]\n" \
861					"Input: [got:%d,want:%d]",
862					(sc->hook != NULL)? NG_H4_HOOK : "",
863					sc->debug,
864					sc->state,
865					_IF_QLEN(&sc->outq),
866					sc->outq.ifq_maxlen,
867					sc->got,
868					sc->want);
869			break;
870
871		default:
872			error = EINVAL;
873			break;
874		}
875		break;
876
877	case NGM_H4_COOKIE:
878		switch (msg->header.cmd) {
879		case NGM_H4_NODE_RESET:
880			_IF_DRAIN(&sc->outq);
881			sc->state = NG_H4_W4_PKT_IND;
882			sc->want = 1;
883			sc->got = 0;
884			break;
885
886		case NGM_H4_NODE_GET_STATE:
887			NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_state_ep),
888				M_NOWAIT);
889			if (resp == NULL)
890				error = ENOMEM;
891			else
892				*((ng_h4_node_state_ep *)(resp->data)) =
893					sc->state;
894			break;
895
896		case NGM_H4_NODE_GET_DEBUG:
897			NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_debug_ep),
898				M_NOWAIT);
899			if (resp == NULL)
900				error = ENOMEM;
901			else
902				*((ng_h4_node_debug_ep *)(resp->data)) =
903					sc->debug;
904			break;
905
906		case NGM_H4_NODE_SET_DEBUG:
907			if (msg->header.arglen != sizeof(ng_h4_node_debug_ep))
908				error = EMSGSIZE;
909			else
910				sc->debug =
911					*((ng_h4_node_debug_ep *)(msg->data));
912			break;
913
914		case NGM_H4_NODE_GET_QLEN:
915			NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_qlen_ep),
916				M_NOWAIT);
917			if (resp == NULL)
918				error = ENOMEM;
919			else
920				*((ng_h4_node_qlen_ep *)(resp->data)) =
921					sc->outq.ifq_maxlen;
922			break;
923
924		case NGM_H4_NODE_SET_QLEN:
925			if (msg->header.arglen != sizeof(ng_h4_node_qlen_ep))
926				error = EMSGSIZE;
927			else if (*((ng_h4_node_qlen_ep *)(msg->data)) <= 0)
928				error = EINVAL;
929			else
930				IFQ_SET_MAXLEN(&sc->outq,
931					*((ng_h4_node_qlen_ep *)(msg->data)));
932			break;
933
934		case NGM_H4_NODE_GET_STAT:
935			NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_stat_ep),
936				M_NOWAIT);
937			if (resp == NULL)
938				error = ENOMEM;
939			else
940				bcopy(&sc->stat, resp->data,
941					sizeof(ng_h4_node_stat_ep));
942			break;
943
944		case NGM_H4_NODE_RESET_STAT:
945			NG_H4_STAT_RESET(sc->stat);
946			break;
947
948		default:
949			error = EINVAL;
950			break;
951		}
952		break;
953
954	default:
955		error = EINVAL;
956		break;
957	}
958
959	NG_H4_UNLOCK(sc);
960
961	NG_RESPOND_MSG(error, node, item, resp);
962	NG_FREE_MSG(msg);
963
964	return (error);
965} /* ng_h4_rcvmsg */
966
967/*
968 * Timeout processing function.
969 * We still have data to output to the device, so try sending more.
970 */
971
972static void
973ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2)
974{
975	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
976
977	mtx_lock(&Giant);
978	ng_h4_start(sc->tp);
979	mtx_unlock(&Giant);
980} /* ng_h4_process_timeout */
981
982/*
983 * Handle loading and unloading for this node type
984 */
985
986static int
987ng_h4_mod_event(module_t mod, int event, void *data)
988{
989	static int	ng_h4_ldisc;
990	int		error = 0;
991
992	switch (event) {
993	case MOD_LOAD:
994		/* Register line discipline */
995		mtx_lock(&Giant);
996		ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc);
997		mtx_unlock(&Giant);
998
999		if (ng_h4_ldisc < 0) {
1000			printf("%s: can't register H4 line discipline\n",
1001				__func__);
1002			error = EIO;
1003		}
1004		break;
1005
1006	case MOD_UNLOAD:
1007		/* Unregister line discipline */
1008		mtx_lock(&Giant);
1009		ldisc_deregister(ng_h4_ldisc);
1010		mtx_unlock(&Giant);
1011		break;
1012
1013	default:
1014		error = EOPNOTSUPP;
1015		break;
1016	}
1017
1018	return (error);
1019} /* ng_h4_mod_event */
1020
1021