Vendor import of netgraph from FreeBSD-current 20080626 vendor/NETGRAPH
authorMatthew Dillon <dillon@dragonflybsd.org>
Thu, 26 Jun 2008 19:34:33 +0000 (19:34 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Thu, 26 Jun 2008 19:34:33 +0000 (19:34 +0000)
168 files changed:
sys/netgraph7/NOTES [new file with mode: 0644]
sys/netgraph7/atm/ccatm/ng_ccatm.c [new file with mode: 0644]
sys/netgraph7/atm/ccatm/ng_ccatm_cust.h [new file with mode: 0644]
sys/netgraph7/atm/ng_atm.c [new file with mode: 0644]
sys/netgraph7/atm/ng_atm.h [new file with mode: 0644]
sys/netgraph7/atm/ng_ccatm.h [new file with mode: 0644]
sys/netgraph7/atm/ng_sscfu.h [new file with mode: 0644]
sys/netgraph7/atm/ng_sscop.h [new file with mode: 0644]
sys/netgraph7/atm/ng_uni.h [new file with mode: 0644]
sys/netgraph7/atm/ngatmbase.c [new file with mode: 0644]
sys/netgraph7/atm/ngatmbase.h [new file with mode: 0644]
sys/netgraph7/atm/sscfu/ng_sscfu.c [new file with mode: 0644]
sys/netgraph7/atm/sscfu/ng_sscfu_cust.h [new file with mode: 0644]
sys/netgraph7/atm/sscop/ng_sscop.c [new file with mode: 0644]
sys/netgraph7/atm/sscop/ng_sscop_cust.h [new file with mode: 0644]
sys/netgraph7/atm/uni/ng_uni.c [new file with mode: 0644]
sys/netgraph7/atm/uni/ng_uni_cust.h [new file with mode: 0644]
sys/netgraph7/bluetooth/common/ng_bluetooth.c [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/h4/TODO [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/h4/ng_h4.c [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/h4/ng_h4_prse.h [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/h4/ng_h4_var.h [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubt/TODO [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubt/ng_ubt.c [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubt/ng_ubt_var.h [new file with mode: 0644]
sys/netgraph7/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/TODO [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_cmds.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_cmds.h [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_evnt.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_evnt.h [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_main.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_misc.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_misc.h [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_prse.h [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_ulpi.c [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_ulpi.h [new file with mode: 0644]
sys/netgraph7/bluetooth/hci/ng_hci_var.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_bluetooth.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_bt3c.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_btsocket.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_btsocket_hci_raw.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_btsocket_l2cap.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_btsocket_rfcomm.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_h4.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_hci.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_l2cap.h [new file with mode: 0644]
sys/netgraph7/bluetooth/include/ng_ubt.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/TODO [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_cmds.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_cmds.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_evnt.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_evnt.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_llpi.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_llpi.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_main.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_misc.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_misc.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_prse.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.c [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.h [new file with mode: 0644]
sys/netgraph7/bluetooth/l2cap/ng_l2cap_var.h [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/TODO [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/ng_btsocket.c [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/ng_btsocket_hci_raw.c [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap_raw.c [new file with mode: 0644]
sys/netgraph7/bluetooth/socket/ng_btsocket_rfcomm.c [new file with mode: 0644]
sys/netgraph7/netflow/netflow.c [new file with mode: 0644]
sys/netgraph7/netflow/netflow.h [new file with mode: 0644]
sys/netgraph7/netflow/ng_netflow.c [new file with mode: 0644]
sys/netgraph7/netflow/ng_netflow.h [new file with mode: 0644]
sys/netgraph7/netgraph.h [new file with mode: 0644]
sys/netgraph7/ng_UI.c [new file with mode: 0644]
sys/netgraph7/ng_UI.h [new file with mode: 0644]
sys/netgraph7/ng_async.c [new file with mode: 0644]
sys/netgraph7/ng_async.h [new file with mode: 0644]
sys/netgraph7/ng_atmllc.c [new file with mode: 0644]
sys/netgraph7/ng_atmllc.h [new file with mode: 0644]
sys/netgraph7/ng_base.c [new file with mode: 0644]
sys/netgraph7/ng_bpf.c [new file with mode: 0644]
sys/netgraph7/ng_bpf.h [new file with mode: 0644]
sys/netgraph7/ng_bridge.c [new file with mode: 0644]
sys/netgraph7/ng_bridge.h [new file with mode: 0644]
sys/netgraph7/ng_car.c [new file with mode: 0644]
sys/netgraph7/ng_car.h [new file with mode: 0644]
sys/netgraph7/ng_cisco.c [new file with mode: 0644]
sys/netgraph7/ng_cisco.h [new file with mode: 0644]
sys/netgraph7/ng_deflate.c [new file with mode: 0644]
sys/netgraph7/ng_deflate.h [new file with mode: 0644]
sys/netgraph7/ng_device.c [new file with mode: 0644]
sys/netgraph7/ng_device.h [new file with mode: 0644]
sys/netgraph7/ng_echo.c [new file with mode: 0644]
sys/netgraph7/ng_echo.h [new file with mode: 0644]
sys/netgraph7/ng_eiface.c [new file with mode: 0644]
sys/netgraph7/ng_eiface.h [new file with mode: 0644]
sys/netgraph7/ng_etf.c [new file with mode: 0644]
sys/netgraph7/ng_etf.h [new file with mode: 0644]
sys/netgraph7/ng_ether.c [new file with mode: 0644]
sys/netgraph7/ng_ether.h [new file with mode: 0644]
sys/netgraph7/ng_fec.c [new file with mode: 0644]
sys/netgraph7/ng_fec.h [new file with mode: 0644]
sys/netgraph7/ng_frame_relay.c [new file with mode: 0644]
sys/netgraph7/ng_frame_relay.h [new file with mode: 0644]
sys/netgraph7/ng_gif.c [new file with mode: 0644]
sys/netgraph7/ng_gif.h [new file with mode: 0644]
sys/netgraph7/ng_gif_demux.c [new file with mode: 0644]
sys/netgraph7/ng_gif_demux.h [new file with mode: 0644]
sys/netgraph7/ng_hole.c [new file with mode: 0644]
sys/netgraph7/ng_hole.h [new file with mode: 0644]
sys/netgraph7/ng_hub.c [new file with mode: 0644]
sys/netgraph7/ng_hub.h [new file with mode: 0644]
sys/netgraph7/ng_iface.c [new file with mode: 0644]
sys/netgraph7/ng_iface.h [new file with mode: 0644]
sys/netgraph7/ng_ip_input.c [new file with mode: 0644]
sys/netgraph7/ng_ip_input.h [new file with mode: 0644]
sys/netgraph7/ng_ipfw.c [new file with mode: 0644]
sys/netgraph7/ng_ipfw.h [new file with mode: 0644]
sys/netgraph7/ng_ksocket.c [new file with mode: 0644]
sys/netgraph7/ng_ksocket.h [new file with mode: 0644]
sys/netgraph7/ng_l2tp.c [new file with mode: 0644]
sys/netgraph7/ng_l2tp.h [new file with mode: 0644]
sys/netgraph7/ng_lmi.c [new file with mode: 0644]
sys/netgraph7/ng_lmi.h [new file with mode: 0644]
sys/netgraph7/ng_message.h [new file with mode: 0644]
sys/netgraph7/ng_mppc.c [new file with mode: 0644]
sys/netgraph7/ng_mppc.h [new file with mode: 0644]
sys/netgraph7/ng_nat.c [new file with mode: 0644]
sys/netgraph7/ng_nat.h [new file with mode: 0644]
sys/netgraph7/ng_one2many.c [new file with mode: 0644]
sys/netgraph7/ng_one2many.h [new file with mode: 0644]
sys/netgraph7/ng_parse.c [new file with mode: 0644]
sys/netgraph7/ng_parse.h [new file with mode: 0644]
sys/netgraph7/ng_ppp.c [new file with mode: 0644]
sys/netgraph7/ng_ppp.h [new file with mode: 0644]
sys/netgraph7/ng_pppoe.c [new file with mode: 0644]
sys/netgraph7/ng_pppoe.h [new file with mode: 0644]
sys/netgraph7/ng_pptpgre.c [new file with mode: 0644]
sys/netgraph7/ng_pptpgre.h [new file with mode: 0644]
sys/netgraph7/ng_pred1.c [new file with mode: 0644]
sys/netgraph7/ng_pred1.h [new file with mode: 0644]
sys/netgraph7/ng_rfc1490.c [new file with mode: 0644]
sys/netgraph7/ng_rfc1490.h [new file with mode: 0644]
sys/netgraph7/ng_sample.c [new file with mode: 0644]
sys/netgraph7/ng_sample.h [new file with mode: 0644]
sys/netgraph7/ng_socket.c [new file with mode: 0644]
sys/netgraph7/ng_socket.h [new file with mode: 0644]
sys/netgraph7/ng_socketvar.h [new file with mode: 0644]
sys/netgraph7/ng_source.c [new file with mode: 0644]
sys/netgraph7/ng_source.h [new file with mode: 0644]
sys/netgraph7/ng_split.c [new file with mode: 0644]
sys/netgraph7/ng_split.h [new file with mode: 0644]
sys/netgraph7/ng_sppp.c [new file with mode: 0644]
sys/netgraph7/ng_sppp.h [new file with mode: 0644]
sys/netgraph7/ng_tag.c [new file with mode: 0644]
sys/netgraph7/ng_tag.h [new file with mode: 0644]
sys/netgraph7/ng_tcpmss.c [new file with mode: 0644]
sys/netgraph7/ng_tcpmss.h [new file with mode: 0644]
sys/netgraph7/ng_tee.c [new file with mode: 0644]
sys/netgraph7/ng_tee.h [new file with mode: 0644]
sys/netgraph7/ng_tty.c [new file with mode: 0644]
sys/netgraph7/ng_tty.h [new file with mode: 0644]
sys/netgraph7/ng_vjc.c [new file with mode: 0644]
sys/netgraph7/ng_vjc.h [new file with mode: 0644]
sys/netgraph7/ng_vlan.c [new file with mode: 0644]
sys/netgraph7/ng_vlan.h [new file with mode: 0644]

diff --git a/sys/netgraph7/NOTES b/sys/netgraph7/NOTES
new file mode 100644 (file)
index 0000000..2050cc4
--- /dev/null
@@ -0,0 +1,103 @@
+$FreeBSD: src/sys/netgraph/NOTES,v 1.2 2001/01/06 00:46:46 julian Exp $
+Development ideas..
+
+Archie's suggestions... :-)
+
+ - There should be a new malloc type: M_NETGRAPH
+       [DONE]
+       - all mallocs/frees now changed to use this.. JRE
+       - might further split them out some time.
+
+ - Use MALLOC and FREE macros instead of direct function calls
+       [DONE]
+       - They allow conditional compilation which keeps
+         statistics & counters on various memory allocation
+         (or so it seems)
+          - Now tend to have NG_FREE_XX() macros. they
+            allow better debugging
+
+ - In struct ng_mesg: at least make "header" into "hdr", if not
+   getting rid of it altogether. It doesn't seem necessary and
+   makes all my C code lines too long.
+
+       - I understand.. one thought however.. consider..
+         if char data[0] were not legal,  so that data[1] needed to be
+         used instead, then the only way to get the size of the header
+         would be sizeof(msg.header) as sizeof(msg) would include the dummy
+         following bytes. this is a portability issue and I hope
+         it will be ported eventually :)
+
+       - Baloney! you can use sizeof(msg) - 1 then.. or just
+         make it a macro, then its always portable:
+
+         #ifdef __GNU_C__
+           #define NG_MSG_HDR_SIZE     (sizeof(struct ng_message))
+         #else
+           #define NG_MSG_HDR_SIZE     (sizeof(struct ng_message) - 1)
+         #endif
+
+         - inertia rules :-b
+
+
+ - Have a user level program to print out and manipulate nodes, etc.
+       - [DONE]
+               see ngctl, nghook
+
+ - "Netgraph global" flags to turn on tracing, etc.
+
+ - ngctl needs to be rewritten using libnetgraph. Also it needs a
+   command to list all existing nodes (in case you don't know the
+   name of what you're looking for).
+       [DONE]
+
+ - Need a way to get a list of ALL nodes.
+       [DONE]
+       - see NGM_LISTNODES
+
+ - Enhance "netstat" to display all netgraph nodes -- or at least
+   all netgraph socket nodes.
+       [DONE]
+
+ - BUG FIX: bind() on a socket should neither require nor allow a
+   colon character at the end of the name. Note ngctl allows you
+   to do it either way!
+       [DONE] (I think)
+       - bind on a control socket has been disabled
+         it was a bad idea.
+
+ - Need to implement passing meta information through socket nodes
+   using sendmsg() and recvmsg().
+
+ - Stuff needing to be added to manual:
+
+   - Awareness of SPL level, use ng_queue*() functions when necessary.
+   - Malloc all memory with type M_NETGRAPH. -DONE
+   - Write code so it can be an LKM or built into the kernel.. this means
+     be careful with things like #ifdef INET.
+   - All nodes assume that all data mbufs have the M_PKTHDR flag set!
+     The ng_send_data() and related functions should have an
+     #ifdef DIAGNOSTICS check to check this assumption for every mbuf.
+     -DONE with INVARIANTS. Framework should test this more.
+   - More generally, netgraph code should make liberal use of the
+     #ifdef DIAGNOSTICS definition.
+     -INVARIANTS.
+   - Since data and messages are sent functionally, programmers need
+     to watch out for infinite feedback loops. Should ng_base.c detect
+     this automatically?
+      - I've been thinking about this. each node could have a 'colour'
+       which is set to the colour of the packet as you pass through.
+       hitting a node already of your colour would abort. Each packet
+       has another (incremented) colour.
+       -new 'item' type can hold a hopcount...
+
+NEW in 2001
+All piggyback responses have gone away.
+use the node ID in the return address field for quick response delivery.
+
+Every node has a queue, plus there is a list of nodes that have queued work.
+Extensive use of Mutexes. Getting in shape for SMP.
+
+Messages and data are deliverd in a new form. An Item now has
+all information needed to queue such a request and deliver it later, so 
+it is now the basis of all data transfer since any transfer may need to
+be queued.
diff --git a/sys/netgraph7/atm/ccatm/ng_ccatm.c b/sys/netgraph7/atm/ccatm/ng_ccatm.c
new file mode 100644 (file)
index 0000000..404f54e
--- /dev/null
@@ -0,0 +1,1201 @@
+/*-
+ * Copyright (c) 2001-2002
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ * Copyright (c) 2003-2004
+ *     Hartmut Brandt
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ccatm/ng_ccatm.c,v 1.3 2006/09/30 12:37:43 netchild Exp $
+ *
+ * ATM call control and API
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/ccatm/ng_ccatm.c,v 1.3 2006/09/30 12:37:43 netchild Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sbuf.h>
+#include <machine/stdarg.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netnatm/unimsg.h>
+#include <netnatm/msg/unistruct.h>
+#include <netnatm/api/unisap.h>
+#include <netnatm/sig/unidef.h>
+#include <netgraph/atm/ngatmbase.h>
+#include <netgraph/atm/ng_uni.h>
+#include <netnatm/api/atmapi.h>
+#include <netgraph/atm/ng_ccatm.h>
+#include <netnatm/api/ccatm.h>
+
+MODULE_DEPEND(ng_ccatm, ngatmbase, 1, 1, 1);
+
+MALLOC_DEFINE(M_NG_CCATM, "ng_ccatm", "netgraph uni api node");
+
+/*
+ * Command structure parsing
+ */
+
+/* ESI */
+static const struct ng_parse_fixedarray_info ng_ccatm_esi_type_info =
+    NGM_CCATM_ESI_INFO;
+static const struct ng_parse_type ng_ccatm_esi_type = {
+       &ng_parse_fixedarray_type,
+       &ng_ccatm_esi_type_info
+};
+
+/* PORT PARAMETERS */
+static const struct ng_parse_struct_field ng_ccatm_atm_port_type_info[] =
+    NGM_CCATM_ATM_PORT_INFO;
+static const struct ng_parse_type ng_ccatm_atm_port_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_atm_port_type_info
+};
+
+/* PORT structure */
+static const struct ng_parse_struct_field ng_ccatm_port_type_info[] =
+    NGM_CCATM_PORT_INFO;
+static const struct ng_parse_type ng_ccatm_port_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_port_type_info
+};
+
+/* the ADDRESS array itself */
+static const struct ng_parse_fixedarray_info ng_ccatm_addr_array_type_info =
+    NGM_CCATM_ADDR_ARRAY_INFO;
+static const struct ng_parse_type ng_ccatm_addr_array_type = {
+       &ng_parse_fixedarray_type,
+       &ng_ccatm_addr_array_type_info
+};
+
+/* one ADDRESS */
+static const struct ng_parse_struct_field ng_ccatm_uni_addr_type_info[] =
+    NGM_CCATM_UNI_ADDR_INFO;
+static const struct ng_parse_type ng_ccatm_uni_addr_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_uni_addr_type_info
+};
+
+/* ADDRESS request */
+static const struct ng_parse_struct_field ng_ccatm_addr_req_type_info[] =
+    NGM_CCATM_ADDR_REQ_INFO;
+static const struct ng_parse_type ng_ccatm_addr_req_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_addr_req_type_info
+};
+
+/* ADDRESS var-array */
+static int
+ng_ccatm_addr_req_array_getlen(const struct ng_parse_type *type,
+    const u_char *start, const u_char *buf)
+{
+       const struct ngm_ccatm_get_addresses *p;
+
+       p = (const struct ngm_ccatm_get_addresses *)
+           (buf - offsetof(struct ngm_ccatm_get_addresses, addr));
+       return (p->count);
+}
+static const struct ng_parse_array_info ng_ccatm_addr_req_array_type_info =
+    NGM_CCATM_ADDR_REQ_ARRAY_INFO;
+static const struct ng_parse_type ng_ccatm_addr_req_array_type = {
+       &ng_parse_array_type,
+       &ng_ccatm_addr_req_array_type_info
+};
+
+/* Outer get_ADDRESSes structure */
+static const struct ng_parse_struct_field ng_ccatm_get_addresses_type_info[] =
+    NGM_CCATM_GET_ADDRESSES_INFO;
+static const struct ng_parse_type ng_ccatm_get_addresses_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_get_addresses_type_info
+};
+
+/* Port array */
+static int
+ng_ccatm_port_array_getlen(const struct ng_parse_type *type,
+    const u_char *start, const u_char *buf)
+{
+       const struct ngm_ccatm_portlist *p;
+
+       p = (const struct ngm_ccatm_portlist *)
+           (buf - offsetof(struct ngm_ccatm_portlist, ports));
+       return (p->nports);
+}
+static const struct ng_parse_array_info ng_ccatm_port_array_type_info =
+    NGM_CCATM_PORT_ARRAY_INFO;
+static const struct ng_parse_type ng_ccatm_port_array_type = {
+       &ng_parse_array_type,
+       &ng_ccatm_port_array_type_info
+};
+
+/* Portlist structure */
+static const struct ng_parse_struct_field ng_ccatm_portlist_type_info[] =
+    NGM_CCATM_PORTLIST_INFO;
+static const struct ng_parse_type ng_ccatm_portlist_type = {
+       &ng_parse_struct_type,
+       ng_ccatm_portlist_type_info
+};
+
+/*
+ * Command list
+ */
+static const struct ng_cmdlist ng_ccatm_cmdlist[] = {
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_DUMP,
+         "dump",
+         NULL,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_STOP,
+         "stop",
+         &ng_ccatm_port_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_START,
+         "start",
+         &ng_ccatm_port_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_GETSTATE,
+         "getstate",
+         &ng_ccatm_port_type,
+         &ng_parse_uint32_type
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_GET_ADDRESSES,
+         "get_addresses",
+         &ng_ccatm_port_type,
+         &ng_ccatm_get_addresses_type
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_CLEAR,
+         "clear",
+         &ng_ccatm_port_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_ADDRESS_REGISTERED,
+         "address_reg",
+         &ng_ccatm_addr_req_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_ADDRESS_UNREGISTERED,
+         "address_unreg",
+         &ng_ccatm_addr_req_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_SET_PORT_PARAM,
+         "set_port_param",
+         &ng_ccatm_atm_port_type,
+         NULL
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_GET_PORT_PARAM,
+         "get_port_param",
+         &ng_ccatm_port_type,
+         &ng_ccatm_atm_port_type,
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_GET_PORTLIST,
+         "get_portlist",
+         NULL,
+         &ng_ccatm_portlist_type,
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_SETLOG,
+         "setlog",
+         &ng_parse_hint32_type,
+         &ng_parse_hint32_type,
+       },
+       {
+         NGM_CCATM_COOKIE,
+         NGM_CCATM_RESET,
+         "reset",
+         NULL,
+         NULL,
+       },
+       { 0 }
+};
+
+/*
+ * Module data
+ */
+static ng_constructor_t                ng_ccatm_constructor;
+static ng_rcvmsg_t             ng_ccatm_rcvmsg;
+static ng_shutdown_t           ng_ccatm_shutdown;
+static ng_newhook_t            ng_ccatm_newhook;
+static ng_rcvdata_t            ng_ccatm_rcvdata;
+static ng_disconnect_t         ng_ccatm_disconnect;
+static int ng_ccatm_mod_event(module_t, int, void *);
+
+static struct ng_type ng_ccatm_typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_CCATM_NODE_TYPE,
+       .mod_event =    ng_ccatm_mod_event,
+       .constructor =  ng_ccatm_constructor,   /* Node constructor */
+       .rcvmsg =       ng_ccatm_rcvmsg,        /* Control messages */
+       .shutdown =     ng_ccatm_shutdown,      /* Node destructor */
+       .newhook =      ng_ccatm_newhook,       /* Arrival of new hook */
+       .rcvdata =      ng_ccatm_rcvdata,       /* receive data */
+       .disconnect =   ng_ccatm_disconnect,    /* disconnect a hook */
+       .cmdlist =      ng_ccatm_cmdlist,
+};
+NETGRAPH_INIT(ccatm, &ng_ccatm_typestruct);
+
+static ng_rcvdata_t    ng_ccatm_rcvuni;
+static ng_rcvdata_t    ng_ccatm_rcvdump;
+static ng_rcvdata_t    ng_ccatm_rcvmanage;
+
+/*
+ * Private node data.
+ */
+struct ccnode {
+       node_p  node;           /* the owning node */
+       hook_p  dump;           /* dump hook */
+       hook_p  manage;         /* hook to ILMI */
+
+       struct ccdata *data;
+       struct mbuf *dump_first;
+       struct mbuf *dump_last; /* first and last mbuf when dumping */
+
+       u_int   hook_cnt;       /* count user and port hooks */
+};
+
+/*
+ * Private UNI hook data
+ */
+struct cchook {
+       int             is_uni; /* true if uni hook, user otherwise */
+       struct ccnode   *node;  /* the owning node */
+       hook_p          hook;
+       void            *inst;  /* port or user */
+};
+
+static void ng_ccatm_send_user(struct ccuser *, void *, u_int, void *, size_t);
+static void ng_ccatm_respond_user(struct ccuser *, void *, int, u_int,
+    void *, size_t);
+static void ng_ccatm_send_uni(struct ccconn *, void *, u_int, u_int,
+    struct uni_msg *);
+static void ng_ccatm_send_uni_glob(struct ccport *, void *, u_int, u_int,
+    struct uni_msg *);
+static void ng_ccatm_log(const char *, ...) __printflike(1, 2);
+
+static const struct cc_funcs cc_funcs = {
+       .send_user =            ng_ccatm_send_user,
+       .respond_user =         ng_ccatm_respond_user,
+       .send_uni =             ng_ccatm_send_uni,
+       .send_uni_glob =        ng_ccatm_send_uni_glob,
+       .log =                  ng_ccatm_log,
+};
+
+/************************************************************
+ *
+ * Create a new node
+ */
+static int
+ng_ccatm_constructor(node_p node)
+{
+       struct ccnode *priv;
+
+       priv = malloc(sizeof(*priv), M_NG_CCATM, M_NOWAIT | M_ZERO);
+       if (priv == NULL)
+               return (ENOMEM);
+
+       priv->node = node;
+       priv->data = cc_create(&cc_funcs);
+       if (priv->data == NULL) {
+               free(priv, M_NG_CCATM);
+               return (ENOMEM);
+       }
+
+       NG_NODE_SET_PRIVATE(node, priv);
+
+       return (0);
+}
+
+/*
+ * Destroy a node. The user list is empty here, because all hooks are
+ * previously disconnected. The connection lists may not be empty, because
+ * connections may be waiting for responses from the stack. This also means,
+ * that no orphaned connections will be made by the port_destroy routine.
+ */
+static int
+ng_ccatm_shutdown(node_p node)
+{
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+
+       cc_destroy(priv->data);
+
+       free(priv, M_NG_CCATM);
+       NG_NODE_SET_PRIVATE(node, NULL);
+
+       NG_NODE_UNREF(node);
+
+       return (0);
+}
+
+/*
+ * Retrieve the registered addresses for one port or all ports.
+ * Returns an error code or 0 on success.
+ */
+static int
+ng_ccatm_get_addresses(node_p node, uint32_t portno, struct ng_mesg *msg,
+    struct ng_mesg **resp)
+{
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+       struct uni_addr *addrs;
+       u_int *ports;
+       struct ngm_ccatm_get_addresses *list;
+       u_int count, i;
+       size_t len;
+       int err;
+
+       err = cc_get_addrs(priv->data, portno, &addrs, &ports, &count);
+       if (err != 0)
+               return (err);
+
+       len = sizeof(*list) + count * sizeof(list->addr[0]);
+       NG_MKRESPONSE(*resp, msg, len, M_NOWAIT);
+       if (*resp == NULL) {
+               free(addrs, M_NG_CCATM);
+               free(ports, M_NG_CCATM);
+               return (ENOMEM);
+       }
+       list = (struct ngm_ccatm_get_addresses *)(*resp)->data;
+
+       list->count = count;
+       for (i = 0; i < count; i++) {
+               list->addr[i].port = ports[i];
+               list->addr[i].addr = addrs[i];
+       }
+
+       free(addrs, M_NG_CCATM);
+       free(ports, M_NG_CCATM);
+
+       return (0);
+}
+
+/*
+ * Dumper function. Pack the data into an mbuf chain.
+ */
+static int
+send_dump(struct ccdata *data, void *uarg, const char *buf)
+{
+       struct mbuf *m;
+       struct ccnode *priv = uarg;
+
+       if (priv->dump == NULL) {
+               m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (m == NULL)
+                       return (ENOBUFS);
+               priv->dump_first = priv->dump_last = m;
+               m->m_pkthdr.len = 0;
+       } else {
+               m = m_getcl(M_DONTWAIT, MT_DATA, 0);
+               if (m == 0) {
+                       m_freem(priv->dump_first);
+                       return (ENOBUFS);
+               }
+               priv->dump_last->m_next = m;
+               priv->dump_last = m;
+       }
+
+       strcpy(m->m_data, buf);
+       priv->dump_first->m_pkthdr.len += (m->m_len = strlen(buf));
+
+       return (0);
+}
+
+/*
+ * Dump current status to dump hook
+ */
+static int
+ng_ccatm_dump(node_p node)
+{
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       int error;
+
+       priv->dump_first = priv->dump_last = NULL;
+       error = cc_dump(priv->data, MCLBYTES, send_dump, priv);
+       if (error != 0)
+               return (error);
+
+       if ((m = priv->dump_first) != NULL) {
+               priv->dump_first = priv->dump_last = NULL;
+               NG_SEND_DATA_ONLY(error, priv->dump, m);
+               return (error);
+       }
+       return (0);
+}
+
+/*
+ * Control message
+ */
+static int
+ng_ccatm_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       struct ng_mesg *resp = NULL;
+       struct ng_mesg *msg;
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+       int error = 0;
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+
+         case NGM_CCATM_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_CCATM_DUMP:
+                       if (priv->dump)
+                               error = ng_ccatm_dump(node);
+                       else
+                               error = ENOTCONN;
+                       break;
+
+                 case NGM_CCATM_STOP:
+                   {
+                       struct ngm_ccatm_port *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       error = cc_port_stop(priv->data, arg->port);
+                       break;
+                   }
+
+                 case NGM_CCATM_START:
+                   {
+                       struct ngm_ccatm_port *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       error = cc_port_start(priv->data, arg->port);
+                       break;
+                   }
+
+                 case NGM_CCATM_GETSTATE:
+                   {
+                       struct ngm_ccatm_port *arg;
+                       int state;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       error = cc_port_isrunning(priv->data, arg->port,
+                           &state);
+                       if (error == 0) {
+                               NG_MKRESPONSE(resp, msg, sizeof(uint32_t),
+                                   M_NOWAIT);
+                               if (resp == NULL) {
+                                       error = ENOMEM;
+                                       break;
+                               }
+                               *(uint32_t *)resp->data = state;
+                       }
+                       break;
+                   }
+
+                 case NGM_CCATM_GET_ADDRESSES:
+                  {
+                       struct ngm_ccatm_port *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       error = ng_ccatm_get_addresses(node, arg->port, msg,
+                           &resp);
+                       break;
+                   }
+
+                 case NGM_CCATM_CLEAR:
+                   {
+                       struct ngm_ccatm_port *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       error = cc_port_clear(priv->data, arg->port);
+                       break;
+                   }
+
+                 case NGM_CCATM_ADDRESS_REGISTERED:
+                   {
+                       struct ngm_ccatm_addr_req *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_addr_req *)msg->data;
+                       error = cc_addr_register(priv->data, arg->port,
+                           &arg->addr);
+                       break;
+                   }
+
+                 case NGM_CCATM_ADDRESS_UNREGISTERED:
+                   {
+                       struct ngm_ccatm_addr_req *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_addr_req *)msg->data;
+                       error = cc_addr_unregister(priv->data, arg->port,
+                           &arg->addr);
+                       break;
+                   }
+
+                 case NGM_CCATM_GET_PORT_PARAM:
+                   {
+                       struct ngm_ccatm_port *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_port *)msg->data;
+                       NG_MKRESPONSE(resp, msg, sizeof(struct atm_port_info),
+                           M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       error = cc_port_get_param(priv->data, arg->port,
+                           (struct atm_port_info *)resp->data);
+                       if (error != 0) {
+                               free(resp, M_NETGRAPH_MSG);
+                               resp = NULL;
+                       }
+                       break;
+                   }
+
+                 case NGM_CCATM_SET_PORT_PARAM:
+                   {
+                       struct atm_port_info *arg;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct atm_port_info *)msg->data;
+                       error = cc_port_set_param(priv->data, arg);
+                       break;
+                   }
+
+                 case NGM_CCATM_GET_PORTLIST:
+                   {
+                       struct ngm_ccatm_portlist *arg;
+                       u_int n, *ports;
+
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       error = cc_port_getlist(priv->data, &n, &ports);
+                       if (error != 0)
+                               break;
+
+                       NG_MKRESPONSE(resp, msg, sizeof(*arg) +
+                           n * sizeof(arg->ports[0]), M_NOWAIT);
+                       if (resp == NULL) {
+                               free(ports, M_NG_CCATM);
+                               error = ENOMEM;
+                               break;
+                       }
+                       arg = (struct ngm_ccatm_portlist *)resp->data;
+
+                       arg->nports = 0;
+                       for (arg->nports = 0; arg->nports < n; arg->nports++)
+                               arg->ports[arg->nports] = ports[arg->nports];
+                       free(ports, M_NG_CCATM);
+                       break;
+                   }
+
+                 case NGM_CCATM_SETLOG:
+                   {
+                       uint32_t log_level;
+
+                       log_level = cc_get_log(priv->data);
+                       if (msg->header.arglen != 0) {
+                               if (msg->header.arglen != sizeof(log_level)) {
+                                       error = EINVAL;
+                                       break;
+                               }
+                               cc_set_log(priv->data, *(uint32_t *)msg->data);
+                       }
+
+                       NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               if (msg->header.arglen != 0)
+                                       cc_set_log(priv->data, log_level);
+                               break;
+                       }
+                       *(uint32_t *)resp->data = log_level;
+                       break;
+                   }
+
+                 case NGM_CCATM_RESET:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+
+                       if (priv->hook_cnt != 0) {
+                               error = EBUSY;
+                               break;
+                       }
+                       cc_reset(priv->data);
+                       break;
+
+                 case NGM_CCATM_GET_EXSTAT:
+                   {
+                       struct atm_exstatus s;
+                       struct atm_exstatus_ep *eps;
+                       struct atm_exstatus_port *ports;
+                       struct atm_exstatus_conn *conns;
+                       struct atm_exstatus_party *parties;
+                       size_t offs;
+
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       error = cc_get_extended_status(priv->data,
+                           &s, &eps, &ports, &conns, &parties);
+                       if (error != 0)
+                               break;
+
+                       offs = sizeof(s) + s.neps * sizeof(*eps) +
+                           s.nports * sizeof(*ports) +
+                           s.nconns * sizeof(*conns) +
+                           s.nparties * sizeof(*parties);
+
+                       NG_MKRESPONSE(resp, msg, offs, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+
+                       memcpy(resp->data, &s, sizeof(s));
+                       offs = sizeof(s);
+
+                       memcpy(resp->data + offs, eps,
+                           sizeof(*eps) * s.neps);
+                       offs += sizeof(*eps) * s.neps;
+
+                       memcpy(resp->data + offs, ports,
+                           sizeof(*ports) * s.nports);
+                       offs += sizeof(*ports) * s.nports;
+
+                       memcpy(resp->data + offs, conns,
+                           sizeof(*conns) * s.nconns);
+                       offs += sizeof(*conns) * s.nconns;
+
+                       memcpy(resp->data + offs, parties,
+                           sizeof(*parties) * s.nparties);
+                       offs += sizeof(*parties) * s.nparties;
+
+                       free(eps, M_NG_CCATM);
+                       free(ports, M_NG_CCATM);
+                       free(conns, M_NG_CCATM);
+                       free(parties, M_NG_CCATM);
+                       break;
+                   }
+
+                 default:
+                       error = EINVAL;
+                       break;
+
+               }
+               break;
+
+         default:
+               error = EINVAL;
+               break;
+
+       }
+
+       NG_RESPOND_MSG(error, node, item, resp);
+       NG_FREE_MSG(msg);
+       return (error);
+}
+
+/************************************************************
+ *
+ * New hook arrival
+ */
+static int
+ng_ccatm_newhook(node_p node, hook_p hook, const char *name)
+{
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+       struct ccport *port;
+       struct ccuser *user;
+       struct cchook *hd;
+       u_long lport;
+       char *end;
+
+       if (strncmp(name, "uni", 3) == 0) {
+               /*
+                * This is a UNI hook. Should be a new port.
+                */
+               if (name[3] == '\0')
+                       return (EINVAL);
+               lport = strtoul(name + 3, &end, 10);
+               if (*end != '\0' || lport == 0 || lport > 0xffffffff)
+                       return (EINVAL);
+
+               hd = malloc(sizeof(*hd), M_NG_CCATM, M_NOWAIT);
+               if (hd == NULL)
+                       return (ENOMEM);
+               hd->is_uni = 1;
+               hd->node = priv;
+               hd->hook = hook;
+
+               port = cc_port_create(priv->data, hd, (u_int)lport);
+               if (port == NULL) {
+                       free(hd, M_NG_CCATM);
+                       return (ENOMEM);
+               }
+               hd->inst = port;
+
+               NG_HOOK_SET_PRIVATE(hook, hd);
+               NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvuni);
+               NG_HOOK_FORCE_QUEUE(hook);
+
+               priv->hook_cnt++;
+
+               return (0);
+       }
+
+       if (strcmp(name, "dump") == 0) {
+               priv->dump = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvdump);
+               return (0);
+       }
+
+       if (strcmp(name, "manage") == 0) {
+               priv->manage = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvmanage);
+               return (0);
+       }
+
+       /*
+        * User hook
+        */
+       hd = malloc(sizeof(*hd), M_NG_CCATM, M_NOWAIT);
+       if (hd == NULL)
+               return (ENOMEM);
+       hd->is_uni = 0;
+       hd->node = priv;
+       hd->hook = hook;
+
+       user = cc_user_create(priv->data, hd, NG_HOOK_NAME(hook));
+       if (user == NULL) {
+               free(hd, M_NG_CCATM);
+               return (ENOMEM);
+       }
+
+       hd->inst = user;
+       NG_HOOK_SET_PRIVATE(hook, hd);
+       NG_HOOK_FORCE_QUEUE(hook);
+
+       priv->hook_cnt++;
+
+       return (0);
+}
+
+/*
+ * Disconnect a hook
+ */
+static int
+ng_ccatm_disconnect(hook_p hook)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct ccnode *priv = NG_NODE_PRIVATE(node);
+       struct cchook *hd = NG_HOOK_PRIVATE(hook);
+       struct ccdata *cc;
+
+       if (hook == priv->dump) {
+               priv->dump = NULL;
+
+       } else if (hook == priv->manage) {
+               priv->manage = NULL;
+               cc_unmanage(priv->data);
+
+       } else {
+               if (hd->is_uni)
+                       cc_port_destroy(hd->inst, 0);
+               else
+                       cc_user_destroy(hd->inst);
+
+               cc = hd->node->data;
+
+               free(hd, M_NG_CCATM);
+               NG_HOOK_SET_PRIVATE(hook, NULL);
+
+               priv->hook_cnt--;
+
+               cc_work(cc);
+       }
+
+       /*
+        * When the number of hooks drops to zero, delete the node.
+        */
+       if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node))
+               ng_rmnode_self(node);
+
+       return (0);
+}
+
+/************************************************************
+ *
+ * Receive data from user hook
+ */
+static int
+ng_ccatm_rcvdata(hook_p hook, item_p item)
+{
+       struct cchook *hd = NG_HOOK_PRIVATE(hook);
+       struct uni_msg *msg;
+       struct mbuf *m;
+       struct ccatm_op op;
+       int err;
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if ((err = uni_msg_unpack_mbuf(m, &msg)) != 0) {
+               m_freem(m);
+               return (err);
+       }
+       m_freem(m);
+
+       if (uni_msg_len(msg) < sizeof(op)) {
+               printf("%s: packet too short\n", __func__);
+               uni_msg_destroy(msg);
+               return (EINVAL);
+       }
+
+       bcopy(msg->b_rptr, &op, sizeof(op));
+       msg->b_rptr += sizeof(op);
+
+       err = cc_user_signal(hd->inst, op.op, msg);
+       cc_work(hd->node->data);
+       return (err);
+}
+
+/*
+ * Pack a header and a data area into an mbuf chain
+ */
+static struct mbuf *
+pack_buf(void *h, size_t hlen, void *t, size_t tlen)
+{
+       struct mbuf *m, *m0, *last;
+       u_char *buf = (u_char *)t;
+       size_t n;
+
+       /* header should fit into a normal mbuf */
+       MGETHDR(m0, M_NOWAIT, MT_DATA);
+       if (m0 == NULL)
+               return NULL;
+
+       KASSERT(hlen <= MHLEN, ("hlen > MHLEN"));
+
+       bcopy(h, m0->m_data, hlen);
+       m0->m_len = hlen;
+       m0->m_pkthdr.len = hlen;
+
+       last = m0;
+       while ((n = tlen) != 0) {
+               if (n > MLEN) {
+                       m = m_getcl(M_NOWAIT, MT_DATA, 0);
+                       if (n > MCLBYTES)
+                               n = MCLBYTES;
+               } else
+                       MGET(m, M_NOWAIT, MT_DATA);
+
+               if(m == NULL)
+                       goto drop;
+
+               last->m_next = m;
+               last = m;
+
+               bcopy(buf, m->m_data, n);
+               buf += n;
+               tlen -= n;
+               m->m_len = n;
+               m0->m_pkthdr.len += n;
+       }
+
+       return (m0);
+
+  drop:
+       m_freem(m0);
+       return NULL;
+}
+
+/*
+ * Send an indication to the user.
+ */
+static void
+ng_ccatm_send_user(struct ccuser *user, void *uarg, u_int op,
+    void *val, size_t len)
+{
+       struct cchook *hd = uarg;
+       struct mbuf *m;
+       struct ccatm_op h;
+       int error;
+
+       h.op = op;
+       m = pack_buf(&h, sizeof(h), val, len);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, hd->hook, m);
+       if (error != 0)
+               printf("%s: error=%d\n", __func__, error);
+}
+
+/*
+ * Send a response to the user.
+ */
+static void
+ng_ccatm_respond_user(struct ccuser *user, void *uarg, int err, u_int data,
+    void *val, size_t len)
+{
+       struct cchook *hd = uarg;
+       struct mbuf *m;
+       struct {
+               struct ccatm_op op;
+               struct atm_resp resp;
+       } resp;
+       int error;
+
+       resp.op.op = ATMOP_RESP;
+       resp.resp.resp = err;
+       resp.resp.data = data;
+       m = pack_buf(&resp, sizeof(resp), val, len);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, hd->hook, m);
+       if (error != 0)
+               printf("%s: error=%d\n", __func__, error);
+}
+
+/*
+ * Receive data from UNI.
+ */
+static int
+ng_ccatm_rcvuni(hook_p hook, item_p item)
+{
+       struct cchook *hd = NG_HOOK_PRIVATE(hook);
+       struct uni_msg *msg;
+       struct uni_arg arg;
+       struct mbuf *m;
+       int err;
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if ((err = uni_msg_unpack_mbuf(m, &msg)) != 0) {
+               m_freem(m);
+               return (err);
+       }
+       m_freem(m);
+
+       if (uni_msg_len(msg) < sizeof(arg)) {
+               printf("%s: packet too short\n", __func__);
+               uni_msg_destroy(msg);
+               return (EINVAL);
+       }
+
+       bcopy(msg->b_rptr, &arg, sizeof(arg));
+       msg->b_rptr += sizeof(arg);
+
+       if (arg.sig == UNIAPI_ERROR) {
+               if (uni_msg_len(msg) != sizeof(struct uniapi_error)) {
+                       printf("%s: bad UNIAPI_ERROR size %zu\n", __func__,
+                           uni_msg_len(msg));
+                       uni_msg_destroy(msg);
+                       return (EINVAL);
+               }
+               err = cc_uni_response(hd->inst, arg.cookie,
+                   ((struct uniapi_error *)msg->b_rptr)->reason,
+                   ((struct uniapi_error *)msg->b_rptr)->state);
+               uni_msg_destroy(msg);
+       } else
+               err = cc_uni_signal(hd->inst, arg.cookie, arg.sig, msg);
+
+       cc_work(hd->node->data);
+       return (err);
+}
+
+/*
+ * Uarg is the port's uarg.
+ */
+static void
+ng_ccatm_send_uni(struct ccconn *conn, void *uarg, u_int op, u_int cookie,
+    struct uni_msg *msg)
+{
+       struct cchook *hd = uarg;
+       struct uni_arg arg;
+       struct mbuf *m;
+       int error;
+
+       arg.sig = op;
+       arg.cookie = cookie;
+
+       m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
+       uni_msg_destroy(msg);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, hd->hook, m);
+       if (error != 0)
+               printf("%s: error=%d\n", __func__, error);
+}
+
+/*
+ * Send a global message to the UNI
+ */
+static void
+ng_ccatm_send_uni_glob(struct ccport *port, void *uarg, u_int op, u_int cookie,
+    struct uni_msg *msg)
+{
+       struct cchook *hd = uarg;
+       struct uni_arg arg;
+       struct mbuf *m;
+       int error;
+
+       arg.sig = op;
+       arg.cookie = cookie;
+
+       m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
+       if (msg != NULL)
+               uni_msg_destroy(msg);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, hd->hook, m);
+       if (error != 0)
+               printf("%s: error=%d\n", __func__, error);
+}
+/*
+ * Receive from ILMID
+ */
+static int
+ng_ccatm_rcvmanage(hook_p hook, item_p item)
+{
+       NG_FREE_ITEM(item);
+       return (0);
+}
+
+static int
+ng_ccatm_rcvdump(hook_p hook, item_p item)
+{
+       NG_FREE_ITEM(item);
+       return (0);
+}
+
+static void
+ng_ccatm_log(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       printf("\n");
+       va_end(ap);
+}
+
+/*
+ * Loading and unloading of node type
+ */
+static int
+ng_ccatm_mod_event(module_t mod, int event, void *data)
+{
+       int s;
+       int error = 0;
+
+       s = splnet();
+       switch (event) {
+
+         case MOD_LOAD:
+               break;
+
+         case MOD_UNLOAD:
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       splx(s);
+       return (error);
+}
diff --git a/sys/netgraph7/atm/ccatm/ng_ccatm_cust.h b/sys/netgraph7/atm/ccatm/ng_ccatm_cust.h
new file mode 100644 (file)
index 0000000..0749a94
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2004
+ *     Hartmut Brandt
+ *     All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Customisation of call control source to the NG environment.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ccatm/ng_ccatm_cust.h,v 1.2 2005/01/07 01:45:41 imp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/atm/ngatmbase.h>
+
+#define        CCASSERT(E, M) KASSERT(E, M)
+
+MALLOC_DECLARE(M_NG_CCATM);
+
+#define        CCMALLOC(S)     (malloc((S), M_NG_CCATM, M_NOWAIT))
+#define        CCZALLOC(S)     (malloc((S), M_NG_CCATM, M_NOWAIT | M_ZERO))
+#define        CCFREE(P)       do { free((P), M_NG_CCATM); } while (0)
+
+#define        CCGETERRNO()    (ENOMEM)
diff --git a/sys/netgraph7/atm/ng_atm.c b/sys/netgraph7/atm/ng_atm.c
new file mode 100644 (file)
index 0000000..9528ac5
--- /dev/null
@@ -0,0 +1,1433 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ */
+
+/*
+ * Netgraph module to connect NATM interfaces to netgraph.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/ng_atm.c,v 1.15 2005/08/10 06:25:40 obrien Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sbuf.h>
+#include <sys/ioccom.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+#include <net/if_atm.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/atm/ng_atm.h>
+
+/*
+ * Hooks in the NATM code
+ */
+extern void    (*ng_atm_attach_p)(struct ifnet *);
+extern void    (*ng_atm_detach_p)(struct ifnet *);
+extern int     (*ng_atm_output_p)(struct ifnet *, struct mbuf **);
+extern void    (*ng_atm_input_p)(struct ifnet *, struct mbuf **,
+                   struct atm_pseudohdr *, void *);
+extern void    (*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
+                   struct atm_pseudohdr *, void *);
+extern void    (*ng_atm_event_p)(struct ifnet *, uint32_t, void *);
+
+/*
+ * Sysctl stuff.
+ */
+SYSCTL_NODE(_net_graph, OID_AUTO, atm, CTLFLAG_RW, 0, "atm related stuff");
+
+#ifdef NGATM_DEBUG
+static int allow_shutdown;
+
+SYSCTL_INT(_net_graph_atm, OID_AUTO, allow_shutdown, CTLFLAG_RW,
+    &allow_shutdown, 0, "allow ng_atm nodes to shutdown");
+#endif
+
+/*
+ * Hook private data
+ */
+struct ngvcc {
+       uint16_t        vpi;    /* VPI of this hook */
+       uint16_t        vci;    /* VCI of this hook, 0 if none */
+       uint32_t        flags;  /* private flags */
+       hook_p          hook;   /* the connected hook */
+
+       LIST_ENTRY(ngvcc) link;
+};
+#define        VCC_OPEN        0x0001  /* open */
+
+/*
+ * Node private data
+ */
+struct priv {
+       struct ifnet    *ifp;           /* the ATM interface */
+       hook_p          input;          /* raw input hook */
+       hook_p          orphans;        /* packets to nowhere */
+       hook_p          output;         /* catch output packets */
+       hook_p          manage;         /* has also entry in vccs */
+       uint64_t        in_packets;
+       uint64_t        in_errors;
+       uint64_t        out_packets;
+       uint64_t        out_errors;
+
+       LIST_HEAD(, ngvcc) vccs;
+};
+
+/*
+ * Parse ifstate state
+ */
+static const struct ng_parse_struct_field ng_atm_if_change_info[] =
+    NGM_ATM_IF_CHANGE_INFO;
+static const struct ng_parse_type ng_atm_if_change_type = {
+       &ng_parse_struct_type,
+       &ng_atm_if_change_info
+};
+
+/*
+ * Parse vcc state change
+ */
+static const struct ng_parse_struct_field ng_atm_vcc_change_info[] =
+    NGM_ATM_VCC_CHANGE_INFO;
+static const struct ng_parse_type ng_atm_vcc_change_type = {
+       &ng_parse_struct_type,
+       &ng_atm_vcc_change_info
+};
+
+/*
+ * Parse acr change
+ */
+static const struct ng_parse_struct_field ng_atm_acr_change_info[] =
+    NGM_ATM_ACR_CHANGE_INFO;
+static const struct ng_parse_type ng_atm_acr_change_type = {
+       &ng_parse_struct_type,
+       &ng_atm_acr_change_info
+};
+
+/*
+ * Parse the configuration structure ng_atm_config
+ */
+static const struct ng_parse_struct_field ng_atm_config_type_info[] =
+    NGM_ATM_CONFIG_INFO;
+
+static const struct ng_parse_type ng_atm_config_type = {
+       &ng_parse_struct_type,
+       &ng_atm_config_type_info
+};
+
+/*
+ * Parse a single vcc structure and a variable array of these ng_atm_vccs
+ */
+static const struct ng_parse_struct_field ng_atm_tparam_type_info[] =
+    NGM_ATM_TPARAM_INFO;
+static const struct ng_parse_type ng_atm_tparam_type = {
+       &ng_parse_struct_type,
+       &ng_atm_tparam_type_info
+};
+static const struct ng_parse_struct_field ng_atm_vcc_type_info[] =
+    NGM_ATM_VCC_INFO;
+static const struct ng_parse_type ng_atm_vcc_type = {
+       &ng_parse_struct_type,
+       &ng_atm_vcc_type_info
+};
+
+
+static int
+ng_atm_vccarray_getlen(const struct ng_parse_type *type,
+       const u_char *start, const u_char *buf)
+{
+       const struct atmio_vcctable *vp;
+
+       vp = (const struct atmio_vcctable *)
+           (buf - offsetof(struct atmio_vcctable, vccs));
+
+       return (vp->count);
+}
+static const struct ng_parse_array_info ng_atm_vccarray_info =
+    NGM_ATM_VCCARRAY_INFO;
+static const struct ng_parse_type ng_atm_vccarray_type = {
+       &ng_parse_array_type,
+       &ng_atm_vccarray_info
+};
+
+
+static const struct ng_parse_struct_field ng_atm_vcctable_type_info[] =
+    NGM_ATM_VCCTABLE_INFO;
+
+static const struct ng_parse_type ng_atm_vcctable_type = {
+       &ng_parse_struct_type,
+       &ng_atm_vcctable_type_info
+};
+
+/*
+ * Parse CPCS INIT structure ng_atm_cpcs_init
+ */
+static const struct ng_parse_struct_field ng_atm_cpcs_init_type_info[] =
+    NGM_ATM_CPCS_INIT_INFO;
+
+static const struct ng_parse_type ng_atm_cpcs_init_type = {
+       &ng_parse_struct_type,
+       &ng_atm_cpcs_init_type_info
+};
+
+/*
+ * Parse CPCS TERM structure ng_atm_cpcs_term
+ */
+static const struct ng_parse_struct_field ng_atm_cpcs_term_type_info[] =
+    NGM_ATM_CPCS_TERM_INFO;
+
+static const struct ng_parse_type ng_atm_cpcs_term_type = {
+       &ng_parse_struct_type,
+       &ng_atm_cpcs_term_type_info
+};
+
+/*
+ * Parse statistic struct
+ */
+static const struct ng_parse_struct_field ng_atm_stats_type_info[] =
+    NGM_ATM_STATS_INFO;
+
+static const struct ng_parse_type ng_atm_stats_type = {
+       &ng_parse_struct_type,
+       &ng_atm_stats_type_info
+};
+
+static const struct ng_cmdlist ng_atm_cmdlist[] = {
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_IFNAME,
+         "getifname",
+         NULL,
+         &ng_parse_string_type
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_CONFIG,
+         "getconfig",
+         NULL,
+         &ng_atm_config_type
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_VCCS,
+         "getvccs",
+         NULL,
+         &ng_atm_vcctable_type
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_CPCS_INIT,
+         "cpcsinit",
+         &ng_atm_cpcs_init_type,
+         NULL
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_CPCS_TERM,
+         "cpcsterm",
+         &ng_atm_cpcs_term_type,
+         NULL
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_VCC,
+         "getvcc",
+         &ng_parse_hookbuf_type,
+         &ng_atm_vcc_type
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_VCCID,
+         "getvccid",
+         &ng_atm_vcc_type,
+         &ng_atm_vcc_type
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_GET_STATS,
+         "getstats",
+         NULL,
+         &ng_atm_stats_type
+       },
+
+       /* events */
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_IF_CHANGE,
+         "if_change",
+         &ng_atm_if_change_type,
+         &ng_atm_if_change_type,
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_VCC_CHANGE,
+         "vcc_change",
+         &ng_atm_vcc_change_type,
+         &ng_atm_vcc_change_type,
+       },
+       {
+         NGM_ATM_COOKIE,
+         NGM_ATM_ACR_CHANGE,
+         "acr_change",
+         &ng_atm_acr_change_type,
+         &ng_atm_acr_change_type,
+       },
+       { 0 }
+};
+
+static int ng_atm_mod_event(module_t, int, void *);
+
+static ng_constructor_t ng_atm_constructor;
+static ng_shutdown_t   ng_atm_shutdown;
+static ng_rcvmsg_t     ng_atm_rcvmsg;
+static ng_newhook_t    ng_atm_newhook;
+static ng_connect_t    ng_atm_connect;
+static ng_disconnect_t ng_atm_disconnect;
+static ng_rcvdata_t    ng_atm_rcvdata;
+static ng_rcvdata_t    ng_atm_rcvdrop;
+
+static struct ng_type ng_atm_typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_ATM_NODE_TYPE,
+       .mod_event =    ng_atm_mod_event,
+       .constructor =  ng_atm_constructor,
+       .rcvmsg =       ng_atm_rcvmsg,
+       .shutdown =     ng_atm_shutdown,
+       .newhook =      ng_atm_newhook,
+       .connect =      ng_atm_connect,
+       .rcvdata =      ng_atm_rcvdata,
+       .disconnect =   ng_atm_disconnect,
+       .cmdlist =      ng_atm_cmdlist,
+};
+NETGRAPH_INIT(atm, &ng_atm_typestruct);
+
+static const struct {
+       u_int   media;
+       const char *name;
+} atmmedia[] = IFM_SUBTYPE_ATM_DESCRIPTIONS;
+
+
+#define        IFP2NG(IFP)     ((node_p)((struct ifatm *)(IFP)->if_softc)->ngpriv)
+#define        IFP2NG_SET(IFP, val)    (((struct ifatm *)(IFP)->if_softc)->ngpriv = (val))
+
+#define        IFFLAGS "\020\001UP\002BROADCAST\003DEBUG\004LOOPBACK" \
+                "\005POINTOPOINT\006SMART\007RUNNING\010NOARP" \
+                "\011PROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX" \
+                "\015LINK0\016LINK1\017LINK2\020MULTICAST"
+
+
+/************************************************************/
+/*
+ * INPUT
+ */
+/*
+ * A packet is received from an interface. 
+ * If we have an input hook, prepend the pseudoheader to the data and
+ * deliver it out to that hook. If not, look whether it is destined for
+ * use. If so locate the appropriate hook, deliver the packet without the
+ * header and we are done. If it is not for us, leave it alone.
+ */
+static void
+ng_atm_input(struct ifnet *ifp, struct mbuf **mp,
+       struct atm_pseudohdr *ah, void *rxhand)
+{
+       node_p node = IFP2NG(ifp);
+       struct priv *priv;
+       const struct ngvcc *vcc;
+       int error;
+
+       if (node == NULL)
+               return;
+       priv = NG_NODE_PRIVATE(node);
+       if (priv->input != NULL) {
+               /*
+                * Prepend the atm_pseudoheader.
+                */
+               M_PREPEND(*mp, sizeof(*ah), M_DONTWAIT);
+               if (*mp == NULL)
+                       return;
+               memcpy(mtod(*mp, struct atm_pseudohdr *), ah, sizeof(*ah));
+               NG_SEND_DATA_ONLY(error, priv->input, *mp);
+               if (error == 0) {
+                       priv->in_packets++;
+                       *mp = NULL;
+               } else {
+#ifdef NGATM_DEBUG
+                       printf("%s: error=%d\n", __func__, error);
+#endif
+                       priv->in_errors++;
+               }
+               return;
+       }
+       if ((ATM_PH_FLAGS(ah) & ATMIO_FLAG_NG) == 0)
+               return;
+
+       vcc = (struct ngvcc *)rxhand;
+
+       NG_SEND_DATA_ONLY(error, vcc->hook, *mp);
+       if (error == 0) {
+               priv->in_packets++;
+               *mp = NULL;
+       } else {
+#ifdef NGATM_DEBUG
+               printf("%s: error=%d\n", __func__, error);
+#endif
+               priv->in_errors++;
+       }
+}
+
+/*
+ * ATM packet is about to be output. The atm_pseudohdr is already prepended.
+ * If the hook is set, reroute the packet to the hook.
+ */
+static int
+ng_atm_output(struct ifnet *ifp, struct mbuf **mp)
+{
+       const node_p node = IFP2NG(ifp);
+       const struct priv *priv;
+       int error = 0;
+
+       if (node == NULL)
+               return (0);
+       priv = NG_NODE_PRIVATE(node);
+       if (priv->output) {
+               NG_SEND_DATA_ONLY(error, priv->output, *mp);
+               *mp = NULL;
+       }
+
+       return (error);
+}
+
+/*
+ * Well, this doesn't make much sense for ATM.
+ */
+static void
+ng_atm_input_orphans(struct ifnet *ifp, struct mbuf *m,
+       struct atm_pseudohdr *ah, void *rxhand)
+{
+       node_p node = IFP2NG(ifp);
+       struct priv *priv;
+       int error;
+
+       if (node == NULL) {
+               m_freem(m);
+               return;
+       }
+       priv = NG_NODE_PRIVATE(node);
+       if (priv->orphans == NULL) {
+               m_freem(m);
+               return;
+       }
+       /*
+        * Prepend the atm_pseudoheader.
+        */
+       M_PREPEND(m, sizeof(*ah), M_DONTWAIT);
+       if (m == NULL)
+               return;
+       memcpy(mtod(m, struct atm_pseudohdr *), ah, sizeof(*ah));
+       NG_SEND_DATA_ONLY(error, priv->orphans, m);
+       if (error == 0)
+               priv->in_packets++;
+       else {
+               priv->in_errors++;
+#ifdef NGATM_DEBUG
+               printf("%s: error=%d\n", __func__, error);
+#endif
+       }
+}
+
+/************************************************************/
+/*
+ * OUTPUT
+ */
+static int
+ng_atm_rcvdata(hook_p hook, item_p item)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       const struct ngvcc *vcc = NG_HOOK_PRIVATE(hook);
+       struct mbuf *m;
+       struct atm_pseudohdr *aph;
+       int error;
+
+       if (vcc->vci == 0) {
+               NG_FREE_ITEM(item);
+               return (ENOTCONN);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       /*
+        * Prepend pseudo-hdr. Drivers don't care about the flags.
+        */
+       M_PREPEND(m, sizeof(*aph), M_DONTWAIT);
+       if (m == NULL) {
+               NG_FREE_M(m);
+               return (ENOMEM);
+       }
+       aph = mtod(m, struct atm_pseudohdr *);
+       ATM_PH_VPI(aph) = vcc->vpi;
+       ATM_PH_SETVCI(aph, vcc->vci);
+       ATM_PH_FLAGS(aph) = 0;
+
+       if ((error = atm_output(priv->ifp, m, NULL, NULL)) == 0)
+               priv->out_packets++;
+       else
+               priv->out_errors++;
+       return (error);
+}
+
+static int
+ng_atm_rcvdrop(hook_p hook, item_p item)
+{
+       NG_FREE_ITEM(item);
+       return (0);
+}
+
+
+/************************************************************
+ *
+ * Event from driver.
+ */
+static void
+ng_atm_event_func(node_p node, hook_p hook, void *arg, int event)
+{
+       const struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ngvcc *vcc;
+       struct ng_mesg *mesg;
+       int error;
+
+       switch (event) {
+
+         case ATMEV_FLOW_CONTROL:
+           {
+               struct atmev_flow_control *ev = arg;
+               struct ngm_queue_state *qstate;
+
+               /* find the connection */
+               LIST_FOREACH(vcc, &priv->vccs, link)
+                       if (vcc->vci == ev->vci && vcc->vpi == ev->vpi)
+                               break;
+               if (vcc == NULL)
+                       break;
+
+               /* convert into a flow control message */
+               NG_MKMESSAGE(mesg, NGM_FLOW_COOKIE,
+                   ev->busy ? NGM_HIGH_WATER_PASSED : NGM_LOW_WATER_PASSED,
+                   sizeof(struct ngm_queue_state), M_NOWAIT);
+               if (mesg == NULL)
+                       break;
+               qstate = (struct ngm_queue_state *)mesg->data;
+
+               /* XXX have to figure out how to get that info */
+
+               NG_SEND_MSG_HOOK(error, node, mesg, vcc->hook, 0);
+               break;
+           }
+
+         case ATMEV_VCC_CHANGED:
+           {
+               struct atmev_vcc_changed *ev = arg;
+               struct ngm_atm_vcc_change *chg;
+
+               if (priv->manage == NULL)
+                       break;
+               NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_VCC_CHANGE,
+                   sizeof(struct ngm_atm_vcc_change), M_NOWAIT);
+               if (mesg == NULL)
+                       break;
+               chg = (struct ngm_atm_vcc_change *)mesg->data;
+               chg->vci = ev->vci;
+               chg->vpi = ev->vpi;
+               chg->state = (ev->up != 0);
+               chg->node = NG_NODE_ID(node);
+               NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, 0);
+               break;
+           }
+
+         case ATMEV_IFSTATE_CHANGED:
+           {
+               struct atmev_ifstate_changed *ev = arg;
+               struct ngm_atm_if_change *chg;
+
+               if (priv->manage == NULL)
+                       break;
+               NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_IF_CHANGE,
+                   sizeof(struct ngm_atm_if_change), M_NOWAIT);
+               if (mesg == NULL)
+                       break;
+               chg = (struct ngm_atm_if_change *)mesg->data;
+               chg->carrier = (ev->carrier != 0);
+               chg->running = (ev->running != 0);
+               chg->node = NG_NODE_ID(node);
+               NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, 0);
+               break;
+           }
+
+         case ATMEV_ACR_CHANGED:
+           {
+               struct atmev_acr_changed *ev = arg;
+               struct ngm_atm_acr_change *acr;
+
+               /* find the connection */
+               LIST_FOREACH(vcc, &priv->vccs, link)
+                       if (vcc->vci == ev->vci && vcc->vpi == ev->vpi)
+                               break;
+               if (vcc == NULL)
+                       break;
+
+               /* convert into a flow control message */
+               NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_ACR_CHANGE,
+                   sizeof(struct ngm_atm_acr_change), M_NOWAIT);
+               if (mesg == NULL)
+                       break;
+               acr = (struct ngm_atm_acr_change *)mesg->data;
+               acr->node = NG_NODE_ID(node);
+               acr->vci = ev->vci;
+               acr->vpi = ev->vpi;
+               acr->acr = ev->acr;
+
+               NG_SEND_MSG_HOOK(error, node, mesg, vcc->hook, 0);
+               break;
+           }
+       }
+}
+
+/*
+ * Use send_fn to get the right lock
+ */
+static void
+ng_atm_event(struct ifnet *ifp, uint32_t event, void *arg)
+{
+       const node_p node = IFP2NG(ifp);
+
+       if (node != NULL)
+               /* may happen during attach/detach */
+               (void)ng_send_fn(node, NULL, ng_atm_event_func, arg, event);
+}
+
+/************************************************************
+ *
+ * CPCS
+ */
+/*
+ * Open a channel for the user
+ */
+static int
+ng_atm_cpcs_init(node_p node, const struct ngm_atm_cpcs_init *arg)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       const struct ifatm_mib *mib;
+       struct ngvcc *vcc;
+       struct atmio_openvcc data;
+       int err;
+
+       if(priv->ifp->if_ioctl == NULL)
+               return (ENXIO);
+
+       mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib);
+
+       LIST_FOREACH(vcc, &priv->vccs, link)
+               if (strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0)
+                       break;
+       if (vcc == NULL)
+               return (ENOTCONN);
+       if (vcc->flags & VCC_OPEN)
+               return (EISCONN);
+
+       /*
+        * Check user arguments and construct ioctl argument
+        */
+       memset(&data, 0, sizeof(data));
+
+       data.rxhand = vcc;
+
+       switch (data.param.aal = arg->aal) {
+
+         case ATMIO_AAL_34:
+         case ATMIO_AAL_5:
+         case ATMIO_AAL_0:
+         case ATMIO_AAL_RAW:
+               break;
+
+         default:
+               return (EINVAL);
+       }
+
+       if (arg->vpi > 0xff)
+               return (EINVAL);
+       data.param.vpi = arg->vpi;
+
+       /* allow 0.0 as catch all receive channel */
+       if (arg->vci == 0 && (arg->vpi != 0 || !(arg->flags & ATMIO_FLAG_NOTX)))
+               return (EINVAL);
+       data.param.vci = arg->vci;
+
+       data.param.tparam.pcr = arg->pcr;
+
+       if (arg->mcr > arg->pcr)
+               return (EINVAL);
+       data.param.tparam.mcr = arg->mcr;
+
+       if (!(arg->flags & ATMIO_FLAG_NOTX)) {
+               if (arg->tmtu == 0)
+                       data.param.tmtu = priv->ifp->if_mtu;
+               else {
+                       data.param.tmtu = arg->tmtu;
+               }
+       }
+       if (!(arg->flags & ATMIO_FLAG_NORX)) {
+               if (arg->rmtu == 0)
+                       data.param.rmtu = priv->ifp->if_mtu;
+               else {
+                       data.param.rmtu = arg->rmtu;
+               }
+       }
+
+       switch (data.param.traffic = arg->traffic) {
+
+         case ATMIO_TRAFFIC_UBR:
+         case ATMIO_TRAFFIC_CBR:
+               break;
+
+         case ATMIO_TRAFFIC_VBR:
+               if (arg->scr > arg->pcr)
+                       return (EINVAL);
+               data.param.tparam.scr = arg->scr;
+
+               if (arg->mbs > (1 << 24))
+                       return (EINVAL);
+               data.param.tparam.mbs = arg->mbs;
+               break;
+
+         case ATMIO_TRAFFIC_ABR:
+               if (arg->icr > arg->pcr || arg->icr < arg->mcr)
+                       return (EINVAL);
+               data.param.tparam.icr = arg->icr;
+
+               if (arg->tbe == 0 || arg->tbe > (1 << 24))
+                       return (EINVAL);
+               data.param.tparam.tbe = arg->tbe;
+
+               if (arg->nrm > 0x7)
+                       return (EINVAL);
+               data.param.tparam.nrm = arg->nrm;
+
+               if (arg->trm > 0x7)
+                       return (EINVAL);
+               data.param.tparam.trm = arg->trm;
+
+               if (arg->adtf > 0x3ff)
+                       return (EINVAL);
+               data.param.tparam.adtf = arg->adtf;
+
+               if (arg->rif > 0xf)
+                       return (EINVAL);
+               data.param.tparam.rif = arg->rif;
+
+               if (arg->rdf > 0xf)
+                       return (EINVAL);
+               data.param.tparam.rdf = arg->rdf;
+
+               if (arg->cdf > 0x7)
+                       return (EINVAL);
+               data.param.tparam.cdf = arg->cdf;
+
+               break;
+
+         default:
+               return (EINVAL);
+       }
+
+       if ((arg->flags & ATMIO_FLAG_NORX) && (arg->flags & ATMIO_FLAG_NOTX))
+               return (EINVAL);
+
+       data.param.flags = arg->flags & ~(ATM_PH_AAL5 | ATM_PH_LLCSNAP);
+       data.param.flags |= ATMIO_FLAG_NG;
+
+       err = (*priv->ifp->if_ioctl)(priv->ifp, SIOCATMOPENVCC, (caddr_t)&data);
+
+       if (err == 0) {
+               vcc->vci = data.param.vci;
+               vcc->vpi = data.param.vpi;
+               vcc->flags = VCC_OPEN;
+       }
+
+       return (err);
+}
+
+/*
+ * Issue the close command to the driver
+ */
+static int
+cpcs_term(const struct priv *priv, u_int vpi, u_int vci)
+{
+       struct atmio_closevcc data;
+
+       if (priv->ifp->if_ioctl == NULL)
+               return ENXIO;
+
+       data.vpi = vpi;
+       data.vci = vci;
+
+       return ((*priv->ifp->if_ioctl)(priv->ifp,
+           SIOCATMCLOSEVCC, (caddr_t)&data));
+}
+
+
+/*
+ * Close a channel by request of the user
+ */
+static int
+ng_atm_cpcs_term(node_p node, const struct ngm_atm_cpcs_term *arg)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ngvcc *vcc;
+       int error;
+
+       LIST_FOREACH(vcc, &priv->vccs, link)
+               if(strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0)
+                       break;
+       if (vcc == NULL)
+               return (ENOTCONN);
+       if (!(vcc->flags & VCC_OPEN))
+               return (ENOTCONN);
+
+       error = cpcs_term(priv, vcc->vpi, vcc->vci);
+
+       vcc->vci = 0;
+       vcc->vpi = 0;
+       vcc->flags = 0;
+
+       return (error);
+}
+
+/************************************************************/
+/*
+ * CONTROL MESSAGES
+ */
+
+/*
+ * Produce a textual description of the current status
+ */
+static int
+text_status(node_p node, char *arg, u_int len)
+{
+       const struct priv *priv = NG_NODE_PRIVATE(node);
+       const struct ifatm_mib *mib;
+       struct sbuf sbuf;
+       u_int i;
+
+       static const struct {
+               const char      *name;
+               const char      *vendor;
+       } devices[] = {
+               ATM_DEVICE_NAMES
+       };
+
+       mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib);
+
+       sbuf_new(&sbuf, arg, len, SBUF_FIXEDLEN);
+       sbuf_printf(&sbuf, "interface: %s\n", priv->ifp->if_xname);
+
+       if (mib->device >= sizeof(devices) / sizeof(devices[0]))
+               sbuf_printf(&sbuf, "device=unknown\nvendor=unknown\n");
+       else
+               sbuf_printf(&sbuf, "device=%s\nvendor=%s\n",
+                   devices[mib->device].name, devices[mib->device].vendor);
+
+       for (i = 0; atmmedia[i].name; i++)
+               if(mib->media == atmmedia[i].media) {
+                       sbuf_printf(&sbuf, "media=%s\n", atmmedia[i].name);
+                       break;
+               }
+       if(atmmedia[i].name == NULL)
+               sbuf_printf(&sbuf, "media=unknown\n");
+
+       sbuf_printf(&sbuf, "serial=%u esi=%6D hardware=%u software=%u\n",
+           mib->serial, mib->esi, ":", mib->hw_version, mib->sw_version);
+       sbuf_printf(&sbuf, "pcr=%u vpi_bits=%u vci_bits=%u max_vpcs=%u "
+           "max_vccs=%u\n", mib->pcr, mib->vpi_bits, mib->vci_bits,
+           mib->max_vpcs, mib->max_vccs);
+       sbuf_printf(&sbuf, "ifflags=%b\n", priv->ifp->if_flags, IFFLAGS);
+
+       sbuf_finish(&sbuf);
+
+       return (sbuf_len(&sbuf));
+}
+
+/*
+ * Get control message
+ */
+static int
+ng_atm_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       const struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ng_mesg *resp = NULL;
+       struct ng_mesg *msg;
+       struct ifatm_mib *mib = (struct ifatm_mib *)(priv->ifp->if_linkmib);
+       int error = 0;
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+
+         case NGM_GENERIC_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_TEXT_STATUS:
+                       NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+
+                       resp->header.arglen = text_status(node,
+                           (char *)resp->data, resp->header.arglen) + 1;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         case NGM_ATM_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_ATM_GET_IFNAME:
+                       NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
+                       break;
+
+                 case NGM_ATM_GET_CONFIG:
+                   {
+                       struct ngm_atm_config *config;
+
+                       NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       config = (struct ngm_atm_config *)resp->data;
+                       config->pcr = mib->pcr;
+                       config->vpi_bits = mib->vpi_bits;
+                       config->vci_bits = mib->vci_bits;
+                       config->max_vpcs = mib->max_vpcs;
+                       config->max_vccs = mib->max_vccs;
+                       break;
+                   }
+
+                 case NGM_ATM_GET_VCCS:
+                   {
+                       struct atmio_vcctable *vccs;
+                       size_t len;
+
+                       if (priv->ifp->if_ioctl == NULL) {
+                               error = ENXIO;
+                               break;
+                       }
+                       error = (*priv->ifp->if_ioctl)(priv->ifp,
+                           SIOCATMGETVCCS, (caddr_t)&vccs);
+                       if (error)
+                               break;
+
+                       len = sizeof(*vccs) +
+                           vccs->count * sizeof(vccs->vccs[0]);
+                       NG_MKRESPONSE(resp, msg, len, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               free(vccs, M_DEVBUF);
+                               break;
+                       }
+
+                       (void)memcpy(resp->data, vccs, len);
+                       free(vccs, M_DEVBUF);
+
+                       break;
+                   }
+
+                 case NGM_ATM_GET_VCC:
+                   {
+                       char hook[NG_HOOKSIZ];
+                       struct atmio_vcctable *vccs;
+                       struct ngvcc *vcc;
+                       u_int i;
+
+                       if (priv->ifp->if_ioctl == NULL) {
+                               error = ENXIO;
+                               break;
+                       }
+                       if (msg->header.arglen != NG_HOOKSIZ) {
+                               error = EINVAL;
+                               break;
+                       }
+                       strncpy(hook, msg->data, NG_HOOKSIZ);
+                       hook[NG_HOOKSIZ - 1] = '\0';
+                       LIST_FOREACH(vcc, &priv->vccs, link)
+                               if (strcmp(NG_HOOK_NAME(vcc->hook), hook) == 0)
+                                       break;
+                       if (vcc == NULL) {
+                               error = ENOTCONN;
+                               break;
+                       }
+                       error = (*priv->ifp->if_ioctl)(priv->ifp,
+                           SIOCATMGETVCCS, (caddr_t)&vccs);
+                       if (error)
+                               break;
+
+                       for (i = 0; i < vccs->count; i++)
+                               if (vccs->vccs[i].vpi == vcc->vpi &&
+                                   vccs->vccs[i].vci == vcc->vci)
+                                       break;
+                       if (i == vccs->count) {
+                               error = ENOTCONN;
+                               free(vccs, M_DEVBUF);
+                               break;
+                       }
+
+                       NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]),
+                           M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               free(vccs, M_DEVBUF);
+                               break;
+                       }
+
+                       *(struct atmio_vcc *)resp->data = vccs->vccs[i];
+                       free(vccs, M_DEVBUF);
+                       break;
+                   }
+
+                 case NGM_ATM_GET_VCCID:
+                   {
+                       struct atmio_vcc *arg;
+                       struct atmio_vcctable *vccs;
+                       u_int i;
+
+                       if (priv->ifp->if_ioctl == NULL) {
+                               error = ENXIO;
+                               break;
+                       }
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct atmio_vcc *)msg->data;
+
+                       error = (*priv->ifp->if_ioctl)(priv->ifp,
+                           SIOCATMGETVCCS, (caddr_t)&vccs);
+                       if (error)
+                               break;
+
+                       for (i = 0; i < vccs->count; i++)
+                               if (vccs->vccs[i].vpi == arg->vpi &&
+                                   vccs->vccs[i].vci == arg->vci)
+                                       break;
+                       if (i == vccs->count) {
+                               error = ENOTCONN;
+                               free(vccs, M_DEVBUF);
+                               break;
+                       }
+
+                       NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]),
+                           M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               free(vccs, M_DEVBUF);
+                               break;
+                       }
+
+                       *(struct atmio_vcc *)resp->data = vccs->vccs[i];
+                       free(vccs, M_DEVBUF);
+                       break;
+                   }
+
+                 case NGM_ATM_CPCS_INIT:
+                       if (msg->header.arglen !=
+                           sizeof(struct ngm_atm_cpcs_init)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       error = ng_atm_cpcs_init(node,
+                           (struct ngm_atm_cpcs_init *)msg->data);
+                       break;
+
+                 case NGM_ATM_CPCS_TERM:
+                       if (msg->header.arglen !=
+                           sizeof(struct ngm_atm_cpcs_term)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       error = ng_atm_cpcs_term(node,
+                           (struct ngm_atm_cpcs_term *)msg->data);
+                       break;
+
+                 case NGM_ATM_GET_STATS:
+                   {
+                       struct ngm_atm_stats *p;
+
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       p = (struct ngm_atm_stats *)resp->data;
+                       p->in_packets = priv->in_packets;
+                       p->out_packets = priv->out_packets;
+                       p->in_errors = priv->in_errors;
+                       p->out_errors = priv->out_errors;
+
+                       break;
+                   }
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         default:
+               error = EINVAL;
+               break;
+       }
+
+       NG_RESPOND_MSG(error, node, item, resp);
+       NG_FREE_MSG(msg);
+       return (error);
+}
+
+/************************************************************/
+/*
+ * HOOK MANAGEMENT
+ */
+
+/*
+ * A new hook is create that will be connected to the node.
+ * Check, whether the name is one of the predefined ones.
+ * If not, create a new entry into the vcc list.
+ */
+static int
+ng_atm_newhook(node_p node, hook_p hook, const char *name)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ngvcc *vcc;
+
+       if (strcmp(name, "input") == 0) {
+               priv->input = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
+               return (0);
+       }
+       if (strcmp(name, "output") == 0) {
+               priv->output = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
+               return (0);
+       }
+       if (strcmp(name, "orphans") == 0) {
+               priv->orphans = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
+               return (0);
+       }
+
+       /*
+        * Allocate a new entry
+        */
+       vcc = malloc(sizeof(*vcc), M_NETGRAPH, M_NOWAIT | M_ZERO);
+       if (vcc == NULL)
+               return (ENOMEM);
+
+       vcc->hook = hook;
+       NG_HOOK_SET_PRIVATE(hook, vcc);
+
+       LIST_INSERT_HEAD(&priv->vccs, vcc, link);
+
+       if (strcmp(name, "manage") == 0)
+               priv->manage = hook;
+
+       return (0);
+}
+
+/*
+ * Connect. Set the peer to queuing.
+ */
+static int
+ng_atm_connect(hook_p hook)
+{
+       if (NG_HOOK_PRIVATE(hook) != NULL)
+               NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+       return (0);
+}
+
+/*
+ * Disconnect a HOOK
+ */
+static int
+ng_atm_disconnect(hook_p hook)
+{
+       struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct ngvcc *vcc = NG_HOOK_PRIVATE(hook);
+
+       if (vcc == NULL) {
+               if (hook == priv->output) {
+                       priv->output = NULL;
+                       return (0);
+               }
+               if (hook == priv->input) {
+                       priv->input = NULL;
+                       return (0);
+               }
+               if (hook == priv->orphans) {
+                       priv->orphans = NULL;
+                       return (0);
+               }
+               log(LOG_ERR, "ng_atm: bad hook '%s'", NG_HOOK_NAME(hook));
+               return (0);
+       }
+
+       /* don't terminate if we are detaching from the interface */
+       if ((vcc->flags & VCC_OPEN) && priv->ifp != NULL)
+               (void)cpcs_term(priv, vcc->vpi, vcc->vci);
+
+       NG_HOOK_SET_PRIVATE(hook, NULL);
+
+       LIST_REMOVE(vcc, link);
+       free(vcc, M_NETGRAPH);
+
+       if (hook == priv->manage)
+               priv->manage = NULL;
+
+       return (0);
+}
+
+/************************************************************/
+/*
+ * NODE MANAGEMENT
+ */
+
+/*
+ * ATM interface attached - create a node and name it like the interface.
+ */
+static void
+ng_atm_attach(struct ifnet *ifp)
+{
+       node_p node;
+       struct priv *priv;
+
+       KASSERT(IFP2NG(ifp) == 0, ("%s: node alreay exists?", __func__));
+
+       if (ng_make_node_common(&ng_atm_typestruct, &node) != 0) {
+               log(LOG_ERR, "%s: can't create node for %s\n",
+                   __func__, ifp->if_xname);
+               return;
+       }
+
+       priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
+       if (priv == NULL) {
+               log(LOG_ERR, "%s: can't allocate memory for %s\n",
+                   __func__, ifp->if_xname);
+               NG_NODE_UNREF(node);
+               return;
+       }
+       NG_NODE_SET_PRIVATE(node, priv);
+       priv->ifp = ifp;
+       LIST_INIT(&priv->vccs);
+       IFP2NG_SET(ifp, node);
+
+       if (ng_name_node(node, ifp->if_xname) != 0) {
+               log(LOG_WARNING, "%s: can't name node %s\n",
+                   __func__, ifp->if_xname);
+       }
+}
+
+/*
+ * ATM interface detached - destroy node.
+ */
+static void
+ng_atm_detach(struct ifnet *ifp)
+{
+       const node_p node = IFP2NG(ifp);
+       struct priv *priv;
+
+       if(node == NULL)
+               return;
+
+       NG_NODE_REALLY_DIE(node);
+
+       priv = NG_NODE_PRIVATE(node);
+       IFP2NG_SET(priv->ifp, NULL);
+       priv->ifp = NULL;
+
+       ng_rmnode_self(node);
+}
+
+/*
+ * Shutdown the node. This is called from the shutdown message processing.
+ */
+static int
+ng_atm_shutdown(node_p node)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if (node->nd_flags & NGF_REALLY_DIE) {
+               /*
+                * We are called from unloading the ATM driver. Really,
+                * really need to shutdown this node. The ifp was
+                * already handled in the detach routine.
+                */
+               NG_NODE_SET_PRIVATE(node, NULL);
+               free(priv, M_NETGRAPH);
+
+               NG_NODE_UNREF(node);
+               return (0);
+       }
+
+#ifdef NGATM_DEBUG
+       if (!allow_shutdown)
+               NG_NODE_REVIVE(node);           /* we persist */
+       else {
+               IFP2NG_SET(priv->ifp, NULL);
+               NG_NODE_SET_PRIVATE(node, NULL);
+               free(priv, M_NETGRAPH);
+               NG_NODE_UNREF(node);
+       }
+#else
+       /*
+        * We are persistant - reinitialize
+        */
+       NG_NODE_REVIVE(node);
+#endif
+       return (0);
+}
+
+/*
+ * Nodes are constructed only via interface attaches.
+ */
+static int
+ng_atm_constructor(node_p nodep)
+{
+       return (EINVAL);
+}
+
+/************************************************************/
+/*
+ * INITIALISATION
+ */
+/*
+ * Loading and unloading of node type
+ *
+ * The assignments to the globals for the hooks should be ok without
+ * a special hook. The use pattern is generally: check that the pointer
+ * is not NULL, call the function. In the attach case this is no problem.
+ * In the detach case we can detach only when no ATM node exists. That
+ * means that there is no ATM interface anymore. So we are sure that
+ * we are not in the code path in if_atmsubr.c. To prevent someone
+ * from adding an interface after we have started to unload the node, we
+ * take the iflist lock so an if_attach will be blocked until we are done.
+ * XXX: perhaps the function pointers should be 'volatile' for this to work
+ * properly.
+ */
+static int
+ng_atm_mod_event(module_t mod, int event, void *data)
+{
+       struct ifnet *ifp;
+       int error = 0;
+
+       switch (event) {
+
+         case MOD_LOAD:
+               /*
+                * Register function hooks
+                */
+               if (ng_atm_attach_p != NULL) {
+                       error = EEXIST;
+                       break;
+               }
+               IFNET_RLOCK();
+
+               ng_atm_attach_p = ng_atm_attach;
+               ng_atm_detach_p = ng_atm_detach;
+               ng_atm_output_p = ng_atm_output;
+               ng_atm_input_p = ng_atm_input;
+               ng_atm_input_orphan_p = ng_atm_input_orphans;
+               ng_atm_event_p = ng_atm_event;
+
+               /* Create nodes for existing ATM interfaces */
+               TAILQ_FOREACH(ifp, &ifnet, if_link) {
+                       if (ifp->if_type == IFT_ATM)
+                               ng_atm_attach(ifp);
+               }
+               IFNET_RUNLOCK();
+               break;
+
+         case MOD_UNLOAD:
+               IFNET_RLOCK();
+
+               ng_atm_attach_p = NULL;
+               ng_atm_detach_p = NULL;
+               ng_atm_output_p = NULL;
+               ng_atm_input_p = NULL;
+               ng_atm_input_orphan_p = NULL;
+               ng_atm_event_p = NULL;
+
+               TAILQ_FOREACH(ifp, &ifnet, if_link) {
+                       if (ifp->if_type == IFT_ATM)
+                               ng_atm_detach(ifp);
+               }
+               IFNET_RUNLOCK();
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       return (error);
+}
diff --git a/sys/netgraph7/atm/ng_atm.h b/sys/netgraph7/atm/ng_atm.h
new file mode 100644 (file)
index 0000000..4b40657
--- /dev/null
@@ -0,0 +1,248 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ */
+
+/*
+ * Netgraph module to connect NATM interfaces to netgraph.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ng_atm.h,v 1.5 2005/01/07 01:45:40 imp Exp $
+ */
+#ifndef _NETGRAPH_ATM_NG_ATM_H_
+#define _NETGRAPH_ATM_NG_ATM_H_
+
+#define NG_ATM_NODE_TYPE "atm"
+#define NGM_ATM_COOKIE 960802260
+
+/* Netgraph control messages */
+enum {
+       NGM_ATM_GET_IFNAME = 1,         /* get the interface name */
+       NGM_ATM_GET_CONFIG,             /* get configuration */
+       NGM_ATM_GET_VCCS,               /* get a list of all active vccs */
+       NGM_ATM_CPCS_INIT,              /* start the channel */
+       NGM_ATM_CPCS_TERM,              /* stop the channel */
+       NGM_ATM_GET_VCC,                /* get VCC config */
+       NGM_ATM_GET_VCCID,              /* get VCC by VCI/VPI */
+       NGM_ATM_GET_STATS,              /* get global statistics */
+
+       /* messages from the node */
+       NGM_ATM_CARRIER_CHANGE = 1000,  /* UNUSED: carrier changed */
+       NGM_ATM_VCC_CHANGE,             /* permanent VCC changed */
+       NGM_ATM_ACR_CHANGE,             /* ABR ACR has changed */
+       NGM_ATM_IF_CHANGE,              /* interface state change */
+};
+
+/*
+ * Hardware interface configuration
+ */
+struct ngm_atm_config {
+       uint32_t        pcr;            /* peak cell rate */
+       uint32_t        vpi_bits;       /* number of active VPI bits */
+       uint32_t        vci_bits;       /* number of active VCI bits */
+       uint32_t        max_vpcs;       /* maximum number of VPCs */
+       uint32_t        max_vccs;       /* maximum number of VCCs */
+};
+#define NGM_ATM_CONFIG_INFO                                    \
+       {                                                       \
+         { "pcr",      &ng_parse_uint32_type },                \
+         { "vpi_bits", &ng_parse_uint32_type },                \
+         { "vci_bits", &ng_parse_uint32_type },                \
+         { "max_vpcs", &ng_parse_uint32_type },                \
+         { "max_vccs", &ng_parse_uint32_type },                \
+         { NULL }                                              \
+       }
+
+/*
+ * Information about an open VCC
+ * See net/if_atm.h. Keep in sync.
+ */
+#define NGM_ATM_TPARAM_INFO                                    \
+       {                                                       \
+         { "pcr",      &ng_parse_uint32_type },                \
+         { "scr",      &ng_parse_uint32_type },                \
+         { "mbs",      &ng_parse_uint32_type },                \
+         { "mcr",      &ng_parse_uint32_type },                \
+         { "icr",      &ng_parse_uint32_type },                \
+         { "tbe",      &ng_parse_uint32_type },                \
+         { "nrm",      &ng_parse_uint8_type },                 \
+         { "trm",      &ng_parse_uint8_type },                 \
+         { "adtf",     &ng_parse_uint16_type },                \
+         { "rif",      &ng_parse_uint8_type },                 \
+         { "rdf",      &ng_parse_uint8_type },                 \
+         { "cdf",      &ng_parse_uint8_type },                 \
+         { NULL }                                              \
+       }
+
+#define NGM_ATM_VCC_INFO                                       \
+       {                                                       \
+         { "flags",    &ng_parse_hint16_type },                \
+         { "vpi",      &ng_parse_uint16_type },                \
+         { "vci",      &ng_parse_uint16_type },                \
+         { "rmtu",     &ng_parse_uint16_type },                \
+         { "tmtu",     &ng_parse_uint16_type },                \
+         { "aal",      &ng_parse_uint8_type },                 \
+         { "traffic",  &ng_parse_uint8_type },                 \
+         { "tparam",   &ng_atm_tparam_type },                  \
+         { NULL }                                              \
+       }
+
+#define NGM_ATM_VCCARRAY_INFO                                  \
+       {                                                       \
+         &ng_atm_vcc_type,                                     \
+         ng_atm_vccarray_getlen,                               \
+         NULL                                                  \
+       }
+
+#define NGM_ATM_VCCTABLE_INFO                                  \
+       {                                                       \
+         { "count",    &ng_parse_uint32_type },                \
+         { "vccs",     &ng_atm_vccarray_type },                \
+         { NULL }                                              \
+       }
+
+/*
+ * Structure to open a VCC.
+ */
+struct ngm_atm_cpcs_init {
+       char            name[NG_HOOKSIZ];
+       uint32_t        flags;          /* flags. (if_atm.h) */
+       uint16_t        vci;            /* VCI to open */
+       uint16_t        vpi;            /* VPI to open */
+       uint16_t        rmtu;           /* Receive maximum CPCS size */
+       uint16_t        tmtu;           /* Transmit maximum CPCS size */
+       uint8_t         aal;            /* AAL type (if_atm.h) */
+       uint8_t         traffic;        /* traffic type (if_atm.h) */
+       uint32_t        pcr;            /* Peak cell rate */
+       uint32_t        scr;            /* VBR: Sustainable cell rate */
+       uint32_t        mbs;            /* VBR: Maximum burst rate */
+       uint32_t        mcr;            /* UBR+: Minimum cell rate */
+       uint32_t        icr;            /* ABR: Initial cell rate */
+       uint32_t        tbe;            /* ABR: Transmit buffer exposure */
+       uint8_t         nrm;            /* ABR: Nrm */
+       uint8_t         trm;            /* ABR: Trm */
+       uint16_t        adtf;           /* ABR: ADTF */
+       uint8_t         rif;            /* ABR: RIF */
+       uint8_t         rdf;            /* ABR: RDF */
+       uint8_t         cdf;            /* ABR: CDF */
+};
+
+#define NGM_ATM_CPCS_INIT_INFO                                         \
+       {                                                       \
+         { "name",     &ng_parse_hookbuf_type },               \
+         { "flags",    &ng_parse_hint32_type },                \
+         { "vci",      &ng_parse_uint16_type },                \
+         { "vpi",      &ng_parse_uint16_type },                \
+         { "rmtu",     &ng_parse_uint16_type },                \
+         { "tmtu",     &ng_parse_uint16_type },                \
+         { "aal",      &ng_parse_uint8_type },                 \
+         { "traffic",  &ng_parse_uint8_type },                 \
+         { "pcr",      &ng_parse_uint32_type },                \
+         { "scr",      &ng_parse_uint32_type },                \
+         { "mbs",      &ng_parse_uint32_type },                \
+         { "mcr",      &ng_parse_uint32_type },                \
+         { "icr",      &ng_parse_uint32_type },                \
+         { "tbe",      &ng_parse_uint32_type },                \
+         { "nrm",      &ng_parse_uint8_type },                 \
+         { "trm",      &ng_parse_uint8_type },                 \
+         { "adtf",     &ng_parse_uint16_type },                \
+         { "rif",      &ng_parse_uint8_type },                 \
+         { "rdf",      &ng_parse_uint8_type },                 \
+         { "cdf",      &ng_parse_uint8_type },                 \
+         { NULL }                                              \
+       }
+
+/*
+ * Structure to close a VCI without disconnecting the hook
+ */
+struct ngm_atm_cpcs_term {
+       char            name[NG_HOOKSIZ];
+};
+#define NGM_ATM_CPCS_TERM_INFO                                         \
+       {                                                       \
+         { "name",     &ng_parse_hookbuf_type },               \
+         { NULL }                                              \
+       }
+
+struct ngm_atm_stats {
+       uint64_t        in_packets;
+       uint64_t        in_errors;
+       uint64_t        out_packets;
+       uint64_t        out_errors;
+};
+#define NGM_ATM_STATS_INFO                                     \
+       {                                                       \
+         { "in_packets",       &ng_parse_uint64_type },        \
+         { "in_errors",        &ng_parse_uint64_type },        \
+         { "out_packets",      &ng_parse_uint64_type },        \
+         { "out_errors",       &ng_parse_uint64_type },        \
+         { NULL }                                              \
+       }
+
+struct ngm_atm_if_change {
+       uint32_t        node;
+       uint8_t         carrier;
+       uint8_t         running;
+};
+#define NGM_ATM_IF_CHANGE_INFO                                         \
+       {                                                       \
+         { "node",     &ng_parse_hint32_type },                \
+         { "carrier",  &ng_parse_uint8_type },                 \
+         { "running",  &ng_parse_uint8_type },                 \
+         { NULL }                                              \
+       }
+
+struct ngm_atm_vcc_change {
+       uint32_t        node;
+       uint16_t        vci;
+       uint8_t         vpi;
+       uint8_t         state;
+};
+#define NGM_ATM_VCC_CHANGE_INFO                                \
+       {                                                       \
+         { "node",     &ng_parse_hint32_type },                \
+         { "vci",      &ng_parse_uint16_type },                \
+         { "vpi",      &ng_parse_uint8_type },                 \
+         { "state",    &ng_parse_uint8_type },                 \
+         { NULL }                                              \
+       }
+
+struct ngm_atm_acr_change {
+       uint32_t        node;
+       uint16_t        vci;
+       uint8_t         vpi;
+       uint32_t        acr;
+};
+#define NGM_ATM_ACR_CHANGE_INFO                                        \
+       {                                                       \
+         { "node",     &ng_parse_hint32_type },                \
+         { "vci",      &ng_parse_uint16_type },                \
+         { "vpi",      &ng_parse_uint8_type },                 \
+         { "acr",      &ng_parse_uint32_type },                \
+         { NULL }                                              \
+       }
+
+#endif /* _NETGRAPH_ATM_NG_ATM_H */
diff --git a/sys/netgraph7/atm/ng_ccatm.h b/sys/netgraph7/atm/ng_ccatm.h
new file mode 100644 (file)
index 0000000..b0ab97a
--- /dev/null
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2001-2002
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ * Copyright (c) 2003-2004
+ *     Hartmut Brandt
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ng_ccatm.h,v 1.2 2005/01/07 01:45:40 imp Exp $
+ */
+
+/*
+ * Interface to ng_ccatm
+ */
+#ifndef _NETGRAPH_ATM_NG_CCATM_H_
+#define _NETGRAPH_ATM_NG_CCATM_H_
+
+#define NG_CCATM_NODE_TYPE     "ccatm"
+#define NGM_CCATM_COOKIE       984046139
+
+enum {
+       NGM_CCATM_DUMP,                 /* dump internal status */
+       NGM_CCATM_STOP,                 /* stop all processing, close all */
+       NGM_CCATM_START,                /* start processing */
+       NGM_CCATM_CLEAR,                /* clear prefix/address table */
+       NGM_CCATM_GET_ADDRESSES,        /* get list of all addresses */
+       NGM_CCATM_ADDRESS_REGISTERED,   /* registration ok */
+       NGM_CCATM_ADDRESS_UNREGISTERED, /* unregistration ok */
+       NGM_CCATM_SET_PORT_PARAM,       /* set port parameters */
+       NGM_CCATM_GET_PORT_PARAM,       /* get port parameters */
+       NGM_CCATM_GET_PORTLIST,         /* get list of port numbers */
+       NGM_CCATM_GETSTATE,             /* get port status */
+       NGM_CCATM_SETLOG,               /* set/get loglevel */
+       NGM_CCATM_RESET,                /* reset everything */
+       NGM_CCATM_GET_EXSTAT,           /* get extended status */
+};
+
+/*
+ * This must be synchronized with unistruct.h::struct uni_addr
+ */
+#define        NGM_CCATM_ADDR_ARRAY_INFO                               \
+       {                                                       \
+         &ng_parse_hint8_type,                                 \
+         UNI_ADDR_MAXLEN                                       \
+       }
+
+#define NGM_CCATM_UNI_ADDR_INFO                                \
+       {                                                       \
+         { "type",     &ng_parse_uint32_type },                \
+         { "plan",     &ng_parse_uint32_type },                \
+         { "len",      &ng_parse_uint32_type },                \
+         { "addr",     &ng_ccatm_addr_array_type },            \
+         { NULL }                                              \
+       }
+
+/*
+ * Address request
+ */
+struct ngm_ccatm_addr_req {
+       uint32_t        port;
+       struct uni_addr addr;
+};
+#define        NGM_CCATM_ADDR_REQ_INFO                                 \
+       {                                                       \
+         { "port",     &ng_parse_uint32_type },                \
+         { "addr",     &ng_ccatm_uni_addr_type },              \
+         { NULL },                                             \
+       }
+
+/*
+ * Get current address list
+ */
+struct ngm_ccatm_get_addresses {
+       uint32_t        count;
+       struct ngm_ccatm_addr_req addr[];
+};
+#define        NGM_CCATM_ADDR_REQ_ARRAY_INFO                           \
+       {                                                       \
+         &ng_ccatm_addr_req_type,                              \
+         ng_ccatm_addr_req_array_getlen                        \
+       }
+#define NGM_CCATM_GET_ADDRESSES_INFO                           \
+       {                                                       \
+         { "count",    &ng_parse_uint32_type },                \
+         { "addr",     &ng_ccatm_addr_req_array_type },        \
+         { NULL }                                              \
+       }
+
+/*
+ * Port as parameter
+ */
+struct ngm_ccatm_port {
+       uint32_t        port;
+};
+#define NGM_CCATM_PORT_INFO                                    \
+       {                                                       \
+         { "port",     &ng_parse_uint32_type },                \
+         { NULL }                                              \
+       }
+
+/*
+ * Port parameters.
+ * This must be synchronized with atmapi.h::struct atm_port_info.
+ */
+#define        NGM_CCATM_ESI_INFO                                              \
+       {                                                               \
+         &ng_parse_hint8_type,                                         \
+         6                                                             \
+       }
+#define NGM_CCATM_ATM_PORT_INFO                                        \
+       {                                                               \
+         { "port",             &ng_parse_uint32_type },                \
+         { "pcr",              &ng_parse_uint32_type },                \
+         { "max_vpi_bits",     &ng_parse_uint32_type },                \
+         { "max_vci_bits",     &ng_parse_uint32_type },                \
+         { "max_svpc_vpi",     &ng_parse_uint32_type },                \
+         { "max_svcc_vpi",     &ng_parse_uint32_type },                \
+         { "min_svcc_vci",     &ng_parse_uint32_type },                \
+         { "esi",              &ng_ccatm_esi_type },                   \
+         { "num_addr",         &ng_parse_uint32_type },                \
+         { NULL }                                                      \
+       }
+
+/*
+ * List of port numbers
+ */
+struct ngm_ccatm_portlist {
+       uint32_t        nports;
+       uint32_t        ports[];
+};
+#define        NGM_CCATM_PORT_ARRAY_INFO                                       \
+       {                                                               \
+         &ng_parse_uint32_type,                                        \
+         ng_ccatm_port_array_getlen                                    \
+       }
+#define NGM_CCATM_PORTLIST_INFO                                        \
+       {                                                               \
+         { "nports",   &ng_parse_uint32_type },                        \
+         { "ports",    &ng_ccatm_port_array_type },                    \
+         { NULL }                                                      \
+       }
+
+struct ccatm_op {
+       uint32_t        op;     /* request code */
+       u_char          data[];
+};
+
+#endif
diff --git a/sys/netgraph7/atm/ng_sscfu.h b/sys/netgraph7/atm/ng_sscfu.h
new file mode 100644 (file)
index 0000000..5a8950a
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ng_sscfu.h,v 1.2 2005/01/07 01:45:40 imp Exp $
+ *
+ * Netgraph module for ITU-T Q.2120 UNI SSCF.
+ */
+#ifndef _NETGRAPH_ATM_NG_SSCFU_H_
+#define        _NETGRAPH_ATM_NG_SSCFU_H_
+
+#define NG_SSCFU_NODE_TYPE "sscfu"
+#define NGM_SSCFU_COOKIE       980517963
+
+/* Netgraph control messages */
+enum {
+       NGM_SSCFU_GETDEFPARAM = 1,      /* get default SSCOP parameters */
+       NGM_SSCFU_ENABLE,               /* enable processing */
+       NGM_SSCFU_DISABLE,              /* disable processing */
+       NGM_SSCFU_GETDEBUG,             /* get debug flags */
+       NGM_SSCFU_SETDEBUG,             /* set debug flags */
+       NGM_SSCFU_GETSTATE,             /* get current state */
+};
+
+/* getdefparam return */
+struct ng_sscfu_getdefparam {
+       struct sscop_param      param;
+       uint32_t                mask;
+};
+#define NG_SSCFU_GETDEFPARAM_INFO                              \
+       {                                                       \
+         { "param",            &ng_sscop_param_type },         \
+         { "mask",             &ng_parse_uint32_type },        \
+         { NULL }                                              \
+       }
+
+/*
+ * Upper interface
+ */
+struct sscfu_arg {
+       uint32_t        sig;
+       u_char          data[];
+};
+#endif
diff --git a/sys/netgraph7/atm/ng_sscop.h b/sys/netgraph7/atm/ng_sscop.h
new file mode 100644 (file)
index 0000000..b4b2b3e
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ng_sscop.h,v 1.3 2005/01/07 01:45:40 imp Exp $
+ *
+ * Netgraph module for Q.2110 SSCOP
+ */
+#ifndef _NETGRAPH_ATM_NG_SSCOP_H_
+#define _NETGRAPH_ATM_NG_SSCOP_H_
+
+#define NG_SSCOP_NODE_TYPE "sscop"
+#define NGM_SSCOP_COOKIE       980175044
+
+/* Netgraph control messages */
+enum {
+       NGM_SSCOP_GETPARAM = 1,         /* get parameters */
+       NGM_SSCOP_SETPARAM,             /* set parameters */
+       NGM_SSCOP_ENABLE,               /* enable processing */
+       NGM_SSCOP_DISABLE,              /* disable and reset */
+       NGM_SSCOP_GETDEBUG,             /* get debugging flags */
+       NGM_SSCOP_SETDEBUG,             /* set debugging flags */
+       NGM_SSCOP_GETSTATE,             /* get current SSCOP state */
+};
+
+/* This must be in-sync with the definition in sscopdef.h */
+#define NG_SSCOP_PARAM_INFO                                    \
+       {                                                       \
+         { "timer_cc",         &ng_parse_uint32_type },        \
+         { "timer_poll",       &ng_parse_uint32_type },        \
+         { "timer_keep_alive", &ng_parse_uint32_type },        \
+         { "timer_no_response",&ng_parse_uint32_type },        \
+         { "timer_idle",       &ng_parse_uint32_type },        \
+         { "maxk",             &ng_parse_uint32_type },        \
+         { "maxj",             &ng_parse_uint32_type },        \
+         { "maxcc",            &ng_parse_uint32_type },        \
+         { "maxpd",            &ng_parse_uint32_type },        \
+         { "maxstat",          &ng_parse_uint32_type },        \
+         { "mr",               &ng_parse_uint32_type },        \
+         { "flags",            &ng_parse_uint32_type },        \
+         { NULL }                                              \
+       }
+
+
+struct ng_sscop_setparam {
+       uint32_t                mask;
+       struct sscop_param      param;
+};
+#define NG_SSCOP_SETPARAM_INFO                                         \
+       {                                                       \
+         { "mask",             &ng_parse_uint32_type },        \
+         { "param",            &ng_sscop_param_type },         \
+         { NULL }                                              \
+       }
+
+struct ng_sscop_setparam_resp {
+       uint32_t                mask;
+       int32_t                 error;
+};
+#define NG_SSCOP_SETPARAM_RESP_INFO                            \
+       {                                                       \
+         { "mask",             &ng_parse_uint32_type },        \
+         { "error",            &ng_parse_int32_type },         \
+         { NULL }                                              \
+       }
+
+/*
+ * Upper interface
+ */
+struct sscop_arg {
+       uint32_t        sig;
+       uint32_t        arg;    /* opt. sequence number or clear-buff */
+       u_char          data[];
+};
+
+struct sscop_marg {
+       uint32_t        sig;
+       u_char          data[];
+};
+struct sscop_merr {
+       uint32_t        sig;
+       uint32_t        err;    /* error code */
+       uint32_t        cnt;    /* error count */
+};
+
+#endif
diff --git a/sys/netgraph7/atm/ng_uni.h b/sys/netgraph7/atm/ng_uni.h
new file mode 100644 (file)
index 0000000..b2dc2c5
--- /dev/null
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ng_uni.h,v 1.2 2005/01/07 01:45:40 imp Exp $
+ *
+ * Netgraph module for UNI 4.0
+ */
+#ifndef _NETGRAPH_ATM_NG_UNI_H_
+#define _NETGRAPH_ATM_NG_UNI_H_
+
+#define NG_UNI_NODE_TYPE "uni"
+#define NGM_UNI_COOKIE 981112392
+
+enum {
+       NGM_UNI_GETDEBUG,       /* get debug flags */
+       NGM_UNI_SETDEBUG,       /* set debug flags */
+       NGM_UNI_GET_CONFIG,     /* get configuration */
+       NGM_UNI_SET_CONFIG,     /* set configuration */
+       NGM_UNI_ENABLE,         /* enable processing */
+       NGM_UNI_DISABLE,        /* free resources and disable */
+       NGM_UNI_GETSTATE,       /* retrieve coord state */
+};
+
+struct ngm_uni_debug {
+       uint32_t        level[UNI_MAXFACILITY];
+};
+#define NGM_UNI_DEBUGLEVEL_INFO {                              \
+       &ng_parse_uint32_type,                                  \
+       UNI_MAXFACILITY                                         \
+}
+#define NGM_UNI_DEBUG_INFO                                     \
+       {                                                       \
+         { "level",    &ng_uni_debuglevel_type },              \
+         { NULL }                                              \
+       }
+
+#define NGM_UNI_CONFIG_INFO                                    \
+       {                                                       \
+         { "proto",    &ng_parse_uint32_type },                \
+         { "popt",     &ng_parse_uint32_type },                \
+         { "option",   &ng_parse_uint32_type },                \
+         { "timer301", &ng_parse_uint32_type },                \
+         { "timer303", &ng_parse_uint32_type },                \
+         { "init303",  &ng_parse_uint32_type },                \
+         { "timer308", &ng_parse_uint32_type },                \
+         { "init308",  &ng_parse_uint32_type },                \
+         { "timer309", &ng_parse_uint32_type },                \
+         { "timer310", &ng_parse_uint32_type },                \
+         { "timer313", &ng_parse_uint32_type },                \
+         { "timer316", &ng_parse_uint32_type },                \
+         { "init316",  &ng_parse_uint32_type },                \
+         { "timer317", &ng_parse_uint32_type },                \
+         { "timer322", &ng_parse_uint32_type },                \
+         { "init322",  &ng_parse_uint32_type },                \
+         { "timer397", &ng_parse_uint32_type },                \
+         { "timer398", &ng_parse_uint32_type },                \
+         { "timer399", &ng_parse_uint32_type },                \
+         { NULL }                                              \
+       }
+
+struct ngm_uni_config_mask {
+       uint32_t                mask;
+       uint32_t                popt_mask;
+       uint32_t                option_mask;
+};
+#define NGM_UNI_CONFIG_MASK_INFO                               \
+       {                                                       \
+         { "mask",             &ng_parse_hint32_type },        \
+         { "popt_mask",        &ng_parse_hint32_type },        \
+         { "option_mask",      &ng_parse_hint32_type },        \
+         { NULL }                                              \
+       }
+
+struct ngm_uni_set_config {
+       struct uni_config               config;
+       struct ngm_uni_config_mask      mask;
+};
+#define NGM_UNI_SET_CONFIG_INFO                                \
+       {                                                       \
+         { "config",           &ng_uni_config_type },          \
+         { "mask",             &ng_uni_config_mask_type },     \
+         { NULL }                                              \
+       }
+
+/*
+ * API message
+ */
+struct uni_arg {
+       uint32_t        sig;
+       uint32_t        cookie;
+       u_char          data[];
+};
+
+#endif
diff --git a/sys/netgraph7/atm/ngatmbase.c b/sys/netgraph7/atm/ngatmbase.c
new file mode 100644 (file)
index 0000000..47fa5bf
--- /dev/null
@@ -0,0 +1,501 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * In-kernel UNI stack message functions.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/ngatmbase.c,v 1.3 2005/01/07 01:45:40 imp Exp $");
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <machine/stdarg.h>
+#include <netnatm/unimsg.h>
+#include <netgraph/atm/ngatmbase.h>
+
+#define NGATMBASE_VERSION      1
+
+static int ngatm_handler(module_t, int, void *);
+
+static moduledata_t ngatm_data = {
+       "ngatmbase",
+       ngatm_handler,
+       0
+};
+
+MODULE_VERSION(ngatmbase, NGATMBASE_VERSION);
+DECLARE_MODULE(ngatmbase, ngatm_data, SI_SUB_EXEC, SI_ORDER_ANY);
+
+/*********************************************************************/
+/*
+ * UNI Stack message handling functions
+ */
+MALLOC_DEFINE(M_UNIMSG, "unimsg", "uni message buffers");
+MALLOC_DEFINE(M_UNIMSGHDR, "unimsghdr", "uni message headers");
+
+#define EXTRA  128
+
+/* mutex to protect the free list (and the used list if debugging) */
+static struct mtx ngatm_unilist_mtx;
+
+/*
+ * Initialize UNI message subsystem
+ */
+static void
+uni_msg_init(void)
+{
+       mtx_init(&ngatm_unilist_mtx, "netgraph UNI msg header lists", NULL,
+           MTX_DEF);
+}
+
+/*
+ * Ensure, that the message can be extended by at least s bytes.
+ * Re-allocate the message (not the header). If that failes,
+ * free the entire message and return ENOMEM. Free space at the start of
+ * the message is retained.
+ */
+int
+uni_msg_extend(struct uni_msg *m, size_t s)
+{
+       u_char *b;
+       size_t len, lead;
+
+       lead = uni_msg_leading(m);
+       len = uni_msg_len(m);
+       s += lead + len + EXTRA;
+       if ((b = malloc(s, M_UNIMSG, M_NOWAIT)) == NULL) {
+               uni_msg_destroy(m);
+               return (ENOMEM);
+       }
+
+       bcopy(m->b_rptr, b + lead, len);
+       free(m->b_buf, M_UNIMSG);
+
+       m->b_buf = b;
+       m->b_rptr = m->b_buf + lead;
+       m->b_wptr = m->b_rptr + len;
+       m->b_lim = m->b_buf + s;
+
+       return (0);
+}
+
+/*
+ * Append a buffer to the message, making space if needed.
+ * If reallocation files, ENOMEM is returned and the message freed.
+ */
+int
+uni_msg_append(struct uni_msg *m, void *buf, size_t size)
+{
+       int error;
+
+       if ((error = uni_msg_ensure(m, size)))
+               return (error);
+       bcopy(buf, m->b_wptr, size);
+       m->b_wptr += size;
+
+       return (0);
+}
+
+/*
+ * Pack/unpack data from/into mbufs. Assume, that the (optional) header
+ * fits into the first mbuf, ie. hdrlen < MHLEN. Note, that the message
+ * can be NULL, but hdrlen should not be 0 in this case.
+ */
+struct mbuf *
+uni_msg_pack_mbuf(struct uni_msg *msg, void *hdr, size_t hdrlen)
+{
+       struct mbuf *m, *m0, *last;
+       size_t n;
+
+       MGETHDR(m0, M_NOWAIT, MT_DATA);
+       if (m0 == NULL)
+               return (NULL);
+
+       KASSERT(hdrlen <= MHLEN, ("uni_msg_pack_mbuf: hdrlen > MHLEN"));
+
+       if (hdrlen != 0) {
+               bcopy(hdr, m0->m_data, hdrlen);
+               m0->m_len = hdrlen;
+               m0->m_pkthdr.len = hdrlen;
+
+       } else {
+               if ((n = uni_msg_len(msg)) > MHLEN) {
+                       MCLGET(m0, M_NOWAIT);
+                       if (!(m0->m_flags & M_EXT))
+                               goto drop;
+                       if (n > MCLBYTES)
+                               n = MCLBYTES;
+               }
+
+               bcopy(msg->b_rptr, m0->m_data, n);
+               msg->b_rptr += n;
+               m0->m_len = n;
+               m0->m_pkthdr.len = n;
+       }
+
+       last = m0;
+       while (msg != NULL && (n = uni_msg_len(msg)) != 0) {
+               MGET(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       goto drop;
+               last->m_next = m;
+               last = m;
+
+               if (n > MLEN) {
+                       MCLGET(m, M_NOWAIT);
+                       if (!(m->m_flags & M_EXT))
+                               goto drop;
+                       if (n > MCLBYTES)
+                               n = MCLBYTES;
+               }
+
+               bcopy(msg->b_rptr, m->m_data, n);
+               msg->b_rptr += n;
+               m->m_len = n;
+               m0->m_pkthdr.len += n;
+       }
+
+       return (m0);
+
+  drop:
+       m_freem(m0);
+       return (NULL);
+}
+
+#ifdef NGATM_DEBUG
+
+/*
+ * Prepend a debugging header to each message
+ */
+struct ngatm_msg {
+       LIST_ENTRY(ngatm_msg) link;
+       const char *file;
+       int line;
+       struct uni_msg msg;
+};
+
+/*
+ * These are the lists of free and used message headers.
+ */
+static LIST_HEAD(, ngatm_msg) ngatm_freeuni =
+    LIST_HEAD_INITIALIZER(ngatm_freeuni);
+static LIST_HEAD(, ngatm_msg) ngatm_useduni =
+    LIST_HEAD_INITIALIZER(ngatm_useduni);
+
+/*
+ * Clean-up UNI message subsystem
+ */
+static void
+uni_msg_fini(void)
+{
+       struct ngatm_msg *h;
+
+       /* free all free message headers */
+       while ((h = LIST_FIRST(&ngatm_freeuni)) != NULL) {
+               LIST_REMOVE(h, link);
+               free(h, M_UNIMSGHDR);
+       }
+
+       /* forget about still used messages */
+       LIST_FOREACH(h, &ngatm_useduni, link)
+               printf("unimsg header in use: %p (%s, %d)\n",
+                   &h->msg, h->file, h->line);
+
+       mtx_destroy(&ngatm_unilist_mtx);
+}
+
+/*
+ * Allocate a message, that can hold at least s bytes.
+ */
+struct uni_msg *
+_uni_msg_alloc(size_t s, const char *file, int line)
+{
+       struct ngatm_msg *m;
+
+       mtx_lock(&ngatm_unilist_mtx);
+       if ((m = LIST_FIRST(&ngatm_freeuni)) != NULL)
+               LIST_REMOVE(m, link);
+       mtx_unlock(&ngatm_unilist_mtx);
+
+       if (m == NULL &&
+           (m = malloc(sizeof(*m), M_UNIMSGHDR, M_NOWAIT)) == NULL)
+               return (NULL);
+
+       s += EXTRA;
+       if((m->msg.b_buf = malloc(s, M_UNIMSG, M_NOWAIT | M_ZERO)) == NULL) {
+               mtx_lock(&ngatm_unilist_mtx);
+               LIST_INSERT_HEAD(&ngatm_freeuni, m, link);
+               mtx_unlock(&ngatm_unilist_mtx);
+               return (NULL);
+       }
+       m->msg.b_rptr = m->msg.b_wptr = m->msg.b_buf;
+       m->msg.b_lim = m->msg.b_buf + s;
+       m->file = file;
+       m->line = line;
+
+       mtx_lock(&ngatm_unilist_mtx);
+       LIST_INSERT_HEAD(&ngatm_useduni, m, link);
+       mtx_unlock(&ngatm_unilist_mtx);
+       return (&m->msg);
+}
+
+/*
+ * Destroy a UNI message.
+ * The header is inserted into the free header list.
+ */
+void
+_uni_msg_destroy(struct uni_msg *m, const char *file, int line)
+{
+       struct ngatm_msg *h, *d;
+
+       d = (struct ngatm_msg *)((char *)m - offsetof(struct ngatm_msg, msg));
+
+       mtx_lock(&ngatm_unilist_mtx);
+       LIST_FOREACH(h, &ngatm_useduni, link)
+               if (h == d)
+                       break;
+
+       if (h == NULL) {
+               /*
+                * Not on used list. Ups.
+                */
+               LIST_FOREACH(h, &ngatm_freeuni, link)
+                       if (h == d)
+                               break;
+
+               if (h == NULL)
+                       printf("uni_msg %p was never allocated; found "
+                           "in %s:%u\n", m, file, line);
+               else
+                       printf("uni_msg %p was already destroyed in %s,%d; "
+                           "found in %s:%u\n", m, h->file, h->line,
+                           file, line);
+       } else {
+               free(m->b_buf, M_UNIMSG);
+
+               LIST_REMOVE(d, link);
+               LIST_INSERT_HEAD(&ngatm_freeuni, d, link);
+
+               d->file = file;
+               d->line = line;
+       }
+
+       mtx_unlock(&ngatm_unilist_mtx);
+}
+
+#else /* !NGATM_DEBUG */
+
+/*
+ * This assumes, that sizeof(struct uni_msg) >= sizeof(struct ngatm_msg)
+ * and the alignment requirements of are the same.
+ */
+struct ngatm_msg {
+       LIST_ENTRY(ngatm_msg) link;
+};
+
+/* Lists of free message headers.  */
+static LIST_HEAD(, ngatm_msg) ngatm_freeuni =
+    LIST_HEAD_INITIALIZER(ngatm_freeuni);
+
+/*
+ * Clean-up UNI message subsystem
+ */
+static void
+uni_msg_fini(void)
+{
+       struct ngatm_msg *h;
+
+       /* free all free message headers */
+       while ((h = LIST_FIRST(&ngatm_freeuni)) != NULL) {
+               LIST_REMOVE(h, link);
+               free(h, M_UNIMSGHDR);
+       }
+
+       mtx_destroy(&ngatm_unilist_mtx);
+}
+
+/*
+ * Allocate a message, that can hold at least s bytes.
+ */
+struct uni_msg *
+uni_msg_alloc(size_t s)
+{
+       struct ngatm_msg *a;
+       struct uni_msg *m;
+
+       mtx_lock(&ngatm_unilist_mtx);
+       if ((a = LIST_FIRST(&ngatm_freeuni)) != NULL)
+               LIST_REMOVE(a, link);
+       mtx_unlock(&ngatm_unilist_mtx);
+
+       if (a == NULL) {
+               if ((m = malloc(sizeof(*m), M_UNIMSGHDR, M_NOWAIT)) == NULL)
+                       return (NULL);
+               a = (struct ngatm_msg *)m;
+       } else
+               m = (struct uni_msg *)a;
+
+       s += EXTRA;
+       if((m->b_buf = malloc(s, M_UNIMSG, M_NOWAIT | M_ZERO)) == NULL) {
+               mtx_lock(&ngatm_unilist_mtx);
+               LIST_INSERT_HEAD(&ngatm_freeuni, a, link);
+               mtx_unlock(&ngatm_unilist_mtx);
+               return (NULL);
+       }
+       m->b_rptr = m->b_wptr = m->b_buf;
+       m->b_lim = m->b_buf + s;
+
+       return (m);
+}
+
+/*
+ * Destroy a UNI message.
+ * The header is inserted into the free header list.
+ */
+void
+uni_msg_destroy(struct uni_msg *m)
+{
+       struct ngatm_msg *a;
+
+       a = (struct ngatm_msg *)m;
+
+       free(m->b_buf, M_UNIMSG);
+
+       mtx_lock(&ngatm_unilist_mtx);
+       LIST_INSERT_HEAD(&ngatm_freeuni, a, link);
+       mtx_unlock(&ngatm_unilist_mtx);
+}
+
+#endif
+
+/*
+ * Build a message from a number of buffers. Arguments are pairs
+ * of (void *, size_t) ending with a NULL pointer.
+ */
+#ifdef NGATM_DEBUG
+struct uni_msg *
+_uni_msg_build(const char *file, int line, void *ptr, ...)
+#else
+struct uni_msg *
+uni_msg_build(void *ptr, ...)
+#endif
+{
+       va_list ap;
+       struct uni_msg *m;
+       size_t len, n;
+       void *p1;
+
+       len = 0;
+       va_start(ap, ptr);
+       p1 = ptr;
+       while (p1 != NULL) {
+               n = va_arg(ap, size_t);
+               len += n;
+               p1 = va_arg(ap, void *);
+       }
+       va_end(ap);
+
+#ifdef NGATM_DEBUG
+       if ((m = _uni_msg_alloc(len, file, line)) == NULL)
+#else
+       if ((m = uni_msg_alloc(len)) == NULL)
+#endif
+               return (NULL);
+
+       va_start(ap, ptr);
+       p1 = ptr;
+       while (p1 != NULL) {
+               n = va_arg(ap, size_t);
+               bcopy(p1, m->b_wptr, n);
+               m->b_wptr += n;
+               p1 = va_arg(ap, void *);
+       }
+       va_end(ap);
+
+       return (m);
+}
+
+/*
+ * Unpack an mbuf chain into a uni_msg buffer.
+ */
+#ifdef NGATM_DEBUG
+int
+_uni_msg_unpack_mbuf(struct mbuf *m, struct uni_msg **pmsg, const char *file,
+    int line)
+#else
+int
+uni_msg_unpack_mbuf(struct mbuf *m, struct uni_msg **pmsg)
+#endif
+{
+       if (!(m->m_flags & M_PKTHDR)) {
+               printf("%s: bogus packet %p\n", __func__, m);
+               return (EINVAL);
+       }
+#ifdef NGATM_DEBUG
+       if ((*pmsg = _uni_msg_alloc(m->m_pkthdr.len, file, line)) == NULL)
+#else
+       if ((*pmsg = uni_msg_alloc(m->m_pkthdr.len)) == NULL)
+#endif
+               return (ENOMEM);
+
+       m_copydata(m, 0, m->m_pkthdr.len, (*pmsg)->b_wptr);
+       (*pmsg)->b_wptr += m->m_pkthdr.len;
+
+       return (0);
+}
+
+/*********************************************************************/
+
+static int
+ngatm_handler(module_t mod, int what, void *arg)
+{
+       int error = 0;
+
+       switch (what) {
+
+         case MOD_LOAD:
+               uni_msg_init();
+               break;
+
+         case MOD_UNLOAD:
+               uni_msg_fini();
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+
+       return (error);
+}
diff --git a/sys/netgraph7/atm/ngatmbase.h b/sys/netgraph7/atm/ngatmbase.h
new file mode 100644 (file)
index 0000000..126cb0b
--- /dev/null
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/ngatmbase.h,v 1.3 2005/01/07 01:45:40 imp Exp $
+ *
+ * In-kernel UNI stack message functions.
+ */
+#ifndef _NETGRAPH_ATM_NGATMBASE_H_
+#define        _NETGRAPH_ATM_NGATMBASE_H_
+
+/* forward declarations */
+struct mbuf;
+struct uni_msg;
+
+struct mbuf *uni_msg_pack_mbuf(struct uni_msg *, void *, size_t);
+
+#ifdef NGATM_DEBUG
+
+struct uni_msg *_uni_msg_alloc(size_t, const char *, int);
+struct uni_msg *_uni_msg_build(const char *, int, void *, ...);
+void _uni_msg_destroy(struct uni_msg *, const char *, int);
+int _uni_msg_unpack_mbuf(struct mbuf *, struct uni_msg **, const char *, int);
+
+#define        uni_msg_alloc(S) _uni_msg_alloc((S), __FILE__, __LINE__)
+#define        uni_msg_build(P...) _uni_msg_build(__FILE__, __LINE__, P)
+#define        uni_msg_destroy(M) _uni_msg_destroy((M), __FILE__, __LINE__)
+#define        uni_msg_unpack_mbuf(M, PP) \
+           _uni_msg_unpack_mbuf((M), (PP), __FILE__, __LINE__)
+
+#else /* !NGATM_DEBUG */
+
+struct uni_msg *uni_msg_alloc(size_t);
+struct uni_msg *uni_msg_build(void *, ...);
+void uni_msg_destroy(struct uni_msg *);
+int uni_msg_unpack_mbuf(struct mbuf *, struct uni_msg **);
+
+#endif
+#endif
diff --git a/sys/netgraph7/atm/sscfu/ng_sscfu.c b/sys/netgraph7/atm/sscfu/ng_sscfu.c
new file mode 100644 (file)
index 0000000..1cf1f5d
--- /dev/null
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Netgraph module for ITU-T Q.2120 UNI SSCF.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/sscfu/ng_sscfu.c,v 1.4 2005/01/07 01:45:41 imp Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sbuf.h>
+#include <machine/stdarg.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netnatm/saal/sscopdef.h>
+#include <netnatm/saal/sscfudef.h>
+#include <netgraph/atm/ng_sscop.h>
+#include <netgraph/atm/ng_sscfu.h>
+#include <netgraph/atm/sscfu/ng_sscfu_cust.h>
+#include <netnatm/saal/sscfu.h>
+
+MALLOC_DEFINE(M_NG_SSCFU, "netgraph_sscfu", "netgraph uni sscf node");
+
+MODULE_DEPEND(ng_sscfu, ngatmbase, 1, 1, 1);
+
+/*
+ * Private data
+ */
+struct priv {
+       hook_p          upper;  /* SAAL interface */
+       hook_p          lower;  /* SSCOP interface */
+       struct sscfu    *sscf;  /* the instance */
+       int             enabled;
+};
+
+/*
+ * PARSING
+ */
+/*
+ * Parse PARAM type
+ */
+static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
+    NG_SSCOP_PARAM_INFO;
+
+static const struct ng_parse_type ng_sscop_param_type = {
+       &ng_parse_struct_type,
+       ng_sscop_param_type_info
+};
+
+static const struct ng_parse_struct_field ng_sscfu_getdefparam_type_info[] =
+    NG_SSCFU_GETDEFPARAM_INFO;
+
+static const struct ng_parse_type ng_sscfu_getdefparam_type = {
+       &ng_parse_struct_type,
+       ng_sscfu_getdefparam_type_info
+};
+
+
+static const struct ng_cmdlist ng_sscfu_cmdlist[] = {
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_GETDEFPARAM,
+         "getdefparam",
+         NULL,
+         &ng_sscfu_getdefparam_type
+       },
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_ENABLE,
+         "enable",
+         NULL,
+         NULL
+       },
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_DISABLE,
+         "disable",
+         NULL,
+         NULL
+       },
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_GETDEBUG,
+         "getdebug",
+         NULL,
+         &ng_parse_hint32_type
+       },
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_SETDEBUG,
+         "setdebug",
+         &ng_parse_hint32_type,
+         NULL
+       },
+       {
+         NGM_SSCFU_COOKIE,
+         NGM_SSCFU_GETSTATE,
+         "getstate",
+         NULL,
+         &ng_parse_uint32_type
+       },
+       { 0 }
+};
+
+static ng_constructor_t ng_sscfu_constructor;
+static ng_shutdown_t   ng_sscfu_shutdown;
+static ng_rcvmsg_t     ng_sscfu_rcvmsg;
+static ng_newhook_t    ng_sscfu_newhook;
+static ng_disconnect_t ng_sscfu_disconnect;
+static ng_rcvdata_t    ng_sscfu_rcvupper;
+static ng_rcvdata_t    ng_sscfu_rcvlower;
+
+static int ng_sscfu_mod_event(module_t, int, void *);
+
+static struct ng_type ng_sscfu_typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_SSCFU_NODE_TYPE,
+       .mod_event =    ng_sscfu_mod_event,
+       .constructor =  ng_sscfu_constructor,
+       .rcvmsg =       ng_sscfu_rcvmsg,
+       .shutdown =     ng_sscfu_shutdown,
+       .newhook =      ng_sscfu_newhook,
+       .rcvdata =      ng_sscfu_rcvupper,
+       .disconnect =   ng_sscfu_disconnect,
+       .cmdlist =      ng_sscfu_cmdlist,
+};
+NETGRAPH_INIT(sscfu, &ng_sscfu_typestruct);
+
+static void sscfu_send_upper(struct sscfu *, void *, enum saal_sig,
+       struct mbuf *);
+static void sscfu_send_lower(struct sscfu *, void *, enum sscop_aasig,
+       struct mbuf *, u_int);
+static void sscfu_window(struct sscfu *, void *, u_int);
+static void sscfu_verbose(struct sscfu *, void *, const char *, ...)
+       __printflike(3, 4);
+
+static const struct sscfu_funcs sscfu_funcs = {
+       sscfu_send_upper,
+       sscfu_send_lower,
+       sscfu_window,
+       sscfu_verbose
+};
+
+/************************************************************/
+/*
+ * CONTROL MESSAGES
+ */
+static int
+text_status(node_p node, struct priv *priv, char *arg, u_int len)
+{
+       struct sbuf sbuf;
+
+       sbuf_new(&sbuf, arg, len, 0);
+
+       if (priv->upper)
+               sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
+                   NG_HOOK_NAME(priv->upper),
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
+       else
+               sbuf_printf(&sbuf, "upper hook: <not connected>\n");
+
+       if (priv->lower)
+               sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
+                   NG_HOOK_NAME(priv->lower),
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
+       else
+               sbuf_printf(&sbuf, "lower hook: <not connected>\n");
+
+       sbuf_printf(&sbuf, "sscf state: %s\n",
+           priv->enabled == 0 ? "<disabled>" :
+           sscfu_statename(sscfu_getstate(priv->sscf)));
+
+       sbuf_finish(&sbuf);
+       return (sbuf_len(&sbuf));
+}
+
+static int
+ng_sscfu_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ng_mesg *resp = NULL;
+       struct ng_mesg *msg;
+       int error = 0;
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+
+         case NGM_GENERIC_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_TEXT_STATUS:
+                       NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       resp->header.arglen = text_status(node, priv,
+                           (char *)resp->data, resp->header.arglen) + 1;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         case NGM_SSCFU_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_SSCFU_GETDEFPARAM:
+                   {
+                       struct ng_sscfu_getdefparam *p;
+
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       p = (struct ng_sscfu_getdefparam *)resp->data;
+                       p->mask = sscfu_getdefparam(&p->param);
+                       break;
+                   }
+
+                 case NGM_SSCFU_ENABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (priv->enabled) {
+                               error = EISCONN;
+                               break;
+                       }
+                       priv->enabled = 1;
+                       break;
+
+                 case NGM_SSCFU_DISABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (!priv->enabled) {
+                               error = ENOTCONN;
+                               break;
+                       }
+                       priv->enabled = 0;
+                       sscfu_reset(priv->sscf);
+                       break;
+
+                 case NGM_SSCFU_GETSTATE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       *(uint32_t *)resp->data =
+                           priv->enabled ? (sscfu_getstate(priv->sscf) + 1)
+                                         : 0;
+                       break;
+
+                 case NGM_SSCFU_GETDEBUG:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       *(uint32_t *)resp->data = sscfu_getdebug(priv->sscf);
+                       break;
+
+                 case NGM_SSCFU_SETDEBUG:
+                       if (msg->header.arglen != sizeof(uint32_t)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       sscfu_setdebug(priv->sscf, *(uint32_t *)msg->data);
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         default:
+               error = EINVAL;
+               break;
+       }
+
+       NG_RESPOND_MSG(error, node, item, resp);
+       NG_FREE_MSG(msg);
+
+       return (error);
+}
+
+/************************************************************/
+/*
+ * HOOK MANAGEMENT
+ */
+static int
+ng_sscfu_newhook(node_p node, hook_p hook, const char *name)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if (strcmp(name, "upper") == 0)
+               priv->upper = hook;
+       else if (strcmp(name, "lower") == 0) {
+               priv->lower = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_sscfu_rcvlower);
+       } else
+               return (EINVAL);
+       return (0);
+}
+
+static int
+ng_sscfu_disconnect(hook_p hook)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if (hook == priv->upper)
+               priv->upper = NULL;
+       else if (hook == priv->lower)
+               priv->lower = NULL;
+       else {
+               log(LOG_ERR, "bogus hook");
+               return (EINVAL);
+       }
+
+       if (NG_NODE_NUMHOOKS(node) == 0) {
+               if (NG_NODE_IS_VALID(node))
+                       ng_rmnode_self(node);
+       } else {
+               /*
+                * Because there are no timeouts reset the protocol
+                * if the lower layer is disconnected.
+                */
+               if (priv->lower == NULL &&
+                   priv->enabled &&
+                   sscfu_getstate(priv->sscf) != SSCFU_RELEASED)
+                       sscfu_reset(priv->sscf);
+       }
+       return (0);
+}
+
+/************************************************************/
+/*
+ * DATA
+ */
+static int
+ng_sscfu_rcvupper(hook_p hook, item_p item)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct sscfu_arg a;
+
+       if (!priv->enabled || priv->lower == NULL) {
+               NG_FREE_ITEM(item);
+               return (0);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if (!(m->m_flags & M_PKTHDR)) {
+               printf("no pkthdr\n");
+               m_freem(m);
+               return (EINVAL);
+       }
+       if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
+               return (ENOMEM);
+       bcopy((caddr_t)mtod(m, struct sscfu_arg *), &a, sizeof(a));
+       m_adj(m, sizeof(a));
+
+       return (sscfu_saalsig(priv->sscf, a.sig, m));
+}
+
+static void
+sscfu_send_upper(struct sscfu *sscf, void *p, enum saal_sig sig, struct mbuf *m)
+{
+       node_p node = (node_p)p;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       int error;
+       struct sscfu_arg *a;
+
+       if (priv->upper == NULL) {
+               if (m != NULL)
+                       m_freem(m);
+               return;
+       }
+       if (m == NULL) {
+               MGETHDR(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       return;
+               m->m_len = sizeof(struct sscfu_arg);
+               m->m_pkthdr.len = m->m_len;
+       } else {
+               M_PREPEND(m, sizeof(struct sscfu_arg), M_NOWAIT);
+               if (m == NULL)
+                       return;
+       }
+       a = mtod(m, struct sscfu_arg *);
+       a->sig = sig;
+
+       NG_SEND_DATA_ONLY(error, priv->upper, m);
+}
+
+static int
+ng_sscfu_rcvlower(hook_p hook, item_p item)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct sscop_arg a;
+
+       if (!priv->enabled || priv->upper == NULL) {
+               NG_FREE_ITEM(item);
+               return (0);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if (!(m->m_flags & M_PKTHDR)) {
+               printf("no pkthdr\n");
+               m_freem(m);
+               return (EINVAL);
+       }
+
+       /*
+        * Strip of the SSCOP header.
+        */
+       if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
+               return (ENOMEM);
+       bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
+       m_adj(m, sizeof(a));
+
+       sscfu_input(priv->sscf, a.sig, m, a.arg);
+
+       return (0);
+}
+
+static void
+sscfu_send_lower(struct sscfu *sscf, void *p, enum sscop_aasig sig,
+    struct mbuf *m, u_int arg)
+{
+       node_p node = (node_p)p;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       int error;
+       struct sscop_arg *a;
+
+       if (priv->lower == NULL) {
+               if (m != NULL)
+                       m_freem(m);
+               return;
+       }
+       if (m == NULL) {
+               MGETHDR(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       return;
+               m->m_len = sizeof(struct sscop_arg);
+               m->m_pkthdr.len = m->m_len;
+       } else {
+               M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
+               if (m == NULL)
+                       return;
+       }
+       a = mtod(m, struct sscop_arg *);
+       a->sig = sig;
+       a->arg = arg;
+
+       NG_SEND_DATA_ONLY(error, priv->lower, m);
+}
+
+/*
+ * Window is handled by ng_sscop so make this a NOP.
+ */
+static void
+sscfu_window(struct sscfu *sscfu, void *arg, u_int w)
+{
+}
+
+/************************************************************/
+/*
+ * NODE MANAGEMENT
+ */
+static int
+ng_sscfu_constructor(node_p node)
+{
+       struct priv *priv;
+
+       if ((priv = malloc(sizeof(*priv), M_NG_SSCFU, M_NOWAIT|M_ZERO)) == NULL)
+               return (ENOMEM);
+
+       if ((priv->sscf = sscfu_create(node, &sscfu_funcs)) == NULL) {
+               free(priv, M_NG_SSCFU);
+               return (ENOMEM);
+       }
+
+       NG_NODE_SET_PRIVATE(node, priv);
+
+       return (0);
+}
+
+static int
+ng_sscfu_shutdown(node_p node)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       sscfu_destroy(priv->sscf);
+
+       free(priv, M_NG_SSCFU);
+       NG_NODE_SET_PRIVATE(node, NULL);
+
+       NG_NODE_UNREF(node);
+
+       return (0);
+}
+
+static void
+sscfu_verbose(struct sscfu *sscfu, void *arg, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       printf("sscfu(%p): ", sscfu);
+       vprintf(fmt, ap);
+       va_end(ap);
+       printf("\n");
+}
+
+/************************************************************/
+/*
+ * INITIALISATION
+ */
+/*
+ * Loading and unloading of node type
+ */
+static int
+ng_sscfu_mod_event(module_t mod, int event, void *data)
+{
+       int s;
+       int error = 0;
+
+       s = splnet();
+       switch (event) {
+
+         case MOD_LOAD:
+               break;
+
+         case MOD_UNLOAD:
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       splx(s);
+       return (error);
+}
diff --git a/sys/netgraph7/atm/sscfu/ng_sscfu_cust.h b/sys/netgraph7/atm/sscfu/ng_sscfu_cust.h
new file mode 100644 (file)
index 0000000..4605ad5
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Customisation of the SSCFU code to ng_sscfu.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/sscfu/ng_sscfu_cust.h,v 1.2 2005/01/07 01:45:41 imp Exp $
+ */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/callout.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <machine/stdarg.h>
+
+/*
+ * Allocate zeroed or non-zeroed memory of some size and cast it.
+ * Return NULL on failure.
+ */
+#ifndef SSCFU_DEBUG
+
+#define        MEMINIT() \
+       MALLOC_DECLARE(M_NG_SSCFU); \
+       DECL_SIGQ_GET
+
+#define        MEMZALLOC(PTR, CAST, SIZE) \
+       ((PTR) = (CAST)malloc((SIZE), M_NG_SSCFU, M_NOWAIT | M_ZERO))
+#define        MEMFREE(PTR) \
+       free(PTR, M_NG_SSCFU)
+
+#define        SIG_ALLOC(PTR) \
+       MEMZALLOC(PTR, struct sscfu_sig *, sizeof(struct sscfu_sig))
+#define        SIG_FREE(PTR) \
+       MEMFREE(PTR)
+
+#else
+
+#define        MEMINIT()                                                       \
+       MALLOC_DEFINE(M_NG_SSCFU_INS, "sscfu_ins", "SSCFU instances");  \
+       MALLOC_DEFINE(M_NG_SSCFU_SIG, "sscfu_sig", "SSCFU signals");    \
+       DECL_SIGQ_GET
+
+#define        MEMZALLOC(PTR, CAST, SIZE)                                      \
+       ((PTR) = (CAST)malloc((SIZE), M_NG_SSCFU_INS, M_NOWAIT | M_ZERO))
+#define        MEMFREE(PTR)                                                    \
+       FREE(PTR, M_NG_SSCFU_INS)
+
+#define        SIG_ALLOC(PTR)                                                  \
+       ((PTR) = malloc(sizeof(struct sscfu_sig),                       \
+           M_NG_SSCFU_SIG, M_NOWAIT | M_ZERO))
+#define        SIG_FREE(PTR)                                                   \
+       FREE(PTR, M_NG_SSCFU_SIG)
+
+#endif
+
+
+/*
+ * Signal queues
+ */
+typedef TAILQ_ENTRY(sscfu_sig) sscfu_sigq_link_t;
+typedef TAILQ_HEAD(sscfu_sigq, sscfu_sig) sscfu_sigq_head_t;
+#define        SIGQ_INIT(Q)            TAILQ_INIT(Q)
+#define        SIGQ_APPEND(Q, S)       TAILQ_INSERT_TAIL(Q, S, link)
+
+#define        SIGQ_GET(Q) ng_sscfu_sigq_get((Q))
+
+#define        DECL_SIGQ_GET                                                   \
+static __inline struct sscfu_sig *                                     \
+ng_sscfu_sigq_get(struct sscfu_sigq *q)                                        \
+{                                                                      \
+       struct sscfu_sig *s;                                            \
+                                                                       \
+       s = TAILQ_FIRST(q);                                             \
+       if (s != NULL)                                                  \
+               TAILQ_REMOVE(q, s, link);                               \
+       return (s);                                                     \
+}
+
+#define        SIGQ_CLEAR(Q)                                                   \
+    do {                                                               \
+       struct sscfu_sig *_s1, *_s2;                                    \
+                                                                       \
+       _s1 = TAILQ_FIRST(Q);                                           \
+       while (_s1 != NULL) {                                           \
+               _s2 = TAILQ_NEXT(_s1, link);                            \
+               if (_s1->m)                                             \
+                       MBUF_FREE(_s1->m);                              \
+               SIG_FREE(_s1);                                          \
+               _s1 = _s2;                                              \
+       }                                                               \
+       TAILQ_INIT(Q);                                                  \
+    } while (0)
+
+
+/*
+ * Message buffers
+ */
+#define        MBUF_FREE(M)    m_freem(M)
+
+#ifdef SSCFU_DEBUG
+#define        ASSERT(S)       KASSERT(S, (#S))
+#else
+#define        ASSERT(S)
+#endif
diff --git a/sys/netgraph7/atm/sscop/ng_sscop.c b/sys/netgraph7/atm/sscop/ng_sscop.c
new file mode 100644 (file)
index 0000000..3b7e1c8
--- /dev/null
@@ -0,0 +1,883 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Netgraph module for ITU-T Q.2110 SSCOP.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/sscop/ng_sscop.c,v 1.4 2005/08/10 06:25:40 obrien Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/callout.h>
+#include <sys/sbuf.h>
+#include <sys/stdint.h>
+#include <machine/stdarg.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netnatm/saal/sscopdef.h>
+#include <netgraph/atm/ng_sscop.h>
+#include <netgraph/atm/sscop/ng_sscop_cust.h>
+#include <netnatm/saal/sscop.h>
+
+#define DDD printf("%s: %d\n", __func__, __LINE__)
+
+#ifdef SSCOP_DEBUG
+#define VERBOSE(P,M,F)                                                 \
+    do {                                                               \
+       if (sscop_getdebug((P)->sscop) & (M))                           \
+               sscop_verbose F ;                                       \
+    } while(0)
+#else
+#define VERBOSE(P,M,F)
+#endif
+
+MALLOC_DEFINE(M_NG_SSCOP, "netgraph_sscop", "netgraph sscop node");
+
+MODULE_DEPEND(ng_sscop, ngatmbase, 1, 1, 1);
+
+struct stats {
+       uint64_t        in_packets;
+       uint64_t        out_packets;
+       uint64_t        aa_signals;
+       uint64_t        errors;
+       uint64_t        data_delivered;
+       uint64_t        aa_dropped;
+       uint64_t        maa_dropped;
+       uint64_t        maa_signals;
+       uint64_t        in_dropped;
+       uint64_t        out_dropped;
+};
+
+/*
+ * Private data
+ */
+struct priv {
+       hook_p          upper;          /* SAAL interface */
+       hook_p          lower;          /* AAL5 interface */
+       hook_p          manage;         /* management interface */
+
+       struct sscop    *sscop;         /* sscop state */
+       int             enabled;        /* whether the protocol is enabled */
+       int             flow;           /* flow control states */
+       struct stats    stats;          /* sadistics */
+};
+
+/*
+ * Parse PARAM type
+ */
+static const struct ng_parse_struct_field ng_sscop_param_type_info[] = 
+    NG_SSCOP_PARAM_INFO;
+
+static const struct ng_parse_type ng_sscop_param_type = {
+       &ng_parse_struct_type,
+       ng_sscop_param_type_info
+};
+
+/*
+ * Parse a SET PARAM type.
+ */
+static const struct ng_parse_struct_field ng_sscop_setparam_type_info[] =
+    NG_SSCOP_SETPARAM_INFO;
+
+static const struct ng_parse_type ng_sscop_setparam_type = {
+       &ng_parse_struct_type,
+       ng_sscop_setparam_type_info,
+};
+
+/*
+ * Parse a SET PARAM response
+ */
+static const struct ng_parse_struct_field ng_sscop_setparam_resp_type_info[] =
+    NG_SSCOP_SETPARAM_RESP_INFO;
+
+static const struct ng_parse_type ng_sscop_setparam_resp_type = {
+       &ng_parse_struct_type,
+       ng_sscop_setparam_resp_type_info,
+};
+
+static const struct ng_cmdlist ng_sscop_cmdlist[] = {
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_GETPARAM,
+         "getparam",
+         NULL,
+         &ng_sscop_param_type
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_SETPARAM,
+         "setparam",
+         &ng_sscop_setparam_type,
+         &ng_sscop_setparam_resp_type
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_ENABLE,
+         "enable",
+         NULL,
+         NULL
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_DISABLE,
+         "disable",
+         NULL,
+         NULL
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_GETDEBUG,
+         "getdebug",
+         NULL,
+         &ng_parse_hint32_type
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_SETDEBUG,
+         "setdebug",
+         &ng_parse_hint32_type,
+         NULL
+       },
+       {
+         NGM_SSCOP_COOKIE,
+         NGM_SSCOP_GETSTATE,
+         "getstate",
+         NULL,
+         &ng_parse_uint32_type
+       },
+       { 0 }
+};
+
+static ng_constructor_t ng_sscop_constructor;
+static ng_shutdown_t   ng_sscop_shutdown;
+static ng_rcvmsg_t     ng_sscop_rcvmsg;
+static ng_newhook_t    ng_sscop_newhook;
+static ng_disconnect_t ng_sscop_disconnect;
+static ng_rcvdata_t    ng_sscop_rcvlower;
+static ng_rcvdata_t    ng_sscop_rcvupper;
+static ng_rcvdata_t    ng_sscop_rcvmanage;
+
+static int ng_sscop_mod_event(module_t, int, void *);
+
+static struct ng_type ng_sscop_typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_SSCOP_NODE_TYPE,
+       .mod_event =    ng_sscop_mod_event,
+       .constructor =  ng_sscop_constructor,
+       .rcvmsg =       ng_sscop_rcvmsg,
+       .shutdown =     ng_sscop_shutdown,
+       .newhook =      ng_sscop_newhook,
+       .rcvdata =      ng_sscop_rcvlower,
+       .disconnect =   ng_sscop_disconnect,
+       .cmdlist =      ng_sscop_cmdlist,
+};
+NETGRAPH_INIT(sscop, &ng_sscop_typestruct);
+
+static void sscop_send_manage(struct sscop *, void *, enum sscop_maasig,
+       struct SSCOP_MBUF_T *, u_int, u_int);
+static void sscop_send_upper(struct sscop *, void *, enum sscop_aasig,
+       struct SSCOP_MBUF_T *, u_int);
+static void sscop_send_lower(struct sscop *, void *,
+       struct SSCOP_MBUF_T *);
+static void sscop_verbose(struct sscop *, void *, const char *, ...)
+       __printflike(3, 4);
+
+static const struct sscop_funcs sscop_funcs = {
+       sscop_send_manage,
+       sscop_send_upper,
+       sscop_send_lower,
+       sscop_verbose
+};
+
+static void
+sscop_verbose(struct sscop *sscop, void *arg, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       printf("sscop(%p): ", sscop);
+       vprintf(fmt, ap);
+       va_end(ap);
+       printf("\n");
+}
+
+/************************************************************/
+/*
+ * NODE MANAGEMENT
+ */
+static int
+ng_sscop_constructor(node_p node)
+{
+       struct priv *p;
+
+       if ((p = malloc(sizeof(*p), M_NG_SSCOP, M_NOWAIT | M_ZERO)) == NULL)
+               return (ENOMEM);
+
+       if ((p->sscop = sscop_create(node, &sscop_funcs)) == NULL) {
+               free(p, M_NG_SSCOP);
+               return (ENOMEM);
+       }
+       NG_NODE_SET_PRIVATE(node, p);
+
+       /* All data message received by the node are expected to change the
+        * node's state. Therefor we must ensure, that we have a writer lock. */
+       NG_NODE_FORCE_WRITER(node);
+
+       return (0);
+}
+static int
+ng_sscop_shutdown(node_p node)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       sscop_destroy(priv->sscop);
+
+       free(priv, M_NG_SSCOP);
+       NG_NODE_SET_PRIVATE(node, NULL);
+
+       NG_NODE_UNREF(node);
+
+       return (0);
+}
+
+/************************************************************/
+/*
+ * CONTROL MESSAGES
+ */
+/*
+ * Flow control message from upper layer.
+ * This is very experimental:
+ * If we get a message from the upper layer, that somebody has passed its
+ * high water mark, we stop updating the receive window.
+ * If we get a low watermark passed, then we raise the window up
+ * to max - current.
+ * If we get a queue status and it indicates a current below the
+ * high watermark, we unstop window updates (if they are stopped) and
+ * raise the window to highwater - current.
+ */
+static int
+flow_upper(node_p node, struct ng_mesg *msg)
+{
+       struct ngm_queue_state *q;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       u_int window, space;
+
+       if (msg->header.arglen != sizeof(struct ngm_queue_state))
+               return (EINVAL);
+       q = (struct ngm_queue_state *)msg->data;
+
+       switch (msg->header.cmd) {
+
+         case NGM_HIGH_WATER_PASSED:
+               if (priv->flow) {
+                       VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
+                           "flow control stopped"));
+                       priv->flow = 0;
+               }
+               break;
+
+         case NGM_LOW_WATER_PASSED:
+               window = sscop_window(priv->sscop, 0);
+               space = q->max_queuelen_packets - q->current;
+               if (space > window) {
+                       VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
+                           "flow control opened window by %u messages",
+                           space - window));
+                       (void)sscop_window(priv->sscop, space - window);
+               }
+               priv->flow = 1;
+               break;
+
+         case NGM_SYNC_QUEUE_STATE:
+               if (q->high_watermark <= q->current)
+                       break;
+               window = sscop_window(priv->sscop, 0);
+               if (priv->flow)
+                       space = q->max_queuelen_packets - q->current;
+               else
+                       space = q->high_watermark - q->current;
+               if (space > window) {
+                       VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
+                           "flow control opened window by %u messages",
+                           space - window));
+                       (void)sscop_window(priv->sscop, space - window);
+               }
+               priv->flow = 1;
+               break;
+
+         default:
+               return (EINVAL);
+       }
+       return (0);
+}
+
+static int
+flow_lower(node_p node, struct ng_mesg *msg)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if (msg->header.arglen != sizeof(struct ngm_queue_state))
+               return (EINVAL);
+
+       switch (msg->header.cmd) {
+
+         case NGM_HIGH_WATER_PASSED:
+               sscop_setbusy(priv->sscop, 1);
+               break;
+
+         case NGM_LOW_WATER_PASSED:
+               sscop_setbusy(priv->sscop, 1);
+               break;
+
+         default:
+               return (EINVAL);
+       }
+       return (0);
+}
+
+/*
+ * Produce a readable status description
+ */
+static int
+text_status(node_p node, struct priv *priv, char *arg, u_int len)
+{
+       struct sbuf sbuf;
+
+       sbuf_new(&sbuf, arg, len, 0);
+
+       if (priv->upper)
+               sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
+                   NG_HOOK_NAME(priv->upper),
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
+       else
+               sbuf_printf(&sbuf, "upper hook: <not connected>\n");
+
+       if (priv->lower)
+               sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
+                   NG_HOOK_NAME(priv->lower),
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
+       else
+               sbuf_printf(&sbuf, "lower hook: <not connected>\n");
+
+       if (priv->manage)
+               sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
+                   NG_HOOK_NAME(priv->manage),
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
+       else
+               sbuf_printf(&sbuf, "manage hook: <not connected>\n");
+
+       sbuf_printf(&sbuf, "sscop state: %s\n",
+           !priv->enabled ? "<disabled>" :
+           sscop_statename(sscop_getstate(priv->sscop)));
+
+       sbuf_printf(&sbuf, "input packets:  %ju\n",
+           (uintmax_t)priv->stats.in_packets);
+       sbuf_printf(&sbuf, "input dropped:  %ju\n",
+           (uintmax_t)priv->stats.in_dropped);
+       sbuf_printf(&sbuf, "output packets: %ju\n",
+           (uintmax_t)priv->stats.out_packets);
+       sbuf_printf(&sbuf, "output dropped: %ju\n",
+           (uintmax_t)priv->stats.out_dropped);
+       sbuf_printf(&sbuf, "aa signals:     %ju\n",
+           (uintmax_t)priv->stats.aa_signals);
+       sbuf_printf(&sbuf, "aa dropped:     %ju\n",
+           (uintmax_t)priv->stats.aa_dropped);
+       sbuf_printf(&sbuf, "maa signals:    %ju\n",
+           (uintmax_t)priv->stats.maa_signals);
+       sbuf_printf(&sbuf, "maa dropped:    %ju\n",
+           (uintmax_t)priv->stats.maa_dropped);
+       sbuf_printf(&sbuf, "errors:         %ju\n",
+           (uintmax_t)priv->stats.errors);
+       sbuf_printf(&sbuf, "data delivered: %ju\n",
+           (uintmax_t)priv->stats.data_delivered);
+       sbuf_printf(&sbuf, "window:         %u\n",
+           sscop_window(priv->sscop, 0));
+
+       sbuf_finish(&sbuf);
+       return (sbuf_len(&sbuf));
+}
+
+
+/*
+ * Control message received.
+ */
+static int
+ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ng_mesg *resp = NULL;
+       struct ng_mesg *msg;
+       int error = 0;
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+
+         case NGM_GENERIC_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_TEXT_STATUS:
+                       NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+
+                       resp->header.arglen = text_status(node, priv,
+                           (char *)resp->data, resp->header.arglen) + 1;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         case NGM_FLOW_COOKIE:
+               if (priv->enabled && lasthook != NULL) {
+                       if (lasthook == priv->upper)
+                               error = flow_upper(node, msg);
+                       else if (lasthook == priv->lower)
+                               error = flow_lower(node, msg);
+               }
+               break;
+
+         case NGM_SSCOP_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_SSCOP_GETPARAM:
+                   {
+                       struct sscop_param *p;
+
+                       NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       p = (struct sscop_param *)resp->data;
+                       sscop_getparam(priv->sscop, p);
+                       break;
+                   }
+
+                 case NGM_SSCOP_SETPARAM:
+                   {
+                       struct ng_sscop_setparam *arg;
+                       struct ng_sscop_setparam_resp *p;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (priv->enabled) {
+                               error = EISCONN;
+                               break;
+                       }
+                       arg = (struct ng_sscop_setparam *)msg->data;
+                       NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       p = (struct ng_sscop_setparam_resp *)resp->data;
+                       p->mask = arg->mask;
+                       p->error = sscop_setparam(priv->sscop,
+                           &arg->param, &p->mask);
+                       break;
+                   }
+
+                 case NGM_SSCOP_ENABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (priv->enabled) {
+                               error = EBUSY;
+                               break;
+                       }
+                       priv->enabled = 1;
+                       priv->flow = 1;
+                       memset(&priv->stats, 0, sizeof(priv->stats));
+                       break;
+
+                 case NGM_SSCOP_DISABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (!priv->enabled) {
+                               error = ENOTCONN;
+                               break;
+                       }
+                       priv->enabled = 0;
+                       sscop_reset(priv->sscop);
+                       break;
+
+                 case NGM_SSCOP_GETDEBUG:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       *(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
+                       break;
+
+                 case NGM_SSCOP_SETDEBUG:
+                       if (msg->header.arglen != sizeof(u_int32_t)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
+                       break;
+
+                 case NGM_SSCOP_GETSTATE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       *(u_int32_t *)resp->data =
+                           priv->enabled ? (sscop_getstate(priv->sscop) + 1)
+                                         : 0;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         default:
+               error = EINVAL;
+               break;
+       }
+
+       NG_RESPOND_MSG(error, node, item, resp);
+       NG_FREE_MSG(msg);
+
+       return (error);
+}
+
+/************************************************************/
+/*
+ * HOOK MANAGEMENT
+ */
+static int
+ng_sscop_newhook(node_p node, hook_p hook, const char *name)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if(strcmp(name, "upper") == 0) {
+               priv->upper = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
+       } else if(strcmp(name, "lower") == 0) {
+               priv->lower = hook;
+       } else if(strcmp(name, "manage") == 0) {
+               priv->manage = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
+       } else
+               return EINVAL;
+
+       return 0;
+}
+static int
+ng_sscop_disconnect(hook_p hook)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if(hook == priv->upper)
+               priv->upper = NULL;
+       else if(hook == priv->lower)
+               priv->lower = NULL;
+       else if(hook == priv->manage)
+               priv->manage = NULL;
+
+       if(NG_NODE_NUMHOOKS(node) == 0) {
+               if(NG_NODE_IS_VALID(node))
+                       ng_rmnode_self(node);
+       } else {
+               /*
+                * Imply a release request, if the upper layer is
+                * disconnected.
+                */
+               if(priv->upper == NULL && priv->lower != NULL &&
+                  priv->enabled &&
+                  sscop_getstate(priv->sscop) != SSCOP_IDLE) {
+                       sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
+                           NULL, 0);
+               }
+       }
+       return 0;
+}
+
+/************************************************************/
+/*
+ * DATA
+ */
+static int
+ng_sscop_rcvlower(hook_p hook, item_p item)
+{
+       struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct mbuf *m;
+
+       if (!priv->enabled) {
+               NG_FREE_ITEM(item);
+               return EINVAL;
+       }
+
+       /*
+        * If we are disconnected at the upper layer and in the IDLE
+        * state, drop any incoming packet.
+        */
+       if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
+               NGI_GET_M(item, m);
+               priv->stats.in_packets++;
+               sscop_input(priv->sscop, m);
+       } else {
+               priv->stats.in_dropped++;
+       }
+       NG_FREE_ITEM(item);
+
+       return (0);
+}
+
+static void
+sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
+{
+       node_p node = (node_p)p;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       int error;
+
+       if (priv->lower == NULL) {
+               m_freem(m);
+               priv->stats.out_dropped++;
+               return;
+       }
+
+       priv->stats.out_packets++;
+       NG_SEND_DATA_ONLY(error, priv->lower, m);
+}
+
+static int
+ng_sscop_rcvupper(hook_p hook, item_p item)
+{
+       struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct sscop_arg a;
+       struct mbuf *m;
+
+       if (!priv->enabled) {
+               NG_FREE_ITEM(item);
+               return (EINVAL);
+       }
+
+       /*
+        * If the lower layer is not connected allow to proceed.
+        * The lower layer sending function will drop outgoing frames,
+        * and the sscop will timeout any establish requests.
+        */
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if (!(m->m_flags & M_PKTHDR)) {
+               printf("no pkthdr\n");
+               m_freem(m);
+               return (EINVAL);
+       }
+       if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
+               return (ENOBUFS);
+       bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
+       m_adj(m, sizeof(a));
+
+       return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
+}
+
+static void
+sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
+    struct SSCOP_MBUF_T *m, u_int arg)
+{
+       node_p node = (node_p)p;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       int error;
+       struct sscop_arg *a;
+
+       if (sig == SSCOP_DATA_indication && priv->flow)
+               sscop_window(priv->sscop, 1);
+
+       if (priv->upper == NULL) {
+               if (m != NULL)
+                       m_freem(m);
+               priv->stats.aa_dropped++;
+               return;
+       }
+
+       priv->stats.aa_signals++;
+       if (sig == SSCOP_DATA_indication)
+               priv->stats.data_delivered++;
+
+       if (m == NULL) {
+               MGETHDR(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       return;
+               m->m_len = sizeof(struct sscop_arg);
+               m->m_pkthdr.len = m->m_len;
+       } else {
+               M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
+               if (m == NULL)
+                       return;
+       }
+       a = mtod(m, struct sscop_arg *);
+       a->sig = sig;
+       a->arg = arg;
+
+       NG_SEND_DATA_ONLY(error, priv->upper, m);
+}
+
+static int
+ng_sscop_rcvmanage(hook_p hook, item_p item)
+{
+       struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+       struct sscop_marg a;
+       struct mbuf *m;
+
+       if (!priv->enabled) {
+               NG_FREE_ITEM(item);
+               return (EINVAL);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
+               return (ENOBUFS);
+       bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
+       m_adj(m, sizeof(a));
+
+       return (sscop_maasig(priv->sscop, a.sig, m));
+}
+
+static void
+sscop_send_manage(struct sscop *sscop, void *p, enum sscop_maasig sig,
+    struct SSCOP_MBUF_T *m, u_int err, u_int cnt)
+{
+       node_p node = (node_p)p;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       int error;
+       struct sscop_merr *e;
+       struct sscop_marg *a;
+
+       if (priv->manage == NULL) {
+               if (m != NULL)
+                       m_freem(m);
+               priv->stats.maa_dropped++;
+               return;
+       }
+
+       if (sig == SSCOP_MERROR_indication) {
+               MGETHDR(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       return;
+               m->m_len = sizeof(*e);
+               m->m_pkthdr.len = m->m_len;
+               e = mtod(m, struct sscop_merr *);
+               e->sig = sig;
+               e->err = err;
+               e->cnt = cnt;
+               priv->stats.errors++;
+       } else if (m == NULL) {
+               MGETHDR(m, M_NOWAIT, MT_DATA);
+               if (m == NULL)
+                       return;
+               m->m_len = sizeof(*a);
+               m->m_pkthdr.len = m->m_len;
+               a = mtod(m, struct sscop_marg *);
+               a->sig = sig;
+               priv->stats.maa_signals++;
+       } else {
+               M_PREPEND(m, sizeof(*a), M_NOWAIT);
+               if (m == NULL)
+                       return;
+               a = mtod(m, struct sscop_marg *);
+               a->sig = sig;
+               priv->stats.maa_signals++;
+       }
+
+       NG_SEND_DATA_ONLY(error, priv->manage, m);
+}
+
+/************************************************************/
+/*
+ * INITIALISATION
+ */
+
+/*
+ * Loading and unloading of node type
+ */
+static int
+ng_sscop_mod_event(module_t mod, int event, void *data)
+{
+       int s;
+       int error = 0;
+
+       s = splnet();
+       switch (event) {
+
+         case MOD_LOAD:
+               break;
+
+         case MOD_UNLOAD:
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       splx(s);
+       return (error);
+}
diff --git a/sys/netgraph7/atm/sscop/ng_sscop_cust.h b/sys/netgraph7/atm/sscop/ng_sscop_cust.h
new file mode 100644 (file)
index 0000000..d8f073b
--- /dev/null
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/sscop/ng_sscop_cust.h,v 1.5 2005/01/07 01:45:41 imp Exp $
+ *
+ * Customisation of the SSCOP code to ng_sscop.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <machine/stdarg.h>
+
+#include <netnatm/saal/sscopdef.h>
+
+/*
+ * Allocate zeroed or non-zeroed memory of some size and cast it.
+ * Return NULL on failure.
+ */
+#ifndef SSCOP_DEBUG
+
+#define        MEMINIT() \
+       MALLOC_DECLARE(M_NG_SSCOP); \
+       DECL_MSGQ_GET \
+       DECL_SIGQ_GET \
+       DECL_MBUF_ALLOC
+
+#define        MEMZALLOC(PTR, CAST, SIZE) \
+       ((PTR) = (CAST)malloc((SIZE), M_NG_SSCOP, M_NOWAIT | M_ZERO))
+#define        MEMFREE(PTR) \
+       free((PTR), M_NG_SSCOP)
+
+#define        MSG_ALLOC(PTR) \
+       MEMZALLOC(PTR, struct sscop_msg *, sizeof(struct sscop_msg))
+#define        MSG_FREE(PTR) \
+       MEMFREE(PTR)
+
+#define        SIG_ALLOC(PTR) \
+       MEMZALLOC(PTR, struct sscop_sig *, sizeof(struct sscop_sig))
+#define        SIG_FREE(PTR) \
+       MEMFREE(PTR)
+
+#else
+
+#define        MEMINIT()                                                       \
+       MALLOC_DEFINE(M_NG_SSCOP_INS, "sscop_ins", "SSCOP instances");  \
+       MALLOC_DEFINE(M_NG_SSCOP_MSG, "sscop_msg", "SSCOP buffers");    \
+       MALLOC_DEFINE(M_NG_SSCOP_SIG, "sscop_sig", "SSCOP signals");    \
+       DECL_MSGQ_GET \
+       DECL_SIGQ_GET \
+       DECL_MBUF_ALLOC
+
+#define        MEMZALLOC(PTR, CAST, SIZE)                                      \
+       ((PTR) = (CAST)malloc((SIZE), M_NG_SSCOP_INS, M_NOWAIT | M_ZERO))
+#define        MEMFREE(PTR)                                                    \
+       free((PTR), M_NG_SSCOP_INS)
+
+#define        MSG_ALLOC(PTR)                                                  \
+       ((PTR) = malloc(sizeof(struct sscop_msg),                       \
+           M_NG_SSCOP_MSG, M_NOWAIT | M_ZERO))
+#define        MSG_FREE(PTR)                                                   \
+       free((PTR), M_NG_SSCOP_MSG)
+
+#define        SIG_ALLOC(PTR)                                                  \
+       ((PTR) = malloc(sizeof(struct sscop_sig),                       \
+           M_NG_SSCOP_SIG, M_NOWAIT | M_ZERO))
+#define        SIG_FREE(PTR)                                                   \
+       free((PTR), M_NG_SSCOP_SIG)
+
+#endif
+
+/*
+ * Timer support.
+ */
+typedef struct callout sscop_timer_t;
+#define        TIMER_INIT(S, T)        ng_callout_init(&(S)->t_##T)
+#define        TIMER_STOP(S,T) do {                                            \
+       ng_uncallout(&(S)->t_##T, (S)->aarg);                           \
+    } while (0)
+#define        TIMER_RESTART(S, T) do {                                        \
+       TIMER_STOP(S, T);                                               \
+       ng_callout(&(S)->t_##T, (S)->aarg, NULL,                        \
+           hz * (S)->timer##T / 1000, T##_func, (S), 0);               \
+    } while (0)
+#define        TIMER_ISACT(S, T) ((S)->t_##T.c_flags & (CALLOUT_PENDING))
+
+/*
+ * This assumes, that the user argument is the node pointer.
+ */
+#define        TIMER_FUNC(T,N)                                                 \
+static void                                                            \
+T##_func(node_p node, hook_p hook, void *arg1, int arg2)               \
+{                                                                      \
+       struct sscop *sscop = arg1;                                     \
+                                                                       \
+       VERBOSE(sscop, SSCOP_DBG_TIMER, (sscop, sscop->aarg,            \
+           "timer_" #T " expired"));                                   \
+       sscop_signal(sscop, SIG_T_##N, NULL);                           \
+}
+
+
+/*
+ * Message queues
+ */
+typedef TAILQ_ENTRY(sscop_msg) sscop_msgq_link_t;
+typedef TAILQ_HEAD(sscop_msgq, sscop_msg) sscop_msgq_head_t;
+#define        MSGQ_EMPTY(Q)           TAILQ_EMPTY(Q)
+#define        MSGQ_INIT(Q)            TAILQ_INIT(Q)
+#define        MSGQ_FOREACH(P, Q)      TAILQ_FOREACH(P, Q, link)
+#define        MSGQ_REMOVE(Q, M)       TAILQ_REMOVE(Q, M, link)
+#define        MSGQ_INSERT_BEFORE(B, M) TAILQ_INSERT_BEFORE(B, M, link)
+#define        MSGQ_APPEND(Q, M)       TAILQ_INSERT_TAIL(Q, M, link)
+#define        MSGQ_PEEK(Q)            TAILQ_FIRST((Q))
+
+#define        MSGQ_GET(Q) ng_sscop_msgq_get((Q))
+
+#define DECL_MSGQ_GET                                                  \
+static __inline struct sscop_msg *                                     \
+ng_sscop_msgq_get(struct sscop_msgq *q)                                        \
+{                                                                      \
+       struct sscop_msg *m;                                            \
+                                                                       \
+       m = TAILQ_FIRST(q);                                             \
+       if (m != NULL)                                                  \
+               TAILQ_REMOVE(q, m, link);                               \
+       return (m);                                                     \
+}
+
+#define        MSGQ_CLEAR(Q)                                                   \
+       do {                                                            \
+               struct sscop_msg *_m1, *_m2;                            \
+                                                                       \
+               _m1 = TAILQ_FIRST(Q);                                   \
+               while (_m1 != NULL) {                                   \
+                       _m2 = TAILQ_NEXT(_m1, link);                    \
+                       SSCOP_MSG_FREE(_m1);                            \
+                       _m1 = _m2;                                      \
+               }                                                       \
+               TAILQ_INIT((Q));                                        \
+       } while (0)
+
+/*
+ * Signal queues
+ */
+typedef TAILQ_ENTRY(sscop_sig) sscop_sigq_link_t;
+typedef TAILQ_HEAD(sscop_sigq, sscop_sig) sscop_sigq_head_t;
+#define        SIGQ_INIT(Q)            TAILQ_INIT(Q)
+#define        SIGQ_APPEND(Q, S)       TAILQ_INSERT_TAIL(Q, S, link)
+#define        SIGQ_EMPTY(Q)           TAILQ_EMPTY(Q)
+
+#define        SIGQ_GET(Q)     ng_sscop_sigq_get((Q))
+#define        DECL_SIGQ_GET                                                   \
+static __inline struct sscop_sig *                                     \
+ng_sscop_sigq_get(struct sscop_sigq *q)                                        \
+{                                                                      \
+       struct sscop_sig *s;                                            \
+                                                                       \
+       s = TAILQ_FIRST(q);                                             \
+       if (s != NULL)                                                  \
+               TAILQ_REMOVE(q, s, link);                               \
+       return (s);                                                     \
+}
+
+#define        SIGQ_MOVE(F, T)                                                 \
+    do {                                                               \
+       struct sscop_sig *_s;                                           \
+                                                                       \
+       while (!TAILQ_EMPTY(F)) {                                       \
+               _s = TAILQ_FIRST(F);                                    \
+               TAILQ_REMOVE(F, _s, link);                              \
+               TAILQ_INSERT_TAIL(T, _s, link);                         \
+       }                                                               \
+    } while (0)
+
+#define        SIGQ_PREPEND(F, T)                                              \
+    do {                                                               \
+       struct sscop_sig *_s;                                           \
+                                                                       \
+       while (!TAILQ_EMPTY(F)) {                                       \
+               _s = TAILQ_LAST(F, sscop_sigq);                         \
+               TAILQ_REMOVE(F, _s, link);                              \
+               TAILQ_INSERT_HEAD(T, _s, link);                         \
+       }                                                               \
+    } while (0)
+
+#define        SIGQ_CLEAR(Q)                                                   \
+    do {                                                               \
+       struct sscop_sig *_s1, *_s2;                                    \
+                                                                       \
+       _s1 = TAILQ_FIRST(Q);                                           \
+       while (_s1 != NULL) {                                           \
+               _s2 = TAILQ_NEXT(_s1, link);                            \
+               SSCOP_MSG_FREE(_s1->msg);                               \
+               SIG_FREE(_s1);                                          \
+               _s1 = _s2;                                              \
+       }                                                               \
+       TAILQ_INIT(Q);                                                  \
+    } while (0)
+
+/*
+ * Message buffers
+ */
+#define        MBUF_FREE(M)    do { if ((M)) m_freem((M)); } while(0)
+#define        MBUF_DUP(M)     m_copypacket((M), M_NOWAIT)
+#define        MBUF_LEN(M)     ((size_t)(M)->m_pkthdr.len)
+
+/*
+ * Return the i-th word counted from the end of the buffer.
+ * i=-1 will return the last 32bit word, i=-2 the 2nd last.
+ * Assumes that there is enough space.
+ */
+#define        MBUF_TRAIL32(M ,I) ng_sscop_mbuf_trail32((M), (I))
+
+static uint32_t __inline       
+ng_sscop_mbuf_trail32(const struct mbuf *m, int i)
+{
+       uint32_t w;
+
+       m_copydata(m, m->m_pkthdr.len + 4 * i, 4, (caddr_t)&w);
+       return (ntohl(w));
+}
+
+/*
+ * Strip 32bit value from the end
+ */
+#define        MBUF_STRIP32(M) ng_sscop_mbuf_strip32((M))
+
+static uint32_t __inline
+ng_sscop_mbuf_strip32(struct mbuf *m)
+{
+       uint32_t w;
+
+       m_copydata(m, m->m_pkthdr.len - 4, 4, (caddr_t)&w);
+       m_adj(m, -4);
+       return (ntohl(w));
+}
+
+#define        MBUF_GET32(M) ng_sscop_mbuf_get32((M))
+
+static uint32_t __inline
+ng_sscop_mbuf_get32(struct mbuf *m)
+{
+       uint32_t w;
+
+       m_copydata(m, 0, 4, (caddr_t)&w);
+       m_adj(m, 4);
+       return (ntohl(w));
+}
+
+/*
+ * Append a 32bit value to an mbuf. Failures are ignored.
+ */
+#define        MBUF_APPEND32(M, W)                                             \
+     do {                                                              \
+       uint32_t _w = (W);                                              \
+                                                                       \
+       _w = htonl(_w);                                                 \
+       m_copyback((M), (M)->m_pkthdr.len, 4, (caddr_t)&_w);            \
+    } while (0)
+
+/*
+ * Pad a message to a multiple of four byte and return the amount of padding
+ * Failures are ignored.
+ */
+#define        MBUF_PAD4(M) ng_sscop_mbuf_pad4((M))
+
+static u_int __inline
+ng_sscop_mbuf_pad4(struct mbuf *m)
+{
+       static u_char pad[4] = { 0, 0, 0, 0 };
+       int len = m->m_pkthdr.len;
+       int npad = 3 - ((len + 3) & 3);
+
+       if (npad != 0)
+               m_copyback(m, len, npad, (caddr_t)pad);
+       return (npad);
+}
+
+#define        MBUF_UNPAD(M, P) do { if( (P) > 0) m_adj((M), -(P)); } while (0)
+
+/*
+ * Allocate a message that will probably hold N bytes.
+ */
+#define        MBUF_ALLOC(N) ng_sscop_mbuf_alloc((N))
+
+#define        DECL_MBUF_ALLOC                                                 \
+static __inline struct mbuf *                                          \
+ng_sscop_mbuf_alloc(size_t n)                                          \
+{                                                                      \
+       struct mbuf *m;                                                 \
+                                                                       \
+       MGETHDR(m, M_NOWAIT, MT_DATA);                                  \
+       if (m != NULL) {                                                \
+               m->m_len = 0;                                           \
+               m->m_pkthdr.len = 0;                                    \
+               if (n > MHLEN) {                                        \
+                       MCLGET(m, M_NOWAIT);                            \
+                       if (!(m->m_flags & M_EXT)){                     \
+                               m_free(m);                              \
+                               m = NULL;                               \
+                       }                                               \
+               }                                                       \
+       }                                                               \
+       return (m);                                                     \
+}
+
+#ifdef SSCOP_DEBUG
+#define        ASSERT(X)       KASSERT(X, (#X))
+#else
+#define        ASSERT(X)
+#endif
diff --git a/sys/netgraph7/atm/uni/ng_uni.c b/sys/netgraph7/atm/uni/ng_uni.c
new file mode 100644 (file)
index 0000000..3eb0584
--- /dev/null
@@ -0,0 +1,931 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Netgraph module for ATM-Forum UNI 4.0 signalling
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netgraph/atm/uni/ng_uni.c,v 1.6 2005/10/31 15:41:26 rwatson Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/callout.h>
+#include <sys/sbuf.h>
+#include <machine/stdarg.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netnatm/unimsg.h>
+#include <netnatm/msg/unistruct.h>
+#include <netgraph/atm/ngatmbase.h>
+#include <netnatm/saal/sscopdef.h>
+#include <netnatm/saal/sscfudef.h>
+#include <netgraph/atm/uni/ng_uni_cust.h>
+#include <netnatm/sig/uni.h>
+#include <netnatm/sig/unisig.h>
+#include <netgraph/atm/ng_sscop.h>
+#include <netgraph/atm/ng_sscfu.h>
+#include <netgraph/atm/ng_uni.h>
+
+MALLOC_DEFINE(M_NG_UNI, "netgraph_uni_node", "netgraph uni node");
+MALLOC_DEFINE(M_UNI, "netgraph_uni_data", "uni protocol data");
+
+MODULE_DEPEND(ng_uni, ngatmbase, 1, 1, 1);
+
+/*
+ * Private node data
+ */
+struct priv {
+       hook_p  upper;
+       hook_p  lower;
+       struct uni *uni;
+       int     enabled;
+};
+
+/* UNI CONFIG MASK */
+static const struct ng_parse_struct_field ng_uni_config_mask_type_info[] =
+       NGM_UNI_CONFIG_MASK_INFO;
+static const struct ng_parse_type ng_uni_config_mask_type = {
+       &ng_parse_struct_type,
+       ng_uni_config_mask_type_info
+};
+
+/* UNI_CONFIG */
+static const struct ng_parse_struct_field ng_uni_config_type_info[] =
+       NGM_UNI_CONFIG_INFO;
+static const struct ng_parse_type ng_uni_config_type = {
+       &ng_parse_struct_type,
+       ng_uni_config_type_info
+};
+
+/* SET CONFIG */
+static const struct ng_parse_struct_field ng_uni_set_config_type_info[] =
+       NGM_UNI_SET_CONFIG_INFO;
+static const struct ng_parse_type ng_uni_set_config_type = {
+       &ng_parse_struct_type,
+       ng_uni_set_config_type_info
+};
+
+/*
+ * Parse DEBUG
+ */
+static const struct ng_parse_fixedarray_info ng_uni_debuglevel_type_info =
+    NGM_UNI_DEBUGLEVEL_INFO;
+static const struct ng_parse_type ng_uni_debuglevel_type = {
+       &ng_parse_fixedarray_type,
+       &ng_uni_debuglevel_type_info
+};
+static const struct ng_parse_struct_field ng_uni_debug_type_info[] =
+    NGM_UNI_DEBUG_INFO;
+static const struct ng_parse_type ng_uni_debug_type = {
+       &ng_parse_struct_type,
+       ng_uni_debug_type_info
+};
+
+/*
+ * Command list
+ */
+static const struct ng_cmdlist ng_uni_cmdlist[] = {
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_GETDEBUG,
+         "getdebug",
+         NULL,
+         &ng_uni_debug_type
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_SETDEBUG,
+         "setdebug",
+         &ng_uni_debug_type,
+         NULL
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_GET_CONFIG,
+         "get_config",
+         NULL,
+         &ng_uni_config_type
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_SET_CONFIG,
+         "set_config",
+         &ng_uni_set_config_type,
+         &ng_uni_config_mask_type,
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_ENABLE,
+         "enable",
+         NULL,
+         NULL,
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_DISABLE,
+         "disable",
+         NULL,
+         NULL,
+       },
+       {
+         NGM_UNI_COOKIE,
+         NGM_UNI_GETSTATE,
+         "getstate",
+         NULL,
+         &ng_parse_uint32_type
+       },
+       { 0 }
+};
+
+/*
+ * Netgraph module data
+ */
+static ng_constructor_t ng_uni_constructor;
+static ng_shutdown_t   ng_uni_shutdown;
+static ng_rcvmsg_t     ng_uni_rcvmsg;
+static ng_newhook_t    ng_uni_newhook;
+static ng_disconnect_t ng_uni_disconnect;
+static ng_rcvdata_t    ng_uni_rcvlower;
+static ng_rcvdata_t    ng_uni_rcvupper;
+
+static int ng_uni_mod_event(module_t, int, void *);
+
+static struct ng_type ng_uni_typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_UNI_NODE_TYPE,
+       .mod_event =    ng_uni_mod_event,
+       .constructor =  ng_uni_constructor,
+       .rcvmsg =       ng_uni_rcvmsg,
+       .shutdown =     ng_uni_shutdown,
+       .newhook =      ng_uni_newhook,
+       .rcvdata =      ng_uni_rcvlower,
+       .disconnect =   ng_uni_disconnect,
+       .cmdlist =      ng_uni_cmdlist,
+};
+NETGRAPH_INIT(uni, &ng_uni_typestruct);
+
+static void uni_uni_output(struct uni *, void *, enum uni_sig, u_int32_t,
+    struct uni_msg *);
+static void uni_saal_output(struct uni *, void *, enum saal_sig,
+    struct uni_msg *);
+static void uni_verbose(struct uni *, void *, u_int, const char *, ...)
+    __printflike(4, 5);
+static void uni_do_status(struct uni *, void *, void *, const char *, ...)
+    __printflike(4, 5);
+
+static const struct uni_funcs uni_funcs = {
+       uni_uni_output,
+       uni_saal_output,
+       uni_verbose,
+       uni_do_status
+};
+
+/************************************************************/
+/*
+ * NODE MANAGEMENT
+ */
+static int
+ng_uni_constructor(node_p node)
+{
+       struct priv *priv;
+
+       if ((priv = malloc(sizeof(*priv), M_NG_UNI, M_NOWAIT | M_ZERO)) == NULL)
+               return (ENOMEM);
+
+       if ((priv->uni = uni_create(node, &uni_funcs)) == NULL) {
+               free(priv, M_NG_UNI);
+               return (ENOMEM);
+       }
+
+       NG_NODE_SET_PRIVATE(node, priv);
+       NG_NODE_FORCE_WRITER(node);
+
+       return (0);
+}
+
+static int
+ng_uni_shutdown(node_p node)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       uni_destroy(priv->uni);
+
+       free(priv, M_NG_UNI);
+       NG_NODE_SET_PRIVATE(node, NULL);
+
+       NG_NODE_UNREF(node);
+
+       return (0);
+}
+
+/************************************************************/
+/*
+ * CONTROL MESSAGES
+ */
+static void
+uni_do_status(struct uni *uni, void *uarg, void *sbuf, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       sbuf_printf(sbuf, fmt, ap);
+       va_end(ap);
+}
+
+static int
+text_status(node_p node, struct priv *priv, char *buf, u_int len)
+{
+       struct sbuf sbuf;
+       u_int f;
+
+       sbuf_new(&sbuf, buf, len, 0);
+
+       if (priv->lower != NULL)
+               sbuf_printf(&sbuf, "lower hook: connected to %s:%s\n",
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
+       else
+               sbuf_printf(&sbuf, "lower hook: <not connected>\n");
+
+       if (priv->upper != NULL)
+               sbuf_printf(&sbuf, "upper hook: connected to %s:%s\n",
+                   NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
+                   NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
+       else
+               sbuf_printf(&sbuf, "upper hook: <not connected>\n");
+
+       sbuf_printf(&sbuf, "debugging:");
+       for (f = 0; f < UNI_MAXFACILITY; f++)
+               if (uni_get_debug(priv->uni, f) != 0)
+                       sbuf_printf(&sbuf, " %s=%u", uni_facname(f),
+                           uni_get_debug(priv->uni, f));
+       sbuf_printf(&sbuf, "\n");
+
+       if (priv->uni)
+               uni_status(priv->uni, &sbuf);
+
+       sbuf_finish(&sbuf);
+       return (sbuf_len(&sbuf));
+}
+
+static int
+ng_uni_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct ng_mesg *resp = NULL;
+       struct ng_mesg *msg;
+       int error = 0;
+       u_int i;
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+
+         case NGM_GENERIC_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_TEXT_STATUS:
+                       NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+
+                       resp->header.arglen = text_status(node, priv,
+                           (char *)resp->data, resp->header.arglen) + 1;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         case NGM_UNI_COOKIE:
+               switch (msg->header.cmd) {
+
+                 case NGM_UNI_SETDEBUG:
+                   {
+                       struct ngm_uni_debug *arg;
+
+                       if (msg->header.arglen > sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_uni_debug *)msg->data;
+                       for (i = 0; i < UNI_MAXFACILITY; i++)
+                               uni_set_debug(priv->uni, i, arg->level[i]);
+                       break;
+                   }
+
+                 case NGM_UNI_GETDEBUG:
+                   {
+                       struct ngm_uni_debug *arg;
+
+                       NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       arg = (struct ngm_uni_debug *)resp->data;
+                       for (i = 0; i < UNI_MAXFACILITY; i++)
+                               arg->level[i] = uni_get_debug(priv->uni, i);
+                       break;
+                   }
+
+                 case NGM_UNI_GET_CONFIG:
+                   {
+                       struct uni_config *config;
+
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       config = (struct uni_config *)resp->data;
+                       uni_get_config(priv->uni, config);
+
+                       break;
+                   }
+
+                 case NGM_UNI_SET_CONFIG:
+                   {
+                       struct ngm_uni_set_config *arg;
+                       struct ngm_uni_config_mask *mask;
+
+                       if (msg->header.arglen != sizeof(*arg)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       arg = (struct ngm_uni_set_config *)msg->data;
+
+                       NG_MKRESPONSE(resp, msg, sizeof(*mask), M_NOWAIT);
+                       if (resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       mask = (struct ngm_uni_config_mask *)resp->data;
+
+                       *mask = arg->mask;
+
+                       uni_set_config(priv->uni, &arg->config,
+                           &mask->mask, &mask->popt_mask, &mask->option_mask);
+
+                       break;
+                   }
+
+                 case NGM_UNI_ENABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (priv->enabled) {
+                               error = EISCONN;
+                               break;
+                       }
+                       priv->enabled = 1;
+                       break;
+
+                 case NGM_UNI_DISABLE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       if (!priv->enabled) {
+                               error = ENOTCONN;
+                               break;
+                       }
+                       priv->enabled = 0;
+                       uni_reset(priv->uni);
+                       break;
+
+                 case NGM_UNI_GETSTATE:
+                       if (msg->header.arglen != 0) {
+                               error = EINVAL;
+                               break;
+                       }
+                       NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
+                       if(resp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+                       *(u_int32_t *)resp->data =
+                           priv->enabled ? (uni_getcustate(priv->uni) + 1)
+                                         : 0;
+                       break;
+
+                 default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+         default:
+               error = EINVAL;
+               break;
+       }
+
+       NG_RESPOND_MSG(error, node, item, resp);
+       NG_FREE_MSG(msg);
+       return (error);
+}
+
+/************************************************************/
+/*
+ * HOOK MANAGEMENT
+ */
+static int
+ng_uni_newhook(node_p node, hook_p hook, const char *name)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if (strcmp(name, "lower") == 0) {
+               priv->lower = hook;
+       } else if(strcmp(name, "upper") == 0) {
+               priv->upper = hook;
+               NG_HOOK_SET_RCVDATA(hook, ng_uni_rcvupper);
+       } else
+               return EINVAL;
+
+       return 0;
+}
+
+static int
+ng_uni_disconnect(hook_p hook)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       if(hook == priv->lower)
+               priv->lower = NULL;
+       else if(hook == priv->upper)
+               priv->upper = NULL;
+       else
+               printf("%s: bogus hook %s\n", __func__, NG_HOOK_NAME(hook));
+
+       if (NG_NODE_NUMHOOKS(node) == 0) {
+               if (NG_NODE_IS_VALID(node))
+                       ng_rmnode_self(node);
+       }
+
+       return (0);
+}
+
+/************************************************************/
+/*
+ * DATA
+ */
+/*
+ * Receive signal from USER.
+ *
+ * Repackage the data into one large buffer.
+ */
+static int
+ng_uni_rcvupper(hook_p hook, item_p item)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct uni_arg arg;
+       struct uni_msg *msg;
+       int error;
+
+       if (!priv->enabled) {
+               NG_FREE_ITEM(item);
+               return (ENOTCONN);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
+               m_freem(m);
+               return (error);
+       }
+       m_freem(m);
+
+       if (uni_msg_len(msg) < sizeof(arg)) {
+               printf("%s: packet too short\n", __func__);
+               uni_msg_destroy(msg);
+               return (EINVAL);
+       }
+
+       bcopy(msg->b_rptr, &arg, sizeof(arg));
+       msg->b_rptr += sizeof(arg);
+
+       if (arg.sig >= UNIAPI_MAXSIG) {
+               printf("%s: bogus signal\n", __func__);
+               uni_msg_destroy(msg);
+               return (EINVAL);
+       }
+       uni_uni_input(priv->uni, arg.sig, arg.cookie, msg);
+       uni_work(priv->uni);
+
+       return (0);
+}
+
+
+/*
+ * Upper layer signal from UNI
+ */
+static void
+uni_uni_output(struct uni *uni, void *varg, enum uni_sig sig, u_int32_t cookie,
+    struct uni_msg *msg)
+{
+       node_p node = (node_p)varg;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct uni_arg arg;
+       int error;
+
+       if (priv->upper == NULL) {
+               if (msg != NULL)
+                       uni_msg_destroy(msg);
+               return;
+       }
+       arg.sig = sig;
+       arg.cookie = cookie;
+
+       m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
+       if (msg != NULL)
+               uni_msg_destroy(msg);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, priv->upper, m);
+}
+
+
+static void
+dump_uni_msg(struct uni_msg *msg)
+{
+       u_int pos;
+
+       for (pos = 0; pos < uni_msg_len(msg); pos++) {
+               if (pos % 16 == 0)
+                       printf("%06o ", pos);
+               if (pos % 16 == 8)
+                       printf("  ");
+               printf(" %02x", msg->b_rptr[pos]);
+               if (pos % 16 == 15)
+                       printf("\n");
+       }
+       if (pos % 16 != 0)
+               printf("\n");
+}
+
+
+/*
+ * Dump a SAAL signal in either direction
+ */
+static void
+dump_saal_signal(node_p node, enum saal_sig sig, struct uni_msg *msg, int to)
+{
+       struct priv *priv = NG_NODE_PRIVATE(node);
+
+       printf("signal %s SAAL: ", to ? "to" : "from");
+
+       switch (sig) {
+
+#define D(S) case S: printf("%s", #S); break
+
+       D(SAAL_ESTABLISH_request);
+       D(SAAL_ESTABLISH_indication);
+       D(SAAL_ESTABLISH_confirm);
+       D(SAAL_RELEASE_request);
+       D(SAAL_RELEASE_confirm);
+       D(SAAL_RELEASE_indication);
+       D(SAAL_DATA_request);
+       D(SAAL_DATA_indication);
+       D(SAAL_UDATA_request);
+       D(SAAL_UDATA_indication);
+
+#undef D
+         default:
+               printf("sig=%d", sig); break;
+       }
+       if (msg != NULL) {
+               printf(" data=%zu\n", uni_msg_len(msg));
+               if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 1)
+                       dump_uni_msg(msg);
+       } else
+               printf("\n");
+}
+
+/*
+ * Receive signal from SSCOP.
+ *
+ * If this is a data signal, repackage the data into one large buffer.
+ * UNI shouldn't be the bottleneck in a system and this greatly simplifies
+ * parsing in UNI.
+ */
+static int
+ng_uni_rcvlower(hook_p hook __unused, item_p item)
+{
+       node_p node = NG_HOOK_NODE(hook);
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct sscfu_arg arg;
+       struct uni_msg *msg;
+       int error;
+
+       if (!priv->enabled) {
+               NG_FREE_ITEM(item);
+               return (ENOTCONN);
+       }
+
+       NGI_GET_M(item, m);
+       NG_FREE_ITEM(item);
+
+       if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
+               m_freem(m);
+               return (error);
+       }
+       m_freem(m);
+
+       if (uni_msg_len(msg) < sizeof(arg)) {
+               uni_msg_destroy(msg);
+               printf("%s: packet too short\n", __func__);
+               return (EINVAL);
+       }
+       bcopy(msg->b_rptr, &arg, sizeof(arg));
+       msg->b_rptr += sizeof(arg);
+
+       if (arg.sig > SAAL_UDATA_indication) {
+               uni_msg_destroy(msg);
+               printf("%s: bogus signal\n", __func__);
+               return (EINVAL);
+       }
+
+       if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
+               dump_saal_signal(node, arg.sig, msg, 0);
+
+       uni_saal_input(priv->uni, arg.sig, msg);
+       uni_work(priv->uni);
+
+       return (0);
+}
+
+/*
+ * Send signal to sscop.
+ * Pack the message into an mbuf chain.
+ */
+static void
+uni_saal_output(struct uni *uni, void *varg, enum saal_sig sig, struct uni_msg *msg)
+{
+       node_p node = (node_p)varg;
+       struct priv *priv = NG_NODE_PRIVATE(node);
+       struct mbuf *m;
+       struct sscfu_arg arg;
+       int error;
+
+       if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
+               dump_saal_signal(node, sig, msg, 1);
+
+       if (priv->lower == NULL) {
+               if (msg != NULL)
+                       uni_msg_destroy(msg);
+               return;
+       }
+
+       arg.sig = sig;
+
+       m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
+       if (msg != NULL)
+               uni_msg_destroy(msg);
+       if (m == NULL)
+               return;
+
+       NG_SEND_DATA_ONLY(error, priv->lower, m);
+}
+
+static void
+uni_verbose(struct uni *uni, void *varg, u_int fac, const char *fmt, ...)
+{
+       va_list ap;
+
+       static char *facnames[] = {
+#define UNI_DEBUG_DEFINE(D) [UNI_FAC_##D] #D,
+               UNI_DEBUG_FACILITIES
+#undef UNI_DEBUG_DEFINE
+       };
+
+       printf("%s: ", facnames[fac]);
+
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       va_end(ap);
+
+       printf("\n");
+}
+
+
+/************************************************************/
+/*
+ * Memory debugging
+ */
+struct unimem_debug {
+       const char      *file;
+       u_int           lno;
+       LIST_ENTRY(unimem_debug) link;
+       char            data[0];
+};
+LIST_HEAD(unimem_debug_list, unimem_debug);
+
+static struct unimem_debug_list nguni_freemem[UNIMEM_TYPES] = {
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+};
+static struct unimem_debug_list nguni_usedmem[UNIMEM_TYPES] = {
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+    LIST_HEAD_INITIALIZER(unimem_debug),
+};
+
+static struct mtx nguni_unilist_mtx;
+
+static const char *unimem_names[UNIMEM_TYPES] = {
+       "instance",
+       "all",
+       "signal",
+       "call",
+       "party"
+};
+
+static void
+uni_init(void)
+{
+       mtx_init(&nguni_unilist_mtx, "netgraph UNI structure lists", NULL,
+           MTX_DEF);
+}
+
+static void
+uni_fini(void)
+{
+       u_int type;
+       struct unimem_debug *h;
+
+       for (type = 0; type < UNIMEM_TYPES; type++) {
+               while ((h = LIST_FIRST(&nguni_freemem[type])) != NULL) {
+                       LIST_REMOVE(h, link);
+                       free(h, M_UNI);
+               }
+
+               while ((h = LIST_FIRST(&nguni_usedmem[type])) != NULL) {
+                       LIST_REMOVE(h, link);
+                       printf("ng_uni: %s in use: %p (%s,%u)\n",
+                           unimem_names[type], (caddr_t)h->data,
+                           h->file, h->lno);
+                       free(h, M_UNI);
+               }
+       }
+
+       mtx_destroy(&nguni_unilist_mtx);
+}
+
+/*
+ * Allocate a chunk of memory from a given type.
+ */
+void *
+ng_uni_malloc(enum unimem type, const char *file, u_int lno)
+{
+       struct unimem_debug *d;
+       size_t full;
+
+       /*
+        * Try to allocate
+        */
+       mtx_lock(&nguni_unilist_mtx);
+       if ((d = LIST_FIRST(&nguni_freemem[type])) != NULL)
+               LIST_REMOVE(d, link);
+       mtx_unlock(&nguni_unilist_mtx);
+
+       if (d == NULL) {
+               /*
+                * allocate
+                */
+               full = unimem_sizes[type] + offsetof(struct unimem_debug, data);
+               if ((d = malloc(full, M_UNI, M_NOWAIT | M_ZERO)) == NULL)
+                       return (NULL);
+       } else {
+               bzero(d->data, unimem_sizes[type]);
+       }
+       d->file = file;
+       d->lno = lno;
+
+       mtx_lock(&nguni_unilist_mtx);
+       LIST_INSERT_HEAD(&nguni_usedmem[type], d, link);
+       mtx_unlock(&nguni_unilist_mtx);
+       return (d->data);
+}
+
+void
+ng_uni_free(enum unimem type, void *ptr, const char *file, u_int lno)
+{
+       struct unimem_debug *d, *h;
+
+       d = (struct unimem_debug *)
+           ((char *)ptr - offsetof(struct unimem_debug, data));
+
+       mtx_lock(&nguni_unilist_mtx);
+
+       LIST_FOREACH(h, &nguni_usedmem[type], link)
+               if (d == h)
+                       break;
+
+       if (h != NULL) {
+               LIST_REMOVE(d, link);
+               LIST_INSERT_HEAD(&nguni_freemem[type], d, link);
+       } else {
+               /*
+                * Not on used list - try free list.
+                */
+               LIST_FOREACH(h, &nguni_freemem[type], link)
+                       if (d == h)
+                               break;
+               if (h == NULL)
+                       printf("ng_uni: %s,%u: %p(%s) was never allocated\n",
+                           file, lno, ptr, unimem_names[type]);
+               else
+                       printf("ng_uni: %s,%u: %p(%s) was already destroyed "
+                           "in %s,%u\n",
+                           file, lno, ptr, unimem_names[type],
+                           h->file, h->lno);
+       }
+       mtx_unlock(&nguni_unilist_mtx);
+}
+/************************************************************/
+/*
+ * INITIALISATION
+ */
+
+/*
+ * Loading and unloading of node type
+ */
+static int
+ng_uni_mod_event(module_t mod, int event, void *data)
+{
+       int s;
+       int error = 0;
+
+       s = splnet();
+       switch(event) {
+
+         case MOD_LOAD:
+               uni_init();
+               break;
+
+         case MOD_UNLOAD:
+               uni_fini();
+               break;
+
+         default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       splx(s);
+       return (error);
+}
diff --git a/sys/netgraph7/atm/uni/ng_uni_cust.h b/sys/netgraph7/atm/uni/ng_uni_cust.h
new file mode 100644 (file)
index 0000000..603c419
--- /dev/null
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 2001-2003
+ *     Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ *     All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Customisation of signalling source to the NG environment.
+ *
+ * $FreeBSD: src/sys/netgraph/atm/uni/ng_uni_cust.h,v 1.6 2006/06/02 09:08:51 dds Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/atm/ngatmbase.h>
+
+#define        ASSERT(E, M) KASSERT(E,M)
+
+/*
+ * Memory
+ */
+enum unimem {
+       UNIMEM_INS = 0,
+       UNIMEM_ALL,
+       UNIMEM_SIG,
+       UNIMEM_CALL,
+       UNIMEM_PARTY,
+};
+#define        UNIMEM_TYPES    5
+
+void *ng_uni_malloc(enum unimem, const char *, u_int);
+void ng_uni_free(enum unimem, void *, const char *, u_int);
+
+#define        INS_ALLOC()     ng_uni_malloc(UNIMEM_INS, __FILE__, __LINE__)
+#define        INS_FREE(P)     ng_uni_free(UNIMEM_INS, P, __FILE__, __LINE__)
+
+#define        UNI_ALLOC()     ng_uni_malloc(UNIMEM_ALL, __FILE__, __LINE__)
+#define        UNI_FREE(P)     ng_uni_free(UNIMEM_ALL, P, __FILE__, __LINE__)
+
+#define        SIG_ALLOC()     ng_uni_malloc(UNIMEM_SIG, __FILE__, __LINE__)
+#define        SIG_FREE(P)     ng_uni_free(UNIMEM_SIG, P, __FILE__, __LINE__)
+
+#define        CALL_ALLOC()    ng_uni_malloc(UNIMEM_CALL, __FILE__, __LINE__)
+#define        CALL_FREE(P)    ng_uni_free(UNIMEM_CALL, P, __FILE__, __LINE__)
+
+#define        PARTY_ALLOC()   ng_uni_malloc(UNIMEM_PARTY, __FILE__, __LINE__)
+#define        PARTY_FREE(P)   ng_uni_free(UNIMEM_PARTY, P, __FILE__, __LINE__)
+
+/*
+ * Timers
+ */
+struct uni_timer {
+       struct callout c;
+};
+
+#define        _TIMER_INIT(X,T)        ng_callout_init(&(X)->T.c)
+#define        _TIMER_DESTROY(UNI,FIELD) _TIMER_STOP(UNI,FIELD)
+#define        _TIMER_STOP(UNI,FIELD) do {                                             \
+       ng_uncallout(&FIELD.c, (UNI)->arg);                                     \
+    } while (0)
+#define        TIMER_ISACT(UNI,T)      ((UNI)->T.c.c_flags & (CALLOUT_ACTIVE | \
+                                                       CALLOUT_PENDING))
+#define        _TIMER_START(UNI,ARG,FIELD,DUE,FUNC) do {                       \
+       _TIMER_STOP(UNI, FIELD);                                        \
+       ng_callout(&FIELD.c, (UNI)->arg, NULL,                          \
+           hz * (DUE) / 1000, FUNC, (ARG), 0);                         \
+    } while (0)
+
+#define        TIMER_FUNC_UNI(T,F)                                             \
+static void F(struct uni *);                                           \
+static void                                                            \
+_##T##_func(node_p node, hook_p hook, void *arg1, int arg2)            \
+{                                                                      \
+       struct uni *uni = (struct uni *)arg1;                           \
+                                                                       \
+       (F)(uni);                                                       \
+       uni_work(uni);                                                  \
+}
+
+/*
+ * Be careful: call may be invalid after the call to F
+ */
+#define        TIMER_FUNC_CALL(T,F)                                            \
+static void F(struct call *);                                          \
+static void                                                            \
+_##T##_func(node_p node, hook_p hook, void *arg1, int arg2)            \
+{                                                                      \
+       struct call *call = (struct call *)arg1;                        \
+       struct uni *uni = call->uni;                                    \
+                                                                       \
+       (F)(call);                                                      \
+       uni_work(uni);                                                  \
+}
+
+/*
+ * Be careful: call/party may be invalid after the call to F
+ */
+#define        TIMER_FUNC_PARTY(T,F)                                           \
+static void F(struct party *);                                         \
+static void                                                            \
+_##T##_func(node_p node, hook_p hook, void *arg1, int arg2)            \
+{                                                                      \
+       struct party *party = (struct party *)arg1;                     \
+       struct uni *uni = party->call->uni;                             \
+                                                                       \
+       (F)(party);                                                     \
+       uni_work(uni);                                                  \
+}
+
+extern size_t unimem_sizes[UNIMEM_TYPES];
+
+#define        UNICORE                                                         \
+size_t unimem_sizes[UNIMEM_TYPES] = {                                  \
+       [UNIMEM_INS]    = sizeof(struct uni),                           \
+       [UNIMEM_ALL]    = sizeof(struct uni_all),                       \
+       [UNIMEM_SIG]    = sizeof(struct sig),                           \
+       [UNIMEM_CALL]   = sizeof(struct call),                          \
+       [UNIMEM_PARTY]  = sizeof(struct party)                          \
+};
+
+#define        memmove(T, F, L) bcopy((F), (T), (L))
diff --git a/sys/netgraph7/bluetooth/common/ng_bluetooth.c b/sys/netgraph7/bluetooth/common/ng_bluetooth.c
new file mode 100644 (file)
index 0000000..aa1bad3
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * bluetooth.c
+ */
+
+/*-
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ng_bluetooth.c,v 1.3 2003/04/26 22:37:31 max Exp $
+ * $FreeBSD: src/sys/netgraph/bluetooth/common/ng_bluetooth.c,v 1.7 2007/06/04 18:25:07 dwmalone Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <netgraph/bluetooth/include/ng_bluetooth.h>
+
+/*
+ * Bluetooth stack sysctl globals
+ */
+
+static u_int32_t       bluetooth_hci_command_timeout_value  = 5;   /* sec */
+static u_int32_t       bluetooth_hci_connect_timeout_value  = 60;  /* sec */
+static u_int32_t       bluetooth_hci_max_neighbor_age_value = 600; /* sec */
+static u_int32_t       bluetooth_l2cap_rtx_timeout_value    = 60;  /* sec */
+static u_int32_t       bluetooth_l2cap_ertx_timeout_value   = 300; /* sec */
+
+/*
+ * Define sysctl tree that shared by other parts of Bluetooth stack
+ */
+
+SYSCTL_NODE(_net, OID_AUTO, bluetooth, CTLFLAG_RW, 0, "Bluetooth family");
+SYSCTL_INT(_net_bluetooth, OID_AUTO, version,
+       CTLFLAG_RD, 0, NG_BLUETOOTH_VERSION, "");
+
+/* 
+ * HCI
+ */
+
+SYSCTL_NODE(_net_bluetooth, OID_AUTO, hci, CTLFLAG_RW,
+       0, "Bluetooth HCI family");
+
+static int
+bluetooth_set_hci_command_timeout_value(SYSCTL_HANDLER_ARGS)
+{
+       u_int32_t       value;
+       int             error;
+
+       value = bluetooth_hci_command_timeout_value;
+       error = sysctl_handle_int(oidp, &value, 0, req);
+       if (error == 0 && req->newptr != NULL) {
+               if (value > 0)
+                       bluetooth_hci_command_timeout_value = value;
+               else
+                       error = EINVAL;
+       }
+
+       return (error);
+} /* bluetooth_set_hci_command_timeout_value */
+
+SYSCTL_PROC(_net_bluetooth_hci, OID_AUTO, command_timeout,
+       CTLTYPE_INT | CTLFLAG_RW,
+       &bluetooth_hci_command_timeout_value, 5, 
+       bluetooth_set_hci_command_timeout_value,
+       "I", "HCI command timeout (sec)");
+
+static int
+bluetooth_set_hci_connect_timeout_value(SYSCTL_HANDLER_ARGS)
+{
+       u_int32_t       value;
+       int             error;
+
+       value = bluetooth_hci_connect_timeout_value;
+       error = sysctl_handle_int(oidp, &value, 0, req);
+       if (error == 0 && req->newptr != NULL) {
+               if (0 < value && value <= bluetooth_l2cap_rtx_timeout_value)
+                       bluetooth_hci_connect_timeout_value = value;
+               else
+                       error = EINVAL;
+       }
+
+       return (error);
+} /* bluetooth_set_hci_connect_timeout_value */
+
+SYSCTL_PROC(_net_bluetooth_hci, OID_AUTO, connection_timeout, 
+       CTLTYPE_INT | CTLFLAG_RW,
+       &bluetooth_hci_connect_timeout_value, 60, 
+       bluetooth_set_hci_connect_timeout_value,
+       "I", "HCI connect timeout (sec)");
+
+SYSCTL_INT(_net_bluetooth_hci, OID_AUTO, max_neighbor_age, CTLFLAG_RW,
+       &bluetooth_hci_max_neighbor_age_value, 600,
+       "Maximal HCI neighbor cache entry age (sec)");
+
+/* 
+ * L2CAP
+ */
+
+SYSCTL_NODE(_net_bluetooth, OID_AUTO, l2cap, CTLFLAG_RW,
+       0, "Bluetooth L2CAP family");
+
+static int
+bluetooth_set_l2cap_rtx_timeout_value(SYSCTL_HANDLER_ARGS)
+{
+       u_int32_t       value;
+       int             error;
+
+       value = bluetooth_l2cap_rtx_timeout_value;
+       error = sysctl_handle_int(oidp, &value, 0, req);
+       if (error == 0 && req->newptr != NULL) {
+               if (bluetooth_hci_connect_timeout_value <= value &&
+                   value <= bluetooth_l2cap_ertx_timeout_value)
+                       bluetooth_l2cap_rtx_timeout_value = value;
+               else
+                       error = EINVAL;
+       }
+
+       return (error);
+} /* bluetooth_set_l2cap_rtx_timeout_value */
+
+SYSCTL_PROC(_net_bluetooth_l2cap, OID_AUTO, rtx_timeout,
+       CTLTYPE_INT | CTLFLAG_RW,
+       &bluetooth_l2cap_rtx_timeout_value, 60,
+       bluetooth_set_l2cap_rtx_timeout_value,
+       "I", "L2CAP RTX timeout (sec)");
+
+static int
+bluetooth_set_l2cap_ertx_timeout_value(SYSCTL_HANDLER_ARGS)
+{
+       u_int32_t       value;
+       int             error;
+
+       value = bluetooth_l2cap_ertx_timeout_value;
+       error = sysctl_handle_int(oidp, &value, 0, req);
+       if (error == 0 && req->newptr != NULL) {
+               if (value >= bluetooth_l2cap_rtx_timeout_value)
+                       bluetooth_l2cap_ertx_timeout_value = value;
+               else
+                       error = EINVAL;
+       }
+
+       return (error);
+} /* bluetooth_set_l2cap_ertx_timeout_value */
+
+SYSCTL_PROC(_net_bluetooth_l2cap, OID_AUTO, ertx_timeout,
+       CTLTYPE_INT | CTLFLAG_RW,
+       &bluetooth_l2cap_ertx_timeout_value, 300,
+       bluetooth_set_l2cap_ertx_timeout_value,
+       "I", "L2CAP ERTX timeout (sec)");
+
+/*
+ * Return various sysctl values
+ */
+
+u_int32_t
+bluetooth_hci_command_timeout(void)
+{
+       return (bluetooth_hci_command_timeout_value * hz);
+} /* bluetooth_hci_command_timeout */
+
+u_int32_t
+bluetooth_hci_connect_timeout(void)
+{
+       return (bluetooth_hci_connect_timeout_value * hz);
+} /* bluetooth_hci_connect_timeout */
+
+u_int32_t
+bluetooth_hci_max_neighbor_age(void)
+{
+       return (bluetooth_hci_max_neighbor_age_value);
+} /* bluetooth_hci_max_neighbor_age */
+
+u_int32_t
+bluetooth_l2cap_rtx_timeout(void)
+{
+       return (bluetooth_l2cap_rtx_timeout_value * hz);
+} /* bluetooth_l2cap_rtx_timeout */
+
+u_int32_t
+bluetooth_l2cap_ertx_timeout(void)
+{
+       return (bluetooth_l2cap_ertx_timeout_value * hz);
+} /* bluetooth_l2cap_ertx_timeout */
+
+/* 
+ * RFCOMM
+ */
+
+SYSCTL_NODE(_net_bluetooth, OID_AUTO, rfcomm, CTLFLAG_RW,
+       0, "Bluetooth RFCOMM family");
+
+/*
+ * Handle loading and unloading for this code.
+ */
+
+static int
+bluetooth_modevent(module_t mod, int event, void *data)
+{
+       int     error = 0;
+
+       switch (event) {
+       case MOD_LOAD:
+               break; 
+
+       case MOD_UNLOAD:
+               break; 
+
+       default:
+               error = EOPNOTSUPP;
+               break;
+       }
+
+       return (error);
+} /* bluetooth_modevent */
+
+/*
+ * Module
+ */
+
+static moduledata_t    bluetooth_mod = {
+       "ng_bluetooth",
+       bluetooth_modevent,
+       NULL
+};
+
+DECLARE_MODULE(ng_bluetooth, bluetooth_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(ng_bluetooth, NG_BLUETOOTH_VERSION);
+
diff --git a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
new file mode 100644 (file)
index 0000000..a9b9716
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * ng_bt3c_pccard.c
+ */
+
+/*-
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ng_bt3c_pccard.c,v 1.5 2003/04/01 18:15:21 max Exp $
+ * $FreeBSD: src/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c,v 1.20 2007/02/23 12:19:02 piso Exp $
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX 
+ *
+ * Based on information obrained from: Jose Orlando Pereira <jop@di.uminho.pt>
+ * and disassembled w2k driver.
+ *
+ * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX 
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/pccard/pccardreg.h>
+#include <dev/pccard/pccardvar.h>
+#include "pccarddevs.h"
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/bluetooth/include/ng_bluetooth.h>
+#include <netgraph/bluetooth/include/ng_hci.h>
+#include <netgraph/bluetooth/include/ng_bt3c.h>
+#include <netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h>
+
+/* Netgraph methods */
+static ng_constructor_t        ng_bt3c_constructor;
+static ng_shutdown_t   ng_bt3c_shutdown;
+static ng_newhook_t    ng_bt3c_newhook;
+static ng_connect_t    ng_bt3c_connect;
+static ng_disconnect_t ng_bt3c_disconnect;
+static ng_rcvmsg_t     ng_bt3c_rcvmsg;
+static ng_rcvdata_t    ng_bt3c_rcvdata;
+
+/* PCMCIA driver methods */
+static int     bt3c_pccard_probe       (device_t);
+static int     bt3c_pccard_attach      (device_t);
+static int     bt3c_pccard_detach      (device_t);
+
+static void    bt3c_intr               (void *);
+static void    bt3c_receive            (bt3c_softc_p);
+
+static void    bt3c_swi_intr           (void *);
+static void    bt3c_forward            (node_p, hook_p, void *, int);
+static void    bt3c_send               (node_p, hook_p, void *, int);
+
+static void    bt3c_download_firmware  (bt3c_softc_p, char const *, int);
+
+#define        bt3c_set_address(sc, address) \
+do { \
+       bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_ADDR_L, ((address) & 0xff)); \
+       bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_ADDR_H, (((address) >> 8) & 0xff)); \
+} while (0)
+
+#define        bt3c_read_data(sc, data) \
+do { \
+       (data)  = bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_DATA_L); \
+       (data) |= ((bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_DATA_H) & 0xff) << 8); \
+} while (0)
+
+#define        bt3c_write_data(sc, data) \
+do { \
+       bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_DATA_L, ((data) & 0xff)); \
+       bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_DATA_H, (((data) >> 8) & 0xff)); \
+} while (0)
+
+#define        bt3c_read_control(sc, data) \
+do { \
+       (data) = bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_CONTROL); \
+} while (0)
+
+#define        bt3c_write_control(sc, data) \
+do { \
+       bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_CONTROL, (data)); \
+} while (0)
+
+#define bt3c_read(sc, address, data) \
+do { \
+       bt3c_set_address((sc), (address)); \
+       bt3c_read_data((sc), (data)); \
+} while(0)
+
+#define bt3c_write(sc, address, data) \
+do { \
+       bt3c_set_address((sc), (address)); \
+       bt3c_write_data((sc), (data)); \
+} while(0)
+
+static MALLOC_DEFINE(M_BT3C, "bt3c", "bt3c data structures");
+       
+/****************************************************************************
+ ****************************************************************************
+ **                           Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node type
+ */
+
+/* Queue length */
+static const struct ng_parse_struct_field      ng_bt3c_node_qlen_type_fields[] =
+{
+       { "queue", &ng_parse_int32_type, },
+       { "qlen",  &ng_parse_int32_type, },
+       { NULL, }
+};
+static const struct ng_parse_type              ng_bt3c_node_qlen_type = {
+       &ng_parse_struct_type,
+       &ng_bt3c_node_qlen_type_fields
+};
+
+/* Stat info */
+static const struct ng_parse_struct_field      ng_bt3c_node_stat_type_fields[] =
+{
+       { "pckts_recv", &ng_parse_uint32_type, },
+       { "bytes_recv", &ng_parse_uint32_type, },
+       { "pckts_sent", &ng_parse_uint32_type, },
+       { "bytes_sent", &ng_parse_uint32_type, },
+       { "oerrors",    &ng_parse_uint32_type, },
+       { "ierrors",    &ng_parse_uint32_type, },
+       { NULL, }
+};
+static const struct ng_parse_type              ng_bt3c_node_stat_type = {
+       &ng_parse_struct_type,
+       &ng_bt3c_node_stat_type_fields
+};
+
+static const struct ng_cmdlist ng_bt3c_cmdlist[] = {
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_GET_STATE,
+       "get_state",
+       NULL,
+       &ng_parse_uint16_type
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_SET_DEBUG,
+       "set_debug",
+       &ng_parse_uint16_type,
+       NULL
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_GET_DEBUG,
+       "get_debug",
+       NULL,
+       &ng_parse_uint16_type
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_GET_QLEN,
+       "get_qlen",
+       NULL,
+       &ng_bt3c_node_qlen_type
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_SET_QLEN,
+       "set_qlen",
+       &ng_bt3c_node_qlen_type,
+       NULL
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_GET_STAT,
+       "get_stat",
+       NULL,
+       &ng_bt3c_node_stat_type
+},
+{
+       NGM_BT3C_COOKIE,
+       NGM_BT3C_NODE_RESET_STAT,
+       "reset_stat",
+       NULL,
+       NULL
+},
+{ 0, }
+};
+
+static struct ng_type  typestruct = {
+       .version =      NG_ABI_VERSION,
+       .name =         NG_BT3C_NODE_TYPE,
+       .constructor =  ng_bt3c_constructor,
+       .rcvmsg =       ng_bt3c_rcvmsg,
+       .shutdown =     ng_bt3c_shutdown,
+       .newhook =      ng_bt3c_newhook,
+       .connect =      ng_bt3c_connect,
+       .rcvdata =      ng_bt3c_rcvdata,
+       .disconnect =   ng_bt3c_disconnect,
+        .cmdlist =     ng_bt3c_cmdlist 
+};
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+static int
+ng_bt3c_constructor(node_p node)
+{
+       return (EINVAL);
+} /* ng_bt3c_constructor */
+
+/*
+ * Netgraph node destructor. Destroy node only when device has been detached
+ */
+
+static int
+ng_bt3c_shutdown(node_p node)
+{
+       bt3c_softc_p    sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+
+       /* Let old node go */
+       NG_NODE_SET_PRIVATE(node, NULL);
+       NG_NODE_UNREF(node);
+
+       /* Create new fresh one if we are not going down */
+       if (sc == NULL)
+               goto out;
+
+       /* Create new Netgraph node */
+       if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+               device_printf(sc->dev, "Could not create Netgraph node\n");
+               sc->node = NULL;
+               goto out;
+       }
+
+       /* Name new Netgraph node */
+       if (ng_name_node(sc->node,  device_get_nameunit(sc->dev)) != 0) {
+               device_printf(sc->dev, "Could not name Netgraph node\n");
+               NG_NODE_UNREF(sc->node);
+               sc->node = NULL;
+               goto out;
+       }
+
+       NG_NODE_SET_PRIVATE(sc->node, sc);
+out:
+       return (0);
+} /* ng_bt3c_shutdown */
+
+/*
+ * Create new hook. There can only be one.
+ */
+
+static int
+ng_bt3c_newhook(node_p node, hook_p hook, char const *name)
+{
+       bt3c_softc_p    sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+
+       if (strcmp(name, NG_BT3C_HOOK) != 0)
+               return (EINVAL);
+
+       if (sc->hook != NULL)
+               return (EISCONN);
+
+       sc->hook = hook;
+
+       return (0);
+} /* ng_bt3c_newhook */
+
+/*
+ * Connect hook. Say YEP, that's OK with me.
+ */
+
+static int
+ng_bt3c_connect(hook_p hook)
+{
+       bt3c_softc_p    sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+       if (hook != sc->hook) {
+               sc->hook = NULL;
+               return (EINVAL);
+       }
+
+       /* set the hook into queueing mode (for incoming (from wire) packets) */
+       NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+       return (0);
+} /* ng_bt3c_connect */
+
+/*
+ * Disconnect hook
+ */
+
+static int
+ng_bt3c_disconnect(hook_p hook)
+{
+       bt3c_softc_p    sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+       /*
+        * We need to check for sc != NULL because we can be called from
+        * bt3c_pccard_detach() via ng_rmnode_self()
+        */
+
+       if (sc != NULL) {
+               if (hook != sc->hook)
+                       return (EINVAL);
+
+               IF_DRAIN(&sc->inq);
+               IF_DRAIN(&sc->outq);
+
+               sc->hook = NULL;
+       }
+
+       return (0);
+} /* ng_bt3c_disconnect */
+
+/*
+ * Process control message
+ */
+
+static int
+ng_bt3c_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+       bt3c_softc_p     sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+       struct ng_mesg  *msg = NULL, *rsp = NULL;
+       int              error = 0;
+
+       if (sc == NULL) {
+               NG_FREE_ITEM(item);
+               return (EHOSTDOWN);
+       }
+
+       NGI_GET_MSG(item, msg);
+
+       switch (msg->header.typecookie) {
+       case NGM_GENERIC_COOKIE:
+               switch (msg->header.cmd) {
+               case NGM_TEXT_STATUS:
+                       NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+                       if (rsp == NULL)
+                               error = ENOMEM;
+                       else
+                               snprintf(rsp->data, NG_TEXTRESPONSE,
+                                       "Hook: %s\n" \
+                                       "Flags: %#x\n" \
+                                       "Debug: %d\n"  \
+                                       "State: %d\n"  \
+                                       "IncmQ: [len:%d,max:%d]\n" \
+                                       "OutgQ: [len:%d,max:%d]\n",
+                                       (sc->hook != NULL)? NG_BT3C_HOOK : "",
+                                       sc->flags,
+                                       sc->debug,
+                                       sc->state,
+                                       _IF_QLEN(&sc->inq), /* XXX */
+                                       sc->inq.ifq_maxlen, /* XXX */
+                                       _IF_QLEN(&sc->outq), /* XXX */
+                                       sc->outq.ifq_maxlen /* XXX */
+                                       );
+                       break;
+
+               default:
+                       error = EINVAL;
+                       break;
+               }
+               break;
+
+       case NGM_BT3C_COOKIE:
+               switch (msg->header.cmd) {
+               case NGM_BT3C_NODE_GET_STATE:
+                       NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_state_ep),
+                               M_NOWAIT);
+                       if (rsp == NULL)
+                               error = ENOMEM;
+                       else
+                               *((ng_bt3c_node_state_ep *)(rsp->data)) = 
+                                       sc->state;
+                       break;
+
+               case NGM_BT3C_NODE_SET_DEBUG:
+                       if (msg->header.arglen != sizeof(ng_bt3c_node_debug_ep))
+                               error = EMSGSIZE;
+                       else
+                               sc->debug =
+                                       *((ng_bt3c_node_debug_ep *)(msg->data));
+                       break;
+
+               case NGM_BT3C_NODE_GET_DEBUG:
+                       NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_debug_ep),
+                               M_NOWAIT);
+                       if (rsp == NULL)
+                               error = ENOMEM;
+                       else
+                               *((ng_bt3c_node_debug_ep *)(rsp->data)) = 
+                                       sc->debug;
+                       break;
+
+               case NGM_BT3C_NODE_GET_QLEN:
+                       NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_qlen_ep),
+                               M_NOWAIT);
+                       if (rsp == NULL) {
+                               error = ENOMEM;
+                               break;
+                       }
+
+                       switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
+                       case NGM_BT3C_NODE_IN_QUEUE:
+                               ((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
+                                       NGM_BT3C_NODE_IN_QUEUE;
+                               ((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
+                                       sc->inq.ifq_maxlen;
+                               break;
+
+                       case NGM_BT3C_NODE_OUT_QUEUE:
+                               ((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
+                                       NGM_BT3C_NODE_OUT_QUEUE;
+                               ((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
+                                       sc->outq.ifq_maxlen;
+                               break;
+
+                       default:
+                               NG_FREE_MSG(rsp);
+                               error = EINVAL;
+                               break;
+                       }
+                       break;
+
+               case NGM_BT3C_NODE_SET_QLEN:
+                       if (msg->header.arglen != sizeof(ng_bt3c_node_qlen_ep)){
+                               error = EMSGSIZE;
+                               break;
+                       }
+
+                       if (((ng_bt3c_node_qlen_ep *)(msg->data))->qlen <= 0) {
+                               error = EINVAL;
+                               break;
+                       }
+
+                       switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
+                       case NGM_BT3C_NODE_IN_QUEUE:
+                               sc->inq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)
+                                       (msg->data))->qlen; /* XXX */
+                               break;
+
+                       case NGM_BT3C_NODE_OUT_QUEUE:
+                               sc->outq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)