ng_h4.c revision 137133
1/*
2 * ng_h4.c
3 *
4 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $Id: ng_h4.c,v 1.7 2004/08/23 18:08:15 max Exp $
29 * $FreeBSD: head/sys/netgraph/bluetooth/drivers/h4/ng_h4.c 137133 2004-11-02 20:01:42Z emax $
30 *
31 * Based on:
32 * ---------
33 *
34 * FreeBSD: src/sys/netgraph/ng_tty.c
35 * Author: Archie Cobbs <archie@freebsd.org>
36 *
37 */
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/conf.h>
43#include <sys/endian.h>
44#include <sys/errno.h>
45#include <sys/fcntl.h>
46#include <sys/ioccom.h>
47#include <sys/malloc.h>
48#include <sys/mbuf.h>
49#include <sys/tty.h>
50#include <sys/ttycom.h>
51#include <netgraph/ng_message.h>
52#include <netgraph/netgraph.h>
53#include <netgraph/ng_parse.h>
54#include <netgraph/bluetooth/include/ng_bluetooth.h>
55#include <netgraph/bluetooth/include/ng_hci.h>
56#include <netgraph/bluetooth/include/ng_h4.h>
57#include <netgraph/bluetooth/drivers/h4/ng_h4_var.h>
58#include <netgraph/bluetooth/drivers/h4/ng_h4_prse.h>
59
60/*****************************************************************************
61 *****************************************************************************
62 ** This node implements a Bluetooth HCI UART transport layer as per chapter
63 ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line
64 ** discipline that is also a netgraph node. Installing this line discipline
65 ** on a terminal device instantiates a new netgraph node of this type, which
66 ** allows access to the device via the "hook" hook of the node.
67 **
68 ** Once the line discipline is installed, you can find out the name of the
69 ** corresponding netgraph node via a NGIOCGINFO ioctl().
70 *****************************************************************************
71 *****************************************************************************/
72
73NET_NEEDS_GIANT("ng_h4");
74
75/* MALLOC define */
76#ifndef NG_SEPARATE_MALLOC
77MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node");
78#else
79#define M_NETGRAPH_H4 M_NETGRAPH
80#endif /* NG_SEPARATE_MALLOC */
81
82/* Line discipline methods */
83static int	ng_h4_open	(struct cdev *, struct tty *);
84static int	ng_h4_close	(struct tty *, int);
85static int	ng_h4_read	(struct tty *, struct uio *, int);
86static int	ng_h4_write	(struct tty *, struct uio *, int);
87static int	ng_h4_input	(int, struct tty *);
88static int	ng_h4_start	(struct tty *);
89static void	ng_h4_start2	(node_p, hook_p, void *, int);
90static int	ng_h4_ioctl	(struct tty *, u_long, caddr_t,
91					int, struct thread *);
92
93/* Line discipline descriptor */
94static struct linesw		ng_h4_disc = {
95	ng_h4_open,		/* open */
96	ng_h4_close,		/* close */
97	ng_h4_read,		/* read */
98	ng_h4_write,		/* write */
99	ng_h4_ioctl,		/* ioctl */
100	ng_h4_input,		/* input */
101	ng_h4_start,		/* start */
102	ttymodem		/* modem */
103};
104
105/* Netgraph methods */
106static ng_constructor_t		ng_h4_constructor;
107static ng_rcvmsg_t		ng_h4_rcvmsg;
108static ng_shutdown_t		ng_h4_shutdown;
109static ng_newhook_t		ng_h4_newhook;
110static ng_connect_t		ng_h4_connect;
111static ng_rcvdata_t		ng_h4_rcvdata;
112static ng_disconnect_t		ng_h4_disconnect;
113
114/* Other stuff */
115static void	ng_h4_timeout		(node_p);
116static void	ng_h4_untimeout		(node_p);
117static void	ng_h4_queue_timeout	(void *);
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	char		 name[NG_NODESIZ];
154	ng_h4_info_p	 sc = NULL;
155	int		 s, error;
156
157	/* Super-user only */
158	error = suser(curthread); /* XXX */
159	if (error != 0)
160		return (error);
161
162	s = splnet(); /* XXX */
163	spltty(); /* XXX */
164
165	/* Initialize private struct */
166	MALLOC(sc, ng_h4_info_p, sizeof(*sc), M_NETGRAPH_H4, M_NOWAIT|M_ZERO);
167	if (sc == NULL) {
168		error = ENOMEM;
169		goto out;
170	}
171
172	sc->tp = tp;
173	sc->debug = NG_H4_WARN_LEVEL;
174
175	sc->state = NG_H4_W4_PKT_IND;
176	sc->want = 1;
177	sc->got = 0;
178
179	NG_BT_MBUFQ_INIT(&sc->outq, NG_H4_DEFAULTQLEN);
180	callout_handle_init(&sc->timo);
181
182	/* Setup netgraph node */
183	error = ng_make_node_common(&typestruct, &sc->node);
184	if (error != 0) {
185		bzero(sc, sizeof(*sc));
186		FREE(sc, M_NETGRAPH_H4);
187		goto out;
188	}
189
190	/* Assign node its name */
191	snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
192
193	error = ng_name_node(sc->node, name);
194	if (error != 0) {
195		NG_H4_ALERT("%s: %s - node name exists?\n", __func__, name);
196		NG_NODE_UNREF(sc->node);
197		bzero(sc, sizeof(*sc));
198		FREE(sc, M_NETGRAPH_H4);
199		goto out;
200	}
201
202	/* Set back pointers */
203	NG_NODE_SET_PRIVATE(sc->node, sc);
204	tp->t_lsc = (caddr_t) sc;
205
206	/* The node has to be a WRITER because data can change node status */
207	NG_NODE_FORCE_WRITER(sc->node);
208
209	/*
210	 * Pre-allocate cblocks to the an appropriate amount.
211	 * I'm not sure what is appropriate.
212	 */
213
214	ttyflush(tp, FREAD | FWRITE);
215	clist_alloc_cblocks(&tp->t_canq, 0, 0);
216	clist_alloc_cblocks(&tp->t_rawq, 0, 0);
217	clist_alloc_cblocks(&tp->t_outq,
218		MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER);
219out:
220	splx(s); /* XXX */
221
222	return (error);
223} /* ng_h4_open */
224
225/*
226 * Line specific close routine, called from device close routine
227 * and from ttioctl. This causes the node to be destroyed as well.
228 */
229
230static int
231ng_h4_close(struct tty *tp, int flag)
232{
233	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
234	int		s;
235
236	s = spltty(); /* XXX */
237
238	ttyflush(tp, FREAD | FWRITE);
239	clist_free_cblocks(&tp->t_outq);
240	if (sc != NULL) {
241		tp->t_lsc = NULL;
242
243		if (sc->node != NULL) {
244			if (sc->flags & NG_H4_TIMEOUT)
245				ng_h4_untimeout(sc->node);
246
247			NG_NODE_SET_PRIVATE(sc->node, NULL);
248			ng_rmnode_self(sc->node);
249			sc->node = NULL;
250		}
251
252		NG_BT_MBUFQ_DESTROY(&sc->outq);
253		bzero(sc, sizeof(*sc));
254		FREE(sc, M_NETGRAPH_H4);
255	}
256
257	splx(s); /* XXX */
258
259	return (0);
260} /* ng_h4_close */
261
262/*
263 * Once the device has been turned into a node, we don't allow reading.
264 */
265
266static int
267ng_h4_read(struct tty *tp, struct uio *uio, int flag)
268{
269	return (EIO);
270} /* ng_h4_read */
271
272/*
273 * Once the device has been turned into a node, we don't allow writing.
274 */
275
276static int
277ng_h4_write(struct tty *tp, struct uio *uio, int flag)
278{
279	return (EIO);
280} /* ng_h4_write */
281
282/*
283 * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
284 */
285
286static int
287ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
288		struct thread *td)
289{
290	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
291	int		s, error = 0;
292
293	s = spltty(); /* XXX */
294
295	switch (cmd) {
296	case NGIOCGINFO:
297#undef	NI
298#define NI(x)	((struct nodeinfo *)(x))
299
300		bzero(data, sizeof(*NI(data)));
301
302		if (NG_NODE_HAS_NAME(sc->node))
303			strncpy(NI(data)->name, NG_NODE_NAME(sc->node),
304				sizeof(NI(data)->name) - 1);
305
306		strncpy(NI(data)->type, sc->node->nd_type->name,
307			sizeof(NI(data)->type) - 1);
308
309		NI(data)->id = (u_int32_t) ng_node2ID(sc->node);
310		NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node);
311		break;
312
313	default:
314		error = ENOIOCTL;
315		break;
316	}
317
318	splx(s); /* XXX */
319
320	return (error);
321} /* ng_h4_ioctl */
322
323/*
324 * Receive data coming from the device. We get one character at a time, which
325 * is kindof silly.
326 */
327
328static int
329ng_h4_input(int c, struct tty *tp)
330{
331	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
332
333	if (sc == NULL || tp != sc->tp ||
334	    sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
335		return (0);
336
337	/* Check for error conditions */
338	if ((sc->tp->t_state & TS_CONNECTED) == 0) {
339		NG_H4_INFO("%s: %s - no carrier\n", __func__,
340			NG_NODE_NAME(sc->node));
341
342		sc->state = NG_H4_W4_PKT_IND;
343		sc->want = 1;
344		sc->got = 0;
345
346		return (0); /* XXX Loss of synchronization here! */
347	}
348
349	/* Check for framing error or overrun on this char */
350	if (c & TTY_ERRORMASK) {
351		NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__,
352			NG_NODE_NAME(sc->node), c & TTY_ERRORMASK,
353			c & TTY_CHARMASK);
354
355		NG_H4_STAT_IERROR(sc->stat);
356
357		sc->state = NG_H4_W4_PKT_IND;
358		sc->want = 1;
359		sc->got = 0;
360
361		return (0); /* XXX Loss of synchronization here! */
362	}
363
364	NG_H4_STAT_BYTES_RECV(sc->stat, 1);
365
366	/* Append char to mbuf */
367	if (sc->got >= sizeof(sc->ibuf)) {
368		NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n",
369			__func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK,
370			sc->got);
371
372		NG_H4_STAT_IERROR(sc->stat);
373
374		sc->state = NG_H4_W4_PKT_IND;
375		sc->want = 1;
376		sc->got = 0;
377
378		return (0); /* XXX Loss of synchronization here! */
379	}
380
381	sc->ibuf[sc->got ++] = (c & TTY_CHARMASK);
382
383	NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__,
384		NG_NODE_NAME(sc->node), c, sc->want, sc->got);
385
386	if (sc->got < sc->want)
387		return (0); /* Wait for more */
388
389	switch (sc->state) {
390	/* Got packet indicator */
391	case NG_H4_W4_PKT_IND:
392		NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__,
393			NG_NODE_NAME(sc->node), sc->ibuf[0]);
394
395		sc->state = NG_H4_W4_PKT_HDR;
396
397		/*
398		 * Since packet indicator included in the packet header
399		 * just set sc->want to sizeof(packet header).
400		 */
401
402		switch (sc->ibuf[0]) {
403		case NG_HCI_ACL_DATA_PKT:
404			sc->want = sizeof(ng_hci_acldata_pkt_t);
405			break;
406
407		case NG_HCI_SCO_DATA_PKT:
408			sc->want = sizeof(ng_hci_scodata_pkt_t);
409			break;
410
411		case NG_HCI_EVENT_PKT:
412			sc->want = sizeof(ng_hci_event_pkt_t);
413			break;
414
415		default:
416			NG_H4_WARN("%s: %s - ignoring unknown packet " \
417				"type=%#x\n", __func__, NG_NODE_NAME(sc->node),
418				sc->ibuf[0]);
419
420			NG_H4_STAT_IERROR(sc->stat);
421
422			sc->state = NG_H4_W4_PKT_IND;
423			sc->want = 1;
424			sc->got = 0;
425			break;
426		}
427		break;
428
429	/* Got packet header */
430	case NG_H4_W4_PKT_HDR:
431		sc->state = NG_H4_W4_PKT_DATA;
432
433		switch (sc->ibuf[0]) {
434		case NG_HCI_ACL_DATA_PKT:
435			c = le16toh(((ng_hci_acldata_pkt_t *)
436				(sc->ibuf))->length);
437			break;
438
439		case NG_HCI_SCO_DATA_PKT:
440			c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length;
441			break;
442
443		case NG_HCI_EVENT_PKT:
444			c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length;
445			break;
446
447		default:
448			KASSERT((0), ("Invalid packet type=%#x\n",
449				sc->ibuf[0]));
450			break;
451		}
452
453		NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \
454			"packet size=%d, payload size=%d\n", __func__,
455			NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c);
456
457		if (c > 0) {
458			sc->want += c;
459
460			/*
461			 * Try to prevent possible buffer overrun
462			 *
463			 * XXX I'm *really* confused here. It turns out
464			 * that Xircom card sends us packets with length
465			 * greater then 512 bytes! This is greater then
466			 * our old receive buffer (ibuf) size. In the same
467			 * time the card demands from us *not* to send
468			 * packets greater then 192 bytes. Weird! How the
469			 * hell i should know how big *receive* buffer
470			 * should be? For now increase receiving buffer
471			 * size to 1K and add the following check.
472			 */
473
474			if (sc->want >= sizeof(sc->ibuf)) {
475				int	b;
476
477				NG_H4_ALERT("%s: %s - packet too big for " \
478					"buffer, type=%#x, got=%d, want=%d, " \
479					"length=%d\n", __func__,
480					NG_NODE_NAME(sc->node), sc->ibuf[0],
481					sc->got, sc->want, c);
482
483				NG_H4_ALERT("Packet header:\n");
484				for (b = 0; b < sc->got; b++)
485					NG_H4_ALERT("%#x ", sc->ibuf[b]);
486				NG_H4_ALERT("\n");
487
488				/* Reset state */
489				NG_H4_STAT_IERROR(sc->stat);
490
491				sc->state = NG_H4_W4_PKT_IND;
492				sc->want = 1;
493				sc->got = 0;
494			}
495
496			break;
497		}
498
499		/* else FALLTHROUGH and deliver frame */
500		/* XXX Is this true? Should we deliver empty frame? */
501
502	/* Got packet data */
503	case NG_H4_W4_PKT_DATA:
504		NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \
505			"packet size=%d\n", __func__,
506			NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got);
507
508		if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
509			struct mbuf	*m = NULL;
510
511			MGETHDR(m, M_DONTWAIT, MT_DATA);
512			if (m != NULL) {
513				m->m_pkthdr.len = 0;
514
515				/* XXX m_copyback() is stupid */
516				m->m_len = min(MHLEN, sc->got);
517
518				m_copyback(m, 0, sc->got, sc->ibuf);
519				NG_SEND_DATA_ONLY(c, sc->hook, m);
520			} else {
521				NG_H4_ERR("%s: %s - could not get mbuf\n",
522					__func__, NG_NODE_NAME(sc->node));
523
524				NG_H4_STAT_IERROR(sc->stat);
525			}
526		}
527
528		sc->state = NG_H4_W4_PKT_IND;
529		sc->want = 1;
530		sc->got = 0;
531
532		NG_H4_STAT_PCKTS_RECV(sc->stat);
533		break;
534
535	default:
536		KASSERT((0), ("Invalid H4 node state=%d", sc->state));
537		break;
538	}
539
540	return (0);
541} /* ng_h4_input */
542
543/*
544 * This is called when the device driver is ready for more output. Called from
545 * tty system.
546 */
547
548static int
549ng_h4_start(struct tty *tp)
550{
551	ng_h4_info_p	sc = (ng_h4_info_p) tp->t_lsc;
552
553	if (sc == NULL || tp != sc->tp ||
554	    sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
555		return (0);
556
557	return (ng_send_fn(sc->node, NULL, ng_h4_start2, NULL, 0));
558} /* ng_h4_start */
559
560/*
561 * Device driver is ready for more output. Part 2. Called (via ng_send_fn)
562 * ng_h4_start() and from ng_h4_rcvdata() when a new mbuf is available for
563 * output.
564 */
565
566static void
567ng_h4_start2(node_p node, hook_p hook, void *arg1, int arg2)
568{
569	ng_h4_info_p	 sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
570	struct mbuf	*m = NULL;
571	int		 s, size;
572
573	s = spltty(); /* XXX */
574
575#if 0
576	while (sc->tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */
577#else
578	while (1) {
579#endif
580		/* Remove first mbuf from queue */
581		NG_BT_MBUFQ_DEQUEUE(&sc->outq, m);
582		if (m == NULL)
583			break;
584
585		/* Send as much of it as possible */
586		while (m != NULL) {
587			size = m->m_len - b_to_q(mtod(m, u_char *),
588					m->m_len, &sc->tp->t_outq);
589
590			NG_H4_STAT_BYTES_SENT(sc->stat, size);
591
592			m->m_data += size;
593			m->m_len -= size;
594			if (m->m_len > 0)
595				break;	/* device can't take no more */
596
597			m = m_free(m);
598		}
599
600		/* Put remainder of mbuf chain (if any) back on queue */
601		if (m != NULL) {
602			NG_BT_MBUFQ_PREPEND(&sc->outq, m);
603			break;
604		}
605
606		/* Full packet has been sent */
607		NG_H4_STAT_PCKTS_SENT(sc->stat);
608	}
609
610	/*
611	 * Call output process whether or not there is any output. We are
612	 * being called in lieu of ttstart and must do what it would.
613	 */
614
615	if (sc->tp->t_oproc != NULL)
616		(*sc->tp->t_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_NODE_REF(node);
961	sc->timo = timeout(ng_h4_queue_timeout, node, 1);
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	untimeout(ng_h4_queue_timeout, node, sc->timo);
976	NG_NODE_UNREF(node);
977} /* ng_h4_untimeout */
978
979/*
980 * OK, timeout has happend, so queue function to process it
981 */
982
983static void
984ng_h4_queue_timeout(void *context)
985{
986	node_p	node = (node_p) context;
987
988	if (NG_NODE_IS_VALID(node))
989		ng_send_fn(node, NULL, &ng_h4_process_timeout, NULL, 0);
990
991	NG_NODE_UNREF(node);
992} /* ng_h4_queue_timeout */
993
994/*
995 * Timeout processing function.
996 * We still have data to output to the device, so try sending more.
997 */
998
999static void
1000ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1001{
1002	ng_h4_info_p	sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
1003
1004	sc->flags &= ~NG_H4_TIMEOUT;
1005
1006	/*
1007	 * We can call ng_h4_start2() directly here because we have lock
1008	 * on the node.
1009	 */
1010
1011	ng_h4_start2(node, NULL, NULL, 0);
1012} /* ng_h4_process_timeout */
1013
1014/*
1015 * Handle loading and unloading for this node type
1016 */
1017
1018static int
1019ng_h4_mod_event(module_t mod, int event, void *data)
1020{
1021	static int	ng_h4_ldisc;
1022	int		s, error = 0;
1023
1024	s = spltty(); /* XXX */
1025
1026	switch (event) {
1027	case MOD_LOAD:
1028		/* Register line discipline */
1029		ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc);
1030		if (ng_h4_ldisc < 0) {
1031			printf("%s: can't register H4 line discipline\n",
1032				__func__);
1033			error = EIO;
1034		}
1035		break;
1036
1037	case MOD_UNLOAD:
1038		/* Unregister line discipline */
1039		ldisc_deregister(ng_h4_ldisc);
1040		break;
1041
1042	default:
1043		error = EOPNOTSUPP;
1044		break;
1045	}
1046
1047	splx(s); /* XXX */
1048
1049	return (error);
1050} /* ng_h4_mod_event */
1051
1052