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