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