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