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