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