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