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