summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/ng_bluetooth.4109
-rw-r--r--share/man/man4/ng_bt3c.4136
-rw-r--r--share/man/man4/ng_btsocket.4255
-rw-r--r--share/man/man4/ng_h4.4121
-rw-r--r--share/man/man4/ng_hci.4342
-rw-r--r--share/man/man4/ng_l2cap.4341
-rw-r--r--share/man/man4/ng_ubt.4102
-rw-r--r--sys/modules/netgraph/bluetooth/Makefile13
-rw-r--r--sys/modules/netgraph/bluetooth/bluetooth/Makefile13
-rw-r--r--sys/modules/netgraph/bluetooth/bt3c/Makefile16
-rw-r--r--sys/modules/netgraph/bluetooth/h4/Makefile16
-rw-r--r--sys/modules/netgraph/bluetooth/hci/Makefile16
-rw-r--r--sys/modules/netgraph/bluetooth/l2cap/Makefile16
-rw-r--r--sys/modules/netgraph/bluetooth/socket/Makefile17
-rw-r--r--sys/modules/netgraph/bluetooth/ubt/Makefile16
-rw-r--r--sys/netgraph/bluetooth/common/ng_bluetooth.c254
-rw-r--r--sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c1247
-rw-r--r--sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h103
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/TODO13
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4.c1059
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h122
-rw-r--r--sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h100
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/TODO30
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c2166
-rw-r--r--sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h143
-rw-r--r--sys/netgraph/bluetooth/hci/TODO42
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_cmds.c887
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_cmds.h45
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_evnt.c1085
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_evnt.h43
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_main.c1063
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_misc.c556
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_misc.h58
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_prse.h203
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_ulpi.c1280
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_ulpi.h53
-rw-r--r--sys/netgraph/bluetooth/hci/ng_hci_var.h217
-rw-r--r--sys/netgraph/bluetooth/include/ng_bluetooth.h232
-rw-r--r--sys/netgraph/bluetooth/include/ng_bt3c.h114
-rw-r--r--sys/netgraph/bluetooth/include/ng_btsocket.h319
-rw-r--r--sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h85
-rw-r--r--sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h200
-rw-r--r--sys/netgraph/bluetooth/include/ng_h4.h118
-rw-r--r--sys/netgraph/bluetooth/include/ng_hci.h1651
-rw-r--r--sys/netgraph/bluetooth/include/ng_l2cap.h655
-rw-r--r--sys/netgraph/bluetooth/include/ng_ubt.h103
-rw-r--r--sys/netgraph/bluetooth/l2cap/TODO43
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c365
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.h403
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c1337
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.h38
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c806
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h48
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c736
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c517
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.h100
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_prse.h71
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c1640
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h77
-rw-r--r--sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h182
-rw-r--r--sys/netgraph/bluetooth/socket/TODO15
-rw-r--r--sys/netgraph/bluetooth/socket/ng_btsocket.c258
-rw-r--r--sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c1320
-rw-r--r--sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c2717
-rw-r--r--sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c1222
-rw-r--r--usr.bin/bluetooth/Makefile5
-rw-r--r--usr.bin/bluetooth/btsockstat/Makefile17
-rw-r--r--usr.bin/bluetooth/btsockstat/btsockstat.173
-rw-r--r--usr.bin/bluetooth/btsockstat/btsockstat.c484
-rw-r--r--usr.sbin/bluetooth/Makefile12
-rw-r--r--usr.sbin/bluetooth/bt3cfw/Makefile15
-rw-r--r--usr.sbin/bluetooth/bt3cfw/bt3cfw.869
-rw-r--r--usr.sbin/bluetooth/bt3cfw/bt3cfw.c227
-rw-r--r--usr.sbin/bluetooth/hccontrol/Makefile14
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.8163
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.c274
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.h75
-rw-r--r--usr.sbin/bluetooth/hccontrol/host_controller_baseband.c1713
-rw-r--r--usr.sbin/bluetooth/hccontrol/info.c219
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_control.c973
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_policy.c310
-rw-r--r--usr.sbin/bluetooth/hccontrol/node.c518
-rw-r--r--usr.sbin/bluetooth/hccontrol/send_recv.c184
-rw-r--r--usr.sbin/bluetooth/hccontrol/status.c245
-rw-r--r--usr.sbin/bluetooth/hccontrol/util.c350
-rw-r--r--usr.sbin/bluetooth/hcseriald/Makefile17
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.881
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.c279
-rw-r--r--usr.sbin/bluetooth/l2control/Makefile12
-rw-r--r--usr.sbin/bluetooth/l2control/l2cap.c256
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.884
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.c226
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.h49
-rw-r--r--usr.sbin/bluetooth/l2ping/Makefile12
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.887
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.c279
96 files changed, 34992 insertions, 0 deletions
diff --git a/share/man/man4/ng_bluetooth.4 b/share/man/man4/ng_bluetooth.4
new file mode 100644
index 0000000..4022786
--- /dev/null
+++ b/share/man/man4/ng_bluetooth.4
@@ -0,0 +1,109 @@
+.\" ng_bluetooth.4
+.\"
+.\" 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.4,v 1.2 2002/11/12 22:14:10 max Exp $
+.\" $FreeBSD$
+.Dd November 9, 2002
+.Dt NG_BLUETOOTH 4
+.Os
+.Sh NAME
+.Nm bluetooth
+.Nd placeholder for global Bluetooth variables
+.Sh SYNOPSIS
+.In sys/types.h
+.In ng_bluetooth.h
+.Sh DESCRIPTION
+The
+.Nm
+module is a placeholder for global Bluetooth variables. All Bluetooth
+variables can be examined and changed via
+.Xr sysctl 8 .
+.Sh BLUETOOTH VARIABLES
+Below is the description of default variables. Each Bluetooth module
+might add its own variables to the tree.
+.Bl -tag -width foobar
+.It net.bluetooth.version
+A read only integer variable that shows the current version of the
+Bluetooth stack.
+.It net.bluetooth.hci.command_timeout
+A read-write interger variable that controls the Host Controller Interface
+(HCI) command timeout (in seconds), i.e. how long the HCI layer will wait
+for the
+.Dv Command_Complete
+or
+.Dv Command_Status
+event from a Bluetooth device.
+.It net.bluetooth.hci.connection_timeout
+A read-write integer variable that controls the HCI connection timeout, i.e.
+how long the HCI layer will wait for the
+.Dv Connection_Complete
+event. Normaly this should not be required as Bluetooth devices have
+connection timeout of their own and will send event back. This timeout
+is required to ensure that no connection will stall in case when the HCI
+transport layer is broken. Be careful when changing this variable.
+Make sure you understand what you are doing.
+.It net.bluetooth.hci.watchdog_timeout
+A read-write integer variable that controls the HCI connection watchdog
+timeout in seconds), i.e. how long the HCI layer should wait before
+disconnecting an inactive baseband connection.
+.Em This has not been implemented yet .
+.It net.bluetooth.hci.max_neighbor_age
+A read-write integer variable that controls time-to-live (in seconds) for
+entries in the HCI neighbor cache. Every time a Bluetooth device performs
+an
+.Dv Inquiry
+operation, the results will be put in cache. Later when a Bluetooth device
+establishes a baseband connection, it will try to find the matching entry in
+the cache and use it. This might speed up establishment of the baseband
+connection.
+.It net.bluetooth.l2cap.rtx_timeout
+A read-write integer variable that controls the Link Layer Control and
+Adaptation Protocol (L2CAP) Retransmission Timeout (RTX) (in seconds).
+Every time the L2CAP layer submits a control command, the RTX timeout is set.
+The value of the RTX timeout should be greater or equal to the value of
+the HCI connection timeout. Be careful when changing this variable. Make
+sure you understand what you are doing.
+.It net.bluetooth.l2cap.ertx_timeout
+A read-write integer variable that controls the L2CAP Extended Retransmission
+Timeout (ERTX) (in seconds). In some cases remote peer may respond with
+.Dv PENDING
+status to the L2CAP control command. In this case the L2CAP command timeout
+is reset to the ERTX timeout value. The value of the ERTX timeout should be
+greater or equal to the value of the RTX timeout. Be careful when changing
+this variable. Make sure you understand what you are doing.
+.El
+.Sh SEE ALSO
+.Xr sysctl 8 ,
+.Xr ng_hci 4 ,
+.Xr ng_l2cap 4 ,
+.Xr ng_btsocket 4
+.Sh HISTORY
+The
+.Nm
+module was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_bt3c.4 b/share/man/man4/ng_bt3c.4
new file mode 100644
index 0000000..f651ea9
--- /dev/null
+++ b/share/man/man4/ng_bt3c.4
@@ -0,0 +1,136 @@
+.\" ng_bt3c.4
+.\"
+.\" 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.4,v 1.3 2002/11/12 17:03:58 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt NG_BT3C 4
+.Os
+.Sh NAME
+.Nm BTCCC
+.Nd Netgraph node type that is also a 3Com Bluetooth PC card driver
+.Sh SYNOPSIS
+.In sys/types.h
+.In ng_bt3c.h
+.Sh DESCRIPTION
+The
+.Nm BTCCC
+node type is both a persistent Netgraph node type and a driver for
+3Com Bluetooth PC card (3CRWB6096-HP). It implements a Bluetooth HCI
+UART transport layer as per chapter H4 of the Bluetooth Specification
+Book v1.1. A new node is created when the card is plugged.
+.Pp
+In order to use the card one
+.Em MUST
+download firmware first. Due to copyright issues I will no longer
+provide firmware with the driver. The firmware can be obtained
+from the Windows driver package that can be downloaded from the 3COM web
+site at no charge. The firmware name is BT3CPCC.BIN. To load firmware
+info the card use
+.Xr bt3cfw 8 .
+I'm using original firmware that came with the card on CD-ROM.
+.Bd -literal -offset indent
+MD5 (BT3CPCC.BIN) = 36170fda56ea9fdbf1702c966f8a97f1
+.Ed
+.Pp
+For OLDCARD systems the entry in
+.Xr pccard.conf 5
+might look like this
+.Bd -literal -offset indent
+# 3Com 3CRWB60-A Bluetooth PC Card
+card "3COM" "3CRWB60-A" "Bluetooth PC Card"
+ config auto "btccc" ?
+ insert /usr/sbin/bt3cfw -n $device -f /etc/BT3CPCC.bin
+.Ed
+.Pp
+Do not forget to load module and SIGHUP
+.Xr pccardd 8 .
+.Pp
+The node has a single hook called
+.Dv hook .
+Incoming bytes received on the device are re-assembled into HCI frames
+(according to the length). Full HCI frames are sent out on the hook. HCI
+frames received on
+.Dv hook
+are transmitted out. No modification to the data is performed in
+either direction.
+.Sh HOOKS
+This node type supports the following hooks:
+.Pp
+.Bl -tag -width foobar
+.It Dv hook
+single HCI frame contained in single
+.Dv mbuf
+structure.
+.El
+.Sh CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_BT3C_NODE_GET_STATE
+Returns current receiving state for the node.
+.It Dv NGM_BT3C_NODE_GET_DEBUG
+Returns an integer containing the current debug level for the node.
+.It Dv NGM_BT3C_NODE_SET_DEBUG
+This command takes an integer argument and sets current debug level
+for the node.
+.It Dv NGM_BT3C_NODE_GET_QLEN
+This command takes a parameter that specifies queue number and returns
+current length of the queue for the node.
+.It Dv NGM_BT3C_NODE_SET_QLEN
+This command takes two parameters that specify queue number and and
+maximum length of the queue and sets maximum length of the queue for
+the node.
+.It Dv NGM_BT3C_NODE_GET_STAT
+Returns various statistic information for the node, such as: number of
+bytes (frames) sent, number of bytes (frames) received and number of
+input (output) errors.
+.It Dv NGM_BT3C_NODE_RESET_STAT
+Reset all statistic counters to zero.
+.It Dv NGM_BT3C_NODE_DOWNLOAD_FIRMWARE
+Download card firmware.
+.El
+.Sh SHUTDOWN
+This node shuts down when the corresponding card is un-plugged.
+.Sh BUGS
+The driver is based on information obrained from Jose Orlando Pereira
+<jop@di.uminho.pt> and disassembled W2K driver.
+.Sh SEE ALSO
+.Xr cardbus 4 ,
+.Xr pccbb 4 ,
+.Xr pcic 4 ,
+.Xr pccardc 8 ,
+.Xr pccardd 8 ,
+.Xr pccard.conf 5 ,
+.Xr netgraph 4 ,
+.Xr ngctl 8 ,
+.Xr bt3cfw 8
+.Sh HISTORY
+The
+.Nm BTCCC
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_btsocket.4 b/share/man/man4/ng_btsocket.4
new file mode 100644
index 0000000..bb2b360
--- /dev/null
+++ b/share/man/man4/ng_btsocket.4
@@ -0,0 +1,255 @@
+.\" ng_btsocket.4
+.\"
+.\" 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_btsocket.4,v 1.4 2002/11/12 22:31:39 max Exp $
+.\" $FreeBSD$
+.Dd July 8, 2002
+.Dt NG_BTSOCKET 4
+.Os
+.Sh NAME
+.Nm btsocket
+.Nd Bluetooth sockets layer
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/socket.h
+.In bitstring.h
+.In netgraph.h
+.In ng_hci.h
+.In ng_l2cap.h
+.In ng_btsocket.h
+.Sh DESCRIPTION
+The
+.Nm
+module implements three Netgraph node types. Each type in its turn implements
+one protocol within
+.Dv PF_BLUETOOTH
+domain.
+.Pp
+.Sh BLUETOOTH_PROTO_HCI protocol
+.Ss SOCK_RAW HCI sockets
+Implemented by
+.Cm btsock_hci_raw
+Netgraph type. Raw HCI sockets allow sending of raw HCI command datagrams
+only to correspondents named in
+.Xr send 2
+calls. Raw HCI datagrams (HCI commands, events and data) are generally
+received with
+.Xr recvfrom 2 ,
+which returns the next datagram with its return address. Also raw HCI
+sockets can be used to control HCI nodes.
+.Pp
+The Bluetooth raw HCI socket address is defined as follows:
+.Bd -literal -offset indent
+/* Bluetooth version of struct sockaddr for raw HCI sockets */
+struct sockaddr_hci {
+ u_char hci_len; /* total length */
+ u_char hci_family; /* address family */
+ char hci_node[16]; /* HCI node name */
+};
+.Ed
+.Pp
+Raw HCI sockets support number of
+.Xr ioctl 2
+requests such as:
+.Bl -tag -width foo
+.It Dv SIOC_HCI_RAW_NODE_GET_STATE
+Returns current state for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_INIT
+Turn on
+.Dq inited
+bit for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_DEBUG
+Returns current debug level for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_SET_DEBUG
+Sets current debug level for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_BUFFER
+Returns current state of data buffers for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_BDADDR
+Returns BD_ADDR for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_FEATURES
+Returns the list of features supported by hardware for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_STAT
+Returns various statistic counters for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_RESET_STAT
+Resets all statistic counters for the HCI node to zero.
+.It Dv SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE
+Remove all neighbor cache entries for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE
+Returns content of the neighbor cache for the HCI node.
+.It Dv SIOC_HCI_RAW_NODE_GET_CON_LIST
+Returns list of active baseband connections (i.e. ACL and SCO links) for
+the HCI node.
+.It SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK
+Returns current link policy settings mask for the HCI node.
+.It SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK
+Sets current link policy settings mask for the HCI node.
+.It SIOC_HCI_RAW_NODE_GET_PACKET_MASK
+Returns current packet mask for the HCI node.
+.It SIOC_HCI_RAW_NODE_SET_PACKET_MASK
+Sets current packet mask for the HCI node.
+.El
+.Pp
+Raw HCI sockets support filters. The application can filter certain
+HCI datagram types. For HCI event datagrams the application can set
+additional filter. The raw HCI socket filter defined as follows:
+.Bd -literal -offset indent
+/*
+ * Raw HCI socket filter.
+ *
+ * For packet mask use (1 << (HCI packet indicator - 1))
+ * For event mask use (1 << (Event - 1))
+ */
+
+struct ng_btsocket_hci_raw_filter {
+ bitstr_t bit_decl(packet_mask, 32);
+ bitstr_t bit_decl(event_mask, (NG_HCI_EVENT_MASK_SIZE * 8));
+};
+.Ed
+.Pp
+The
+.Dv SO_HCI_RAW_FILTER
+option defined at
+.Dv SOL_HCI_RAW
+level can be used to obtain via
+.Xr getsockopt 2
+or change via
+.Xr setsockopt 2
+raw HCI socket's filter.
+.Pp
+.Sh BLUETOOTH_PROTO_L2CAP protocol
+The Bluetooth L2CAP socket address is defined as follows:
+.Bd -literal -offset indent
+/* Bluetooth version of struct sockaddr for L2CAP sockets */
+struct sockaddr_l2cap {
+ u_char l2cap_len; /* total length */
+ u_char l2cap_family; /* address family */
+ u_int16_t l2cap_psm; /* Protocol/Service Multiplexor */
+ bdaddr_t l2cap_bdaddr; /* address */
+};
+.Ed
+.Pp
+.Ss SOCK_RAW L2CAP sockets
+Implemented by
+.Cm btsock_l2c_raw
+Netgraph type.
+Raw L2CAP sockets do not provide access to raw L2CAP datagrams. These
+sockets used to control L2CAP nodes and to issue special L2CAP requests
+such as ECHO_REQUEST and GET_INFO request.
+.Pp
+Raw L2CAP sockets support number of
+.Xr ioctl 2
+requests such as:
+.Bl -tag -width foo
+.It Dv SIOC_L2CAP_NODE_GET_FLAGS
+Returns current state for the L2CAP node.
+.It Dv SIOC_L2CAP_NODE_GET_DEBUG
+Returns current debug level for the L2CAP node.
+.It Dv SIOC_L2CAP_NODE_SET_DEBUG
+Sets current debug level for the L2CAP node.
+.It Dv SIOC_L2CAP_NODE_GET_CON_LIST
+Returns list of active baseband connections (i.e. ACL links) for the L2CAP
+node.
+.It Dv SIOC_L2CAP_NODE_GET_CHAN_LIST
+Returns list of active channels for the L2CAP node.
+.It Dv SIOC_L2CAP_L2CA_PING
+Issues L2CAP ECHO_REQUEST.
+.It Dv SIOC_L2CAP_L2CA_GET_INFO
+Issues L2CAP GET_INFO request.
+.El
+.Pp
+.Ss SOCK_SEQPACKET L2CAP sockets
+Implemented by
+.Cm btsock_l2c
+Netgraph type.
+L2CAP sockets are either
+.Dq active
+or
+.Dq passive .
+Active sockets initiate connections to passive sockets. By default L2CAP
+sockets are created active; to create a passive socket the
+.Xr listen 2
+system call must be used after binding the socket with the
+.Xr bind 2
+system call. Only passive sockets may use the
+.Xr accept 2
+call to accept incoming connections. Only active sockets may use the
+.Xr connect 2
+call to initiate connections.
+.Pp
+L2CAP sockets supports
+.Dq wildcard addressing .
+In this case socket must be bound to
+.Dv NG_HCI_BDADDR_ANY
+address. Note that PSM (Protocol/Service Multiplexor) filed is always
+required. Once a connection has been established the socket's address is
+fixed by the peer entity's location. The address assigned the socket is
+the address associated with the Bluetooth device through which packets are
+being transmitted and received, and PSM (Protocol/Service Multiplexor).
+.Pp
+L2CAP sockets support number of options defined at
+.Dv SOL_L2CAP
+level which can be set with
+.Xr setsockopt 2
+and tested with
+.Xr getsockopt 2 :
+.Bl -tag -width foo
+.It Dv SO_L2CAP_IMTU
+Get (set) maximum payload size the local socket is capable of accepting.
+.It Dv SO_L2CAP_OMTU
+Get maximum payload size the remote socket is capable of accepting.
+.It Dv SO_L2CAP_IFLOW
+Get incoming flow specification for the socket.
+.Em Not implemented .
+.It Dv SO_L2CAP_OFLOW
+Get (set) outgoing flow specification for the socket.
+.Em Not implemented .
+.It Dv SO_L2CAP_FLUSH
+Get (set) value of the flush timeout.
+.Em Not implemeted .
+.El
+.Sh HOOKS
+This node type supports hooks with arbitrary names (as long as they are
+unique) and always accepts hook connection requests.
+.Sh NETGRAPH CONTROL MESSAGES
+This node type supports the generic control messages.
+.Sh SHUTDOWN
+These nodes are persistent and cannot be shut down.
+.Sh BUGS
+Most likely. Please report if found.
+.Sh SEE ALSO
+.Xr socket 2 ,
+.Xr netgraph 4 ,
+.Xr ngctl 8 ,
+.Xr ng_hci 4 ,
+.Xr ng_l2cap 4 ,
+.Xr btsockstat 1
+.Sh HISTORY
+The
+.Nm
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_h4.4 b/share/man/man4/ng_h4.4
new file mode 100644
index 0000000..bc76e1c
--- /dev/null
+++ b/share/man/man4/ng_h4.4
@@ -0,0 +1,121 @@
+.\" ng_h4.4
+.\"
+.\" 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_h4.4,v 1.3 2002/11/12 17:10:13 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt NG_H4 4
+.Os
+.Sh NAME
+.Nm h4
+.Nd Netgraph node type that is also a H4 line discipline
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/ttycom.h
+.In netgraph/ng_message.h
+.In netgraph/ng_h4.h
+.Sh DESCRIPTION
+The
+.Nm
+node type is both a persistent Netgraph node type and a H4 line
+discipline. It implements a Bluetooth HCI UART transport layer as
+per chapter H4 of the Bluetooth Specification Book v1.1. A new
+node is created when the corresponding line discipline,
+.Dv H4DISC ,
+is registered on a tty device (see
+.Xr tty 4 ) .
+.Pp
+The node has a single hook called
+.Dv hook .
+Incoming bytes received on the tty device are re-assembled into
+HCI frames (according to the length). Full HCI frames are sent out on
+the hook. HCI frames received on
+.Dv hook
+are transmitted out on the tty device.
+No modification to the data is performed in either direction.
+While the line discipline is installed on a tty, the normal
+read and write operations are unavailable, returning
+.Er EIO .
+.Pp
+Information about the node is available via the netgraph
+.Xr ioctl 2
+command
+.Dv NGIOCGINFO .
+This command returns a
+.Dv "struct nodeinfo"
+similar to the
+.Dv NGM_NODEINFO
+netgraph control message.
+.Sh HOOKS
+This node type supports the following hooks:
+.Pp
+.Bl -tag -width foobar
+.It Dv hook
+single HCI frame contained in single
+.Dv mbuf
+structure.
+.El
+.Sh CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_H4_NODE_RESET
+Reset the node.
+.It Dv NGM_H4_NODE_GET_STATE
+Returns current receiving state for the node.
+.It Dv NGM_H4_NODE_GET_DEBUG
+Returns an integer containing the current debug level for the node.
+.It Dv NGM_H4_NODE_SET_DEBUG
+This command takes an integer argument and sets current debug level
+for the node.
+.It Dv NGM_H4_NODE_GET_QLEN
+Returns current length of outgoing queue for the node.
+.It Dv NGM_H4_NODE_SET_QLEN
+This command takes an integer argument and sets maximum length of
+outgoing queue for the node.
+.It Dv NGM_H4_NODE_GET_STAT
+Returns various statistic information for the node, such as: number of
+bytes (frames) sent, number of bytes (frames) received and number of
+input (output) errors.
+.It Dv NGM_H4_NODE_RESET_STAT
+Reset all statistic counters to zero.
+.El
+.Sh SHUTDOWN
+This node shuts down when the corresponding device is closed
+(or the line discipline is uninstalled on the device).
+.Sh BUGS
+This node still uses spltty() to lock tty layer. This is wrong.
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr netgraph 4 ,
+.Xr tty 4 ,
+.Xr ngctl 8
+.Sh HISTORY
+The
+.Nm
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_hci.4 b/share/man/man4/ng_hci.4
new file mode 100644
index 0000000..36272e0
--- /dev/null
+++ b/share/man/man4/ng_hci.4
@@ -0,0 +1,342 @@
+.\" ng_hci.4
+.\"
+.\" 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_hci.4,v 1.8 2002/11/12 22:35:39 max Exp $
+.\" $FreeBSD$
+.Dd June 25, 2002
+.Dt NG_HCI 4
+.Os
+.Sh NAME
+.Nm hci
+.Nd Netgraph node type that is also a Bluetooth Host Controller Interface
+(HCI) layer
+.Sh SYNOPSIS
+.In sys/types.h
+.In netgraph/ng_message.h
+.In netgraph/netgraph.h
+.In netgraph/ng_hci.h
+.Sh DESCRIPTION
+The
+.Nm
+node type is a Netgraph node type that implements Bluetooth Host Controller
+Interface (HCI) layer as per chapter H1 of the Bluetooth Specification Book
+v1.1.
+.Sh INTRODUCTION TO BLUETOOTH
+Bluetooth is a short-range radio link intended to replace the cable(s)
+connecting portable and/or fixed electronic devices. Bluetooth operates
+in the unlicensed ISM band at 2.4 GHz. The Bluetooth protocol uses a
+combination of circuit and packet switching. Bluetooth can support an
+asynchronous data channel, up to three simultaneous synchronous voice
+channels, or a channel which simultaneously supports asynchronous data
+and synchronous voice. Each voice channel supports a 64 kb/s synchronous
+(voice) channel in each direction. The asynchronous channel can support
+maximal 723.2 kb/s asymmetric (and still up to 57.6 kb/s in the return
+direction), or 433.9 kb/s symmetric.
+.Pp
+The Bluetooth system provides a point-to-point connection (only two
+Bluetooth units involved), or a point-to-multipoint connection. In the
+point-to-multipoint connection, the channel is shared among several
+Bluetooth units. Two or more units sharing the same channel form a
+.Em piconet .
+One Bluetooth unit acts as the master of the piconet, whereas the other
+unit(s) acts as slave(s). Up to seven slaves can be active in the piconet.
+In addition, many more slaves can remain locked to the master in a so-called
+parked state. These parked slaves cannot be active on the channel, but remain
+synchronized to the master. Both for active and parked slaves, the channel
+access is controlled by the master.
+.Pp
+Multiple piconets with overlapping coverage areas form a
+.Em scatternet .
+Each piconet can only have a single master. However, slaves can participate
+in different piconets on a time-division multiplex basis. In addition, a
+master in one piconet can be a slave in another piconet. The piconets shall
+not be frequency-synchronized. Each piconet has its own hopping channel.
+.Ss Time slots
+The channel is divided into time slots, each 625 usec in length. The time
+slots are numbered according to the Bluetooth clock of the piconet master.
+The slot numbering ranges from 0 to 2^27 -1 and is cyclic with a cycle length
+of 2^27. In the time slots, master and slave can transmit packets.
+.Ss SCO link
+The SCO link is a symmetric, point-to-point link between the master and a
+specific slave. The SCO link reserves slots and can therefore be considered
+as a circuit-switched connection between the master and the slave. The SCO
+link typically supports time-bounded information like voice. The master can
+support up to three SCO links to the same slave or to different slaves. A
+slave can support up to three SCO links from the same master, or two SCO
+links if the links originate from different masters. SCO packets are never
+retransmitted.
+.Ss ACL link
+In the slots not reserved for SCO links, the master can exchange packets
+with any slave on a per-slot basis. The ACL link provides a packet-switched
+connection between the master and all active slaves participating in the
+piconet. Both asynchronous and isochronous services are supported. Between
+a master and a slave only a single ACL link can exist. For most ACL packets,
+packet retransmission is applied to assure data integrity.
+.Sh HOST CONTROLLER INTERFACE (HCI)
+The HCI provides a command interface to the baseband controller and link
+manager, and access to hardware status and control registers. This interface
+provides a uniform method of accessing the Bluetooth baseband capabilities.
+.Pp
+The HCI layer on the Host exchanges data and commands with the HCI firmware
+on the Bluetooth hardware. The Host Controller Transport Layer (i.e. physical
+bus) driver provides both HCI layers with the ability to exchange information
+with each other.
+.Pp
+The Host will receive asynchronous notifications of HCI events independent
+of which Host Controller Transport Layer is used. HCI events are used for
+notifying the Host when something occurs. When the Host discovers that an
+event has occurred it will then parse the received event packet to determine
+which event occurred.
+The next sections specify the HCI packet formats.
+.Ss HCI command packet
+.Bd -literal -offset indent
+#define NG_HCI_CMD_PKT 0x01
+typedef struct {
+ u_int8_t type; /* MUST be 0x1 */
+ u_int16_t opcode; /* OpCode */
+ u_int8_t length; /* parameter(s) length in bytes */
+} __attribute__ ((packed)) ng_hci_cmd_pkt_t;
+.Ed
+.Pp
+The HCI command packet is used to send commands to the Host Controller
+from the Host. When the Host Controller completes most of the commands,
+a Command Complete event is sent to the Host. Some commands do not receive
+a Command Complete event when they have been completed. Instead, when the
+Host Controller receives one of these commands the Host Controller sends
+a Command Status event back to the Host when it has begun to execute the
+command. Later on, when the actions associated with the command have finished,
+an event that is associated with the sent command will be sent by the Host
+Controller to the Host.
+.Ss HCI event packet
+.Bd -literal -offset indent
+#define NG_HCI_EVENT_PKT 0x04
+typedef struct {
+ u_int8_t type; /* MUST be 0x4 */
+ u_int8_t event; /* event */
+ u_int8_t length; /* parameter(s) length in bytes */
+} __attribute__ ((packed)) ng_hci_event_pkt_t;
+.Ed
+.Pp
+The HCI event packet is used by the Host Controller to notify the Host
+when events occur.
+.Ss HCI ACL data packet
+.Bd -literal -offset indent
+#define NG_HCI_ACL_DATA_PKT 0x02
+typedef struct {
+ u_int8_t type; /* MUST be 0x2 */
+ u_int16_t con_handle; /* connection handle + PB + BC flags */
+ u_int16_t length; /* payload length in bytes */
+} __attribute__ ((packed)) ng_hci_acldata_pkt_t;
+.Ed
+.Pp
+HCI ACL data packets are used to exchange ACL data between the Host and
+Host Controller.
+.Ss HCI SCO data packet
+.Bd -literal -offset indent
+#define NG_HCI_SCO_DATA_PKT 0x03
+typedef struct {
+ u_int8_t type; /* MUST be 0x3 */
+ u_int16_t con_handle; /* connection handle + reserved bits */
+ u_int8_t length; /* payload length in bytes */
+} __attribute__ ((packed)) ng_hci_scodata_pkt_t;
+.Ed
+.Pp
+HCI SCO data packets are used to exchange SCO data between the Host and
+Host Controller.
+.Sh HCI INITIALIZATION
+On initialization, HCI control application must issue the following HCI
+commands (in any order).
+.Bl -tag -width foobar
+.It Dv Read_BD_ADDR
+To obtain BD_ADDR of the Bluetooth unit.
+.It Dv Read_Local_Supported_Features
+To obtain the list of features supported by Bluetooth unit.
+.It Dv Read_Buffer_Size
+To determine the maximum size of HCI ACL and SCO HCI data packets (excluding
+header) that can be sent from the Host to the Host Controller. There are also
+two additional return parameters that specify the total number of HCI ACL and
+SCO data packets that the Host Controller can have waiting for transmission in
+its buffers.
+.El
+.Pp
+As soon as HCI initialization has been successfuly performed, HCI control
+application must turn on
+.Dq inited
+bit for the node. Once HCI node has been initialized all upsteam hooks
+will receive a
+.Dv NGM_HCI_NODE_UP
+Netgraph message defined as follows.
+.Bd -literal -offset indent
+#define NGM_HCI_NODE_UP 112 /* HCI -> Upper */
+typedef struct {
+ u_int16_t pkt_size; /* max. ACL/SCO packet size (w/o hdr) */
+ u_int16_t num_pkts; /* ACL/SCO packet queue size */
+ u_int16_t reserved; /* place holder */
+ bdaddr_t bdaddr; /* bdaddr */
+} ng_hci_node_up_ep;
+.Ed
+.Sh HCI FLOW CONTROL
+HCI layer performs flow control on baseband connection basis (i.e. ACL and
+SCO link). Each baseband connection has
+.Em connection handle
+and queue of outgoing data packets. Upper layers protocols are allowed to
+send up to (
+.Dv num_pkts -
+.Dv pending )
+packets at one time. HCI layer will send
+.Dv NGM_HCI_SYNC_CON_QUEUE
+Netgraph messages to inform upper layers about current queue state for each
+connection handle. The
+.Dv NGM_HCI_SYNC_CON_QUEUE
+Netgraph message is defined as follows.
+.Bd -literal -offset indent
+#define NGM_HCI_SYNC_CON_QUEUE 113 /* HCI -> Upper */
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t completed; /* number of completed packets */
+} ng_hci_sync_con_queue_ep;
+.Ed
+.Sh HOOKS
+This node type supports the following hooks:
+.Pp
+.Bl -tag -width foobar
+.It Dv drv
+Bluetooth Host Controller Transport Layer hook. Single HCI packet contained in
+single
+.Dv mbuf
+structure.
+.It Dv acl
+Upper layer protocol/node is connected to the hook. Single HCI ACL
+data packet contained in single
+.Dv mbuf
+structure.
+.It Dv sco
+Upper layer protocol/node is connected to the hook. Single HCI SCO
+data packet contained in single
+.Dv mbuf
+structure.
+.It Dv raw
+Raw hook. Every HCI frame (including HCI command frame) that goes in
+or out will be delivired to the hook. Usually Bluetooth raw HCI sockets
+layer is connected to the hook. Single HCI frame contained in single
+. Dv mbuf
+structure.
+.El
+.Sh BLUETOOTH UPPER LAYER PROTOCOLS INTERFACE (LP CONTROL MESSAGES)
+.Bl -tag -width foo
+.It Dv NGM_HCI_LP_CON_REQ
+Requests the lower protocol to create a connection. If a physical link
+to the remote device does not exist, this message must be sent to the lower
+protocol (baseband) to establish the physical connection.
+.It Dv NGM_HCI_LP_DISCON_REQ
+Requests the lower protocol (baseband) to terminate a connection.
+.It Dv NGM_HCI_LP_CON_CFM
+Confirms success or failure of the
+.Dv
+NGM_HCI_LP_CON_REQ request to establish a lower layer (baseband) connection.
+This includes passing the authentication challenge if authentication is
+required to establish the physical link.
+.It Dv NGM_HCI_LP_CON_IND
+Indicates the lower protocol (baseband) has successfully established
+incoming connection.
+.It Dv NGM_HCI_LP_CON_RSP
+A response accepting or rejecting the previous connection indication request.
+.It Dv NGM_HCI_LP_DISCON_IND
+Indicates the lower protocol (baseband) has terminated connection. This
+could be a response to
+.Dv NGM_HCI_LP_DISCON_REQ
+or a timeout event.
+.It Dv NGM_HCI_LP_QOS_REQ
+Requests the lower protocol (baseband) to accommodate a particular QoS
+parameter set.
+.It Dv NGM_HCI_LP_QOS_CFM
+Confirms success or failure of the request for a given quality of service.
+.It Dv NGM_HCI_LP_QOS_IND
+Indicates the lower protocol (baseband) has detected a violation of the QoS
+agreement.
+.El
+.Sh NETGRAPH CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_HCI_NODE_GET_STATE
+Returns current state for the node.
+.It Dv NGM_HCI_NODE_INIT
+Turn on
+.Dq inited
+bit for the node.
+.It Dv NGM_HCI_NODE_GET_DEBUG
+Returns an integer containing the current debug level for the node.
+.It Dv NGM_HCI_NODE_SET_DEBUG
+This command takes an integer argument and sets current debug level
+for the node.
+.It Dv NGM_HCI_NODE_GET_BUFFER
+Returns current state of data buffers.
+.It Dv NGM_HCI_NODE_GET_BDADDR
+Returns BD_ADDR as cached in the node.
+.It Dv NGM_HCI_NODE_GET_FEATURES
+Returns the list of features supported by hardware (as cached by the node).
+.It Dv NGM_HCI_NODE_GET_NEIGHBOR_CACHE
+Returns content of the neighbor cache.
+.It Dv NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE
+Remove all neighbor cache entries.
+.It Dv NGM_HCI_NODE_GET_CON_LIST
+Returns list of active baseband connections (i.e. ACL and SCO links).
+.It Dv NGM_HCI_NODE_GET_STAT
+Returns various statistic counters.
+.It Dv NGM_HCI_NODE_RESET_STAT
+Resets all statistic counters to zero.
+.It NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK
+Sets current link policy settings mask. After the new ACL connection is
+created the HCI node will try set link policy for the ACL connection. By
+default every supported Link Manager (LM) mode will be enabled. User can
+override this by setting link policy settings mask which specifies LM
+modes to be enabled.
+.It NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK
+Returns current link policy settings mask.
+.It NGM_HCI_NODE_SET_PACKET_MASK
+Sets current packet mask. When new baseband (ACL or SCO) connection is
+created the HCI node will specify every packet type supported by the device.
+User can override this by setting packet mask which specifies packet types
+to be used for new baseband connections.
+.It NGM_HCI_NODE_GET_PACKET_MASK
+Returns current packet mask.
+.El
+.Sh SHUTDOWN
+This node shuts down upon receipt of a NGM_SHUTDOWN control message, or
+when all hooks have been disconnected.
+.Sh BUGS
+Most likely. Please report if found.
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr ngctl 8 ,
+.Xr hccontrol 8
+.Sh HISTORY
+The
+.Nm
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_l2cap.4 b/share/man/man4/ng_l2cap.4
new file mode 100644
index 0000000..38f4bc8
--- /dev/null
+++ b/share/man/man4/ng_l2cap.4
@@ -0,0 +1,341 @@
+.\" ng_l2cap.4
+.\"
+.\" 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_l2cap.4,v 1.4 2002/11/12 17:16:19 max Exp $
+.\" $FreeBSD$
+.Dd July 4, 2002
+.Dt NG_L2CAP 4
+.Os
+.Sh NAME
+.Nm l2cap
+.Nd Netgraph node type that implements Bluetooth Logical Link Control and
+Adaptation Protocol (L2CAP)
+.Sh SYNOPSIS
+.In sys/types.h
+.In netgraph/ng_message.h
+.In netgraph/netgraph.h
+.In netgraph/ng_hci.h
+.In netgraph/ng_l2cap.h
+.Sh DESCRIPTION
+The
+.Nm
+node type is a Netgraph node type that implements Bluetooth Logical Link
+Control and Adaptation Protocol as per chapter D of the Bluetooth Specification
+Book v1.1.
+.Pp
+L2CAP provides connection-oriented and connectionless data services to upper
+layer protocols with protocol multiplexing capability, segmentation and
+reassembly operation, and group abstractions. L2CAP permits higher level
+protocols and applications to transmit and receive L2CAP data packets up to
+64 kilobytes in length.
+.Ss L2CAP assumptions
+.Bl -enum -offset indent
+.It
+The ACL link between two units is set up. The Baseband provides orderly
+delivery of data packets, although there might be individual packet corruption
+and duplicates. No more than 1 ACL link exists between any two devices.
+.It
+The Baseband always provides the impression of full-duplex communication
+channels. This does not imply that all L2CAP communications are bi-directional.
+Multicasts and unidirectional traffic (e.g., video) do not require duplex
+channels.
+.It
+L2CAP provides a reliable channel using the mechanisms available at the
+Baseband layer. The Baseband always performs data integrity checks when
+requested and resends data until it has been successfully acknowledged or
+a timeout occurs. Because acknowledgements may be lost, timeouts may
+occur even after the data has been successfully sent.
+.El
+.Sh L2CAP GENERAL OPERATION
+The Logical Link Control and Adaptation Protocol (L2CAP) is based around the
+concept of
+.Em channels .
+Each channel is bound to a single protocol in a many-to-one fashion. Multiple
+channels can be bound to the same protocol, but a channel cannot be bound to
+multiple protocols. Each L2CAP packet received on a channel is directed to
+the appropriate higher level protocol.
+.Pp
+Each one of the end-points of an L2CAP channel is referred to by a channel
+identifier. Channel identifiers (CIDs) are local names representing a logical
+channel end-point on the device. Identifiers from 0x0001 to 0x003F are reserved
+for specific L2CAP functions. The null identifier (0x0000) is defined as an
+illegal identifier and must never be used as a destination end-point.
+All L2CAP signalling commands are sent to CID 0x0001. CID 0x0002 is reserved
+for group-oriented channel. The same CID must not be reused as a local L2CAP
+channel endpoint for multiple simultaneous L2CAP channels between a local
+device and some remote device.
+.Pp
+CID assignment is relative to a particular device and a device can assign CIDs
+independently from other devices. Thus, even if the same CID value has been
+assigned to (remote) channel endpoints by several remote devices connected
+to a single local device, the local device can still uniquely associate each
+remote CID with a different device.
+.Ss Channel operational states
+.Bl -tag -width foobar
+.It Dv NG_L2CAP_CLOSED
+In this state, there is no channel associated with this CID. This is the only
+state when a link level connection (Baseband) may not exist. Link disconnection
+forces all other states into the NG_L2CAP_CLOSED state.
+.It Dv NG_L2CAP_W4_L2CAP_CON_RSP
+In this state, the CID represents a local end-point and an L2CAP Connect
+Request message has been sent referencing this endpoint and it is now waiting
+for the corresponding L2CAP Connect Response message.
+.It Dv NG_L2CAP_W4_L2CA_CON_RSP
+In this state, the remote end-point exists and an L2CAP Connect Request has
+been received by the local L2CAP entity. An L2CA Connect Indication has been
+sent to the upper layer and the part of the local L2CAP entity processing the
+received L2CAP Connect Request waits for the corresponding response. The
+response may require a security check to be performed.
+.It Dv NG_L2CAP_CONFIG
+In this state, the connection has been established but both sides are still
+negotiating the channel parameters. The Configuration state may also be
+entered when the channel parameters are being renegotiated. Prior to entering
+the NG_L2CAP_CONFIG state, all outgoing data traffic is suspended since
+the traffic parameters of the data traffic are to be renegotiated. Incoming
+data traffic is accepted until the remote channel endpoint has entered
+the NG_L2CAP_CONFIG state. In the NG_L2CAP_CONFIG state, both sides will issue
+L2CAP Configuration Request messages if only defaults are being used, a null
+message will be sent. If a large amount of parameters need to be negotiated,
+multiple messages will be sent to avoid any MTU limitations and negotiate
+incrementally. Moving from the NG_L2CAP_CONFIG state to the NG_L2CAP_OPEN state
+requires both sides to be ready. An L2CAP entity is ready when it has received
+a positive response to its final request and it has positively responded to
+the final request from the remote device.
+.It Dv NG_L2CAP_OPEN
+In this state, the connection has been established and configured, and data
+flow may proceed.
+.It Dv NG_L2CAP_W4_L2CAP_DISCON_RSP
+In this state, the connection is shutting down and an L2CAP Disconnect Request
+message has been sent. This state is now waiting for the corresponding response.
+.It Dv NG_L2CAP_W4_L2CA_DISCON_RSP
+In this state, the connection on the remote endpoint is shutting down and an
+L2CAP Disconnect Request message has been received. An L2CA Disconnect
+Indication has been sent to the upper layer to notify the owner of the CID
+that the remote endpoint is being closed. This state is now waiting for the
+corresponding response from the upper layer before responding to the remote
+endpoint.
+.El
+.Ss Protocol Multiplexing
+L2CAP supports protocol multiplexing because the Baseband Protocol does not
+support any
+.Dq type
+field identifying the higher layer protocol being multiplexed above it.
+L2CAP is able to distinguish between upper layer protocols such as the Service
+Discovery Protocol, RFCOMM and Telephony Control.
+.Ss Segmentation and Reassembly
+The data packets defined by the Baseband Protocol are limited in size. Large
+L2CAP packets must be segmented into multiple smaller Baseband packets prior
+to their transmission over the air. Similarly, multiple received Baseband
+packets may be reassembled into a single larger L2CAP packet.
+.Ss Quality of Service
+The L2CAP connection establishment process allows the exchange of information
+regarding the quality of service (QoS) expected between two Bluetooth units.
+.Ss Groups
+The Baseband Protocol supports the concept of a piconet, a group of devices
+synchronously hopping together using the same clock. The L2CAP group
+abstraction permits implementations to efficiently map protocol groups on to
+piconets.
+.Pp
+The following features are outside the scope of L2CAP responsibilities:
+.Bl -dash -offset indent
+.It
+L2CAP does not transport audio designated for SCO links.
+.It
+L2CAP does not enforce a reliable channel or ensure data integrity,
+that is, L2CAP performs no retransmissions or checksum calculations.
+.It
+L2CAP does not support a reliable multicast channel.
+.It
+L2CAP does not support the concept of a global group name.
+.El
+.Sh HOOKS
+This node type supports the following hooks:
+.Pp
+.Bl -tag -width foobar
+.It Dv hci
+Bluetooth Host Controller Interface downstream hook.
+.It Dv l2c
+Upper layer protocol upstream hook. Usually Bluetooth L2CAP sockets layer
+is connected to the hook.
+.It Dv ctl
+Control hook. Usually Bluetooth raw L2CAP sockets layer is connected
+to the hook.
+.El
+.Sh INTERFACE TO THE UPPER LAYER PROTOCOLS (L2CA CONTROL MESSAGES)
+Bluetooth specification says that L2CA request must block until response
+is ready. L2CAP node uses
+.Dq token
+field from Netgraph message header to match L2CA request and response. The
+upper layer protocol must populate
+.Dq token .
+L2CAP node will queue request and start processing. Later, when response is
+ready or timeout has occur L2CAP node will create new Netgraph message, set
+.Dq token
+and
+.Dv NFG_RESP
+flag and send message to the upper layer. Note that L2CA indication messages
+will not populate
+.Dq token
+and will not set
+.Dv NGF_RESP
+flag. There is no reason for this, because they are just notifications and do
+not require acknowledgment.
+.Pp
+.Bl -tag -width foo
+.It Dv NGM_L2CAP_L2CA_CON
+Requests the creation of a channel representing a logical connection to a
+physical address. Input parameters are the target protocol (PSM) and remote
+device's 48-bit address (BD_ADDR). Output parameters are the local CID (LCID)
+allocated by the local L2CAP entity, and Result of the request. If Result
+indicates a pending notification, the Status value may contain more information
+of what processing is delaying the establishment of the connection.
+.It Dv NGM_L2CAP_L2CA_CON_IND
+This message includes the parameters for the address of the remote device that
+issued the connection request, the local CID representing the channel being
+requested, the Identifier contained in the request, and the PSM value the
+request is targeting.
+.It Dv NGM_L2CAP_L2CA_CON_RSP
+Issues a response to a connection request event indication. Input parameters
+are the remote device's 48-bit address, Identifier sent in the request, local
+CID, the Response code, and the Status attached to the Response code. The
+output parameter is the Result of the service request. This primitive must be
+called no more than once after receiving the indication.
+.It Dv NGM_L2CAP_L2CA_CFG
+Requests the initial configuration (or reconfiguration) of a channel to a new
+set of channel parameters. Input parameters are the local CID endpoint, new
+incoming receivable MTU (InMTU), new outgoing flow spec-ification, and flush
+and link timeouts. Output parameters are the Result, accepted incoming MTU
+(InMTU), the remote side's flow requests, and flush and link timeouts.
+.It Dv NGM_L2CAP_L2CA_CFG_IND
+This message includes the parameters indicating the local CID of the channel
+the request has been sent to, the outgoing MTU size (maximum packet that can
+be sent across the channel) and the flowspec describing the characteristics of
+the incoming data. All other channel parameters are set to their default values if not provided by the remote device.
+.It Dv NGM_L2CAP_L2CA_CFG_RSP
+Issues a response to a configuration request event indication. Input parameters
+include the local CID of the endpoint being configured, outgoing transmit MTU
+(which may be equal or less to the OutMTU parameter in the configuration
+indication event) and the accepted flowspec for incoming traffic. The output
+parameter is the Result value.
+.It Dv NGM_L2CAP_L2CA_QOS_IND
+This message includes the parameter indicating the address of the remote
+Bluetooth device where the QoS contract has been violated.
+.It Dv NGM_L2CAP_L2CA_DISCON
+Requests the disconnection of the channel. Input parameter is the CID
+representing the local channel endpoint. Output parameter is Result. Result
+is zero if a L2CAP Disconnect Response is received, otherwise a non-zero value
+is returned. Once disconnection has been requested, no process will be able to
+successfully read or write from the CID.
+.It Dv NGM_L2CAP_L2CA_DISCON_IND
+This message includes the parameter indicating the local CID the request has
+been sent to.
+.It Dv NGM_L2CAP_L2CA_WRITE
+Response to transfer of data request. Actual data must be received from
+appropriate upstream hook and must be prepended with header defined as follows.
+.Pp
+.Bd -literal -offset indent
+/* L2CA data packet header */
+typedef struct {
+ u_int32_t token; /* token to use in L2CAP_L2CA_WRITE */
+ u_int16_t length; /* length of the data */
+ u_int16_t lcid; /* local channel ID */
+} __attribute__ ((packed)) ng_l2cap_l2ca_hdr_t;
+.Ed
+.Pp
+The output parameters are Result and Length of data written.
+.It Dv NGM_L2CAP_L2CA_GRP_CREATE
+Requests the creation of a CID to represent a logical connection to multiple
+devices. Input parameter is the PSM value that the outgoing connectionless
+traffic is labelled with, and the filter used for incoming traffic. Output
+parameter is the CID representing the local endpoint. On creation, the group
+is empty but incoming traffic destined for the PSM value is readable.
+.Em This request has not been implemented .
+.It Dv NGM_L2CAP_L2CA_GRP_CLOSE
+The use of this message closes down a Group.
+.Em This request has not been implemented .
+.It Dv NGM_L2CAP_L2CA_GRP_ADD_MEMBER
+Requests the addition of a member to a group. The input parameter includes the
+CID representing the group and the BD_ADDR of the group member to be added.
+The output parameter Result confirms the success or failure of the request.
+.Em This request has not been implemented .
+.It Dv NGM_L2CAP_L2CA_GRP_REM_MEMBER
+Requests the removal of a member from a group. The input parameters include
+the CID representing the group and BD_ADDR of the group member to be removed.
+The output parameter Result confirms the success or failure of the request.
+.Em This request has not been implemented .
+.It Dv NGM_L2CAP_L2CA_GRP_MEMBERSHIP
+Requests a report of the members of a group. The input parameter CID represents
+the group being queried. The output parameter Result confirms the success or
+failure of the operation. If the Result is successful, BD_ADDR_Lst is a list
+of the Bluetooth addresses of the N members of the group.
+.Em This request has not been implemented .
+.It Dv NGM_L2CAP_L2CA_PING
+Initiates a L2CA Echo Request message and the reception of the corresponding
+L2CAP Echo Response message. The input parameters are remote Bluetooth device
+BD_ADDR, Echo Data and Length of the echo data. The output parameters are
+Result, Echo Data and Length of the echo data.
+.It Dv NGM_L2CAP_L2CA_GET_INFO
+Initiates a L2CA Information Request message and the reception of the
+corresponding L2CAP Info Response message. The input parameters are remote
+Bluetooth device BD_ADDR and Information Type. The output parameters are
+Result, Information Data and Size of the information data.
+.It Dv NGM_L2CAP_L2CA_ENABLE_CLT
+Request to disable (enable) the reception of connectionless packets. The input
+parameter is the PSM value indicating service that should be blocked
+(unblocked) and Enable flag.
+.El
+.Sh NETGRAPH CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_L2CAP_NODE_GET_FLAGS
+Returns current state for the node.
+.It Dv NGM_L2CAP_NODE_GET_DEBUG
+Returns an integer containing the current debug level for the node.
+.It Dv NGM_L2CAP_NODE_SET_DEBUG
+This command takes an integer argument and sets current debug level
+for the node.
+.It Dv NGM_L2CAP_NODE_GET_CON_LIST
+Returns list of active baseband connections (i.e. ACL links).
+.It Dv NGM_L2CAP_NODE_GET_CHAN_LIST
+Returns list of active L2CAP channels.
+.El
+.Sh SHUTDOWN
+This node shuts down upon receipt of a NGM_SHUTDOWN control message, or
+when all hooks have been disconnected.
+.Sh BUGS
+Most likely. Please report if found.
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr ngctl 8 ,
+.Xr l2control 8 ,
+.Xr l2ping 8
+.Sh HISTORY
+The
+.Nm
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/share/man/man4/ng_ubt.4 b/share/man/man4/ng_ubt.4
new file mode 100644
index 0000000..648d27b
--- /dev/null
+++ b/share/man/man4/ng_ubt.4
@@ -0,0 +1,102 @@
+.\" ng_ubt.4
+.\"
+.\" 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_ubt.4,v 1.2 2002/11/12 17:20:16 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt NG_UBT 4
+.Os
+.Sh NAME
+.Nm ubt
+.Nd Netgraph node type that is also a driver for Bluetooth USB devices
+.Sh SYNOPSIS
+.In sys/types.h
+.In ng_ubt.h
+.Sh DESCRIPTION
+The
+.Nm
+node type is both a persistent Netgraph node type and a driver for
+Bluetooth USB devices. It implements a Bluetooth USB transport layer
+as per chapter H2 of the Bluetooth Specification Book v1.1. A new
+node is created when supported USB device is plugged.
+.Pp
+The node has a single hook called
+.Dv hook .
+Incoming bytes received on the device are re-assembled into HCI frames
+(according to the length). Full HCI frames are sent out on the hook. The node
+will add HCI frame indicator if device did not send it. HCI frames received
+on
+.Dv hook
+are transmitted out. The node will drop HCI frame indicator unless device
+requires it to be present.
+.Sh HOOKS
+This node type supports the following hooks:
+.Pp
+.Bl -tag -width foobar
+.It Dv hook
+single HCI frame contained in single
+.Dv mbuf
+structure.
+.El
+.Sh CONTROL MESSAGES
+This node type supports the generic control messages, plus the following:
+.Bl -tag -width foo
+.It Dv NGM_UBT_NODE_GET_DEBUG
+Returns an integer containing the current debug level for the node.
+.It Dv NGM_UBT_NODE_SET_DEBUG
+This command takes an integer argument and sets current debug level
+for the node.
+.It Dv NGM_UBT_NODE_GET_QLEN
+This command takes a parameter that specifies queue number and returns
+current maximal length of the queue for the node.
+.It Dv NGM_UBT_NODE_SET_QLEN
+This command takes two parameters that specify queue number and maximum
+length of the queue and sets maximal length of the queue for the node.
+.It Dv NGM_UBT_NODE_GET_STAT
+Returns various statistic information for the node, such as: number of
+bytes (frames) sent, number of bytes (frames) received and number of
+input (output) errors.
+.It Dv NGM_UBT_NODE_RESET_STAT
+Reset all statistic counters to zero.
+.El
+.Sh SHUTDOWN
+This node shuts down when the corresponding USB device is un-plugged.
+.Sh BUGS
+Isochronous USB transfers are probably broken. It means that USB device
+probably will not be able to transfer SCO data (voice). Driver does not
+support firmware upgrade procedure. USB interrupt transfers are
+implemented as bulk-in transfers (not really a bug).
+.Sh SEE ALSO
+.Xr usb 4 ,
+.Xr netgraph 4 ,
+.Xr ngctl 8
+.Sh HISTORY
+The
+.Nm
+node type was implemented in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/sys/modules/netgraph/bluetooth/Makefile b/sys/modules/netgraph/bluetooth/Makefile
new file mode 100644
index 0000000..e39315e
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+SUBDIR= \
+ bluetooth \
+ hci \
+ l2cap \
+ socket \
+ bt3c \
+ h4 \
+ ubt
+
+.include <bsd.subdir.mk>
+
diff --git a/sys/modules/netgraph/bluetooth/bluetooth/Makefile b/sys/modules/netgraph/bluetooth/bluetooth/Makefile
new file mode 100644
index 0000000..d4aa60f
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/bluetooth/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.1.1.1 2002/09/04 21:47:41 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/common
+
+CFLAGS+= -I../../../../netgraph/bluetooth/include
+
+KMOD= ng_bluetooth
+SRCS= ng_bluetooth.c
+MAN4= ng_bluetooth.4
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/netgraph/bluetooth/bt3c/Makefile b/sys/modules/netgraph/bluetooth/bt3c/Makefile
new file mode 100644
index 0000000..31f5784
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/bt3c/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.6 2002/09/04 21:42:00 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/drivers/bt3c
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -I../../../../netgraph/bluetooth/drivers/bt3c \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1 \
+ -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_bt3c
+SRCS= ng_bt3c_pccard.c bus_if.h card_if.h device_if.h
+MAN4= ng_bt3c.4
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/netgraph/bluetooth/h4/Makefile b/sys/modules/netgraph/bluetooth/h4/Makefile
new file mode 100644
index 0000000..7af1768
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/h4/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.7 2002/11/03 02:15:54 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/drivers/h4
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -I../../../../netgraph/bluetooth/drivers/h4 \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1
+# -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_h4
+SRCS= ng_h4.c
+MAN4= ng_h4.4
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/netgraph/bluetooth/hci/Makefile b/sys/modules/netgraph/bluetooth/hci/Makefile
new file mode 100644
index 0000000..92ffcc5
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/hci/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.5 2002/09/04 21:36:51 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/hci
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -I../../../../netgraph/bluetooth/hci \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1 \
+ -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_hci
+SRCS= ng_hci_main.c ng_hci_cmds.c ng_hci_evnt.c \
+ ng_hci_ulpi.c ng_hci_misc.c
+MAN4= ng_hci.4
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/netgraph/bluetooth/l2cap/Makefile b/sys/modules/netgraph/bluetooth/l2cap/Makefile
new file mode 100644
index 0000000..cd90fd3
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/l2cap/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.4 2002/09/04 21:38:38 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/l2cap
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -I../../../../netgraph/bluetooth/l2cap \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1 \
+ -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_l2cap
+SRCS= ng_l2cap_main.c ng_l2cap_cmds.c ng_l2cap_evnt.c \
+ ng_l2cap_ulpi.c ng_l2cap_llpi.c ng_l2cap_misc.c
+MAN4= ng_l2cap.4
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/netgraph/bluetooth/socket/Makefile b/sys/modules/netgraph/bluetooth/socket/Makefile
new file mode 100644
index 0000000..b9969a5
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/socket/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.7 2002/09/04 21:43:59 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/socket
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1 \
+ -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_btsocket
+SRCS= ng_btsocket.c \
+ ng_btsocket_hci_raw.c \
+ ng_btsocket_l2cap_raw.c \
+ ng_btsocket_l2cap.c
+MAN4= ng_btsocket.4
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/netgraph/bluetooth/ubt/Makefile b/sys/modules/netgraph/bluetooth/ubt/Makefile
new file mode 100644
index 0000000..1e07218
--- /dev/null
+++ b/sys/modules/netgraph/bluetooth/ubt/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.5 2002/09/04 21:41:06 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../netgraph/bluetooth/drivers/ubt
+
+CFLAGS+= -g -I../../../../netgraph/bluetooth/include \
+ -I../../../../netgraph/bluetooth/drivers/ubt \
+ -DINVARIANTS=1 -DINVARIANT_SUPPORT=1 \
+ -DWITNESS=1 -DWITNESS_SKIPSPIN=1
+
+KMOD= ng_ubt
+SRCS= ng_ubt.c bus_if.h device_if.h opt_usb.h
+MAN4= ng_ubt.4
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/netgraph/bluetooth/common/ng_bluetooth.c b/sys/netgraph/bluetooth/common/ng_bluetooth.c
new file mode 100644
index 0000000..d6604b5
--- /dev/null
+++ b/sys/netgraph/bluetooth/common/ng_bluetooth.c
@@ -0,0 +1,254 @@
+/*
+ * 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.1.1.1 2002/09/04 21:47:41 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#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_watchdog_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, sizeof(value), 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, sizeof(value), 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, watchdog_timeout, CTLFLAG_RW,
+ &bluetooth_hci_watchdog_timeout_value, 60,
+ "HCI connection watchdog 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, sizeof(value), 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, sizeof(value), 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_watchdog_timeout(void)
+{
+ return (bluetooth_hci_watchdog_timeout_value * hz);
+} /* bluetooth_hci_watchdog_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 */
+
+/*
+ * 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 = {
+ "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/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
new file mode 100644
index 0000000..443c18a
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
@@ -0,0 +1,1247 @@
+/*
+ * 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.2 2002/11/12 00:51:45 max Exp $
+ * $FreeBSD$
+ *
+ * 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 <dev/pccard/pccarddevs.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_bt3c.h"
+#include "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_match (device_t);
+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 int bt3c_append (struct mbuf *, int);
+
+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 { \
+ outb(rman_get_start((sc)->iobase) + BT3C_ADDR_L, ((address) & 0xff)); \
+ outb(rman_get_start((sc)->iobase) + BT3C_ADDR_H, (((address) >> 8) & 0xff)); \
+} while (0)
+
+#define bt3c_read_data(sc, data) \
+do { \
+ (data) = inb(rman_get_start((sc)->iobase) + BT3C_DATA_L); \
+ (data) |= ((inb(rman_get_start((sc)->iobase) + BT3C_DATA_H) & 0xff) << 8); \
+} while (0)
+
+#define bt3c_write_data(sc, data) \
+do { \
+ outb(rman_get_start((sc)->iobase) + BT3C_DATA_L, ((data) & 0xff)); \
+ outb(rman_get_start((sc)->iobase) + BT3C_DATA_H, (((data) >> 8) & 0xff)); \
+} while (0)
+
+#define bt3c_read_control(sc, data) \
+do { \
+ (data) = inb(rman_get_start((sc)->iobase) + BT3C_CONTROL); \
+} while (0)
+
+#define bt3c_write_control(sc, data) \
+do { \
+ outb(rman_get_start((sc)->iobase) + 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 = {
+ NG_ABI_VERSION,
+ NG_BT3C_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_bt3c_constructor, /* constructor */
+ ng_bt3c_rcvmsg, /* control message */
+ ng_bt3c_shutdown, /* destructor */
+ ng_bt3c_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_bt3c_connect, /* connect hook */
+ ng_bt3c_rcvdata, /* data */
+ ng_bt3c_disconnect, /* disconnect hook */
+ ng_bt3c_cmdlist /* node command list */
+};
+NETGRAPH_INIT(bt3c, &typestruct);
+MODULE_VERSION(ng_bt3c, NG_BLUETOOTH_VERSION);
+
+/*
+ * 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 *)
+ (msg->data))->qlen; /* XXX */
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_BT3C_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_stat_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->stat, rsp->data,
+ sizeof(ng_bt3c_node_stat_ep));
+ break;
+
+ case NGM_BT3C_NODE_RESET_STAT:
+ NG_BT3C_STAT_RESET(sc->stat);
+ break;
+
+ case NGM_BT3C_NODE_DOWNLOAD_FIRMWARE:
+ if (msg->header.arglen <
+ sizeof(ng_bt3c_firmware_block_ep))
+ error = EMSGSIZE;
+ else
+ bt3c_download_firmware(sc, msg->data,
+ msg->header.arglen);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_bt3c_rcvmsg */
+
+/*
+ * Process data
+ */
+
+static int
+ng_bt3c_rcvdata(hook_p hook, item_p item)
+{
+ bt3c_softc_p sc = (bt3c_softc_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ if (hook != sc->hook) {
+ error = EINVAL;
+ goto out;
+ }
+
+ NGI_GET_M(item, m);
+
+ IF_LOCK(&sc->outq);
+ if (_IF_QFULL(&sc->outq)) {
+ NG_BT3C_ERR(sc->dev,
+"Outgoing queue is full. Dropping mbuf, len=%d\n", m->m_pkthdr.len);
+
+ _IF_DROP(&sc->outq);
+ NG_BT3C_STAT_OERROR(sc->stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->outq, m);
+ IF_UNLOCK(&sc->outq);
+
+ error = ng_send_fn(sc->node, NULL, bt3c_send, NULL, 0 /* new send */);
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_bt3c_rcvdata */
+
+/****************************************************************************
+ ****************************************************************************
+ ** PCMCIA driver specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * PC-Card (PCMCIA) match routine
+ */
+
+static int
+bt3c_pccard_match(device_t dev)
+{
+ static struct pccard_product const bt3c_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWB609, 0),
+ { NULL, }
+ };
+
+ struct pccard_product const *pp = NULL;
+
+ pp = pccard_product_lookup(dev, bt3c_pccard_products,
+ sizeof(bt3c_pccard_products[0]), NULL);
+ if (pp == NULL)
+ return (EIO);
+
+ device_set_desc(dev, pp->pp_name);
+
+ return (0);
+} /* bt3c_pccacd_match */
+
+/*
+ * PC-Card (PCMCIA) probe routine
+ * XXX FIXME
+ */
+
+static int
+bt3c_pccard_probe(device_t dev)
+{
+ return (0);
+} /* bt3c_pccacd_probe */
+
+/*
+ * PC-Card (PCMCIA) attach routine
+ */
+
+static int
+bt3c_pccard_attach(device_t dev)
+{
+ bt3c_softc_p sc = NULL;
+
+ sc = (bt3c_softc_p) malloc(sizeof(*sc), M_BT3C, M_NOWAIT|M_ZERO);
+ if (sc == NULL)
+ return (ENOMEM);
+
+ /* Allocate I/O ports */
+ sc->iobase_rid = 0;
+ sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid,
+ 0, ~0, 8, RF_ACTIVE);
+ if (sc->iobase == NULL) {
+ device_printf(dev, "Could not allocate I/O ports\n");
+ goto bad;
+ }
+
+ /* Allocate IRQ */
+ sc->irq_rid = 0;
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "Could not allocate IRQ\n");
+ goto bad;
+ }
+
+ sc->irq_cookie = NULL;
+ if (bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, bt3c_intr, sc,
+ &sc->irq_cookie) != 0) {
+ device_printf(dev, "Could not setup ISR\n");
+ goto bad;
+ }
+
+ /* Attach handler to TTY SWI thread */
+ sc->ith = NULL;
+ if (swi_add(&tty_ithd, device_get_nameunit(dev),
+ bt3c_swi_intr, sc, SWI_TTY, 0, &sc->ith) < 0) {
+ device_printf(dev, "Could not setup SWI ISR\n");
+ goto bad;
+ }
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+ device_printf(dev, "Could not create Netgraph node\n");
+ sc->node = NULL;
+ goto bad;
+ }
+
+ /* Name Netgraph node */
+ if (ng_name_node(sc->node, device_get_nameunit(dev)) != 0) {
+ device_printf(dev, "Could not name Netgraph node\n");
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto bad;
+ }
+
+ sc->dev = dev;
+ sc->debug = NG_BT3C_WARN_LEVEL;
+
+ sc->inq.ifq_maxlen = sc->outq.ifq_maxlen = BT3C_DEFAULTQLEN;
+ mtx_init(&sc->inq.ifq_mtx, "BT3C inq", NULL, MTX_DEF);
+ mtx_init(&sc->outq.ifq_mtx, "BT3C outq", NULL, MTX_DEF);
+
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ device_set_softc(dev, sc);
+
+ return (0);
+bad:
+ if (sc->ith != NULL) {
+ ithread_remove_handler(sc->ith);
+ sc->ith = NULL;
+ }
+
+ if (sc->irq != NULL) {
+ if (sc->irq_cookie != NULL)
+ bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
+
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->irq_rid, sc->irq);
+
+ sc->irq = NULL;
+ sc->irq_rid = 0;
+ }
+
+ if (sc->iobase != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ sc->iobase_rid, sc->iobase);
+
+ sc->iobase = NULL;
+ sc->iobase_rid = 0;
+ }
+
+ free(sc, M_BT3C);
+
+ return (ENXIO);
+} /* bt3c_pccacd_attach */
+
+/*
+ * PC-Card (PCMCIA) detach routine
+ */
+
+static int
+bt3c_pccard_detach(device_t dev)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) device_get_softc(dev);
+
+ if (sc == NULL)
+ return (0);
+
+ device_set_softc(dev, NULL);
+
+ ithread_remove_handler(sc->ith);
+ sc->ith = NULL;
+
+ bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+ sc->irq_cookie = NULL;
+ sc->irq = NULL;
+ sc->irq_rid = 0;
+
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
+ sc->iobase = NULL;
+ sc->iobase_rid = 0;
+
+ if (sc->node != NULL) {
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ ng_rmnode_self(sc->node);
+ sc->node = NULL;
+ }
+
+ NG_FREE_M(sc->m);
+ IF_DRAIN(&sc->inq);
+ IF_DRAIN(&sc->outq);
+
+ mtx_destroy(&sc->inq.ifq_mtx);
+ mtx_destroy(&sc->outq.ifq_mtx);
+
+ bzero(sc, sizeof(*sc));
+ free(sc, M_BT3C);
+
+ return (0);
+} /* bt3c_pccacd_detach */
+
+/*
+ * Interrupt service routine's
+ */
+
+static void
+bt3c_intr(void *context)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) context;
+ u_int16_t control, status;
+
+ if (sc == NULL || sc->ith == NULL) {
+ printf("%s: bogus interrupt\n", NG_BT3C_NODE_TYPE);
+ return;
+ }
+
+ bt3c_read_control(sc, control);
+ if ((control & 0x80) == 0)
+ return;
+
+ bt3c_read(sc, 0x7001, status);
+ NG_BT3C_INFO(sc->dev, "control=%#x, status=%#x\n", control, status);
+
+ if ((status & 0xff) == 0x7f || (status & 0xff) == 0xff) {
+ NG_BT3C_WARN(sc->dev, "Strange status=%#x\n", status);
+ return;
+ }
+
+ /* Receive complete */
+ if (status & 0x0001)
+ bt3c_receive(sc);
+
+ /* Record status and schedule SWI */
+ sc->status |= status;
+ swi_sched(sc->ith, 0);
+
+ /* Complete interrupt */
+ bt3c_write(sc, 0x7001, 0x0000);
+ bt3c_write_control(sc, control);
+} /* bt3c_intr */
+
+/*
+ * Receive data
+ */
+
+static void
+bt3c_receive(bt3c_softc_p sc)
+{
+ u_int16_t i, count, c;
+
+ /* Receive data from the card */
+ bt3c_read(sc, 0x7006, count);
+ NG_BT3C_INFO(sc->dev, "The card has %d characters\n", count);
+
+ bt3c_set_address(sc, 0x7480);
+
+ for (i = 0; i < count; i++) {
+ /* Allocate new mbuf if needed */
+ if (sc->m == NULL) {
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ MGETHDR(sc->m, M_DONTWAIT, MT_DATA);
+ if (sc->m == NULL) {
+ NG_BT3C_ERR(sc->dev, "Could not get mbuf\n");
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ break; /* XXX lost of sync */
+ }
+
+ sc->m->m_len = sc->m->m_pkthdr.len = 0;
+ }
+
+ /* Read and append character to mbuf */
+ bt3c_read_data(sc, c);
+ if (bt3c_append(sc->m, c) != 0) {
+ NG_FREE_M(sc->m);
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+
+ break; /* XXX lost of sync */
+ }
+
+ NG_BT3C_INFO(sc->dev,
+"Got char %#x, want=%d, got=%d\n", c, sc->want, sc->m->m_pkthdr.len);
+
+ if (sc->m->m_pkthdr.len < sc->want)
+ continue; /* wait for more */
+
+ switch (sc->state) {
+ /* Got packet indicator */
+ case NG_BT3C_W4_PKT_IND:
+ NG_BT3C_INFO(sc->dev,
+"Got packet indicator %#x\n", *mtod(sc->m, u_int8_t *));
+
+ sc->state = NG_BT3C_W4_PKT_HDR;
+
+ /*
+ * Since packet indicator included in the packet
+ * header just set sc->want to sizeof(packet header).
+ */
+
+ switch (*mtod(sc->m, u_int8_t *)) {
+ case NG_HCI_ACL_DATA_PKT:
+ sc->want = sizeof(ng_hci_acldata_pkt_t);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ sc->want = sizeof(ng_hci_scodata_pkt_t);
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ sc->want = sizeof(ng_hci_event_pkt_t);
+ break;
+
+ default:
+ NG_BT3C_ERR(sc->dev,
+"Ignoring unknown packet type=%#x\n", *mtod(sc->m, u_int8_t *));
+
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ NG_FREE_M(sc->m);
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+ break;
+ }
+ break;
+
+ /* Got packet header */
+ case NG_BT3C_W4_PKT_HDR:
+ sc->state = NG_BT3C_W4_PKT_DATA;
+
+ switch (*mtod(sc->m, u_int8_t *)) {
+ case NG_HCI_ACL_DATA_PKT:
+ c = le16toh(mtod(sc->m,
+ ng_hci_acldata_pkt_t *)->length);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ c = mtod(sc->m, ng_hci_scodata_pkt_t*)->length;
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ c = mtod(sc->m, ng_hci_event_pkt_t *)->length;
+ break;
+
+ default:
+ KASSERT(0,
+("Invalid packet type=%#x\n", *mtod(sc->m, u_int8_t *)));
+ break;
+ }
+
+ NG_BT3C_INFO(sc->dev,
+"Got packet header, packet type=%#x, got so far %d, payload size=%d\n",
+ *mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len,
+ c);
+
+ if (c > 0) {
+ sc->want += c;
+ break;
+ }
+
+ /* else FALLTHROUGH and deliver frame */
+ /* XXX is this true? should we deliver empty frame? */
+
+ /* Got packet data */
+ case NG_BT3C_W4_PKT_DATA:
+ NG_BT3C_INFO(sc->dev,
+"Got full packet, packet type=%#x, packet size=%d\n",
+ *mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len);
+
+ NG_BT3C_STAT_BYTES_RECV(sc->stat, sc->m->m_pkthdr.len);
+ NG_BT3C_STAT_PCKTS_RECV(sc->stat);
+
+ IF_LOCK(&sc->inq);
+ if (_IF_QFULL(&sc->inq)) {
+ NG_BT3C_ERR(sc->dev,
+"Incoming queue is full. Dropping mbuf, len=%d\n", sc->m->m_pkthdr.len);
+
+ _IF_DROP(&sc->inq);
+ NG_BT3C_STAT_IERROR(sc->stat);
+
+ NG_FREE_M(sc->m);
+ } else {
+ _IF_ENQUEUE(&sc->inq, sc->m);
+ sc->m = NULL;
+ }
+ IF_UNLOCK(&sc->inq);
+
+ sc->state = NG_BT3C_W4_PKT_IND;
+ sc->want = 1;
+ break;
+
+ default:
+ KASSERT(0,
+("Invalid node state=%d", sc->state));
+ break;
+ }
+ }
+
+ bt3c_write(sc, 0x7006, 0x0000);
+} /* bt3c_receive */
+
+/*
+ * Append character to the mbuf.
+ * XXX assumes mbuf has header
+ * XXX does not handle external mbuf's
+ * XXX always appends char to the end of chain
+ */
+
+static int
+bt3c_append(struct mbuf *m0, int c)
+{
+ struct mbuf *m = m0;
+ int len;
+
+ if (m0->m_next == NULL)
+ len = MHLEN;
+ else {
+ len = MLEN;
+
+ while (m->m_next != NULL)
+ m = m->m_next;
+ }
+
+ if (m->m_len >= len) {
+ MGET(m->m_next, M_DONTWAIT, m0->m_type);
+ if (m->m_next == NULL)
+ return (ENOBUFS);
+
+ m = m->m_next;
+ m->m_len = 0;
+ }
+
+ m->m_data[m->m_len ++] = (char) c;
+ m0->m_pkthdr.len ++;
+
+ return (0);
+} /* bt3c_append */
+
+/*
+ * SWI interrupt handler
+ * Netgraph part is handled via ng_send_fn() to avoid race with hook
+ * connection/disconnection
+ */
+
+static void
+bt3c_swi_intr(void *context)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) context;
+ u_int16_t data;
+
+ /* Receive complete */
+ if (sc->status & 0x0001) {
+ sc->status &= ~0x0001; /* XXX is it safe? */
+
+ if (ng_send_fn(sc->node, NULL, &bt3c_forward, NULL, 0) != 0)
+ NG_BT3C_ALERT(sc->dev, "Could not forward frames!\n");
+ }
+
+ /* Send complete */
+ if (sc->status & 0x0002) {
+ sc->status &= ~0x0002; /* XXX is it safe */
+
+ if (ng_send_fn(sc->node, NULL, &bt3c_send, NULL, 1) != 0)
+ NG_BT3C_ALERT(sc->dev, "Could not send frames!\n");
+ }
+
+ /* Antenna position */
+ if (sc->status & 0x0020) {
+ sc->status &= ~0x0020; /* XXX is it safe */
+
+ bt3c_read(sc, 0x7002, data);
+ data &= 0x10;
+
+ if (data)
+ sc->flags |= BT3C_ANTENNA_OUT;
+ else
+ sc->flags &= ~BT3C_ANTENNA_OUT;
+
+ NG_BT3C_INFO(sc->dev, "Antenna %s\n", data? "OUT" : "IN");
+ }
+} /* bt3c_swi_intr */
+
+/*
+ * Send all incoming frames to the upper layer
+ */
+
+static void
+bt3c_forward(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int error;
+
+ if (sc == NULL)
+ return;
+
+ if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
+ for (;;) {
+ IF_DEQUEUE(&sc->inq, m);
+ if (m == NULL)
+ break;
+
+ NG_SEND_DATA_ONLY(error, sc->hook, m);
+ if (error != 0)
+ NG_BT3C_STAT_IERROR(sc->stat);
+ }
+ } else {
+ IF_LOCK(&sc->inq);
+ for (;;) {
+ _IF_DEQUEUE(&sc->inq, m);
+ if (m == NULL)
+ break;
+
+ NG_BT3C_STAT_IERROR(sc->stat);
+ NG_FREE_M(m);
+ }
+ IF_UNLOCK(&sc->inq);
+ }
+} /* bt3c_forward */
+
+/*
+ * Send more data to the device. Must be called when node is locked
+ */
+
+static void
+bt3c_send(node_p node, hook_p hook, void *arg, int completed)
+{
+ bt3c_softc_p sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int i, wrote, len;
+
+ if (sc == NULL)
+ return;
+
+ if (completed)
+ sc->flags &= ~BT3C_XMIT;
+
+ if (sc->flags & BT3C_XMIT)
+ return;
+
+ bt3c_set_address(sc, 0x7080);
+
+ for (wrote = 0; wrote < BT3C_FIFO_SIZE; ) {
+ IF_DEQUEUE(&sc->outq, m);
+ if (m == NULL)
+ break;
+
+ while (m != NULL) {
+ len = min((BT3C_FIFO_SIZE - wrote), m->m_len);
+
+ for (i = 0; i < m->m_len; i++)
+ bt3c_write_data(sc, m->m_data[i]);
+
+ wrote += len;
+ m->m_data += len;
+ m->m_len -= len;
+
+ if (m->m_len > 0)
+ break;
+
+ m = m_free(m);
+ }
+
+ if (m != NULL) {
+ IF_PREPEND(&sc->outq, m);
+ break;
+ }
+
+ NG_BT3C_STAT_PCKTS_SENT(sc->stat);
+ }
+
+ if (wrote > 0) {
+ NG_BT3C_INFO(sc->dev, "Wrote %d bytes\n", wrote);
+ NG_BT3C_STAT_BYTES_SENT(sc->stat, wrote);
+
+ bt3c_write(sc, 0x7005, wrote);
+ sc->flags |= BT3C_XMIT;
+ }
+} /* bt3c_send */
+
+/*
+ * Download chip firmware
+ */
+
+static void
+bt3c_download_firmware(bt3c_softc_p sc, char const *firmware, int firmware_size)
+{
+ ng_bt3c_firmware_block_ep const *block = NULL;
+ u_int16_t const *data = NULL;
+ int i, size;
+ u_int8_t c;
+
+ /* Reset */
+ device_printf(sc->dev, "Reseting the card...\n");
+ bt3c_write(sc, 0x8040, 0x0404);
+ bt3c_write(sc, 0x8040, 0x0400);
+ DELAY(1);
+
+ bt3c_write(sc, 0x8040, 0x0404);
+ DELAY(17);
+
+ /* Download firmware */
+ device_printf(sc->dev, "Starting firmware download process...\n");
+
+ for (size = 0; size < firmware_size; ) {
+ block = (ng_bt3c_firmware_block_ep const *)(firmware + size);
+ data = (u_int16_t const *)(block + 1);
+
+ if (bootverbose)
+ device_printf(sc->dev, "Download firmware block, " \
+ "address=%#08x, size=%d words, aligment=%d\n",
+ block->block_address, block->block_size,
+ block->block_alignment);
+
+ bt3c_set_address(sc, block->block_address);
+ for (i = 0; i < block->block_size; i++)
+ bt3c_write_data(sc, data[i]);
+
+ size += (sizeof(*block) + (block->block_size * 2) +
+ block->block_alignment);
+ }
+
+ DELAY(17);
+ device_printf(sc->dev, "Firmware download process complete\n");
+
+ /* Boot */
+ device_printf(sc->dev, "Starting the card...\n");
+ bt3c_set_address(sc, 0x3000);
+ bt3c_read_control(sc, c);
+ bt3c_write_control(sc, (c | 0x40));
+ DELAY(17);
+
+ /* Clear registers */
+ device_printf(sc->dev, "Clearing card registers...\n");
+ bt3c_write(sc, 0x7006, 0x0000);
+ bt3c_write(sc, 0x7005, 0x0000);
+ bt3c_write(sc, 0x7001, 0x0000);
+ DELAY(1000);
+} /* bt3c_download_firmware */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Driver module
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * PC-Card (PCMCIA) driver
+ */
+
+static device_method_t bt3c_pccard_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pccard_compat_probe),
+ DEVMETHOD(device_attach, pccard_compat_attach),
+ DEVMETHOD(device_detach, bt3c_pccard_detach),
+
+ /* Card interface */
+ DEVMETHOD(card_compat_match, bt3c_pccard_match),
+ DEVMETHOD(card_compat_probe, bt3c_pccard_probe),
+ DEVMETHOD(card_compat_attach, bt3c_pccard_attach),
+ { 0, 0 }
+};
+
+static driver_t bt3c_pccard_driver = {
+ NG_BT3C_NODE_TYPE,
+ bt3c_pccard_methods,
+ 0
+};
+
+static devclass_t bt3c_devclass;
+
+DRIVER_MODULE(bt3c, pccard, bt3c_pccard_driver, bt3c_devclass, 0, 0);
+
diff --git a/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h
new file mode 100644
index 0000000..89ac57c
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_var.h
@@ -0,0 +1,103 @@
+/*
+ * ng_bt3c_var.h
+ *
+ * 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_var.h,v 1.1 2002/11/09 19:07:56 max Exp $
+ * $FreeBSD$
+ *
+ * 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
+ *
+ */
+
+#ifndef _NG_BT3C_VAR_H_
+#define _NG_BT3C_VAR_H_
+
+/* Debug printf's */
+#define NG_BT3C_ALERT if (sc->debug >= NG_BT3C_ALERT_LEVEL) device_printf
+#define NG_BT3C_ERR if (sc->debug >= NG_BT3C_ERR_LEVEL) device_printf
+#define NG_BT3C_WARN if (sc->debug >= NG_BT3C_WARN_LEVEL) device_printf
+#define NG_BT3C_INFO if (sc->debug >= NG_BT3C_INFO_LEVEL) device_printf
+
+/* Device registers */
+#define BT3C_DATA_L 0x00 /* data low byte */
+#define BT3C_DATA_H 0x01 /* high byte */
+#define BT3C_ADDR_L 0x02 /* address low byte */
+#define BT3C_ADDR_H 0x03 /* high byte */
+#define BT3C_CONTROL 0x04 /* control */
+
+#define BT3C_FIFO_SIZE 256
+
+/* Device softc structure */
+struct bt3c_softc {
+ /* Device specific */
+ device_t dev; /* pointer back to device */
+ int iobase_rid; /* iobase RID */
+ struct resource *iobase; /* iobase */
+ int irq_rid; /* irq RID */
+ struct resource *irq; /* irq */
+ void *irq_cookie; /* irq cookie */
+
+ /* Netgraph specific */
+ node_p node; /* pointer back to node */
+ hook_p hook; /* hook */
+
+ ng_bt3c_node_debug_ep debug; /* debug level */
+ u_int16_t flags; /* device flags */
+#define BT3C_ANTENNA_OUT (1 << 0) /* antena is out */
+#define BT3C_XMIT (1 << 1) /* xmit in progress */
+
+ ng_bt3c_node_state_ep state; /* receiving state */
+
+ ng_bt3c_node_stat_ep stat; /* statistic */
+#define NG_BT3C_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_BT3C_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_BT3C_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_BT3C_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_BT3C_STAT_OERROR(s) (s).oerrors ++
+#define NG_BT3C_STAT_IERROR(s) (s).ierrors ++
+#define NG_BT3C_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ u_int32_t status; /* from ISR */
+ void *ith; /* ithread handler */
+
+ struct mbuf *m; /* current frame */
+ u_int32_t want; /* # of chars we want */
+
+ struct ifqueue inq; /* queue of incoming mbuf's */
+ struct ifqueue outq; /* queue of outgoing mbuf's */
+#define BT3C_DEFAULTQLEN 12 /* XXX max. size of out queue */
+};
+
+typedef struct bt3c_softc bt3c_softc_t;
+typedef struct bt3c_softc * bt3c_softc_p;
+
+#endif /* ndef _NG_BT3C_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/TODO b/sys/netgraph/bluetooth/drivers/h4/TODO
new file mode 100644
index 0000000..67cfbba
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/TODO
@@ -0,0 +1,13 @@
+# $FreeBSD$
+$Id: TODO,v 1.6 2002/06/27 09:50:17 max Exp $
+
+FIXME/TODO list
+
+This is a list of open issues for H4 node
+
+1) Locking/SMP
+
+ External code now uses ng_send_fn to inject data into Netgraph, but
+ i still use splXXX to lock tty level. this is wrong and should be
+ fixed!
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4.c b/sys/netgraph/bluetooth/drivers/h4/ng_h4.c
new file mode 100644
index 0000000..650c6f4
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4.c
@@ -0,0 +1,1059 @@
+/*
+ * ng_h4.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_h4.c,v 1.25 2002/11/03 02:17:31 max Exp $
+ * $FreeBSD$
+ *
+ * Based on:
+ * ---------
+ *
+ * FreeBSD: src/sys/netgraph/ng_tty.c
+ * Author: Archie Cobbs <archie@freebsd.org>
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/tty.h>
+#include <sys/ttycom.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_h4.h"
+#include "ng_h4_var.h"
+#include "ng_h4_prse.h"
+
+/*****************************************************************************
+ *****************************************************************************
+ ** This node implements a Bluetooth HCI UART transport layer as per chapter
+ ** H4 of the Bluetooth Specification Book v1.1. It is a terminal line
+ ** discipline that is also a netgraph node. Installing this line discipline
+ ** on a terminal device instantiates a new netgraph node of this type, which
+ ** allows access to the device via the "hook" hook of the node.
+ **
+ ** Once the line discipline is installed, you can find out the name of the
+ ** corresponding netgraph node via a NGIOCGINFO ioctl().
+ *****************************************************************************
+ *****************************************************************************/
+
+/* MALLOC define */
+#ifndef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_H4, "netgraph_h4", "Netgraph Bluetooth H4 node");
+#else
+#define M_NETGRAPH_H4 M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Line discipline methods */
+static int ng_h4_open (dev_t, struct tty *);
+static int ng_h4_close (struct tty *, int);
+static int ng_h4_read (struct tty *, struct uio *, int);
+static int ng_h4_write (struct tty *, struct uio *, int);
+static int ng_h4_input (int, struct tty *);
+static int ng_h4_start (struct tty *);
+static void ng_h4_start2 (node_p, hook_p, void *, int);
+static int ng_h4_ioctl (struct tty *, u_long, caddr_t,
+ int, struct thread *);
+
+/* Line discipline descriptor */
+static struct linesw ng_h4_disc = {
+ ng_h4_open, /* open */
+ ng_h4_close, /* close */
+ ng_h4_read, /* read */
+ ng_h4_write, /* write */
+ ng_h4_ioctl, /* ioctl */
+ ng_h4_input, /* input */
+ ng_h4_start, /* start */
+ ttymodem, /* modem */
+ 0 /* hotchar (don't really care which one) */
+};
+
+/* Netgraph methods */
+static ng_constructor_t ng_h4_constructor;
+static ng_rcvmsg_t ng_h4_rcvmsg;
+static ng_shutdown_t ng_h4_shutdown;
+static ng_newhook_t ng_h4_newhook;
+static ng_connect_t ng_h4_connect;
+static ng_rcvdata_t ng_h4_rcvdata;
+static ng_disconnect_t ng_h4_disconnect;
+
+/* Other stuff */
+static void ng_h4_timeout (node_p);
+static void ng_h4_untimeout (node_p);
+static void ng_h4_queue_timeout (void *);
+static void ng_h4_process_timeout (node_p, hook_p, void *, int);
+static int ng_h4_mod_event (module_t, int, void *);
+
+/* Netgraph node type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_H4_NODE_TYPE, /* typename */
+ ng_h4_mod_event, /* modevent */
+ ng_h4_constructor, /* constructor */
+ ng_h4_rcvmsg, /* control message */
+ ng_h4_shutdown, /* destructor */
+ ng_h4_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_h4_connect, /* connect hook */
+ ng_h4_rcvdata, /* data */
+ ng_h4_disconnect, /* disconnect hook */
+ ng_h4_cmdlist /* node command list */
+};
+NETGRAPH_INIT(h4, &typestruct);
+MODULE_VERSION(ng_h4, NG_BLUETOOTH_VERSION);
+
+static int ng_h4_node = 0;
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Line discipline methods
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Set our line discipline on the tty.
+ */
+
+static int
+ng_h4_open(dev_t dev, struct tty *tp)
+{
+ char name[NG_NODELEN + 1];
+ ng_h4_info_p sc = NULL;
+ int s, error;
+
+ /* Super-user only */
+ error = suser(curthread); /* XXX */
+ if (error != 0)
+ return (error);
+
+ s = splnet(); /* XXX */
+ spltty(); /* XXX */
+
+ /* Already installed? */
+ if (tp->t_line == H4DISC) {
+ sc = (ng_h4_info_p) tp->t_sc;
+ if (sc != NULL && sc->tp == tp)
+ goto out;
+ }
+
+ /* Initialize private struct */
+ MALLOC(sc, ng_h4_info_p, sizeof(*sc), M_NETGRAPH_H4, M_WAITOK | M_ZERO);
+ if (sc == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ sc->tp = tp;
+ sc->debug = NG_H4_WARN_LEVEL;
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ NG_BT_MBUFQ_INIT(&sc->outq, NG_H4_DEFAULTQLEN);
+ callout_handle_init(&sc->timo);
+
+ /* Setup netgraph node */
+ error = ng_make_node_common(&typestruct, &sc->node);
+ if (error != 0) {
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ goto out;
+ }
+
+ /* Assign node its name */
+ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
+
+ error = ng_name_node(sc->node, name);
+ if (error != 0) {
+ NG_H4_ALERT("%s: %s - node name exists?\n", __func__, name);
+ NG_NODE_UNREF(sc->node);
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ goto out;
+ }
+
+ /* Set back pointers */
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ tp->t_sc = (caddr_t) sc;
+
+ /* The node has to be a WRITER because data can change node status */
+ NG_NODE_FORCE_WRITER(sc->node);
+
+ /*
+ * Pre-allocate cblocks to the an appropriate amount.
+ * I'm not sure what is appropriate.
+ */
+
+ ttyflush(tp, FREAD | FWRITE);
+ clist_alloc_cblocks(&tp->t_canq, 0, 0);
+ clist_alloc_cblocks(&tp->t_rawq, 0, 0);
+ clist_alloc_cblocks(&tp->t_outq,
+ MLEN + NG_H4_HIWATER, MLEN + NG_H4_HIWATER);
+out:
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_open */
+
+/*
+ * Line specific close routine, called from device close routine
+ * and from ttioctl. This causes the node to be destroyed as well.
+ */
+
+static int
+ng_h4_close(struct tty *tp, int flag)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+ int s;
+
+ s = spltty(); /* XXX */
+
+ ttyflush(tp, FREAD | FWRITE);
+ clist_free_cblocks(&tp->t_outq);
+ tp->t_line = 0;
+ if (sc != NULL) {
+ tp->t_sc = NULL;
+
+ if (sc->node != NULL) {
+ if (sc->flags & NG_H4_TIMEOUT)
+ ng_h4_untimeout(sc->node);
+
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ ng_rmnode_self(sc->node);
+ sc->node = NULL;
+ }
+
+ NG_BT_MBUFQ_DESTROY(&sc->outq);
+ bzero(sc, sizeof(*sc));
+ FREE(sc, M_NETGRAPH_H4);
+ }
+
+ splx(s); /* XXX */
+
+ return (0);
+} /* ng_h4_close */
+
+/*
+ * Once the device has been turned into a node, we don't allow reading.
+ */
+
+static int
+ng_h4_read(struct tty *tp, struct uio *uio, int flag)
+{
+ return (EIO);
+} /* ng_h4_read */
+
+/*
+ * Once the device has been turned into a node, we don't allow writing.
+ */
+
+static int
+ng_h4_write(struct tty *tp, struct uio *uio, int flag)
+{
+ return (EIO);
+} /* ng_h4_write */
+
+/*
+ * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
+ */
+
+static int
+ng_h4_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
+ struct thread *td)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+ int s, error = 0;
+
+ s = spltty(); /* XXX */
+
+ switch (cmd) {
+ case NGIOCGINFO:
+#undef NI
+#define NI(x) ((struct nodeinfo *)(x))
+
+ bzero(data, sizeof(*NI(data)));
+
+ if (NG_NODE_HAS_NAME(sc->node))
+ strncpy(NI(data)->name, NG_NODE_NAME(sc->node),
+ sizeof(NI(data)->name) - 1);
+
+ strncpy(NI(data)->type, sc->node->nd_type->name,
+ sizeof(NI(data)->type) - 1);
+
+ NI(data)->id = (u_int32_t) ng_node2ID(sc->node);
+ NI(data)->hooks = NG_NODE_NUMHOOKS(sc->node);
+ break;
+
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_ioctl */
+
+/*
+ * Receive data coming from the device. We get one character at a time, which
+ * is kindof silly.
+ */
+
+static int
+ng_h4_input(int c, struct tty *tp)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+
+ if (sc == NULL || tp != sc->tp ||
+ sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
+ return (0);
+
+ /* Check for error conditions */
+ if ((sc->tp->t_state & TS_CONNECTED) == 0) {
+ NG_H4_INFO("%s: %s - no carrier\n", __func__,
+ NG_NODE_NAME(sc->node));
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ /* Check for framing error or overrun on this char */
+ if (c & TTY_ERRORMASK) {
+ NG_H4_ERR("%s: %s - line error %#x, c=%#x\n", __func__,
+ NG_NODE_NAME(sc->node), c & TTY_ERRORMASK,
+ c & TTY_CHARMASK);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ NG_H4_STAT_BYTES_RECV(sc->stat, 1);
+
+ /* Append char to mbuf */
+ if (sc->got >= sizeof(sc->ibuf)) {
+ NG_H4_ALERT("%s: %s - input buffer overflow, c=%#x, got=%d\n",
+ __func__, NG_NODE_NAME(sc->node), c & TTY_CHARMASK,
+ sc->got);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ return (0); /* XXX Loss of synchronization here! */
+ }
+
+ sc->ibuf[sc->got ++] = (c & TTY_CHARMASK);
+
+ NG_H4_INFO("%s: %s - got char %#x, want=%d, got=%d\n", __func__,
+ NG_NODE_NAME(sc->node), c, sc->want, sc->got);
+
+ if (sc->got < sc->want)
+ return (0); /* Wait for more */
+
+ switch (sc->state) {
+ /* Got packet indicator */
+ case NG_H4_W4_PKT_IND:
+ NG_H4_INFO("%s: %s - got packet indicator %#x\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0]);
+
+ sc->state = NG_H4_W4_PKT_HDR;
+
+ /*
+ * Since packet indicator included in the packet header
+ * just set sc->want to sizeof(packet header).
+ */
+
+ switch (sc->ibuf[0]) {
+ case NG_HCI_ACL_DATA_PKT:
+ sc->want = sizeof(ng_hci_acldata_pkt_t);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ sc->want = sizeof(ng_hci_scodata_pkt_t);
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ sc->want = sizeof(ng_hci_event_pkt_t);
+ break;
+
+ default:
+ NG_H4_WARN("%s: %s - ignoring unknown packet " \
+ "type=%#x\n", __func__, NG_NODE_NAME(sc->node),
+ sc->ibuf[0]);
+
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ break;
+ }
+ break;
+
+ /* Got packet header */
+ case NG_H4_W4_PKT_HDR:
+ sc->state = NG_H4_W4_PKT_DATA;
+
+ switch (sc->ibuf[0]) {
+ case NG_HCI_ACL_DATA_PKT:
+ c = le16toh(((ng_hci_acldata_pkt_t *)
+ (sc->ibuf))->length);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ c = ((ng_hci_scodata_pkt_t *)(sc->ibuf))->length;
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ c = ((ng_hci_event_pkt_t *)(sc->ibuf))->length;
+ break;
+
+ default:
+ KASSERT((0), ("Invalid packet type=%#x\n",
+ sc->ibuf[0]));
+ break;
+ }
+
+ NG_H4_INFO("%s: %s - got packet header, packet type=%#x, " \
+ "packet size=%d, payload size=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got, c);
+
+ if (c > 0) {
+ sc->want += c;
+
+ /*
+ * Try to prevent possible buffer overrun
+ *
+ * XXX I'm *really* confused here. It turns out
+ * that Xircom card sends us packets with length
+ * greater then 512 bytes! This is greater then
+ * our old receive buffer (ibuf) size. In the same
+ * time the card demands from us *not* to send
+ * packets greater then 192 bytes. Weird! How the
+ * hell i should know how big *receive* buffer
+ * should be? For now increase receiving buffer
+ * size to 1K and add the following check.
+ */
+
+ if (sc->want >= sizeof(sc->ibuf)) {
+ int b;
+
+ NG_H4_ALERT("%s: %s - packet too big for " \
+ "buffer, type=%#x, got=%d, want=%d, " \
+ "length=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0],
+ sc->got, sc->want, c);
+
+ NG_H4_ALERT("Packet header:\n");
+ for (b = 0; b < sc->got; b++)
+ NG_H4_ALERT("%#x ", sc->ibuf[b]);
+ NG_H4_ALERT("\n");
+
+ /* Reset state */
+ NG_H4_STAT_IERROR(sc->stat);
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ }
+
+ break;
+ }
+
+ /* else FALLTHROUGH and deliver frame */
+ /* XXX Is this true? Should we deliver empty frame? */
+
+ /* Got packet data */
+ case NG_H4_W4_PKT_DATA:
+ NG_H4_INFO("%s: %s - got full packet, packet type=%#x, " \
+ "packet size=%d\n", __func__,
+ NG_NODE_NAME(sc->node), sc->ibuf[0], sc->got);
+
+ if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
+ struct mbuf *m = NULL;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ m->m_pkthdr.len = 0;
+
+ /* XXX m_copyback() is stupid */
+ m->m_len = min(MHLEN, sc->got);
+
+ m_copyback(m, 0, sc->got, sc->ibuf);
+ NG_SEND_DATA_ONLY(c, sc->hook, m);
+ } else {
+ NG_H4_ERR("%s: %s - could not get mbuf\n",
+ __func__, NG_NODE_NAME(sc->node));
+
+ NG_H4_STAT_IERROR(sc->stat);
+ }
+ }
+
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ NG_H4_STAT_PCKTS_RECV(sc->stat);
+ break;
+
+ default:
+ KASSERT((0), ("Invalid H4 node state=%d", sc->state));
+ break;
+ }
+
+ return (0);
+} /* ng_h4_input */
+
+/*
+ * This is called when the device driver is ready for more output. Called from
+ * tty system.
+ */
+
+static int
+ng_h4_start(struct tty *tp)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) tp->t_sc;
+
+ if (sc == NULL || tp != sc->tp ||
+ sc->node == NULL || NG_NODE_NOT_VALID(sc->node))
+ return (0);
+
+ return (ng_send_fn(sc->node, NULL, ng_h4_start2, NULL, 0));
+} /* ng_h4_start */
+
+/*
+ * Device driver is ready for more output. Part 2. Called (via ng_send_fn)
+ * ng_h4_start() and from ng_h4_rcvdata() when a new mbuf is available for
+ * output.
+ */
+
+static void
+ng_h4_start2(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ struct mbuf *m = NULL;
+ int s, size;
+
+ s = spltty(); /* XXX */
+
+#if 0
+ while (sc->tp->t_outq.c_cc < NG_H4_HIWATER) { /* XXX 2.2 specific ? */
+#else
+ while (1) {
+#endif
+ /* Remove first mbuf from queue */
+ NG_BT_MBUFQ_DEQUEUE(&sc->outq, m);
+ if (m == NULL)
+ break;
+
+ /* Send as much of it as possible */
+ while (m != NULL) {
+ size = m->m_len - b_to_q(mtod(m, u_char *),
+ m->m_len, &sc->tp->t_outq);
+
+ NG_H4_STAT_BYTES_SENT(sc->stat, size);
+
+ m->m_data += size;
+ m->m_len -= size;
+ if (m->m_len > 0)
+ break; /* device can't take no more */
+
+ m = m_free(m);
+ }
+
+ /* Put remainder of mbuf chain (if any) back on queue */
+ if (m != NULL) {
+ NG_BT_MBUFQ_PREPEND(&sc->outq, m);
+ break;
+ }
+
+ /* Full packet has been sent */
+ NG_H4_STAT_PCKTS_SENT(sc->stat);
+ }
+
+ /*
+ * Call output process whether or not there is any output. We are
+ * being called in lieu of ttstart and must do what it would.
+ */
+
+ if (sc->tp->t_oproc != NULL)
+ (*sc->tp->t_oproc)(sc->tp);
+
+ /*
+ * This timeout is needed for operation on a pseudo-tty, because the
+ * pty code doesn't call pppstart after it has drained the t_outq.
+ */
+
+ if (NG_BT_MBUFQ_LEN(&sc->outq) > 0 && (sc->flags & NG_H4_TIMEOUT) == 0)
+ ng_h4_timeout(node);
+
+ splx(s); /* XXX */
+} /* ng_h4_start2 */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph node methods
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Initialize a new node of this type. We only allow nodes to be created as
+ * a result of setting the line discipline on a tty, so always return an error
+ * if not.
+ */
+
+static int
+ng_h4_constructor(node_p node)
+{
+ return (EOPNOTSUPP);
+} /* ng_h4_constructor */
+
+/*
+ * Add a new hook. There can only be one.
+ */
+
+static int
+ng_h4_newhook(node_p node, hook_p hook, const char *name)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_H4_HOOK) != 0)
+ return (EINVAL);
+
+ if (sc->hook != NULL)
+ return (EISCONN);
+
+ sc->hook = hook;
+
+ return (0);
+} /* ng_h4_newhook */
+
+/*
+ * Connect hook. Just say yes.
+ */
+
+static int
+ng_h4_connect(hook_p hook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (hook != sc->hook) {
+ sc->hook = NULL;
+ return (EINVAL);
+ }
+
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ return (0);
+} /* ng_h4_connect */
+
+/*
+ * Disconnect the hook
+ */
+
+static int
+ng_h4_disconnect(hook_p hook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ /*
+ * We need to check for sc != NULL because we can be called from
+ * ng_h4_clsoe() via ng_rmnode_self()
+ */
+
+ if (sc != NULL) {
+ if (hook != sc->hook)
+ return (EINVAL);
+
+ /* XXX do we have to untimeout and drain out queue? */
+ if (sc->flags & NG_H4_TIMEOUT)
+ ng_h4_untimeout(NG_HOOK_NODE(hook));
+
+ NG_BT_MBUFQ_DRAIN(&sc->outq);
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+
+ sc->hook = NULL;
+ }
+
+ return (0);
+} /* ng_h4_disconnect */
+
+/*
+ * Remove this node. The does the netgraph portion of the shutdown.
+ * This should only be called indirectly from ng_h4_close().
+ */
+
+static int
+ng_h4_shutdown(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ char name[NG_NODELEN + 1];
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ /* Check if device was closed */
+ if (sc == NULL)
+ goto out;
+
+ /* Setup new netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->node) != 0) {
+ printf("%s: Unable to create new node!\n", __func__);
+ sc->node = NULL;
+ goto out;
+ }
+
+ /* Assign node its name */
+ snprintf(name, sizeof(name), "%s%d", typestruct.name, ng_h4_node ++);
+
+ if (ng_name_node(sc->node, name) != 0) {
+ printf("%s: %s - node name exists?\n", __func__, name);
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto out;
+ }
+
+ /* The node has to be a WRITER because data can change node status */
+ NG_NODE_FORCE_WRITER(sc->node);
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+out:
+ return (0);
+} /* ng_h4_shutdown */
+
+/*
+ * Receive incoming data from Netgraph system. Put it on our
+ * output queue and start output if necessary.
+ */
+
+static int
+ng_h4_rcvdata(hook_p hook, item_p item)
+{
+ ng_h4_info_p sc = (ng_h4_info_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error = 0;
+ struct mbuf *m = NULL;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ if (hook != sc->hook) {
+ error = EINVAL;
+ goto out;
+ }
+
+ NGI_GET_M(item, m);
+
+ if (NG_BT_MBUFQ_FULL(&sc->outq)) {
+ NG_H4_ERR("%s: %s - dropping mbuf, len=%d\n", __func__,
+ NG_NODE_NAME(sc->node), m->m_pkthdr.len);
+
+ NG_BT_MBUFQ_DROP(&sc->outq);
+ NG_H4_STAT_OERROR(sc->stat);
+
+ NG_FREE_M(m);
+ error = ENOBUFS;
+ } else {
+ NG_H4_INFO("%s: %s - queue mbuf, len=%d\n", __func__,
+ NG_NODE_NAME(sc->node), m->m_pkthdr.len);
+
+ NG_BT_MBUFQ_ENQUEUE(&sc->outq, m);
+
+ /*
+ * We have lock on the node, so we can call ng_h4_start2()
+ * directly
+ */
+
+ ng_h4_start2(sc->node, NULL, NULL, 0);
+ }
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_h4_rcvdata */
+
+/*
+ * Receive control message
+ */
+
+static int
+ng_h4_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *resp = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto out;
+ }
+
+ 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;
+ else
+ snprintf(resp->data, NG_TEXTRESPONSE,
+ "Hook: %s\n" \
+ "Flags: %#x\n" \
+ "Debug: %d\n" \
+ "State: %d\n" \
+ "Queue: [have:%d,max:%d]\n" \
+ "Input: [got:%d,want:%d]",
+ (sc->hook != NULL)? NG_H4_HOOK : "",
+ sc->flags,
+ sc->debug,
+ sc->state,
+ NG_BT_MBUFQ_LEN(&sc->outq),
+ sc->outq.maxlen,
+ sc->got,
+ sc->want);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_H4_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_H4_NODE_RESET:
+ NG_BT_MBUFQ_DRAIN(&sc->outq);
+ sc->state = NG_H4_W4_PKT_IND;
+ sc->want = 1;
+ sc->got = 0;
+ break;
+
+ case NGM_H4_NODE_GET_STATE:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_state_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_state_ep *)(resp->data)) =
+ sc->state;
+ break;
+
+ case NGM_H4_NODE_GET_DEBUG:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_debug_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_debug_ep *)(resp->data)) =
+ sc->debug;
+ break;
+
+ case NGM_H4_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_h4_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ sc->debug =
+ *((ng_h4_node_debug_ep *)(msg->data));
+ break;
+
+ case NGM_H4_NODE_GET_QLEN:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_qlen_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_h4_node_qlen_ep *)(resp->data)) =
+ sc->outq.maxlen;
+ break;
+
+ case NGM_H4_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_h4_node_qlen_ep))
+ error = EMSGSIZE;
+ else if (*((ng_h4_node_qlen_ep *)(msg->data)) <= 0)
+ error = EINVAL;
+ else
+ sc->outq.maxlen =
+ *((ng_h4_node_qlen_ep *)(msg->data));
+ break;
+
+ case NGM_H4_NODE_GET_STAT:
+ NG_MKRESPONSE(resp, msg, sizeof(ng_h4_node_stat_ep),
+ M_NOWAIT);
+ if (resp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->stat, resp->data,
+ sizeof(ng_h4_node_stat_ep));
+ break;
+
+ case NGM_H4_NODE_RESET_STAT:
+ NG_H4_STAT_RESET(sc->stat);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+out:
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_h4_rcvmsg */
+
+/*
+ * Set timeout
+ */
+
+static void
+ng_h4_timeout(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ NG_NODE_REF(node);
+ sc->timo = timeout(ng_h4_queue_timeout, node, 1);
+ sc->flags |= NG_H4_TIMEOUT;
+} /* ng_h4_timeout */
+
+/*
+ * Unset timeout
+ */
+
+static void
+ng_h4_untimeout(node_p node)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ sc->flags &= ~NG_H4_TIMEOUT;
+ untimeout(ng_h4_queue_timeout, node, sc->timo);
+ NG_NODE_UNREF(node);
+} /* ng_h4_untimeout */
+
+/*
+ * OK, timeout has happend, so queue function to process it
+ */
+
+static void
+ng_h4_queue_timeout(void *context)
+{
+ node_p node = (node_p) context;
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node, NULL, &ng_h4_process_timeout, NULL, 0);
+
+ NG_NODE_UNREF(node);
+} /* ng_h4_queue_timeout */
+
+/*
+ * Timeout processing function.
+ * We still have data to output to the device, so try sending more.
+ */
+
+static void
+ng_h4_process_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_h4_info_p sc = (ng_h4_info_p) NG_NODE_PRIVATE(node);
+
+ sc->flags &= ~NG_H4_TIMEOUT;
+
+ /*
+ * We can call ng_h4_start2() directly here because we have lock
+ * on the node.
+ */
+
+ ng_h4_start2(node, NULL, NULL, 0);
+} /* ng_h4_process_timeout */
+
+/*
+ * Handle loading and unloading for this node type
+ */
+
+static int
+ng_h4_mod_event(module_t mod, int event, void *data)
+{
+ static int ng_h4_ldisc;
+ int s, error = 0;
+
+ s = spltty(); /* XXX */
+
+ switch (event) {
+ case MOD_LOAD:
+ /* Register line discipline */
+ ng_h4_ldisc = ldisc_register(H4DISC, &ng_h4_disc);
+ if (ng_h4_ldisc < 0) {
+ printf("%s: can't register H4 line discipline\n",
+ __func__);
+ error = EIO;
+ }
+ break;
+
+ case MOD_UNLOAD:
+ /* Unregister line discipline */
+ ldisc_deregister(ng_h4_ldisc);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ splx(s); /* XXX */
+
+ return (error);
+} /* ng_h4_mod_event */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h b/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h
new file mode 100644
index 0000000..a4ba75d
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4_prse.h
@@ -0,0 +1,122 @@
+/*
+ * ng_h4_prse.h
+ *
+ * Copyright (c) 2001 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_h4_prse.h,v 1.6 2002/09/04 21:35:01 max Exp $
+ * $FreeBSD$
+ */
+
+/***************************************************************************
+ ***************************************************************************
+ ** ng_parse definitions for the H4 node
+ ***************************************************************************
+ ***************************************************************************/
+
+#ifndef _NETGRAPH_H4_PRSE_H_
+#define _NETGRAPH_H4_PRSE_H_ 1
+
+/*
+ * H4 node command list
+ */
+
+/* Stat info */
+static const struct ng_parse_struct_field ng_h4_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_h4_stat_type = {
+ &ng_parse_struct_type,
+ &ng_h4_stat_type_fields
+};
+
+static const struct ng_cmdlist ng_h4_cmdlist[] = {
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_RESET,
+ "reset",
+ NULL,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_STATE,
+ "get_state",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_QLEN,
+ "get_qlen",
+ NULL,
+ &ng_parse_int32_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_parse_int32_type,
+ NULL
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_h4_stat_type
+ },
+ {
+ NGM_H4_COOKIE,
+ NGM_H4_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+ },
+ { 0, }
+};
+
+#endif /* ndef _NETGRAPH_H4_PRSE_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h b/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h
new file mode 100644
index 0000000..1b0d5b8
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/h4/ng_h4_var.h
@@ -0,0 +1,100 @@
+/*
+ * ng_h4_var.h
+ *
+ * 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_h4_var.h,v 1.14 2002/11/03 02:16:31 max Exp $
+ * $FreeBSD$
+ *
+ * Based on:
+ * ---------
+ *
+ * FreeBSD: src/sys/netgraph/ng_tty.h
+ * Author: Archie Cobbs <archie@freebsd.org>
+ */
+
+#ifndef _NETGRAPH_H4_VAR_H_
+#define _NETGRAPH_H4_VAR_H_ 1
+
+/*
+ * Malloc declaration
+ */
+
+#ifndef NG_SEPARATE_MALLOC
+MALLOC_DECLARE(M_NETGRAPH_H4);
+#else
+#define M_NETGRAPH_H4 M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/*
+ * Debug
+ */
+
+#define NG_H4_ALERT if (sc->debug >= NG_H4_ALERT_LEVEL) printf
+#define NG_H4_ERR if (sc->debug >= NG_H4_ERR_LEVEL) printf
+#define NG_H4_WARN if (sc->debug >= NG_H4_WARN_LEVEL) printf
+#define NG_H4_INFO if (sc->debug >= NG_H4_INFO_LEVEL) printf
+
+#define NG_H4_HIWATER 256 /* High water mark on output */
+
+/*
+ * Per-node private info
+ */
+
+typedef struct ng_h4_info {
+ struct tty *tp; /* Terminal device */
+ node_p node; /* Netgraph node */
+
+ u_int32_t flags; /* Flags */
+#define NG_H4_TIMEOUT (1 << 0) /* A timeout is pending */
+
+ ng_h4_node_debug_ep debug; /* Debug level */
+ ng_h4_node_state_ep state; /* State */
+
+ ng_h4_node_stat_ep stat;
+#define NG_H4_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_H4_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_H4_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_H4_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_H4_STAT_OERROR(s) (s).oerrors ++
+#define NG_H4_STAT_IERROR(s) (s).ierrors ++
+#define NG_H4_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ ng_bt_mbufq_t outq; /* Queue of outgoing mbuf's */
+#define NG_H4_DEFAULTQLEN 12 /* XXX max number of mbuf's in outq */
+
+#define NG_H4_IBUF_SIZE 1024 /* XXX must be big enough to hold full
+ frame */
+ u_int8_t ibuf[NG_H4_IBUF_SIZE]; /* Incoming data */
+ u_int32_t got; /* Number of bytes we have received */
+ u_int32_t want; /* Number of bytes we want to receive */
+
+ hook_p hook; /* Upstream hook */
+ struct callout_handle timo; /* See man timeout(9) */
+} ng_h4_info_t;
+typedef ng_h4_info_t * ng_h4_info_p;
+
+#endif /* _NETGRAPH_H4_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/drivers/ubt/TODO b/sys/netgraph/bluetooth/drivers/ubt/TODO
new file mode 100644
index 0000000..aba45ed
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/TODO
@@ -0,0 +1,30 @@
+# $FreeBSD$
+$Id: TODO,v 1.1.1.1 2002/06/09 20:21:47 max Exp $
+
+1) SMP/Locking
+
+ The code makes use of ng_send_fn() whenever possible. Just
+ need to verify and make sure i did it right
+
+2) Review USB ATTACH function
+
+ It is a bit ugly now. Probably need a better way to discover
+ USB device configuration.
+
+2) Firmware upgrade
+
+ According to Bluetooth spec device may present third interface
+ to perform firmware upgrade. 3Com USB Bluetooth dongle has
+ such interface. Need to implement set of Netgraph messages.
+
+3) Understand and fix isoc. USB transfers (SCO data)
+
+ Currenty device reports that is got zero bytes and calls
+ isoc_in_complete callback over and over again. Why?
+ Also might need to setup at least two isoc. transfers in
+ both directions and switch them on the fly. Just to ensure
+ there at least one transfer at any time ready to run.
+
+4) Currently interrupt transfers are done as bulk-in transfers
+
+ Need to check if that is allowed.
diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
new file mode 100644
index 0000000..982b939
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
@@ -0,0 +1,2166 @@
+/*
+ * ng_ubt.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_ubt.c,v 1.1 2002/11/09 19:09:02 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/interrupt.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <ng_bluetooth.h>
+#include <ng_hci.h>
+#include "ng_ubt.h"
+#include "ng_ubt_var.h"
+
+/*
+ * USB methods
+ */
+
+USB_DECLARE_DRIVER(ubt);
+
+Static usbd_status ubt_request_start (ubt_softc_p, struct mbuf *);
+Static void ubt_request_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_intr_start (ubt_softc_p);
+Static void ubt_intr_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_in_start (ubt_softc_p);
+Static void ubt_bulk_in_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_out_start (ubt_softc_p, struct mbuf *);
+Static void ubt_bulk_out_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_in_start (ubt_softc_p);
+Static void ubt_isoc_in_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_out_start (ubt_softc_p, struct mbuf *);
+Static void ubt_isoc_out_complete (usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static void ubt_swi_intr (void *);
+
+/*
+ * Netgraph methods
+ */
+
+Static ng_constructor_t ng_ubt_constructor;
+Static ng_shutdown_t ng_ubt_shutdown;
+Static ng_newhook_t ng_ubt_newhook;
+Static ng_connect_t ng_ubt_connect;
+Static ng_disconnect_t ng_ubt_disconnect;
+Static ng_rcvmsg_t ng_ubt_rcvmsg;
+Static ng_rcvdata_t ng_ubt_rcvdata;
+Static void ng_ubt_reset (ubt_softc_p);
+
+/* Queue length */
+Static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
+{
+ { "queue", &ng_parse_int32_type, },
+ { "qlen", &ng_parse_int32_type, },
+ { NULL, }
+};
+Static const struct ng_parse_type ng_ubt_node_qlen_type = {
+ &ng_parse_struct_type,
+ &ng_ubt_node_qlen_type_fields
+};
+
+/* Stat info */
+Static const struct ng_parse_struct_field ng_ubt_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_ubt_node_stat_type = {
+ &ng_parse_struct_type,
+ &ng_ubt_node_stat_type_fields
+};
+
+/* Netgraph node command list */
+Static const struct ng_cmdlist ng_ubt_cmdlist[] = {
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_ubt_node_qlen_type,
+ NULL
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_QLEN,
+ "get_qlen",
+ &ng_ubt_node_qlen_type,
+ &ng_ubt_node_qlen_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_ubt_node_stat_type
+},
+{
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+},
+{ 0, }
+};
+
+/* Netgraph node type */
+Static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_UBT_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_ubt_constructor, /* constructor */
+ ng_ubt_rcvmsg, /* control message */
+ ng_ubt_shutdown, /* destructor */
+ ng_ubt_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_ubt_connect, /* connect hook */
+ ng_ubt_rcvdata, /* data */
+ ng_ubt_disconnect, /* disconnect hook */
+ ng_ubt_cmdlist /* node command list */
+};
+NETGRAPH_INIT(ubt, &typestruct);
+
+/*
+ * Module
+ */
+
+DRIVER_MODULE(ubt, uhub, ubt_driver, ubt_devclass, usbd_driver_load, 0);
+MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
+
+/****************************************************************************
+ ****************************************************************************
+ ** USB specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Probe for a USB Bluetooth device
+ */
+
+USB_MATCH(ubt)
+{
+ Static struct usb_devno const ubt_devices[] = {
+ { USB_VENDOR_3COM, USB_PRODUCT_3COM_3CREB96 },
+ { USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_BT_DONGLE },
+ { USB_VENDOR_TDK, USB_PRODUCT_TDK_BT_DONGLE },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_BT_DONGLE },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_DBW_120M_BT_DONGLE },
+ { USB_VENDOR_EPOX, USB_PRODUCT_BT_DG02_DONGLE },
+ { 0, 0 }
+ };
+
+ USB_MATCH_START(ubt, uaa);
+
+ if (uaa->iface == NULL ||
+ usb_lookup(ubt_devices, uaa->vendor, uaa->product) == NULL)
+ return (UMATCH_NONE);
+
+ return (UMATCH_VENDOR_PRODUCT);
+} /* USB_MATCH(ubt) */
+
+/*
+ * Attach the device
+ */
+
+USB_ATTACH(ubt)
+{
+ USB_ATTACH_START(ubt, sc, uaa);
+ usb_config_descriptor_t *cd = NULL;
+ usb_interface_descriptor_t *id = NULL;
+ usb_endpoint_descriptor_t *ed = NULL;
+ char devinfo[1024];
+ usbd_status error;
+ int i, ai, alt_no, isoc_in, isoc_out,
+ isoc_isize, isoc_osize;
+
+ /* Get USB device info */
+ sc->sc_udev = uaa->device;
+ usbd_devinfo(sc->sc_udev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ /*
+ * Initialize device softc structure
+ */
+
+ /* State */
+ sc->sc_debug = NG_UBT_WARN_LEVEL;
+ sc->sc_flags = 0;
+ NG_UBT_STAT_RESET(sc->sc_stat);
+
+ /* Interfaces */
+ sc->sc_iface0 = sc->sc_iface1 = NULL;
+
+ /* Input queue */
+ bzero(&sc->sc_inq, sizeof(sc->sc_inq));
+ sc->sc_inq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_inq.ifq_mtx, "UBT inq", NULL, MTX_DEF);
+
+ /* Interrupt pipe */
+ sc->sc_intr_ep = -1;
+ sc->sc_intr_pipe = NULL;
+ sc->sc_intr_xfer = NULL;
+ sc->sc_intr_buffer = NULL;
+
+ /* Control pipe */
+ sc->sc_ctrl_xfer = NULL;
+ sc->sc_ctrl_buffer = NULL;
+ bzero(&sc->sc_cmdq, sizeof(sc->sc_cmdq));
+ sc->sc_cmdq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_cmdq.ifq_mtx, "UBT cmdq", NULL, MTX_DEF);
+
+ /* Bulk-in pipe */
+ sc->sc_bulk_in_ep = -1;
+ sc->sc_bulk_in_pipe = NULL;
+ sc->sc_bulk_in_xfer = NULL;
+ sc->sc_bulk_in_buffer = NULL;
+
+ /* Bulk-out pipe */
+ sc->sc_bulk_out_ep = -1;
+ sc->sc_bulk_out_pipe = NULL;
+ sc->sc_bulk_out_xfer = NULL;
+ sc->sc_bulk_out_buffer = NULL;
+ bzero(&sc->sc_aclq, sizeof(sc->sc_aclq));
+ sc->sc_aclq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_aclq.ifq_mtx, "UBT aclq", NULL, MTX_DEF);
+
+ /* Isoc-in pipe */
+ sc->sc_isoc_in_ep = -1;
+ sc->sc_isoc_in_pipe = NULL;
+ sc->sc_isoc_in_xfer = NULL;
+
+ /* Isoc-out pipe */
+ sc->sc_isoc_out_ep = -1;
+ sc->sc_isoc_out_pipe = NULL;
+ sc->sc_isoc_out_xfer = NULL;
+ sc->sc_isoc_size = -1;
+ bzero(&sc->sc_scoq, sizeof(sc->sc_scoq));
+ sc->sc_scoq.ifq_maxlen = UBT_DEFAULT_QLEN;
+ mtx_init(&sc->sc_scoq.ifq_mtx, "UBT scoq", NULL, MTX_DEF);
+
+ /* Netgraph part */
+ sc->sc_node = NULL;
+ sc->sc_hook = NULL;
+
+ /* Attach SWI handler to TTY SWI thread */
+ sc->sc_ith = NULL;
+ if (swi_add(&tty_ithd, USBDEVNAME(sc->sc_dev),
+ ubt_swi_intr, sc, SWI_TTY, 0, &sc->sc_ith) < 0) {
+ printf("%s: Could not setup SWI ISR\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * XXX set configuration?
+ *
+ * Configure Bluetooth USB device. Discover all required USB interfaces
+ * and endpoints.
+ *
+ * USB device must present two interfaces:
+ * 1) Interface 0 that has 3 endpoints
+ * 1) Interrupt endpoint to receive HCI events
+ * 2) Bulk IN endpoint to receive ACL data
+ * 3) Bulk OUT endpoint to send ACL data
+ *
+ * 2) Interface 1 then has 2 endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ *
+ * Interface 1 (with isochronous endpoints) has several alternate
+ * configurations with different packet size.
+ */
+
+ /*
+ * Interface 0
+ */
+
+ error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
+ if (error || sc->sc_iface0 == NULL) {
+ printf("%s: Could not get interface 0 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface0);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface0);
+ if (id == NULL) {
+ printf("%s: Could not get interface 0 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER ||
+ id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER ||
+ id->bInterfaceProtocol != UIPROTO_BLUETOOTH) {
+ printf("%s: Interface 0 is not supported, " \
+ "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\
+ "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev),
+ id->bInterfaceClass, id->bInterfaceSubClass,
+ id->bInterfaceProtocol);
+ goto bad;
+ }
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint descriptor for " \
+ "interface 0, i=%d\n", USBDEVNAME(sc->sc_dev),
+ i);
+ goto bad;
+ }
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_BULK:
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_bulk_in_ep = ed->bEndpointAddress;
+ else
+ sc->sc_bulk_out_ep = ed->bEndpointAddress;
+ break;
+
+ case UE_INTERRUPT:
+ sc->sc_intr_ep = ed->bEndpointAddress;
+ break;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_intr_ep == -1) {
+ printf("%s: Could not detect interrupt endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_in_ep == -1) {
+ printf("%s: Could not detect bulk-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_out_ep == -1) {
+ printf("%s: Could not detect bulk-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 0 endpoints: interrupt=%#x, bulk-in=%#x, " \
+ "bulk-out=%#x\n", USBDEVNAME(sc->sc_dev),
+ sc->sc_intr_ep, sc->sc_bulk_in_ep, sc->sc_bulk_out_ep);
+
+ /*
+ * Interface 1
+ */
+
+ cd = usbd_get_config_descriptor(sc->sc_udev);
+ if (cd == NULL) {
+ printf("%s: Could not get device configuration descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ error = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
+ if (error || sc->sc_iface1 == NULL) {
+ printf("%s: Could not get interface 1 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface1);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER ||
+ id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER ||
+ id->bInterfaceProtocol != UIPROTO_BLUETOOTH) {
+ printf("%s: Interface 1 is not supported, " \
+ "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\
+ "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev),
+ id->bInterfaceClass, id->bInterfaceSubClass,
+ id->bInterfaceProtocol);
+ goto bad;
+ }
+
+ /*
+ * Scan all alternate configurations for interface 1
+ */
+
+ alt_no = -1;
+
+ for (ai = 0; ai < usbd_get_no_alts(cd, 1); ai++) {
+ error = usbd_set_interface(sc->sc_iface1, ai);
+ if (error) {
+ printf("%s: [SCAN] Could not set alternate " \
+ "configuration %d for interface 1. %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), ai, usbd_errstr(error),
+ error);
+ goto bad;
+ }
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor for " \
+ "alternate configuration %d\n",
+ USBDEVNAME(sc->sc_dev), ai);
+ goto bad;
+ }
+
+ isoc_in = isoc_out = -1;
+ isoc_isize = isoc_osize = 0;
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint " \
+ "descriptor for interface 1, " \
+ "alternate configuration %d, i=%d\n",
+ USBDEVNAME(sc->sc_dev), ai, i);
+ goto bad;
+ }
+
+ if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ isoc_in = ed->bEndpointAddress;
+ isoc_isize = UGETW(ed->wMaxPacketSize);
+ } else {
+ isoc_out = ed->bEndpointAddress;
+ isoc_osize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ /*
+ * Make sure that configuration looks sane and if so
+ * update current settings
+ */
+
+ if (isoc_in != -1 && isoc_out != -1 &&
+ isoc_isize > 0 && isoc_osize > 0 &&
+ isoc_isize == isoc_osize && isoc_isize > sc->sc_isoc_size) {
+ sc->sc_isoc_in_ep = isoc_in;
+ sc->sc_isoc_out_ep = isoc_out;
+ sc->sc_isoc_size = isoc_isize;
+ alt_no = ai;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_isoc_in_ep == -1) {
+ printf("%s: Could not detect isoc-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_out_ep == -1) {
+ printf("%s: Could not detect isoc-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_size <= 0) {
+ printf("%s: Invalid isoc. packet size=%d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_isoc_size);
+ goto bad;
+ }
+
+ error = usbd_set_interface(sc->sc_iface1, alt_no);
+ if (error) {
+ printf("%s: Could not set alternate configuration " \
+ "%d for interface 1. %s (%d)\n", USBDEVNAME(sc->sc_dev),
+ alt_no, usbd_errstr(error), error);
+ goto bad;
+ }
+
+ /* Allocate USB transfer handles and buffers */
+ sc->sc_ctrl_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ctrl_xfer == NULL) {
+ printf("%s: Could not allocate control xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_ctrl_buffer = usbd_alloc_buffer(sc->sc_ctrl_xfer,
+ UBT_CTRL_BUFFER_SIZE);
+ if (sc->sc_ctrl_buffer == NULL) {
+ printf("%s: Could not allocate control buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_intr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_intr_xfer == NULL) {
+ printf("%s: Could not allocate interrupt xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_intr_buffer = usbd_alloc_buffer(sc->sc_intr_xfer,
+ UBT_INTR_BUFFER_SIZE);
+ if (sc->sc_intr_buffer == NULL) {
+ printf("%s: Could not allocate interrupt buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_in_xfer == NULL) {
+ printf("%s: Could not allocate bulk-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_in_buffer = usbd_alloc_buffer(sc->sc_bulk_in_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_in_buffer == NULL) {
+ printf("%s: Could not allocate bulk-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_out_xfer == NULL) {
+ printf("%s: Could not allocate bulk-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_out_buffer = usbd_alloc_buffer(sc->sc_bulk_out_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_out_buffer == NULL) {
+ printf("%s: Could not allocate bulk-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * Allocate buffers for isoc. transfers
+ */
+
+ sc->sc_isoc_nframes = (UBT_ISOC_BUFFER_SIZE / sc->sc_isoc_size) + 1;
+
+ sc->sc_isoc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_in_xfer == NULL) {
+ printf("%s: Could not allocate isoc-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_buffer = usbd_alloc_buffer(sc->sc_isoc_in_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_in_buffer == NULL) {
+ printf("%s: Could not allocate isoc-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_in_frlen == NULL) {
+ printf("%s: Could not allocate isoc-in frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_isoc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_out_xfer == NULL) {
+ printf("%s: Could not allocate isoc-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_buffer = usbd_alloc_buffer(sc->sc_isoc_out_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_out_buffer == NULL) {
+ printf("%s: Could not allocate isoc-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_out_frlen == NULL) {
+ printf("%s: Could not allocate isoc-out frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 1 (alt.config %d) endpoints: isoc-in=%#x, " \
+ "isoc-out=%#x; wMaxPacketSize=%d; nframes=%d, buffer size=%d\n",
+ USBDEVNAME(sc->sc_dev), alt_no, sc->sc_isoc_in_ep,
+ sc->sc_isoc_out_ep, sc->sc_isoc_size, sc->sc_isoc_nframes,
+ (sc->sc_isoc_nframes * sc->sc_isoc_size));
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_node = NULL;
+ goto bad;
+ }
+
+ /* Name node */
+ if (ng_name_node(sc->sc_node, USBDEVNAME(sc->sc_dev)) != 0) {
+ printf("%s: Could not name Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto bad;
+ }
+
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+
+ /*
+ * XXX Is that correct?
+ * Claim all interfaces on the device
+ */
+
+ for (i = 0; i < uaa->nifaces; i++)
+ uaa->ifaces[i] = NULL;
+
+ USB_ATTACH_SUCCESS_RETURN;
+bad:
+ ubt_detach(self);
+
+ USB_ATTACH_ERROR_RETURN;
+} /* USB_ATTACH(ubt) */
+
+/*
+ * Detach the device
+ */
+
+USB_DETACH(ubt)
+{
+ USB_DETACH_START(ubt, sc);
+
+ ng_ubt_reset(sc);
+
+ /* Destroy Netgraph node */
+ if (sc->sc_node != NULL) {
+ NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
+ ng_rmnode_self(sc->sc_node);
+ sc->sc_node = NULL;
+ }
+
+ /* Destroy USB transfer handles */
+ if (sc->sc_ctrl_xfer != NULL) {
+ usbd_free_xfer(sc->sc_ctrl_xfer);
+ sc->sc_ctrl_xfer = NULL;
+ }
+
+ if (sc->sc_intr_xfer != NULL) {
+ usbd_free_xfer(sc->sc_intr_xfer);
+ sc->sc_intr_xfer = NULL;
+ }
+
+ if (sc->sc_bulk_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_in_xfer);
+ sc->sc_bulk_in_xfer = NULL;
+ }
+ if (sc->sc_bulk_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_out_xfer);
+ sc->sc_bulk_out_xfer = NULL;
+ }
+
+ if (sc->sc_isoc_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_in_xfer);
+ sc->sc_isoc_in_xfer = NULL;
+ }
+ if (sc->sc_isoc_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_out_xfer);
+ sc->sc_isoc_out_xfer = NULL;
+ }
+
+ /* Destroy isoc. frame size buffers */
+ if (sc->sc_isoc_in_frlen != NULL) {
+ free(sc->sc_isoc_in_frlen, M_USBDEV);
+ sc->sc_isoc_in_frlen = NULL;
+ }
+ if (sc->sc_isoc_out_frlen != NULL) {
+ free(sc->sc_isoc_out_frlen, M_USBDEV);
+ sc->sc_isoc_out_frlen = NULL;
+ }
+
+ if (sc->sc_ith != NULL) {
+ ithread_remove_handler(sc->sc_ith);
+ sc->sc_ith = NULL;
+ }
+
+ /* Destroy queues */
+ IF_DRAIN(&sc->sc_cmdq);
+ IF_DRAIN(&sc->sc_aclq);
+ IF_DRAIN(&sc->sc_scoq);
+ IF_DRAIN(&sc->sc_inq);
+
+ mtx_destroy(&sc->sc_cmdq.ifq_mtx);
+ mtx_destroy(&sc->sc_aclq.ifq_mtx);
+ mtx_destroy(&sc->sc_scoq.ifq_mtx);
+ mtx_destroy(&sc->sc_inq.ifq_mtx);
+
+ return (0);
+} /* USB_DETACH(ubt) */
+
+/*
+ * Start USB control request (HCI command)
+ */
+
+Static usbd_status
+ubt_request_start(ubt_softc_p sc, struct mbuf *m)
+{
+ usb_device_request_t req;
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_cmdq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_cmdq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI command frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_cmdq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_cmdq, m);
+ } else
+ sc->sc_flags &= ~UBT_CMD_XMIT;
+
+ if (sc->sc_flags & UBT_CMD_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another control request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_cmdq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - HCI command queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /*
+ * Check HCI command frame size and copy it back
+ * to linear USB transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE)
+ panic(
+"%s: %s - HCI command frame too big, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_CTRL_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_ctrl_buffer);
+
+ /* Initialize a USB control request and then schedule it */
+
+ bzero(&req, sizeof(req));
+ req.bmRequestType = UBT_HCI_REQUEST;
+ USETW(req.wLength, m->m_pkthdr.len);
+
+ NG_UBT_INFO(
+"%s: %s - Sending control request, bmRequestType=%#x, wLength=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), req.bmRequestType,
+ UGETW(req.wLength));
+
+ usbd_setup_default_xfer(
+ sc->sc_ctrl_xfer,
+ sc->sc_udev,
+ (usbd_private_handle) sc,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ &req,
+ sc->sc_ctrl_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ ubt_request_complete);
+
+ status = usbd_transfer(sc->sc_ctrl_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start control request. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ _IF_DROP(&sc->sc_cmdq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME: should we try to resubmit another request? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Control request has been started\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ sc->sc_flags |= UBT_CMD_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+done:
+ IF_UNLOCK(&sc->sc_cmdq);
+
+ return (status);
+} /* ubt_request_start */
+
+/*
+ * USB control request callback
+ */
+
+Static void
+ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Control request cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ERR(
+"%s: %s - Control request failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(h->pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to control pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_request_start(sc, NULL /* completed request */);
+} /* ubt_request_complete */
+
+/*
+ * Start interrupt transfer
+ */
+
+Static usbd_status
+ubt_intr_start(ubt_softc_p sc)
+{
+ usbd_status status;
+
+ /* Initialize a USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_intr_xfer,
+ sc->sc_intr_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_intr_buffer,
+ UBT_INTR_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT,
+ ubt_intr_complete);
+
+ status = usbd_transfer(sc->sc_intr_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start intrerrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_intr_start */
+
+/*
+ * Process interrupt from USB device (We got data from interrupt pipe)
+ */
+
+Static void
+ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_event_pkt_t *hdr = NULL;
+ int off;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Interrupt xfer cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Interrupt xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from interrupt pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ /* Copy HCI event frame to mbuf */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n", __func__, USBDEVNAME(sc->sc_dev));
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /*
+ * Copy data from USB buffer into mbuf and check if we got
+ * full HCI event frame. Fix HCI event frame if required.
+ */
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ off = 1;
+ *mtod(m, u_int8_t *) = NG_HCI_EVENT_PKT;
+ } else
+ off = 0;
+
+ m->m_pkthdr.len = 0;
+ m->m_len = min(MHLEN, h->actlen + off); /* XXX m_copyback is stupid */
+ m_copyback(m, off, h->actlen, sc->sc_intr_buffer);
+
+ hdr = mtod(m, ng_hci_event_pkt_t *);
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete HCI event frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid HCI event frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_intr_start(sc);
+} /* ubt_intr_complete */
+
+/*
+ * Start bulk-in USB transfer (ACL data)
+ */
+
+Static usbd_status
+ubt_bulk_in_start(ubt_softc_p sc)
+{
+ usbd_status status;
+
+ /* Initialize a bulk-in USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_in_xfer,
+ sc->sc_bulk_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_in_buffer,
+ UBT_BULK_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT,
+ ubt_bulk_in_complete);
+
+ status = usbd_transfer(sc->sc_bulk_in_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_bulk_in_start */
+
+/*
+ * USB bulk-in transfer callback
+ */
+
+Static void
+ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_acldata_pkt_t *hdr = NULL;
+ int len;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_bulk_in_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-in xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from bulk-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /*
+ * Copy data from linear USB buffer into mbuf and check if we got
+ * full ACL data frame. Fix ACL data frame header if required.
+ */
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ len = 1;
+ *mtod(m, u_int8_t *) = NG_HCI_ACL_DATA_PKT;
+ } else
+ len = 0;
+
+ m->m_pkthdr.len = 0;
+ m->m_len = min(MHLEN, h->actlen + len); /* XXX m_copyback is stupid */
+ m_copyback(m, len, h->actlen, sc->sc_bulk_in_buffer);
+
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ len = le16toh(hdr->length);
+
+ if (len == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete ACL data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ len);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid ACL frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), len,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_bulk_in_start(sc);
+} /* ubt_bulk_in_complete */
+
+/*
+ * Start bulk-out USB transfer
+ */
+
+Static usbd_status
+ubt_bulk_out_start(ubt_softc_p sc, struct mbuf *m)
+{
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_aclq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_aclq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI ACL frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_aclq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_aclq, m);
+ } else
+ sc->sc_flags &= ~UBT_ACL_XMIT;
+
+ if (sc->sc_flags & UBT_ACL_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another bulk-out transfer is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_aclq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - ACL data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /*
+ * Check ACL data frame size and copy it back to linear USB
+ * transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_BULK_BUFFER_SIZE)
+ panic(
+"%s: %s - ACL data frame too big, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_BULK_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_bulk_out_buffer);
+
+ /* Initialize a bulk-out USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_out_xfer,
+ sc->sc_bulk_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_out_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ ubt_bulk_out_complete);
+
+ status = usbd_transfer(sc->sc_bulk_out_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start bulk-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ _IF_DROP(&sc->sc_aclq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME: should we try to start another transfer? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out transfer has been started, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len);
+
+ sc->sc_flags |= UBT_ACL_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+done:
+ IF_UNLOCK(&sc->sc_aclq);
+
+ return (status);
+} /* ubt_bulk_out_start */
+
+/*
+ * USB bulk-out transfer callback
+ */
+
+Static void
+ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ sc->sc_bulk_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to bulk-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_bulk_out_start(sc, NULL /* completed request */);
+} /* ubt_bulk_out_complete */
+
+/*
+ * Start Isochronous-in USB transfer
+ */
+
+Static usbd_status
+ubt_isoc_in_start(ubt_softc_p sc)
+{
+ usbd_status status;
+ int i;
+
+ /* Initialize a isoc-in USB transfer and then schedule it */
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++)
+ sc->sc_isoc_in_frlen[i] = sc->sc_isoc_size;
+
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_in_xfer,
+ sc->sc_isoc_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_in_frlen,
+ sc->sc_isoc_nframes,
+ USBD_NO_COPY, /* XXX flags */
+ ubt_isoc_in_complete);
+
+ status = usbd_transfer(sc->sc_isoc_in_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ return (status);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_isoc_in_start */
+
+/*
+ * USB isochronous transfer callback
+ */
+
+Static void
+ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+ struct mbuf *m = NULL;
+ ng_hci_scodata_pkt_t *hdr = NULL;
+ u_int8_t *b = NULL;
+ int i;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_in_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-in xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from isoc-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (h->actlen < sizeof(*hdr))
+ goto done;
+
+ /* Copy SCO data frame to mbuf */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /* Fix SCO data frame header if required */
+
+ if (sc->sc_flags & UBT_HAVE_FRAME_TYPE) {
+ m->m_pkthdr.len = m->m_len = 0;
+ } else {
+ *mtod(m, u_int8_t *) = NG_HCI_SCO_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+ }
+
+ /*
+ * XXX FIXME: how do we know how many frames we have received?
+ * XXX use frlen for now.
+ * XXX is that correct?
+ */
+
+ b = (u_int8_t *) sc->sc_isoc_in_buffer;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ if (sc->sc_isoc_in_frlen[i] > 0)
+ m_copyback(m, m->m_pkthdr.len,
+ sc->sc_isoc_in_frlen[i], b);
+ }
+
+ NG_UBT_M_PULLUP(m, sizeof(*hdr));
+ if (m == NULL) {
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ hdr = mtod(m, ng_hci_scodata_pkt_t *);
+
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete SCO data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+ IF_LOCK(&sc->sc_inq);
+ if (_IF_QFULL(&sc->sc_inq)) {
+ NG_UBT_ERR(
+"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_inq);
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_inq, m);
+ IF_UNLOCK(&sc->sc_inq);
+
+ /* Schedule SWI */
+ swi_sched(sc->sc_ith, 0);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid SCO frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_isoc_in_start(sc);
+} /* ubt_bulk_in_complete */
+
+/*
+ * Start isochronous-out USB transfer
+ */
+
+Static usbd_status
+ubt_isoc_out_start(ubt_softc_p sc, struct mbuf *m)
+{
+ u_int8_t *b = NULL;
+ int i, len, nframes;
+ usbd_status status = USBD_NORMAL_COMPLETION;
+
+ IF_LOCK(&sc->sc_scoq);
+
+ if (m != NULL) {
+ if (_IF_QFULL(&sc->sc_scoq)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI SCO frame, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ m->m_pkthdr.len);
+
+ _IF_DROP(&sc->sc_scoq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ NG_FREE_M(m);
+ } else
+ _IF_ENQUEUE(&sc->sc_scoq, m);
+ } else
+ sc->sc_flags &= ~UBT_SCO_XMIT;
+
+ if (sc->sc_flags & UBT_SCO_XMIT) {
+ NG_UBT_INFO(
+"%s: %s - Another isoc-out transfer is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ _IF_DEQUEUE(&sc->sc_scoq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - SCO data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+ goto done;
+ }
+
+ /* Copy entire SCO frame into USB transfer buffer and start transfer */
+
+ b = (u_int8_t *) sc->sc_isoc_out_buffer;
+ nframes = 0;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ len = min(m->m_pkthdr.len, sc->sc_isoc_size);
+ if (len > 0) {
+ m_copydata(m, 0, len, b);
+ m_adj(m, len);
+ nframes ++;
+ }
+
+ sc->sc_isoc_out_frlen[i] = len;
+ }
+
+ if (m->m_pkthdr.len > 0)
+ panic(
+"%s: %s - SCO data frame is too big, nframes=%d, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_nframes,
+ sc->sc_isoc_size, m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+
+ /* Initialize a isoc-out USB transfer and then schedule it */
+
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_out_xfer,
+ sc->sc_isoc_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_out_frlen,
+ nframes,
+ USBD_NO_COPY,
+ ubt_isoc_out_complete);
+
+ status = usbd_transfer(sc->sc_isoc_out_xfer);
+ if (status && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start isoc-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ _IF_DROP(&sc->sc_scoq); /* XXX */
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out transfer has been started, nframes=%d, size=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), nframes,
+ sc->sc_isoc_size);
+
+ sc->sc_flags |= UBT_SCO_XMIT;
+
+ status = USBD_NORMAL_COMPLETION;
+ }
+done:
+ IF_UNLOCK(&sc->sc_scoq);
+
+ return (status);
+} /* ubt_isoc_out_start */
+
+/*
+ * USB isoc-out. transfer callback
+ */
+
+Static void
+ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ ubt_softc_p sc = (ubt_softc_p) p;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ sc->sc_isoc_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to isoc-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ ubt_isoc_out_start(sc, NULL /* completed request */);
+} /* ubt_isoc_out_complete */
+
+/*
+ * SWI interrupt handler
+ */
+
+Static void
+ubt_swi_intr(void *context)
+{
+ ubt_softc_p sc = (ubt_softc_p) context;
+ struct mbuf *m = NULL;
+ int error;
+
+ if (sc->sc_hook != NULL && NG_HOOK_IS_VALID(sc->sc_hook)) {
+ for (;;) {
+ IF_DEQUEUE(&sc->sc_inq, m);
+ if (m == NULL)
+ break;
+
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+ if (error != 0)
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ }
+ } else {
+ IF_LOCK(&sc->sc_inq);
+ for (;;) {
+ _IF_DEQUEUE(&sc->sc_inq, m);
+ if (m == NULL)
+ break;
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+ IF_UNLOCK(&sc->sc_inq);
+ }
+} /* ubt_swi_intr */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+Static int
+ng_ubt_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_ubt_constructor */
+
+/*
+ * Netgraph node destructor. Destroy node only when device has been detached
+ */
+
+Static int
+ng_ubt_shutdown(node_p node)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ if (sc == NULL)
+ goto done;
+
+ /* Create Netgraph node */
+ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_node = NULL;
+ goto done;
+ }
+
+ /* Name node */
+ if (ng_name_node(sc->sc_node, USBDEVNAME(sc->sc_dev)) != 0) {
+ printf("%s: Could not name Netgraph node\n",
+ USBDEVNAME(sc->sc_dev));
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto done;
+ }
+
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+done:
+ return (0);
+} /* ng_ubt_shutdown */
+
+/*
+ * Create new hook. There can only be one.
+ */
+
+Static int
+ng_ubt_newhook(node_p node, hook_p hook, char const *name)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+ usbd_status status;
+
+ if (strcmp(name, NG_UBT_HOOK) != 0)
+ return (EINVAL);
+
+ if (sc->sc_hook != NULL)
+ return (EISCONN);
+
+ sc->sc_hook = hook;
+
+ /* Interrupt */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_intr_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open interrupt pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Bulk-in */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_in_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open bulk-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Bulk-out */
+ status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_out_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open bulk-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Isoc-in */
+ status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_in_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open isoc-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Isoc-out */
+ status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_out_pipe);
+ if (status) {
+ NG_UBT_ALERT(
+"%s: %s - Could not open isoc-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+#endif /* __broken__ */
+
+ return (0);
+fail:
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+
+ return (ENXIO);
+} /* ng_ubt_newhook */
+
+/*
+ * Connect hook. Start incoming USB transfers
+ */
+
+Static int
+ng_ubt_connect(hook_p hook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ usbd_status status;
+
+ /* Start intr transfer */
+ status = ubt_intr_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start interrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+ /* Start bulk-in transfer */
+ status = ubt_bulk_in_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Start isoc-in transfer */
+ status = ubt_isoc_in_start(sc);
+ if (status != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+ goto fail;
+ }
+#endif /* __broken__ */
+
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ return (0);
+fail:
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+
+ return (ENXIO);
+} /* ng_ubt_connect */
+
+/*
+ * Disconnect hook
+ */
+
+Static int
+ng_ubt_disconnect(hook_p hook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (sc != NULL) {
+ if (hook != sc->sc_hook)
+ return (EINVAL);
+
+ ng_ubt_reset(sc);
+ sc->sc_hook = NULL;
+ }
+
+ return (0);
+} /* ng_ubt_disconnect */
+
+/*
+ * Process control message
+ */
+
+Static int
+ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *rsp = NULL;
+ int error = 0, queue, qlen;
+ struct ifqueue *q = NULL;
+
+ 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" \
+ "CMD queue: [have:%d,max:%d]\n" \
+ "ACL queue: [have:%d,max:%d]\n" \
+ "SCO queue: [have:%d,max:%d]\n" \
+ "Inp queue: [have:%d,max:%d]",
+ (sc->sc_hook != NULL)? NG_UBT_HOOK : "",
+ sc->sc_flags,
+ sc->sc_debug,
+ _IF_QLEN(&sc->sc_cmdq), /* XXX */
+ sc->sc_cmdq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_aclq), /* XXX */
+ sc->sc_aclq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_scoq), /* XXX */
+ sc->sc_scoq.ifq_maxlen, /* XXX */
+ _IF_QLEN(&sc->sc_inq), /* XXX */
+ sc->sc_inq.ifq_maxlen /* XXX */ );
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_UBT_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UBT_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ sc->sc_debug =
+ *((ng_ubt_node_debug_ep *)(msg->data));
+ break;
+
+ case NGM_UBT_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_ubt_node_debug_ep *)(rsp->data)) =
+ sc->sc_debug;
+ break;
+
+ case NGM_UBT_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep))
+ error = EMSGSIZE;
+ else {
+ queue = ((ng_ubt_node_qlen_ep *)
+ (msg->data))->queue;
+ qlen = ((ng_ubt_node_qlen_ep *)
+ (msg->data))->qlen;
+
+ if (qlen <= 0) {
+ error = EINVAL;
+ break;
+ }
+
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_IN:
+ q = &sc->sc_inq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ q = NULL;
+ error = EINVAL;
+ break;
+ }
+
+ if (q != NULL)
+ q->ifq_maxlen = qlen; /* XXX */
+ }
+ break;
+
+ case NGM_UBT_NODE_GET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ queue = ((ng_ubt_node_qlen_ep *)(msg->data))->queue;
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_IN:
+ q = &sc->sc_inq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ q = NULL;
+ error = EINVAL;
+ break;
+ }
+
+ if (q != NULL) {
+ NG_MKRESPONSE(rsp, msg,
+ sizeof(ng_ubt_node_qlen_ep), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ ((ng_ubt_node_qlen_ep *)(rsp->data))->queue =
+ queue;
+ ((ng_ubt_node_qlen_ep *)(rsp->data))->qlen =
+ q->ifq_maxlen; /* XXX */
+ }
+ break;
+
+ case NGM_UBT_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ bcopy(&sc->sc_stat, rsp->data,
+ sizeof(ng_ubt_node_stat_ep));
+ break;
+
+ case NGM_UBT_NODE_RESET_STAT:
+ NG_UBT_STAT_RESET(sc->sc_stat);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_ubt_rcvmsg */
+
+/*
+ * Process data
+ */
+
+Static int
+ng_ubt_rcvdata(hook_p hook, item_p item)
+{
+ ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ usbd_status (*f)(ubt_softc_p, struct mbuf *) = NULL;
+ int error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto done;
+ }
+
+ if (hook != sc->sc_hook) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Deatch mbuf and get HCI frame type */
+ NGI_GET_M(item, m);
+
+ /* Process HCI frame */
+ switch (*mtod(m, u_int8_t *)) { /* XXX call m_pullup ? */
+ case NG_HCI_CMD_PKT:
+ f = ubt_request_start;
+ break;
+
+ case NG_HCI_ACL_DATA_PKT:
+ f = ubt_bulk_out_start;
+ break;
+
+#if __broken__ /* XXX FIXME */
+ case NG_HCI_SCO_DATA_PKT:
+ f = ubt_isoc_out_start;
+ break;
+#endif /* __broken__ */
+
+ default:
+ NG_UBT_ERR(
+"%s: %s - Dropping unknown/unsupported HCI frame, type=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), *mtod(m, u_int8_t *),
+ m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ error = EINVAL;
+
+ goto done;
+ /* NOT REACHED */
+ }
+
+ /* Loose frame type, if required */
+ if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE))
+ m_adj(m, sizeof(u_int8_t));
+
+ if ((*f)(sc, m) != USBD_NORMAL_COMPLETION)
+ error = EIO;
+done:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_ubt_rcvdata */
+
+/*
+ * Abort transfers and close all USB pipes.
+ */
+
+Static void
+ng_ubt_reset(ubt_softc_p sc)
+{
+ /* Abort transfers and close all USB pipes */
+
+ /* Interrupt */
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ /* Bulk-in/out */
+ if (sc->sc_bulk_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulk_in_pipe);
+ usbd_close_pipe(sc->sc_bulk_in_pipe);
+ sc->sc_bulk_in_pipe = NULL;
+ }
+ if (sc->sc_bulk_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulk_out_pipe);
+ usbd_close_pipe(sc->sc_bulk_out_pipe);
+ sc->sc_bulk_out_pipe = NULL;
+ }
+
+ /* Isoc-in/out */
+ if (sc->sc_isoc_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_isoc_in_pipe);
+ usbd_close_pipe(sc->sc_isoc_in_pipe);
+ sc->sc_isoc_in_pipe = NULL;
+ }
+ if (sc->sc_isoc_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_isoc_out_pipe);
+ usbd_close_pipe(sc->sc_isoc_out_pipe);
+ sc->sc_isoc_out_pipe = NULL;
+ }
+
+ /* Cleanup queues */
+ IF_DRAIN(&sc->sc_cmdq);
+ IF_DRAIN(&sc->sc_aclq);
+ IF_DRAIN(&sc->sc_scoq);
+ IF_DRAIN(&sc->sc_inq);
+} /* ng_ubt_reset */
+
diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
new file mode 100644
index 0000000..10b8911
--- /dev/null
+++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
@@ -0,0 +1,143 @@
+/*
+ * ng_ubt_var.h
+ *
+ * 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_ubt_var.h,v 1.1 2002/11/09 19:09:02 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NG_UBT_VAR_H_
+#define _NG_UBT_VAR_H_
+
+/* pullup wrapper */
+#define NG_UBT_M_PULLUP(m, s) \
+ do { \
+ if ((m)->m_len < (s)) \
+ (m) = m_pullup((m), (s)); \
+ if ((m) == NULL) \
+ NG_UBT_ALERT("%s: %s - m_pullup(%d) failed\n", \
+ __func__, USBDEVNAME(sc->sc_dev), (s)); \
+ } while (0)
+
+/* Debug printf's */
+#define NG_UBT_ALERT if (sc->sc_debug >= NG_UBT_ALERT_LEVEL) printf
+#define NG_UBT_ERR if (sc->sc_debug >= NG_UBT_ERR_LEVEL) printf
+#define NG_UBT_WARN if (sc->sc_debug >= NG_UBT_WARN_LEVEL) printf
+#define NG_UBT_INFO if (sc->sc_debug >= NG_UBT_INFO_LEVEL) printf
+
+/* Bluetooth USB control request type */
+#define UBT_HCI_REQUEST 0x20
+#define UBT_DEFAULT_QLEN 12
+
+/* USB device softc structure */
+struct ubt_softc {
+ /* State */
+ ng_ubt_node_debug_ep sc_debug; /* debug level */
+ u_int32_t sc_flags; /* device flags */
+#define UBT_NEED_FRAME_TYPE (1 << 0) /* device required frame type */
+#define UBT_HAVE_FRAME_TYPE UBT_NEED_FRAME_TYPE
+#define UBT_CMD_XMIT (1 << 1) /* CMD xmit in progress */
+#define UBT_ACL_XMIT (1 << 2) /* ACL xmit in progress */
+#define UBT_SCO_XMIT (1 << 3) /* SCO xmit in progress */
+
+ ng_ubt_node_stat_ep sc_stat; /* statistic */
+#define NG_UBT_STAT_PCKTS_SENT(s) (s).pckts_sent ++
+#define NG_UBT_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n)
+#define NG_UBT_STAT_PCKTS_RECV(s) (s).pckts_recv ++
+#define NG_UBT_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n)
+#define NG_UBT_STAT_OERROR(s) (s).oerrors ++
+#define NG_UBT_STAT_IERROR(s) (s).ierrors ++
+#define NG_UBT_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ /* USB device specific */
+ USBBASEDEVICE sc_dev; /* pointer back to USB device */
+ usbd_device_handle sc_udev; /* USB device handle */
+
+ usbd_interface_handle sc_iface0; /* USB interface 0 */
+ usbd_interface_handle sc_iface1; /* USB interface 1 */
+
+ struct ifqueue sc_inq; /* incoming queue */
+ void *sc_ith; /* SWI interrupt handler */
+
+ /* Interrupt pipe (HCI events) */
+ int sc_intr_ep; /* interrupt endpoint */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe handle */
+ usbd_xfer_handle sc_intr_xfer; /* intr xfer */
+ u_int8_t *sc_intr_buffer; /* interrupt buffer */
+#define UBT_INTR_BUFFER_SIZE \
+ (sizeof(ng_hci_event_pkt_t) + NG_HCI_EVENT_PKT_SIZE)
+
+ /* Control pipe (HCI commands) */
+ usbd_xfer_handle sc_ctrl_xfer; /* control xfer handle */
+ void *sc_ctrl_buffer; /* control buffer */
+ struct ifqueue sc_cmdq; /* HCI command queue */
+#define UBT_CTRL_BUFFER_SIZE \
+ (sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE)
+
+ /* Bulk in pipe (ACL data) */
+ int sc_bulk_in_ep; /* bulk-in enpoint */
+ usbd_pipe_handle sc_bulk_in_pipe; /* bulk-in pipe */
+ usbd_xfer_handle sc_bulk_in_xfer; /* bulk-in xfer */
+ void *sc_bulk_in_buffer; /* bulk-in buffer */
+
+ /* Bulk out pipe (ACL data) */
+ int sc_bulk_out_ep; /* bulk-out endpoint */
+ usbd_pipe_handle sc_bulk_out_pipe; /* bulk-out pipe */
+ usbd_xfer_handle sc_bulk_out_xfer; /* bulk-out xfer */
+ void *sc_bulk_out_buffer; /* bulk-out buffer */
+ struct ifqueue sc_aclq; /* ACL data queue */
+#define UBT_BULK_BUFFER_SIZE \
+ 512 /* XXX should be big enough to hold one frame */
+
+ /* Isoc. in pipe (SCO data) */
+ int sc_isoc_in_ep; /* isoc-in endpoint */
+ usbd_pipe_handle sc_isoc_in_pipe; /* isoc-in pipe */
+ usbd_xfer_handle sc_isoc_in_xfer; /* isoc-in xfer */
+ void *sc_isoc_in_buffer; /* isoc-in buffer */
+ u_int16_t *sc_isoc_in_frlen; /* isoc-in. frame length */
+
+ /* Isoc. out pipe (ACL data) */
+ int sc_isoc_out_ep; /* isoc-out endpoint */
+ usbd_pipe_handle sc_isoc_out_pipe; /* isoc-out pipe */
+ usbd_xfer_handle sc_isoc_out_xfer; /* isoc-out xfer */
+ void *sc_isoc_out_buffer; /* isoc-in buffer */
+ u_int16_t *sc_isoc_out_frlen; /* isoc-out. frame length */
+ struct ifqueue sc_scoq; /* SCO data queue */
+
+ int sc_isoc_size; /* max. size of isoc. packet */
+ u_int32_t sc_isoc_nframes; /* num. isoc. frames */
+#define UBT_ISOC_BUFFER_SIZE \
+ (sizeof(ng_hci_scodata_pkt_t) + NG_HCI_SCO_PKT_SIZE)
+
+ /* Netgraph specific */
+ node_p sc_node; /* pointer back to node */
+ hook_p sc_hook; /* upstream hook */
+};
+typedef struct ubt_softc ubt_softc_t;
+typedef struct ubt_softc * ubt_softc_p;
+
+#endif /* ndef _NG_UBT_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/TODO b/sys/netgraph/bluetooth/hci/TODO
new file mode 100644
index 0000000..f277e7e
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/TODO
@@ -0,0 +1,42 @@
+# $FreeBSD$
+
+$Id: TODO,v 1.10 2002/09/06 21:03:57 max Exp $
+
+FIXME/TODO list
+
+This is a list of open issues for HCI node
+
+1) Locking/SMP
+
+ External code now uses ng_send_fn to inject data into Netgraph, so
+ it should be fine as long as Netgraph is SMP safe. Just need to
+ verify it.
+
+3) HCI QoS handling
+
+ Some code exists, but i have no idea how it should work. Will
+ understand and fix later. I only have CSR based hardware and
+ it does not support QoS.
+
+4) Add proper handling for some HCI commands
+
+ HCI testing commands is one example. Also might implement Host to
+ Host Controller flow control (not sure if it is required).
+
+5) Link security
+
+ Manage link keys and PINs. Options:
+
+ 1) manage keys inside HCI node/unit itself
+ 2) use user space daemon.
+ 3) Mix option 1 and option 2.
+
+6) Implement watchdog routine for HCI connections
+
+ Some code exists, but it is not used
+
+7) Code cleanup
+
+ Verify return codes from functions
+ Remove some waringns/errors
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_cmds.c b/sys/netgraph/bluetooth/hci/ng_hci_cmds.c
new file mode 100644
index 0000000..5769ee5
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_cmds.c
@@ -0,0 +1,887 @@
+/*
+ * ng_hci_cmds.c
+ *
+ * Copyright (c) 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_hci_cmds.c,v 1.22 2002/10/30 00:18:18 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_hci_var.h"
+#include "ng_hci_cmds.h"
+#include "ng_hci_evnt.h"
+#include "ng_hci_ulpi.h"
+#include "ng_hci_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** HCI commands processing module
+ ******************************************************************************
+ ******************************************************************************/
+
+#undef min
+#define min(a, b) ((a) < (b))? (a) : (b)
+
+static int complete_command (ng_hci_unit_p, int, struct mbuf **);
+
+static int process_link_control_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+static int process_link_policy_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+static int process_hc_baseband_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+static int process_info_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+static int process_status_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+static int process_testing_params
+ (ng_hci_unit_p, u_int16_t, struct mbuf *, struct mbuf *);
+
+static int process_link_control_status
+ (ng_hci_unit_p, ng_hci_command_status_ep *, struct mbuf *);
+static int process_link_policy_status
+ (ng_hci_unit_p, ng_hci_command_status_ep *, struct mbuf *);
+
+/*
+ * Send HCI command to the driver.
+ */
+
+int
+ng_hci_send_command(ng_hci_unit_p unit)
+{
+ struct mbuf *m0 = NULL, *m = NULL;
+ int free, error = 0;
+
+ /* Check if other command is pending */
+ if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
+ return (0);
+
+ /* Check if unit can accept our command */
+ NG_HCI_BUFF_CMD_GET(unit->buffer, free);
+ if (free == 0)
+ return (0);
+
+ /* Check if driver hook is still ok */
+ if (unit->drv == NULL || NG_HOOK_NOT_VALID(unit->drv)) {
+ NG_HCI_WARN(
+"%s: %s - hook \"%s\" is not connected or valid\n",
+ __func__, NG_NODE_NAME(unit->node), NG_HCI_HOOK_DRV);
+
+ NG_BT_MBUFQ_DRAIN(&unit->cmdq);
+
+ return (ENOTCONN);
+ }
+
+ /*
+ * Get first command from queue, give it to RAW hook then
+ * make copy of it and send it to the driver
+ */
+
+ m0 = NG_BT_MBUFQ_FIRST(&unit->cmdq);
+ if (m0 == NULL)
+ return (0);
+
+ ng_hci_mtap(unit, m0);
+
+ m = m_dup(m0, M_DONTWAIT);
+ if (m != NULL)
+ NG_SEND_DATA_ONLY(error, unit->drv, m);
+ else
+ error = ENOBUFS;
+
+ if (error != 0)
+ NG_HCI_ERR(
+"%s: %s - could not send HCI command, error=%d\n",
+ __func__, NG_NODE_NAME(unit->node), error);
+
+ /*
+ * Even if we were not able to send command we still pretend
+ * that everything is OK and let timeout handle that.
+ */
+
+ NG_HCI_BUFF_CMD_USE(unit->buffer, 1);
+ NG_HCI_STAT_CMD_SENT(unit->stat);
+ NG_HCI_STAT_BYTES_SENT(unit->stat, m0->m_pkthdr.len);
+
+ /*
+ * Note: ng_hci_command_timeout() will set
+ * NG_HCI_UNIT_COMMAND_PENDING flag
+ */
+
+ ng_hci_command_timeout(unit);
+
+ return (0);
+} /* ng_hci_send_command */
+
+/*
+ * Process HCI Command_Compete event. Complete HCI command, and do post
+ * processing on the command parameters (cp) and command return parameters
+ * (e) if required (for example adjust state).
+ */
+
+int
+ng_hci_process_command_complete(ng_hci_unit_p unit, struct mbuf *e)
+{
+ ng_hci_command_compl_ep *ep = NULL;
+ struct mbuf *cp = NULL;
+ int error = 0;
+
+ /* Get event packet and update command buffer info */
+ NG_HCI_M_PULLUP(e, sizeof(*ep));
+ if (e == NULL)
+ return (ENOBUFS); /* XXX this is bad */
+
+ ep = mtod(e, ng_hci_command_compl_ep *);
+ NG_HCI_BUFF_CMD_SET(unit->buffer, ep->num_cmd_pkts);
+
+ /* Check for special NOOP command */
+ if (ep->opcode == 0x0000) {
+ NG_FREE_M(e);
+ goto out;
+ }
+
+ /* Try to match first command item in the queue */
+ error = complete_command(unit, ep->opcode, &cp);
+ if (error != 0) {
+ NG_FREE_M(e);
+ goto out;
+ }
+
+ /*
+ * Perform post processing on command parameters and return parameters
+ * do it only if status is OK (status == 0). Status is the first byte
+ * of any command return parameters.
+ */
+
+ ep->opcode = le16toh(ep->opcode);
+ m_adj(e, sizeof(*ep));
+
+ if (*mtod(e, u_int8_t *) == 0) { /* XXX m_pullup here? */
+ switch (NG_HCI_OGF(ep->opcode)) {
+ case NG_HCI_OGF_LINK_CONTROL:
+ error = process_link_control_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_LINK_POLICY:
+ error = process_link_policy_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_HC_BASEBAND:
+ error = process_hc_baseband_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_INFO:
+ error = process_info_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_STATUS:
+ error = process_status_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_TESTING:
+ error = process_testing_params(unit,
+ NG_HCI_OCF(ep->opcode), cp, e);
+ break;
+
+ case NG_HCI_OGF_BT_LOGO:
+ case NG_HCI_OGF_VENDOR:
+ NG_FREE_M(cp);
+ NG_FREE_M(e);
+ break;
+
+ default:
+ NG_FREE_M(cp);
+ NG_FREE_M(e);
+ error = EINVAL;
+ break;
+ }
+ } else {
+ NG_HCI_ERR(
+"%s: %s - HCI command failed, OGF=%#x, OCF=%#x, status=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NG_HCI_OGF(ep->opcode), NG_HCI_OCF(ep->opcode),
+ *mtod(e, u_int8_t *));
+
+ NG_FREE_M(cp);
+ NG_FREE_M(e);
+ }
+out:
+ ng_hci_send_command(unit);
+
+ return (error);
+} /* ng_hci_process_command_complete */
+
+/*
+ * Process HCI Command_Status event. Check the status (mst) and do post
+ * processing (if required).
+ */
+
+int
+ng_hci_process_command_status(ng_hci_unit_p unit, struct mbuf *e)
+{
+ ng_hci_command_status_ep *ep = NULL;
+ struct mbuf *cp = NULL;
+ int error = 0;
+
+ /* Update command buffer info */
+ NG_HCI_M_PULLUP(e, sizeof(*ep));
+ if (e == NULL)
+ return (ENOBUFS); /* XXX this is bad */
+
+ ep = mtod(e, ng_hci_command_status_ep *);
+ NG_HCI_BUFF_CMD_SET(unit->buffer, ep->num_cmd_pkts);
+
+ /* Check for special NOOP command */
+ if (ep->opcode == 0x0000)
+ goto out;
+
+ /* Try to match first command item in the queue */
+ error = complete_command(unit, ep->opcode, &cp);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Perform post processing on HCI Command_Status event
+ */
+
+ ep->opcode = le16toh(ep->opcode);
+
+ switch (NG_HCI_OGF(ep->opcode)) {
+ case NG_HCI_OGF_LINK_CONTROL:
+ error = process_link_control_status(unit, ep, cp);
+ break;
+
+ case NG_HCI_OGF_LINK_POLICY:
+ error = process_link_policy_status(unit, ep, cp);
+ break;
+
+ case NG_HCI_OGF_BT_LOGO:
+ case NG_HCI_OGF_VENDOR:
+ NG_FREE_M(cp);
+ break;
+
+ case NG_HCI_OGF_HC_BASEBAND:
+ case NG_HCI_OGF_INFO:
+ case NG_HCI_OGF_STATUS:
+ case NG_HCI_OGF_TESTING:
+ default:
+ NG_FREE_M(cp);
+ error = EINVAL;
+ break;
+ }
+out:
+ NG_FREE_M(e);
+ ng_hci_send_command(unit);
+
+ return (error);
+} /* ng_hci_process_command_status */
+
+/*
+ * Complete queued HCI command.
+ */
+
+static int
+complete_command(ng_hci_unit_p unit, int opcode, struct mbuf **cp)
+{
+ struct mbuf *m = NULL;
+
+ /* Check unit state */
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) {
+ NG_HCI_ALERT(
+"%s: %s - no pending command, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state);
+
+ return (EINVAL);
+ }
+
+ /* Get first command in the queue */
+ m = NG_BT_MBUFQ_FIRST(&unit->cmdq);
+ if (m == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - empty command queue?!\n", __func__, NG_NODE_NAME(unit->node));
+
+ return (EINVAL);
+ }
+
+ /*
+ * Match command opcode, if does not match - do nothing and
+ * let timeout handle that.
+ */
+
+ if (mtod(m, ng_hci_cmd_pkt_t *)->opcode != opcode) {
+ NG_HCI_ALERT(
+"%s: %s - command queue is out of sync\n", __func__, NG_NODE_NAME(unit->node));
+
+ return (EINVAL);
+ }
+
+ /*
+ * Now we can remove command timeout, dequeue completed command
+ * and return command parameters. Note: ng_hci_command_untimeout()
+ * will drop NG_HCI_UNIT_COMMAND_PENDING flag.
+ */
+
+ ng_hci_command_untimeout(unit);
+ NG_BT_MBUFQ_DEQUEUE(&unit->cmdq, *cp);
+ m_adj(*cp, sizeof(ng_hci_cmd_pkt_t));
+
+ return (0);
+} /* complete_command */
+
+/*
+ * Process HCI command timeout
+ */
+
+void
+ng_hci_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) arg1;
+ struct mbuf *m = NULL;
+ u_int16_t opcode;
+
+ if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) {
+ NG_BT_MBUFQ_DEQUEUE(&unit->cmdq, m);
+
+ if (m == NULL) {
+ KASSERT(0,
+("%s: %s - command queue is out of sync!\n",
+ __func__, NG_NODE_NAME(unit->node)));
+
+ return;
+ }
+
+ opcode = le16toh(mtod(m, ng_hci_cmd_pkt_t *)->opcode);
+ NG_FREE_M(m);
+
+ NG_HCI_ERR(
+"%s: %s - unable to complete HCI command OGF=%#x, OCF=%#x. Timeout\n",
+ __func__, NG_NODE_NAME(unit->node), NG_HCI_OGF(opcode),
+ NG_HCI_OCF(opcode));
+
+ /*
+ * Try to send more commands
+ */
+
+ NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
+
+ unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
+ ng_hci_send_command(unit);
+ } else
+ KASSERT(0,
+("%s: %s - no pending command, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state));
+} /* ng_hci_process_command_timeout */
+
+/*
+ * Process link command return parameters
+ */
+
+static int
+process_link_control_params(ng_hci_unit_p unit, u_int16_t ocf,
+ struct mbuf *mcp, struct mbuf *mrp)
+{
+ int error = 0;
+
+ switch (ocf) {
+ case NG_HCI_OCF_INQUIRY_CANCEL:
+ case NG_HCI_OCF_PERIODIC_INQUIRY:
+ case NG_HCI_OCF_EXIT_PERIODIC_INQUIRY:
+ case NG_HCI_OCF_LINK_KEY_REP:
+ case NG_HCI_OCF_LINK_KEY_NEG_REP:
+ case NG_HCI_OCF_PIN_CODE_REP:
+ case NG_HCI_OCF_PIN_CODE_NEG_REP:
+ /* These do not need post processing */
+ break;
+
+ case NG_HCI_OCF_INQUIRY:
+ case NG_HCI_OCF_CREATE_CON:
+ case NG_HCI_OCF_DISCON:
+ case NG_HCI_OCF_ADD_SCO_CON:
+ case NG_HCI_OCF_ACCEPT_CON:
+ case NG_HCI_OCF_REJECT_CON:
+ case NG_HCI_OCF_CHANGE_CON_PKT_TYPE:
+ case NG_HCI_OCF_AUTH_REQ:
+ case NG_HCI_OCF_SET_CON_ENCRYPTION:
+ case NG_HCI_OCF_CHANGE_CON_LINK_KEY:
+ case NG_HCI_OCF_MASTER_LINK_KEY:
+ case NG_HCI_OCF_REMOTE_NAME_REQ:
+ case NG_HCI_OCF_READ_REMOTE_FEATURES:
+ case NG_HCI_OCF_READ_REMOTE_VER_INFO:
+ case NG_HCI_OCF_READ_CLOCK_OFFSET:
+ default:
+
+ /*
+ * None of these command was supposed to generate
+ * Command_Complete event. Instead Command_Status event
+ * should have been generated and then appropriate event
+ * should have been sent to indicate the final result.
+ */
+
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_link_control_params */
+
+/*
+ * Process link policy command return parameters
+ */
+
+static int
+process_link_policy_params(ng_hci_unit_p unit, u_int16_t ocf,
+ struct mbuf *mcp, struct mbuf *mrp)
+{
+ int error = 0;
+
+ switch (ocf){
+ case NG_HCI_OCF_ROLE_DISCOVERY: {
+ ng_hci_role_discovery_rp *rp = NULL;
+ ng_hci_unit_con_t *con = NULL;
+ u_int16_t h;
+
+ NG_HCI_M_PULLUP(mrp, sizeof(*rp));
+ if (mrp != NULL) {
+ rp = mtod(mrp, ng_hci_role_discovery_rp *);
+
+ h = NG_HCI_CON_HANDLE(le16toh(rp->con_handle));
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ } else if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con->link_type);
+ error = EINVAL;
+ } else
+ con->role = rp->role;
+ } else
+ error = ENOBUFS;
+ } break;
+
+ case NG_HCI_OCF_READ_LINK_POLICY_SETTINGS:
+ case NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS:
+ /* These do not need post processing */
+ break;
+
+ case NG_HCI_OCF_HOLD_MODE:
+ case NG_HCI_OCF_SNIFF_MODE:
+ case NG_HCI_OCF_EXIT_SNIFF_MODE:
+ case NG_HCI_OCF_PARK_MODE:
+ case NG_HCI_OCF_EXIT_PARK_MODE:
+ case NG_HCI_OCF_QOS_SETUP:
+ case NG_HCI_OCF_SWITCH_ROLE:
+ default:
+
+ /*
+ * None of these command was supposed to generate
+ * Command_Complete event. Instead Command_Status event
+ * should have been generated and then appropriate event
+ * should have been sent to indicate the final result.
+ */
+
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_link_policy_params */
+
+/*
+ * Process HC and baseband command return parameters
+ */
+
+int
+process_hc_baseband_params(ng_hci_unit_p unit, u_int16_t ocf,
+ struct mbuf *mcp, struct mbuf *mrp)
+{
+ int error = 0;
+
+ switch (ocf) {
+ case NG_HCI_OCF_SET_EVENT_MASK:
+ case NG_HCI_OCF_SET_EVENT_FILTER:
+ case NG_HCI_OCF_FLUSH: /* XXX Do we need to handle that? */
+ case NG_HCI_OCF_READ_PIN_TYPE:
+ case NG_HCI_OCF_WRITE_PIN_TYPE:
+ case NG_HCI_OCF_CREATE_NEW_UNIT_KEY:
+ case NG_HCI_OCF_WRITE_STORED_LINK_KEY:
+ case NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO:
+ case NG_HCI_OCF_WRITE_PAGE_TIMO:
+ case NG_HCI_OCF_READ_SCAN_ENABLE:
+ case NG_HCI_OCF_WRITE_SCAN_ENABLE:
+ case NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY:
+ case NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY:
+ case NG_HCI_OCF_READ_AUTH_ENABLE:
+ case NG_HCI_OCF_WRITE_AUTH_ENABLE:
+ case NG_HCI_OCF_READ_ENCRYPTION_MODE:
+ case NG_HCI_OCF_WRITE_ENCRYPTION_MODE:
+ case NG_HCI_OCF_WRITE_VOICE_SETTINGS:
+ case NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS:
+ case NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS:
+ case NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY:
+ case NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY:
+ case NG_HCI_OCF_READ_SCO_FLOW_CONTROL:
+ case NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL:
+ case NG_HCI_OCF_H2HC_FLOW_CONTROL: /* XXX Not supported this time */
+ case NG_HCI_OCF_HOST_BUFFER_SIZE:
+ case NG_HCI_OCF_READ_IAC_LAP:
+ case NG_HCI_OCF_WRITE_IAC_LAP:
+ case NG_HCI_OCF_READ_PAGE_SCAN_PERIOD:
+ case NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD:
+ case NG_HCI_OCF_READ_PAGE_SCAN:
+ case NG_HCI_OCF_WRITE_PAGE_SCAN:
+ case NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO:
+ case NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO:
+ case NG_HCI_OCF_READ_SUPPORTED_IAC_NUM:
+ case NG_HCI_OCF_READ_STORED_LINK_KEY:
+ case NG_HCI_OCF_DELETE_STORED_LINK_KEY:
+ case NG_HCI_OCF_READ_CON_ACCEPT_TIMO:
+ case NG_HCI_OCF_READ_PAGE_TIMO:
+ case NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY:
+ case NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY:
+ case NG_HCI_OCF_READ_VOICE_SETTINGS:
+ case NG_HCI_OCF_READ_AUTO_FLUSH_TIMO:
+ case NG_HCI_OCF_WRITE_AUTO_FLUSH_TIMO:
+ case NG_HCI_OCF_READ_XMIT_LEVEL:
+ case NG_HCI_OCF_HOST_NUM_COMPL_PKTS: /* XXX Can get here? */
+ case NG_HCI_OCF_CHANGE_LOCAL_NAME:
+ case NG_HCI_OCF_READ_LOCAL_NAME:
+ case NG_HCI_OCF_READ_UNIT_CLASS:
+ case NG_HCI_OCF_WRITE_UNIT_CLASS:
+ /* These do not need post processing */
+ break;
+
+ case NG_HCI_OCF_RESET: {
+ ng_hci_unit_con_p con = NULL;
+ int size;
+
+ /*
+ * XXX
+ *
+ * After RESET command unit goes into standby mode
+ * and all operational state is lost. Host controller
+ * will revert to default values for all parameters.
+ *
+ * For now we shall terminate all connections and drop
+ * inited bit. After RESET unit must be re-initialized.
+ */
+
+ while (!LIST_EMPTY(&unit->con_list)) {
+ con = LIST_FIRST(&unit->con_list);
+
+ /* Connection terminated by local host */
+ ng_hci_lp_discon_ind(con, 0x16);
+ ng_hci_free_con(con);
+ }
+
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
+ NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
+
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
+ NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
+
+ unit->state &= ~NG_HCI_UNIT_INITED;
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_hc_baseband_params */
+
+/*
+ * Process info command return parameters
+ */
+
+static int
+process_info_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
+ struct mbuf *mrp)
+{
+ int error = 0, len;
+
+ switch (ocf) {
+ case NG_HCI_OCF_READ_LOCAL_VER:
+ case NG_HCI_OCF_READ_COUNTRY_CODE:
+ break;
+
+ case NG_HCI_OCF_READ_LOCAL_FEATURES:
+ m_adj(mrp, sizeof(u_int8_t));
+ len = min(mrp->m_pkthdr.len, sizeof(unit->features));
+ m_copydata(mrp, 0, len, (caddr_t) unit->features);
+ break;
+
+ case NG_HCI_OCF_READ_BUFFER_SIZE: {
+ ng_hci_read_buffer_size_rp *rp = NULL;
+
+ /* Do not update buffer descriptor if node was initialized */
+ if ((unit->state & NG_HCI_UNIT_READY) == NG_HCI_UNIT_READY)
+ break;
+
+ NG_HCI_M_PULLUP(mrp, sizeof(*rp));
+ if (mrp != NULL) {
+ rp = mtod(mrp, ng_hci_read_buffer_size_rp *);
+
+ NG_HCI_BUFF_ACL_SET(
+ unit->buffer,
+ le16toh(rp->num_acl_pkt), /* number */
+ le16toh(rp->max_acl_size), /* size */
+ le16toh(rp->num_acl_pkt) /* free */
+ );
+
+ NG_HCI_BUFF_SCO_SET(
+ unit->buffer,
+ le16toh(rp->num_sco_pkt), /* number */
+ rp->max_sco_size, /* size */
+ le16toh(rp->num_sco_pkt) /* free */
+ );
+
+ /* Let upper layers know */
+ ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
+ ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
+ } else
+ error = ENOBUFS;
+ } break;
+
+ case NG_HCI_OCF_READ_BDADDR:
+ /* Do not update BD_ADDR if node was initialized */
+ if ((unit->state & NG_HCI_UNIT_READY) == NG_HCI_UNIT_READY)
+ break;
+
+ m_adj(mrp, sizeof(u_int8_t));
+ len = min(mrp->m_pkthdr.len, sizeof(unit->bdaddr));
+ m_copydata(mrp, 0, len, (caddr_t) &unit->bdaddr);
+
+ /* Let upper layers know */
+ ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
+ ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_info_params */
+
+/*
+ * Process status command return parameters
+ */
+
+static int
+process_status_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
+ struct mbuf *mrp)
+{
+ int error = 0;
+
+ switch (ocf) {
+ case NG_HCI_OCF_READ_FAILED_CONTACT_CNTR:
+ case NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR:
+ case NG_HCI_OCF_GET_LINK_QUALITY:
+ case NG_HCI_OCF_READ_RSSI:
+ /* These do not need post processing */
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_status_params */
+
+/*
+ * Process testing command return parameters
+ */
+
+int
+process_testing_params(ng_hci_unit_p unit, u_int16_t ocf, struct mbuf *mcp,
+ struct mbuf *mrp)
+{
+ int error = 0;
+
+ switch (ocf) {
+
+ /*
+ * XXX FIXME
+ * We do not support these features at this time. However,
+ * HCI node could support this and do something smart. At least
+ * node can change unit state.
+ */
+
+ case NG_HCI_OCF_READ_LOOPBACK_MODE:
+ case NG_HCI_OCF_WRITE_LOOPBACK_MODE:
+ case NG_HCI_OCF_ENABLE_UNIT_UNDER_TEST:
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+ NG_FREE_M(mrp);
+
+ return (error);
+} /* process_testing_params */
+
+/*
+ * Process link control command status
+ */
+
+static int
+process_link_control_status(ng_hci_unit_p unit, ng_hci_command_status_ep *ep,
+ struct mbuf *mcp)
+{
+ int error = 0;
+
+ switch (NG_HCI_OCF(ep->opcode)) {
+ case NG_HCI_OCF_INQUIRY:
+ case NG_HCI_OCF_DISCON: /* XXX */
+ case NG_HCI_OCF_REJECT_CON: /* XXX */
+ case NG_HCI_OCF_CHANGE_CON_PKT_TYPE:
+ case NG_HCI_OCF_AUTH_REQ:
+ case NG_HCI_OCF_SET_CON_ENCRYPTION:
+ case NG_HCI_OCF_CHANGE_CON_LINK_KEY:
+ case NG_HCI_OCF_MASTER_LINK_KEY:
+ case NG_HCI_OCF_REMOTE_NAME_REQ:
+ case NG_HCI_OCF_READ_REMOTE_FEATURES:
+ case NG_HCI_OCF_READ_REMOTE_VER_INFO:
+ case NG_HCI_OCF_READ_CLOCK_OFFSET:
+ /* These do not need post processing */
+ break;
+
+ case NG_HCI_OCF_CREATE_CON:
+ break;
+
+ case NG_HCI_OCF_ADD_SCO_CON:
+ break;
+
+ case NG_HCI_OCF_ACCEPT_CON:
+ break;
+
+ case NG_HCI_OCF_INQUIRY_CANCEL:
+ case NG_HCI_OCF_PERIODIC_INQUIRY:
+ case NG_HCI_OCF_EXIT_PERIODIC_INQUIRY:
+ case NG_HCI_OCF_LINK_KEY_REP:
+ case NG_HCI_OCF_LINK_KEY_NEG_REP:
+ case NG_HCI_OCF_PIN_CODE_REP:
+ case NG_HCI_OCF_PIN_CODE_NEG_REP:
+ default:
+
+ /*
+ * None of these command was supposed to generate
+ * Command_Status event. Instead Command_Complete event
+ * should have been sent.
+ */
+
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+
+ return (error);
+} /* process_link_control_status */
+
+/*
+ * Process link policy command status
+ */
+
+static int
+process_link_policy_status(ng_hci_unit_p unit, ng_hci_command_status_ep *ep,
+ struct mbuf *mcp)
+{
+ int error = 0;
+
+ switch (NG_HCI_OCF(ep->opcode)) {
+ case NG_HCI_OCF_HOLD_MODE:
+ case NG_HCI_OCF_SNIFF_MODE:
+ case NG_HCI_OCF_EXIT_SNIFF_MODE:
+ case NG_HCI_OCF_PARK_MODE:
+ case NG_HCI_OCF_EXIT_PARK_MODE:
+ case NG_HCI_OCF_SWITCH_ROLE:
+ /* These do not need post processing */
+ break;
+
+ case NG_HCI_OCF_QOS_SETUP:
+ break;
+
+ case NG_HCI_OCF_ROLE_DISCOVERY:
+ case NG_HCI_OCF_READ_LINK_POLICY_SETTINGS:
+ case NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS:
+ default:
+
+ /*
+ * None of these command was supposed to generate
+ * Command_Status event. Instead Command_Complete event
+ * should have been sent.
+ */
+
+ error = EINVAL;
+ break;
+ }
+
+ NG_FREE_M(mcp);
+
+ return (error);
+} /* process_link_policy_status */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_cmds.h b/sys/netgraph/bluetooth/hci/ng_hci_cmds.h
new file mode 100644
index 0000000..1123802
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_cmds.h
@@ -0,0 +1,45 @@
+/*
+ * ng_hci_cmds.h
+ *
+ * Copyright (c) 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_hci_cmds.h,v 1.6 2002/09/04 21:36:51 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_HCI_CMDS_H_
+#define _NETGRAPH_HCI_CMDS_H_ 1
+
+/*
+ * HCI command return parameters processing routines
+ */
+
+int ng_hci_send_command (ng_hci_unit_p);
+int ng_hci_process_command_complete (ng_hci_unit_p, struct mbuf *);
+int ng_hci_process_command_status (ng_hci_unit_p, struct mbuf *);
+void ng_hci_process_command_timeout (node_p, hook_p, void *, int);
+
+#endif /* ndef _NETGRAPH_HCI_CMDS_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_evnt.c b/sys/netgraph/bluetooth/hci/ng_hci_evnt.c
new file mode 100644
index 0000000..9d81380
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_evnt.c
@@ -0,0 +1,1085 @@
+/*
+ * ng_hci_evnt.c
+ *
+ * Copyright (c) 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_hci_evnt.c,v 1.18 2002/11/12 22:35:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_hci_var.h"
+#include "ng_hci_cmds.h"
+#include "ng_hci_evnt.h"
+#include "ng_hci_ulpi.h"
+#include "ng_hci_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** HCI event processing module
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Event processing routines
+ */
+
+static int inquiry_result (ng_hci_unit_p, struct mbuf *);
+static int con_compl (ng_hci_unit_p, struct mbuf *);
+static int con_req (ng_hci_unit_p, struct mbuf *);
+static int discon_compl (ng_hci_unit_p, struct mbuf *);
+static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
+static int qos_setup_compl (ng_hci_unit_p, struct mbuf *);
+static int hardware_error (ng_hci_unit_p, struct mbuf *);
+static int role_change (ng_hci_unit_p, struct mbuf *);
+static int num_compl_pkts (ng_hci_unit_p, struct mbuf *);
+static int mode_change (ng_hci_unit_p, struct mbuf *);
+static int data_buffer_overflow (ng_hci_unit_p, struct mbuf *);
+static int read_clock_offset_compl (ng_hci_unit_p, struct mbuf *);
+static int qos_violation (ng_hci_unit_p, struct mbuf *);
+static int page_scan_mode_change (ng_hci_unit_p, struct mbuf *);
+static int page_scan_rep_mode_change (ng_hci_unit_p, struct mbuf *);
+static int sync_con_queue (ng_hci_unit_p, ng_hci_unit_con_p, int);
+static int send_data_packets (ng_hci_unit_p, int, int);
+
+/*
+ * Process HCI event packet
+ */
+
+int
+ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_event_pkt_t *hdr = NULL;
+ int error = 0;
+
+ /* Get event packet header */
+ NG_HCI_M_PULLUP(event, sizeof(*hdr));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ hdr = mtod(event, ng_hci_event_pkt_t *);
+
+ NG_HCI_INFO(
+"%s: %s - got HCI event=%#x, length=%d\n",
+ __func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length);
+
+ /* Get rid of event header and process event */
+ m_adj(event, sizeof(*hdr));
+
+ switch (hdr->event) {
+ case NG_HCI_EVENT_INQUIRY_COMPL:
+/* case NG_HCI_EVENT_MODE_CHANGE: */
+ case NG_HCI_EVENT_RETURN_LINK_KEYS:
+ case NG_HCI_EVENT_PIN_CODE_REQ:
+ case NG_HCI_EVENT_LINK_KEY_REQ:
+ case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
+ case NG_HCI_EVENT_LOOPBACK_COMMAND:
+ case NG_HCI_EVENT_AUTH_COMPL:
+ case NG_HCI_EVENT_ENCRYPTION_CHANGE:
+ case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:
+ case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL:
+ case NG_HCI_EVENT_FLUSH_OCCUR: /* XXX Do we have to handle it? */
+/* case NG_HCI_EVENT_ROLE_CHANGE: */
+ case NG_HCI_EVENT_MAX_SLOT_CHANGE:
+ case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED:
+ case NG_HCI_EVENT_BT_LOGO:
+ case NG_HCI_EVENT_VENDOR:
+ case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL:
+ case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:
+ /* These do not need post processing */
+ NG_FREE_M(event);
+ break;
+
+ case NG_HCI_EVENT_INQUIRY_RESULT:
+ error = inquiry_result(unit, event);
+ break;
+
+ case NG_HCI_EVENT_CON_COMPL:
+ error = con_compl(unit, event);
+ break;
+
+ case NG_HCI_EVENT_CON_REQ:
+ error = con_req(unit, event);
+ break;
+
+ case NG_HCI_EVENT_DISCON_COMPL:
+ error = discon_compl(unit, event);
+ break;
+
+ case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
+ error = read_remote_features_compl(unit, event);
+ break;
+
+ case NG_HCI_EVENT_QOS_SETUP_COMPL:
+ error = qos_setup_compl(unit, event);
+ break;
+
+ case NG_HCI_EVENT_COMMAND_COMPL:
+ error = ng_hci_process_command_complete(unit, event);
+ break;
+
+ case NG_HCI_EVENT_COMMAND_STATUS:
+ error = ng_hci_process_command_status(unit, event);
+ break;
+
+ case NG_HCI_EVENT_HARDWARE_ERROR:
+ error = hardware_error(unit, event);
+ break;
+
+ case NG_HCI_EVENT_ROLE_CHANGE:
+ error = role_change(unit, event);
+ break;
+
+ case NG_HCI_EVENT_NUM_COMPL_PKTS:
+ error = num_compl_pkts(unit, event);
+ break;
+
+ case NG_HCI_EVENT_MODE_CHANGE:
+ error = mode_change(unit, event);
+ break;
+
+ case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:
+ error = data_buffer_overflow(unit, event);
+ break;
+
+ case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
+ error = read_clock_offset_compl(unit, event);
+ break;
+
+ case NG_HCI_EVENT_QOS_VIOLATION:
+ error = qos_violation(unit, event);
+ break;
+
+ case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
+ error = page_scan_mode_change(unit, event);
+ break;
+
+ case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
+ error = page_scan_rep_mode_change(unit, event);
+ break;
+
+ default:
+ NG_FREE_M(event);
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ng_hci_process_event */
+
+/*
+ * Send ACL and/or SCO data to the unit driver
+ */
+
+void
+ng_hci_send_data(ng_hci_unit_p unit)
+{
+ int count;
+
+ /* Send ACL data */
+ NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);
+
+ NG_HCI_INFO(
+"%s: %s - sending ACL data packets, count=%d\n",
+ __func__, NG_NODE_NAME(unit->node), count);
+
+ if (count > 0) {
+ count = send_data_packets(unit, NG_HCI_LINK_ACL, count);
+ NG_HCI_STAT_ACL_SENT(unit->stat, count);
+ NG_HCI_BUFF_ACL_USE(unit->buffer, count);
+ }
+
+ /* Send SCO data */
+ NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);
+
+ NG_HCI_INFO(
+"%s: %s - sending SCO data packets, count=%d\n",
+ __func__, NG_NODE_NAME(unit->node), count);
+
+ if (count > 0) {
+ count = send_data_packets(unit, NG_HCI_LINK_SCO, count);
+ NG_HCI_STAT_SCO_SENT(unit->stat, count);
+ NG_HCI_BUFF_SCO_USE(unit->buffer, count);
+ }
+} /* ng_hci_send_data */
+
+/*
+ * Send data packets to the lower layer.
+ */
+
+static int
+send_data_packets(ng_hci_unit_p unit, int link_type, int limit)
+{
+ ng_hci_unit_con_p con = NULL, winner = NULL;
+ item_p item = NULL;
+ int min_pending, total_sent, sent, error, v;
+
+ for (total_sent = 0; limit > 0; ) {
+ min_pending = 0x0fffffff;
+ winner = NULL;
+
+ /*
+ * Find the connection that has has data to send
+ * and the smallest number of pending packets
+ */
+
+ LIST_FOREACH(con, &unit->con_list, next) {
+ if (con->link_type != link_type)
+ continue;
+ if (NG_BT_ITEMQ_LEN(&con->conq) == 0)
+ continue;
+
+ if (con->pending < min_pending) {
+ winner = con;
+ min_pending = con->pending;
+ }
+ }
+
+ if (winner == NULL)
+ break;
+
+ /*
+ * OK, we have a winner now send as much packets as we can
+ * Count the number of packets we have sent and then sync
+ * winner connection queue.
+ */
+
+ for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {
+ NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);
+ if (item == NULL)
+ break;
+
+ NG_HCI_INFO(
+"%s: %s - sending data packet, handle=%d, len=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ winner->con_handle, NGI_M(item)->m_pkthdr.len);
+
+ /* Check if driver hook still there */
+ v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));
+ if (!v || (unit->state & NG_HCI_UNIT_READY) !=
+ NG_HCI_UNIT_READY) {
+ NG_HCI_ERR(
+"%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NG_HCI_HOOK_DRV, ((v)? "" : "not "),
+ unit->state);
+
+ NG_FREE_ITEM(item);
+ error = ENOTCONN;
+ } else {
+ v = NGI_M(item)->m_pkthdr.len;
+
+ /* Give packet to raw hook */
+ ng_hci_mtap(unit, NGI_M(item));
+
+ /* ... and forward item to the driver */
+ NG_FWD_ITEM_HOOK(error, item, unit->drv);
+ }
+
+ if (error != 0) {
+ NG_HCI_ERR(
+"%s: %s - could not send data packet, handle=%d, error=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ winner->con_handle, error);
+ break;
+ }
+
+ winner->pending ++;
+ NG_HCI_STAT_BYTES_SENT(unit->stat, v);
+ }
+
+ /*
+ * Sync connection queue for the winner
+ */
+
+ sync_con_queue(unit, winner, sent);
+ }
+
+ return (total_sent);
+} /* send_data_packets */
+
+/*
+ * Send flow control messages to the upper layer
+ */
+
+static int
+sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)
+{
+ hook_p hook = NULL;
+ struct ng_mesg *msg = NULL;
+ ng_hci_sync_con_queue_ep *state = NULL;
+ int error;
+
+ hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco;
+ if (hook == NULL || NG_HOOK_NOT_VALID(hook))
+ return (ENOTCONN);
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,
+ sizeof(*state), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ state = (ng_hci_sync_con_queue_ep *)(msg->data);
+ state->con_handle = con->con_handle;
+ state->completed = completed;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL);
+
+ return (error);
+} /* sync_con_queue */
+
+/* Inquiry result event */
+static int
+inquiry_result(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_inquiry_result_ep *ep = NULL;
+ ng_hci_neighbor_p n = NULL;
+ bdaddr_t bdaddr;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_inquiry_result_ep *);
+ m_adj(event, sizeof(*ep));
+
+ for (; ep->num_responses > 0; ep->num_responses --) {
+ /* Get remote unit address */
+ m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
+ m_adj(event, sizeof(bdaddr));
+
+ /* Lookup entry in the cache */
+ n = ng_hci_get_neighbor(unit, &bdaddr);
+ if (n == NULL) {
+ /* Create new entry */
+ n = ng_hci_new_neighbor(unit);
+ if (n == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ } else
+ getmicrotime(&n->updated);
+
+ bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
+
+ /* XXX call m_pullup here? */
+
+ n->page_scan_rep_mode = *mtod(event, u_int8_t *);
+ m_adj(event, sizeof(u_int8_t));
+
+ /* page_scan_period_mode */
+ m_adj(event, sizeof(u_int8_t));
+
+ n->page_scan_mode = *mtod(event, u_int8_t *);
+ m_adj(event, sizeof(u_int8_t));
+
+ /* class */
+ m_adj(event, NG_HCI_CLASS_SIZE);
+
+ /* clock offset */
+ m_copydata(event, 0, sizeof(n->clock_offset),
+ (caddr_t) &n->clock_offset);
+ n->clock_offset = le16toh(n->clock_offset);
+ }
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* inquiry_result */
+
+/* Connection complete event */
+static int
+con_compl(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_con_compl_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_con_compl_ep *);
+
+ /*
+ * Find the first connection descriptor that matches the following:
+ *
+ * 1) con->link_type == ep->link_type
+ * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
+ * 3) con->bdaddr == ep->bdaddr
+ */
+
+ LIST_FOREACH(con, &unit->con_list, next)
+ if (con->link_type == ep->link_type &&
+ con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
+ bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ /*
+ * Two possible cases:
+ *
+ * 1) We have found connection descriptor. That means upper layer has
+ * requested this connection via LP_CON_REQ message
+ *
+ * 2) We do not have connection descriptor. That means upper layer
+ * nas not requested this connection or (less likely) we gave up
+ * on this connection (timeout). The most likely scenario is that
+ * we have received Create_Connection/Add_SCO_Connection command
+ * from the RAW hook
+ */
+
+ if (con == NULL) {
+ if (ep->status != 0)
+ goto out;
+
+ con = ng_hci_new_con(unit, ep->link_type);
+ if (con == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
+ } else
+ ng_hci_con_untimeout(con);
+
+ /*
+ * Update connection descriptor and send notification
+ * to the upper layers.
+ */
+
+ con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+ con->encryption_mode = ep->encryption_mode;
+
+ ng_hci_lp_con_cfm(con, ep->status);
+
+ /* Adjust connection state */
+ if (ep->status != 0)
+ ng_hci_free_con(con);
+ else {
+ con->state = NG_HCI_CON_OPEN;
+
+ /*
+ * Change link policy for the ACL connections. Enable all
+ * supported link modes. Enable Role switch as well if
+ * device supports it.
+ */
+
+ if (ep->link_type == NG_HCI_LINK_ACL) {
+ struct __link_policy {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_write_link_policy_settings_cp cp;
+ } __attribute__ ((packed)) *lp;
+ struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ m->m_pkthdr.len = m->m_len = sizeof(*lp);
+ lp = mtod(m, struct __link_policy *);
+
+ lp->hdr.type = NG_HCI_CMD_PKT;
+ lp->hdr.opcode = htole16(NG_HCI_OPCODE(
+ NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
+ lp->hdr.length = sizeof(lp->cp);
+
+ lp->cp.con_handle = ep->con_handle;
+
+ lp->cp.settings = 0;
+ if (unit->features[0] & NG_HCI_LMP_SWITCH)
+ lp->cp.settings |= 0x1;
+ if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
+ lp->cp.settings |= 0x2;
+ if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
+ lp->cp.settings |= 0x4;
+ if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
+ lp->cp.settings |= 0x8;
+
+ lp->cp.settings &= unit->link_policy_mask;
+ lp->cp.settings = htole16(lp->cp.settings);
+
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ ng_hci_send_command(unit);
+ }
+ }
+ }
+out:
+ NG_FREE_M(event);
+
+ return (error);
+} /* com_compl */
+
+/* Connection request event */
+static int
+con_req(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_con_req_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_con_req_ep *);
+
+ /*
+ * Find the first connection descriptor that matches the following:
+ *
+ * 1) con->link_type == ep->link_type
+ *
+ * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ * con->state == NG_HCI_CON_W4_CONN_COMPL
+ *
+ * 3) con->bdaddr == ep->bdaddr
+ *
+ * Possible cases:
+ *
+ * 1) We do not have connection descriptor. This is simple. Create
+ * new fresh connection descriptor and send notification to the
+ * appropriate upstream hook (based on link_type).
+ *
+ * 2) We found connection handle. This is more complicated.
+ *
+ * 2.1) ACL links
+ *
+ * Since only one ACL link can exist between each pair of
+ * units then we have a race. Our upper layer has requested
+ * an ACL connection to the remote unit, but we did not send
+ * command yet. At the same time the remote unit has requested
+ * an ACL connection from us. In this case we will ignore
+ * Connection_Request event. This probably will cause connect
+ * failure on both units.
+ *
+ * 2.2) SCO links
+ *
+ * The spec on page 45 says :
+ *
+ * "The master can support up to three SCO links to the same
+ * slave or to different slaves. A slave can support up to
+ * three SCO links from the same master, or two SCO links if
+ * the links originate from different masters."
+ *
+ * The only problem is how to handle multiple SCO links between
+ * matster and slave. For now we will assume that multiple SCO
+ * links MUST be opened one after another.
+ */
+
+ LIST_FOREACH(con, &unit->con_list, next)
+ if (con->link_type == ep->link_type &&
+ (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
+ bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ if (con == NULL) {
+ con = ng_hci_new_con(unit, ep->link_type);
+ if (con != NULL) {
+ bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
+
+ con->state = NG_HCI_CON_W4_LP_CON_RSP;
+ ng_hci_con_timeout(con);
+
+ error = ng_hci_lp_con_ind(con, ep->uclass);
+ if (error != 0)
+ ng_hci_free_con(con);
+ } else
+ error = ENOMEM;
+ }
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* con_req */
+
+/* Disconnect complete event */
+static int
+discon_compl(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_discon_compl_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ int error = 0;
+ u_int16_t h;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_discon_compl_ep *);
+
+ /*
+ * XXX
+ * Do we have to send notification if ep->status != 0?
+ * For now we will send notification for both ACL and SCO connections
+ * ONLY if ep->status == 0.
+ */
+
+ if (ep->status == 0) {
+ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+ con = ng_hci_con_by_handle(unit, h);
+ if (con != NULL) {
+ error = ng_hci_lp_discon_ind(con, ep->reason);
+ ng_hci_free_con(con);
+ } else {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ }
+ }
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* discon_compl */
+
+/* Read remote feature complete event */
+static int
+read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_read_remote_features_compl_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ ng_hci_neighbor_p n = NULL;
+ u_int16_t h;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
+
+ if (ep->status == 0) {
+ /* Check if we have this connection handle */
+ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Update cache entry */
+ n = ng_hci_get_neighbor(unit, &con->bdaddr);
+ if (n == NULL) {
+ n = ng_hci_new_neighbor(unit);
+ if (n == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
+ } else
+ getmicrotime(&n->updated);
+
+ bcopy(ep->features, n->features, sizeof(n->features));
+ } else
+ NG_HCI_ERR(
+"%s: %s - failed to read remote unit features, status=%d\n",
+ __func__, NG_NODE_NAME(unit->node), ep->status);
+out:
+ NG_FREE_M(event);
+
+ return (error);
+} /* read_remote_features_compl */
+
+/* QoS setup complete event */
+static int
+qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_qos_setup_compl_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ u_int16_t h;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_qos_setup_compl_ep *);
+
+ /* Check if we have this connection handle */
+ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ } else if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid link type=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->link_type, h);
+ error = EINVAL;
+ } else if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection state=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ con->state, h);
+ error = EINVAL;
+ } else /* Notify upper layer */
+ error = ng_hci_lp_qos_cfm(con, ep->status);
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* qos_setup_compl */
+
+/* Hardware error event */
+static int
+hardware_error(ng_hci_unit_p unit, struct mbuf *event)
+{
+ NG_HCI_ALERT(
+"%s: %s - hardware error %#x\n",
+ __func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
+
+ NG_FREE_M(event);
+
+ return (0);
+} /* hardware_error */
+
+/* Role change event */
+static int
+role_change(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_role_change_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_role_change_ep *);
+
+ if (ep->status == 0) {
+ /* XXX shoud we also change "role" for SCO connections? */
+ con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
+ if (con != NULL)
+ con->role = ep->role;
+ else
+ NG_HCI_ALERT(
+"%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ ep->bdaddr.b[5], ep->bdaddr.b[4],
+ ep->bdaddr.b[3], ep->bdaddr.b[2],
+ ep->bdaddr.b[1], ep->bdaddr.b[0]);
+ } else
+ NG_HCI_ERR(
+"%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
+ __func__, NG_NODE_NAME(unit->node), ep->status,
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+
+ NG_FREE_M(event);
+
+ return (0);
+} /* role_change */
+
+/* Number of completed packets event */
+static int
+num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_num_compl_pkts_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ u_int16_t h, p;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_num_compl_pkts_ep *);
+ m_adj(event, sizeof(*ep));
+
+ for (; ep->num_con_handles > 0; ep->num_con_handles --) {
+ /* Get connection handle */
+ m_copydata(event, 0, sizeof(h), (caddr_t) &h);
+ m_adj(event, sizeof(h));
+ h = NG_HCI_CON_HANDLE(le16toh(h));
+
+ /* Get number of completed packets */
+ m_copydata(event, 0, sizeof(p), (caddr_t) &p);
+ m_adj(event, sizeof(p));
+ p = le16toh(p);
+
+ /* Check if we have this connection handle */
+ con = ng_hci_con_by_handle(unit, h);
+ if (con != NULL) {
+ con->pending -= p;
+ if (con->pending < 0) {
+ NG_HCI_WARN(
+"%s: %s - pending packet counter is out of sync! " \
+"handle=%d, pending=%d, ncp=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con->con_handle, con->pending, p);
+
+ con->pending = 0;
+ }
+
+ /* Update buffer descriptor */
+ if (con->link_type == NG_HCI_LINK_ACL)
+ NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
+ else
+ NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
+ } else
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ }
+
+ NG_FREE_M(event);
+
+ /* Send more data */
+ ng_hci_send_data(unit);
+
+ return (0);
+} /* num_compl_pkts */
+
+/* Mode change event */
+static int
+mode_change(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_mode_change_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_mode_change_ep *);
+
+ if (ep->status == 0) {
+ u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ } else if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid link type=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ con->link_type);
+ error = EINVAL;
+ } else
+ con->mode = ep->unit_mode;
+ } else
+ NG_HCI_ERR(
+"%s: %s - failed to change mode, status=%d\n",
+ __func__, NG_NODE_NAME(unit->node), ep->status);
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* mode_change */
+
+/* Data buffer overflow event */
+static int
+data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
+{
+ NG_HCI_ALERT(
+"%s: %s - %s data buffer overflow\n",
+ __func__, NG_NODE_NAME(unit->node),
+ (*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
+
+ NG_FREE_M(event);
+
+ return (0);
+} /* data_buffer_overflow */
+
+/* Read clock offset complete event */
+static int
+read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_read_clock_offset_compl_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ ng_hci_neighbor_p n = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
+
+ if (ep->status == 0) {
+ u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Update cache entry */
+ n = ng_hci_get_neighbor(unit, &con->bdaddr);
+ if (n == NULL) {
+ n = ng_hci_new_neighbor(unit);
+ if (n == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
+ } else
+ getmicrotime(&n->updated);
+
+ n->clock_offset = le16toh(ep->clock_offset);
+ } else
+ NG_HCI_ERR(
+"%s: %s - failed to Read Remote Clock Offset, status=%d\n",
+ __func__, NG_NODE_NAME(unit->node), ep->status);
+out:
+ NG_FREE_M(event);
+
+ return (error);
+} /* read_clock_offset_compl */
+
+/* QoS violation event */
+static int
+qos_violation(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_qos_violation_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ u_int16_t h;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_qos_violation_ep *);
+
+ /* Check if we have this connection handle */
+ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
+ con = ng_hci_con_by_handle(unit, h);
+ if (con == NULL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), h);
+ error = ENOENT;
+ } else if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ALERT(
+"%s: %s - invalid link type=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->link_type);
+ error = EINVAL;
+ } else if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ALERT(
+"%s: %s - invalid connection state=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->state, h);
+ error = EINVAL;
+ } else /* Notify upper layer */
+ error = ng_hci_lp_qos_ind(con);
+
+ NG_FREE_M(event);
+
+ return (error);
+} /* qos_violation */
+
+/* Page scan mode change event */
+static int
+page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_page_scan_mode_change_ep *ep = NULL;
+ ng_hci_neighbor_p n = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
+
+ /* Update cache entry */
+ n = ng_hci_get_neighbor(unit, &ep->bdaddr);
+ if (n == NULL) {
+ n = ng_hci_new_neighbor(unit);
+ if (n == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
+ } else
+ getmicrotime(&n->updated);
+
+ n->page_scan_mode = ep->page_scan_mode;
+out:
+ NG_FREE_M(event);
+
+ return (error);
+} /* page_scan_mode_change */
+
+/* Page scan repetition mode change event */
+static int
+page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
+{
+ ng_hci_page_scan_rep_mode_change_ep *ep = NULL;
+ ng_hci_neighbor_p n = NULL;
+ int error = 0;
+
+ NG_HCI_M_PULLUP(event, sizeof(*ep));
+ if (event == NULL)
+ return (ENOBUFS);
+
+ ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
+
+ /* Update cache entry */
+ n = ng_hci_get_neighbor(unit, &ep->bdaddr);
+ if (n == NULL) {
+ n = ng_hci_new_neighbor(unit);
+ if (n == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
+ } else
+ getmicrotime(&n->updated);
+
+ n->page_scan_rep_mode = ep->page_scan_rep_mode;
+out:
+ NG_FREE_M(event);
+
+ return (error);
+} /* page_scan_rep_mode_change */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_evnt.h b/sys/netgraph/bluetooth/hci/ng_hci_evnt.h
new file mode 100644
index 0000000..706d7b0
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_evnt.h
@@ -0,0 +1,43 @@
+/*
+ * ng_hci_evnt.h
+ *
+ * Copyright (c) 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_hci_evnt.h,v 1.4 2002/09/04 21:36:51 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_HCI_EVNT_H_
+#define _NETGRAPH_HCI_EVNT_H_ 1
+
+/*
+ * HCI events processing routines
+ */
+
+int ng_hci_process_event (ng_hci_unit_p, struct mbuf *);
+void ng_hci_send_data (ng_hci_unit_p);
+
+#endif /* ndef _NETGRAPH_HCI_EVNT_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_main.c b/sys/netgraph/bluetooth/hci/ng_hci_main.c
new file mode 100644
index 0000000..c3c77fc
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_main.c
@@ -0,0 +1,1063 @@
+/*
+ * ng_hci_main.c
+ *
+ * Copyright (c) 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_hci_main.c,v 1.28 2002/11/12 22:35:40 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_hci_var.h"
+#include "ng_hci_prse.h"
+#include "ng_hci_cmds.h"
+#include "ng_hci_evnt.h"
+#include "ng_hci_ulpi.h"
+#include "ng_hci_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** This node implements Bluetooth Host Controller Interface (HCI)
+ ******************************************************************************
+ ******************************************************************************/
+
+/* MALLOC define */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node");
+#else
+#define M_NETGRAPH_HCI M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Netgraph node methods */
+static ng_constructor_t ng_hci_constructor;
+static ng_shutdown_t ng_hci_shutdown;
+static ng_newhook_t ng_hci_newhook;
+static ng_connect_t ng_hci_connect;
+static ng_disconnect_t ng_hci_disconnect;
+static ng_rcvmsg_t ng_hci_default_rcvmsg;
+static ng_rcvmsg_t ng_hci_upper_rcvmsg;
+static ng_rcvdata_t ng_hci_drv_rcvdata;
+static ng_rcvdata_t ng_hci_acl_rcvdata;
+static ng_rcvdata_t ng_hci_sco_rcvdata;
+static ng_rcvdata_t ng_hci_raw_rcvdata;
+
+/* Netgraph node type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_HCI_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_hci_constructor, /* constructor */
+ ng_hci_default_rcvmsg, /* control message */
+ ng_hci_shutdown, /* destructor */
+ ng_hci_newhook, /* new hook */
+ NULL, /* findhook */
+ ng_hci_connect, /* connect hook */
+ ng_hci_drv_rcvdata, /* data */
+ ng_hci_disconnect, /* disconnect hook */
+ ng_hci_cmdlist /* node command list */
+};
+NETGRAPH_INIT(hci, &typestruct);
+MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION,
+ NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph methods implementation
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Create new instance of HCI node (new unit)
+ */
+
+static int
+ng_hci_constructor(node_p node)
+{
+ ng_hci_unit_p unit = NULL;
+
+ MALLOC(unit, ng_hci_unit_p, sizeof(*unit), M_NETGRAPH_HCI,
+ M_NOWAIT | M_ZERO);
+ if (unit == NULL)
+ return (ENOMEM);
+
+ unit->node = node;
+ unit->debug = NG_HCI_WARN_LEVEL;
+
+ unit->link_policy_mask = 0xffff; /* Enable all supported modes */
+ unit->packet_mask = 0xffff; /* Enable all packet types */
+
+ /*
+ * Set default buffer info
+ *
+ * One HCI command
+ * One ACL packet with max. size of 17 bytes (1 DM1 packet)
+ * One SCO packet with max. size of 10 bytes (1 HV1 packet)
+ */
+
+ NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
+ NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1);
+ NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1);
+
+ /* Init command queue & command timeout handler */
+ callout_handle_init(&unit->cmd_timo);
+ NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN);
+
+ /* Init lists */
+ LIST_INIT(&unit->con_list);
+ LIST_INIT(&unit->neighbors);
+
+ /*
+ * This node has to be a WRITER because both data and messages
+ * can change node state.
+ */
+
+ NG_NODE_FORCE_WRITER(node);
+ NG_NODE_SET_PRIVATE(node, unit);
+
+ return (0);
+} /* ng_hci_constructor */
+
+/*
+ * Destroy the node
+ */
+
+static int
+ng_hci_shutdown(node_p node)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
+
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ unit->node = NULL;
+ ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */);
+
+ NG_BT_MBUFQ_DESTROY(&unit->cmdq);
+
+ bzero(unit, sizeof(*unit));
+ FREE(unit, M_NETGRAPH_HCI);
+
+ return (0);
+} /* ng_hci_shutdown */
+
+/*
+ * Give our OK for a hook to be added. Unit driver is connected to the driver
+ * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate
+ * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks.
+ */
+
+static int
+ng_hci_newhook(node_p node, hook_p hook, char const *name)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
+ hook_p *h = NULL;
+
+ if (strcmp(name, NG_HCI_HOOK_DRV) == 0)
+ h = &unit->drv;
+ else if (strcmp(name, NG_HCI_HOOK_ACL) == 0)
+ h = &unit->acl;
+ else if (strcmp(name, NG_HCI_HOOK_SCO) == 0)
+ h = &unit->sco;
+ else if (strcmp(name, NG_HCI_HOOK_RAW) == 0)
+ h = &unit->raw;
+ else
+ return (EINVAL);
+
+ if (*h != NULL)
+ return (EISCONN);
+
+ *h = hook;
+
+ return (0);
+} /* ng_hci_newhook */
+
+/*
+ * Give our final OK to connect hook
+ */
+
+static int
+ng_hci_connect(hook_p hook)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (hook != unit->drv) {
+ if (hook == unit->acl) {
+ NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
+ NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata);
+ } else if (hook == unit->sco) {
+ NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
+ NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata);
+ } else
+ NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata);
+
+ /* Send delayed notification to the upper layers */
+ if (hook != unit->raw)
+ ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0);
+ } else
+ unit->state |= NG_HCI_UNIT_CONNECTED;
+
+ return (0);
+} /* ng_hci_connect */
+
+/*
+ * Disconnect the hook
+ */
+
+static int
+ng_hci_disconnect(hook_p hook)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ if (hook == unit->acl)
+ unit->acl = NULL;
+ else if (hook == unit->sco)
+ unit->sco = NULL;
+ else if (hook == unit->raw)
+ unit->raw = NULL;
+ else if (hook == unit->drv) {
+ unit->drv = NULL;
+
+ /* Connection terminated by local host */
+ ng_hci_unit_clean(unit, 0x16);
+ unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED);
+ } else
+ return (EINVAL);
+
+ /* Shutdown when all hooks are disconnected */
+ if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
+ (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
+ ng_rmnode_self(NG_HOOK_NODE(hook));
+
+ return (0);
+} /* ng_hci_disconnect */
+
+/*
+ * Default control message processing routine. Control message could be:
+ *
+ * 1) GENERIC Netgraph messages
+ *
+ * 2) Control message directed to the node itself.
+ */
+
+static int
+ng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *rsp = NULL;
+ int error = 0;
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_GENERIC_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_TEXT_STATUS: {
+ int cmd_avail,
+ acl_total, acl_avail, acl_size,
+ sco_total, sco_avail, sco_size;
+
+ NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail);
+
+ NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail);
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total);
+ NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size);
+
+ NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail);
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total);
+ NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size);
+
+ snprintf(rsp->data, NG_TEXTRESPONSE,
+ "bdaddr %x:%x:%x:%x:%x:%x\n" \
+ "Hooks %s %s %s %s\n" \
+ "State %#x\n" \
+ "Queue cmd:%d\n" \
+ "Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d",
+ unit->bdaddr.b[5], unit->bdaddr.b[4],
+ unit->bdaddr.b[3], unit->bdaddr.b[2],
+ unit->bdaddr.b[1], unit->bdaddr.b[0],
+ (unit->drv != NULL)? NG_HCI_HOOK_DRV : "",
+ (unit->acl != NULL)? NG_HCI_HOOK_ACL : "",
+ (unit->sco != NULL)? NG_HCI_HOOK_SCO : "",
+ (unit->raw != NULL)? NG_HCI_HOOK_RAW : "",
+ unit->state,
+ NG_BT_MBUFQ_LEN(&unit->cmdq),
+ cmd_avail,
+ acl_avail, acl_total, acl_size,
+ sco_avail, sco_total, sco_size);
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_HCI_COOKIE:
+ switch (msg->header.cmd) {
+ /* Get current node state */
+ case NGM_HCI_NODE_GET_STATE:
+ NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_hci_node_state_ep *)(rsp->data)) = unit->state;
+ break;
+
+ /* Turn INITED bit - node initialized */
+ case NGM_HCI_NODE_INIT:
+ if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY,
+ sizeof(bdaddr_t)) == 0) {
+ error = ENXIO;
+ break;
+ }
+
+ unit->state |= NG_HCI_UNIT_INITED;
+
+ ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
+ ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
+ break;
+
+ /* Get node debug level */
+ case NGM_HCI_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug;
+ break;
+
+ /* Set node debug level */
+ case NGM_HCI_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){
+ error = EMSGSIZE;
+ break;
+ }
+
+ unit->debug = *((ng_hci_node_debug_ep *)(msg->data));
+ break;
+
+ /* Get buffer info */
+ case NGM_HCI_NODE_GET_BUFFER: {
+ ng_hci_node_buffer_ep *ep = NULL;
+
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ ep = (ng_hci_node_buffer_ep *)(rsp->data);
+
+ NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free);
+ NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free);
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts);
+ NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size);
+ NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free);
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts);
+ NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size);
+ } break;
+
+ /* Get BDADDR */
+ case NGM_HCI_NODE_GET_BDADDR:
+ NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t));
+ break;
+
+ /* Get features */
+ case NGM_HCI_NODE_GET_FEATURES:
+ NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ bcopy(&unit->features,rsp->data,sizeof(unit->features));
+ break;
+
+ /* Get stat */
+ case NGM_HCI_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ bcopy(&unit->stat, rsp->data, sizeof(unit->stat));
+ break;
+
+ /* Reset stat */
+ case NGM_HCI_NODE_RESET_STAT:
+ NG_HCI_STAT_RESET(unit->stat);
+ break;
+
+ /* Clean up neighbors list */
+ case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE:
+ ng_hci_flush_neighbor_cache(unit);
+ break;
+
+ /* Get neighbor cache entries */
+ case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: {
+ ng_hci_neighbor_p n = NULL;
+ ng_hci_node_get_neighbor_cache_ep *e1 = NULL;
+ ng_hci_node_neighbor_cache_entry_ep *e2 = NULL;
+ int s = 0;
+
+ /* Look for the fresh entries in the cache */
+ for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
+ ng_hci_neighbor_p nn = LIST_NEXT(n, next);
+
+ if (ng_hci_neighbor_stale(n))
+ ng_hci_free_neighbor(n);
+ else
+ s ++;
+
+ n = nn;
+ }
+ if (s > NG_HCI_MAX_NEIGHBOR_NUM)
+ s = NG_HCI_MAX_NEIGHBOR_NUM;
+
+ /* Prepare response */
+ NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data);
+ e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1);
+
+ e1->num_entries = s;
+
+ LIST_FOREACH(n, &unit->neighbors, next) {
+ e2->page_scan_rep_mode = n->page_scan_rep_mode;
+ e2->page_scan_mode = n->page_scan_mode;
+ e2->clock_offset = n->clock_offset;
+ bcopy(&n->bdaddr, &e2->bdaddr,
+ sizeof(e2->bdaddr));
+ bcopy(&n->features, &e2->features,
+ sizeof(e2->features));
+
+ e2 ++;
+ if (--s <= 0)
+ break;
+ }
+ } break;
+
+ /* Get connection list */
+ case NGM_HCI_NODE_GET_CON_LIST: {
+ ng_hci_unit_con_p c = NULL;
+ ng_hci_node_con_list_ep *e1 = NULL;
+ ng_hci_node_con_ep *e2 = NULL;
+ int s = 0;
+
+ /* Count number of connections in the list */
+ LIST_FOREACH(c, &unit->con_list, next)
+ s ++;
+ if (s > NG_HCI_MAX_CON_NUM)
+ s = NG_HCI_MAX_CON_NUM;
+
+ /* Prepare response */
+ NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ e1 = (ng_hci_node_con_list_ep *)(rsp->data);
+ e2 = (ng_hci_node_con_ep *)(e1 + 1);
+
+ e1->num_connections = s;
+
+ LIST_FOREACH(c, &unit->con_list, next) {
+ e2->link_type = c->link_type;
+ e2->encryption_mode= c->encryption_mode;
+ e2->mode = c->mode;
+ e2->role = c->role;
+
+ e2->state = c->state;
+
+ e2->pending = c->pending;
+ e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq);
+
+ e2->con_handle = c->con_handle;
+ bcopy(&c->bdaddr, &e2->bdaddr,
+ sizeof(e2->bdaddr));
+
+
+ e2 ++;
+ if (--s <= 0)
+ break;
+ }
+ } break;
+
+ /* Get link policy settings mask */
+ case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK:
+ NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_hci_node_link_policy_mask_ep *)(rsp->data)) =
+ unit->link_policy_mask;
+ break;
+
+ /* Set link policy settings mask */
+ case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK:
+ if (msg->header.arglen !=
+ sizeof(ng_hci_node_link_policy_mask_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ unit->link_policy_mask =
+ *((ng_hci_node_link_policy_mask_ep *)
+ (msg->data));
+ break;
+
+ /* Get packet mask */
+ case NGM_HCI_NODE_GET_PACKET_MASK:
+ NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_hci_node_packet_mask_ep *)(rsp->data)) =
+ unit->packet_mask;
+ break;
+
+ /* Set packet mask */
+ case NGM_HCI_NODE_SET_PACKET_MASK:
+ if (msg->header.arglen !=
+ sizeof(ng_hci_node_packet_mask_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ unit->packet_mask =
+ *((ng_hci_node_packet_mask_ep *)(msg->data));
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ /* NG_RESPOND_MSG should take care of "item" and "rsp" */
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_hci_default_rcvmsg */
+
+/*
+ * Process control message from upstream hooks (ACL and SCO).
+ * Handle LP_xxx messages here, give everything else to default routine.
+ */
+
+static int
+ng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
+ int error = 0;
+
+ switch (NGI_MSG(item)->header.typecookie) {
+ case NGM_HCI_COOKIE:
+ switch (NGI_MSG(item)->header.cmd) {
+ case NGM_HCI_LP_CON_REQ:
+ error = ng_hci_lp_con_req(unit, item, lasthook);
+ break;
+
+ case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */
+ error = ng_hci_lp_discon_req(unit, item, lasthook);
+ break;
+
+ case NGM_HCI_LP_CON_RSP:
+ error = ng_hci_lp_con_rsp(unit, item, lasthook);
+ break;
+
+ case NGM_HCI_LP_QOS_REQ:
+ error = ng_hci_lp_qos_req(unit, item, lasthook);
+ break;
+
+ default:
+ error = ng_hci_default_rcvmsg(node, item, lasthook);
+ break;
+ }
+ break;
+
+ default:
+ error = ng_hci_default_rcvmsg(node, item, lasthook);
+ break;
+ }
+
+ return (error);
+} /* ng_hci_upper_rcvmsg */
+
+/*
+ * Process data packet from the driver hook.
+ * We expect HCI events, ACL or SCO data packets.
+ */
+
+static int
+ng_hci_drv_rcvdata(hook_p hook, item_p item)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Process packet */
+ m = NGI_M(item); /* item still has mbuf, just peeking */
+ m->m_flags |= M_PROTO1; /* mark as incoming packet */
+
+ NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len);
+
+ /* Give copy packet to RAW hook */
+ ng_hci_mtap(unit, m);
+
+ /*
+ * XXX XXX XXX
+ * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at
+ * the beginning of the chain. HCI layer WILL NOT call m_pullup() here.
+ */
+
+ switch (*mtod(m, u_int8_t *)) {
+ case NG_HCI_ACL_DATA_PKT:
+ NG_HCI_STAT_ACL_RECV(unit->stat);
+
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
+ unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) {
+ NG_HCI_WARN(
+"%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node),
+ unit->state, unit->acl);
+
+ NG_FREE_ITEM(item);
+ } else
+ NG_FWD_ITEM_HOOK(error, item, unit->acl);
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ NG_HCI_STAT_SCO_RECV(unit->stat);
+
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
+ unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) {
+ NG_HCI_WARN(
+"%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node),
+ unit->state, unit->sco);
+
+ NG_FREE_ITEM(item);
+ } else
+ NG_FWD_ITEM_HOOK(error, item, unit->sco);
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ NG_HCI_STAT_EVNT_RECV(unit->stat);
+
+ /* Detach mbuf, discard item and process event */
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ error = ng_hci_process_event(unit, m);
+ break;
+
+ default:
+ NG_HCI_ALERT(
+"%s: %s - got unknown HCI packet type=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ *mtod(m, u_int8_t *));
+
+ NG_FREE_ITEM(item);
+
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ng_hci_drv_rcvdata */
+
+/*
+ * Process data packet from ACL upstream hook.
+ * We expect valid HCI ACL data packets.
+ */
+
+static int
+ng_hci_acl_rcvdata(hook_p hook, item_p item)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ ng_hci_unit_con_p con = NULL;
+ u_int16_t con_handle;
+ int size, error = 0;
+
+ NG_HCI_BUFF_ACL_SIZE(unit->buffer, size);
+
+ /* Check packet */
+ NGI_GET_M(item, m);
+
+ if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI data packet type=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ *mtod(m, u_int8_t *));
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) ||
+ m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ m->m_pkthdr.len, size);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t));
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ con_handle = NG_HCI_CON_HANDLE(le16toh(
+ mtod(m, ng_hci_acldata_pkt_t *)->con_handle));
+ size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length);
+
+ if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ m->m_pkthdr.len, size);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Queue packet */
+ con = ng_hci_con_by_handle(unit, con_handle);
+ if (con == NULL) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \
+"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);
+
+ error = ENOENT;
+ goto drop;
+ }
+
+ if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \
+"link_type=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con_handle, con->link_type);
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \
+"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con->state, con_handle);
+
+ error = EHOSTDOWN;
+ goto drop;
+ }
+
+ if (NG_BT_ITEMQ_FULL(&con->conq)) {
+ NG_HCI_ALERT(
+"%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con_handle,
+ m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
+
+ NG_BT_ITEMQ_DROP(&con->conq);
+
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ /* Queue item and schedule data transfer */
+ NGI_M(item) = m;
+ NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
+ item = NULL;
+ m = NULL;
+
+ ng_hci_send_data(unit);
+drop:
+ if (item != NULL)
+ NG_FREE_ITEM(item);
+
+ NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
+
+ return (error);
+} /* ng_hci_acl_rcvdata */
+
+/*
+ * Process data packet from SCO upstream hook.
+ * We expect valid HCI SCO data packets
+ */
+
+static int
+ng_hci_sco_rcvdata(hook_p hook, item_p item)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ ng_hci_unit_con_p con = NULL;
+ u_int16_t con_handle;
+ int size, error = 0;
+
+ NG_HCI_BUFF_SCO_SIZE(unit->buffer, size);
+
+ /* Check packet */
+ NGI_GET_M(item, m);
+
+ if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI data packet type=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ *mtod(m, u_int8_t *));
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) ||
+ m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ m->m_pkthdr.len, size);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t));
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ con_handle = NG_HCI_CON_HANDLE(le16toh(
+ mtod(m, ng_hci_scodata_pkt_t *)->con_handle));
+ size = mtod(m, ng_hci_scodata_pkt_t *)->length;
+
+ if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ m->m_pkthdr.len, size);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Queue packet */
+ con = ng_hci_con_by_handle(unit, con_handle);
+ if (con == NULL) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \
+"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);
+
+ error = ENOENT;
+ goto drop;
+ }
+
+ if (con->link_type != NG_HCI_LINK_SCO) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \
+"link_type=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con_handle, con->link_type);
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ERR(
+"%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \
+"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),
+ con->state, con_handle);
+
+ error = EHOSTDOWN;
+ goto drop;
+ }
+
+ if (NG_BT_ITEMQ_FULL(&con->conq)) {
+ NG_HCI_ALERT(
+"%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con_handle,
+ m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
+
+ NG_BT_ITEMQ_DROP(&con->conq);
+
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ /* Queue item and schedule data transfer */
+ NGI_M(item) = m;
+ NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
+ item = NULL;
+ m = NULL;
+
+ ng_hci_send_data(unit);
+drop:
+ if (item != NULL)
+ NG_FREE_ITEM(item);
+
+ NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
+
+ return (error);
+} /* ng_hci_sco_rcvdata */
+
+/*
+ * Process data packet from uptream RAW hook.
+ * We expect valid HCI command packets.
+ */
+
+static int
+ng_hci_raw_rcvdata(hook_p hook, item_p item)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /* Check packet */
+ if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI command packet type=%#x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ *mtod(m, u_int8_t *));
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI command packet len=%d\n",
+ __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t));
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ if (m->m_pkthdr.len !=
+ mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI command packet size, len=%d, length=%d\n",
+ __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
+ mtod(m, ng_hci_cmd_pkt_t *)->length);
+
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) {
+ NG_HCI_ALERT(
+"%s: %s - invalid HCI command opcode\n",
+ __func__, NG_NODE_NAME(unit->node));
+
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (NG_BT_MBUFQ_FULL(&unit->cmdq)) {
+ NG_HCI_ALERT(
+"%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n",
+ __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
+ NG_BT_MBUFQ_LEN(&unit->cmdq));
+
+ NG_BT_MBUFQ_DROP(&unit->cmdq);
+
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ /* Queue and send command */
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ m = NULL;
+
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+drop:
+ NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
+
+ return (error);
+} /* ng_hci_raw_rcvdata */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_misc.c b/sys/netgraph/bluetooth/hci/ng_hci_misc.c
new file mode 100644
index 0000000..ba92f89
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_misc.c
@@ -0,0 +1,556 @@
+/*
+ * ng_hci_misc.c
+ *
+ * Copyright (c) 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_hci_misc.c,v 1.18 2002/10/30 00:18:19 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_hci_var.h"
+#include "ng_hci_cmds.h"
+#include "ng_hci_evnt.h"
+#include "ng_hci_ulpi.h"
+#include "ng_hci_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** Utility routines
+ ******************************************************************************
+ ******************************************************************************/
+
+static void ng_hci_command_queue_timeout (void *);
+static void ng_hci_con_queue_timeout (void *);
+static void ng_hci_con_queue_watchdog_timeout (void *);
+
+/*
+ * Give packet to RAW hook
+ * Assumes input mbuf is read only.
+ */
+
+void
+ng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0)
+{
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) {
+ m = m_dup(m0, M_DONTWAIT);
+ if (m != NULL)
+ NG_SEND_DATA_ONLY(error, unit->raw, m);
+
+ if (error != 0)
+ NG_HCI_INFO(
+"%s: %s - Could not forward packet, error=%d\n",
+ __func__, NG_NODE_NAME(unit->node), error);
+ }
+} /* ng_hci_mtap */
+
+/*
+ * Send notification to the upper layer's
+ */
+
+void
+ng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_hci_unit_p unit = NULL;
+ struct ng_mesg *msg = NULL;
+ ng_hci_node_up_ep *ep = NULL;
+ int error;
+
+ if (node == NULL || NG_NODE_NOT_VALID(node) ||
+ hook == NULL || NG_HOOK_NOT_VALID(hook))
+ return;
+
+ unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY)
+ return;
+
+ if (hook != unit->acl && hook != unit->sco)
+ return;
+
+ NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_NOWAIT);
+ if (msg != NULL) {
+ ep = (ng_hci_node_up_ep *)(msg->data);
+
+ if (hook == unit->acl) {
+ NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size);
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts);
+ } else {
+ NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size);
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts);
+ }
+
+ bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
+
+ NG_SEND_MSG_HOOK(error, node, msg, hook, NULL);
+ } else
+ error = ENOMEM;
+
+ if (error != 0)
+ NG_HCI_INFO(
+"%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NG_HOOK_NAME(hook), error);
+} /* ng_hci_node_is_up */
+
+/*
+ * Clean unit (helper)
+ */
+
+void
+ng_hci_unit_clean(ng_hci_unit_p unit, int reason)
+{
+ int size;
+
+ /* Drain command queue */
+ if (unit->state & NG_HCI_UNIT_COMMAND_PENDING)
+ ng_hci_command_untimeout(unit);
+
+ NG_BT_MBUFQ_DRAIN(&unit->cmdq);
+ NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
+
+ /* Clean up connection list */
+ while (!LIST_EMPTY(&unit->con_list)) {
+ ng_hci_unit_con_p con = LIST_FIRST(&unit->con_list);
+
+ /*
+ * Notify upper layer protocol and destroy connection
+ * descriptor. Do not really care about the result.
+ */
+
+ ng_hci_lp_discon_ind(con, reason);
+ ng_hci_free_con(con);
+ }
+
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size);
+ NG_HCI_BUFF_ACL_FREE(unit->buffer, size);
+
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size);
+ NG_HCI_BUFF_SCO_FREE(unit->buffer, size);
+
+ /* Clean up neighbors list */
+ ng_hci_flush_neighbor_cache(unit);
+} /* ng_hci_unit_clean */
+
+/*
+ * Allocate and link new unit neighbor cache entry
+ */
+
+ng_hci_neighbor_p
+ng_hci_new_neighbor(ng_hci_unit_p unit)
+{
+ ng_hci_neighbor_p n = NULL;
+
+ MALLOC(n, ng_hci_neighbor_p, sizeof(*n), M_NETGRAPH_HCI,
+ M_NOWAIT | M_ZERO);
+ if (n != NULL) {
+ getmicrotime(&n->updated);
+ LIST_INSERT_HEAD(&unit->neighbors, n, next);
+ }
+
+ return (n);
+} /* ng_hci_new_neighbor */
+
+/*
+ * Free unit neighbor cache entry
+ */
+
+void
+ng_hci_free_neighbor(ng_hci_neighbor_p n)
+{
+ LIST_REMOVE(n, next);
+ bzero(n, sizeof(*n));
+ FREE(n, M_NETGRAPH_HCI);
+} /* ng_hci_free_neighbor */
+
+/*
+ * Flush neighbor cache
+ */
+
+void
+ng_hci_flush_neighbor_cache(ng_hci_unit_p unit)
+{
+ while (!LIST_EMPTY(&unit->neighbors))
+ ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors));
+} /* ng_hci_flush_neighbor_cache */
+
+/*
+ * Lookup unit in neighbor cache
+ */
+
+ng_hci_neighbor_p
+ng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr)
+{
+ ng_hci_neighbor_p n = NULL;
+
+ for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
+ ng_hci_neighbor_p nn = LIST_NEXT(n, next);
+
+ if (!ng_hci_neighbor_stale(n)) {
+ if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0)
+ break;
+ } else
+ ng_hci_free_neighbor(n); /* remove old entry */
+
+ n = nn;
+ }
+
+ return (n);
+} /* ng_hci_get_neighbor */
+
+/*
+ * Check if neighbor entry is stale
+ */
+
+int
+ng_hci_neighbor_stale(ng_hci_neighbor_p n)
+{
+ struct timeval now;
+
+ getmicrotime(&now);
+
+ return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age());
+} /* ng_hci_neighbor_stale */
+
+/*
+ * Allocate and link new connection descriptor
+ */
+
+ng_hci_unit_con_p
+ng_hci_new_con(ng_hci_unit_p unit, int link_type)
+{
+ ng_hci_unit_con_p con = NULL;
+ int num_pkts;
+
+ MALLOC(con, ng_hci_unit_con_p, sizeof(*con), M_NETGRAPH_HCI,
+ M_NOWAIT | M_ZERO);
+ if (con != NULL) {
+ con->unit = unit;
+ con->state = NG_HCI_CON_CLOSED;
+ con->link_type = link_type;
+
+ if (con->link_type == NG_HCI_LINK_ACL)
+ NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts);
+ else
+ NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts);
+
+ NG_BT_ITEMQ_INIT(&con->conq, num_pkts);
+
+ callout_handle_init(&con->con_timo);
+ callout_handle_init(&con->watchdog_timo);
+
+ LIST_INSERT_HEAD(&unit->con_list, con, next);
+ }
+
+ return (con);
+} /* ng_hci_new_con */
+
+/*
+ * Free connection descriptor
+ */
+
+void
+ng_hci_free_con(ng_hci_unit_con_p con)
+{
+ LIST_REMOVE(con, next);
+
+ /* Remove all timeouts (if any) */
+ if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
+ ng_hci_con_untimeout(con);
+
+ if (con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING)
+ ng_hci_con_watchdog_untimeout(con);
+
+ /*
+ * If we have pending packets then assume that Host Controller has
+ * flushed these packets and we can free them too
+ */
+
+ if (con->link_type == NG_HCI_LINK_ACL)
+ NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending);
+ else
+ NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending);
+
+ NG_BT_ITEMQ_DESTROY(&con->conq);
+
+ bzero(con, sizeof(*con));
+ FREE(con, M_NETGRAPH_HCI);
+} /* ng_hci_free_con */
+
+/*
+ * Lookup connection for given unit and connection handle.
+ */
+
+ng_hci_unit_con_p
+ng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle)
+{
+ ng_hci_unit_con_p con = NULL;
+
+ LIST_FOREACH(con, &unit->con_list, next)
+ if (con->con_handle == con_handle)
+ break;
+
+ return (con);
+} /* ng_hci_con_by_handle */
+
+/*
+ * Lookup connection for given unit, link type and remove unit address
+ */
+
+ng_hci_unit_con_p
+ng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type)
+{
+ ng_hci_unit_con_p con = NULL;
+
+ LIST_FOREACH(con, &unit->con_list, next)
+ if (con->link_type == link_type &&
+ bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ return (con);
+} /* ng_hci_con_by_bdaddr */
+
+/*
+ * Set HCI command timeout
+ */
+
+void
+ng_hci_command_timeout(ng_hci_unit_p unit)
+{
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) {
+ NG_NODE_REF(unit->node);
+ unit->state |= NG_HCI_UNIT_COMMAND_PENDING;
+ unit->cmd_timo = timeout(ng_hci_command_queue_timeout, unit,
+ bluetooth_hci_command_timeout());
+ } else
+ KASSERT(0,
+("%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node)));
+} /* ng_hci_command_timeout */
+
+/*
+ * Unset HCI command timeout
+ */
+
+void
+ng_hci_command_untimeout(ng_hci_unit_p unit)
+{
+ if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) {
+ unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING;
+ untimeout(ng_hci_command_queue_timeout, unit, unit->cmd_timo);
+ NG_NODE_UNREF(unit->node);
+ } else
+ KASSERT(0,
+("%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node)));
+} /* ng_hci_command_untimeout */
+
+/*
+ * OK timeout has happend, so queue timeout processing function
+ */
+
+static void
+ng_hci_command_queue_timeout(void *context)
+{
+ ng_hci_unit_p unit = (ng_hci_unit_p) context;
+ node_p node = unit->node;
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node,NULL,&ng_hci_process_command_timeout,unit,0);
+
+ NG_NODE_UNREF(node);
+} /* ng_hci_command_queue_timeout */
+
+/*
+ * Set HCI connection timeout
+ */
+
+void
+ng_hci_con_timeout(ng_hci_unit_con_p con)
+{
+ if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
+ NG_NODE_REF(con->unit->node);
+ con->flags |= NG_HCI_CON_TIMEOUT_PENDING;
+ con->con_timo = timeout(ng_hci_con_queue_timeout, con,
+ bluetooth_hci_connect_timeout());
+ } else
+ KASSERT(0,
+("%s: %s - Duplicated connection timeout!\n",
+ __func__, NG_NODE_NAME(con->unit->node)));
+} /* ng_hci_con_timeout */
+
+/*
+ * Unset HCI connection timeout
+ */
+
+void
+ng_hci_con_untimeout(ng_hci_unit_con_p con)
+{
+ if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) {
+ con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
+ untimeout(ng_hci_con_queue_timeout, con, con->con_timo);
+ NG_NODE_UNREF(con->unit->node);
+ } else
+ KASSERT(0,
+("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node)));
+} /* ng_hci_con_untimeout */
+
+/*
+ * OK timeout has happend, so queue timeout processing function
+ */
+
+static void
+ng_hci_con_queue_timeout(void *context)
+{
+ ng_hci_unit_con_p con = (ng_hci_unit_con_p) context;
+ node_p node = con->unit->node;
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node, NULL, &ng_hci_process_con_timeout, con, 0);
+
+ NG_NODE_UNREF(node);
+} /* ng_hci_con_queue_timeout */
+
+/*
+ * Set HCI connection watchdog timeout
+ */
+
+void
+ng_hci_con_watchdog_timeout(ng_hci_unit_con_p con)
+{
+ if (!(con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING)) {
+ NG_NODE_REF(con->unit->node);
+ con->flags |= NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
+ con->watchdog_timo = timeout(ng_hci_con_queue_watchdog_timeout,
+ con, bluetooth_hci_watchdog_timeout());
+ } else
+ KASSERT(0,
+("%s: %s - Duplicated connection watchdog timeout!\n",
+ __func__, NG_NODE_NAME(con->unit->node)));
+} /* ng_hci_con_watchdog_timeout */
+
+/*
+ * Unset HCI connection watchdog timeout
+ */
+
+void
+ng_hci_con_watchdog_untimeout(ng_hci_unit_con_p con)
+{
+ if (con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING) {
+ con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
+ untimeout(ng_hci_con_queue_watchdog_timeout, con,
+ con->watchdog_timo);
+ NG_NODE_UNREF(con->unit->node);
+ } else
+ KASSERT(0,
+("%s: %s - No connection watchdog timeout!\n",
+ __func__, NG_NODE_NAME(con->unit->node)));
+} /* ng_hci_con_watchdog_untimeout */
+
+/*
+ * OK timeout has happend, so queue timeout processing function
+ */
+
+static void
+ng_hci_con_queue_watchdog_timeout(void *context)
+{
+ ng_hci_unit_con_p con = (ng_hci_unit_con_p) context;
+ node_p node = con->unit->node;
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node, NULL, &ng_hci_process_con_watchdog_timeout,
+ con, 0);
+
+ NG_NODE_UNREF(node);
+} /* ng_hci_con_queue_watchdog_timeout */
+
+#if 0
+/*
+ * Convert numeric error code/reason to a string
+ */
+
+char const * const
+ng_hci_str_error(u_int16_t code)
+{
+#define LAST_ERROR_CODE ((sizeof(s)/sizeof(s[0]))-1)
+ static char const * const s[] = {
+ /* 0x00 */ "No error",
+ /* 0x01 */ "Unknown HCI command",
+ /* 0x02 */ "No connection",
+ /* 0x03 */ "Hardware failure",
+ /* 0x04 */ "Page timeout",
+ /* 0x05 */ "Authentication failure",
+ /* 0x06 */ "Key missing",
+ /* 0x07 */ "Memory full",
+ /* 0x08 */ "Connection timeout",
+ /* 0x09 */ "Max number of connections",
+ /* 0x0a */ "Max number of SCO connections to a unit",
+ /* 0x0b */ "ACL connection already exists",
+ /* 0x0c */ "Command disallowed",
+ /* 0x0d */ "Host rejected due to limited resources",
+ /* 0x0e */ "Host rejected due to securiity reasons",
+ /* 0x0f */ "Host rejected due to remote unit is a personal unit",
+ /* 0x10 */ "Host timeout",
+ /* 0x11 */ "Unsupported feature or parameter value",
+ /* 0x12 */ "Invalid HCI command parameter",
+ /* 0x13 */ "Other end terminated connection: User ended connection",
+ /* 0x14 */ "Other end terminated connection: Low resources",
+ /* 0x15 */ "Other end terminated connection: About to power off",
+ /* 0x16 */ "Connection terminated by local host",
+ /* 0x17 */ "Repeated attempts",
+ /* 0x18 */ "Pairing not allowed",
+ /* 0x19 */ "Unknown LMP PDU",
+ /* 0x1a */ "Unsupported remote feature",
+ /* 0x1b */ "SCO offset rejected",
+ /* 0x1c */ "SCO interval rejected",
+ /* 0x1d */ "SCO air mode rejected",
+ /* 0x1e */ "Invalid LMP parameters",
+ /* 0x1f */ "Unspecified error",
+ /* 0x20 */ "Unsupported LMP parameter value",
+ /* 0x21 */ "Role change not allowed",
+ /* 0x22 */ "LMP response timeout",
+ /* 0x23 */ "LMP error transaction collision",
+ /* 0x24 */ "LMP PSU not allowed",
+ /* 0x25 */ "Encryption mode not acceptable",
+ /* 0x26 */ "Unit key used",
+ /* 0x27 */ "QoS is not supported",
+ /* 0x28 */ "Instant passed",
+ /* 0x29 */ "Paring with unit key not supported",
+ /* SHOULD ALWAYS BE LAST */ "Unknown error"
+ };
+
+ return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]);
+} /* ng_hci_str_error */
+#endif
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_misc.h b/sys/netgraph/bluetooth/hci/ng_hci_misc.h
new file mode 100644
index 0000000..862d7db
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_misc.h
@@ -0,0 +1,58 @@
+/*
+ * ng_hci_misc.h
+ *
+ * Copyright (c) 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_hci_misc.h,v 1.8 2002/09/04 21:36:52 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_HCI_MISC_H_
+#define _NETGRAPH_HCI_MISC_H_ 1
+
+void ng_hci_mtap (ng_hci_unit_p, struct mbuf *);
+void ng_hci_node_is_up (node_p, hook_p, void *, int);
+void ng_hci_unit_clean (ng_hci_unit_p, int);
+
+ng_hci_neighbor_p ng_hci_new_neighbor (ng_hci_unit_p);
+void ng_hci_free_neighbor (ng_hci_neighbor_p);
+void ng_hci_flush_neighbor_cache (ng_hci_unit_p);
+ng_hci_neighbor_p ng_hci_get_neighbor (ng_hci_unit_p, bdaddr_p);
+int ng_hci_neighbor_stale (ng_hci_neighbor_p);
+
+ng_hci_unit_con_p ng_hci_new_con (ng_hci_unit_p, int);
+void ng_hci_free_con (ng_hci_unit_con_p);
+ng_hci_unit_con_p ng_hci_con_by_handle (ng_hci_unit_p, int);
+ng_hci_unit_con_p ng_hci_con_by_bdaddr (ng_hci_unit_p, bdaddr_p, int);
+
+void ng_hci_command_timeout (ng_hci_unit_p);
+void ng_hci_command_untimeout (ng_hci_unit_p);
+void ng_hci_con_timeout (ng_hci_unit_con_p);
+void ng_hci_con_untimeout (ng_hci_unit_con_p);
+void ng_hci_con_watchdog_timeout (ng_hci_unit_con_p);
+void ng_hci_con_watchdog_untimeout (ng_hci_unit_con_p);
+
+#endif /* ndef _NETGRAPH_HCI_MISC_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_prse.h b/sys/netgraph/bluetooth/hci/ng_hci_prse.h
new file mode 100644
index 0000000..4c4bd6e
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_prse.h
@@ -0,0 +1,203 @@
+/*
+ * ng_hci_prse.h
+ *
+ * Copyright (c) 2001 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_hci_prse.h,v 1.9 2002/11/12 22:35:40 max Exp $
+ * $FreeBSD$
+ */
+
+/***************************************************************************
+ ***************************************************************************
+ ** ng_parse definitions for the HCI node
+ ***************************************************************************
+ ***************************************************************************/
+
+#ifndef _NETGRAPH_HCI_PRSE_H_
+#define _NETGRAPH_HCI_PRSE_H_ 1
+
+/* BDADDR */
+static const struct ng_parse_fixedarray_info ng_hci_bdaddr_type_info = {
+ &ng_parse_uint8_type,
+ NG_HCI_BDADDR_SIZE
+};
+static const struct ng_parse_type ng_hci_bdaddr_type = {
+ &ng_parse_fixedarray_type,
+ &ng_hci_bdaddr_type_info
+};
+
+/* Features */
+static const struct ng_parse_fixedarray_info ng_hci_features_type_info = {
+ &ng_parse_uint8_type,
+ NG_HCI_FEATURES_SIZE
+};
+static const struct ng_parse_type ng_hci_features_type = {
+ &ng_parse_fixedarray_type,
+ &ng_hci_features_type_info
+};
+
+/* Buffer info */
+static const struct ng_parse_struct_field ng_hci_buffer_type_fields[] =
+{
+ { "cmd_free", &ng_parse_uint8_type, },
+ { "sco_size", &ng_parse_uint8_type, },
+ { "sco_pkts", &ng_parse_uint16_type, },
+ { "sco_free", &ng_parse_uint16_type, },
+ { "acl_size", &ng_parse_uint16_type, },
+ { "acl_pkts", &ng_parse_uint16_type, },
+ { "acl_free", &ng_parse_uint16_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_hci_buffer_type = {
+ &ng_parse_struct_type,
+ &ng_hci_buffer_type_fields
+};
+
+/* Stat info */
+static const struct ng_parse_struct_field ng_hci_stat_type_fields[] =
+{
+ { "cmd_sent", &ng_parse_uint32_type, },
+ { "evnt_recv", &ng_parse_uint32_type, },
+ { "acl_recv", &ng_parse_uint32_type, },
+ { "acl_sent", &ng_parse_uint32_type, },
+ { "sco_recv", &ng_parse_uint32_type, },
+ { "sco_sent", &ng_parse_uint32_type, },
+ { "bytes_recv", &ng_parse_uint32_type, },
+ { "bytes_sent", &ng_parse_uint32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_hci_stat_type = {
+ &ng_parse_struct_type,
+ &ng_hci_stat_type_fields
+};
+
+/*
+ * HCI node command list
+ */
+
+static const struct ng_cmdlist ng_hci_cmdlist[] = {
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_STATE,
+ "get_state",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_INIT,
+ "init",
+ NULL,
+ NULL
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_BUFFER,
+ "get_buff_info",
+ NULL,
+ &ng_hci_buffer_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_BDADDR,
+ "get_bdaddr",
+ NULL,
+ &ng_hci_bdaddr_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_FEATURES,
+ "get_features",
+ NULL,
+ &ng_hci_features_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_hci_stat_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE,
+ "flush_ncache",
+ NULL,
+ NULL
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK,
+ "get_lm_mask",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK,
+ "set_lm_mask",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_PACKET_MASK,
+ "get_pkt_mask",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_HCI_COOKIE,
+ NGM_HCI_NODE_SET_PACKET_MASK,
+ "set_pkt_mask",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ { 0, }
+};
+
+#endif /* ndef _NETGRAPH_HCI_PRSE_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c b/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c
new file mode 100644
index 0000000..97a0ea8
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c
@@ -0,0 +1,1280 @@
+/*
+ * ng_hci_ulpi.c
+ *
+ * Copyright (c) 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_hci_ulpi.c,v 1.14 2002/11/12 22:35:40 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_hci_var.h"
+#include "ng_hci_cmds.h"
+#include "ng_hci_evnt.h"
+#include "ng_hci_ulpi.h"
+#include "ng_hci_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** Upper Layer Protocol Interface module
+ ******************************************************************************
+ ******************************************************************************/
+
+static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
+static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
+
+/*
+ * Process LP_ConnectReq event from the upper layer protocol
+ */
+
+int
+ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
+ NG_HCI_WARN(
+"%s: %s - unit is not ready, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state);
+
+ NG_FREE_ITEM(item);
+
+ return (ENXIO);
+ }
+
+ if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid LP_ConnectReq message size=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NGI_MSG(item)->header.arglen);
+
+ NG_FREE_ITEM(item);
+
+ return (EMSGSIZE);
+ }
+
+ if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
+ return (ng_hci_lp_acl_con_req(unit, item, hook));
+
+ if (hook != unit->sco) {
+ NG_HCI_WARN(
+"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), hook);
+
+ NG_FREE_ITEM(item);
+
+ return (EINVAL);
+ }
+
+ return (ng_hci_lp_sco_con_req(unit, item, hook));
+} /* ng_hci_lp_con_req */
+
+/*
+ * Request to create new ACL connection
+ */
+
+static int
+ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ struct acl_con_req {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_create_con_cp cp;
+ } __attribute__ ((packed)) *req = NULL;
+ ng_hci_lp_con_req_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ ng_hci_neighbor_t *n = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
+
+ /*
+ * Only one ACL connection can exist between each pair of units.
+ * So try to find ACL connection descriptor (in any state) that
+ * has requested remote BD_ADDR.
+ *
+ * Two cases:
+ *
+ * 1) We do not have connection to the remote unit. This is simple.
+ * Just create new connection descriptor and send HCI command to
+ * create new connection.
+ *
+ * 2) We do have connection descriptor. We need to check connection
+ * state:
+ *
+ * 2.1) NG_HCI_CON_CLOSED mean we are in the process of closing
+ * connection to the remote unit. We will reject connection
+ * request until connection is closed.
+ *
+ * 2.2) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
+ * accepting connection from the remote unit. This is a race
+ * condition. We will ignore this message.
+ *
+ * 2.3) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
+ * requested connection or we just accepted it. In any case
+ * all we need to do here is set appropriate notification bit
+ * and wait.
+ *
+ * 2.4) NG_HCI_CON_OPEN means connection is open. Just reply back
+ * and let upper layer know that we have connection already.
+ */
+
+ con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
+ if (con != NULL) {
+ switch (con->state) {
+ case NG_HCI_CON_CLOSED:
+ error = EBUSY;
+ break;
+
+ case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
+ error = EALREADY;
+ break;
+
+ case NG_HCI_CON_W4_CONN_COMPLETE:
+ if (hook == unit->acl)
+ con->flags |= NG_HCI_CON_NOTIFY_ACL;
+ else
+ con->flags |= NG_HCI_CON_NOTIFY_SCO;
+ break;
+
+ case NG_HCI_CON_OPEN: {
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_con_cfm_ep *cfm = NULL;
+
+ if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
+ NGI_GET_MSG(item, msg);
+ NG_FREE_MSG(msg);
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
+ NGM_HCI_LP_CON_CFM, sizeof(*cfm),
+ M_NOWAIT);
+ if (msg != NULL) {
+ cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
+ cfm->status = 0;
+ cfm->link_type = con->link_type;
+ cfm->con_handle = con->con_handle;
+ bcopy(&con->bdaddr, &cfm->bdaddr,
+ sizeof(cfm->bdaddr));
+
+ /*
+ * This will forward item back to
+ * sender and set item to NULL
+ */
+
+ _NGI_MSG(item) = msg;
+ NG_FWD_ITEM_HOOK(error, item, hook);
+ } else
+ error = ENOMEM;
+ } else
+ NG_HCI_INFO(
+"%s: %s - Source hook is not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node),
+ hook);
+ } break;
+
+ default:
+ KASSERT(0,
+("%s: %s - Invalid connection state=%d\n",
+ __func__, NG_NODE_NAME(unit->node),con->state));
+
+ error = EINVAL;
+ break;
+ }
+
+ goto out;
+ }
+
+ /*
+ * If we got here then we need to create new ACL connection descriptor
+ * and submit HCI command. First create new connection desriptor, set
+ * bdaddr and notification flags.
+ */
+
+ con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
+ if (con == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ ng_hci_free_con(con);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ m->m_pkthdr.len = m->m_len = sizeof(*req);
+ req = mtod(m, struct acl_con_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+ req->hdr.length = sizeof(req->cp);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_CREATE_CON));
+
+ bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
+
+ req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
+ if (unit->features[0] & NG_HCI_LMP_3SLOT)
+ req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
+ if (unit->features[0] & NG_HCI_LMP_5SLOT)
+ req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
+
+ req->cp.pkt_type &= unit->packet_mask;
+ if (req->cp.pkt_type == 0)
+ req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
+
+ req->cp.pkt_type = htole16(req->cp.pkt_type);
+
+ if (unit->features[0] & NG_HCI_LMP_SWITCH)
+ req->cp.accept_role_switch = 1;
+ else
+ req->cp.accept_role_switch = 0;
+
+ /*
+ * We may speed up connect by specifying valid parameters.
+ * So check the neighbor cache.
+ */
+
+ n = ng_hci_get_neighbor(unit, &ep->bdaddr);
+ if (n == NULL) {
+ req->cp.page_scan_rep_mode = 0;
+ req->cp.page_scan_mode = 0;
+ req->cp.clock_offset = 0;
+ } else {
+ req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
+ req->cp.page_scan_mode = n->page_scan_mode;
+ req->cp.clock_offset = htole16(n->clock_offset);
+ }
+
+ /*
+ * Adust connection state
+ */
+
+ if (hook == unit->acl)
+ con->flags |= NG_HCI_CON_NOTIFY_ACL;
+ else
+ con->flags |= NG_HCI_CON_NOTIFY_SCO;
+
+ con->state = NG_HCI_CON_W4_CONN_COMPLETE;
+ ng_hci_con_timeout(con);
+
+ /*
+ * Queue and send HCI command
+ */
+
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+out:
+ if (item != NULL)
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_hci_lp_acl_con_req */
+
+/*
+ * Request to create new SCO connection
+ */
+
+static int
+ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ struct sco_con_req {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_add_sco_con_cp cp;
+ } __attribute__ ((packed)) *req = NULL;
+ ng_hci_lp_con_req_ep *ep = NULL;
+ ng_hci_unit_con_p acl_con = NULL, sco_con = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
+
+ /*
+ * SCO connection without ACL link
+ *
+ * If upper layer requests SCO connection and there is no open ACL
+ * connection to the desired remote unit, we will reject the request.
+ */
+
+ LIST_FOREACH(acl_con, &unit->con_list, next)
+ if (acl_con->link_type == NG_HCI_LINK_ACL &&
+ acl_con->state == NG_HCI_CON_OPEN &&
+ bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ if (acl_con == NULL) {
+ NG_HCI_INFO(
+"%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
+ __func__, NG_NODE_NAME(unit->node),
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+
+ error = ENOENT;
+ goto out;
+ }
+
+ /*
+ * Multiple SCO connections can exist between the same pair of units.
+ * We assume that multiple SCO connections have to be opened one after
+ * another.
+ *
+ * Try to find SCO connection descriptor that matches the following:
+ *
+ * 1) sco_con->link_type == NG_HCI_LINK_SCO
+ *
+ * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
+ *
+ * 3) sco_con->bdaddr == ep->bdaddr
+ *
+ * Two cases:
+ *
+ * 1) We do not have connection descriptor. This is simple. Just
+ * create new connection and submit Add_SCO_Connection command.
+ *
+ * 2) We do have connection descriptor. We need to check the state.
+ *
+ * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
+ * connection from the remote unit. This is a race condition and
+ * we will ignore the request.
+ *
+ * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
+ * connection or we just accepted it.
+ *
+ * XXX FIXME what to do with connection(s) in CLOSED state?
+ */
+
+ LIST_FOREACH(sco_con, &unit->con_list, next)
+ if (sco_con->link_type == NG_HCI_LINK_SCO &&
+ (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
+ bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ if (sco_con != NULL) {
+ switch (sco_con->state) {
+ case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
+ error = EALREADY;
+ break;
+
+ case NG_HCI_CON_W4_CONN_COMPLETE:
+ sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
+ break;
+
+ default:
+ KASSERT(0,
+("%s: %s - Inavalid connection state=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ sco_con->state));
+
+ error = EINVAL;
+ break;
+ }
+
+ goto out;
+ }
+
+ /*
+ * If we got here then we need to create new SCO connection descriptor
+ * and submit HCI command.
+ */
+
+ sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
+ if (sco_con == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ ng_hci_free_con(sco_con);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ m->m_pkthdr.len = m->m_len = sizeof(*req);
+ req = mtod(m, struct sco_con_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+ req->hdr.length = sizeof(req->cp);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_ADD_SCO_CON));
+
+ req->cp.con_handle = htole16(acl_con->con_handle);
+
+ req->cp.pkt_type = NG_HCI_PKT_HV1;
+ if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
+ req->cp.pkt_type |= NG_HCI_PKT_HV2;
+ if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
+ req->cp.pkt_type |= NG_HCI_PKT_HV3;
+
+ req->cp.pkt_type &= unit->packet_mask;
+ if (req->cp.pkt_type == 0)
+ req->cp.pkt_type = NG_HCI_PKT_HV1;
+
+ req->cp.pkt_type = htole16(req->cp.pkt_type);
+
+ /*
+ * Adust connection state
+ */
+
+ sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
+
+ sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
+ ng_hci_con_timeout(sco_con);
+
+ /*
+ * Queue and send HCI command
+ */
+
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_hci_lp_sco_con_req */
+
+/*
+ * Process LP_DisconnectReq event from the upper layer protocol
+ *
+ * XXX XXX XXX
+ *
+ * NOTE: This is NOT defined by Bluetooth specification (why?) But i think
+ * this might be useful (at least for testing), so please do not depend on
+ * this interface.
+ */
+
+int
+ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ struct discon_req {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_discon_cp cp;
+ } __attribute__ ((packed)) *req = NULL;
+ ng_hci_lp_discon_req_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Check if unit is ready */
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
+ NG_HCI_WARN(
+"%s: %s - unit is not ready, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state);
+
+ error = ENXIO;
+ goto out;
+ }
+
+ if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid LP_DisconnectReq message size=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NGI_MSG(item)->header.arglen);
+
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
+
+ con = ng_hci_con_by_handle(unit, ep->con_handle);
+ if (con == NULL) {
+ NG_HCI_ERR(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), ep->con_handle);
+
+ error = ENOENT;
+ goto out;
+ }
+
+ if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ERR(
+"%s: %s - invalid connection state=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->state,
+ ep->con_handle);
+
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ m->m_pkthdr.len = m->m_len = sizeof(*req);
+ req = mtod(m, struct discon_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+ req->hdr.length = sizeof(req->cp);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_DISCON));
+
+ req->cp.con_handle = htole16(ep->con_handle);
+ req->cp.reason = ep->reason;
+
+ /*
+ * Adjust connection state
+ */
+
+ con->state = NG_HCI_CON_CLOSED;
+ ng_hci_con_timeout(con);
+
+ /*
+ * Queue and send HCI command
+ */
+
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_hci_lp_discon_req */
+
+/*
+ * Send LP_ConnectCfm event to the upper layer protocol
+ */
+
+int
+ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
+{
+ ng_hci_unit_p unit = con->unit;
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_con_cfm_ep *ep = NULL;
+ int error;
+
+ /*
+ * Check who wants to be notified. For ACL links both ACL and SCO
+ * upstream hooks will be notified (if required). For SCO links
+ * only SCO upstream hook will receive notification
+ */
+
+ if (con->link_type == NG_HCI_LINK_ACL &&
+ con->flags & NG_HCI_CON_NOTIFY_ACL) {
+ if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
+ sizeof(*ep), M_NOWAIT);
+ if (msg != NULL) {
+ ep = (ng_hci_lp_con_cfm_ep *) msg->data;
+ ep->status = status;
+ ep->link_type = con->link_type;
+ ep->con_handle = con->con_handle;
+ bcopy(&con->bdaddr, &ep->bdaddr,
+ sizeof(ep->bdaddr));
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg,
+ unit->acl, NULL);
+ }
+ } else
+ NG_HCI_INFO(
+"%s: %s - ACL hook not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->acl);
+
+ con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
+ }
+
+ if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
+ if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
+ sizeof(*ep), M_NOWAIT);
+ if (msg != NULL) {
+ ep = (ng_hci_lp_con_cfm_ep *) msg->data;
+ ep->status = status;
+ ep->link_type = con->link_type;
+ ep->con_handle = con->con_handle;
+ bcopy(&con->bdaddr, &ep->bdaddr,
+ sizeof(ep->bdaddr));
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg,
+ unit->sco, NULL);
+ }
+ } else
+ NG_HCI_INFO(
+"%s: %s - SCO hook not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->acl);
+
+ con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
+ }
+
+ return (0);
+} /* ng_hci_lp_con_cfm */
+
+/*
+ * Send LP_ConnectInd event to the upper layer protocol
+ */
+
+int
+ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
+{
+ ng_hci_unit_p unit = con->unit;
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_con_ind_ep *ep = NULL;
+ hook_p hook = NULL;
+ int error = 0;
+
+ /*
+ * Connection_Request event is generated for specific link type.
+ * Use link_type to select upstream hook.
+ */
+
+ if (con->link_type == NG_HCI_LINK_ACL)
+ hook = unit->acl;
+ else
+ hook = unit->sco;
+
+ if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_con_ind_ep *)(msg->data);
+ ep->link_type = con->link_type;
+ bcopy(uclass, ep->uclass, sizeof(ep->uclass));
+ bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL);
+ } else {
+ NG_HCI_WARN(
+"%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), hook);
+
+ error = ENOTCONN;
+ }
+
+ return (error);
+} /* ng_hci_lp_con_ind */
+
+/*
+ * Process LP_ConnectRsp event from the upper layer protocol
+ */
+
+int
+ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ struct con_rsp_req {
+ ng_hci_cmd_pkt_t hdr;
+ union {
+ ng_hci_accept_con_cp acc;
+ ng_hci_reject_con_cp rej;
+ } __attribute__ ((packed)) cp;
+ } __attribute__ ((packed)) *req = NULL;
+ ng_hci_lp_con_rsp_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Check if unit is ready */
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
+ NG_HCI_WARN(
+"%s: %s - unit is not ready, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state);
+
+ error = ENXIO;
+ goto out;
+ }
+
+ if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid LP_ConnectRsp message size=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NGI_MSG(item)->header.arglen);
+
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
+
+ /*
+ * Here we have to deal with race. Upper layers might send conflicting
+ * requests. One might send Accept and other Reject. We will not try
+ * to solve all the problems, so first request will always win.
+ *
+ * Try to find connection that matches the following:
+ *
+ * 1) con->link_type == ep->link_type
+ *
+ * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ * con->state == NG_HCI_CON_W4_CONN_COMPLETE
+ *
+ * 3) con->bdaddr == ep->bdaddr
+ *
+ * Two cases:
+ *
+ * 1) We do not have connection descriptor. Could be bogus request or
+ * we have rejected connection already.
+ *
+ * 2) We do have connection descriptor. Then we need to check state:
+ *
+ * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
+ * connection and it is a first response from the upper layer.
+ * if "status == 0" (Accept) then we will send Accept_Connection
+ * command and change connection state to W4_CONN_COMPLETE, else
+ * send reject and delete connection.
+ *
+ * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
+ * connection. If "status == 0" we just need to link request
+ * and wait, else ignore Reject request.
+ */
+
+ LIST_FOREACH(con, &unit->con_list, next)
+ if (con->link_type == ep->link_type &&
+ (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
+ con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
+ bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ if (con == NULL) {
+ /* Reject for non-existing connection is fine */
+ error = (ep->status == 0)? ENOENT : 0;
+ goto out;
+ }
+
+ /*
+ * Remove connection timeout and check connection state
+ */
+
+ ng_hci_con_untimeout(con);
+
+ switch (con->state) {
+ case NG_HCI_CON_W4_LP_CON_RSP:
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ req = mtod(m, struct con_rsp_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+
+ if (ep->status == 0) {
+ req->hdr.length = sizeof(req->cp.acc);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(
+ NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_ACCEPT_CON));
+
+ bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
+ sizeof(req->cp.acc.bdaddr));
+
+ /*
+ * XXX should be configurable?
+ *
+ * We are accepting connection, so if we support role
+ * switch then set role to NG_HCI_ROLE_MASTER and let
+ * LM peform role switch. Otherwise it is probably
+ * makes sense to remain slave. In this case LM WILL
+ * NOT perform role switch.
+ */
+
+ if (unit->features[0] & NG_HCI_LMP_SWITCH)
+ req->cp.acc.role = NG_HCI_ROLE_MASTER;
+ else
+ req->cp.acc.role = NG_HCI_ROLE_SLAVE;
+
+ /*
+ * Adjust connection state
+ */
+
+ if (hook == unit->acl)
+ con->flags |= NG_HCI_CON_NOTIFY_ACL;
+ else
+ con->flags |= NG_HCI_CON_NOTIFY_SCO;
+
+ con->state = NG_HCI_CON_W4_CONN_COMPLETE;
+ ng_hci_con_timeout(con);
+ } else {
+ req->hdr.length = sizeof(req->cp.rej);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(
+ NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_REJECT_CON));
+
+ bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
+ sizeof(req->cp.rej.bdaddr));
+
+ req->cp.rej.reason = ep->status;
+
+ /*
+ * Free connection descritor
+ * Item will be deleted just before return.
+ */
+
+ ng_hci_free_con(con);
+ }
+
+ m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
+
+ /* Queue and send HCI command */
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+ break;
+
+ case NG_HCI_CON_W4_CONN_COMPLETE:
+ if (ep->status == 0) {
+ if (hook == unit->acl)
+ con->flags |= NG_HCI_CON_NOTIFY_ACL;
+ else
+ con->flags |= NG_HCI_CON_NOTIFY_SCO;
+ } else
+ error = EPERM;
+ break;
+
+ default:
+ KASSERT(0,
+("%s: %s - Invalid connection state=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->state));
+
+ error = EINVAL;
+ break;
+ }
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_hci_lp_con_rsp */
+
+/*
+ * Send LP_DisconnectInd to the upper layer protocol
+ */
+
+int
+ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
+{
+ ng_hci_unit_p unit = con->unit;
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_discon_ind_ep *ep = NULL;
+ int error = 0;
+
+ /*
+ * Disconnect_Complete event is generated for specific connection
+ * handle. For ACL connection handles both ACL and SCO upstream
+ * hooks will receive notification. For SCO connection handles
+ * only SCO upstream hook will receive notification.
+ */
+
+ if (con->link_type == NG_HCI_LINK_ACL) {
+ if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
+ NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_discon_ind_ep *) msg->data;
+ ep->reason = reason;
+ ep->link_type = con->link_type;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL);
+ } else
+ NG_HCI_INFO(
+"%s: %s - ACL hook is not connected or not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->acl);
+ }
+
+ if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_discon_ind_ep *) msg->data;
+ ep->reason = reason;
+ ep->link_type = con->link_type;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
+ } else
+ NG_HCI_INFO(
+"%s: %s - SCO hook is not connected or not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->sco);
+
+ return (0);
+} /* ng_hci_lp_discon_ind */
+
+/*
+ * Process LP_QoSReq action from the upper layer protocol
+ */
+
+int
+ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
+{
+ struct qos_setup_req {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_qos_setup_cp cp;
+ } __attribute__ ((packed)) *req = NULL;
+ ng_hci_lp_qos_req_ep *ep = NULL;
+ ng_hci_unit_con_p con = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Check if unit is ready */
+ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
+ NG_HCI_WARN(
+"%s: %s - unit is not ready, state=%#x\n",
+ __func__, NG_NODE_NAME(unit->node), unit->state);
+
+ error = ENXIO;
+ goto out;
+ }
+
+ if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
+ NG_HCI_ALERT(
+"%s: %s - invalid LP_QoSSetupReq message size=%d\n",
+ __func__, NG_NODE_NAME(unit->node),
+ NGI_MSG(item)->header.arglen);
+
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
+
+ con = ng_hci_con_by_handle(unit, ep->con_handle);
+ if (con == NULL) {
+ NG_HCI_ERR(
+"%s: %s - invalid connection handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), ep->con_handle);
+
+ error = EINVAL;
+ goto out;
+ }
+
+ if (con->link_type != NG_HCI_LINK_ACL) {
+ NG_HCI_ERR("%s: %s - invalid link type=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->link_type);
+
+ error = EINVAL;
+ goto out;
+ }
+
+ if (con->state != NG_HCI_CON_OPEN) {
+ NG_HCI_ERR(
+"%s: %s - invalid connection state=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(unit->node), con->state,
+ con->con_handle);
+
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ m->m_pkthdr.len = m->m_len = sizeof(*req);
+ req = mtod(m, struct qos_setup_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+ req->hdr.length = sizeof(req->cp);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_QOS_SETUP));
+
+ req->cp.con_handle = htole16(ep->con_handle);
+ req->cp.flags = ep->flags;
+ req->cp.service_type = ep->service_type;
+ req->cp.token_rate = htole32(ep->token_rate);
+ req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
+ req->cp.latency = htole32(ep->latency);
+ req->cp.delay_variation = htole32(ep->delay_variation);
+
+ /*
+ * Adjust connection state
+ */
+
+ if (hook == unit->acl)
+ con->flags |= NG_HCI_CON_NOTIFY_ACL;
+ else
+ con->flags |= NG_HCI_CON_NOTIFY_SCO;
+
+ /*
+ * Queue and send HCI command
+ */
+
+ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
+ if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ error = ng_hci_send_command(unit);
+out:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_hci_lp_qos_req */
+
+/*
+ * Send LP_QoSCfm event to the upper layer protocol
+ */
+
+int
+ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
+{
+ ng_hci_unit_p unit = con->unit;
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_qos_cfm_ep *ep = NULL;
+ int error;
+
+ if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
+ if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
+ sizeof(*ep), M_NOWAIT);
+ if (msg != NULL) {
+ ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
+ ep->status = status;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg,
+ unit->acl, NULL);
+ }
+ } else
+ NG_HCI_INFO(
+"%s: %s - ACL hook not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->acl);
+
+ con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
+ }
+
+ if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
+ if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
+ sizeof(*ep), M_NOWAIT);
+ if (msg != NULL) {
+ ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
+ ep->status = status;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg,
+ unit->sco, NULL);
+ }
+ } else
+ NG_HCI_INFO(
+"%s: %s - SCO hook not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->sco);
+
+ con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
+ }
+
+ return (0);
+} /* ng_hci_lp_qos_cfm */
+
+/*
+ * Send LP_QoSViolationInd event to the upper layer protocol
+ */
+
+int
+ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
+{
+ ng_hci_unit_p unit = con->unit;
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_qos_ind_ep *ep = NULL;
+ int error;
+
+ /*
+ * QoS Violation can only be generated for ACL connection handles.
+ * Both ACL and SCO upstream hooks will receive notification.
+ */
+
+ if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_qos_ind_ep *) msg->data;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL);
+ } else
+ NG_HCI_INFO(
+"%s: %s - ACL hook is not connected or not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->acl);
+
+ if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_qos_ind_ep *) msg->data;
+ ep->con_handle = con->con_handle;
+
+ NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
+ } else
+ NG_HCI_INFO(
+"%s: %s - SCO hook is not connected or not valid, hook=%p\n",
+ __func__, NG_NODE_NAME(unit->node), unit->sco);
+
+ return (0);
+} /* ng_hci_lp_qos_ind */
+
+/*
+ * Process connection timeout
+ */
+
+void
+ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_hci_unit_con_p con = (ng_hci_unit_con_p) arg1;
+
+ KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING),
+("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node)));
+
+ con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
+
+ /*
+ * We expect to receive connection timeout in one of the following
+ * states:
+ *
+ * 1) NG_HCI_CON_CLOSED means that upper layer has requested disconnect
+ * via LP_DISCON_REQ and we have not received Disconnect_Complete
+ * event. In this case we will send LP_DISCON_IND to upper layer.
+ *
+ * 2) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
+ * to our LP_CON_IND. Do nothing and destroy connection. Remote peer
+ * most likely already gave up on us.
+ *
+ * 3) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
+ * (or we in the process of accepting it) and baseband has timedout
+ * on us. Inform upper layers and send LP_CON_CFM.
+ */
+
+ switch (con->state) {
+ case NG_HCI_CON_CLOSED:
+ ng_hci_lp_discon_ind(con, 0x16);
+ break;
+
+ case NG_HCI_CON_W4_LP_CON_RSP:
+ break;
+
+ case NG_HCI_CON_W4_CONN_COMPLETE:
+ ng_hci_lp_con_cfm(con, 0xee);
+ break;
+
+ default:
+ KASSERT(0,
+("%s: %s - Invalid connection state=%d\n",
+ __func__, NG_NODE_NAME(node), con->state));
+ break;
+ }
+
+ ng_hci_free_con(con);
+} /* ng_hci_process_con_timeout */
+
+/*
+ * Process connection watchdog timeout
+ */
+
+void
+ng_hci_process_con_watchdog_timeout(node_p node, hook_p hook,
+ void *arg1, int arg2)
+{
+ ng_hci_unit_con_p con = (ng_hci_unit_con_p) arg1;
+ struct discon_req {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_discon_cp cp;
+ } __attribute__ ((packed)) *req = NULL;
+ struct mbuf *m = NULL;
+
+ KASSERT((con->state == NG_HCI_CON_OPEN),
+("%s: %s - invalid connection state=%d, handle=%d\n",
+ __func__, NG_NODE_NAME(node), con->state, con->con_handle));
+
+ KASSERT((con->flags & NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING),
+("%s: %s - No connection watchdog timeout!\n",
+ __func__, NG_NODE_NAME(node)));
+
+ con->flags &= ~NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING;
+
+ /*
+ * Create HCI command
+ */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return; /* XXX this is bad */
+
+ m->m_pkthdr.len = m->m_len = sizeof(*req);
+ req = mtod(m, struct discon_req *);
+ req->hdr.type = NG_HCI_CMD_PKT;
+ req->hdr.length = sizeof(req->cp);
+ req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_DISCON));
+
+ req->cp.con_handle = htole16(con->con_handle);
+ req->cp.reason = 0x13; /* User ended connection */
+
+ /*
+ * Queue and send HCI command
+ */
+
+ NG_BT_MBUFQ_ENQUEUE(&con->unit->cmdq, m);
+ if (!(con->unit->state & NG_HCI_UNIT_COMMAND_PENDING))
+ ng_hci_send_command(con->unit);
+
+ /*
+ * Send LP_DISCON_IND to the upper layers
+ * Connection terminated by local host
+ */
+
+ ng_hci_lp_discon_ind(con, 0x16);
+ ng_hci_free_con(con);
+} /* ng_hci_process_con_watchdog_timeout */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h b/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h
new file mode 100644
index 0000000..9f4e0ef
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h
@@ -0,0 +1,53 @@
+/*
+ * ng_hci_ulpi.h
+ *
+ * Copyright (c) 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_hci_ulpi.h,v 1.4 2002/09/04 21:36:52 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_HCI_ULPI_H_
+#define _NETGRAPH_HCI_ULPI_H_
+
+/*
+ * LP_xxx event handlers
+ */
+
+int ng_hci_lp_con_req (ng_hci_unit_p, item_p, hook_p);
+int ng_hci_lp_discon_req (ng_hci_unit_p, item_p, hook_p);
+int ng_hci_lp_con_cfm (ng_hci_unit_con_p, int);
+int ng_hci_lp_con_ind (ng_hci_unit_con_p, u_int8_t *);
+int ng_hci_lp_con_rsp (ng_hci_unit_p, item_p, hook_p);
+int ng_hci_lp_discon_ind (ng_hci_unit_con_p, int);
+int ng_hci_lp_qos_req (ng_hci_unit_p, item_p, hook_p);
+int ng_hci_lp_qos_cfm (ng_hci_unit_con_p, int);
+int ng_hci_lp_qos_ind (ng_hci_unit_con_p);
+
+void ng_hci_process_con_timeout (node_p, hook_p, void *, int);
+void ng_hci_process_con_watchdog_timeout (node_p, hook_p, void *, int);
+
+#endif /* ndef _NETGRAPH_HCI_ULPI_H_ */
+
diff --git a/sys/netgraph/bluetooth/hci/ng_hci_var.h b/sys/netgraph/bluetooth/hci/ng_hci_var.h
new file mode 100644
index 0000000..43940a9
--- /dev/null
+++ b/sys/netgraph/bluetooth/hci/ng_hci_var.h
@@ -0,0 +1,217 @@
+/*
+ * ng_hci_var.h
+ *
+ * Copyright (c) 2001 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_hci_var.h,v 1.14 2002/11/12 22:35:40 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_HCI_VAR_H_
+#define _NETGRAPH_HCI_VAR_H_ 1
+
+/* MALLOC decalation */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DECLARE(M_NETGRAPH_HCI);
+#else
+#define M_NETGRAPH_HCI M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Debug */
+#define NG_HCI_ALERT if (unit->debug >= NG_HCI_ALERT_LEVEL) printf
+#define NG_HCI_ERR if (unit->debug >= NG_HCI_ERR_LEVEL) printf
+#define NG_HCI_WARN if (unit->debug >= NG_HCI_WARN_LEVEL) printf
+#define NG_HCI_INFO if (unit->debug >= NG_HCI_INFO_LEVEL) printf
+
+/* Wrapper around m_pullup */
+#define NG_HCI_M_PULLUP(m, s) \
+ do { \
+ if ((m)->m_len < (s)) \
+ (m) = m_pullup((m), (s)); \
+ if ((m) == NULL) \
+ NG_HCI_ALERT("%s: %s - m_pullup(%d) failed\n", \
+ __func__, NG_NODE_NAME(unit->node), (s)); \
+ } while (0)
+
+/*
+ * Unit hardware buffer descriptor
+ */
+
+typedef struct ng_hci_unit_buff {
+ u_int8_t cmd_free; /* space available (cmds) */
+
+ u_int8_t sco_size; /* max. size of one packet */
+ u_int16_t sco_pkts; /* size of buffer (packets) */
+ u_int16_t sco_free; /* space available (packets)*/
+
+ u_int16_t acl_size; /* max. size of one packet */
+ u_int16_t acl_pkts; /* size of buffer (packets) */
+ u_int16_t acl_free; /* space available (packets)*/
+} ng_hci_unit_buff_t;
+
+/*
+ * These macro's must be used everywhere in the code. So if extra locking
+ * is required later, it can be added without much troubles.
+ */
+
+#define NG_HCI_BUFF_CMD_SET(b, v) (b).cmd_free = (v)
+#define NG_HCI_BUFF_CMD_GET(b, v) (v) = (b).cmd_free
+#define NG_HCI_BUFF_CMD_USE(b, v) (b).cmd_free -= (v)
+
+#define NG_HCI_BUFF_ACL_USE(b, v) (b).acl_free -= (v)
+#define NG_HCI_BUFF_ACL_FREE(b, v) \
+ do { \
+ (b).acl_free += (v); \
+ if ((b).acl_free > (b).acl_pkts) \
+ (b).acl_free = (b).acl_pkts; \
+ } while (0)
+#define NG_HCI_BUFF_ACL_AVAIL(b, v) (v) = (b).acl_free
+#define NG_HCI_BUFF_ACL_TOTAL(b, v) (v) = (b).acl_pkts
+#define NG_HCI_BUFF_ACL_SIZE(b, v) (v) = (b).acl_size
+#define NG_HCI_BUFF_ACL_SET(b, n, s, f) \
+ do { \
+ (b).acl_free = (f); \
+ (b).acl_size = (s); \
+ (b).acl_pkts = (n); \
+ } while (0)
+
+#define NG_HCI_BUFF_SCO_USE(b, v) (b).sco_free -= (v)
+#define NG_HCI_BUFF_SCO_FREE(b, v) \
+ do { \
+ (b).sco_free += (v); \
+ if ((b).sco_free > (b).sco_pkts) \
+ (b).sco_free = (b).sco_pkts; \
+ } while (0)
+#define NG_HCI_BUFF_SCO_AVAIL(b, v) (v) = (b).sco_free
+#define NG_HCI_BUFF_SCO_TOTAL(b, v) (v) = (b).sco_pkts
+#define NG_HCI_BUFF_SCO_SIZE(b, v) (v) = (b).sco_size
+#define NG_HCI_BUFF_SCO_SET(b, n, s, f) \
+ do { \
+ (b).sco_free = (f); \
+ (b).sco_size = (s); \
+ (b).sco_pkts = (n); \
+ } while (0)
+
+/*
+ * Unit (Node private)
+ */
+
+struct ng_hci_unit_con;
+struct ng_hci_neighbor;
+
+typedef struct ng_hci_unit {
+ node_p node; /* node ptr */
+
+ ng_hci_node_debug_ep debug; /* debug level */
+ ng_hci_node_state_ep state; /* unit state */
+
+ bdaddr_t bdaddr; /* unit address */
+ u_int8_t features[NG_HCI_FEATURES_SIZE];
+ /* LMP features */
+
+ ng_hci_node_link_policy_mask_ep link_policy_mask; /* link policy mask */
+ ng_hci_node_packet_mask_ep packet_mask; /* packet mask */
+
+ ng_hci_node_stat_ep stat; /* statistic */
+#define NG_HCI_STAT_CMD_SENT(s) (s).cmd_sent ++
+#define NG_HCI_STAT_EVNT_RECV(s) (s).evnt_recv ++
+#define NG_HCI_STAT_ACL_SENT(s, n) (s).acl_sent += (n)
+#define NG_HCI_STAT_ACL_RECV(s) (s).acl_recv ++
+#define NG_HCI_STAT_SCO_SENT(s, n) (s).sco_sent += (n)
+#define NG_HCI_STAT_SCO_RECV(s) (s).sco_recv ++
+#define NG_HCI_STAT_BYTES_SENT(s, b) (s).bytes_sent += (b)
+#define NG_HCI_STAT_BYTES_RECV(s, b) (s).bytes_recv += (b)
+#define NG_HCI_STAT_RESET(s) bzero(&(s), sizeof((s)))
+
+ ng_hci_unit_buff_t buffer; /* buffer info */
+
+ struct callout_handle cmd_timo; /* command timeout */
+ ng_bt_mbufq_t cmdq; /* command queue */
+#define NG_HCI_CMD_QUEUE_LEN 12 /* max. size of cmd q */
+
+ hook_p drv; /* driver hook */
+ hook_p acl; /* upstream hook */
+ hook_p sco; /* upstream hook */
+ hook_p raw; /* upstream hook */
+
+ LIST_HEAD(, ng_hci_unit_con) con_list; /* connections */
+ LIST_HEAD(, ng_hci_neighbor) neighbors; /* unit neighbors */
+} ng_hci_unit_t;
+typedef ng_hci_unit_t * ng_hci_unit_p;
+
+/*
+ * Unit connection descriptor
+ */
+
+typedef struct ng_hci_unit_con {
+ ng_hci_unit_p unit; /* pointer back */
+
+ u_int16_t state; /* con. state */
+ u_int16_t flags; /* con. flags */
+#define NG_HCI_CON_TIMEOUT_PENDING (1 << 0)
+#define NG_HCI_CON_WATCHDOG_TIMEOUT_PENDING (1 << 1)
+#define NG_HCI_CON_NOTIFY_ACL (1 << 2)
+#define NG_HCI_CON_NOTIFY_SCO (1 << 3)
+
+ bdaddr_t bdaddr; /* remote address */
+ u_int16_t con_handle; /* con. handle */
+
+ u_int8_t link_type; /* ACL or SCO */
+ u_int8_t encryption_mode; /* none, p2p, ... */
+ u_int8_t mode; /* ACTIVE, HOLD ... */
+ u_int8_t role; /* MASTER/SLAVE */
+
+ struct callout_handle con_timo; /* con. timeout */
+ struct callout_handle watchdog_timo; /* watch dog */
+
+ int pending; /* # of data pkts */
+ ng_bt_itemq_t conq; /* con. queue */
+
+ LIST_ENTRY(ng_hci_unit_con) next; /* next */
+} ng_hci_unit_con_t;
+typedef ng_hci_unit_con_t * ng_hci_unit_con_p;
+
+/*
+ * Unit's neighbor descriptor.
+ * Neighbor is a remote unit that responded to our inquiry.
+ */
+
+typedef struct ng_hci_neighbor {
+ struct timeval updated; /* entry was updated */
+
+ bdaddr_t bdaddr; /* address */
+ u_int8_t features[NG_HCI_FEATURES_SIZE];
+ /* LMP features */
+
+ u_int8_t page_scan_rep_mode; /* PS rep. mode */
+ u_int8_t page_scan_mode; /* page scan mode */
+ u_int16_t clock_offset; /* clock offset */
+
+ LIST_ENTRY(ng_hci_neighbor) next;
+} ng_hci_neighbor_t;
+typedef ng_hci_neighbor_t * ng_hci_neighbor_p;
+
+#endif /* ndef _NETGRAPH_HCI_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_bluetooth.h b/sys/netgraph/bluetooth/include/ng_bluetooth.h
new file mode 100644
index 0000000..238da42
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_bluetooth.h
@@ -0,0 +1,232 @@
+/*
+ * bluetooth.h
+ *
+ * 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.h,v 1.1.1.1 2002/09/04 21:47:41 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_BLUETOOTH_H_
+#define _NETGRAPH_BLUETOOTH_H_ 1
+
+/*
+ * Version of the stack
+ */
+
+#define NG_BLUETOOTH_VERSION 1
+
+/*
+ * Declare the base of the Bluetooth sysctl hierarchy,
+ * but only if this file cares about sysctl's
+ */
+
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_bluetooth);
+SYSCTL_DECL(_net_bluetooth_hci);
+SYSCTL_DECL(_net_bluetooth_l2cap);
+#endif /* SYSCTL_DECL */
+
+/*
+ * Mbuf qeueue and useful mbufq macros. We do not use ifqueue because we
+ * do not need mutex and other locking stuff
+ */
+
+struct mbuf;
+
+struct ng_bt_mbufq {
+ struct mbuf *head; /* first item in the queue */
+ struct mbuf *tail; /* last item in the queue */
+ u_int32_t len; /* number of items in the queue */
+ u_int32_t maxlen; /* maximal number of items in the queue */
+ u_int32_t drops; /* number if dropped items */
+};
+typedef struct ng_bt_mbufq ng_bt_mbufq_t;
+typedef struct ng_bt_mbufq * ng_bt_mbufq_p;
+
+#define NG_BT_MBUFQ_INIT(q, _maxlen) \
+ do { \
+ (q)->head = NULL; \
+ (q)->tail = NULL; \
+ (q)->len = 0; \
+ (q)->maxlen = (_maxlen); \
+ (q)->drops = 0; \
+ } while (0)
+
+#define NG_BT_MBUFQ_DESTROY(q) \
+ do { \
+ NG_BT_MBUFQ_DRAIN((q)); \
+ } while (0)
+
+#define NG_BT_MBUFQ_FIRST(q) (q)->head
+
+#define NG_BT_MBUFQ_LEN(q) (q)->len
+
+#define NG_BT_MBUFQ_FULL(q) (q)->len >= (q)->maxlen
+
+#define NG_BT_MBUFQ_DROP(q) (q)->drops ++
+
+#define NG_BT_MBUFQ_ENQUEUE(q, i) \
+ do { \
+ (i)->m_nextpkt = NULL; \
+ \
+ if ((q)->tail == NULL) \
+ (q)->head = (i); \
+ else \
+ (q)->tail->m_nextpkt = (i); \
+ \
+ (q)->tail = (i); \
+ (q)->len ++; \
+ } while (0)
+
+#define NG_BT_MBUFQ_DEQUEUE(q, i) \
+ do { \
+ (i) = (q)->head; \
+ if ((i) != NULL) { \
+ (q)->head = (q)->head->m_nextpkt; \
+ if ((q)->head == NULL) \
+ (q)->tail = NULL; \
+ \
+ (q)->len --; \
+ (i)->m_nextpkt = NULL; \
+ } \
+ } while (0)
+
+#define NG_BT_MBUFQ_PREPEND(q, i) \
+ do { \
+ (i)->m_nextpkt = (q)->head; \
+ if ((q)->tail == NULL) \
+ (q)->tail = (i); \
+ \
+ (q)->head = (i); \
+ (q)->len ++; \
+ } while (0)
+
+#define NG_BT_MBUFQ_DRAIN(q) \
+ do { \
+ struct mbuf *m = NULL; \
+ \
+ for (;;) { \
+ NG_BT_MBUFQ_DEQUEUE((q), m); \
+ if (m == NULL) \
+ break; \
+ \
+ NG_FREE_M(m); \
+ } \
+ } while (0)
+
+/*
+ * Netgraph item queue and useful itemq macros
+ */
+
+struct ng_item;
+
+struct ng_bt_itemq {
+ struct ng_item *head; /* first item in the queue */
+ struct ng_item *tail; /* last item in the queue */
+ u_int32_t len; /* number of items in the queue */
+ u_int32_t maxlen; /* maximal number of items in the queue */
+ u_int32_t drops; /* number if dropped items */
+};
+typedef struct ng_bt_itemq ng_bt_itemq_t;
+typedef struct ng_bt_itemq * ng_bt_itemq_p;
+
+#define NG_BT_ITEMQ_INIT(q, _maxlen) NG_BT_MBUFQ_INIT((q), (_maxlen))
+
+#define NG_BT_ITEMQ_DESTROY(q) \
+ do { \
+ NG_BT_ITEMQ_DRAIN((q)); \
+ } while (0)
+
+#define NG_BT_ITEMQ_FIRST(q) NG_BT_MBUFQ_FIRST((q))
+
+#define NG_BT_ITEMQ_LEN(q) NG_BT_MBUFQ_LEN((q))
+
+#define NG_BT_ITEMQ_FULL(q) NG_BT_MBUFQ_FULL((q))
+
+#define NG_BT_ITEMQ_DROP(q) NG_BT_MBUFQ_DROP((q))
+
+#define NG_BT_ITEMQ_ENQUEUE(q, i) \
+ do { \
+ (i)->el_next = NULL; \
+ \
+ if ((q)->tail == NULL) \
+ (q)->head = (i); \
+ else \
+ (q)->tail->el_next = (i); \
+ \
+ (q)->tail = (i); \
+ (q)->len ++; \
+ } while (0)
+
+#define NG_BT_ITEMQ_DEQUEUE(q, i) \
+ do { \
+ (i) = (q)->head; \
+ if ((i) != NULL) { \
+ (q)->head = (q)->head->el_next; \
+ if ((q)->head == NULL) \
+ (q)->tail = NULL; \
+ \
+ (q)->len --; \
+ (i)->el_next = NULL; \
+ } \
+ } while (0)
+
+#define NG_BT_ITEMQ_PREPEND(q, i) \
+ do { \
+ (i)->el_next = (q)->head; \
+ if ((q)->tail == NULL) \
+ (q)->tail = (i); \
+ \
+ (q)->head = (i); \
+ (q)->len ++; \
+ } while (0)
+
+#define NG_BT_ITEMQ_DRAIN(q) \
+ do { \
+ struct ng_item *i = NULL; \
+ \
+ for (;;) { \
+ NG_BT_ITEMQ_DEQUEUE((q), i); \
+ if (i == NULL) \
+ break; \
+ \
+ NG_FREE_ITEM(i); \
+ } \
+ } while (0)
+
+/*
+ * Get Bluetooth stack sysctl globals
+ */
+
+u_int32_t bluetooth_hci_command_timeout (void);
+u_int32_t bluetooth_hci_connect_timeout (void);
+u_int32_t bluetooth_hci_watchdog_timeout (void);
+u_int32_t bluetooth_hci_max_neighbor_age (void);
+u_int32_t bluetooth_l2cap_rtx_timeout (void);
+u_int32_t bluetooth_l2cap_ertx_timeout (void);
+
+#endif /* _NETGRAPH_BLUETOOTH_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_bt3c.h b/sys/netgraph/bluetooth/include/ng_bt3c.h
new file mode 100644
index 0000000..6030b81
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_bt3c.h
@@ -0,0 +1,114 @@
+/*
+ * ng_bt3c.h
+ *
+ * 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.h,v 1.2 2002/11/12 00:51:45 max Exp $
+ * $FreeBSD$
+ *
+ * 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
+ *
+ */
+
+#ifndef _NG_BT3C_H_
+#define _NG_BT3C_H_
+
+/* XXX FIME: does not belong here. Move to pccarddevs.h later */
+#define PCMCIA_PRODUCT_3COM_3CRWB609 0x0040
+#define PCMCIA_STR_3COM_3CRWB609 "3Com Bluetooth PC Card 3CRWB60-A"
+#define PCMCIA_CIS_3COM_3CRWB609 { NULL, NULL, NULL, NULL }
+
+/**************************************************************************
+ **************************************************************************
+ ** Netgraph node hook name, type name and type cookie and commands
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_BT3C_NODE_TYPE "btccc" /* XXX can't use bt3c in pccard.conf */
+#define NG_BT3C_HOOK "hook"
+
+#define NGM_BT3C_COOKIE 1014752016
+
+/* Debug levels */
+#define NG_BT3C_ALERT_LEVEL 1
+#define NG_BT3C_ERR_LEVEL 2
+#define NG_BT3C_WARN_LEVEL 3
+#define NG_BT3C_INFO_LEVEL 4
+
+/* Node states */
+#define NG_BT3C_W4_PKT_IND 1 /* wait for packet indicator */
+#define NG_BT3C_W4_PKT_HDR 2 /* wait for packet header */
+#define NG_BT3C_W4_PKT_DATA 3 /* wait for packet data */
+
+/**************************************************************************
+ **************************************************************************
+ ** BT3C node command/event parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NGM_BT3C_NODE_GET_STATE 1 /* get node state */
+typedef u_int16_t ng_bt3c_node_state_ep;
+
+#define NGM_BT3C_NODE_SET_DEBUG 2 /* set debug level */
+#define NGM_BT3C_NODE_GET_DEBUG 3 /* get debug level */
+typedef u_int16_t ng_bt3c_node_debug_ep;
+
+#define NGM_BT3C_NODE_GET_QLEN 4 /* get queue length */
+#define NGM_BT3C_NODE_SET_QLEN 5 /* set queue length */
+typedef struct {
+ int32_t queue; /* queue index */
+#define NGM_BT3C_NODE_IN_QUEUE 1 /* incoming queue */
+#define NGM_BT3C_NODE_OUT_QUEUE 2 /* outgoing queue */
+
+ int32_t qlen; /* queue length */
+} ng_bt3c_node_qlen_ep;
+
+#define NGM_BT3C_NODE_GET_STAT 6 /* get statistic */
+typedef struct {
+ u_int32_t pckts_recv; /* # of packets received */
+ u_int32_t bytes_recv; /* # of bytes received */
+ u_int32_t pckts_sent; /* # of packets sent */
+ u_int32_t bytes_sent; /* # of bytes sent */
+ u_int32_t oerrors; /* # of output errors */
+ u_int32_t ierrors; /* # of input errors */
+} ng_bt3c_node_stat_ep;
+
+#define NGM_BT3C_NODE_RESET_STAT 7 /* reset statistic */
+
+#define NGM_BT3C_NODE_DOWNLOAD_FIRMWARE 8 /* download firmware */
+
+typedef struct {
+ u_int32_t block_address;
+ u_int16_t block_size; /* in words */
+ u_int16_t block_alignment; /* in bytes */
+} ng_bt3c_firmware_block_ep;
+
+#endif /* ndef _NG_BT3C_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_btsocket.h b/sys/netgraph/bluetooth/include/ng_btsocket.h
new file mode 100644
index 0000000..5ace3fa
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_btsocket.h
@@ -0,0 +1,319 @@
+/*
+ * ng_btsocket.h
+ *
+ * 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_btsocket.h,v 1.7 2002/11/12 22:31:39 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_BTSOCKET_H_
+#define _NETGRAPH_BTSOCKET_H_ 1
+
+/*
+ * XXX FIXME: does not belong here, move to sys/socket.h later and fix AF_MAX
+ */
+
+#define AF_BLUETOOTH 36 /* Address family */
+#define PF_BLUETOOTH AF_BLUETOOTH /* Protocol family */
+
+/*
+ * XXX FIXME: does not belong here, move to other places later
+ */
+
+#define BLUETOOTH_PROTO_HCI 134 /* HCI protocol number */
+#define BLUETOOTH_PROTO_L2CAP 135 /* L2CAP protocol number */
+
+/*
+ * XXX FIXME: probably does not belong here
+ * Bluetooth version of struct sockaddr for raw HCI sockets
+ */
+
+struct sockaddr_hci {
+ u_char hci_len; /* total length */
+ u_char hci_family; /* address family */
+ char hci_node[16]; /* address (size == NG_NODELEN + 1) */
+};
+
+/* Raw HCI socket options */
+#define SOL_HCI_RAW 0x0802 /* socket options level */
+
+#define SO_HCI_RAW_FILTER 1 /* get/set filter on socket */
+#define SO_HCI_RAW_DIRECTION 2 /* turn on/off direction info */
+#define SCM_HCI_RAW_DIRECTION SO_HCI_RAW_DIRECTION /* cmsg_type */
+
+/*
+ * Raw HCI socket filter.
+ *
+ * For packet mask use (1 << (HCI packet indicator - 1))
+ * For event mask use (1 << (Event - 1))
+ */
+
+struct ng_btsocket_hci_raw_filter {
+ bitstr_t bit_decl(packet_mask, 32);
+ bitstr_t bit_decl(event_mask, (NG_HCI_EVENT_MASK_SIZE * 8));
+};
+
+/*
+ * Raw HCI sockets ioctl's
+ */
+
+/* Get state */
+struct ng_btsocket_hci_raw_node_state {
+ char hci_node[16];
+ ng_hci_node_state_ep state;
+};
+#define SIOC_HCI_RAW_NODE_GET_STATE \
+ _IOWR('b', NGM_HCI_NODE_GET_STATE, \
+ struct ng_btsocket_hci_raw_node_state)
+
+/* Initialize */
+struct ng_btsocket_hci_raw_node_init {
+ char hci_node[16];
+};
+#define SIOC_HCI_RAW_NODE_INIT \
+ _IOWR('b', NGM_HCI_NODE_INIT, \
+ struct ng_btsocket_hci_raw_node_init)
+
+/* Get/Set debug level */
+struct ng_btsocket_hci_raw_node_debug {
+ char hci_node[16];
+ ng_hci_node_debug_ep debug;
+};
+#define SIOC_HCI_RAW_NODE_GET_DEBUG \
+ _IOWR('b', NGM_HCI_NODE_GET_DEBUG, \
+ struct ng_btsocket_hci_raw_node_debug)
+#define SIOC_HCI_RAW_NODE_SET_DEBUG \
+ _IOWR('b', NGM_HCI_NODE_SET_DEBUG, \
+ struct ng_btsocket_hci_raw_node_debug)
+
+/* Get buffer info */
+struct ng_btsocket_hci_raw_node_buffer {
+ char hci_node[16];
+ ng_hci_node_buffer_ep buffer;
+};
+#define SIOC_HCI_RAW_NODE_GET_BUFFER \
+ _IOWR('b', NGM_HCI_NODE_GET_BUFFER, \
+ struct ng_btsocket_hci_raw_node_buffer)
+
+/* Get BD_ADDR */
+struct ng_btsocket_hci_raw_node_bdaddr {
+ char hci_node[16];
+ bdaddr_t bdaddr;
+};
+#define SIOC_HCI_RAW_NODE_GET_BDADDR \
+ _IOWR('b', NGM_HCI_NODE_GET_BDADDR, \
+ struct ng_btsocket_hci_raw_node_bdaddr)
+
+/* Get features */
+struct ng_btsocket_hci_raw_node_features {
+ char hci_node [16];
+ u_int8_t features[NG_HCI_FEATURES_SIZE];
+};
+#define SIOC_HCI_RAW_NODE_GET_FEATURES \
+ _IOWR('b', NGM_HCI_NODE_GET_FEATURES, \
+ struct ng_btsocket_hci_raw_node_features)
+
+/* Get stat */
+struct ng_btsocket_hci_raw_node_stat {
+ char hci_node[16];
+ ng_hci_node_stat_ep stat;
+};
+#define SIOC_HCI_RAW_NODE_GET_STAT \
+ _IOWR('b', NGM_HCI_NODE_GET_STAT, \
+ struct ng_btsocket_hci_raw_node_stat)
+
+/* Reset stat */
+struct ng_btsocket_hci_raw_node_reset_stat {
+ char hci_node[16];
+};
+#define SIOC_HCI_RAW_NODE_RESET_STAT \
+ _IOWR('b', NGM_HCI_NODE_RESET_STAT, \
+ struct ng_btsocket_hci_raw_node_reset_stat)
+
+/* Flush neighbor cache */
+struct ng_btsocket_hci_raw_node_flush_neighbor_cache {
+ char hci_node[16];
+};
+#define SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE \
+ _IOWR('b', NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE, \
+ struct ng_btsocket_hci_raw_node_flush_neighbor_cache)
+
+/* Get neighbor cache */
+struct ng_btsocket_hci_raw_node_neighbor_cache {
+ char hci_node[16];
+ u_int32_t num_entries;
+ ng_hci_node_neighbor_cache_entry_ep *entries;
+};
+#define SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE \
+ _IOWR('b', NGM_HCI_NODE_GET_NEIGHBOR_CACHE, \
+ struct ng_btsocket_hci_raw_node_neighbor_cache)
+
+/* Get connection list */
+struct ng_btsocket_hci_raw_con_list {
+ char hci_node[16];
+ u_int32_t num_connections;
+ ng_hci_node_con_ep *connections;
+};
+#define SIOC_HCI_RAW_NODE_GET_CON_LIST \
+ _IOWR('b', NGM_HCI_NODE_GET_CON_LIST, \
+ struct ng_btsocket_hci_raw_con_list)
+
+/* Get/Set link policy settings mask */
+struct ng_btsocket_hci_raw_node_link_policy_mask {
+ char hci_node[16];
+ ng_hci_node_link_policy_mask_ep policy_mask;
+};
+#define SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK \
+ _IOWR('b', NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK, \
+ struct ng_btsocket_hci_raw_node_link_policy_mask)
+#define SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK \
+ _IOWR('b', NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK, \
+ struct ng_btsocket_hci_raw_node_link_policy_mask)
+
+/* Get/Set packet mask */
+struct ng_btsocket_hci_raw_node_packet_mask {
+ char hci_node[16];
+ ng_hci_node_packet_mask_ep packet_mask;
+};
+#define SIOC_HCI_RAW_NODE_GET_PACKET_MASK \
+ _IOWR('b', NGM_HCI_NODE_GET_PACKET_MASK, \
+ struct ng_btsocket_hci_raw_node_packet_mask)
+#define SIOC_HCI_RAW_NODE_SET_PACKET_MASK \
+ _IOWR('b', NGM_HCI_NODE_SET_PACKET_MASK, \
+ struct ng_btsocket_hci_raw_node_packet_mask)
+
+/*
+ * XXX FIXME: probably does not belong here
+ * Bluetooth version of struct sockaddr for L2CAP sockets (RAW and SEQPACKET)
+ */
+
+struct sockaddr_l2cap {
+ u_char l2cap_len; /* total length */
+ u_char l2cap_family; /* address family */
+ u_int16_t l2cap_psm; /* PSM (Protocol/Service Multiplexor) */
+ bdaddr_t l2cap_bdaddr; /* address */
+};
+
+/* L2CAP socket options */
+#define SOL_L2CAP 0x1609 /* socket option level */
+
+#define SO_L2CAP_IMTU 1 /* get/set incoming MTU */
+#define SO_L2CAP_OMTU 2 /* get outgoing (peer incoming) MTU */
+#define SO_L2CAP_IFLOW 3 /* get incoming flow spec. */
+#define SO_L2CAP_OFLOW 4 /* get/set outgoing flow spec. */
+#define SO_L2CAP_FLUSH 5 /* get/set flush timeout */
+
+/*
+ * Raw L2CAP sockets ioctl's
+ */
+
+/* Ping */
+struct ng_btsocket_l2cap_raw_ping {
+ bdaddr_t bdaddr[2];
+#define echo_src bdaddr[0]
+#define echo_dst bdaddr[1]
+ u_int32_t result;
+ u_int32_t echo_size;
+ u_int8_t *echo_data;
+};
+#define SIOC_L2CAP_L2CA_PING \
+ _IOWR('b', NGM_L2CAP_L2CA_PING, \
+ struct ng_btsocket_l2cap_raw_ping)
+
+/* Get info */
+struct ng_btsocket_l2cap_raw_get_info {
+ bdaddr_t bdaddr[2];
+#define info_src bdaddr[0]
+#define info_dst bdaddr[1]
+ u_int32_t result;
+ u_int32_t info_type;
+ u_int32_t info_size;
+ u_int8_t *info_data;
+};
+#define SIOC_L2CAP_L2CA_GET_INFO \
+ _IOWR('b', NGM_L2CAP_L2CA_GET_INFO, \
+ struct ng_btsocket_l2cap_raw_get_info)
+
+/* Get flags */
+struct ng_btsocket_l2cap_raw_node_flags {
+ bdaddr_t src;
+ ng_l2cap_node_flags_ep flags;
+};
+#define SIOC_L2CAP_NODE_GET_FLAGS \
+ _IOWR('b', NGM_L2CAP_NODE_GET_FLAGS, \
+ struct ng_btsocket_l2cap_raw_node_flags)
+
+/* Get/Set debug level */
+struct ng_btsocket_l2cap_raw_node_debug {
+ bdaddr_t src;
+ ng_l2cap_node_debug_ep debug;
+};
+#define SIOC_L2CAP_NODE_GET_DEBUG \
+ _IOWR('b', NGM_L2CAP_NODE_GET_DEBUG, \
+ struct ng_btsocket_l2cap_raw_node_debug)
+#define SIOC_L2CAP_NODE_SET_DEBUG \
+ _IOWR('b', NGM_L2CAP_NODE_SET_DEBUG, \
+ struct ng_btsocket_l2cap_raw_node_debug)
+
+/* Get connection list */
+struct ng_btsocket_l2cap_raw_con_list {
+ bdaddr_t src;
+ u_int32_t num_connections;
+ ng_l2cap_node_con_ep *connections;
+};
+#define SIOC_L2CAP_NODE_GET_CON_LIST \
+ _IOWR('b', NGM_L2CAP_NODE_GET_CON_LIST, \
+ struct ng_btsocket_l2cap_raw_con_list)
+
+/* Get channel list */
+struct ng_btsocket_l2cap_raw_chan_list {
+ bdaddr_t src;
+ u_int32_t num_channels;
+ ng_l2cap_node_chan_ep *channels;
+};
+#define SIOC_L2CAP_NODE_GET_CHAN_LIST \
+ _IOWR('b', NGM_L2CAP_NODE_GET_CHAN_LIST, \
+ struct ng_btsocket_l2cap_raw_chan_list)
+
+/*
+ * Netgraph node type name and cookie
+ */
+
+#define NG_BTSOCKET_HCI_RAW_NODE_TYPE "btsock_hci_raw"
+#define NG_BTSOCKET_L2CAP_RAW_NODE_TYPE "btsock_l2c_raw"
+#define NG_BTSOCKET_L2CAP_NODE_TYPE "btsock_l2c"
+
+/*
+ * Debug levels
+ */
+
+#define NG_BTSOCKET_ALERT_LEVEL 1
+#define NG_BTSOCKET_ERR_LEVEL 2
+#define NG_BTSOCKET_WARN_LEVEL 3
+#define NG_BTSOCKET_INFO_LEVEL 4
+
+#endif /* _NETGRAPH_BTSOCKET_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h b/sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h
new file mode 100644
index 0000000..399e8ac
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h
@@ -0,0 +1,85 @@
+/*
+ * ng_btsocket_hci_raw.h
+ *
+ * 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_btsocket_hci_raw.h,v 1.2 2002/09/16 19:46:02 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_BTSOCKET_HCI_RAW_H_
+#define _NETGRAPH_BTSOCKET_HCI_RAW_H_ 1
+
+#define NG_BTSOCKET_HCI_RAW_SENDSPACE (4 * 1024)
+#define NG_BTSOCKET_HCI_RAW_RECVSPACE (4 * 1024)
+
+/*
+ * Bluetooth raw HCI socket PCB
+ */
+
+struct ng_btsocket_hci_raw_pcb {
+ struct socket *so; /* socket */
+ int flags; /* flags */
+#define NG_BTSOCKET_HCI_RAW_DIRECTION (1 << 0)
+ struct sockaddr_hci addr; /* local address */
+ struct ng_btsocket_hci_raw_filter filter; /* filter */
+ u_int32_t token; /* message token */
+ struct ng_mesg *msg; /* message */
+ LIST_ENTRY(ng_btsocket_hci_raw_pcb) next; /* link to next */
+};
+typedef struct ng_btsocket_hci_raw_pcb ng_btsocket_hci_raw_pcb_t;
+typedef struct ng_btsocket_hci_raw_pcb * ng_btsocket_hci_raw_pcb_p;
+
+#define so2hci_raw_pcb(so) \
+ ((struct ng_btsocket_hci_raw_pcb *)((so)->so_pcb))
+
+/*
+ * Bluetooth raw HCI socket methods
+ */
+
+#ifdef _KERNEL
+
+void ng_btsocket_hci_raw_init (void);
+int ng_btsocket_hci_raw_abort (struct socket *);
+int ng_btsocket_hci_raw_attach (struct socket *, int, struct thread *);
+int ng_btsocket_hci_raw_bind (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_hci_raw_connect (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_hci_raw_control (struct socket *, u_long, caddr_t,
+ struct ifnet *, struct thread *);
+int ng_btsocket_hci_raw_ctloutput (struct socket *, struct sockopt *);
+int ng_btsocket_hci_raw_detach (struct socket *);
+int ng_btsocket_hci_raw_disconnect (struct socket *);
+int ng_btsocket_hci_raw_peeraddr (struct socket *, struct sockaddr **);
+int ng_btsocket_hci_raw_send (struct socket *, int, struct mbuf *,
+ struct sockaddr *, struct mbuf *,
+ struct thread *);
+int ng_btsocket_hci_raw_sockaddr (struct socket *, struct sockaddr **);
+
+#endif /* _KERNEL */
+
+#endif /* ndef _NETGRAPH_BTSOCKET_HCI_RAW_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h b/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h
new file mode 100644
index 0000000..cfae52e
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h
@@ -0,0 +1,200 @@
+/*
+ * ng_btsocket_l2cap.h
+ *
+ * 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_btsocket_l2cap.h,v 1.3 2002/09/22 18:23:31 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_BTSOCKET_L2CAP_H_
+#define _NETGRAPH_BTSOCKET_L2CAP_H_ 1
+
+/*
+ * L2CAP routing entry
+ */
+
+struct ng_hook;
+struct ng_message;
+
+struct ng_btsocket_l2cap_rtentry {
+ bdaddr_t src; /* source BD_ADDR */
+ struct ng_hook *hook; /* downstream hook */
+ LIST_ENTRY(ng_btsocket_l2cap_rtentry) next; /* linkt to next */
+};
+typedef struct ng_btsocket_l2cap_rtentry ng_btsocket_l2cap_rtentry_t;
+typedef struct ng_btsocket_l2cap_rtentry * ng_btsocket_l2cap_rtentry_p;
+
+/*****************************************************************************
+ *****************************************************************************
+ ** SOCK_RAW L2CAP sockets **
+ *****************************************************************************
+ *****************************************************************************/
+
+#define NG_BTSOCKET_L2CAP_RAW_SENDSPACE NG_L2CAP_MTU_DEFAULT
+#define NG_BTSOCKET_L2CAP_RAW_RECVSPACE NG_L2CAP_MTU_DEFAULT
+
+/*
+ * Bluetooth raw L2CAP socket PCB
+ */
+
+struct ng_btsocket_l2cap_raw_pcb {
+ struct socket *so; /* socket */
+
+ bdaddr_t src; /* source address */
+ ng_btsocket_l2cap_rtentry_p rt; /* routing info */
+
+ u_int32_t token; /* message token */
+ struct ng_mesg *msg; /* message */
+
+ LIST_ENTRY(ng_btsocket_l2cap_raw_pcb) next; /* link to next PCB */
+};
+typedef struct ng_btsocket_l2cap_raw_pcb ng_btsocket_l2cap_raw_pcb_t;
+typedef struct ng_btsocket_l2cap_raw_pcb * ng_btsocket_l2cap_raw_pcb_p;
+
+#define so2l2cap_raw_pcb(so) \
+ ((struct ng_btsocket_l2cap_raw_pcb *)((so)->so_pcb))
+
+/*
+ * Bluetooth raw L2CAP socket methods
+ */
+
+#ifdef _KERNEL
+
+void ng_btsocket_l2cap_raw_init (void);
+int ng_btsocket_l2cap_raw_abort (struct socket *);
+int ng_btsocket_l2cap_raw_attach (struct socket *, int, struct thread *);
+int ng_btsocket_l2cap_raw_bind (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_l2cap_raw_connect (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_l2cap_raw_control (struct socket *, u_long, caddr_t,
+ struct ifnet *, struct thread *);
+int ng_btsocket_l2cap_raw_detach (struct socket *);
+int ng_btsocket_l2cap_raw_disconnect (struct socket *);
+int ng_btsocket_l2cap_raw_peeraddr (struct socket *, struct sockaddr **);
+int ng_btsocket_l2cap_raw_send (struct socket *, int, struct mbuf *,
+ struct sockaddr *, struct mbuf *,
+ struct thread *);
+int ng_btsocket_l2cap_raw_sockaddr (struct socket *, struct sockaddr **);
+
+#endif /* _KERNEL */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** SOCK_SEQPACKET L2CAP sockets **
+ *****************************************************************************
+ *****************************************************************************/
+
+#define NG_BTSOCKET_L2CAP_SENDSPACE NG_L2CAP_MTU_DEFAULT /* (64 * 1024) */
+#define NG_BTSOCKET_L2CAP_RECVSPACE (64 * 1024)
+
+/*
+ * Bluetooth L2CAP socket PCB
+ */
+
+struct ng_btsocket_l2cap_pcb {
+ struct socket *so; /* Pointer to socket */
+
+ bdaddr_t src; /* Source address */
+ bdaddr_t dst; /* Destination address */
+
+ u_int16_t psm; /* PSM */
+ u_int16_t cid; /* Local channel ID */
+
+ u_int16_t flags; /* socket flags */
+#define NG_BTSOCKET_L2CAP_CLIENT (1 << 0) /* socket is client */
+#define NG_BTSOCKET_L2CAP_TIMO (1 << 1) /* timeout pending */
+
+ u_int8_t state; /* socket state */
+#define NG_BTSOCKET_L2CAP_CLOSED 0 /* socket closed */
+#define NG_BTSOCKET_L2CAP_CONNECTING 1 /* wait for connect */
+#define NG_BTSOCKET_L2CAP_CONFIGURING 2 /* wait for config */
+#define NG_BTSOCKET_L2CAP_OPEN 3 /* socket open */
+#define NG_BTSOCKET_L2CAP_DISCONNECTING 4 /* wait for disconnect */
+
+ u_int8_t cfg_state; /* config state */
+#define NG_BTSOCKET_L2CAP_CFG_IN (1 << 0) /* incoming path done */
+#define NG_BTSOCKET_L2CAP_CFG_OUT (1 << 1) /* outgoing path done */
+#define NG_BTSOCKET_L2CAP_CFG_BOTH \
+ (NG_BTSOCKET_L2CAP_CFG_IN | NG_BTSOCKET_L2CAP_CFG_OUT)
+
+#define NG_BTSOCKET_L2CAP_CFG_IN_SENT (1 << 2) /* L2CAP ConfigReq sent */
+#define NG_BTSOCKET_L2CAP_CFG_OUT_SENT (1 << 3) /* ---/--- */
+
+ u_int16_t imtu; /* Incoming MTU */
+ ng_l2cap_flow_t iflow; /* Input flow spec */
+
+ u_int16_t omtu; /* Outgoing MTU */
+ ng_l2cap_flow_t oflow; /* Outgoing flow spec */
+
+ u_int16_t flush_timo; /* flush timeout */
+ u_int16_t link_timo; /* link timeout */
+
+ struct callout_handle timo; /* timeout */
+
+ u_int32_t token; /* message token */
+ ng_btsocket_l2cap_rtentry_p rt; /* routing info */
+
+ struct mtx pcb_mtx; /* pcb mutex */
+
+ LIST_ENTRY(ng_btsocket_l2cap_pcb) next; /* link to next PCB */
+};
+typedef struct ng_btsocket_l2cap_pcb ng_btsocket_l2cap_pcb_t;
+typedef struct ng_btsocket_l2cap_pcb * ng_btsocket_l2cap_pcb_p;
+
+#define so2l2cap_pcb(so) \
+ ((struct ng_btsocket_l2cap_pcb *)((so)->so_pcb))
+
+/*
+ * Bluetooth L2CAP socket methods
+ */
+
+#ifdef _KERNEL
+
+void ng_btsocket_l2cap_init (void);
+int ng_btsocket_l2cap_abort (struct socket *);
+int ng_btsocket_l2cap_accept (struct socket *, struct sockaddr **);
+int ng_btsocket_l2cap_attach (struct socket *, int, struct thread *);
+int ng_btsocket_l2cap_bind (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_l2cap_connect (struct socket *, struct sockaddr *,
+ struct thread *);
+int ng_btsocket_l2cap_control (struct socket *, u_long, caddr_t,
+ struct ifnet *, struct thread *);
+int ng_btsocket_l2cap_ctloutput (struct socket *, struct sockopt *);
+int ng_btsocket_l2cap_detach (struct socket *);
+int ng_btsocket_l2cap_disconnect (struct socket *);
+int ng_btsocket_l2cap_listen (struct socket *, struct thread *);
+int ng_btsocket_l2cap_peeraddr (struct socket *, struct sockaddr **);
+int ng_btsocket_l2cap_send (struct socket *, int, struct mbuf *,
+ struct sockaddr *, struct mbuf *,
+ struct thread *);
+int ng_btsocket_l2cap_sockaddr (struct socket *, struct sockaddr **);
+
+#endif /* _KERNEL */
+
+#endif /* _NETGRAPH_BTSOCKET_L2CAP_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_h4.h b/sys/netgraph/bluetooth/include/ng_h4.h
new file mode 100644
index 0000000..8f6f849
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_h4.h
@@ -0,0 +1,118 @@
+/*
+ * ng_h4.h
+ *
+ * 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_h4.h,v 1.5 2002/06/27 09:50:18 max Exp $
+ * $FreeBSD$
+ *
+ * Based on:
+ * ---------
+ *
+ * FreeBSD: src/sys/netgraph/ng_tty.h
+ * Author: Archie Cobbs <archie@freebsd.org>
+ */
+
+/*
+ * This file contains everything that application needs to know about
+ * Bluetooth HCI UART transport layer as per chapter H4 of the Bluetooth
+ * Specification Book v1.1.
+ *
+ * This file can be included by both kernel and userland applications.
+ */
+
+#ifndef _NETGRAPH_H4_H_
+#define _NETGRAPH_H4_H_ 1
+
+/*
+ * H4 line discipline
+ * XXX FIXME: does not belong here. Move to "ttycom.h" later
+ */
+
+#define H4DISC 7
+
+/**************************************************************************
+ **************************************************************************
+ ** Netgraph node hook name, type name and type cookie and commands
+ **************************************************************************
+ **************************************************************************/
+
+/* Hook name */
+#define NG_H4_HOOK "hook"
+
+/* Node type name and magic cookie */
+#define NG_H4_NODE_TYPE "h4"
+#define NGM_H4_COOKIE 1013899512
+
+/* Node states */
+#define NG_H4_W4_PKT_IND 1 /* Waiting for packet indicator */
+#define NG_H4_W4_PKT_HDR 2 /* Waiting for packet header */
+#define NG_H4_W4_PKT_DATA 3 /* Waiting for packet data */
+
+/* Debug levels */
+#define NG_H4_ALERT_LEVEL 1
+#define NG_H4_ERR_LEVEL 2
+#define NG_H4_WARN_LEVEL 3
+#define NG_H4_INFO_LEVEL 4
+
+/**************************************************************************
+ **************************************************************************
+ ** H4 node command/event parameters
+ **************************************************************************
+ **************************************************************************/
+
+/* Reset node */
+#define NGM_H4_NODE_RESET 1
+
+/* Get node state (see states above) */
+#define NGM_H4_NODE_GET_STATE 2
+typedef u_int16_t ng_h4_node_state_ep;
+
+/* Get/Set node debug level (see levels above) */
+#define NGM_H4_NODE_GET_DEBUG 3
+#define NGM_H4_NODE_SET_DEBUG 4
+typedef u_int16_t ng_h4_node_debug_ep;
+
+/* Get/Set max queue length for the node */
+#define NGM_H4_NODE_GET_QLEN 5
+#define NGM_H4_NODE_SET_QLEN 6
+typedef int32_t ng_h4_node_qlen_ep;
+
+/* Get node statistic */
+#define NGM_H4_NODE_GET_STAT 7
+typedef struct {
+ u_int32_t pckts_recv; /* # of packets received */
+ u_int32_t bytes_recv; /* # of bytes received */
+ u_int32_t pckts_sent; /* # of packets sent */
+ u_int32_t bytes_sent; /* # of bytes sent */
+ u_int32_t oerrors; /* # of output errors */
+ u_int32_t ierrors; /* # of input errors */
+} ng_h4_node_stat_ep;
+
+/* Reset node statistic */
+#define NGM_H4_NODE_RESET_STAT 8
+
+#endif /* _NETGRAPH_H4_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_hci.h b/sys/netgraph/bluetooth/include/ng_hci.h
new file mode 100644
index 0000000..c4b1aba
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_hci.h
@@ -0,0 +1,1651 @@
+/*
+ * ng_hci.h
+ *
+ * Copyright (c) 2001 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_hci.h,v 1.13 2002/11/12 22:35:39 max Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains everything that application needs to know about
+ * Host Controller Interface (HCI). All information was obtained from
+ * Bluetooth Specification Book v1.1.
+ *
+ * This file can be included by both kernel and userland applications.
+ *
+ * NOTE: Here and after Bluetooth device is called a "unit". Bluetooth
+ * specification refers to both devices and units. They are the
+ * same thing (i think), so to be consistent word "unit" will be
+ * used.
+ */
+
+#ifndef _NETGRAPH_HCI_H_
+#define _NETGRAPH_HCI_H_ 1
+
+/**************************************************************************
+ **************************************************************************
+ ** Netgraph node hook name, type name and type cookie and commands
+ **************************************************************************
+ **************************************************************************/
+
+/* Node type name and type cookie */
+#define NG_HCI_NODE_TYPE "hci"
+#define NGM_HCI_COOKIE 1000774184
+
+/* Netgraph node hook names */
+#define NG_HCI_HOOK_DRV "drv" /* Driver <-> HCI */
+#define NG_HCI_HOOK_ACL "acl" /* HCI <-> Upper */
+#define NG_HCI_HOOK_SCO "sco" /* HCI <-> Upper */
+#define NG_HCI_HOOK_RAW "raw" /* HCI <-> Upper */
+
+/**************************************************************************
+ **************************************************************************
+ ** Common defines and types (HCI)
+ **************************************************************************
+ **************************************************************************/
+
+/* All sizes are in bytes */
+#define NG_HCI_BDADDR_SIZE 6 /* unit address */
+#define NG_HCI_LAP_SIZE 3 /* unit LAP */
+#define NG_HCI_KEY_SIZE 16 /* link key */
+#define NG_HCI_PIN_SIZE 16 /* link PIN */
+#define NG_HCI_EVENT_MASK_SIZE 8 /* event mask */
+#define NG_HCI_CLASS_SIZE 3 /* unit class */
+#define NG_HCI_FEATURES_SIZE 8 /* LMP features */
+#define NG_HCI_UNIT_NAME_SIZE 248 /* unit name size */
+
+/* HCI specification */
+#define NG_HCI_SPEC_V10 0x00 /* v1.0 */
+#define NG_HCI_SPEC_V11 0x01 /* v1.1 */
+/* 0x02 - 0xFF - reserved for future use */
+
+/* LMP features */
+/* ------------------- byte 0 --------------------*/
+#define NG_HCI_LMP_3SLOT 0x01
+#define NG_HCI_LMP_5SLOT 0x02
+#define NG_HCI_LMP_ENCRYPTION 0x04
+#define NG_HCI_LMP_SLOT_OFFSET 0x08
+#define NG_HCI_LMP_TIMING_ACCURACY 0x10
+#define NG_HCI_LMP_SWITCH 0x20
+#define NG_HCI_LMP_HOLD_MODE 0x40
+#define NG_HCI_LMP_SNIFF_MODE 0x80
+/* ------------------- byte 1 --------------------*/
+#define NG_HCI_LMP_PARK_MODE 0x01
+#define NG_HCI_LMP_RSSI 0x02
+#define NG_HCI_LMP_CHANNEL_QUALITY 0x04
+#define NG_HCI_LMP_SCO_LINK 0x08
+#define NG_HCI_LMP_HV2_PKT 0x10
+#define NG_HCI_LMP_HV3_PKT 0x20
+#define NG_HCI_LMP_ULAW_LOG 0x40
+#define NG_HCI_LMP_ALAW_LOG 0x80
+/* ------------------- byte 2 --------------------*/
+#define NG_HCI_LMP_CVSD 0x01
+#define NG_HCI_LMP_PAGING_SCHEME 0x02
+#define NG_HCI_LMP_POWER_CONTROL 0x04
+#define NG_HCI_LMP_TRANSPARENT_SCO 0x08
+#define NG_HCI_LMP_FLOW_CONTROL_LAG0 0x10
+#define NG_HCI_LMP_FLOW_CONTROL_LAG1 0x20
+#define NG_HCI_LMP_FLOW_CONTROL_LAG2 0x40
+
+/* Link types */
+#define NG_HCI_LINK_SCO 0x00 /* Voice */
+#define NG_HCI_LINK_ACL 0x01 /* Data */
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Packet types */
+ /* 0x0001 - 0x0004 - reserved for future use */
+#define NG_HCI_PKT_DM1 0x0008 /* ACL link */
+#define NG_HCI_PKT_DH1 0x0010 /* ACL link */
+#define NG_HCI_PKT_HV1 0x0020 /* SCO link */
+#define NG_HCI_PKT_HV2 0x0040 /* SCO link */
+#define NG_HCI_PKT_HV3 0x0080 /* SCO link */
+ /* 0x0100 - 0x0200 - reserved for future use */
+#define NG_HCI_PKT_DM3 0x0400 /* ACL link */
+#define NG_HCI_PKT_DH3 0x0800 /* ACL link */
+ /* 0x1000 - 0x2000 - reserved for future use */
+#define NG_HCI_PKT_DM5 0x4000 /* ACL link */
+#define NG_HCI_PKT_DH5 0x8000 /* ACL link */
+
+/*
+ * Connection modes/Unit modes
+ *
+ * This is confusing. It means that one of the units change its mode
+ * for the specific connection. For example one connection was put on
+ * hold (but i could be wrong :)
+ */
+
+#define NG_HCI_UNIT_MODE_ACTIVE 0x00
+#define NG_HCI_UNIT_MODE_HOLD 0x01
+#define NG_HCI_UNIT_MODE_SNIFF 0x02
+#define NG_HCI_UNIT_MODE_PARK 0x03
+/* 0x04 - 0xFF - reserved for future use */
+
+/* Page scan modes */
+#define NG_HCI_MANDATORY_PAGE_SCAN_MODE 0x00
+#define NG_HCI_OPTIONAL_PAGE_SCAN_MODE1 0x01
+#define NG_HCI_OPTIONAL_PAGE_SCAN_MODE2 0x02
+#define NG_HCI_OPTIONAL_PAGE_SCAN_MODE3 0x03
+/* 0x04 - 0xFF - reserved for future use */
+
+/* Page scan repetition modes */
+#define NG_HCI_SCAN_REP_MODE0 0x00
+#define NG_HCI_SCAN_REP_MODE1 0x01
+#define NG_HCI_SCAN_REP_MODE2 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Page scan period modes */
+#define NG_HCI_PAGE_SCAN_PERIOD_MODE0 0x00
+#define NG_HCI_PAGE_SCAN_PERIOD_MODE1 0x01
+#define NG_HCI_PAGE_SCAN_PERIOD_MODE2 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Scan enable */
+#define NG_HCI_NO_SCAN_ENABLE 0x00
+#define NG_HCI_INQUIRY_ENABLE_PAGE_DISABLE 0x01
+#define NG_HCI_INQUIRY_DISABLE_PAGE_ENABLE 0x02
+#define NG_HCI_INQUIRY_ENABLE_PAGE_ENABLE 0x03
+/* 0x04 - 0xFF - reserved for future use */
+
+/* Hold mode activities */
+#define NG_HCI_HOLD_MODE_NO_CHANGE 0x00
+#define NG_HCI_HOLD_MODE_SUSPEND_PAGE_SCAN 0x01
+#define NG_HCI_HOLD_MODE_SUSPEND_INQUIRY_SCAN 0x02
+#define NG_HCI_HOLD_MODE_SUSPEND_PERIOD_INQUIRY 0x04
+/* 0x08 - 0x80 - reserved for future use */
+
+/* Connection roles */
+#define NG_HCI_ROLE_MASTER 0x00
+#define NG_HCI_ROLE_SLAVE 0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Key flags */
+#define NG_HCI_USE_SEMI_PERMANENT_LINK_KEYS 0x00
+#define NG_HCI_USE_TEMPORARY_LINK_KEY 0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Pin types */
+#define NG_HCI_PIN_TYPE_VARIABLE 0x00
+#define NG_HCI_PIN_TYPE_FIXED 0x01
+
+/* Link key types */
+#define NG_HCI_LINK_KEY_TYPE_COMBINATION_KEY 0x00
+#define NG_HCI_LINK_KEY_TYPE_LOCAL_UNIT_KEY 0x01
+#define NG_HCI_LINK_KEY_TYPE_REMOTE_UNIT_KEY 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Encryption modes */
+#define NG_HCI_ENCRYPTION_MODE_NONE 0x00
+#define NG_HCI_ENCRYPTION_MODE_P2P 0x01
+#define NG_HCI_ENCRYPTION_MODE_ALL 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Quality of service types */
+#define NG_HCI_SERVICE_TYPE_NO_TRAFFIC 0x00
+#define NG_HCI_SERVICE_TYPE_BEST_EFFORT 0x01
+#define NG_HCI_SERVICE_TYPE_GUARANTEED 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Link policy settings */
+#define NG_HCI_LINK_POLICY_DISABLE_ALL_LM_MODES 0x0000
+#define NG_HCI_LINK_POLICY_ENABLE_ROLE_SWITCH 0x0001 /* Master/Slave switch */
+#define NG_HCI_LINK_POLICY_ENABLE_HOLD_MODE 0x0002
+#define NG_HCI_LINK_POLICY_ENABLE_SNIFF_MODE 0x0004
+#define NG_HCI_LINK_POLICY_ENABLE_PARK_MODE 0x0008
+/* 0x0010 - 0x8000 - reserved for future use */
+
+/* Event masks */
+#define NG_HCI_EVMSK_ALL 0x00000000ffffffff
+#define NG_HCI_EVMSK_NONE 0x0000000000000000
+#define NG_HCI_EVMSK_INQUIRY_COMPL 0x0000000000000001
+#define NG_HCI_EVMSK_INQUIRY_RESULT 0x0000000000000002
+#define NG_HCI_EVMSK_CON_COMPL 0x0000000000000004
+#define NG_HCI_EVMSK_CON_REQ 0x0000000000000008
+#define NG_HCI_EVMSK_DISCON_COMPL 0x0000000000000010
+#define NG_HCI_EVMSK_AUTH_COMPL 0x0000000000000020
+#define NG_HCI_EVMSK_REMOTE_NAME_REQ_COMPL 0x0000000000000040
+#define NG_HCI_EVMSK_ENCRYPTION_CHANGE 0x0000000000000080
+#define NG_HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL 0x0000000000000100
+#define NG_HCI_EVMSK_MASTER_LINK_KEY_COMPL 0x0000000000000200
+#define NG_HCI_EVMSK_READ_REMOTE_FEATURES_COMPL 0x0000000000000400
+#define NG_HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL 0x0000000000000800
+#define NG_HCI_EVMSK_QOS_SETUP_COMPL 0x0000000000001000
+#define NG_HCI_EVMSK_COMMAND_COMPL 0x0000000000002000
+#define NG_HCI_EVMSK_COMMAND_STATUS 0x0000000000004000
+#define NG_HCI_EVMSK_HARDWARE_ERROR 0x0000000000008000
+#define NG_HCI_EVMSK_FLUSH_OCCUR 0x0000000000010000
+#define NG_HCI_EVMSK_ROLE_CHANGE 0x0000000000020000
+#define NG_HCI_EVMSK_NUM_COMPL_PKTS 0x0000000000040000
+#define NG_HCI_EVMSK_MODE_CHANGE 0x0000000000080000
+#define NG_HCI_EVMSK_RETURN_LINK_KEYS 0x0000000000100000
+#define NG_HCI_EVMSK_PIN_CODE_REQ 0x0000000000200000
+#define NG_HCI_EVMSK_LINK_KEY_REQ 0x0000000000400000
+#define NG_HCI_EVMSK_LINK_KEY_NOTIFICATION 0x0000000000800000
+#define NG_HCI_EVMSK_LOOPBACK_COMMAND 0x0000000001000000
+#define NG_HCI_EVMSK_DATA_BUFFER_OVERFLOW 0x0000000002000000
+#define NG_HCI_EVMSK_MAX_SLOT_CHANGE 0x0000000004000000
+#define NG_HCI_EVMSK_READ_CLOCK_OFFSET_COMLETE 0x0000000008000000
+#define NG_HCI_EVMSK_CON_PKT_TYPE_CHANGED 0x0000000010000000
+#define NG_HCI_EVMSK_QOS_VIOLATION 0x0000000020000000
+#define NG_HCI_EVMSK_PAGE_SCAN_MODE_CHANGE 0x0000000040000000
+#define NG_HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE 0x0000000080000000
+/* 0x0000000100000000 - 0x8000000000000000 - reserved for future use */
+
+/* Filter types */
+#define NG_HCI_FILTER_TYPE_NONE 0x00
+#define NG_HCI_FILTER_TYPE_INQUIRY_RESULT 0x01
+#define NG_HCI_FILTER_TYPE_CON_SETUP 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Filter condition types for NG_HCI_FILTER_TYPE_INQUIRY_RESULT */
+#define NG_HCI_FILTER_COND_INQUIRY_NEW_UNIT 0x00
+#define NG_HCI_FILTER_COND_INQUIRY_UNIT_CLASS 0x01
+#define NG_HCI_FILTER_COND_INQUIRY_BDADDR 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Filter condition types for NG_HCI_FILTER_TYPE_CON_SETUP */
+#define NG_HCI_FILTER_COND_CON_ANY_UNIT 0x00
+#define NG_HCI_FILTER_COND_CON_UNIT_CLASS 0x01
+#define NG_HCI_FILTER_COND_CON_BDADDR 0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Xmit level types */
+#define NG_HCI_XMIT_LEVEL_CURRENT 0x00
+#define NG_HCI_XMIT_LEVEL_MAXIMUM 0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Host to Host Controller flow control */
+#define NG_HCI_H2HC_FLOW_CONTROL_NONE 0x00
+#define NG_HCI_H2HC_FLOW_CONTROL_ACL 0x01
+#define NG_HCI_H2HC_FLOW_CONTROL_SCO 0x02
+#define NG_HCI_H2HC_FLOW_CONTROL_BOTH 0x03 /* ACL and SCO */
+/* 0x04 - 0xFF - reserved future use */
+
+/* Country codes */
+#define NG_HCI_COUNTRY_CODE_NAM_EUR_JP 0x00
+#define NG_HCI_COUNTRY_CODE_FRANCE 0x01
+/* 0x02 - 0xFF - reserved future use */
+
+/* Loopback modes */
+#define NG_HCI_LOOPBACK_NONE 0x00
+#define NG_HCI_LOOPBACK_LOCAL 0x01
+#define NG_HCI_LOOPBACK_REMOTE 0x02
+/* 0x03 - 0xFF - reserved future use */
+
+/**************************************************************************
+ **************************************************************************
+ ** Link level defines, headers and types
+ **************************************************************************
+ **************************************************************************/
+
+/*
+ * Macro(s) to combine OpCode and extract OGF (OpCode Group Field)
+ * and OCF (OpCode Command Field) from OpCode.
+ */
+
+#define NG_HCI_OPCODE(gf,cf) ((((gf) & 0x3f) << 10) | ((cf) & 0x3ff))
+#define NG_HCI_OCF(op) ((op) & 0x3ff)
+#define NG_HCI_OGF(op) (((op) >> 10) & 0x3f)
+
+/*
+ * Marco(s) to extract/combine connection handle, BC (Broadcast) and
+ * PB (Packet boundary) flags.
+ */
+
+#define NG_HCI_CON_HANDLE(h) ((h) & 0x0fff)
+#define NG_HCI_PB_FLAG(h) (((h) & 0x3000) >> 12)
+#define NG_HCI_BC_FLAG(h) (((h) & 0xc000) >> 14)
+#define NG_HCI_MK_CON_HANDLE(h, pb, bc) \
+ (((h) & 0x0fff) | (((pb) & 3) << 12) | (((bc) & 3) << 14))
+
+/* PB flag values */
+ /* 00 - reserved for future use */
+#define NG_HCI_PACKET_FRAGMENT 0x1
+#define NG_HCI_PACKET_START 0x2
+ /* 11 - reserved for future use */
+
+/* BC flag values */
+#define NG_HCI_POINT2POINT 0x0 /* only Host controller to Host */
+#define NG_HCI_BROADCAST_ACTIVE 0x1 /* both directions */
+#define NG_HCI_BROADCAST_PICONET 0x2 /* both directions */
+ /* 11 - reserved for future use */
+
+/* HCI command packet header */
+#define NG_HCI_CMD_PKT 0x01
+#define NG_HCI_CMD_PKT_SIZE 0xff /* without header */
+typedef struct {
+ u_int8_t type; /* MUST be 0x1 */
+ u_int16_t opcode; /* OpCode */
+ u_int8_t length; /* parameter(s) length in bytes */
+} __attribute__ ((packed)) ng_hci_cmd_pkt_t;
+
+/* ACL data packet header */
+#define NG_HCI_ACL_DATA_PKT 0x02
+#define NG_HCI_ACL_PKT_SIZE 0xffff /* without header */
+typedef struct {
+ u_int8_t type; /* MUST be 0x2 */
+ u_int16_t con_handle; /* connection handle + PB + BC flags */
+ u_int16_t length; /* payload length in bytes */
+} __attribute__ ((packed)) ng_hci_acldata_pkt_t;
+
+/* SCO data packet header */
+#define NG_HCI_SCO_DATA_PKT 0x03
+#define NG_HCI_SCO_PKT_SIZE 0xff /* without header */
+typedef struct {
+ u_int8_t type; /* MUST be 0x3 */
+ u_int16_t con_handle; /* connection handle + reserved bits */
+ u_int8_t length; /* payload length in bytes */
+} __attribute__ ((packed)) ng_hci_scodata_pkt_t;
+
+/* HCI event packet header */
+#define NG_HCI_EVENT_PKT 0x04
+#define NG_HCI_EVENT_PKT_SIZE 0xff /* without header */
+typedef struct {
+ u_int8_t type; /* MUST be 0x4 */
+ u_int8_t event; /* event */
+ u_int8_t length; /* parameter(s) length in bytes */
+} __attribute__ ((packed)) ng_hci_event_pkt_t;
+
+/* Bluetooth unit address */
+typedef struct {
+ u_int8_t b[NG_HCI_BDADDR_SIZE];
+} __attribute__ ((packed)) bdaddr_t;
+typedef bdaddr_t * bdaddr_p;
+
+/* Any BD_ADDR. Note: This is actually 7 bytes (count '\0' terminator) */
+#define NG_HCI_BDADDR_ANY ((bdaddr_p) "\000\000\000\000\000\000")
+
+/* HCI status return parameter */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+} __attribute__ ((packed)) ng_hci_status_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Upper layer protocol interface. LP_xxx event parameters
+ **************************************************************************
+ **************************************************************************/
+
+/* Connection Request Event */
+#define NGM_HCI_LP_CON_REQ 1 /* Upper -> HCI */
+typedef struct {
+ u_int16_t link_type; /* type of connection */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_hci_lp_con_req_ep;
+
+/*
+ * XXX XXX XXX
+ *
+ * NOTE: This request is not defined by Bluetooth specification,
+ * but i find it useful :)
+ */
+#define NGM_HCI_LP_DISCON_REQ 2 /* Upper -> HCI */
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t reason; /* reason to disconnect (only low byte) */
+} ng_hci_lp_discon_req_ep;
+
+/* Connection Confirmation Event */
+#define NGM_HCI_LP_CON_CFM 3 /* HCI -> Upper */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t link_type; /* link type */
+ u_int16_t con_handle; /* con_handle */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_hci_lp_con_cfm_ep;
+
+/* Connection Indication Event */
+#define NGM_HCI_LP_CON_IND 4 /* HCI -> Upper */
+typedef struct {
+ u_int8_t link_type; /* link type */
+ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_hci_lp_con_ind_ep;
+
+/* Connection Response Event */
+#define NGM_HCI_LP_CON_RSP 5 /* Upper -> HCI */
+typedef struct {
+ u_int8_t status; /* 0x00 - accept connection */
+ u_int8_t link_type; /* link type */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_hci_lp_con_rsp_ep;
+
+/* Disconnection Indication Event */
+#define NGM_HCI_LP_DISCON_IND 6 /* HCI -> Upper */
+typedef struct {
+ u_int8_t reason; /* reason to disconnect (only low byte) */
+ u_int8_t link_type; /* link type */
+ u_int16_t con_handle; /* connection handle */
+} ng_hci_lp_discon_ind_ep;
+
+/* QoS Setup Request Event */
+#define NGM_HCI_LP_QOS_REQ 7 /* Upper -> HCI */
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t flags; /* reserved */
+ u_int8_t service_type; /* service type */
+ u_int32_t token_rate; /* bytes/sec */
+ u_int32_t peak_bandwidth; /* bytes/sec */
+ u_int32_t latency; /* msec */
+ u_int32_t delay_variation; /* msec */
+} ng_hci_lp_qos_req_ep;
+
+/* QoS Conformition Event */
+#define NGM_HCI_LP_QOS_CFM 8 /* HCI -> Upper */
+typedef struct {
+ u_int16_t status; /* 0x00 - success (only low byte) */
+ u_int16_t con_handle; /* connection handle */
+} ng_hci_lp_qos_cfm_ep;
+
+/* QoS Violation Indication Event */
+#define NGM_HCI_LP_QOS_IND 9 /* HCI -> Upper */
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} ng_hci_lp_qos_ind_ep;
+
+/**************************************************************************
+ **************************************************************************
+ ** HCI node command/event parameters
+ **************************************************************************
+ **************************************************************************/
+
+/* Debug levels */
+#define NG_HCI_ALERT_LEVEL 1
+#define NG_HCI_ERR_LEVEL 2
+#define NG_HCI_WARN_LEVEL 3
+#define NG_HCI_INFO_LEVEL 4
+
+/* Unit states */
+#define NG_HCI_UNIT_CONNECTED (1 << 0)
+#define NG_HCI_UNIT_INITED (1 << 1)
+#define NG_HCI_UNIT_READY (NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED)
+#define NG_HCI_UNIT_COMMAND_PENDING (1 << 2)
+
+/* Connection state */
+#define NG_HCI_CON_CLOSED 0 /* connection closed */
+#define NG_HCI_CON_W4_LP_CON_RSP 1 /* wait for LP_ConnectRsp */
+#define NG_HCI_CON_W4_CONN_COMPLETE 2 /* wait for Connection_Complete evt */
+#define NG_HCI_CON_OPEN 3 /* connection open */
+
+/* Get HCI node (unit) state (see states above) */
+#define NGM_HCI_NODE_GET_STATE 100 /* HCI -> User */
+typedef u_int16_t ng_hci_node_state_ep;
+
+/* Turn on "inited" bit */
+#define NGM_HCI_NODE_INIT 101 /* User -> HCI */
+/* No parameters */
+
+/* Get/Set node debug level (see debug levels above) */
+#define NGM_HCI_NODE_GET_DEBUG 102 /* HCI -> User */
+#define NGM_HCI_NODE_SET_DEBUG 103 /* User -> HCI */
+typedef u_int16_t ng_hci_node_debug_ep;
+
+/* Get node buffer info */
+#define NGM_HCI_NODE_GET_BUFFER 104 /* HCI -> User */
+typedef struct {
+ u_int8_t cmd_free; /* number of free command packets */
+ u_int8_t sco_size; /* max. size of SCO packet */
+ u_int16_t sco_pkts; /* number of SCO packets */
+ u_int16_t sco_free; /* number of free SCO packets */
+ u_int16_t acl_size; /* max. size of ACL packet */
+ u_int16_t acl_pkts; /* number of ACL packets */
+ u_int16_t acl_free; /* number of free ACL packets */
+} ng_hci_node_buffer_ep;
+
+/* Get BDADDR */
+#define NGM_HCI_NODE_GET_BDADDR 105 /* HCI -> User */
+/* bdaddr_t -- BDADDR */
+
+/* Get features */
+#define NGM_HCI_NODE_GET_FEATURES 106 /* HCI -> User */
+/* features[NG_HCI_FEATURES_SIZE] -- features */
+
+#define NGM_HCI_NODE_GET_STAT 107 /* HCI -> User */
+typedef struct {
+ u_int32_t cmd_sent; /* number of HCI commands sent */
+ u_int32_t evnt_recv; /* number of HCI events received */
+ u_int32_t acl_recv; /* number of ACL packets received */
+ u_int32_t acl_sent; /* number of ACL packets sent */
+ u_int32_t sco_recv; /* number of SCO packets received */
+ u_int32_t sco_sent; /* number of SCO packets sent */
+ u_int32_t bytes_recv; /* total number of bytes received */
+ u_int32_t bytes_sent; /* total number of bytes sent */
+} ng_hci_node_stat_ep;
+
+#define NGM_HCI_NODE_RESET_STAT 108 /* User -> HCI */
+/* No parameters */
+
+#define NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE 109 /* User -> HCI */
+
+#define NGM_HCI_NODE_GET_NEIGHBOR_CACHE 110 /* HCI -> User */
+typedef struct {
+ u_int32_t num_entries; /* number of entries */
+} ng_hci_node_get_neighbor_cache_ep;
+
+typedef struct {
+ u_int16_t page_scan_rep_mode; /* page rep scan mode */
+ u_int16_t page_scan_mode; /* page scan mode */
+ u_int16_t clock_offset; /* clock offset */
+ bdaddr_t bdaddr; /* bdaddr */
+ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* features */
+} ng_hci_node_neighbor_cache_entry_ep;
+
+#define NG_HCI_MAX_NEIGHBOR_NUM \
+ ((0xffff - sizeof(ng_hci_node_get_neighbor_cache_ep))/sizeof(ng_hci_node_neighbor_cache_entry_ep))
+
+#define NGM_HCI_NODE_GET_CON_LIST 111 /* HCI -> User */
+typedef struct {
+ u_int32_t num_connections; /* number of connections */
+} ng_hci_node_con_list_ep;
+
+typedef struct {
+ u_int8_t link_type; /* ACL or SCO */
+ u_int8_t encryption_mode; /* none, p2p, ... */
+ u_int8_t mode; /* ACTIVE, HOLD ... */
+ u_int8_t role; /* MASTER/SLAVE */
+ u_int16_t state; /* connection state */
+ u_int16_t reserved; /* place holder */
+ u_int16_t pending; /* number of pending packets */
+ u_int16_t queue_len; /* number of packets in queue */
+ u_int16_t con_handle; /* connection handle */
+ bdaddr_t bdaddr; /* remote bdaddr */
+} ng_hci_node_con_ep;
+
+#define NG_HCI_MAX_CON_NUM \
+ ((0xffff - sizeof(ng_hci_node_con_list_ep))/sizeof(ng_hci_node_con_ep))
+
+#define NGM_HCI_NODE_UP 112 /* HCI -> Upper */
+typedef struct {
+ u_int16_t pkt_size; /* max. ACL/SCO packet size (w/out header) */
+ u_int16_t num_pkts; /* ACL/SCO packet queue size */
+ u_int16_t reserved; /* place holder */
+ bdaddr_t bdaddr; /* bdaddr */
+} ng_hci_node_up_ep;
+
+#define NGM_HCI_SYNC_CON_QUEUE 113 /* HCI -> Upper */
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t completed; /* number of completed packets */
+} ng_hci_sync_con_queue_ep;
+
+#define NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK 114 /* HCI -> User */
+#define NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK 115 /* User -> HCI */
+typedef u_int16_t ng_hci_node_link_policy_mask_ep;
+
+#define NGM_HCI_NODE_GET_PACKET_MASK 116 /* HCI -> User */
+#define NGM_HCI_NODE_SET_PACKET_MASK 117 /* User -> HCI */
+typedef u_int16_t ng_hci_node_packet_mask_ep;
+
+/**************************************************************************
+ **************************************************************************
+ ** Link control commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_LINK_CONTROL 0x01 /* OpCode Group Field */
+
+#define NG_HCI_OCF_INQUIRY 0x0001
+typedef struct {
+ u_int8_t lap[NG_HCI_LAP_SIZE]; /* LAP */
+ u_int8_t inquiry_length; /* (N x 1.28) sec */
+ u_int8_t num_responses; /* Max. # of responses before halted */
+} __attribute__ ((packed)) ng_hci_inquiry_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_INQUIRY_CANCEL 0x0002
+/* No command parameter(s) */
+typedef ng_hci_status_rp ng_hci_inquiry_cancel_rp;
+
+#define NG_HCI_OCF_PERIODIC_INQUIRY 0x0003
+typedef struct {
+ u_int16_t max_period_length; /* Max. and min. amount of time */
+ u_int16_t min_period_length; /* between consecutive inquiries */
+ u_int8_t lap[NG_HCI_LAP_SIZE]; /* LAP */
+ u_int8_t inquiry_length; /* (inquiry_length * 1.28) sec */
+ u_int8_t num_responses; /* Max. # of responses */
+} __attribute__ ((packed)) ng_hci_periodic_inquiry_cp;
+
+typedef ng_hci_status_rp ng_hci_periodic_inquiry_rp;
+
+#define NG_HCI_OCF_EXIT_PERIODIC_INQUIRY 0x0004
+/* No command parameter(s) */
+typedef ng_hci_status_rp ng_hci_exit_periodic_inquiry_rp;
+
+#define NG_HCI_OCF_CREATE_CON 0x0005
+typedef struct {
+ bdaddr_t bdaddr; /* destination address */
+ u_int16_t pkt_type; /* packet type */
+ u_int8_t page_scan_rep_mode; /* page scan repetition mode */
+ u_int8_t page_scan_mode; /* page scan mode */
+ u_int16_t clock_offset; /* clock offset */
+ u_int8_t accept_role_switch; /* accept role switch? 0x00 - no */
+} __attribute__ ((packed)) ng_hci_create_con_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_DISCON 0x0006
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t reason; /* reason to disconnect */
+} __attribute__ ((packed)) ng_hci_discon_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_ADD_SCO_CON 0x0007
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t pkt_type; /* packet type */
+} __attribute__ ((packed)) ng_hci_add_sco_con_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_ACCEPT_CON 0x0009
+typedef struct {
+ bdaddr_t bdaddr; /* address of unit to be connected */
+ u_int8_t role; /* connection role */
+} __attribute__ ((packed)) ng_hci_accept_con_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_REJECT_CON 0x000a
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+ u_int8_t reason; /* reason to reject */
+} __attribute__ ((packed)) ng_hci_reject_con_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_LINK_KEY_REP 0x000b
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+ u_int8_t key[NG_HCI_KEY_SIZE]; /* key */
+} __attribute__ ((packed)) ng_hci_link_key_rep_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* unit address */
+} __attribute__ ((packed)) ng_hci_link_key_rep_rp;
+
+#define NG_HCI_OCF_LINK_KEY_NEG_REP 0x000c
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+} __attribute__ ((packed)) ng_hci_link_key_neg_rep_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* unit address */
+} __attribute__ ((packed)) ng_hci_link_key_neg_rep_rp;
+
+#define NG_HCI_OCF_PIN_CODE_REP 0x000d
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+ u_int8_t pin_size; /* pin code length (in bytes) */
+ u_int8_t pin[NG_HCI_PIN_SIZE]; /* pin code */
+} __attribute__ ((packed)) ng_hci_pin_code_rep_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* unit address */
+} __attribute__ ((packed)) ng_hci_pin_code_rep_rp;
+
+#define NG_HCI_OCF_PIN_CODE_NEG_REP 0x000e
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+} __attribute__ ((packed)) ng_hci_pin_code_neg_rep_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* unit address */
+} __attribute__ ((packed)) ng_hci_pin_code_neg_rep_rp;
+
+#define NG_HCI_OCF_CHANGE_CON_PKT_TYPE 0x000f
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t pkt_type; /* packet type */
+} __attribute__ ((packed)) ng_hci_change_con_pkt_type_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_AUTH_REQ 0x0011
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_auth_req_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_SET_CON_ENCRYPTION 0x0013
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t encryption_enable; /* 0x00 - disable, 0x01 - enable */
+} __attribute__ ((packed)) ng_hci_set_con_encryption_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_CHANGE_CON_LINK_KEY 0x0015
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_change_con_link_key_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_MASTER_LINK_KEY 0x0017
+typedef struct {
+ u_int8_t key_flag; /* key flag */
+} __attribute__ ((packed)) ng_hci_master_link_key_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_REMOTE_NAME_REQ 0x0019
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+ u_int8_t page_scan_rep_mode; /* page scan repetition mode */
+ u_int8_t page_scan_mode; /* page scan mode */
+ u_int16_t clock_offset; /* clock offset */
+} __attribute__ ((packed)) ng_hci_remote_name_req_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_READ_REMOTE_FEATURES 0x001b
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_remote_features_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_READ_REMOTE_VER_INFO 0x001d
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_remote_ver_info_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_READ_CLOCK_OFFSET 0x001f
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_clock_offset_cp;
+/* No return parameter(s) */
+
+/**************************************************************************
+ **************************************************************************
+ ** Link policy commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_LINK_POLICY 0x02 /* OpCode Group Field */
+
+#define NG_HCI_OCF_HOLD_MODE 0x0001
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t max_interval; /* (max_interval * 0.625) msec */
+ u_int16_t min_interval; /* (max_interval * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_hold_mode_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_SNIFF_MODE 0x0003
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t max_interval; /* (max_interval * 0.625) msec */
+ u_int16_t min_interval; /* (max_interval * 0.625) msec */
+ u_int16_t attempt; /* (2 * attempt - 1) * 0.625 msec */
+ u_int16_t timeout; /* (2 * attempt - 1) * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_sniff_mode_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_EXIT_SNIFF_MODE 0x0004
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_exit_sniff_mode_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_PARK_MODE 0x0005
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t max_interval; /* (max_interval * 0.625) msec */
+ u_int16_t min_interval; /* (max_interval * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_park_mode_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_EXIT_PARK_MODE 0x0006
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_exit_park_mode_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_QOS_SETUP 0x0007
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t flags; /* reserved for future use */
+ u_int8_t service_type; /* service type */
+ u_int32_t token_rate; /* bytes per second */
+ u_int32_t peak_bandwidth; /* bytes per second */
+ u_int32_t latency; /* microseconds */
+ u_int32_t delay_variation; /* microseconds */
+} __attribute__ ((packed)) ng_hci_qos_setup_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_ROLE_DISCOVERY 0x0009
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_role_discovery_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t role; /* role for the connection handle */
+} __attribute__ ((packed)) ng_hci_role_discovery_rp;
+
+#define NG_HCI_OCF_SWITCH_ROLE 0x000b
+typedef struct {
+ bdaddr_t bdaddr; /* remote address */
+ u_int8_t role; /* new local role */
+} __attribute__ ((packed)) ng_hci_switch_role_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_READ_LINK_POLICY_SETTINGS 0x000c
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_link_policy_settings_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t settings; /* link policy settings */
+} __attribute__ ((packed)) ng_hci_read_link_policy_settings_rp;
+
+#define NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS 0x000d
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t settings; /* link policy settings */
+} __attribute__ ((packed)) ng_hci_write_link_policy_settings_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_write_link_policy_settings_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Host controller and baseband commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_HC_BASEBAND 0x03 /* OpCode Group Field */
+
+#define NG_HCI_OCF_SET_EVENT_MASK 0x0001
+typedef struct {
+ u_int8_t event_mask[NG_HCI_EVENT_MASK_SIZE]; /* event_mask */
+} __attribute__ ((packed)) ng_hci_set_event_mask_cp;
+
+typedef ng_hci_status_rp ng_hci_set_event_mask_rp;
+
+#define NG_HCI_OCF_RESET 0x0003
+/* No command parameter(s) */
+typedef ng_hci_status_rp ng_hci_reset_rp;
+
+#define NG_HCI_OCF_SET_EVENT_FILTER 0x0005
+typedef struct {
+ u_int8_t filter_type; /* filter type */
+ u_int8_t filter_condition_type; /* filter condition type */
+ u_int8_t condition[0]; /* conditions - variable size */
+} __attribute__ ((packed)) ng_hci_set_event_filter_cp;
+
+typedef ng_hci_status_rp ng_hci_set_event_filter_rp;
+
+#define NG_HCI_OCF_FLUSH 0x0008
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_flush_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_flush_rp;
+
+#define NG_HCI_OCF_READ_PIN_TYPE 0x0009
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t pin_type; /* PIN type */
+} __attribute__ ((packed)) ng_hci_read_pin_type_rp;
+
+#define NG_HCI_OCF_WRITE_PIN_TYPE 0x000a
+typedef struct {
+ u_int8_t pin_type; /* PIN type */
+} __attribute__ ((packed)) ng_hci_write_pin_type_cp;
+
+typedef ng_hci_status_rp ng_hci_write_pin_type_rp;
+
+#define NG_HCI_OCF_CREATE_NEW_UNIT_KEY 0x000b
+/* No command parameter(s) */
+typedef ng_hci_status_rp ng_hci_create_new_unit_key_rp;
+
+#define NG_HCI_OCF_READ_STORED_LINK_KEY 0x000d
+typedef struct {
+ bdaddr_t bdaddr; /* address */
+ u_int8_t read_all; /* read all keys? 0x01 - yes */
+} __attribute__ ((packed)) ng_hci_read_stored_link_key_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t max_num_keys; /* Max. number of keys */
+ u_int16_t num_keys_read; /* Number of stored keys */
+} __attribute__ ((packed)) ng_hci_read_stored_link_key_rp;
+
+#define NG_HCI_OCF_WRITE_STORED_LINK_KEY 0x0011
+typedef struct {
+ u_int8_t num_keys_write; /* # of keys to write */
+/* these are repeated "num_keys_write" times
+ bdaddr_t bdaddr; --- remote address(es)
+ u_int8_t key[NG_HCI_KEY_SIZE]; --- key(s) */
+} __attribute__ ((packed)) ng_hci_write_stored_link_key_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t num_keys_written; /* # of keys successfully written */
+} __attribute__ ((packed)) ng_hci_write_stored_link_key_rp;
+
+#define NG_HCI_OCF_DELETE_STORED_LINK_KEY 0x0012
+typedef struct {
+ bdaddr_t bdaddr; /* address */
+ u_int8_t delete_all; /* delete all keys? 0x01 - yes */
+} __attribute__ ((packed)) ng_hci_delete_stored_link_key_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t num_keys_deleted; /* Number of keys deleted */
+} __attribute__ ((packed)) ng_hci_delete_stored_link_key_rp;
+
+#define NG_HCI_OCF_CHANGE_LOCAL_NAME 0x0013
+typedef struct {
+ char name[NG_HCI_UNIT_NAME_SIZE]; /* new unit name */
+} __attribute__ ((packed)) ng_hci_change_local_name_cp;
+
+typedef ng_hci_status_rp ng_hci_change_local_name_rp;
+
+#define NG_HCI_OCF_READ_LOCAL_NAME 0x0014
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ char name[NG_HCI_UNIT_NAME_SIZE]; /* unit name */
+} __attribute__ ((packed)) ng_hci_read_local_name_rp;
+
+#define NG_HCI_OCF_READ_CON_ACCEPT_TIMO 0x0015
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_read_con_accept_timo_rp;
+
+#define NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO 0x0016
+typedef struct {
+ u_int16_t timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_write_con_accept_timo_cp;
+
+typedef ng_hci_status_rp ng_hci_write_con_accept_timo_rp;
+
+#define NG_HCI_OCF_READ_PAGE_TIMO 0x0017
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_read_page_timo_rp;
+
+#define NG_HCI_OCF_WRITE_PAGE_TIMO 0x0018
+typedef struct {
+ u_int16_t timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((packed)) ng_hci_write_page_timo_cp;
+
+typedef ng_hci_status_rp ng_hci_write_page_timo_rp;
+
+#define NG_HCI_OCF_READ_SCAN_ENABLE 0x0019
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t scan_enable; /* Scan enable */
+} __attribute__ ((packed)) ng_hci_read_scan_enable_rp;
+
+#define NG_HCI_OCF_WRITE_SCAN_ENABLE 0x001a
+typedef struct {
+ u_int8_t scan_enable; /* Scan enable */
+} __attribute__ ((packed)) ng_hci_write_scan_enable_cp;
+
+typedef ng_hci_status_rp ng_hci_write_scan_enable_rp;
+
+#define NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY 0x001b
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t page_scan_interval; /* interval * 0.625 msec */
+ u_int16_t page_scan_window; /* window * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_read_page_scan_activity_rp;
+
+#define NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY 0x001c
+typedef struct {
+ u_int16_t page_scan_interval; /* interval * 0.625 msec */
+ u_int16_t page_scan_window; /* window * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_write_page_scan_activity_cp;
+
+typedef ng_hci_status_rp ng_hci_write_page_scan_activity_rp;
+
+#define NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY 0x001d
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t inquiry_scan_interval; /* interval * 0.625 msec */
+ u_int16_t inquiry_scan_window; /* window * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_read_inquiry_scan_activity_rp;
+
+#define NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY 0x001e
+typedef struct {
+ u_int16_t inquiry_scan_interval; /* interval * 0.625 msec */
+ u_int16_t inquiry_scan_window; /* window * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_write_inquiry_scan_activity_cp;
+
+typedef ng_hci_status_rp ng_hci_write_inquiry_scan_activity_rp;
+
+#define NG_HCI_OCF_READ_AUTH_ENABLE 0x001f
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t auth_enable; /* 0x01 - enabled */
+} __attribute__ ((packed)) ng_hci_read_auth_enable_rp;
+
+#define NG_HCI_OCF_WRITE_AUTH_ENABLE 0x0020
+typedef struct {
+ u_int8_t auth_enable; /* 0x01 - enabled */
+} __attribute__ ((packed)) ng_hci_write_auth_enable_cp;
+
+typedef ng_hci_status_rp ng_hci_write_auth_enable_rp;
+
+#define NG_HCI_OCF_READ_ENCRYPTION_MODE 0x0021
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t encryption_mode; /* encryption mode */
+} __attribute__ ((packed)) ng_hci_read_encryption_mode_rp;
+
+#define NG_HCI_OCF_WRITE_ENCRYPTION_MODE 0x0022
+typedef struct {
+ u_int8_t encryption_mode; /* encryption mode */
+} __attribute__ ((packed)) ng_hci_write_encryption_mode_cp;
+
+typedef ng_hci_status_rp ng_hci_write_encryption_mode_rp;
+
+#define NG_HCI_OCF_READ_UNIT_CLASS 0x0023
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */
+} __attribute__ ((packed)) ng_hci_read_unit_class_rp;
+
+#define NG_HCI_OCF_WRITE_UNIT_CLASS 0x0024
+typedef struct {
+ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */
+} __attribute__ ((packed)) ng_hci_write_unit_class_cp;
+
+typedef ng_hci_status_rp ng_hci_write_unit_class_rp;
+
+#define NG_HCI_OCF_READ_VOICE_SETTINGS 0x0025
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t settings; /* voice settings */
+} __attribute__ ((packed)) ng_hci_read_voice_settings_rp;
+
+#define NG_HCI_OCF_WRITE_VOICE_SETTINGS 0x0026
+typedef struct {
+ u_int16_t settings; /* voice settings */
+} __attribute__ ((packed)) ng_hci_write_voice_settings_cp;
+
+typedef ng_hci_status_rp ng_hci_write_voice_settings_rp;
+
+#define NG_HCI_OCF_READ_AUTO_FLUSH_TIMO 0x0027
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_auto_flush_timo_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t timeout; /* 0x00 - no flush, timeout * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_read_auto_flush_timo_rp;
+
+#define NG_HCI_OCF_WRITE_AUTO_FLUSH_TIMO 0x0028
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t timeout; /* 0x00 - no flush, timeout * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_write_auto_flush_timo_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_write_auto_flush_timo_rp;
+
+#define NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS 0x0029
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t counter; /* number of broadcast retransmissions */
+} __attribute__ ((packed)) ng_hci_read_num_broadcast_retrans_rp;
+
+#define NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS 0x002a
+typedef struct {
+ u_int8_t counter; /* number of broadcast retransmissions */
+} __attribute__ ((packed)) ng_hci_write_num_broadcast_retrans_cp;
+
+typedef ng_hci_status_rp ng_hci_write_num_broadcast_retrans_rp;
+
+#define NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY 0x002b
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t hold_mode_activity; /* Hold mode activities */
+} __attribute__ ((packed)) ng_hci_read_hold_mode_activity_rp;
+
+#define NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY 0x002c
+typedef struct {
+ u_int8_t hold_mode_activity; /* Hold mode activities */
+} __attribute__ ((packed)) ng_hci_write_hold_mode_activity_cp;
+
+typedef ng_hci_status_rp ng_hci_write_hold_mode_activity_rp;
+
+#define NG_HCI_OCF_READ_XMIT_LEVEL 0x002d
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t type; /* Xmit level type */
+} __attribute__ ((packed)) ng_hci_read_xmit_level_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ char level; /* -30 <= level <= 30 dBm */
+} __attribute__ ((packed)) ng_hci_read_xmit_level_rp;
+
+#define NG_HCI_OCF_READ_SCO_FLOW_CONTROL 0x002e
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t flow_control; /* 0x00 - disabled */
+} __attribute__ ((packed)) ng_hci_read_sco_flow_control_rp;
+
+#define NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL 0x002f
+typedef struct {
+ u_int8_t flow_control; /* 0x00 - disabled */
+} __attribute__ ((packed)) ng_hci_write_sco_flow_control_cp;
+
+typedef ng_hci_status_rp ng_hci_write_sco_flow_control_rp;
+
+#define NG_HCI_OCF_H2HC_FLOW_CONTROL 0x0031
+typedef struct {
+ u_int8_t h2hc_flow; /* Host to Host controller flow control */
+} __attribute__ ((packed)) ng_hci_h2hc_flow_control_cp;
+
+typedef ng_hci_status_rp ng_hci_h2hc_flow_control_rp;
+
+#define NG_HCI_OCF_HOST_BUFFER_SIZE 0x0033
+typedef struct {
+ u_int16_t max_acl_size; /* Max. size of ACL packet (bytes) */
+ u_int8_t max_sco_size; /* Max. size of SCO packet (bytes) */
+ u_int16_t num_acl_pkt; /* Max. number of ACL packets */
+ u_int16_t num_sco_pkt; /* Max. number of SCO packets */
+} __attribute__ ((packed)) ng_hci_host_buffer_size_cp;
+
+typedef ng_hci_status_rp ng_hci_host_buffer_size_rp;
+
+#define NG_HCI_OCF_HOST_NUM_COMPL_PKTS 0x0035
+typedef struct {
+ u_int8_t num_con_handles; /* # of connection handles */
+/* these are repeated "num_con_handles" times
+ u_int16_t con_handle; --- connection handle(s)
+ u_int16_t compl_pkt; --- # of completed packets */
+} __attribute__ ((packed)) ng_hci_host_num_compl_pkts_cp;
+/* No return parameter(s) */
+
+#define NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO 0x0036
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_link_supervision_timo_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t timeout; /* Link supervision timeout * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_read_link_supervision_timo_rp;
+
+#define NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO 0x0037
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t timeout; /* Link supervision timeout * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_write_link_supervision_timo_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_write_link_supervision_timo_rp;
+
+#define NG_HCI_OCF_READ_SUPPORTED_IAC_NUM 0x0038
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t num_iac; /* # of supported IAC during scan */
+} __attribute__ ((packed)) ng_hci_read_supported_iac_num_rp;
+
+#define NG_HCI_OCF_READ_IAC_LAP 0x0039
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t num_iac; /* # of IAC */
+/* these are repeated "num_iac" times
+ u_int8_t laps[NG_HCI_LAP_SIZE]; --- LAPs */
+} __attribute__ ((packed)) ng_hci_read_iac_lap_rp;
+
+#define NG_HCI_OCF_WRITE_IAC_LAP 0x003a
+typedef struct {
+ u_int8_t num_iac; /* # of IAC */
+/* these are repeated "num_iac" times
+ u_int8_t laps[NG_HCI_LAP_SIZE]; --- LAPs */
+} __attribute__ ((packed)) ng_hci_write_iac_lap_cp;
+
+typedef ng_hci_status_rp ng_hci_write_iac_lap_rp;
+
+#define NG_HCI_OCF_READ_PAGE_SCAN_PERIOD 0x003b
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t page_scan_period_mode; /* Page scan period mode */
+} __attribute__ ((packed)) ng_hci_read_page_scan_period_rp;
+
+#define NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD 0x003c
+typedef struct {
+ u_int8_t page_scan_period_mode; /* Page scan period mode */
+} __attribute__ ((packed)) ng_hci_write_page_scan_period_cp;
+
+typedef ng_hci_status_rp ng_hci_write_page_scan_period_rp;
+
+#define NG_HCI_OCF_READ_PAGE_SCAN 0x003d
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t page_scan_mode; /* Page scan mode */
+} __attribute__ ((packed)) ng_hci_read_page_scan_rp;
+
+#define NG_HCI_OCF_WRITE_PAGE_SCAN 0x003e
+typedef struct {
+ u_int8_t page_scan_mode; /* Page scan mode */
+} __attribute__ ((packed)) ng_hci_write_page_scan_cp;
+
+typedef ng_hci_status_rp ng_hci_write_page_scan_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Informational commands and return parameters
+ ** All commands in this category do not accept any parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_INFO 0x04 /* OpCode Group Field */
+
+#define NG_HCI_OCF_READ_LOCAL_VER 0x0001
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t hci_version; /* HCI version */
+ u_int16_t hci_revision; /* HCI revision */
+ u_int8_t lmp_version; /* LMP version */
+ u_int16_t manufacturer; /* Hardware manufacturer name */
+ u_int16_t lmp_subversion; /* LMP sub-version */
+} __attribute__ ((packed)) ng_hci_read_local_ver_rp;
+
+#define NG_HCI_OCF_READ_LOCAL_FEATURES 0x0003
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* LMP features bitmsk*/
+} __attribute__ ((packed)) ng_hci_read_local_features_rp;
+
+#define NG_HCI_OCF_READ_BUFFER_SIZE 0x0005
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t max_acl_size; /* Max. size of ACL packet (bytes) */
+ u_int8_t max_sco_size; /* Max. size of SCO packet (bytes) */
+ u_int16_t num_acl_pkt; /* Max. number of ACL packets */
+ u_int16_t num_sco_pkt; /* Max. number of SCO packets */
+} __attribute__ ((packed)) ng_hci_read_buffer_size_rp;
+
+#define NG_HCI_OCF_READ_COUNTRY_CODE 0x0007
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t country_code; /* 0x00 - NAM, EUR, JP; 0x01 - France */
+} __attribute__ ((packed)) ng_hci_read_country_code_rp;
+
+#define NG_HCI_OCF_READ_BDADDR 0x0009
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* unit address */
+} __attribute__ ((packed)) ng_hci_read_bdaddr_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Status commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_STATUS 0x05 /* OpCode Group Field */
+
+#define NG_HCI_OCF_READ_FAILED_CONTACT_CNTR 0x0001
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_failed_contact_cntr_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t counter; /* number of consecutive failed contacts */
+} __attribute__ ((packed)) ng_hci_read_failed_contact_cntr_rp;
+
+#define NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR 0x0002
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_reset_failed_contact_cntr_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_reset_failed_contact_cntr_rp;
+
+#define NG_HCI_OCF_GET_LINK_QUALITY 0x0003
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_get_link_quality_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t quality; /* higher value means better quality */
+} __attribute__ ((packed)) ng_hci_get_link_quality_rp;
+
+#define NG_HCI_OCF_READ_RSSI 0x0005
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_read_rssi_cp;
+
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ char rssi; /* -127 <= rssi <= 127 dB */
+} __attribute__ ((packed)) ng_hci_read_rssi_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Testing commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_TESTING 0x06 /* OpCode Group Field */
+
+#define NG_HCI_OCF_READ_LOOPBACK_MODE 0x0001
+/* No command parameter(s) */
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int8_t lbmode; /* loopback mode */
+} __attribute__ ((packed)) ng_hci_read_loopback_mode_rp;
+
+#define NG_HCI_OCF_WRITE_LOOPBACK_MODE 0x0002
+typedef struct {
+ u_int8_t lbmode; /* loopback mode */
+} __attribute__ ((packed)) ng_hci_write_loopback_mode_cp;
+
+typedef ng_hci_status_rp ng_hci_write_loopback_mode_rp;
+
+#define NG_HCI_OCF_ENABLE_UNIT_UNDER_TEST 0x0003
+/* No command parameter(s) */
+typedef ng_hci_status_rp ng_hci_enable_unit_under_test_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** Special HCI OpCode group field values
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_OGF_BT_LOGO 0x3e
+
+#define NG_HCI_OGF_VENDOR 0x3f
+
+/**************************************************************************
+ **************************************************************************
+ ** Events and event parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_HCI_EVENT_INQUIRY_COMPL 0x01
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+} __attribute__ ((packed)) ng_hci_inquiry_compl_ep;
+
+#define NG_HCI_EVENT_INQUIRY_RESULT 0x02
+typedef struct {
+ u_int8_t num_responses; /* number of responses */
+/* these are repeated "num_responses" times
+ bdaddr_t bdaddr; --- unit address(es)
+ u_int8_t page_scan_rep_mode; --- page scan rep. mode(s)
+ u_int8_t page_scan_period_mode; --- page scan period mode(s)
+ u_int8_t page_scan_mode; --- page scan mode(s)
+ u_int8_t uclass[NG_HCI_CLASS_SIZE]; --- unit class(es)
+ u_int16_t clock_offset; --- clock offset(s) */
+} __attribute__ ((packed)) ng_hci_inquiry_result_ep;
+
+#define NG_HCI_EVENT_CON_COMPL 0x03
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int8_t link_type; /* Link type */
+ u_int8_t encryption_mode; /* Encryption mode */
+} __attribute__ ((packed)) ng_hci_con_compl_ep;
+
+#define NG_HCI_EVENT_CON_REQ 0x04
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* remote unit class */
+ u_int8_t link_type; /* link type */
+} __attribute__ ((packed)) ng_hci_con_req_ep;
+
+#define NG_HCI_EVENT_DISCON_COMPL 0x05
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t reason; /* reason to disconnect */
+} __attribute__ ((packed)) ng_hci_discon_compl_ep;
+
+#define NG_HCI_EVENT_AUTH_COMPL 0x06
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_auth_compl_ep;
+
+#define NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL 0x7
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* remote unit address */
+ char name[NG_HCI_UNIT_NAME_SIZE]; /* remote unit name */
+} __attribute__ ((packed)) ng_hci_remote_name_req_compl_ep;
+
+#define NG_HCI_EVENT_ENCRYPTION_CHANGE 0x08
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ u_int8_t encryption_enable; /* 0x00 - disable */
+} __attribute__ ((packed)) ng_hci_encryption_change_ep;
+
+#define NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL 0x09
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+} __attribute__ ((packed)) ng_hci_change_con_link_key_compl_ep;
+
+#define NG_HCI_EVENT_MASTER_LINK_KEY_COMPL 0x0a
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ u_int8_t key_flag; /* Key flag */
+} __attribute__ ((packed)) ng_hci_master_link_key_compl_ep;
+
+#define NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL 0x0b
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* LMP features bitmsk*/
+} __attribute__ ((packed)) ng_hci_read_remote_features_compl_ep;
+
+#define NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL 0x0c
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ u_int8_t lmp_version; /* LMP version */
+ u_int16_t manufacturer; /* Hardware manufacturer name */
+ u_int16_t lmp_subversion; /* LMP sub-version */
+} __attribute__ ((packed)) ng_hci_read_remote_ver_info_compl_ep;
+
+#define NG_HCI_EVENT_QOS_SETUP_COMPL 0x0d
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t flags; /* reserved for future use */
+ u_int8_t service_type; /* service type */
+ u_int32_t token_rate; /* bytes per second */
+ u_int32_t peak_bandwidth; /* bytes per second */
+ u_int32_t latency; /* microseconds */
+ u_int32_t delay_variation; /* microseconds */
+} __attribute__ ((packed)) ng_hci_qos_setup_compl_ep;
+
+#define NG_HCI_EVENT_COMMAND_COMPL 0x0e
+typedef struct {
+ u_int8_t num_cmd_pkts; /* # of HCI command packets */
+ u_int16_t opcode; /* command OpCode */
+ /* command return parameters (if any) */
+} __attribute__ ((packed)) ng_hci_command_compl_ep;
+
+#define NG_HCI_EVENT_COMMAND_STATUS 0x0f
+typedef struct {
+ u_int8_t status; /* 0x00 - pending */
+ u_int8_t num_cmd_pkts; /* # of HCI command packets */
+ u_int16_t opcode; /* command OpCode */
+} __attribute__ ((packed)) ng_hci_command_status_ep;
+
+#define NG_HCI_EVENT_HARDWARE_ERROR 0x10
+typedef struct {
+ u_int8_t hardware_code; /* hardware error code */
+} __attribute__ ((packed)) ng_hci_hardware_error_ep;
+
+#define NG_HCI_EVENT_FLUSH_OCCUR 0x11
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_flush_occur_ep;
+
+#define NG_HCI_EVENT_ROLE_CHANGE 0x12
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ bdaddr_t bdaddr; /* address of remote unit */
+ u_int8_t role; /* new connection role */
+} __attribute__ ((packed)) ng_hci_role_change_ep;
+
+#define NG_HCI_EVENT_NUM_COMPL_PKTS 0x13
+typedef struct {
+ u_int8_t num_con_handles; /* # of connection handles */
+/* these are repeated "num_con_handles" times
+ u_int16_t con_handle; --- connection handle(s)
+ u_int16_t compl_pkt; --- # of completed packets */
+} __attribute__ ((packed)) ng_hci_num_compl_pkts_ep;
+
+#define NG_HCI_EVENT_MODE_CHANGE 0x14
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t unit_mode; /* remote unit mode */
+ u_int16_t interval; /* interval * 0.625 msec */
+} __attribute__ ((packed)) ng_hci_mode_change_ep;
+
+#define NG_HCI_EVENT_RETURN_LINK_KEYS 0x15
+typedef struct {
+ u_int8_t num_keys; /* # of keys */
+/* these are repeated "num_keys" times
+ bdaddr_t bdaddr; --- remote address(es)
+ u_int8_t key[NG_HCI_KEY_SIZE]; --- key(s) */
+} __attribute__ ((packed)) ng_hci_return_link_keys_ep;
+
+#define NG_HCI_EVENT_PIN_CODE_REQ 0x16
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+} __attribute__ ((packed)) ng_hci_pin_code_req_ep;
+
+#define NG_HCI_EVENT_LINK_KEY_REQ 0x17
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+} __attribute__ ((packed)) ng_hci_link_key_req_ep;
+
+#define NG_HCI_EVENT_LINK_KEY_NOTIFICATION 0x18
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int8_t key[NG_HCI_KEY_SIZE]; /* link key */
+ u_int8_t key_type; /* type of the key */
+} __attribute__ ((packed)) ng_hci_link_key_notification_ep;
+
+#define NG_HCI_EVENT_LOOPBACK_COMMAND 0x19
+typedef struct {
+ u_int8_t command[0]; /* Command packet */
+} __attribute__ ((packed)) ng_hci_loopback_command_ep;
+
+#define NG_HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1a
+typedef struct {
+ u_int8_t link_type; /* Link type */
+} __attribute__ ((packed)) ng_hci_data_buffer_overflow_ep;
+
+#define NG_HCI_EVENT_MAX_SLOT_CHANGE 0x1b
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+ u_int8_t lmp_max_slots; /* Max. # of slots allowed */
+} __attribute__ ((packed)) ng_hci_max_slot_change_ep;
+
+#define NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL 0x1c
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* Connection handle */
+ u_int16_t clock_offset; /* Clock offset */
+} __attribute__ ((packed)) ng_hci_read_clock_offset_compl_ep;
+
+#define NG_HCI_EVENT_CON_PKT_TYPE_CHANGED 0x1d
+typedef struct {
+ u_int8_t status; /* 0x00 - success */
+ u_int16_t con_handle; /* connection handle */
+ u_int16_t pkt_type; /* packet type */
+} __attribute__ ((packed)) ng_hci_con_pkt_type_changed_ep;
+
+#define NG_HCI_EVENT_QOS_VIOLATION 0x1e
+typedef struct {
+ u_int16_t con_handle; /* connection handle */
+} __attribute__ ((packed)) ng_hci_qos_violation_ep;
+
+#define NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE 0x1f
+typedef struct {
+ bdaddr_t bdaddr; /* destination address */
+ u_int8_t page_scan_mode; /* page scan mode */
+} __attribute__ ((packed)) ng_hci_page_scan_mode_change_ep;
+
+#define NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE 0x20
+typedef struct {
+ bdaddr_t bdaddr; /* destination address */
+ u_int8_t page_scan_rep_mode; /* page scan repetition mode */
+} __attribute__ ((packed)) ng_hci_page_scan_rep_mode_change_ep;
+
+#define NG_HCI_EVENT_BT_LOGO 0xfe
+
+#define NG_HCI_EVENT_VENDOR 0xff
+
+#endif /* ndef _NETGRAPH_HCI_H_ */
diff --git a/sys/netgraph/bluetooth/include/ng_l2cap.h b/sys/netgraph/bluetooth/include/ng_l2cap.h
new file mode 100644
index 0000000..e277c21
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_l2cap.h
@@ -0,0 +1,655 @@
+/*
+ * ng_l2cap.h
+ *
+ * Copyright (c) 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_l2cap.h,v 1.13 2002/09/08 23:35:51 max Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains everything that application needs to know about
+ * Link Layer Control and Adaptation Protocol (L2CAP). All information
+ * was obtained from Bluetooth Specification Book v1.1.
+ *
+ * This file can be included by both kernel and userland applications.
+ */
+
+#ifndef _NETGRAPH_L2CAP_H_
+#define _NETGRAPH_L2CAP_H_
+
+/**************************************************************************
+ **************************************************************************
+ ** Netgraph node hook name, type name and type cookie and commands
+ **************************************************************************
+ **************************************************************************/
+
+/* Netgraph node hook names */
+#define NG_L2CAP_HOOK_HCI "hci" /* HCI <-> L2CAP */
+#define NG_L2CAP_HOOK_L2C "l2c" /* L2CAP <-> Upper */
+#define NG_L2CAP_HOOK_CTL "ctl" /* L2CAP <-> User */
+
+/* Node type name and type cookie */
+#define NG_L2CAP_NODE_TYPE "l2cap"
+#define NGM_L2CAP_COOKIE 1000774185
+
+/**************************************************************************
+ **************************************************************************
+ ** Common defines and types (L2CAP)
+ **************************************************************************
+ **************************************************************************/
+
+/*
+ * Channel IDs are assigned relative to the instance of L2CAP node, i.e.
+ * relative to the unit. So the total number of channels that unit can have
+ * open at the same time is 0xffff - 0x0040 = 0xffbf (65471). This number
+ * does not depend on number of connections.
+ */
+
+#define NG_L2CAP_NULL_CID 0x0000 /* DO NOT USE THIS CID */
+#define NG_L2CAP_SIGNAL_CID 0x0001 /* signaling channel ID */
+#define NG_L2CAP_CLT_CID 0x0002 /* connectionless channel ID */
+ /* 0x0003 - 0x003f Reserved */
+#define NG_L2CAP_FIRST_CID 0x0040 /* dynamically alloc. (start) */
+#define NG_L2CAP_LAST_CID 0xffff /* dynamically alloc. (end) */
+
+/* L2CAP MTU */
+#define NG_L2CAP_MTU_MINIMUM 48
+#define NG_L2CAP_MTU_DEFAULT 672
+#define NG_L2CAP_MTU_MAXIMUM 0xffff
+
+/* L2CAP flush and link timeouts */
+#define NG_L2CAP_FLUSH_TIMO_DEFAULT 0xffff /* always retransmit */
+#define NG_L2CAP_LINK_TIMO_DEFAULT 0xffff
+
+/* L2CAP Command Reject reasons */
+#define NG_L2CAP_REJ_NOT_UNDERSTOOD 0x0000
+#define NG_L2CAP_REJ_MTU_EXCEEDED 0x0001
+#define NG_L2CAP_REJ_INVALID_CID 0x0002
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* Protocol/Service Multioplexor (PSM) values */
+#define NG_L2CAP_PSM_ANY 0x0000 /* Any/Invalid PSM */
+#define NG_L2CAP_PSM_SDP 0x0001 /* Service Discovery Protocol */
+#define NG_L2CAP_PSM_RFCOMM 0x0003 /* RFCOMM protocol */
+#define NG_L2CAP_PSM_TCP 0x0005 /* Telephony Control Protocol */
+/* 0x0006 - 0x1000 - reserved for future use */
+
+/* L2CAP Connection response command result codes */
+#define NG_L2CAP_SUCCESS 0x0000
+#define NG_L2CAP_PENDING 0x0001
+#define NG_L2CAP_PSM_NOT_SUPPORTED 0x0002
+#define NG_L2CAP_SEQUIRY_BLOCK 0x0003
+#define NG_L2CAP_NO_RESOURCES 0x0004
+#define NG_L2CAP_TIMEOUT 0xeeee
+#define NG_L2CAP_UNKNOWN 0xffff
+/* 0x0005 - 0xffff - reserved for future use */
+
+/* L2CAP Connection response status codes */
+#define NG_L2CAP_NO_INFO 0x0000
+#define NG_L2CAP_AUTH_PENDING 0x0001
+#define NG_L2CAP_AUTZ_PENDING 0x0002
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* L2CAP Configuration response result codes */
+#define NG_L2CAP_UNACCEPTABLE_PARAMS 0x0001
+#define NG_L2CAP_REJECT 0x0002
+#define NG_L2CAP_UNKNOWN_OPTION 0x0003
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* L2CAP Configuration options */
+#define NG_L2CAP_OPT_CFLAG_BIT 0x0001
+#define NG_L2CAP_OPT_CFLAG(flags) ((flags) & NG_L2CAP_OPT_CFLAG_BIT)
+#define NG_L2CAP_OPT_HINT_BIT 0x80
+#define NG_L2CAP_OPT_HINT(type) ((type) & NG_L2CAP_OPT_HINT_BIT)
+#define NG_L2CAP_OPT_HINT_MASK 0x7f
+#define NG_L2CAP_OPT_MTU 0x01
+#define NG_L2CAP_OPT_MTU_SIZE sizeof(u_int16_t)
+#define NG_L2CAP_OPT_FLUSH_TIMO 0x02
+#define NG_L2CAP_OPT_FLUSH_TIMO_SIZE sizeof(u_int16_t)
+#define NG_L2CAP_OPT_QOS 0x03
+#define NG_L2CAP_OPT_QOS_SIZE sizeof(ng_l2cap_flow_t)
+/* 0x4 - 0xff - reserved for future use */
+
+/* L2CAP Information request type codes */
+#define NG_L2CAP_CONNLESS_MTU 0x0001
+/* 0x0002 - 0xffff - reserved for future use */
+
+/* L2CAP Information response codes */
+#define NG_L2CAP_NOT_SUPPORTED 0x0001
+/* 0x0002 - 0xffff - reserved for future use */
+
+/* L2CAP flow control */
+typedef struct {
+ u_int8_t flags; /* reserved for future use */
+ u_int8_t service_type; /* service type */
+ u_int32_t token_rate; /* bytes per second */
+ u_int32_t token_bucket_size; /* bytes */
+ u_int32_t peak_bandwidth; /* bytes per second */
+ u_int32_t latency; /* microseconds */
+ u_int32_t delay_variation; /* microseconds */
+} __attribute__ ((packed)) ng_l2cap_flow_t;
+typedef ng_l2cap_flow_t * ng_l2cap_flow_p;
+
+/**************************************************************************
+ **************************************************************************
+ ** Link level defines, headers and types
+ **************************************************************************
+ **************************************************************************/
+
+/* L2CAP header */
+typedef struct {
+ u_int16_t length; /* payload size */
+ u_int16_t dcid; /* destination channel ID */
+} __attribute__ ((packed)) ng_l2cap_hdr_t;
+
+/* L2CAP ConnectionLess Traffic (CLT) (if destination cid == 0x2) */
+typedef struct {
+ u_int16_t psm; /* Protocol/Service Multiplexor */
+} __attribute__ ((packed)) ng_l2cap_clt_hdr_t;
+
+#define NG_L2CAP_CLT_MTU_MAXIMUM \
+ (NG_L2CAP_MTU_MAXIMUM - sizeof(ng_l2cap_clt_hdr_t))
+
+/* L2CAP command header */
+typedef struct {
+ u_int8_t code; /* command OpCode */
+ u_int8_t ident; /* identifier to match request and response */
+ u_int16_t length; /* command parameters length */
+} __attribute__ ((packed)) ng_l2cap_cmd_hdr_t;
+
+/* L2CAP Command Reject */
+#define NG_L2CAP_CMD_REJ 0x01
+typedef struct {
+ u_int16_t reason; /* reason to reject command */
+/* u_int8_t data[]; -- optional data (depends on reason) */
+} __attribute__ ((packed)) ng_l2cap_cmd_rej_cp;
+
+/* CommandReject data */
+typedef union {
+ /* NG_L2CAP_REJ_MTU_EXCEEDED */
+ struct {
+ u_int16_t mtu; /* actual signaling MTU */
+ } __attribute__ ((packed)) mtu;
+ /* NG_L2CAP_REJ_INVALID_CID */
+ struct {
+ u_int16_t scid; /* local CID */
+ u_int16_t dcid; /* remote CID */
+ } __attribute__ ((packed)) cid;
+} ng_l2cap_cmd_rej_data_t;
+typedef ng_l2cap_cmd_rej_data_t * ng_l2cap_cmd_rej_data_p;
+
+/* L2CAP Connection Request */
+#define NG_L2CAP_CON_REQ 0x02
+typedef struct {
+ u_int16_t psm; /* Protocol/Service Multiplexor (PSM) */
+ u_int16_t scid; /* source channel ID */
+} __attribute__ ((packed)) ng_l2cap_con_req_cp;
+
+/* L2CAP Connection Response */
+#define NG_L2CAP_CON_RSP 0x03
+typedef struct {
+ u_int16_t dcid; /* destination channel ID */
+ u_int16_t scid; /* source channel ID */
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t status; /* more info if result != 0x00 */
+} __attribute__ ((packed)) ng_l2cap_con_rsp_cp;
+
+/* L2CAP Configuration Request */
+#define NG_L2CAP_CFG_REQ 0x04
+typedef struct {
+ u_int16_t dcid; /* destination channel ID */
+ u_int16_t flags; /* flags */
+/* u_int8_t options[] -- options */
+} __attribute__ ((packed)) ng_l2cap_cfg_req_cp;
+
+/* L2CAP Configuration Response */
+#define NG_L2CAP_CFG_RSP 0x05
+typedef struct {
+ u_int16_t scid; /* source channel ID */
+ u_int16_t flags; /* flags */
+ u_int16_t result; /* 0x00 - success */
+/* u_int8_t options[] -- options */
+} __attribute__ ((packed)) ng_l2cap_cfg_rsp_cp;
+
+/* L2CAP configuration option */
+typedef struct {
+ u_int8_t type;
+ u_int8_t length;
+/* u_int8_t value[] -- option value (depends on type) */
+} __attribute__ ((packed)) ng_l2cap_cfg_opt_t;
+typedef ng_l2cap_cfg_opt_t * ng_l2cap_cfg_opt_p;
+
+/* L2CAP configuration option value */
+typedef union {
+ u_int16_t mtu; /* NG_L2CAP_OPT_MTU */
+ u_int16_t flush_timo; /* NG_L2CAP_OPT_FLUSH_TIMO */
+ ng_l2cap_flow_t flow; /* NG_L2CAP_OPT_QOS */
+} ng_l2cap_cfg_opt_val_t;
+typedef ng_l2cap_cfg_opt_val_t * ng_l2cap_cfg_opt_val_p;
+
+/* L2CAP Disconnect Request */
+#define NG_L2CAP_DISCON_REQ 0x06
+typedef struct {
+ u_int16_t dcid; /* destination channel ID */
+ u_int16_t scid; /* source channel ID */
+} __attribute__ ((packed)) ng_l2cap_discon_req_cp;
+
+/* L2CAP Disconnect Response */
+#define NG_L2CAP_DISCON_RSP 0x07
+typedef ng_l2cap_discon_req_cp ng_l2cap_discon_rsp_cp;
+
+/* L2CAP Echo Request */
+#define NG_L2CAP_ECHO_REQ 0x08
+/* No command parameters, only optional data */
+
+/* L2CAP Echo Response */
+#define NG_L2CAP_ECHO_RSP 0x09
+#define NG_L2CAP_MAX_ECHO_SIZE \
+ (NG_L2CAP_MTU_MAXIMUM - sizeof(ng_l2cap_cmd_hdr_t))
+/* No command parameters, only optional data */
+
+/* L2CAP Information Request */
+#define NG_L2CAP_INFO_REQ 0x0a
+typedef struct {
+ u_int16_t type; /* requested information type */
+} __attribute__ ((packed)) ng_l2cap_info_req_cp;
+
+/* L2CAP Information Response */
+#define NG_L2CAP_INFO_RSP 0x0b
+typedef struct {
+ u_int16_t type; /* requested information type */
+ u_int16_t result; /* 0x00 - success */
+/* u_int8_t info[] -- info data (depends on type)
+ *
+ * NG_L2CAP_CONNLESS_MTU - 2 bytes connectionless MTU
+ */
+} __attribute__ ((packed)) ng_l2cap_info_rsp_cp;
+
+typedef union {
+ /* NG_L2CAP_CONNLESS_MTU */
+ struct {
+ u_int16_t mtu;
+ } __attribute__ ((packed)) mtu;
+} ng_l2cap_info_rsp_data_t;
+typedef ng_l2cap_info_rsp_data_t * ng_l2cap_info_rsp_data_p;
+
+/**************************************************************************
+ **************************************************************************
+ ** Upper layer protocol interface. L2CA_xxx messages
+ **************************************************************************
+ **************************************************************************/
+
+/*
+ * NOTE! NOTE! NOTE!
+ *
+ * Bluetooth specification says that L2CA_xxx request must block until
+ * response is ready. We are not allowed to block in Netgraph, so we
+ * need to queue request and save some information that can be used
+ * later and help match request and response.
+ *
+ * The idea is to use "token" field from Netgraph message header. The
+ * upper layer protocol _MUST_ populate "token". L2CAP will queue request
+ * (using L2CAP command descriptor) and start processing. Later, when
+ * response is ready or timeout has occur L2CAP layer will create new
+ * Netgraph message, set "token" and RESP flag and send the message to
+ * the upper layer protocol.
+ *
+ * L2CA_xxx_Ind messages _WILL_NOT_ populate "token" and _WILL_NOT_
+ * set RESP flag. There is no reason for this, because they are just
+ * notifications and do not require acknowlegment.
+ *
+ * NOTE: This is _NOT_ what NG_MKRESPONSE and NG_RESPOND_MSG do, however
+ * it is somewhat similar.
+ */
+
+/* L2CA data packet header */
+typedef struct {
+ u_int32_t token; /* token to use in L2CAP_L2CA_WRITE */
+ u_int16_t length; /* length of the data */
+ u_int16_t lcid; /* local channel ID */
+} __attribute__ ((packed)) ng_l2cap_l2ca_hdr_t;
+
+/* L2CA_Connect */
+#define NGM_L2CAP_L2CA_CON 0x80
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t psm; /* Protocol/Service Multiplexor */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_l2cap_l2ca_con_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t status; /* if result != 0x00 */
+} ng_l2cap_l2ca_con_op;
+
+/* L2CA_ConnectInd */
+#define NGM_L2CAP_L2CA_CON_IND 0x81
+/* L2CAP -> Upper */
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t psm; /* Procotol/Service Multiplexor */
+ u_int8_t ident; /* indentifier */
+ u_int8_t unused; /* place holder */
+} ng_l2cap_l2ca_con_ind_ip;
+/* No output parameters */
+
+/* L2CA_ConnectRsp */
+#define NGM_L2CAP_L2CA_CON_RSP 0x82
+/* Upper -> L2CAP */
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int8_t ident; /* "ident" from L2CAP_ConnectInd event */
+ u_int8_t unused; /* place holder */
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t status; /* if response != 0x00 */
+} ng_l2cap_l2ca_con_rsp_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+} ng_l2cap_l2ca_con_rsp_op;
+
+/* L2CA_Config */
+#define NGM_L2CAP_L2CA_CFG 0x83
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t imtu; /* receiving MTU for the local channel */
+ ng_l2cap_flow_t oflow; /* out flow */
+ u_int16_t flush_timo; /* flush timeout (msec) */
+ u_int16_t link_timo; /* link timeout (msec) */
+} ng_l2cap_l2ca_cfg_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t imtu; /* sending MTU for the remote channel */
+ ng_l2cap_flow_t oflow; /* out flow */
+ u_int16_t flush_timo; /* flush timeout (msec) */
+} ng_l2cap_l2ca_cfg_op;
+
+/* L2CA_ConfigRsp */
+#define NGM_L2CAP_L2CA_CFG_RSP 0x84
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t omtu; /* sending MTU for the local channel */
+ ng_l2cap_flow_t iflow; /* in FLOW */
+} ng_l2cap_l2ca_cfg_rsp_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - sucsess */
+} ng_l2cap_l2ca_cfg_rsp_op;
+
+/* L2CA_ConfigInd */
+#define NGM_L2CAP_L2CA_CFG_IND 0x85
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t lcid; /* local channel ID */
+ u_int16_t omtu; /* outgoing MTU for the local channel */
+ ng_l2cap_flow_t iflow; /* in flow */
+ u_int16_t flush_timo; /* flush timeout (msec) */
+} ng_l2cap_l2ca_cfg_ind_ip;
+/* No output parameters */
+
+/* L2CA_QoSViolationInd */
+#define NGM_L2CAP_L2CA_QOS_IND 0x86
+/* L2CAP -> Upper */
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_l2cap_l2ca_qos_ind_ip;
+/* No output parameters */
+
+/* L2CA_Disconnect */
+#define NGM_L2CAP_L2CA_DISCON 0x87
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local channel ID */
+} ng_l2cap_l2ca_discon_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - sucsess */
+} ng_l2cap_l2ca_discon_op;
+
+/* L2CA_DisconnectInd */
+#define NGM_L2CAP_L2CA_DISCON_IND 0x88
+/* L2CAP -> Upper */
+typedef ng_l2cap_l2ca_discon_ip ng_l2cap_l2ca_discon_ind_ip;
+/* No output parameters */
+
+/* L2CA_Write response */
+#define NGM_L2CAP_L2CA_WRITE 0x89
+/* No input parameters */
+
+/* L2CAP -> Upper */
+typedef struct {
+ int result; /* result (0x00 - success) */
+ u_int16_t length; /* amount of data written */
+ u_int16_t lcid; /* local channel ID */
+} ng_l2cap_l2ca_write_op;
+
+/* L2CA_GroupCreate */
+#define NGM_L2CAP_L2CA_GRP_CREATE 0x8a
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t psm; /* Protocol/Service Multiplexor */
+} ng_l2cap_l2ca_grp_create_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t lcid; /* local group channel ID */
+} ng_l2cap_l2ca_grp_create_op;
+
+/* L2CA_GroupClose */
+#define NGM_L2CAP_L2CA_GRP_CLOSE 0x8b
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local group channel ID */
+} ng_l2cap_l2ca_grp_close_ip;
+
+#if 0
+/* L2CAP -> Upper */
+ * typedef struct {
+ * u_int16_t result; /* 0x00 - success */
+ * } ng_l2cap_l2ca_grp_close_op;
+#endif
+
+/* L2CA_GroupAddMember */
+#define NGM_L2CAP_L2CA_GRP_ADD_MEMBER 0x8c
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local group channel ID */
+ bdaddr_t bdaddr; /* remote unit address */
+} ng_l2cap_l2ca_grp_add_member_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+} ng_l2cap_l2ca_grp_add_member_op;
+
+/* L2CA_GroupRemoveMember */
+#define NGM_L2CAP_L2CA_GRP_REM_MEMBER 0x8d
+/* Upper -> L2CAP */
+typedef ng_l2cap_l2ca_grp_add_member_ip ng_l2cap_l2ca_grp_rem_member_ip;
+
+/* L2CAP -> Upper */
+#if 0
+ * typedef ng_l2cap_l2ca_grp_add_member_op ng_l2cap_l2ca_grp_rem_member_op;
+#endif
+
+/* L2CA_GroupMembeship */
+#define NGM_L2CAP_L2CA_GRP_MEMBERSHIP 0x8e
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t lcid; /* local group channel ID */
+} ng_l2cap_l2ca_grp_get_members_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t nmembers; /* number of group members */
+/* bdaddr_t members[] -- group memebers */
+} ng_l2cap_l2ca_grp_get_members_op;
+
+/* L2CA_Ping */
+#define NGM_L2CAP_L2CA_PING 0x8f
+/* Upper -> L2CAP */
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int16_t echo_size; /* size of echo data in bytes */
+/* u_int8_t echo_data[] -- echo data */
+} ng_l2cap_l2ca_ping_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int16_t echo_size; /* size of echo data in bytes */
+/* u_int8_t echo_data[] -- echo data */
+} ng_l2cap_l2ca_ping_op;
+
+/* L2CA_GetInfo */
+#define NGM_L2CAP_L2CA_GET_INFO 0x90
+/* Upper -> L2CAP */
+typedef struct {
+ bdaddr_t bdaddr; /* remote unit address */
+ u_int16_t info_type; /* info type */
+} ng_l2cap_l2ca_get_info_ip;
+
+/* L2CAP -> Upper */
+typedef struct {
+ u_int16_t result; /* 0x00 - success */
+ u_int16_t info_size; /* size of info data in bytes */
+/* u_int8_t info_data[] -- info data */
+} ng_l2cap_l2ca_get_info_op;
+
+/* L2CA_EnableCLT/L2CA_DisableCLT */
+#define NGM_L2CAP_L2CA_ENABLE_CLT 0x91
+/* Upper -> L2CAP */
+typedef struct {
+ u_int16_t psm; /* Protocol/Service Multiplexor */
+ u_int16_t enable; /* 0x00 - disable */
+} ng_l2cap_l2ca_enable_clt_ip;
+
+#if 0
+/* L2CAP -> Upper */
+ * typedef struct {
+ * u_int16_t result; /* 0x00 - success */
+ * } ng_l2cap_l2ca_enable_clt_op;
+#endif
+
+/**************************************************************************
+ **************************************************************************
+ ** L2CAP node messages
+ **************************************************************************
+ **************************************************************************/
+
+/* L2CAP connection states */
+#define NG_L2CAP_CON_CLOSED 0 /* connection closed */
+#define NG_L2CAP_W4_LP_CON_CFM 1 /* waiting... */
+#define NG_L2CAP_CON_OPEN 2 /* connection open */
+
+/* L2CAP channel states */
+#define NG_L2CAP_CLOSED 0 /* channel closed */
+#define NG_L2CAP_W4_L2CAP_CON_RSP 1 /* wait for L2CAP resp. */
+#define NG_L2CAP_W4_L2CA_CON_RSP 2 /* wait for upper resp. */
+#define NG_L2CAP_CONFIG 3 /* L2CAP configuration */
+#define NG_L2CAP_OPEN 4 /* channel open */
+#define NG_L2CAP_W4_L2CAP_DISCON_RSP 5 /* wait for L2CAP discon. */
+#define NG_L2CAP_W4_L2CA_DISCON_RSP 6 /* wait for upper discon. */
+
+/* Node flags */
+#define NG_L2CAP_CLT_SDP_DISABLED (1 << 0) /* disable SDP CLT */
+#define NG_L2CAP_CLT_RFCOMM_DISABLED (1 << 1) /* disable RFCOMM CLT */
+#define NG_L2CAP_CLT_TCP_DISABLED (1 << 2) /* disable TCP CLT */
+
+/* Debug levels */
+#define NG_L2CAP_ALERT_LEVEL 1
+#define NG_L2CAP_ERR_LEVEL 2
+#define NG_L2CAP_WARN_LEVEL 3
+#define NG_L2CAP_INFO_LEVEL 4
+
+/* Get node flags (see flags above) */
+#define NGM_L2CAP_NODE_GET_FLAGS 0x400 /* L2CAP -> User */
+typedef u_int16_t ng_l2cap_node_flags_ep;
+
+/* Get/Set debug level (see levels above) */
+#define NGM_L2CAP_NODE_GET_DEBUG 0x401 /* L2CAP -> User */
+#define NGM_L2CAP_NODE_SET_DEBUG 0x402 /* User -> L2CAP */
+typedef u_int16_t ng_l2cap_node_debug_ep;
+
+#define NGM_L2CAP_NODE_HOOK_INFO 0x409 /* L2CAP -> Upper */
+/* bdaddr_t bdaddr; -- local (source BDADDR) */
+
+#define NGM_L2CAP_NODE_GET_CON_LIST 0x40a /* User -> L2CAP */
+typedef struct {
+ u_int32_t num_connections; /* number of connections */
+} ng_l2cap_node_con_list_ep;
+
+/* Connection flags */
+#define NG_L2CAP_CON_TX (1 << 0) /* sending data */
+#define NG_L2CAP_CON_RX (1 << 1) /* receiving data */
+
+typedef struct {
+ u_int8_t state; /* connection state */
+ u_int8_t flags; /* flags */
+ int16_t pending; /* num. pending packets */
+ u_int16_t con_handle; /* connection handle */
+ bdaddr_t remote; /* remote bdaddr */
+} ng_l2cap_node_con_ep;
+
+#define NG_L2CAP_MAX_CON_NUM \
+ ((0xffff - sizeof(ng_l2cap_node_con_list_ep))/sizeof(ng_l2cap_node_con_ep))
+
+#define NGM_L2CAP_NODE_GET_CHAN_LIST 0x40b /* User -> L2CAP */
+typedef struct {
+ u_int32_t num_channels; /* number of channels */
+} ng_l2cap_node_chan_list_ep;
+
+typedef struct {
+ u_int32_t state; /* channel state */
+
+ u_int16_t scid; /* source (local) channel ID */
+ u_int16_t dcid; /* destination (remote) channel ID */
+
+ u_int16_t imtu; /* incomming MTU */
+ u_int16_t omtu; /* outgoing MTU */
+
+ u_int16_t psm; /* PSM */
+ bdaddr_t remote; /* remote bdaddr */
+} ng_l2cap_node_chan_ep;
+
+#define NG_L2CAP_MAX_CHAN_NUM \
+ ((0xffff - sizeof(ng_l2cap_node_chan_list_ep))/sizeof(ng_l2cap_node_chan_ep))
+
+#endif /* ndef _NETGRAPH_L2CAP_H_ */
+
diff --git a/sys/netgraph/bluetooth/include/ng_ubt.h b/sys/netgraph/bluetooth/include/ng_ubt.h
new file mode 100644
index 0000000..1c55ad4
--- /dev/null
+++ b/sys/netgraph/bluetooth/include/ng_ubt.h
@@ -0,0 +1,103 @@
+/*
+ * ng_ubt.h
+ *
+ * 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_ubt.h,v 1.1 2002/11/09 19:09:02 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NG_UBT_H_
+#define _NG_UBT_H_
+
+/* XXX FIXME Does not belong here. Move to usbdevs.h later */
+#define USB_VENDOR_MSI 0x0db0 /* MSI www.msi.com.tw */
+#define USB_VENDOR_EPOX 0x0a12 /* EPoX www.epox.com */
+
+#define USB_PRODUCT_3COM_3CREB96 0x00a0 /* 3Com Bluetooth USB dongle */
+#define USB_PRODUCT_MITSUMI_BT_DONGLE 0x641f /* Mitsumi Bluetooth USB dongle*/
+#define USB_PRODUCT_TDK_BT_DONGLE 0x0309 /* TDK Bluetooth USB dongle */
+#define USB_PRODUCT_MSI_BT_DONGLE 0x1967 /* MSI Bluetooth USB dongle */
+#define USB_PRODUCT_DBW_120M_BT_DONGLE 0x2033 /* D-Link DBW-120M */
+#define USB_PRODUCT_BT_DG02_DONGLE 0x0001 /* EPoX BT-DG02 USB dongle */
+
+/* XXX FIXME Does not belong here. Move to usb.h later */
+#define UICLASS_WIRELESS_CONTROLLER 0xe0 /* Wireless Controller */
+#define UISUBCLASS_RF_CONTROLLER 0x01 /* RF Controller */
+#define UIPROTO_BLUETOOTH 0x01 /* Bluetooth programming */
+
+/**************************************************************************
+ **************************************************************************
+ ** Netgraph node hook name, type name and type cookie and commands
+ **************************************************************************
+ **************************************************************************/
+
+#define NG_UBT_NODE_TYPE "ubt"
+#define NG_UBT_HOOK "hook"
+
+#define NGM_UBT_COOKIE 1021837971
+
+/* Debug levels */
+#define NG_UBT_ALERT_LEVEL 1
+#define NG_UBT_ERR_LEVEL 2
+#define NG_UBT_WARN_LEVEL 3
+#define NG_UBT_INFO_LEVEL 4
+
+/**************************************************************************
+ **************************************************************************
+ ** UBT node command/event parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define NGM_UBT_NODE_SET_DEBUG 1 /* set debug level */
+#define NGM_UBT_NODE_GET_DEBUG 2 /* get debug level */
+typedef u_int16_t ng_ubt_node_debug_ep;
+
+#define NGM_UBT_NODE_SET_QLEN 3 /* set queue length */
+#define NGM_UBT_NODE_GET_QLEN 4 /* get queue length */
+typedef struct {
+ int32_t queue; /* queue index */
+#define NGM_UBT_NODE_QUEUE_IN 1 /* incoming queue */
+#define NGM_UBT_NODE_QUEUE_CMD 2 /* commands */
+#define NGM_UBT_NODE_QUEUE_ACL 3 /* ACL data */
+#define NGM_UBT_NODE_QUEUE_SCO 4 /* SCO data */
+
+ int32_t qlen; /* queue length */
+} ng_ubt_node_qlen_ep;
+
+#define NGM_UBT_NODE_GET_STAT 5 /* get statistic */
+typedef struct {
+ u_int32_t pckts_recv; /* # of packets received */
+ u_int32_t bytes_recv; /* # of bytes received */
+ u_int32_t pckts_sent; /* # of packets sent */
+ u_int32_t bytes_sent; /* # of bytes sent */
+ u_int32_t oerrors; /* # of output errors */
+ u_int32_t ierrors; /* # of input errors */
+} ng_ubt_node_stat_ep;
+
+#define NGM_UBT_NODE_RESET_STAT 6 /* reset statistic */
+
+#endif /* ndef _NG_UBT_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/TODO b/sys/netgraph/bluetooth/l2cap/TODO
new file mode 100644
index 0000000..98d0440
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/TODO
@@ -0,0 +1,43 @@
+$Id: TODO,v 1.8 2002/09/06 21:03:58 max Exp $
+$FreeBSD$
+
+FIXME/TODO list
+
+0) Ping itself. Should L2CAP layer loopback data?
+
+1) Locking/SMP
+
+ External code now uses ng_send_fn to inject data into Netgraph, so
+ it should be fine as long as Netgraph is SMP safe. Just need to
+ verify it.
+
+2) Understand and implement L2CAP QoS
+
+ Will fix later. I only have CSR based hardware and it does not
+ support QoS.
+
+3) Better functions to manage CIDs and command ident's.
+
+ Resource manager is not good because it uses MTX_DEF mutexes,
+ (i.e. could block/sleep)
+
+4) Implement group channels (multicast)
+
+ Will fix later
+
+5) Add bytes/packets counters and commands to get/reset them
+
+ Will fix later. What to count?
+
+6) Better way to get information about channels
+
+ L2CAP can support about 65000 channels. Need define some good way
+ to get data from kernel to user space. For example if we need to pass
+ 1K of information for every channel, then worst case is that we need
+ to pass 65Mbytes of data from kernel to user space. Not good.
+
+7) Deal properly with "shutdown"s and hook "disconnect"s
+
+ For now we destroy all channels when upstream hook is disconnected.
+ Is there a better way to handle this?
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
new file mode 100644
index 0000000..0d28f1a
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
@@ -0,0 +1,365 @@
+/*
+ * ng_l2cap_cmds.c
+ *
+ * Copyright (c) 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_l2cap_cmds.c,v 1.14 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** L2CAP commands processing module
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Process L2CAP command queue on connection
+ */
+
+void
+ng_l2cap_con_wakeup(ng_l2cap_con_p con)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Find first non-pending command in the queue */
+ TAILQ_FOREACH(cmd, &con->cmd_list, next) {
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+
+ if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
+ break;
+ }
+
+ if (cmd == NULL)
+ return;
+
+ /* Detach command packet */
+ m = cmd->aux;
+ cmd->aux = NULL;
+
+ /* Process command */
+ switch (cmd->code) {
+ case NG_L2CAP_CMD_REJ:
+ case NG_L2CAP_DISCON_RSP:
+ case NG_L2CAP_ECHO_RSP:
+ case NG_L2CAP_INFO_RSP:
+ ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_CON_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_NO_RESOURCES, 0);
+ ng_l2cap_free_chan(cmd->ch); /* will free commands */
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_CON_RSP:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ if (cmd->ch != NULL) {
+ ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS :
+ NG_L2CAP_NO_RESOURCES);
+ if (error != 0)
+ ng_l2cap_free_chan(cmd->ch);
+ }
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_NO_RESOURCES);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_CFG_RSP:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_unlink_cmd(cmd);
+ if (cmd->ch != NULL)
+ ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS :
+ NG_L2CAP_NO_RESOURCES);
+ ng_l2cap_free_cmd(cmd);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
+ if (error != 0)
+ ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
+ else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_ping_rsp(con, cmd->token,
+ NG_L2CAP_NO_RESOURCES, NULL);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_get_info_rsp(con, cmd->token,
+ NG_L2CAP_NO_RESOURCES, NULL);
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } else
+ ng_l2cap_command_timeout(cmd,
+ bluetooth_l2cap_rtx_timeout());
+ break;
+
+ case NGM_L2CAP_L2CA_WRITE: {
+ int length = m->m_pkthdr.len;
+
+ if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
+ m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
+ if (m == NULL)
+ error = ENOBUFS;
+ else
+ mtod(m, ng_l2cap_clt_hdr_t *)->psm =
+ htole16(cmd->ch->psm);
+ }
+
+ if (error == 0)
+ error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
+
+ ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
+ (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
+ length);
+
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ } break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unknown command code=%d\n",
+ __func__, NG_NODE_NAME(con->l2cap->node), cmd->code));
+
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ break;
+ }
+} /* ng_l2cap_con_wakeup */
+
+/*
+ * We have failed to open ACL connection to the remote unit. Could be negative
+ * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
+ * remove all channels and remove connection descriptor.
+ */
+
+void
+ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_p cmd = NULL;
+ ng_l2cap_chan_p ch = NULL;
+
+ NG_L2CAP_INFO(
+"%s: %s - ACL connection failed, result=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), result);
+
+ /* Clean command queue */
+ while (!TAILQ_EMPTY(&con->cmd_list)) {
+ cmd = TAILQ_FIRST(&con->cmd_list);
+
+ ng_l2cap_unlink_cmd(cmd);
+ if(cmd->flags & NG_L2CAP_CMD_PENDING)
+ ng_l2cap_command_untimeout(cmd);
+
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+
+ switch (cmd->code) {
+ case NG_L2CAP_CMD_REJ:
+ case NG_L2CAP_DISCON_RSP:
+ case NG_L2CAP_ECHO_RSP:
+ case NG_L2CAP_INFO_RSP:
+ break;
+
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
+ break;
+
+ case NG_L2CAP_CON_RSP:
+ if (cmd->ch != NULL)
+ ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
+ result);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ case NG_L2CAP_CFG_RSP:
+ case NGM_L2CAP_L2CA_WRITE:
+ ng_l2cap_l2ca_discon_ind(cmd->ch);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
+ NG_L2CAP_SUCCESS);
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ result, NULL);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ result, NULL);
+ break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unexpected command code=%d\n",
+ __func__, NG_NODE_NAME(con->l2cap->node),
+ cmd->code));
+ break;
+ }
+
+ if (cmd->ch != NULL)
+ ng_l2cap_free_chan(cmd->ch);
+
+ ng_l2cap_free_cmd(cmd);
+ }
+
+ /*
+ * There still might be channels (in OPEN state?) that
+ * did not submit any commands, so diconnect them
+ */
+
+ LIST_FOREACH(ch, &l2cap->chan_list, next)
+ if (ch->con == con)
+ ng_l2cap_l2ca_discon_ind(ch);
+
+ /* Free connection descriptor */
+ ng_l2cap_free_con(con);
+} /* ng_l2cap_con_fail */
+
+/*
+ * Process L2CAP command timeout. In general - notify upper layer and destroy
+ * channel. Do not pay much attension to return code, just do our best.
+ */
+
+void
+ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_l2cap_cmd_p cmd = (ng_l2cap_cmd_p) arg1;
+
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command flags flags=%#x!\n",
+ __func__, NG_NODE_NAME(cmd->con->l2cap->node), cmd->flags));
+
+ cmd->flags &= ~NG_L2CAP_CMD_PENDING;
+ ng_l2cap_unlink_cmd(cmd);
+
+ switch (cmd->code) {
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
+ ng_l2cap_free_chan(cmd->ch);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
+ ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ /* Echo request timed out. Let the upper layer know */
+ ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ NG_L2CAP_TIMEOUT, NULL);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ /* Info request timed out. Let the upper layer know */
+ ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ NG_L2CAP_TIMEOUT, NULL);
+ break;
+
+ /* XXX FIXME add other commands */
+
+ default:
+ KASSERT(0,
+("%s: %s - unexpected command code=%d\n",
+ __func__, NG_NODE_NAME(cmd->con->l2cap->node),
+ cmd->code));
+ break;
+ }
+
+ ng_l2cap_free_cmd(cmd);
+} /* ng_l2cap_process_command_timeout */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.h
new file mode 100644
index 0000000..d107ad6
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.h
@@ -0,0 +1,403 @@
+/*
+ * ng_l2cap_cmds.h
+ *
+ * Copyright (c) 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_l2cap_cmds.h,v 1.9 2002/04/16 00:43:56 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_CMDS_H_
+#define _NETGRAPH_L2CAP_CMDS_H_
+
+/******************************************************************************
+ ******************************************************************************
+ ** L2CAP to L2CAP signaling command macros
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Note: All L2CAP implementations are required to support minimal signaling
+ * MTU of 48 bytes. In order to simplify things we will send one command
+ * per one L2CAP packet. Given evrything above we can assume that one
+ * signaling packet will fit into single mbuf.
+ */
+
+/* L2CAP_CommandRej */
+#define _ng_l2cap_cmd_rej(_m, _ident, _reason, _mtu, _scid, _dcid) \
+do { \
+ struct _cmd_rej { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_cmd_rej_cp param; \
+ ng_l2cap_cmd_rej_data_t data; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ c = mtod((_m), struct _cmd_rej *); \
+ c->hdr.code = NG_L2CAP_CMD_REJ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = sizeof(c->param); \
+ \
+ c->param.reason = htole16((_reason)); \
+ \
+ if ((_reason) == NG_L2CAP_REJ_MTU_EXCEEDED) { \
+ c->data.mtu.mtu = htole16((_mtu)); \
+ c->hdr.length += sizeof(c->data.mtu); \
+ } else if ((_reason) == NG_L2CAP_REJ_INVALID_CID) { \
+ c->data.cid.scid = htole16((_scid)); \
+ c->data.cid.dcid = htole16((_dcid)); \
+ c->hdr.length += sizeof(c->data.cid); \
+ } \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(c->hdr) + \
+ c->hdr.length; \
+ \
+ c->hdr.length = htole16(c->hdr.length); \
+} while (0)
+
+/* L2CAP_ConnectReq */
+#define _ng_l2cap_con_req(_m, _ident, _psm, _scid) \
+do { \
+ struct _con_req { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_con_req_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _con_req *); \
+ c->hdr.code = NG_L2CAP_CON_REQ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = htole16(sizeof(c->param)); \
+ \
+ c->param.psm = htole16((_psm)); \
+ c->param.scid = htole16((_scid)); \
+} while (0)
+
+/* L2CAP_ConnectRsp */
+#define _ng_l2cap_con_rsp(_m, _ident, _dcid, _scid, _result, _status) \
+do { \
+ struct _con_rsp { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_con_rsp_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _con_rsp *); \
+ c->hdr.code = NG_L2CAP_CON_RSP; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = htole16(sizeof(c->param)); \
+ \
+ c->param.dcid = htole16((_dcid)); \
+ c->param.scid = htole16((_scid)); \
+ c->param.result = htole16((_result)); \
+ c->param.status = htole16((_status)); \
+} while (0)
+
+/* L2CAP_ConfigReq */
+#define _ng_l2cap_cfg_req(_m, _ident, _dcid, _flags, _data) \
+do { \
+ struct _cfg_req { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_cfg_req_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) { \
+ NG_FREE_M((_data)); \
+ break; \
+ } \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _cfg_req *); \
+ c->hdr.code = NG_L2CAP_CFG_REQ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = sizeof(c->param); \
+ \
+ c->param.dcid = htole16((_dcid)); \
+ c->param.flags = htole16((_flags)); \
+ if ((_data) != NULL) { \
+ m_cat((_m), (_data)); \
+ c->hdr.length += (_data)->m_pkthdr.len; \
+ (_m)->m_pkthdr.len += (_data)->m_pkthdr.len; \
+ } \
+ \
+ c->hdr.length = htole16(c->hdr.length); \
+} while (0)
+
+/* L2CAP_ConfigRsp */
+#define _ng_l2cap_cfg_rsp(_m, _ident, _scid, _flags, _result, _data) \
+do { \
+ struct _cfg_rsp { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_cfg_rsp_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) { \
+ NG_FREE_M((_data)); \
+ break; \
+ } \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _cfg_rsp *); \
+ c->hdr.code = NG_L2CAP_CFG_RSP; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = sizeof(c->param); \
+ \
+ c->param.scid = htole16((_scid)); \
+ c->param.flags = htole16((_flags)); \
+ c->param.result = htole16((_result)); \
+ if ((_data) != NULL) { \
+ m_cat((_m), (_data)); \
+ c->hdr.length += (_data)->m_pkthdr.len; \
+ (_m)->m_pkthdr.len += (_data)->m_pkthdr.len; \
+ } \
+ \
+ c->hdr.length = htole16(c->hdr.length); \
+} while (0)
+
+/* Build configuration options */
+#define _ng_l2cap_build_cfg_options(_m, _mtu, _flush_timo, _flow) \
+do { \
+ u_int8_t *p = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = 0; \
+ p = mtod((_m), u_int8_t *); \
+ \
+ if ((_mtu) != NULL) { \
+ struct _cfg_opt_mtu { \
+ ng_l2cap_cfg_opt_t hdr; \
+ u_int16_t val; \
+ } __attribute__ ((packed)) *o = NULL; \
+ \
+ o = (struct _cfg_opt_mtu *) p; \
+ o->hdr.type = NG_L2CAP_OPT_MTU; \
+ o->hdr.length = sizeof(o->val); \
+ o->val = htole16(*(u_int16_t *)(_mtu)); \
+ \
+ (_m)->m_pkthdr.len += sizeof(*o); \
+ p += sizeof(*o); \
+ } \
+ \
+ if ((_flush_timo) != NULL) { \
+ struct _cfg_opt_flush { \
+ ng_l2cap_cfg_opt_t hdr; \
+ u_int16_t val; \
+ } __attribute__ ((packed)) *o = NULL; \
+ \
+ o = (struct _cfg_opt_flush *) p; \
+ o->hdr.type = NG_L2CAP_OPT_FLUSH_TIMO; \
+ o->hdr.length = sizeof(o->val); \
+ o->val = htole16(*(u_int16_t *)(_flush_timo)); \
+ \
+ (_m)->m_pkthdr.len += sizeof(*o); \
+ p += sizeof(*o); \
+ } \
+ \
+ if ((_flow) != NULL) { \
+ struct _cfg_opt_flow { \
+ ng_l2cap_cfg_opt_t hdr; \
+ ng_l2cap_flow_t val; \
+ } __attribute__ ((packed)) *o = NULL; \
+ \
+ o = (struct _cfg_opt_flow *) p; \
+ o->hdr.type = NG_L2CAP_OPT_QOS; \
+ o->hdr.length = sizeof(o->val); \
+ o->val.flags = ((ng_l2cap_flow_p)(_flow))->flags; \
+ o->val.service_type = ((ng_l2cap_flow_p) \
+ (_flow))->service_type; \
+ o->val.token_rate = \
+ htole32(((ng_l2cap_flow_p)(_flow))->token_rate);\
+ o->val.token_bucket_size = \
+ htole32(((ng_l2cap_flow_p) \
+ (_flow))->token_bucket_size); \
+ o->val.peak_bandwidth = \
+ htole32(((ng_l2cap_flow_p) \
+ (_flow))->peak_bandwidth); \
+ o->val.latency = htole32(((ng_l2cap_flow_p) \
+ (_flow))->latency); \
+ o->val.delay_variation = \
+ htole32(((ng_l2cap_flow_p) \
+ (_flow))->delay_variation); \
+ \
+ (_m)->m_pkthdr.len += sizeof(*o); \
+ } \
+ \
+ (_m)->m_len = (_m)->m_pkthdr.len; \
+} while (0)
+
+/* L2CAP_DisconnectReq */
+#define _ng_l2cap_discon_req(_m, _ident, _dcid, _scid) \
+do { \
+ struct _discon_req { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_discon_req_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _discon_req *); \
+ c->hdr.code = NG_L2CAP_DISCON_REQ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = htole16(sizeof(c->param)); \
+ \
+ c->param.dcid = htole16((_dcid)); \
+ c->param.scid = htole16((_scid)); \
+} while (0)
+
+/* L2CA_DisconnectRsp */
+#define _ng_l2cap_discon_rsp(_m, _ident, _dcid, _scid) \
+do { \
+ struct _discon_rsp { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_discon_rsp_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _discon_rsp *); \
+ c->hdr.code = NG_L2CAP_DISCON_RSP; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = htole16(sizeof(c->param)); \
+ \
+ c->param.dcid = htole16((_dcid)); \
+ c->param.scid = htole16((_scid)); \
+} while (0)
+
+/* L2CAP_EchoReq */
+#define _ng_l2cap_echo_req(_m, _ident, _data, _size) \
+do { \
+ ng_l2cap_cmd_hdr_t *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), ng_l2cap_cmd_hdr_t *); \
+ c->code = NG_L2CAP_ECHO_REQ; \
+ c->ident = (_ident); \
+ c->length = 0; \
+ \
+ if ((_data) != NULL) { \
+ m_copyback((_m), sizeof(*c), (_size), (_data)); \
+ c->length += (_size); \
+ } \
+ \
+ c->length = htole16(c->length); \
+} while (0)
+
+/* L2CAP_InfoReq */
+#define _ng_l2cap_info_req(_m, _ident, _type) \
+do { \
+ struct _info_req { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_info_req_cp param; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(*c); \
+ \
+ c = mtod((_m), struct _info_req *); \
+ c->hdr.code = NG_L2CAP_INFO_REQ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = htole16(sizeof(c->param)); \
+ \
+ c->param.type = htole16((_type)); \
+} while (0)
+
+/* L2CAP_InfoRsp */
+#define _ng_l2cap_info_rsp(_m, _ident, _type, _result, _mtu) \
+do { \
+ struct _info_rsp { \
+ ng_l2cap_cmd_hdr_t hdr; \
+ ng_l2cap_info_rsp_cp param; \
+ ng_l2cap_info_rsp_data_t data; \
+ } __attribute__ ((packed)) *c = NULL; \
+ \
+ MGETHDR((_m), M_DONTWAIT, MT_DATA); \
+ if ((_m) == NULL) \
+ break; \
+ \
+ c = mtod((_m), struct _info_rsp *); \
+ c->hdr.code = NG_L2CAP_INFO_REQ; \
+ c->hdr.ident = (_ident); \
+ c->hdr.length = sizeof(c->param); \
+ \
+ c->param.type = htole16((_type)); \
+ c->param.result = htole16((_result)); \
+ \
+ if ((_result) == NG_L2CAP_SUCCESS) { \
+ switch ((_type)) { \
+ case NG_L2CAP_CONNLESS_MTU: \
+ c->data.mtu.mtu = htole16((_mtu)); \
+ c->hdr.length += sizeof((c->data.mtu.mtu)); \
+ break; \
+ } \
+ } \
+ \
+ (_m)->m_pkthdr.len = (_m)->m_len = sizeof(c->hdr) + \
+ c->hdr.length; \
+ \
+ c->hdr.length = htole16(c->hdr.length); \
+} while (0)
+
+void ng_l2cap_con_wakeup (ng_l2cap_con_p);
+void ng_l2cap_con_fail (ng_l2cap_con_p, u_int16_t);
+void ng_l2cap_process_command_timeout (node_p, hook_p, void *, int);
+
+#endif /* ndef _NETGRAPH_L2CAP_CMDS_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c
new file mode 100644
index 0000000..3779b91
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c
@@ -0,0 +1,1337 @@
+/*
+ * ng_l2cap_evnt.c
+ *
+ * Copyright (c) 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_l2cap_evnt.c,v 1.18 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** L2CAP events processing module
+ ******************************************************************************
+ ******************************************************************************/
+
+static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);
+static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t);
+static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t);
+static int send_l2cap_reject
+ (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
+static int send_l2cap_con_rej
+ (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
+static int send_l2cap_cfg_rsp
+ (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
+static int get_next_l2cap_opt
+ (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
+
+/*
+ * Receive L2CAP packet. First get L2CAP header and verify packet. Than
+ * get destination channel and process packet.
+ */
+
+int
+ng_l2cap_receive(ng_l2cap_con_p con)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_hdr_t *hdr = NULL;
+ int error = 0;
+
+ /* Check packet */
+ if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->rx_pkt->m_pkthdr.len);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Get L2CAP header */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
+ hdr->length = le16toh(hdr->length);
+ hdr->dcid = le16toh(hdr->dcid);
+
+ /* Check payload size */
+ if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), hdr->length,
+ con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Process packet */
+ switch (hdr->dcid) {
+ case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
+ m_adj(con->rx_pkt, sizeof(*hdr));
+ error = ng_l2cap_process_signal_cmd(con);
+ break;
+
+ case NG_L2CAP_CLT_CID: /* Connectionless packet */
+ error = ng_l2cap_l2ca_clt_receive(con);
+ break;
+
+ default: /* Data packet */
+ error = ng_l2cap_l2ca_receive(con);
+ break;
+ }
+
+ return (error);
+drop:
+ NG_FREE_M(con->rx_pkt);
+
+ return (error);
+} /* ng_l2cap_receive */
+
+/*
+ * Process L2CAP signaling command. We already know that destination channel ID
+ * is 0x1 that means we have received signaling command from peer's L2CAP layer.
+ * So get command header, decode and process it.
+ *
+ * XXX do we need to check signaling MTU here?
+ */
+
+static int
+ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_hdr_t *hdr = NULL;
+ struct mbuf *m = NULL;
+
+ while (con->rx_pkt != NULL) {
+ /* Verify packet length */
+ if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->rx_pkt->m_pkthdr.len);
+ NG_FREE_M(con->rx_pkt);
+
+ return (EMSGSIZE);
+ }
+
+ /* Get signaling command */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
+ hdr->length = le16toh(hdr->length);
+ m_adj(con->rx_pkt, sizeof(*hdr));
+
+ /* Verify command length */
+ if (con->rx_pkt->m_pkthdr.len < hdr->length) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
+"Invalid command length=%d, m_pkthdr.len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ hdr->code, hdr->ident, hdr->length,
+ con->rx_pkt->m_pkthdr.len);
+ NG_FREE_M(con->rx_pkt);
+
+ return (EMSGSIZE);
+ }
+
+ /* Get the command, save the rest (if any) */
+ if (con->rx_pkt->m_pkthdr.len > hdr->length)
+ m = m_split(con->rx_pkt, hdr->length, M_DONTWAIT);
+ else
+ m = NULL;
+
+ /* Process command */
+ switch (hdr->code) {
+ case NG_L2CAP_CMD_REJ:
+ ng_l2cap_process_cmd_rej(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_process_con_req(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_CON_RSP:
+ ng_l2cap_process_con_rsp(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ ng_l2cap_process_cfg_req(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_CFG_RSP:
+ ng_l2cap_process_cfg_rsp(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_process_discon_req(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_DISCON_RSP:
+ ng_l2cap_process_discon_rsp(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ ng_l2cap_process_echo_req(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_ECHO_RSP:
+ ng_l2cap_process_echo_rsp(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ ng_l2cap_process_info_req(con, hdr->ident);
+ break;
+
+ case NG_L2CAP_INFO_RSP:
+ ng_l2cap_process_info_rsp(con, hdr->ident);
+ break;
+
+ default:
+ NG_L2CAP_ERR(
+"%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ hdr->code, hdr->ident, hdr->length);
+
+ /*
+ * Send L2CAP_CommandRej. Do not really care
+ * about the result
+ */
+
+ send_l2cap_reject(con, hdr->ident,
+ NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
+ NG_FREE_M(con->rx_pkt);
+ break;
+ }
+
+ con->rx_pkt = m;
+ }
+
+ return (0);
+} /* ng_l2cap_process_signal_cmd */
+
+/*
+ * Process L2CAP_CommandRej command
+ */
+
+static int
+ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_rej_cp *cp = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
+ cp->reason = le16toh(cp->reason);
+
+ /* Check if we have pending command descriptor */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd != NULL) {
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(con->l2cap->node), cmd->flags));
+
+ ng_l2cap_command_untimeout(cmd);
+ ng_l2cap_unlink_cmd(cmd);
+
+ switch (cmd->code) {
+ case NG_L2CAP_CON_REQ:
+ ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
+ ng_l2cap_free_chan(cmd->ch);
+ break;
+
+ case NG_L2CAP_CFG_REQ:
+ ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
+ break;
+
+ case NG_L2CAP_DISCON_REQ:
+ ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
+ ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
+ break;
+
+ case NG_L2CAP_ECHO_REQ:
+ ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ cp->reason, NULL);
+ break;
+
+ case NG_L2CAP_INFO_REQ:
+ ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ cp->reason, NULL);
+ break;
+
+ default:
+ NG_L2CAP_ALERT(
+"%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->code);
+ break;
+ }
+
+ ng_l2cap_free_cmd(cmd);
+ } else
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_CommandRej command. " \
+"Requested ident does not exist, ident=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident);
+
+ NG_FREE_M(con->rx_pkt);
+
+ return (0);
+} /* ng_l2cap_process_cmd_rej */
+
+/*
+ * Process L2CAP_ConnectReq command
+ */
+
+static int
+ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct mbuf *m = con->rx_pkt;
+ ng_l2cap_con_req_cp *cp = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ int error = 0;
+ u_int16_t dcid, psm;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(m, sizeof(*cp));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(m, ng_l2cap_con_req_cp *);
+ psm = le16toh(cp->psm);
+ dcid = le16toh(cp->scid);
+
+ NG_FREE_M(m);
+ con->rx_pkt = NULL;
+
+ /*
+ * Create new channel and send L2CA_ConnectInd notification
+ * to the upper layer protocol.
+ */
+
+ ch = ng_l2cap_new_chan(l2cap, con, psm);
+ if (ch == NULL)
+ return (send_l2cap_con_rej(con, ident, 0, dcid,
+ NG_L2CAP_NO_RESOURCES));
+
+ /* Update channel IDs */
+ ch->dcid = dcid;
+
+ /* Sent L2CA_ConnectInd notification to the upper layer */
+ ch->ident = ident;
+ ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
+
+ error = ng_l2cap_l2ca_con_ind(ch);
+ if (error != 0) {
+ send_l2cap_con_rej(con, ident, ch->scid, dcid,
+ (error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
+ NG_L2CAP_PSM_NOT_SUPPORTED);
+ ng_l2cap_free_chan(ch);
+ }
+
+ return (error);
+} /* ng_l2cap_process_con_req */
+
+/*
+ * Process L2CAP_ConnectRsp command
+ */
+
+static int
+ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct mbuf *m = con->rx_pkt;
+ ng_l2cap_con_rsp_cp *cp = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t scid, dcid, result, status;
+ int error = 0;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(m, sizeof(*cp));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(m, ng_l2cap_con_rsp_cp *);
+ dcid = le16toh(cp->dcid);
+ scid = le16toh(cp->scid);
+ result = le16toh(cp->result);
+ status = le16toh(cp->status);
+
+ NG_FREE_M(m);
+ con->rx_pkt = NULL;
+
+ /* Check if we have pending command descriptor */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident,
+ con->con_handle);
+
+ return (ENOENT);
+ }
+
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->flags));
+
+ /* Verify channel state, if invalid - do nothing */
+ if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConnectRsp. " \
+"Invalid channel state, cid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), scid,
+ cmd->ch->state);
+ goto reject;
+ }
+
+ /* Verify CIDs and send reject if does not match */
+ if (cmd->ch->scid != scid) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
+ scid);
+ goto reject;
+ }
+
+ /*
+ * Looks good. We got confirmation from our peer. Now process
+ * it. First disable RTX timer. Then check the result and send
+ * notification to the upper layer.
+ */
+
+ ng_l2cap_command_untimeout(cmd);
+
+ if (result == NG_L2CAP_PENDING) {
+ /*
+ * Our peer wants more time to complete connection. We shall
+ * start ERTX timer and wait. Keep command in the list.
+ */
+
+ cmd->ch->dcid = dcid;
+ ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
+
+ error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
+ result, status);
+ if (error != 0)
+ ng_l2cap_free_chan(cmd->ch);
+ } else {
+ ng_l2cap_unlink_cmd(cmd);
+
+ if (result == NG_L2CAP_SUCCESS) {
+ /*
+ * Channel is open. Complete command and move to CONFIG
+ * state. Since we have sent positive confirmation we
+ * expect to receive L2CA_Config request from the upper
+ * layer protocol.
+ */
+
+ cmd->ch->dcid = dcid;
+ cmd->ch->state = NG_L2CAP_CONFIG;
+ } else
+ /* There was an error, so close the channel */
+ NG_L2CAP_INFO(
+"%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), result,
+ status);
+
+ error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
+ result, status);
+
+ /* XXX do we have to remove the channel on error? */
+ if (error != 0 || result != NG_L2CAP_SUCCESS)
+ ng_l2cap_free_chan(cmd->ch);
+
+ ng_l2cap_free_cmd(cmd);
+ }
+
+ return (error);
+
+reject:
+ /* Send reject. Do not really care about the result */
+ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
+
+ return (0);
+} /* ng_l2cap_process_con_rsp */
+
+/*
+ * Process L2CAP_ConfigReq command
+ */
+
+static int
+ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct mbuf *m = con->rx_pkt;
+ ng_l2cap_cfg_req_cp *cp = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ u_int16_t dcid, respond, result;
+ ng_l2cap_cfg_opt_t hdr;
+ ng_l2cap_cfg_opt_val_t val;
+ int off, error = 0;
+
+ /* Get command parameters */
+ con->rx_pkt = NULL;
+ NG_L2CAP_M_PULLUP(m, sizeof(*cp));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(m, ng_l2cap_cfg_req_cp *);
+ dcid = le16toh(cp->dcid);
+ respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
+ m_adj(m, sizeof(*cp));
+
+ /* Check if we have this channel and it is in valid state */
+ ch = ng_l2cap_chan_by_scid(l2cap, dcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConfigReq command. " \
+"Channel does not exist, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), dcid);
+ goto reject;
+ }
+
+ /* Verify channel state */
+ if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConfigReq. " \
+"Invalid channel state, cid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
+ goto reject;
+ }
+
+ if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
+ ch->cfg_state = 0;
+ ch->state = NG_L2CAP_CONFIG;
+ }
+
+ for (result = 0, off = 0; ; ) {
+ error = get_next_l2cap_opt(m, &off, &hdr, &val);
+ if (error == 0) { /* We done with this packet */
+ NG_FREE_M(m);
+ break;
+ } else if (error > 0) { /* Got option */
+ switch (hdr.type) {
+ case NG_L2CAP_OPT_MTU:
+ ch->omtu = val.mtu;
+ break;
+
+ case NG_L2CAP_OPT_FLUSH_TIMO:
+ ch->flush_timo = val.flush_timo;
+ break;
+
+ case NG_L2CAP_OPT_QOS:
+ bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
+ break;
+
+ default:
+ KASSERT(0,
+("%s: %s - unknown option: %d\n", __func__, NG_NODE_NAME(l2cap->node),
+ hdr.type));
+ break;
+ }
+ } else { /* Oops, something is wrong */
+ respond = 1;
+
+ if (error == -3) {
+
+ /*
+ * Adjust mbuf so we can get to the start
+ * of the first option we did not like.
+ */
+
+ m_adj(m, off - sizeof(hdr));
+ m->m_pkthdr.len = sizeof(hdr) + hdr.length;
+
+ result = NG_L2CAP_UNKNOWN_OPTION;
+ } else {
+ /* XXX FIXME Send other reject codes? */
+ NG_FREE_M(m);
+ result = NG_L2CAP_REJECT;
+ }
+
+ break;
+ }
+ }
+
+ /*
+ * Now check and see if we have to respond. If everything was OK then
+ * respond contain "C flag" and (if set) we will respond with empty
+ * packet and will wait for more options.
+ *
+ * Other case is that we did not like peer's options and will respond
+ * with L2CAP_Config response command with Reject error code.
+ *
+ * When "respond == 0" than we have received all options and we will
+ * sent L2CA_ConfigInd event to the upper layer protocol.
+ */
+
+ if (respond) {
+ error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
+ if (error != 0) {
+ ng_l2cap_l2ca_discon_ind(ch);
+ ng_l2cap_free_chan(ch);
+ }
+ } else {
+ /* Send L2CA_ConfigInd event to the upper layer protocol */
+ ch->ident = ident;
+ error = ng_l2cap_l2ca_cfg_ind(ch);
+ if (error != 0)
+ ng_l2cap_free_chan(ch);
+ }
+
+ return (error);
+
+reject:
+ /* Send reject. Do not really care about the result */
+ NG_FREE_M(m);
+
+ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
+
+ return (0);
+} /* ng_l2cap_process_cfg_req */
+
+/*
+ * Process L2CAP_ConfigRsp command
+ */
+
+static int
+ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct mbuf *m = con->rx_pkt;
+ ng_l2cap_cfg_rsp_cp *cp = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t scid, cflag, result;
+ ng_l2cap_cfg_opt_t hdr;
+ ng_l2cap_cfg_opt_val_t val;
+ int off, error = 0;
+
+ /* Get command parameters */
+ con->rx_pkt = NULL;
+ NG_L2CAP_M_PULLUP(m, sizeof(*cp));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
+ scid = le16toh(cp->scid);
+ cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
+ result = le16toh(cp->result);
+ m_adj(m, sizeof(*cp));
+
+ /* Check if we have this command */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident,
+ con->con_handle);
+ NG_FREE_M(m);
+
+ return (ENOENT);
+ }
+
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->flags));
+
+ /* Verify CIDs and send reject if does not match */
+ if (cmd->ch->scid != scid) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConfigRsp. " \
+"Channel ID does not match, scid=%d(%d)\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
+ scid);
+ goto reject;
+ }
+
+ /* Verify channel state and reject if invalid */
+ if (cmd->ch->state != NG_L2CAP_CONFIG) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_ConfigRsp. " \
+"Invalid channel state, scid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
+ cmd->ch->state);
+ goto reject;
+ }
+
+ /*
+ * Looks like it is our response, so process it. First parse options,
+ * then verify C flag. If it is set then we shall expect more
+ * configuration options from the peer and we will wait. Otherwise we
+ * have received all options and we will send L2CA_ConfigRsp event to
+ * the upper layer protocol.
+ */
+
+ ng_l2cap_command_untimeout(cmd);
+
+ for (off = 0; ; ) {
+ error = get_next_l2cap_opt(m, &off, &hdr, &val);
+ if (error == 0) /* We done with this packet */
+ break;
+ else if (error > 0) { /* Got option */
+ switch (hdr.type) {
+ case NG_L2CAP_OPT_MTU:
+ cmd->ch->imtu = val.mtu;
+ break;
+
+ case NG_L2CAP_OPT_FLUSH_TIMO:
+ cmd->ch->flush_timo = val.flush_timo;
+ break;
+
+ case NG_L2CAP_OPT_QOS:
+ bcopy(&val.flow, &cmd->ch->oflow,
+ sizeof(cmd->ch->oflow));
+ break;
+
+ default:
+ KASSERT(0,
+("%s: %s - unknown option: %d\n", __func__, NG_NODE_NAME(l2cap->node),
+ hdr.type));
+ break;
+ }
+ } else {
+ /*
+ * XXX FIXME What to do here?
+ *
+ * This is really BAD :( options packet was broken,
+ * so let upper layer know and do not wait for more
+ * options
+ */
+
+ NG_L2CAP_ALERT(
+"%s: %s - failed to parse configuration options, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), error);
+
+ result = NG_L2CAP_UNKNOWN;
+ cflag = 0;
+
+ break;
+ }
+ }
+
+ NG_FREE_M(m);
+
+ if (cflag) /* Restart timer and wait for more options */
+ ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
+ else {
+ ng_l2cap_unlink_cmd(cmd);
+
+ /* Send L2CA_Config response to the upper layer protocol */
+ error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
+ if (error != 0) {
+ /*
+ * XXX FIXME what to do here? we were not able to send
+ * response to the upper layer protocol, so for now
+ * just close the channel. Send L2CAP_Disconnect to
+ * remote peer?
+ */
+
+ NG_L2CAP_ERR(
+"%s: %s - failed to send L2CA_Config response, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), error);
+
+ ng_l2cap_free_chan(cmd->ch);
+ }
+
+ ng_l2cap_free_cmd(cmd);
+ }
+
+ return (error);
+
+reject:
+ /* Send reject. Do not really care about the result */
+ NG_FREE_M(m);
+
+ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
+
+ return (0);
+} /* ng_l2cap_process_cfg_rsp */
+
+/*
+ * Process L2CAP_DisconnectReq command
+ */
+
+static int
+ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_discon_req_cp *cp = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t scid, dcid;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
+ dcid = le16toh(cp->dcid);
+ scid = le16toh(cp->scid);
+
+ NG_FREE_M(con->rx_pkt);
+
+ /* Check if we have this channel and it is in valid state */
+ ch = ng_l2cap_chan_by_scid(l2cap, dcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectReq message. " \
+"Channel does not exist, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), dcid);
+ goto reject;
+ }
+
+ /* XXX Verify channel state and reject if invalid -- is that true? */
+ if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectReq. " \
+"Invalid channel state, cid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
+ goto reject;
+ }
+
+ /* Match destination channel ID */
+ if (ch->dcid != scid || ch->scid != dcid) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectReq. " \
+"Channel IDs does not match, channel: scid=%d, dcid=%d, " \
+"request: scid=%d, dcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
+ scid, dcid);
+ goto reject;
+ }
+
+ /*
+ * Looks good, so notify upper layer protocol that channel is about
+ * to be disconnected and send L2CA_DisconnectInd message. Then respond
+ * with L2CAP_DisconnectRsp.
+ */
+
+ ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
+ ng_l2cap_free_chan(ch);
+
+ /* Send L2CAP_DisconnectRsp */
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
+ if (cmd == NULL)
+ return (ENOMEM);
+
+ _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+
+ return (ENOBUFS);
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+
+reject:
+ /* Send reject. Do not really care about the result */
+ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
+
+ return (0);
+} /* ng_l2cap_process_discon_req */
+
+/*
+ * Process L2CAP_DisconnectRsp command
+ */
+
+static int
+ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_discon_rsp_cp *cp = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t scid, dcid;
+ int error = 0;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
+ dcid = le16toh(cp->dcid);
+ scid = le16toh(cp->scid);
+
+ NG_FREE_M(con->rx_pkt);
+
+ /* Check if we have pending command descriptor */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident,
+ con->con_handle);
+ goto out;
+ }
+
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->flags));
+
+ /* Verify channel state, do nothing if invalid */
+ if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectRsp. " \
+"Invalid channel state, cid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), scid,
+ cmd->ch->state);
+ goto out;
+ }
+
+ /* Verify CIDs and send reject if does not match */
+ if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_DisconnectRsp. " \
+"Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
+ scid, cmd->ch->dcid, dcid);
+ goto out;
+ }
+
+ /*
+ * Looks like we have successfuly disconnected channel,
+ * so notify upper layer.
+ */
+
+ ng_l2cap_command_untimeout(cmd);
+ error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
+ ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
+out:
+ return (error);
+} /* ng_l2cap_process_discon_rsp */
+
+/*
+ * Process L2CAP_EchoReq command
+ */
+
+static int
+ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_hdr_t *hdr = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+
+ con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
+ if (con->rx_pkt == NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - ng_l2cap_prepend() failed, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
+
+ return (ENOBUFS);
+ }
+
+ hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
+ hdr->code = NG_L2CAP_ECHO_RSP;
+ hdr->ident = ident;
+ hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
+
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
+ if (cmd == NULL) {
+ NG_FREE_M(con->rx_pkt);
+
+ return (ENOBUFS);
+ }
+
+ /* Attach data and link command to the queue */
+ cmd->aux = con->rx_pkt;
+ con->rx_pkt = NULL;
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+} /* ng_l2cap_process_echo_req */
+
+/*
+ * Process L2CAP_EchoRsp command
+ */
+
+static int
+ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Check if we have this command */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd != NULL) {
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(l2cap->node)));
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->flags));
+
+ ng_l2cap_command_untimeout(cmd);
+ ng_l2cap_unlink_cmd(cmd);
+
+ error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
+ NG_L2CAP_SUCCESS, con->rx_pkt);
+
+ ng_l2cap_free_cmd(cmd);
+ con->rx_pkt = NULL;
+ } else {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_EchoRsp command. " \
+"Requested ident does not exist, ident=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident);
+ NG_FREE_M(con->rx_pkt);
+ }
+
+ return (error);
+} /* ng_l2cap_process_echo_rsp */
+
+/*
+ * Process L2CAP_InfoReq command
+ */
+
+static int
+ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t type;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
+ NG_FREE_M(con->rx_pkt);
+
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
+ if (cmd == NULL)
+ return (ENOMEM);
+
+ switch (type) {
+ case NG_L2CAP_CONNLESS_MTU:
+ _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
+ NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
+ break;
+
+ default:
+ _ng_l2cap_info_rsp(cmd->aux, ident, type,
+ NG_L2CAP_NOT_SUPPORTED, 0);
+ break;
+ }
+
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+
+ return (ENOBUFS);
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+} /* ng_l2cap_process_info_req */
+
+/*
+ * Process L2CAP_InfoRsp command
+ */
+
+static int
+ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_info_rsp_cp *cp = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Get command parameters */
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
+ cp->type = le16toh(cp->type);
+ cp->result = le16toh(cp->result);
+ m_adj(con->rx_pkt, sizeof(*cp));
+
+ /* Check if we have pending command descriptor */
+ cmd = ng_l2cap_cmd_by_ident(con, ident);
+ if (cmd == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP_InfoRsp command. " \
+"Requested ident does not exist, ident=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ident);
+ NG_FREE_M(con->rx_pkt);
+
+ return (ENOENT);
+ }
+
+ KASSERT((cmd->con == con),
+("%s: %s - invalid connection pointer!\n",
+ __func__, NG_NODE_NAME(l2cap->node)));
+ KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
+("%s: %s - invalid command state, flags=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), cmd->flags));
+
+ ng_l2cap_command_untimeout(cmd);
+ ng_l2cap_unlink_cmd(cmd);
+
+ if (cp->result == NG_L2CAP_SUCCESS) {
+ switch (cp->type) {
+ case NG_L2CAP_CONNLESS_MTU:
+ if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
+ *mtod(con->rx_pkt, u_int16_t *) =
+ le16toh(*mtod(con->rx_pkt,u_int16_t *));
+ else {
+ cp->result = NG_L2CAP_UNKNOWN; /* XXX */
+
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP_InfoRsp command. " \
+"Bad connectionless MTU parameter, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->rx_pkt->m_pkthdr.len);
+ }
+ break;
+
+ default:
+ NG_L2CAP_WARN(
+"%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), cp->type);
+ break;
+ }
+ }
+
+ error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
+ cp->result, con->rx_pkt);
+
+ ng_l2cap_free_cmd(cmd);
+ con->rx_pkt = NULL;
+
+ return (error);
+} /* ng_l2cap_process_info_rsp */
+
+/*
+ * Send L2CAP reject
+ */
+
+static int
+send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
+ u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
+ if (cmd == NULL)
+ return (ENOMEM);
+
+ _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+
+ return (ENOBUFS);
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+} /* send_l2cap_reject */
+
+/*
+ * Send L2CAP connection reject
+ */
+
+static int
+send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
+ u_int16_t dcid, u_int16_t result)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
+ if (cmd == NULL)
+ return (ENOMEM);
+
+ _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+
+ return (ENOBUFS);
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+} /* send_l2cap_con_rej */
+
+/*
+ * Send L2CAP config response
+ */
+
+static int
+send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
+ u_int16_t result, struct mbuf *opt)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+
+ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
+ if (cmd == NULL) {
+ NG_FREE_M(opt);
+
+ return (ENOMEM);
+ }
+
+ _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+
+ return (ENOBUFS);
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+
+ return (0);
+} /* send_l2cap_cfg_rsp */
+
+/*
+ * Get next L2CAP configuration option
+ *
+ * Return codes:
+ * 0 no option
+ * 1 we have got option
+ * -1 header too short
+ * -2 bad option value or length
+ * -3 unknown option
+ */
+
+static int
+get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
+ ng_l2cap_cfg_opt_val_p val)
+{
+ int hint, len = m->m_pkthdr.len - (*off);
+
+ if (len == 0)
+ return (0);
+ if (len < 0 || len < sizeof(*hdr))
+ return (-1);
+
+ m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
+ *off += sizeof(*hdr);
+ len -= sizeof(*hdr);
+
+ hint = NG_L2CAP_OPT_HINT(hdr->type);
+ hdr->type &= NG_L2CAP_OPT_HINT_MASK;
+
+ switch (hdr->type) {
+ case NG_L2CAP_OPT_MTU:
+ if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
+ return (-2);
+
+ m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
+ val->mtu = le16toh(val->mtu);
+ *off += NG_L2CAP_OPT_MTU_SIZE;
+ break;
+
+ case NG_L2CAP_OPT_FLUSH_TIMO:
+ if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
+ len < hdr->length)
+ return (-2);
+
+ m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
+ val->flush_timo = le16toh(val->flush_timo);
+ *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
+ break;
+
+ case NG_L2CAP_OPT_QOS:
+ if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
+ return (-2);
+
+ m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
+ val->flow.token_rate = le32toh(val->flow.token_rate);
+ val->flow.token_bucket_size =
+ le32toh(val->flow.token_bucket_size);
+ val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
+ val->flow.latency = le32toh(val->flow.latency);
+ val->flow.delay_variation = le32toh(val->flow.delay_variation);
+ *off += NG_L2CAP_OPT_QOS_SIZE;
+ break;
+
+ default:
+ if (hint)
+ *off += hdr->length;
+ else
+ return (-3);
+ break;
+ }
+
+ return (1);
+} /* get_next_l2cap_opt */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.h
new file mode 100644
index 0000000..26fcac5
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.h
@@ -0,0 +1,38 @@
+/*
+ * ng_l2cap_evnt.h
+ *
+ * Copyright (c) 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_l2cap_evnt.h,v 1.2 2002/04/16 00:43:56 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_EVNT_H_
+#define _NETGRAPH_L2CAP_EVNT_H_
+
+int ng_l2cap_receive (ng_l2cap_con_p);
+
+#endif /* ndef _NETGRAPH_L2CAP_EVNT_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c
new file mode 100644
index 0000000..f505158
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c
@@ -0,0 +1,806 @@
+/*
+ * ng_l2cap_llpi.c
+ *
+ * Copyright (c) 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_l2cap_llpi.c,v 1.16 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** Lower Layer Protocol (HCI) Interface module
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Send LP_ConnectReq event to the lower layer protocol. Create new connection
+ * descriptor and initialize it. Create LP_ConnectReq event and send it to the
+ * lower layer, then adjust connection state and start timer. The function WILL
+ * FAIL if connection to the remote unit already exists.
+ */
+
+int
+ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr)
+{
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_con_req_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Verify that we DO NOT have connection to the remote unit */
+ con = ng_l2cap_con_by_addr(l2cap, bdaddr);
+ if (con != NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - unexpected LP_ConnectReq event. " \
+"Connection already exists, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+
+ return (EEXIST);
+ }
+
+ /* Check if lower layer protocol is still connected */
+ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
+ NG_L2CAP_ERR(
+"%s: %s - hook \"%s\" is not connected or valid\n",
+ __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and intialize new connection descriptor */
+ con = ng_l2cap_new_con(l2cap, bdaddr);
+ if (con == NULL)
+ return (ENOMEM);
+
+ /* Create and send LP_ConnectReq event */
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL) {
+ ng_l2cap_free_con(con);
+
+ return (ENOMEM);
+ }
+
+ ep = (ng_hci_lp_con_req_ep *) (msg->data);
+ bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
+ ep->link_type = NG_HCI_LINK_ACL;
+
+ con->state = NG_L2CAP_W4_LP_CON_CFM;
+ ng_l2cap_lp_timeout(con);
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, NULL);
+ if (error != 0)
+ ng_l2cap_free_con(con); /* will remove timeout */
+
+ return (error);
+} /* ng_l2cap_lp_con_req */
+
+/*
+ * Process LP_ConnectCfm event from the lower layer protocol. It could be
+ * positive or negative. Verify remote unit address then stop the timer and
+ * process event.
+ */
+
+int
+ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_hci_lp_con_cfm_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ep)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
+
+ /* Check if we have requested/accepted this connection */
+ con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
+ if (con == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Check connection state */
+ if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
+ NG_L2CAP_ALERT(
+"%s: %s - unexpected LP_ConnectCfm event. " \
+"Invalid connection state, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Looks like it is our confirmation. It is safe now to cancel
+ * connection timer and notify upper layer.
+ */
+
+ ng_l2cap_lp_untimeout(con);
+
+ if (ep->status == 0) {
+ con->state = NG_L2CAP_CON_OPEN;
+ con->con_handle = ep->con_handle;
+ ng_l2cap_lp_deliver(con);
+ } else {
+ /* Negative confirmation - remove connection descriptor */
+ con->state = NG_L2CAP_CON_CLOSED;
+ ng_l2cap_con_fail(con, ep->status);
+ }
+out:
+ return (error);
+} /* ng_l2cap_lp_con_cfm */
+
+/*
+ * Process LP_ConnectInd event from the lower layer protocol. This is a good
+ * place to put some extra check on remote unit address and/or class. We could
+ * even forward this information to control hook (or check against internal
+ * black list) and thus implement some kind of firewall. But for now be simple
+ * and create new connection descriptor, start timer and send LP_ConnectRsp
+ * event (i.e. accept connection).
+ */
+
+int
+ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_hci_lp_con_ind_ep *ep = NULL;
+ ng_hci_lp_con_rsp_ep *rp = NULL;
+ struct ng_mesg *rsp = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ep)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid LP_ConnectInd message size\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_con_ind_ep *) (msg->data);
+
+ /* Make sure we have only one connection to the remote unit */
+ con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
+ if (con != NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - unexpected LP_ConnectInd event. " \
+"Connection already exists, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+ error = EEXIST;
+ goto out;
+ }
+
+ /* Check if lower layer protocol is still connected */
+ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
+ NG_L2CAP_ERR(
+"%s: %s - hook \"%s\" is not connected or valid",
+ __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
+ error = ENOTCONN;
+ goto out;
+ }
+
+ /* Create and intialize new connection descriptor */
+ con = ng_l2cap_new_con(l2cap, &ep->bdaddr);
+ if (con == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Create and send LP_ConnectRsp event */
+ NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
+ sizeof(*rp), M_NOWAIT);
+ if (msg == NULL) {
+ ng_l2cap_free_con(con);
+ error = ENOMEM;
+ goto out;
+ }
+
+ rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
+ rp->status = 0x00; /* accept connection */
+ rp->link_type = NG_HCI_LINK_ACL;
+ bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
+
+ con->state = NG_L2CAP_W4_LP_CON_CFM;
+ ng_l2cap_lp_timeout(con);
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, NULL);
+ if (error != 0)
+ ng_l2cap_free_con(con); /* will remove timeout */
+out:
+ return (error);
+} /* ng_hci_lp_con_ind */
+
+/*
+ * Process LP_DisconnectInd event from the lower layer protocol. We have been
+ * disconnected from the remote unit. So notify the upper layer protocol.
+ */
+
+int
+ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_hci_lp_discon_ind_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ep)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid LP_DisconnectInd message size\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
+
+ /* Check if we have this connection */
+ con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
+ if (con == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_DisconnectInd event. " \
+"Connection does not exist, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* XXX Verify connection state -- do we need to check this? */
+ if (con->state != NG_L2CAP_CON_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_DisconnectInd event. " \
+"Invalid connection state, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* Notify upper layer and remove connection */
+ con->state = NG_L2CAP_CON_CLOSED;
+ ng_l2cap_con_fail(con, ep->reason);
+out:
+ return (error);
+} /* ng_l2cap_lp_discon_ind */
+
+/*
+ * Send LP_QoSSetupReq event to the lower layer protocol
+ */
+
+int
+ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
+ ng_l2cap_flow_p flow)
+{
+ struct ng_mesg *msg = NULL;
+ ng_hci_lp_qos_req_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Verify that we have this connection */
+ con = ng_l2cap_con_by_handle(l2cap, con_handle);
+ if (con == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_QoSSetupReq event. " \
+"Connection does not exist, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con_handle);
+
+ return (ENOENT);
+ }
+
+ /* Verify connection state */
+ if (con->state != NG_L2CAP_CON_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_QoSSetupReq event. " \
+"Invalid connection state, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+
+ return (EINVAL);
+ }
+
+ /* Check if lower layer protocol is still connected */
+ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
+ NG_L2CAP_ERR(
+"%s: %s - hook \"%s\" is not connected or valid",
+ __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send LP_QoSSetupReq event */
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
+ sizeof(*ep), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ ep = (ng_hci_lp_qos_req_ep *) (msg->data);
+ ep->con_handle = con_handle;
+ ep->flags = flow->flags;
+ ep->service_type = flow->service_type;
+ ep->token_rate = flow->token_rate;
+ ep->peak_bandwidth = flow->peak_bandwidth;
+ ep->latency = flow->latency;
+ ep->delay_variation = flow->delay_variation;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, NULL);
+
+ return (error);
+} /* ng_l2cap_lp_con_req */
+
+/*
+ * Process LP_QoSSetupCfm from the lower layer protocol
+ */
+
+int
+ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_hci_lp_qos_cfm_ep *ep = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ep)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
+ /* XXX FIXME do something */
+out:
+ return (error);
+} /* ng_l2cap_lp_qos_cfm */
+
+/*
+ * Process LP_QoSViolationInd event from the lower layer protocol. Lower
+ * layer protocol has detected QoS Violation, so we MUST notify the
+ * upper layer.
+ */
+
+int
+ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_hci_lp_qos_ind_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ep)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid LP_QoSViolation message size\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
+
+ /* Check if we have this connection */
+ con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
+ if (con == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_QoSViolationInd event. " \
+"Connection does not exist, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Verify connection state */
+ if (con->state != NG_L2CAP_CON_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected LP_QoSViolationInd event. " \
+"Invalid connection state, state=%d, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state,
+ con->con_handle);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* XXX FIXME Notify upper layer and terminate channels if required */
+out:
+ return (error);
+} /* ng_l2cap_qos_ind */
+
+/*
+ * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
+ * segment it according to HCI MTU.
+ */
+
+int
+ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_hdr_t *l2cap_hdr = NULL;
+ ng_hci_acldata_pkt_t *acl_hdr = NULL;
+ struct mbuf *m_last = NULL, *m = NULL;
+ int len, flag = NG_HCI_PACKET_START;
+
+ KASSERT((con->tx_pkt == NULL),
+("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
+ KASSERT((l2cap->pkt_size > 0),
+("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
+
+ /* Prepend mbuf with L2CAP header */
+ m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
+ if (m0 == NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - ng_l2cap_prepend(%d) failed\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ sizeof(*l2cap_hdr));
+
+ goto fail;
+ }
+
+ l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
+ l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
+ l2cap_hdr->dcid = htole16(dcid);
+
+ /*
+ * Segment single L2CAP packet according to the HCI layer MTU. Convert
+ * each segment into ACL data packet and prepend it with ACL data packet
+ * header. Link all segments together via m_nextpkt link.
+ *
+ * XXX BC (Broadcast flag) will always be 0 (zero).
+ */
+
+ while (m0 != NULL) {
+ /* Check length of the packet against HCI MTU */
+ len = m0->m_pkthdr.len;
+ if (len > l2cap->pkt_size) {
+ m = m_split(m0, l2cap->pkt_size, M_DONTWAIT);
+ if (m == NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - m_split(%d) failed\n", __func__, NG_NODE_NAME(l2cap->node),
+ l2cap->pkt_size);
+ goto fail;
+ }
+
+ len = l2cap->pkt_size;
+ }
+
+ /* Convert packet fragment into ACL data packet */
+ m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
+ if (m0 == NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - ng_l2cap_prepend(%d) failed\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ sizeof(*acl_hdr));
+ goto fail;
+ }
+
+ acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
+ acl_hdr->type = NG_HCI_ACL_DATA_PKT;
+ acl_hdr->length = htole16(len);
+ acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
+ con->con_handle, flag, 0));
+
+ /* Add fragment to the chain */
+ m0->m_nextpkt = NULL;
+
+ if (con->tx_pkt == NULL)
+ con->tx_pkt = m_last = m0;
+ else {
+ m_last->m_nextpkt = m0;
+ m_last = m0;
+ }
+
+ NG_L2CAP_INFO(
+"%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->con_handle,
+ flag, len);
+
+ m0 = m;
+ m = NULL;
+ flag = NG_HCI_PACKET_FRAGMENT;
+ }
+
+ return (0);
+fail:
+ NG_FREE_M(m0);
+ NG_FREE_M(m);
+
+ while (con->tx_pkt != NULL) {
+ m = con->tx_pkt->m_nextpkt;
+ m_freem(con->tx_pkt);
+ con->tx_pkt = m;
+ }
+
+ return (ENOBUFS);
+} /* ng_l2cap_lp_send */
+
+/*
+ * Receive ACL data packet from the HCI layer. First strip ACL packet header
+ * and get connection handle, PB (Packet Boundary) flag and payload length.
+ * Then find connection descriptor and verify its state. Then process ACL
+ * packet as follows.
+ *
+ * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
+ * header and get total length of the L2CAP packet. Then start new L2CAP
+ * packet.
+ *
+ * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
+ * then add segment to the packet.
+ */
+
+int
+ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
+{
+ ng_hci_acldata_pkt_t *acl_hdr = NULL;
+ ng_l2cap_hdr_t *l2cap_hdr = NULL;
+ ng_l2cap_con_p con = NULL;
+ u_int16_t con_handle, length, pb;
+ int error = 0;
+
+ /* Check ACL data packet */
+ if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Strip ACL data packet header */
+ NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ m_adj(m, sizeof(*acl_hdr));
+
+ /* Get ACL connection handle, PB flag and payload length */
+ acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
+ con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
+ pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
+ length = le16toh(acl_hdr->length);
+
+ NG_L2CAP_INFO(
+"%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
+
+ /* Get connection descriptor */
+ con = ng_l2cap_con_by_handle(l2cap, con_handle);
+ if (con == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected ACL data packet. " \
+"Connection does not exist, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con_handle);
+ error = ENOENT;
+ goto drop;
+ }
+
+ /* Verify connection state */
+ if (con->state != NG_L2CAP_CON_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->state);
+ error = EHOSTDOWN;
+ goto drop;
+ }
+
+ /* Process packet */
+ if (pb == NG_HCI_PACKET_START) {
+ if (con->rx_pkt != NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
+ NG_FREE_M(con->rx_pkt);
+ con->rx_pkt_len = 0;
+ }
+
+ /* Get L2CAP header */
+ if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ m->m_pkthdr.len);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
+
+ NG_L2CAP_INFO(
+"%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con_handle,
+ le16toh(l2cap_hdr->length));
+
+ /* Start new L2CAP packet */
+ con->rx_pkt = m;
+ con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
+ } else if (pb == NG_HCI_PACKET_FRAGMENT) {
+ if (con->rx_pkt == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->con_handle);
+ goto drop;
+ }
+
+ /* Add fragment to the L2CAP packet */
+ m_cat(con->rx_pkt, m);
+ con->rx_pkt->m_pkthdr.len += length;
+ } else {
+ NG_L2CAP_ERR(
+"%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
+ __func__, NG_NODE_NAME(l2cap->node), pb);
+ error = EINVAL;
+ goto drop;
+ }
+
+ con->rx_pkt_len -= length;
+ if (con->rx_pkt_len < 0) {
+ NG_L2CAP_ALERT(
+"%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
+ NG_FREE_M(con->rx_pkt);
+ con->rx_pkt_len = 0;
+ } else if (con->rx_pkt_len == 0) {
+ /* OK, we have got complete L2CAP packet, so process it */
+ error = ng_l2cap_receive(con);
+ con->rx_pkt = NULL;
+ con->rx_pkt_len = 0;
+ }
+
+ return (error);
+
+drop:
+ NG_FREE_M(m);
+
+ return (error);
+} /* ng_l2cap_lp_receive */
+
+/*
+ * Send queued ACL packets to the HCI layer
+ */
+
+void
+ng_l2cap_lp_deliver(ng_l2cap_con_p con)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct mbuf *m = NULL;
+ int error;
+
+ /* Check connection */
+ if (con->state != NG_L2CAP_CON_OPEN)
+ return;
+
+ if (con->tx_pkt == NULL)
+ ng_l2cap_con_wakeup(con);
+
+ if (con->tx_pkt == NULL)
+ return;
+
+ /* Check if lower layer protocol is still connected */
+ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
+ NG_L2CAP_ERR(
+"%s: %s - hook \"%s\" is not connected or valid",
+ __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
+
+ goto drop; /* XXX what to do with "pending"? */
+ }
+
+ /* Send ACL data packets */
+ while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
+ m = con->tx_pkt;
+ con->tx_pkt = con->tx_pkt->m_nextpkt;
+ m->m_nextpkt = NULL;
+
+ NG_L2CAP_INFO(
+"%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->con_handle,
+ m->m_pkthdr.len);
+
+ NG_SEND_DATA_ONLY(error, l2cap->hci, m);
+ if (error != 0) {
+ NG_L2CAP_ERR(
+"%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ con->con_handle, error);
+
+ goto drop; /* XXX what to do with "pending"? */
+ }
+
+ con->pending ++;
+ }
+
+ NG_L2CAP_INFO(
+"%s: %s - %d ACL packets have been sent, con_handle=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), con->pending,
+ con->con_handle);
+
+ return;
+
+drop:
+ while (con->tx_pkt != NULL) {
+ m = con->tx_pkt->m_nextpkt;
+ m_freem(con->tx_pkt);
+ con->tx_pkt = m;
+ }
+} /* ng_l2cap_lp_deliver */
+
+/*
+ * Process connection timeout. Remove connection from the list. If there
+ * are any channels that wait for the connection then notify them. Free
+ * connection descriptor.
+ */
+
+void
+ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_l2cap_con_p con = (ng_l2cap_con_p) arg1;
+ ng_l2cap_p l2cap = con->l2cap;
+
+ NG_L2CAP_ERR(
+"%s: %s - ACL connection timeout\n", __func__, NG_NODE_NAME(l2cap->node));
+
+ /*
+ * Notify channels that connection has timed out. This will remove
+ * connection, channels and pending commands.
+ */
+
+ con->state = NG_L2CAP_CON_CLOSED;
+ ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
+} /* ng_l2cap_process_lp_timeout */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h
new file mode 100644
index 0000000..161761e
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h
@@ -0,0 +1,48 @@
+/*
+ * ng_l2cap_llpi.h
+ *
+ * Copyright (c) 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_l2cap_llpi.h,v 1.6 2002/04/16 00:43:57 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_LLPI_H_
+#define _NETGRAPH_L2CAP_LLPI_H_
+
+int ng_l2cap_lp_con_req (ng_l2cap_p, bdaddr_p);
+int ng_l2cap_lp_con_cfm (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_lp_con_ind (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_lp_discon_ind (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_lp_qos_req (ng_l2cap_p, u_int16_t, ng_l2cap_flow_p);
+int ng_l2cap_lp_qos_cfm (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_lp_qos_ind (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_lp_send (ng_l2cap_con_p, u_int16_t,struct mbuf *);
+int ng_l2cap_lp_receive (ng_l2cap_p, struct mbuf *);
+void ng_l2cap_lp_deliver (ng_l2cap_con_p);
+void ng_l2cap_process_lp_timeout (node_p, hook_p, void *, int);
+
+#endif /* ndef _NETGRAPH_L2CAP_LLPI_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c
new file mode 100644
index 0000000..1dfc4c0
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c
@@ -0,0 +1,736 @@
+/*
+ * ng_l2cap_main.c
+ *
+ * Copyright (c) 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_l2cap_main.c,v 1.24 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+#include "ng_l2cap_prse.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** This node implements Link Layer Control and Adaptation Protocol (L2CAP)
+ ******************************************************************************
+ ******************************************************************************/
+
+/* MALLOC define */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_L2CAP, "netgraph_l2cap",
+ "Netgraph Bluetooth L2CAP node");
+#else
+#define M_NETGRAPH_L2CAP M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Netgraph node methods */
+static ng_constructor_t ng_l2cap_constructor;
+static ng_shutdown_t ng_l2cap_shutdown;
+static ng_newhook_t ng_l2cap_newhook;
+static ng_connect_t ng_l2cap_connect;
+static ng_disconnect_t ng_l2cap_disconnect;
+static ng_rcvmsg_t ng_l2cap_lower_rcvmsg;
+static ng_rcvmsg_t ng_l2cap_upper_rcvmsg;
+static ng_rcvmsg_t ng_l2cap_default_rcvmsg;
+static ng_rcvdata_t ng_l2cap_rcvdata;
+
+/* Netgraph node type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_L2CAP_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_l2cap_constructor, /* constructor */
+ ng_l2cap_default_rcvmsg,/* control message */
+ ng_l2cap_shutdown, /* destructor */
+ ng_l2cap_newhook, /* new hook */
+ NULL, /* findhook */
+ ng_l2cap_connect, /* connect hook */
+ ng_l2cap_rcvdata, /* data */
+ ng_l2cap_disconnect, /* disconnect hook */
+ ng_l2cap_cmdlist /* node command list */
+};
+NETGRAPH_INIT(l2cap, &typestruct);
+MODULE_VERSION(ng_l2cap, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_l2cap, ng_bluetooth, NG_BLUETOOTH_VERSION,
+ NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph methods implementation
+ *****************************************************************************
+ *****************************************************************************/
+
+static void ng_l2cap_cleanup (ng_l2cap_p);
+static void ng_l2cap_destroy_channels (ng_l2cap_p);
+
+/*
+ * Create new instance of L2CAP node
+ */
+
+static int
+ng_l2cap_constructor(node_p node)
+{
+ ng_l2cap_p l2cap = NULL;
+
+ /* Create new L2CAP node */
+ MALLOC(l2cap, ng_l2cap_p, sizeof(*l2cap),
+ M_NETGRAPH_L2CAP, M_NOWAIT|M_ZERO);
+ if (l2cap == NULL)
+ return (ENOMEM);
+
+ l2cap->node = node;
+ l2cap->debug = NG_L2CAP_WARN_LEVEL;
+
+ LIST_INIT(&l2cap->con_list);
+ LIST_INIT(&l2cap->chan_list);
+
+ NG_NODE_SET_PRIVATE(node, l2cap);
+ NG_NODE_FORCE_WRITER(node);
+
+ return (0);
+} /* ng_l2cap_constructor */
+
+/*
+ * Shutdown L2CAP node
+ */
+
+static int
+ng_l2cap_shutdown(node_p node)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+
+ /* Clean up L2CAP node. Delete all connection, channels and commands */
+ l2cap->node = NULL;
+ ng_l2cap_cleanup(l2cap);
+
+ bzero(l2cap, sizeof(*l2cap));
+ FREE(l2cap, M_NETGRAPH_L2CAP);
+
+ return (0);
+} /* ng_l2cap_shutdown */
+
+/*
+ * Give our OK for a hook to be added. HCI layer is connected to the HCI
+ * (NG_L2CAP_HOOK_HCI) hook. As per specification L2CAP layer MUST provide
+ * Procol/Service Multiplexing, so the L2CAP node provides separate hooks
+ * for SDP (NG_L2CAP_HOOK_SDP), RFCOMM (NG_L2CAP_HOOK_RFCOMM) and TCP
+ * (NG_L2CAP_HOOK_TCP) protcols. Unknown PSM will be forwarded to
+ * NG_L2CAP_HOOK_ORPHAN hook. Control node/application is connected to
+ * control (NG_L2CAP_HOOK_CTL) hook.
+ */
+
+static int
+ng_l2cap_newhook(node_p node, hook_p hook, char const *name)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+ hook_p *h = NULL;
+
+ if (strcmp(name, NG_L2CAP_HOOK_HCI) == 0)
+ h = &l2cap->hci;
+ else if (strcmp(name, NG_L2CAP_HOOK_L2C) == 0)
+ h = &l2cap->l2c;
+ else if (strcmp(name, NG_L2CAP_HOOK_CTL) == 0)
+ h = &l2cap->ctl;
+ else
+ return (EINVAL);
+
+ if (*h != NULL)
+ return (EISCONN);
+
+ *h = hook;
+
+ return (0);
+} /* ng_l2cap_newhook */
+
+/*
+ * Give our final OK to connect hook. Nothing to do here.
+ */
+
+static int
+ng_l2cap_connect(hook_p hook)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error = 0;
+
+ if (hook == l2cap->hci)
+ NG_HOOK_SET_RCVMSG(hook, ng_l2cap_lower_rcvmsg);
+ else
+ if (hook == l2cap->l2c || hook == l2cap->ctl) {
+ NG_HOOK_SET_RCVMSG(hook, ng_l2cap_upper_rcvmsg);
+
+ /* Send delayed notification to the upper layer */
+ error = ng_send_fn(l2cap->node, hook, ng_l2cap_send_hook_info,
+ NULL, 0);
+ } else
+ error = EINVAL;
+
+ return (error);
+} /* ng_l2cap_connect */
+
+/*
+ * Disconnect the hook. For downstream hook we must notify upper layers.
+ *
+ * XXX For upstream hooks this is really ugly :( Hook was disconnected and it
+ * XXX is now too late to do anything. For now we just clean up our own mess
+ * XXX and remove all channels that use disconnected upstream hook. If we don't
+ * XXX do that then L2CAP node can get out of sync with upper layers.
+ * XXX No notification will be sent to remote peer.
+ */
+
+static int
+ng_l2cap_disconnect(hook_p hook)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ hook_p *h = NULL;
+
+ if (hook == l2cap->hci) {
+ ng_l2cap_cleanup(l2cap);
+ h = &l2cap->hci;
+ } else
+ if (hook == l2cap->l2c) {
+ ng_l2cap_destroy_channels(l2cap);
+ h = &l2cap->l2c;
+ } else
+ if (hook == l2cap->ctl)
+ h = &l2cap->ctl;
+ else
+ return (EINVAL);
+
+ *h = NULL;
+
+ /* Shutdown when all hooks are disconnected */
+ if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
+ NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
+ ng_rmnode_self(NG_HOOK_NODE(hook));
+
+ return (0);
+} /* ng_l2cap_disconnect */
+
+/*
+ * Process control message from lower layer
+ */
+
+static int
+ng_l2cap_lower_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NGI_MSG(item); /* item still has message */
+ int error = 0;
+
+ switch (msg->header.typecookie) {
+ case NGM_HCI_COOKIE:
+ switch (msg->header.cmd) {
+ /* HCI node is ready */
+ case NGM_HCI_NODE_UP: {
+ ng_hci_node_up_ep *ep = NULL;
+
+ if (msg->header.arglen != sizeof(*ep))
+ error = EMSGSIZE;
+ else {
+ ep = (ng_hci_node_up_ep *)(msg->data);
+
+ NG_L2CAP_INFO(
+"%s: %s - HCI node is up, bdaddr: %x:%x:%x:%x:%x:%x, " \
+"pkt_size=%d bytes, num_pkts=%d\n", __func__, NG_NODE_NAME(l2cap->node),
+ ep->bdaddr.b[5], ep->bdaddr.b[4],
+ ep->bdaddr.b[3], ep->bdaddr.b[2],
+ ep->bdaddr.b[1], ep->bdaddr.b[0],
+ ep->pkt_size, ep->num_pkts);
+
+ bcopy(&ep->bdaddr, &l2cap->bdaddr,
+ sizeof(l2cap->bdaddr));
+ l2cap->pkt_size = ep->pkt_size;
+ l2cap->num_pkts = ep->num_pkts;
+
+ /* Notify upper layers */
+ ng_l2cap_send_hook_info(l2cap->node,
+ l2cap->l2c, NULL, 0);
+ ng_l2cap_send_hook_info(l2cap->node,
+ l2cap->ctl, NULL, 0);
+ }
+ } break;
+
+ case NGM_HCI_SYNC_CON_QUEUE: {
+ ng_hci_sync_con_queue_ep *ep = NULL;
+ ng_l2cap_con_p con = NULL;
+
+ if (msg->header.arglen != sizeof(*ep))
+ error = EMSGSIZE;
+ else {
+ ep = (ng_hci_sync_con_queue_ep *)(msg->data);
+ con = ng_l2cap_con_by_handle(l2cap,
+ ep->con_handle);
+ if (con == NULL)
+ break;
+
+ NG_L2CAP_INFO(
+"%s: %s - sync HCI connection queue, con_handle=%d, pending=%d, completed=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ ep->con_handle, con->pending,
+ ep->completed);
+
+ con->pending -= ep->completed;
+ if (con->pending < 0) {
+ NG_L2CAP_WARN(
+"%s: %s - pending packet counter is out of sync! " \
+"con_handle=%d, pending=%d, completed=%d\n", __func__,
+ NG_NODE_NAME(l2cap->node),
+ con->con_handle, con->pending,
+ ep->completed);
+
+ con->pending = 0;
+ }
+
+ ng_l2cap_lp_deliver(con);
+ }
+ } break;
+
+ /* LP_ConnectCfm[Neg] */
+ case NGM_HCI_LP_CON_CFM:
+ error = ng_l2cap_lp_con_cfm(l2cap, msg);
+ break;
+
+ /* LP_ConnectInd */
+ case NGM_HCI_LP_CON_IND:
+ error = ng_l2cap_lp_con_ind(l2cap, msg);
+ break;
+
+ /* LP_DisconnectInd */
+ case NGM_HCI_LP_DISCON_IND:
+ error = ng_l2cap_lp_discon_ind(l2cap, msg);
+ break;
+
+ /* LP_QoSSetupCfm[Neg] */
+ case NGM_HCI_LP_QOS_CFM:
+ error = ng_l2cap_lp_qos_cfm(l2cap, msg);
+ break;
+
+ /* LP_OoSViolationInd */
+ case NGM_HCI_LP_QOS_IND:
+ error = ng_l2cap_lp_qos_ind(l2cap, msg);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ return (ng_l2cap_default_rcvmsg(node, item, lasthook));
+ /* NOT REACHED */
+ }
+
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_l2cap_lower_rcvmsg */
+
+/*
+ * Process control message from upper layer
+ */
+
+static int
+ng_l2cap_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NGI_MSG(item); /* item still has message */
+ int error = 0;
+
+ switch (msg->header.typecookie) {
+ case NGM_L2CAP_COOKIE:
+ switch (msg->header.cmd) {
+ /* L2CA_Connect */
+ case NGM_L2CAP_L2CA_CON:
+ error = ng_l2cap_l2ca_con_req(l2cap, msg);
+ break;
+
+ /* L2CA_ConnectRsp */
+ case NGM_L2CAP_L2CA_CON_RSP:
+ error = ng_l2cap_l2ca_con_rsp_req(l2cap, msg);
+ break;
+
+ /* L2CA_Config */
+ case NGM_L2CAP_L2CA_CFG:
+ error = ng_l2cap_l2ca_cfg_req(l2cap, msg);
+ break;
+
+ /* L2CA_ConfigRsp */
+ case NGM_L2CAP_L2CA_CFG_RSP:
+ error = ng_l2cap_l2ca_cfg_rsp_req(l2cap, msg);
+ break;
+
+ /* L2CA_Disconnect */
+ case NGM_L2CAP_L2CA_DISCON:
+ error = ng_l2cap_l2ca_discon_req(l2cap, msg);
+ break;
+
+ /* L2CA_GroupCreate */
+ case NGM_L2CAP_L2CA_GRP_CREATE:
+ error = ng_l2cap_l2ca_grp_create(l2cap, msg);
+ break;
+
+ /* L2CA_GroupClose */
+ case NGM_L2CAP_L2CA_GRP_CLOSE:
+ error = ng_l2cap_l2ca_grp_close(l2cap, msg);
+ break;
+
+ /* L2CA_GroupAddMember */
+ case NGM_L2CAP_L2CA_GRP_ADD_MEMBER:
+ error = ng_l2cap_l2ca_grp_add_member_req(l2cap, msg);
+ break;
+
+ /* L2CA_GroupDeleteMember */
+ case NGM_L2CAP_L2CA_GRP_REM_MEMBER:
+ error = ng_l2cap_l2ca_grp_rem_member(l2cap, msg);
+ break;
+
+ /* L2CA_GroupMembership */
+ case NGM_L2CAP_L2CA_GRP_MEMBERSHIP:
+ error = ng_l2cap_l2ca_grp_get_members(l2cap, msg);
+ break;
+
+ /* L2CA_Ping */
+ case NGM_L2CAP_L2CA_PING:
+ error = ng_l2cap_l2ca_ping_req(l2cap, msg);
+ break;
+
+ /* L2CA_GetInfo */
+ case NGM_L2CAP_L2CA_GET_INFO:
+ error = ng_l2cap_l2ca_get_info_req(l2cap, msg);
+ break;
+
+ /* L2CA_EnableCLT */
+ case NGM_L2CAP_L2CA_ENABLE_CLT:
+ error = ng_l2cap_l2ca_enable_clt(l2cap, msg);
+ break;
+
+ default:
+ return (ng_l2cap_default_rcvmsg(node, item, lasthook));
+ /* NOT REACHED */
+ }
+ break;
+
+ default:
+ return (ng_l2cap_default_rcvmsg(node, item, lasthook));
+ /* NOT REACHED */
+ }
+
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_l2cap_upper_rcvmsg */
+
+/*
+ * Default control message processing routine
+ */
+
+static int
+ng_l2cap_default_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg = NULL, *rsp = NULL;
+ int error = 0;
+
+ /* Detach and process message */
+ 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,
+ "bdaddr %x:%x:%x:%x:%x:%x, " \
+ "pkt_size %d\n" \
+ "Hooks %s %s %s\n" \
+ "Flags %#x\n",
+ l2cap->bdaddr.b[5], l2cap->bdaddr.b[4],
+ l2cap->bdaddr.b[3], l2cap->bdaddr.b[2],
+ l2cap->bdaddr.b[1], l2cap->bdaddr.b[0],
+ l2cap->pkt_size,
+ (l2cap->hci != NULL)?
+ NG_L2CAP_HOOK_HCI : "",
+ (l2cap->l2c != NULL)?
+ NG_L2CAP_HOOK_L2C : "",
+ (l2cap->ctl != NULL)?
+ NG_L2CAP_HOOK_CTL : "",
+ l2cap->flags);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ /* Messages from the upper layer or directed to the local node */
+ case NGM_L2CAP_COOKIE:
+ switch (msg->header.cmd) {
+ /* Get node flags */
+ case NGM_L2CAP_NODE_GET_FLAGS:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_flags_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_l2cap_node_flags_ep *)(rsp->data)) =
+ l2cap->flags;
+ break;
+
+ /* Get node debug */
+ case NGM_L2CAP_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_debug_ep),
+ M_NOWAIT);
+ if (rsp == NULL)
+ error = ENOMEM;
+ else
+ *((ng_l2cap_node_debug_ep *)(rsp->data)) =
+ l2cap->debug;
+ break;
+
+ /* Set node debug */
+ case NGM_L2CAP_NODE_SET_DEBUG:
+ if (msg->header.arglen !=
+ sizeof(ng_l2cap_node_debug_ep))
+ error = EMSGSIZE;
+ else
+ l2cap->debug =
+ *((ng_l2cap_node_debug_ep *)(msg->data));
+ break;
+
+ /* Get connection list */
+ case NGM_L2CAP_NODE_GET_CON_LIST: {
+ ng_l2cap_con_p con = NULL;
+ ng_l2cap_node_con_list_ep *e1 = NULL;
+ ng_l2cap_node_con_ep *e2 = NULL;
+ int n = 0;
+
+ /* Count number of connections */
+ LIST_FOREACH(con, &l2cap->con_list, next)
+ n++;
+ if (n > NG_L2CAP_MAX_CON_NUM)
+ n = NG_L2CAP_MAX_CON_NUM;
+
+ /* Prepare response */
+ NG_MKRESPONSE(rsp, msg,
+ sizeof(*e1) + n * sizeof(*e2), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ e1 = (ng_l2cap_node_con_list_ep *)(rsp->data);
+ e2 = (ng_l2cap_node_con_ep *)(e1 + 1);
+
+ e1->num_connections = n;
+
+ LIST_FOREACH(con, &l2cap->con_list, next) {
+ e2->state = con->state;
+
+ if (con->tx_pkt != NULL)
+ e2->flags |= NG_L2CAP_CON_TX;
+ if (con->rx_pkt != NULL)
+ e2->flags |= NG_L2CAP_CON_RX;
+
+ e2->pending = con->pending;
+
+ e2->con_handle = con->con_handle;
+ bcopy(&con->remote, &e2->remote,
+ sizeof(e2->remote));
+
+ e2 ++;
+ if (--n <= 0)
+ break;
+ }
+ } break;
+
+ /* Get channel list */
+ case NGM_L2CAP_NODE_GET_CHAN_LIST: {
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_node_chan_list_ep *e1 = NULL;
+ ng_l2cap_node_chan_ep *e2 = NULL;
+ int n = 0;
+
+ /* Count number of channels */
+ LIST_FOREACH(ch, &l2cap->chan_list, next)
+ n ++;
+ if (n > NG_L2CAP_MAX_CHAN_NUM)
+ n = NG_L2CAP_MAX_CHAN_NUM;
+
+ /* Prepare response */
+ NG_MKRESPONSE(rsp, msg,
+ sizeof(ng_l2cap_node_chan_list_ep) +
+ n * sizeof(ng_l2cap_node_chan_ep), M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ e1 = (ng_l2cap_node_chan_list_ep *)(rsp->data);
+ e2 = (ng_l2cap_node_chan_ep *)(e1 + 1);
+
+ e1->num_channels = n;
+
+ LIST_FOREACH(ch, &l2cap->chan_list, next) {
+ e2->state = ch->state;
+
+ e2->scid = ch->scid;
+ e2->dcid = ch->dcid;
+
+ e2->imtu = ch->imtu;
+ e2->omtu = ch->omtu;
+
+ e2->psm = ch->psm;
+ bcopy(&ch->con->remote, &e2->remote,
+ sizeof(e2->remote));
+
+ e2 ++;
+ if (--n <= 0)
+ break;
+ }
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_l2cap_rcvmsg */
+
+/*
+ * Process data packet from one of our hooks.
+ *
+ * From the HCI hook we expect to receive ACL data packets. ACL data packets
+ * gets re-assembled into one L2CAP packet (according to length) and then gets
+ * processed.
+ *
+ * NOTE: We expect to receive L2CAP packet header in the first fragment.
+ * Otherwise we WILL NOT be able to get length of the L2CAP packet.
+ *
+ * Signaling L2CAP packets (destination channel ID == 0x1) are processed within
+ * the node. Connectionless data packets (destination channel ID == 0x2) will
+ * be forwarded to appropriate upstream hook unless it is not connected or
+ * connectionless traffic for the specified PSM was disabled.
+ *
+ * From the upstream hooks we expect to receive data packets. These data
+ * packets will be converted into L2CAP data packets. The length of each
+ * L2CAP packet must not exceed channel's omtu (our peer's imtu). Then
+ * these L2CAP packets will be converted to ACL data packets (according to
+ * HCI layer MTU) and sent to lower layer.
+ *
+ * No data is expected from the control hook.
+ */
+
+static int
+ng_l2cap_rcvdata(hook_p hook, item_p item)
+{
+ ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m = NULL;
+ int error = 0;
+
+ /* Detach mbuf, discard item and process data */
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ if (hook == l2cap->hci)
+ error = ng_l2cap_lp_receive(l2cap, m);
+ else if (hook == l2cap->l2c)
+ error = ng_l2cap_l2ca_write_req(l2cap, m);
+ else {
+ NG_FREE_M(m);
+ error = EINVAL;
+ }
+
+ return (error);
+} /* ng_l2cap_rcvdata */
+
+/*
+ * Clean all connections, channels and commands for the L2CAP node
+ */
+
+static void
+ng_l2cap_cleanup(ng_l2cap_p l2cap)
+{
+ ng_l2cap_con_p con = NULL;
+
+ /* Clean up connection and channels */
+ while (!LIST_EMPTY(&l2cap->con_list)) {
+ con = LIST_FIRST(&l2cap->con_list);
+
+ if (con->state == NG_L2CAP_W4_LP_CON_CFM)
+ ng_l2cap_lp_untimeout(con);
+
+ con->state = NG_L2CAP_CON_CLOSED;
+ ng_l2cap_con_fail(con, 0x16);
+ /* Connection terminated by local host */
+ }
+} /* ng_l2cap_cleanup */
+
+/*
+ * Destroy all channels that use specified upstream hook
+ */
+
+static void
+ng_l2cap_destroy_channels(ng_l2cap_p l2cap)
+{
+ while (!LIST_EMPTY(&l2cap->chan_list))
+ ng_l2cap_free_chan(LIST_FIRST(&l2cap->chan_list));
+} /* ng_l2cap_destroy_channels_by_hook */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c
new file mode 100644
index 0000000..9fcbc6e
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c
@@ -0,0 +1,517 @@
+/*
+ * ng_l2cap_misc.c
+ *
+ * Copyright (c) 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_l2cap_misc.c,v 1.16 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+static u_int16_t ng_l2cap_get_cid (ng_l2cap_p);
+static void ng_l2cap_queue_lp_timeout (void *);
+static void ng_l2cap_queue_command_timeout (void *);
+
+/******************************************************************************
+ ******************************************************************************
+ ** Utility routines
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Send hook information to the upper layer
+ */
+
+void
+ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ ng_l2cap_p l2cap = NULL;
+ struct ng_mesg *msg = NULL;
+ int error = 0;
+
+ if (node == NULL || NG_NODE_NOT_VALID(node) ||
+ hook == NULL || NG_HOOK_NOT_VALID(hook))
+ return;
+
+ l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
+ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) ||
+ bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0)
+ return;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO,
+ sizeof(bdaddr_t), M_NOWAIT);
+ if (msg != NULL) {
+ bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t));
+ NG_SEND_MSG_HOOK(error, node, msg, hook, NULL);
+ } else
+ error = ENOMEM;
+
+ if (error != 0)
+ NG_L2CAP_INFO(
+"%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook),
+ error);
+} /* ng_l2cap_send_hook_info */
+
+/*
+ * Create new connection descriptor for the "remote" unit. Will create new
+ * connection descriptor and signal channel. Will link both connection and
+ * channel to the l2cap node.
+ */
+
+ng_l2cap_con_p
+ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr)
+{
+ ng_l2cap_con_p con = NULL;
+
+ /* Create new connection descriptor */
+ MALLOC(con, ng_l2cap_con_p, sizeof(*con), M_NETGRAPH_L2CAP,
+ M_NOWAIT|M_ZERO);
+ if (con == NULL)
+ return (NULL);
+
+ con->l2cap = l2cap;
+ con->state = NG_L2CAP_CON_CLOSED;
+
+ bcopy(bdaddr, &con->remote, sizeof(con->remote));
+ callout_handle_init(&con->con_timo);
+
+ con->ident = NG_L2CAP_FIRST_IDENT - 1;
+ TAILQ_INIT(&con->cmd_list);
+
+ /* Link connection */
+ LIST_INSERT_HEAD(&l2cap->con_list, con, next);
+
+ return (con);
+} /* ng_l2cap_new_con */
+
+/*
+ * Free connection descriptor. Will unlink connection and free everything.
+ */
+
+void
+ng_l2cap_free_con(ng_l2cap_con_p con)
+{
+ ng_l2cap_chan_p f = NULL, n = NULL;
+
+ if (con->state == NG_L2CAP_W4_LP_CON_CFM)
+ ng_l2cap_lp_untimeout(con);
+
+ if (con->tx_pkt != NULL) {
+ while (con->tx_pkt != NULL) {
+ struct mbuf *m = con->tx_pkt->m_nextpkt;
+
+ m_freem(con->tx_pkt);
+ con->tx_pkt = m;
+ }
+ }
+
+ NG_FREE_M(con->rx_pkt);
+
+ for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) {
+ n = LIST_NEXT(f, next);
+
+ if (f->con == con)
+ ng_l2cap_free_chan(f);
+
+ f = n;
+ }
+
+ while (!TAILQ_EMPTY(&con->cmd_list)) {
+ ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list);
+
+ ng_l2cap_unlink_cmd(cmd);
+ ng_l2cap_free_cmd(cmd);
+ }
+
+ LIST_REMOVE(con, next);
+ bzero(con, sizeof(*con));
+ FREE(con, M_NETGRAPH_L2CAP);
+} /* ng_l2cap_free_con */
+
+/*
+ * Get connection by "remote" address
+ */
+
+ng_l2cap_con_p
+ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr)
+{
+ ng_l2cap_con_p con = NULL;
+
+ LIST_FOREACH(con, &l2cap->con_list, next)
+ if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)
+ break;
+
+ return (con);
+} /* ng_l2cap_con_by_addr */
+
+/*
+ * Get connection by "handle"
+ */
+
+ng_l2cap_con_p
+ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle)
+{
+ ng_l2cap_con_p con = NULL;
+
+ LIST_FOREACH(con, &l2cap->con_list, next)
+ if (con->con_handle == con_handle)
+ break;
+
+ return (con);
+} /* ng_l2cap_con_by_handle */
+
+/*
+ * Allocate new L2CAP channel descriptor on "con" conection with "psm".
+ * Will link the channel to the l2cap node
+ */
+
+ng_l2cap_chan_p
+ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm)
+{
+ ng_l2cap_chan_p ch = NULL;
+
+ MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP,
+ M_NOWAIT|M_ZERO);
+ if (ch == NULL)
+ return (NULL);
+
+ ch->scid = ng_l2cap_get_cid(l2cap);
+
+ if (ch->scid != NG_L2CAP_NULL_CID) {
+ /* Initialize channel */
+ ch->psm = psm;
+ ch->con = con;
+ ch->state = NG_L2CAP_CLOSED;
+
+ /* Set MTU and flow control settings to defaults */
+ ch->imtu = NG_L2CAP_MTU_DEFAULT;
+ bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow));
+
+ ch->omtu = NG_L2CAP_MTU_DEFAULT;
+ bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow));
+
+ ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
+ ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
+
+ LIST_INSERT_HEAD(&l2cap->chan_list, ch, next);
+ } else {
+ bzero(ch, sizeof(*ch));
+ FREE(ch, M_NETGRAPH_L2CAP);
+ ch = NULL;
+ }
+
+ return (ch);
+} /* ng_l2cap_new_chan */
+
+/*
+ * Get channel by source (local) channel ID
+ */
+
+ng_l2cap_chan_p
+ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid)
+{
+ ng_l2cap_chan_p ch = NULL;
+
+ LIST_FOREACH(ch, &l2cap->chan_list, next)
+ if (ch->scid == scid)
+ break;
+
+ return (ch);
+} /* ng_l2cap_chan_by_scid */
+
+/*
+ * Free channel descriptor.
+ */
+
+void
+ng_l2cap_free_chan(ng_l2cap_chan_p ch)
+{
+ ng_l2cap_cmd_p f = NULL, n = NULL;
+
+ f = TAILQ_FIRST(&ch->con->cmd_list);
+ while (f != NULL) {
+ n = TAILQ_NEXT(f, next);
+
+ if (f->ch == ch) {
+ ng_l2cap_unlink_cmd(f);
+ ng_l2cap_free_cmd(f);
+ }
+
+ f = n;
+ }
+
+ LIST_REMOVE(ch, next);
+ bzero(ch, sizeof(*ch));
+ FREE(ch, M_NETGRAPH_L2CAP);
+} /* ng_l2cap_free_chan */
+
+/*
+ * Create new L2CAP command descriptor. WILL NOT add command to the queue.
+ */
+
+ng_l2cap_cmd_p
+ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident,
+ u_int8_t code, u_int32_t token)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+
+ KASSERT((ch == NULL || ch->con == con),
+("%s: %s - invalid channel pointer!\n",
+ __func__, NG_NODE_NAME(con->l2cap->node)));
+
+ MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP,
+ M_NOWAIT|M_ZERO);
+ if (cmd == NULL)
+ return (NULL);
+
+ cmd->con = con;
+ cmd->ch = ch;
+ cmd->ident = ident;
+ cmd->code = code;
+ cmd->token = token;
+ callout_handle_init(&cmd->timo);
+
+ return (cmd);
+} /* ng_l2cap_new_cmd */
+
+/*
+ * Get L2CAP command descriptor by ident
+ */
+
+ng_l2cap_cmd_p
+ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident)
+{
+ ng_l2cap_cmd_p cmd = NULL;
+
+ TAILQ_FOREACH(cmd, &con->cmd_list, next)
+ if (cmd->ident == ident)
+ break;
+
+ return (cmd);
+} /* ng_l2cap_cmd_by_ident */
+
+/*
+ * Set LP timeout
+ */
+
+void
+ng_l2cap_lp_timeout(ng_l2cap_con_p con)
+{
+ NG_NODE_REF(con->l2cap->node);
+ con->con_timo = timeout(ng_l2cap_queue_lp_timeout, con,
+ bluetooth_hci_connect_timeout());
+} /* ng_l2cap_lp_timeout */
+
+/*
+ * Unset LP timeout
+ */
+
+void
+ng_l2cap_lp_untimeout(ng_l2cap_con_p con)
+{
+ untimeout(ng_l2cap_queue_lp_timeout, con, con->con_timo);
+ NG_NODE_UNREF(con->l2cap->node);
+} /* ng_l2cap_lp_untimeout */
+
+/*
+ * OK, timeout has happend so queue LP timeout processing function
+ */
+
+static void
+ng_l2cap_queue_lp_timeout(void *context)
+{
+ ng_l2cap_con_p con = (ng_l2cap_con_p) context;
+ node_p node = con->l2cap->node;
+
+ /*
+ * We need to save node pointer here, because ng_send_fn()
+ * can execute ng_l2cap_process_lp_timeout() without putting
+ * item into node's queue (if node can be locked). Once
+ * ng_l2cap_process_lp_timeout() executed the con pointer
+ * is no longer valid.
+ */
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node, NULL, &ng_l2cap_process_lp_timeout, con, 0);
+
+ NG_NODE_UNREF(node);
+} /* ng_l2cap_queue_lp_timeout */
+
+/*
+ * Set L2CAP command timeout
+ */
+
+void
+ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo)
+{
+ NG_NODE_REF(cmd->con->l2cap->node);
+ cmd->flags |= NG_L2CAP_CMD_PENDING;
+ cmd->timo = timeout(ng_l2cap_queue_command_timeout, cmd, timo);
+} /* ng_l2cap_command_timeout */
+
+/*
+ * Unset L2CAP command timeout
+ */
+
+void
+ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd)
+{
+ cmd->flags &= ~NG_L2CAP_CMD_PENDING;
+ untimeout(ng_l2cap_queue_command_timeout, cmd, cmd->timo);
+ NG_NODE_UNREF(cmd->con->l2cap->node);
+} /* ng_l2cap_command_untimeout */
+
+/*
+ * OK, timeout has happend so queue L2CAP command timeout processing function
+ */
+
+static void
+ng_l2cap_queue_command_timeout(void *context)
+{
+ ng_l2cap_cmd_p cmd = (ng_l2cap_cmd_p) context;
+ node_p node = cmd->con->l2cap->node;
+
+ /*
+ * We need to save node pointer here, because ng_send_fn()
+ * can execute ng_l2cap_process_command_timeout() without
+ * putting item into node's queue (if node can be locked).
+ * Once ng_l2cap_process_command_timeout() executed the
+ * cmd pointer is no longer valid.
+ */
+
+ if (NG_NODE_IS_VALID(node))
+ ng_send_fn(node,NULL,&ng_l2cap_process_command_timeout,cmd,0);
+
+ NG_NODE_UNREF(node);
+} /* ng_l2cap_queue_command_timeout */
+
+/*
+ * Prepend "m"buf with "size" bytes
+ */
+
+struct mbuf *
+ng_l2cap_prepend(struct mbuf *m, int size)
+{
+ M_PREPEND(m, size, M_DONTWAIT);
+ if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL))
+ return (NULL);
+
+ return (m);
+} /* ng_l2cap_prepend */
+
+/*
+ * Default flow settings
+ */
+
+ng_l2cap_flow_p
+ng_l2cap_default_flow(void)
+{
+ static ng_l2cap_flow_t default_flow = {
+ /* flags */ 0x0,
+ /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT,
+ /* token_rate */ 0xffffffff, /* maximum */
+ /* token_bucket_size */ 0xffffffff, /* maximum */
+ /* peak_bandwidth */ 0x00000000, /* maximum */
+ /* latency */ 0xffffffff, /* don't care */
+ /* delay_variation */ 0xffffffff /* don't care */
+ };
+
+ return (&default_flow);
+} /* ng_l2cap_default_flow */
+
+/*
+ * Get next available channel ID
+ * XXX FIXME this is *UGLY* but will do for now
+ */
+
+static u_int16_t
+ng_l2cap_get_cid(ng_l2cap_p l2cap)
+{
+ u_int16_t cid = l2cap->cid + 1;
+
+ if (cid < NG_L2CAP_FIRST_CID)
+ cid = NG_L2CAP_FIRST_CID;
+
+ while (cid != l2cap->cid) {
+ if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) {
+ l2cap->cid = cid;
+
+ return (cid);
+ }
+
+ cid ++;
+ if (cid < NG_L2CAP_FIRST_CID)
+ cid = NG_L2CAP_FIRST_CID;
+ }
+
+ return (NG_L2CAP_NULL_CID);
+} /* ng_l2cap_get_cid */
+
+/*
+ * Get next available command ident
+ * XXX FIXME this is *UGLY* but will do for now
+ */
+
+u_int8_t
+ng_l2cap_get_ident(ng_l2cap_con_p con)
+{
+ u_int8_t ident = con->ident + 1;
+
+ if (ident < NG_L2CAP_FIRST_IDENT)
+ ident = NG_L2CAP_FIRST_IDENT;
+
+ while (ident != con->ident) {
+ if (ng_l2cap_cmd_by_ident(con, ident) == NULL) {
+ con->ident = ident;
+
+ return (ident);
+ }
+
+ ident ++;
+ if (ident < NG_L2CAP_FIRST_IDENT)
+ ident = NG_L2CAP_FIRST_IDENT;
+ }
+
+ return (NG_L2CAP_NULL_IDENT);
+} /* ng_l2cap_get_ident */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.h
new file mode 100644
index 0000000..f0f72a3
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.h
@@ -0,0 +1,100 @@
+/*
+ * ng_l2cap_misc.h
+ *
+ * Copyright (c) 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_l2cap_misc.h,v 1.6 2002/04/16 00:43:57 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_MISC_H_
+#define _NETGRAPH_L2CAP_MISC_H_ 1
+
+void ng_l2cap_send_hook_info (node_p, hook_p, void *, int);
+
+/*
+ * ACL Connections
+ */
+
+ng_l2cap_con_p ng_l2cap_new_con (ng_l2cap_p, bdaddr_p);
+ng_l2cap_con_p ng_l2cap_con_by_addr (ng_l2cap_p, bdaddr_p);
+ng_l2cap_con_p ng_l2cap_con_by_handle (ng_l2cap_p, u_int16_t);
+void ng_l2cap_free_con (ng_l2cap_con_p);
+
+/*
+ * L2CAP channels
+ */
+
+ng_l2cap_chan_p ng_l2cap_new_chan (ng_l2cap_p, ng_l2cap_con_p, u_int16_t);
+ng_l2cap_chan_p ng_l2cap_chan_by_scid (ng_l2cap_p, u_int16_t);
+void ng_l2cap_free_chan (ng_l2cap_chan_p);
+
+/*
+ * L2CAP command descriptors
+ */
+
+#define ng_l2cap_link_cmd(con, cmd) \
+do { \
+ TAILQ_INSERT_TAIL(&(con)->cmd_list, (cmd), next); \
+} while (0)
+
+#define ng_l2cap_unlink_cmd(cmd) \
+do { \
+ TAILQ_REMOVE(&((cmd)->con->cmd_list), (cmd), next); \
+} while (0)
+
+#define ng_l2cap_free_cmd(cmd) \
+do { \
+ if ((cmd)->flags & NG_L2CAP_CMD_PENDING) \
+ ng_l2cap_command_untimeout((cmd)); \
+ \
+ NG_FREE_M((cmd)->aux); \
+ bzero((cmd), sizeof(*(cmd))); \
+ FREE((cmd), M_NETGRAPH_L2CAP); \
+} while (0)
+
+ng_l2cap_cmd_p ng_l2cap_new_cmd (ng_l2cap_con_p, ng_l2cap_chan_p,
+ u_int8_t, u_int8_t, u_int32_t);
+ng_l2cap_cmd_p ng_l2cap_cmd_by_ident (ng_l2cap_con_p, u_int8_t);
+u_int8_t ng_l2cap_get_ident (ng_l2cap_con_p);
+
+/*
+ * Timeout
+ */
+
+void ng_l2cap_lp_timeout (ng_l2cap_con_p);
+void ng_l2cap_lp_untimeout (ng_l2cap_con_p);
+void ng_l2cap_command_timeout (ng_l2cap_cmd_p, int);
+void ng_l2cap_command_untimeout (ng_l2cap_cmd_p);
+
+/*
+ * Other stuff
+ */
+
+struct mbuf * ng_l2cap_prepend (struct mbuf *, int);
+ng_l2cap_flow_p ng_l2cap_default_flow (void);
+
+#endif /* ndef _NETGRAPH_L2CAP_MISC_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_prse.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_prse.h
new file mode 100644
index 0000000..5209c24
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_prse.h
@@ -0,0 +1,71 @@
+/*
+ * ng_l2cap_prse.h
+ *
+ * Copyright (c) 2001 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_l2cap_prse.h,v 1.2 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+/***************************************************************************
+ ***************************************************************************
+ ** ng_parse definitions for the L2CAP node
+ ***************************************************************************
+ ***************************************************************************/
+
+#ifndef _NETGRAPH_L2CAP_PRSE_H_
+#define _NETGRAPH_L2CAP_PRSE_H_ 1
+
+/*
+ * L2CAP node command list
+ */
+
+static const struct ng_cmdlist ng_l2cap_cmdlist[] = {
+ {
+ NGM_L2CAP_COOKIE,
+ NGM_L2CAP_NODE_GET_FLAGS,
+ "get_flags",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_L2CAP_COOKIE,
+ NGM_L2CAP_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_L2CAP_COOKIE,
+ NGM_L2CAP_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ { 0, }
+};
+
+#endif /* ndef _NETGRAPH_L2CAP_PRSE_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c b/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c
new file mode 100644
index 0000000..c913d1c
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c
@@ -0,0 +1,1640 @@
+/*
+ * ng_l2cap_ulpi.c
+ *
+ * Copyright (c) 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_l2cap_ulpi.c,v 1.22 2002/09/08 23:35:51 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_l2cap_var.h"
+#include "ng_l2cap_cmds.h"
+#include "ng_l2cap_evnt.h"
+#include "ng_l2cap_llpi.h"
+#include "ng_l2cap_ulpi.h"
+#include "ng_l2cap_misc.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ** Upper Layer Protocol Interface module
+ ******************************************************************************
+ ******************************************************************************/
+
+/*
+ * Process L2CA_Connect request from the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_con_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_con_ip *ip = NULL;
+ ng_l2cap_con_p con = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_Connect request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_con_ip *)(msg->data);
+
+ /* Check if we have connection to the remote unit */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ if (con == NULL) {
+ /* Submit LP_ConnectReq to the lower layer */
+ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
+ if (error != 0) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), error);
+ goto out;
+ }
+
+ /* This should not fail */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ KASSERT((con != NULL),
+("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
+ }
+
+ /*
+ * Create new empty channel descriptor. In case of any failure do
+ * not touch connection descriptor.
+ */
+
+ ch = ng_l2cap_new_chan(l2cap, con, ip->psm);
+ if (ch == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Now create L2CAP_ConnectReq command */
+ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(con),
+ NG_L2CAP_CON_REQ, msg->header.token);
+ if (cmd == NULL) {
+ ng_l2cap_free_chan(ch);
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (cmd->ident == NG_L2CAP_NULL_IDENT) {
+ ng_l2cap_free_cmd(cmd);
+ ng_l2cap_free_chan(ch);
+ error = EIO;
+ goto out;
+ }
+
+ /* Create L2CAP command packet */
+ _ng_l2cap_con_req(cmd->aux, cmd->ident, ch->psm, ch->scid);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+ ng_l2cap_free_chan(ch);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ ch->state = NG_L2CAP_W4_L2CAP_CON_RSP;
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(ch->con, cmd);
+ ng_l2cap_lp_deliver(ch->con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_con_req */
+
+/*
+ * Send L2CA_Connect response to the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_con_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
+ u_int16_t status)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_con_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_Connect response message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_Connect response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_con_op *)(msg->data);
+
+ /*
+ * XXX Spec. says we should only populate LCID when result == 0
+ * What about PENDING? What the heck, for now always populate
+ * LCID :)
+ */
+
+ op->lcid = ch->scid;
+ op->result = result;
+ op->status = status;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_con_rsp */
+
+/*
+ * Process L2CA_ConnectRsp request from the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_con_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_con_rsp_ip *ip = NULL;
+ ng_l2cap_con_p con = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ u_int16_t dcid;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_ConnectRsp request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
+
+ /* Check if we have this channel */
+ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
+ if (ch == NULL) {
+ NG_L2CAP_ALERT(
+"%s: %s - unexpected L2CA_ConnectRsp request message. " \
+"Channel does not exist, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ip->lcid);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Check channel state */
+ if (ch->state != NG_L2CAP_W4_L2CA_CON_RSP) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_ConnectRsp request message. " \
+"Invalid channel state, state=%d, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->state,
+ ip->lcid);
+ error = EINVAL;
+ goto out;
+ }
+
+ dcid = ch->dcid;
+ con = ch->con;
+
+ /*
+ * Now we are pretty much sure it is our response. So create and send
+ * L2CAP_ConnectRsp message to our peer.
+ */
+
+ if (ch->ident != ip->ident)
+ NG_L2CAP_WARN(
+"%s: %s - channel ident and response ident do not match, scid=%d, ident=%d. " \
+"Will use response ident=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->scid,
+ ch->ident, ip->ident);
+
+ /* Check result */
+ switch (ip->result) {
+ case NG_L2CAP_SUCCESS:
+ ch->state = NG_L2CAP_CONFIG;
+ ch->cfg_state = 0;
+ break;
+
+ case NG_L2CAP_PENDING:
+ break;
+
+ default:
+ ng_l2cap_free_chan(ch);
+ ch = NULL;
+ break;
+ }
+
+ /* Create L2CAP command */
+ cmd = ng_l2cap_new_cmd(con, ch, ip->ident, NG_L2CAP_CON_RSP,
+ msg->header.token);
+ if (cmd == NULL) {
+ if (ch != NULL)
+ ng_l2cap_free_chan(ch);
+
+ error = ENOMEM;
+ goto out;
+ }
+
+ _ng_l2cap_con_rsp(cmd->aux, cmd->ident, ip->lcid, dcid,
+ ip->result, ip->status);
+ if (cmd->aux == NULL) {
+ if (ch != NULL)
+ ng_l2cap_free_chan(ch);
+
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_con_rsp_req */
+
+/*
+ * Send L2CAP_ConnectRsp response to the upper layer
+ */
+
+int
+ng_l2cap_l2ca_con_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_con_rsp_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_ConnectRsp response message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_ConnectRsp response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
+ op->result = result;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_con_rsp_rsp */
+
+/*
+ * Send L2CA_ConnectInd message to the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_con_ind(ng_l2cap_chan_p ch)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_con_ind_ip *ip = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_ConnectInd message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_ConnectInd message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_IND,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
+
+ bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
+ ip->lcid = ch->scid;
+ ip->psm = ch->psm;
+ ip->ident = ch->ident;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_con_ind */
+
+/*
+ * Process L2CA_Config request from the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_cfg_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_cfg_ip *ip = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ struct mbuf *opt = NULL;
+ u_int16_t *mtu = NULL, *flush_timo = NULL;
+ ng_l2cap_flow_p flow = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - Invalid L2CA_Config request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
+
+ /* Check if we have this channel */
+ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_Config request message. " \
+"Channel does not exist, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ip->lcid);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Check channel state */
+ if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_Config request message. " \
+"Invalid channel state, state=%d, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->state,
+ ch->scid);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* Set requested channel configuration options */
+ ch->imtu = ip->imtu;
+ bcopy(&ip->oflow, &ch->oflow, sizeof(ch->oflow));
+ ch->flush_timo = ip->flush_timo;
+ ch->link_timo = ip->link_timo;
+
+ /* Compare channel settings with defaults */
+ if (ch->imtu != NG_L2CAP_MTU_DEFAULT)
+ mtu = &ch->imtu;
+ if (ch->flush_timo != NG_L2CAP_FLUSH_TIMO_DEFAULT)
+ flush_timo = &ch->flush_timo;
+ if (bcmp(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)) != 0)
+ flow = &ch->oflow;
+
+ /* Create configuration options */
+ _ng_l2cap_build_cfg_options(opt, mtu, flush_timo, flow);
+ if (opt == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* Create L2CAP command descriptor */
+ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
+ NG_L2CAP_CFG_REQ, msg->header.token);
+ if (cmd == NULL) {
+ NG_FREE_M(opt);
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (cmd->ident == NG_L2CAP_NULL_IDENT) {
+ ng_l2cap_free_cmd(cmd);
+ NG_FREE_M(opt);
+ error = EIO;
+ goto out;
+ }
+
+ /* Create L2CAP command packet */
+ _ng_l2cap_cfg_req(cmd->aux, cmd->ident, ch->dcid, 0, opt);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* Adjust channel state for re-configuration */
+ if (ch->state == NG_L2CAP_OPEN) {
+ ch->state = NG_L2CAP_CONFIG;
+ ch->cfg_state = 0;
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(ch->con, cmd);
+ ng_l2cap_lp_deliver(ch->con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_cfg_req */
+
+/*
+ * Send L2CA_Config response to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_cfg_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_cfg_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_Config response message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_Config response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
+ op->result = result;
+ op->imtu = ch->imtu;
+ bcopy(&ch->oflow, &op->oflow, sizeof(op->oflow));
+ op->flush_timo = ch->flush_timo;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+
+ if (error == 0 && result == NG_L2CAP_SUCCESS) {
+ ch->cfg_state |= NG_L2CAP_CFG_IN;
+
+ if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
+ ch->state = NG_L2CAP_OPEN;
+ }
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_cfg_rsp */
+
+/*
+ * Process L2CA_ConfigRsp request from the upper layer protocol
+ *
+ * XXX XXX XXX
+ *
+ * NOTE: The Bluetooth specification says that Configuration_Response
+ * (L2CA_ConfigRsp) should be used to issue response to configuration request
+ * indication. The minor problem here is L2CAP command ident. We should use
+ * ident from original L2CAP request to make sure our peer can match request
+ * and response. For some reason Bluetooth specification does not include
+ * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
+ * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
+ * field. So we should store last known L2CAP request command ident in channel.
+ * Also it seems that upper layer can not reject configuration request, as
+ * Configuration_Response message does not have status/reason field.
+ */
+
+int
+ng_l2cap_l2ca_cfg_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_cfg_rsp_ip *ip = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ struct mbuf *opt = NULL;
+ u_int16_t *mtu = NULL;
+ ng_l2cap_flow_p flow = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_ConfigRsp request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
+
+ /* Check if we have this channel */
+ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_ConfigRsp request message. " \
+"Channel does not exist, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ip->lcid);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Check channel state */
+ if (ch->state != NG_L2CAP_CONFIG) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_ConfigRsp request message. " \
+"Invalid channel state, state=%d, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->state,
+ ch->scid);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* Set channel settings */
+ if (ip->omtu != ch->omtu) {
+ ch->omtu = ip->omtu;
+ mtu = &ch->omtu;
+ }
+
+ if (bcmp(&ip->iflow, &ch->iflow, sizeof(ch->iflow)) != 0) {
+ bcopy(&ip->iflow, &ch->iflow, sizeof(ch->iflow));
+ flow = &ch->iflow;
+ }
+
+ if (mtu != NULL || flow != NULL) {
+ _ng_l2cap_build_cfg_options(opt, mtu, NULL, flow);
+ if (opt == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ }
+
+ /* Create L2CAP command */
+ cmd = ng_l2cap_new_cmd(ch->con, ch, ch->ident, NG_L2CAP_CFG_RSP,
+ msg->header.token);
+ if (cmd == NULL) {
+ NG_FREE_M(opt);
+ error = ENOMEM;
+ goto out;
+ }
+
+ _ng_l2cap_cfg_rsp(cmd->aux,cmd->ident,ch->dcid,0,NG_L2CAP_SUCCESS,opt);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* XXX FIXME - not here ??? */
+ ch->cfg_state |= NG_L2CAP_CFG_OUT;
+ if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
+ ch->state = NG_L2CAP_OPEN;
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(ch->con, cmd);
+ ng_l2cap_lp_deliver(ch->con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_cfg_rsp_req */
+
+/*
+ * Send L2CA_ConfigRsp response to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_cfg_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_cfg_rsp_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_ConfigRsp response message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_ConfigRsp response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
+ op->result = result;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_cfg_rsp_rsp */
+
+/*
+ * Send L2CA_ConfigInd message to the upper layer protocol
+ *
+ * XXX XXX XXX
+ *
+ * NOTE: The Bluetooth specification says that Configuration_Response
+ * (L2CA_ConfigRsp) should be used to issue response to configuration request
+ * indication. The minor problem here is L2CAP command ident. We should use
+ * ident from original L2CAP request to make sure our peer can match request
+ * and response. For some reason Bluetooth specification does not include
+ * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
+ * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
+ * field. So we should store last known L2CAP request command ident in channel.
+ * Also it seems that upper layer can not reject configuration request, as
+ * Configuration_Response message does not have status/reason field.
+ */
+
+int
+ng_l2cap_l2ca_cfg_ind(ng_l2cap_chan_p ch)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_cfg_ind_ip *ip = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - Unable to send L2CA_ConfigInd message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_ConnectInd message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_IND,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
+ ip->lcid = ch->scid;
+ ip->omtu = ch->omtu;
+ bcopy(&ch->iflow, &ip->iflow, sizeof(ip->iflow));
+ ip->flush_timo = ch->flush_timo;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_cfg_ind */
+
+/*
+ * Process L2CA_Write event
+ */
+
+int
+ng_l2cap_l2ca_write_req(ng_l2cap_p l2cap, struct mbuf *m)
+{
+ ng_l2cap_l2ca_hdr_t *l2ca_hdr = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+ u_int32_t token = 0;
+
+ /* Make sure we can access L2CA data packet header */
+ if (m->m_pkthdr.len < sizeof(*l2ca_hdr)) {
+ NG_L2CAP_ERR(
+"%s: %s - L2CA Data packet too small, len=%d\n",
+ __func__,NG_NODE_NAME(l2cap->node),m->m_pkthdr.len);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Get L2CA data packet header */
+ NG_L2CAP_M_PULLUP(m, sizeof(*l2ca_hdr));
+ if (m == NULL)
+ return (ENOBUFS);
+
+ l2ca_hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
+ token = l2ca_hdr->token;
+ m_adj(m, sizeof(*l2ca_hdr));
+
+ /* Verify payload size */
+ if (l2ca_hdr->length != m->m_pkthdr.len) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CA Data packet. " \
+"Payload length does not match, length=%d, len=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->length,
+ m->m_pkthdr.len);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Check channel ID */
+ if (l2ca_hdr->lcid < NG_L2CAP_FIRST_CID) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CA Data packet. Inavlid channel ID, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
+ error = EINVAL;
+ goto drop;
+ }
+
+ /* Verify that we have the channel and make sure it is open */
+ ch = ng_l2cap_chan_by_scid(l2cap, l2ca_hdr->lcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CA Data packet. Channel does not exist, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
+ error = ENOENT;
+ goto drop;
+ }
+
+ if (ch->state != NG_L2CAP_OPEN) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CA Data packet. Invalid channel state, scid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->scid,
+ ch->state);
+ error = EHOSTDOWN;
+ goto drop; /* XXX not always - re-configure */
+ }
+
+ /* Create L2CAP command descriptor */
+ cmd = ng_l2cap_new_cmd(ch->con, ch, 0, NGM_L2CAP_L2CA_WRITE, token);
+ if (cmd == NULL) {
+ error = ENOMEM;
+ goto drop;
+ }
+
+ /* Attach data packet and link command to the queue */
+ cmd->aux = m;
+ ng_l2cap_link_cmd(ch->con, cmd);
+ ng_l2cap_lp_deliver(ch->con);
+
+ return (error);
+drop:
+ NG_FREE_M(m);
+
+ return (error);
+} /* ng_l2cap_l2ca_write_req */
+
+/*
+ * Send L2CA_Write response
+ */
+
+int
+ng_l2cap_l2ca_write_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
+ u_int16_t length)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_write_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_WriteRsp message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_WriteRsp message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_WRITE,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_write_op *)(msg->data);
+ op->result = result;
+ op->length = length;
+ op->lcid = ch->scid;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_write_rsp */
+
+/*
+ * Receive packet from the lower layer protocol and send it to the upper
+ * layer protocol (L2CAP_Read)
+ */
+
+int
+ng_l2cap_l2ca_receive(ng_l2cap_con_p con)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ ng_l2cap_hdr_t *hdr = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ int error = 0;
+
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
+
+ /* Check channel */
+ ch = ng_l2cap_chan_by_scid(l2cap, hdr->dcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CAP data packet. Channel does not exist, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), hdr->dcid);
+ error = ENOENT;
+ goto drop;
+ }
+
+ /* Check channel state */
+ if (ch->state != NG_L2CAP_OPEN) {
+ NG_L2CAP_WARN(
+"%s: %s - unexpected L2CAP data packet. " \
+"Invalid channel state, cid=%d, state=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->scid,
+ ch->state);
+ error = EHOSTDOWN; /* XXX not always - re-configuration */
+ goto drop;
+ }
+
+ /* Check payload size and channel's MTU */
+ if (hdr->length > ch->imtu) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP data packet. " \
+"Packet too big, length=%d, imtu=%d, cid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), hdr->length,
+ ch->imtu, ch->scid);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /*
+ * If we got here then everything looks good and we can sent packet
+ * to the upper layer protocol.
+ */
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CAP data packet. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+ error = ENOTCONN;
+ goto drop;
+ }
+
+ NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
+ con->rx_pkt = NULL;
+drop:
+ NG_FREE_M(con->rx_pkt); /* checks for != NULL */
+
+ return (error);
+} /* ng_l2cap_receive */
+
+/*
+ * Receive connectioless (multicast) packet from the lower layer protocol and
+ * send it to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_clt_receive(ng_l2cap_con_p con)
+{
+ struct _clt_pkt {
+ ng_l2cap_hdr_t h;
+ ng_l2cap_clt_hdr_t c_h;
+ } __attribute__ ((packed)) *hdr = NULL;
+ ng_l2cap_p l2cap = con->l2cap;
+ int length, error = 0;
+
+ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
+ if (con->rx_pkt == NULL)
+ return (ENOBUFS);
+
+ hdr = mtod(con->rx_pkt, struct _clt_pkt *);
+
+ /* Check packet */
+ length = con->rx_pkt->m_pkthdr.len - sizeof(*hdr);
+ if (length < 0) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP CLT data packet. Packet too small, length=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), length);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /* Check payload size against CLT MTU */
+ if (length > NG_L2CAP_MTU_DEFAULT) {
+ NG_L2CAP_ERR(
+"%s: %s - invalid L2CAP CLT data packet. Packet too big, length=%d, mtu=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), length,
+ NG_L2CAP_MTU_DEFAULT);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ hdr->c_h.psm = le16toh(hdr->c_h.psm);
+
+ /*
+ * If we got here then everything looks good and we can sent packet
+ * to the upper layer protocol.
+ */
+
+ /* Select upstream hook based on PSM */
+ switch (hdr->c_h.psm) {
+ case NG_L2CAP_PSM_SDP:
+ if (l2cap->flags & NG_L2CAP_CLT_SDP_DISABLED)
+ goto drop;
+ break;
+
+ case NG_L2CAP_PSM_RFCOMM:
+ if (l2cap->flags & NG_L2CAP_CLT_RFCOMM_DISABLED)
+ goto drop;
+ break;
+
+ case NG_L2CAP_PSM_TCP:
+ if (l2cap->flags & NG_L2CAP_CLT_TCP_DISABLED)
+ goto drop;
+ break;
+ }
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CAP CLT data packet. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), hdr->c_h.psm);
+ error = ENOTCONN;
+ goto drop;
+ }
+
+ NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
+ con->rx_pkt = NULL;
+drop:
+ NG_FREE_M(con->rx_pkt); /* checks for != NULL */
+
+ return (error);
+} /* ng_l2cap_l2ca_clt_receive */
+
+/*
+ * Send L2CA_QoSViolationInd to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_qos_ind(ng_l2cap_chan_p ch)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_qos_ind_ip *ip = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_QoSViolationInd message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_QoSViolationInd message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_QOS_IND,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ ip = (ng_l2cap_l2ca_qos_ind_ip *)(msg->data);
+ bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_qos_ind */
+
+/*
+ * Process L2CA_Disconnect request from the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_discon_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_discon_ip *ip = NULL;
+ ng_l2cap_chan_p ch = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_Disconnect request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
+
+ /* Check if we have this channel */
+ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
+ if (ch == NULL) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_Disconnect request message. " \
+"Channel does not exist, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ip->lcid);
+ error = ENOENT;
+ goto out;
+ }
+
+ /* Check channel state */
+ if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN &&
+ ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
+ NG_L2CAP_ERR(
+"%s: %s - unexpected L2CA_Disconnect request message. " \
+"Invalid channel state, state=%d, lcid=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->state,
+ ch->scid);
+ error = EINVAL;
+ goto out;
+ }
+
+ /* Create and send L2CAP_DisconReq message */
+ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
+ NG_L2CAP_DISCON_REQ, msg->header.token);
+ if (cmd == NULL) {
+ ng_l2cap_free_chan(ch);
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (cmd->ident == NG_L2CAP_NULL_IDENT) {
+ ng_l2cap_free_chan(ch);
+ ng_l2cap_free_cmd(cmd);
+ error = EIO;
+ goto out;
+ }
+
+ _ng_l2cap_discon_req(cmd->aux, cmd->ident, ch->dcid, ch->scid);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_chan(ch);
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ ch->state = NG_L2CAP_W4_L2CAP_DISCON_RSP;
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(ch->con, cmd);
+ ng_l2cap_lp_deliver(ch->con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_discon_req */
+
+/*
+ * Send L2CA_Disconnect response to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_discon_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_discon_op *op = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_Disconnect response message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_Disconnect response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
+ sizeof(*op), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_discon_op *)(msg->data);
+ op->result = result;
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_discon_rsp */
+
+/*
+ * Send L2CA_DisconnectInd message to the upper layer protocol.
+ */
+
+int
+ng_l2cap_l2ca_discon_ind(ng_l2cap_chan_p ch)
+{
+ ng_l2cap_p l2cap = ch->con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_discon_ind_ip *ip = NULL;
+ int error = 0;
+
+ /* Check if upstream hook is connected and valid */
+ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send L2CA_DisconnectInd message. " \
+"Hook is not connected or valid, psm=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ch->psm);
+
+ return (ENOTCONN);
+ }
+
+ /* Create and send L2CA_DisconnectInd message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON_IND,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
+ ip->lcid = ch->scid;
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, NULL);
+ }
+
+ return (error);
+} /* ng_l2cap_l2ca_discon_ind */
+
+/*
+ * Process L2CA_GroupCreate request from the upper layer protocol.
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_create(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ return (ENOTSUP);
+} /* ng_l2cap_l2ca_grp_create */
+
+/*
+ * Process L2CA_GroupClose request from the upper layer protocol
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_close(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ return (ENOTSUP);
+} /* ng_l2cap_l2ca_grp_close */
+
+/*
+ * Process L2CA_GroupAddMember request from the upper layer protocol.
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_add_member_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ return (ENOTSUP);
+} /* ng_l2cap_l2ca_grp_add_member_req */
+
+/*
+ * Send L2CA_GroupAddMember response to the upper layer protocol.
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_add_member_rsp(ng_l2cap_chan_p ch, u_int32_t token,
+ u_int16_t result)
+{
+ return (0);
+} /* ng_l2cap_l2ca_grp_add_member_rsp */
+
+/*
+ * Process L2CA_GroupDeleteMember request from the upper layer protocol
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_rem_member(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ return (ENOTSUP);
+} /* ng_l2cap_l2ca_grp_rem_member */
+
+/*
+ * Process L2CA_GroupGetMembers request from the upper layer protocol
+ * XXX FIXME
+ */
+
+int
+ng_l2cap_l2ca_grp_get_members(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ return (ENOTSUP);
+} /* ng_l2cap_l2ca_grp_get_members */
+
+/*
+ * Process L2CA_Ping request from the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_ping_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_ping_ip *ip = NULL;
+ ng_l2cap_con_p con = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Verify message */
+ if (msg->header.arglen < sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_Ping request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
+ if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
+ NG_L2CAP_WARN(
+"%s: %s - invalid L2CA_Ping request. Echo size is too big, echo_size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), ip->echo_size);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ /* Check if we have connection to the unit */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ if (con == NULL) {
+ /* Submit LP_ConnectReq to the lower layer */
+ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
+ if (error != 0) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), error);
+ goto out;
+ }
+
+ /* This should not fail */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ KASSERT((con != NULL),
+("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
+ }
+
+ /* Create L2CAP command descriptor */
+ cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
+ NG_L2CAP_ECHO_REQ, msg->header.token);
+ if (cmd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (cmd->ident == NG_L2CAP_NULL_IDENT) {
+ ng_l2cap_free_cmd(cmd);
+ error = EIO;
+ goto out;
+ }
+
+ /* Create L2CAP command packet */
+ _ng_l2cap_echo_req(cmd->aux, cmd->ident,
+ msg->data + sizeof(*ip), ip->echo_size);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_ping_req */
+
+/*
+ * Send L2CA_Ping response to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_ping_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result,
+ struct mbuf *data)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_ping_op *op = NULL;
+ int error = 0, size = 0;
+
+ /* Check if control hook is connected and valid */
+ if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
+ NG_L2CAP_WARN(
+"%s: %s - unable to send L2CA_Ping response message. " \
+"Hook is not connected or valid\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = ENOTCONN;
+ goto out;
+ }
+
+ size = (data == NULL)? 0 : data->m_pkthdr.len;
+
+ /* Create and send L2CA_Ping response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_PING,
+ sizeof(*op) + size, M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_ping_op *)(msg->data);
+ op->result = result;
+ bcopy(&con->remote, &op->bdaddr, sizeof(op->bdaddr));
+ if (data != NULL && size > 0) {
+ op->echo_size = size;
+ m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
+ }
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, NULL);
+ }
+out:
+ NG_FREE_M(data);
+
+ return (error);
+} /* ng_l2cap_l2ca_ping_rsp */
+
+/*
+ * Process L2CA_GetInfo request from the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_get_info_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_get_info_ip *ip = NULL;
+ ng_l2cap_con_p con = NULL;
+ ng_l2cap_cmd_p cmd = NULL;
+ int error = 0;
+
+ /* Verify message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_GetInfo request message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+ error = EMSGSIZE;
+ goto out;
+ }
+
+ ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
+
+ /* Check if we have connection to the unit */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ if (con == NULL) {
+ /* Submit LP_ConnectReq to the lower layer */
+ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
+ if (error != 0) {
+ NG_L2CAP_ERR(
+"%s: %s - unable to send LP_ConnectReq message, error=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node), error);
+ goto out;
+ }
+
+ /* This should not fail */
+ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
+ KASSERT((con != NULL),
+("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
+ }
+
+ /* Create L2CAP command descriptor */
+ cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
+ NG_L2CAP_INFO_REQ, msg->header.token);
+ if (cmd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (cmd->ident == NG_L2CAP_NULL_IDENT) {
+ ng_l2cap_free_cmd(cmd);
+ error = EIO;
+ goto out;
+ }
+
+ /* Create L2CAP command packet */
+ _ng_l2cap_info_req(cmd->aux, cmd->ident, ip->info_type);
+ if (cmd->aux == NULL) {
+ ng_l2cap_free_cmd(cmd);
+ error = ENOBUFS;
+ goto out;
+ }
+
+ /* Link command to the queue */
+ ng_l2cap_link_cmd(con, cmd);
+ ng_l2cap_lp_deliver(con);
+out:
+ return (error);
+} /* ng_l2cap_l2ca_get_info_req */
+
+/*
+ * Send L2CA_GetInfo response to the upper layer protocol
+ */
+
+int
+ng_l2cap_l2ca_get_info_rsp(ng_l2cap_con_p con, u_int32_t token,
+ u_int16_t result, struct mbuf *data)
+{
+ ng_l2cap_p l2cap = con->l2cap;
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_get_info_op *op = NULL;
+ int error = 0, size;
+
+ /* Check if control hook is connected and valid */
+ if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
+ NG_L2CAP_WARN(
+"%s: %s - unable to send L2CA_GetInfo response message. " \
+"Hook is not connected or valid\n",
+ __func__, NG_NODE_NAME(l2cap->node));
+ error = ENOTCONN;
+ goto out;
+ }
+
+ size = (data == NULL)? 0 : data->m_pkthdr.len;
+
+ /* Create and send L2CA_GetInfo response message */
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_GET_INFO,
+ sizeof(*op) + size, M_NOWAIT);
+ if (msg == NULL)
+ error = ENOMEM;
+ else {
+ msg->header.token = token;
+ msg->header.flags |= NGF_RESP;
+
+ op = (ng_l2cap_l2ca_get_info_op *)(msg->data);
+ op->result = result;
+ if (data != NULL && size > 0) {
+ op->info_size = size;
+ m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
+ }
+
+ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, NULL);
+ }
+out:
+ NG_FREE_M(data);
+
+ return (error);
+} /* ng_l2cap_l2ca_get_info_rsp */
+
+/*
+ * Process L2CA_EnableCLT message from the upper layer protocol
+ * XXX convert to NGN_L2CAP_NODE_SET_FLAGS?
+ */
+
+int
+ng_l2cap_l2ca_enable_clt(ng_l2cap_p l2cap, struct ng_mesg *msg)
+{
+ ng_l2cap_l2ca_enable_clt_ip *ip = NULL;
+ int error = 0;
+#if 0
+ * ng_l2cap_l2ca_enable_clt_op *op = NULL;
+ * u_int16_t result;
+ * u_int32_t token;
+#endif
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip)) {
+ NG_L2CAP_ALERT(
+"%s: %s - invalid L2CA_EnableCLT message size, size=%d\n",
+ __func__, NG_NODE_NAME(l2cap->node),
+ msg->header.arglen);
+
+ return (EMSGSIZE);
+ }
+
+ /* Process request */
+ ip = (ng_l2cap_l2ca_enable_clt_ip *) (msg->data);
+#if 0
+ * result = NG_L2CAP_SUCCESS;
+#endif
+
+ switch (ip->psm)
+ {
+ case 0:
+ /* Special case: disable/enable all PSM */
+ if (ip->enable)
+ l2cap->flags &= ~(NG_L2CAP_CLT_SDP_DISABLED |
+ NG_L2CAP_CLT_RFCOMM_DISABLED |
+ NG_L2CAP_CLT_TCP_DISABLED);
+ else
+ l2cap->flags |= (NG_L2CAP_CLT_SDP_DISABLED |
+ NG_L2CAP_CLT_RFCOMM_DISABLED |
+ NG_L2CAP_CLT_TCP_DISABLED);
+ break;
+
+ case NG_L2CAP_PSM_SDP:
+ if (ip->enable)
+ l2cap->flags &= ~NG_L2CAP_CLT_SDP_DISABLED;
+ else
+ l2cap->flags |= NG_L2CAP_CLT_SDP_DISABLED;
+ break;
+
+ case NG_L2CAP_PSM_RFCOMM:
+ if (ip->enable)
+ l2cap->flags &= ~NG_L2CAP_CLT_RFCOMM_DISABLED;
+ else
+ l2cap->flags |= NG_L2CAP_CLT_RFCOMM_DISABLED;
+ break;
+
+ case NG_L2CAP_PSM_TCP:
+ if (ip->enable)
+ l2cap->flags &= ~NG_L2CAP_CLT_TCP_DISABLED;
+ else
+ l2cap->flags |= NG_L2CAP_CLT_TCP_DISABLED;
+ break;
+
+ default:
+ NG_L2CAP_ERR(
+"%s: %s - unsupported PSM=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->psm);
+#if 0
+ * result = NG_L2CAP_PSM_NOT_SUPPORTED;
+#endif
+ error = ENOTSUP;
+ break;
+ }
+
+#if 0
+ * /* Create and send response message */
+ * token = msg->header.token;
+ * NG_FREE_MSG(msg);
+ * NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENABLE_CLT,
+ * sizeof(*op), M_NOWAIT);
+ * if (msg == NULL)
+ * error = ENOMEM;
+ * else {
+ * msg->header.token = token;
+ * msg->header.flags |= NGF_RESP;
+ *
+ * op = (ng_l2cap_l2ca_enable_clt_op *)(msg->data);
+ * op->result = result;
+ * }
+ *
+ * /* Send response to control hook */
+ * if (l2cap->ctl != NULL && NG_HOOK_IS_VALID(l2cap->ctl))
+ * NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, NULL);
+#endif
+
+ return (error);
+} /* ng_l2cap_l2ca_enable_clt */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h
new file mode 100644
index 0000000..6c464e6
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h
@@ -0,0 +1,77 @@
+/*
+ * ng_l2cap_ulpi.h
+ *
+ * Copyright (c) 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_l2cap_ulpi.h,v 1.5 2002/07/04 21:48:53 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_ULPI_H_
+#define _NETGRAPH_L2CAP_ULPI_H_
+
+int ng_l2cap_l2ca_con_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_con_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t, u_int16_t);
+int ng_l2cap_l2ca_con_rsp_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_con_rsp_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t);
+int ng_l2cap_l2ca_con_ind (ng_l2cap_chan_p);
+
+int ng_l2cap_l2ca_cfg_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_cfg_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t);
+int ng_l2cap_l2ca_cfg_rsp_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_cfg_rsp_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t);
+int ng_l2cap_l2ca_cfg_ind (ng_l2cap_chan_p);
+
+int ng_l2cap_l2ca_write_req (ng_l2cap_p, struct mbuf *);
+int ng_l2cap_l2ca_write_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t, u_int16_t);
+
+int ng_l2cap_l2ca_receive (ng_l2cap_con_p);
+int ng_l2cap_l2ca_clt_receive (ng_l2cap_con_p);
+
+int ng_l2cap_l2ca_qos_ind (ng_l2cap_chan_p);
+
+int ng_l2cap_l2ca_discon_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_discon_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t);
+int ng_l2cap_l2ca_discon_ind (ng_l2cap_chan_p);
+
+int ng_l2cap_l2ca_grp_create (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_grp_close (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_grp_add_member_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_grp_add_member_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t);
+int ng_l2cap_l2ca_grp_rem_member (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_grp_get_members (ng_l2cap_p, struct ng_mesg *);
+
+int ng_l2cap_l2ca_ping_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_ping_rsp (ng_l2cap_con_p, u_int32_t, u_int16_t,
+ struct mbuf *);
+
+int ng_l2cap_l2ca_get_info_req (ng_l2cap_p, struct ng_mesg *);
+int ng_l2cap_l2ca_get_info_rsp (ng_l2cap_con_p, u_int32_t, u_int16_t,
+ struct mbuf *);
+
+int ng_l2cap_l2ca_enable_clt (ng_l2cap_p, struct ng_mesg *);
+
+#endif /* ndef _NETGRAPH_L2CAP_ULPI_H_ */
+
diff --git a/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h b/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h
new file mode 100644
index 0000000..057fa28
--- /dev/null
+++ b/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h
@@ -0,0 +1,182 @@
+/*
+ * ng_l2cap_var.h
+ *
+ * Copyright (c) 2001 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_l2cap_var.h,v 1.13 2002/09/04 21:38:38 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_L2CAP_VAR_H_
+#define _NETGRAPH_L2CAP_VAR_H_ 1
+
+/* MALLOC decalation */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DECLARE(M_NETGRAPH_L2CAP);
+#else
+#define M_NETGRAPH_L2CAP M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Debug */
+#define NG_L2CAP_ALERT if (l2cap->debug >= NG_L2CAP_ALERT_LEVEL) printf
+#define NG_L2CAP_ERR if (l2cap->debug >= NG_L2CAP_ERR_LEVEL) printf
+#define NG_L2CAP_WARN if (l2cap->debug >= NG_L2CAP_WARN_LEVEL) printf
+#define NG_L2CAP_INFO if (l2cap->debug >= NG_L2CAP_INFO_LEVEL) printf
+
+/* Wrapper around m_pullup */
+#define NG_L2CAP_M_PULLUP(m, s) \
+ do { \
+ if ((m)->m_len < (s)) \
+ (m) = m_pullup((m), (s)); \
+ if ((m) == NULL) \
+ NG_L2CAP_ALERT("%s: %s - m_pullup(%d) failed\n", \
+ __func__, NG_NODE_NAME(l2cap->node), (s)); \
+ } while (0)
+
+/*
+ * L2CAP signaling command ident's are assigned relative to the connection,
+ * because there is only one signaling channel (cid == 0x01) for every
+ * connection. So up to 254 (0xff - 0x01) L2CAP commands can be pending at the
+ * same time for the same connection.
+ */
+
+#define NG_L2CAP_NULL_IDENT 0x00 /* DO NOT USE THIS IDENT */
+#define NG_L2CAP_FIRST_IDENT 0x01 /* dynamically alloc. (start) */
+#define NG_L2CAP_LAST_IDENT 0xff /* dynamically alloc. (end) */
+
+/*
+ * L2CAP (Node private)
+ */
+
+struct ng_l2cap_con;
+struct ng_l2cap_chan;
+
+typedef struct ng_l2cap {
+ node_p node; /* node ptr */
+
+ ng_l2cap_node_debug_ep debug; /* debug level */
+ ng_l2cap_node_flags_ep flags; /* L2CAP node flags */
+
+ bdaddr_t bdaddr; /* unit BDADDR */
+ u_int16_t pkt_size; /* max. ACL packet size */
+ u_int16_t num_pkts; /* out queue size */
+
+ hook_p hci; /* HCI downstream hook */
+ hook_p l2c; /* L2CAP upstream hook */
+ hook_p ctl; /* control hook */
+
+ LIST_HEAD(, ng_l2cap_con) con_list; /* ACL connections */
+
+ u_int16_t cid; /* last allocated CID */
+ LIST_HEAD(, ng_l2cap_chan) chan_list; /* L2CAP channels */
+} ng_l2cap_t;
+typedef ng_l2cap_t * ng_l2cap_p;
+
+/*
+ * L2CAP connection descriptor
+ */
+
+struct ng_l2cap_cmd;
+
+typedef struct ng_l2cap_con {
+ ng_l2cap_p l2cap; /* pointer to L2CAP */
+
+ u_int16_t state; /* ACL connection state */
+
+ bdaddr_t remote; /* remote unit address */
+ u_int16_t con_handle; /* ACL connection handle */
+ struct callout_handle con_timo; /* connection timeout */
+
+ u_int8_t ident; /* last allocated ident */
+ TAILQ_HEAD(, ng_l2cap_cmd) cmd_list; /* pending L2CAP cmds */
+
+ struct mbuf *tx_pkt; /* xmitted L2CAP packet */
+ int pending; /* num. of pending pkts */
+
+ struct mbuf *rx_pkt; /* received L2CAP packet */
+ int rx_pkt_len; /* packet len. so far */
+
+ LIST_ENTRY(ng_l2cap_con) next; /* link */
+} ng_l2cap_con_t;
+typedef ng_l2cap_con_t * ng_l2cap_con_p;
+
+/*
+ * L2CAP channel descriptor
+ */
+
+typedef struct ng_l2cap_chan {
+ ng_l2cap_con_p con; /* pointer to connection */
+
+ u_int16_t state; /* channel state */
+
+ u_int8_t cfg_state; /* configuration state */
+#define NG_L2CAP_CFG_IN (1 << 0) /* incoming cfg path done */
+#define NG_L2CAP_CFG_OUT (1 << 1) /* outgoing cfg path done */
+#define NG_L2CAP_CFG_BOTH (NG_L2CAP_CFG_IN|NG_L2CAP_CFG_OUT)
+
+ u_int8_t ident; /* last L2CAP req. ident */
+
+ u_int16_t psm; /* channel PSM */
+ u_int16_t scid; /* source channel ID */
+ u_int16_t dcid; /* destination channel ID */
+
+ u_int16_t imtu; /* incoming channel MTU */
+ ng_l2cap_flow_t iflow; /* incoming flow control */
+
+ u_int16_t omtu; /* outgoing channel MTU */
+ ng_l2cap_flow_t oflow; /* outgoing flow control */
+
+ u_int16_t flush_timo; /* flush timeout */
+ u_int16_t link_timo; /* link timeout */
+
+ LIST_ENTRY(ng_l2cap_chan) next; /* link */
+} ng_l2cap_chan_t;
+typedef ng_l2cap_chan_t * ng_l2cap_chan_p;
+
+/*
+ * L2CAP command descriptor
+ */
+
+typedef struct ng_l2cap_cmd {
+ ng_l2cap_con_p con; /* L2CAP connection */
+ ng_l2cap_chan_p ch; /* L2CAP channel */
+
+ u_int16_t flags; /* command flags */
+#define NG_L2CAP_CMD_PENDING (1 << 0) /* command is pending */
+
+ u_int8_t code; /* L2CAP command opcode */
+ u_int8_t ident; /* L2CAP command ident */
+ u_int32_t token; /* L2CA message token */
+
+ struct callout_handle timo; /* RTX/ERTX timeout */
+
+ struct mbuf *aux; /* optional data */
+
+ TAILQ_ENTRY(ng_l2cap_cmd) next; /* link */
+} ng_l2cap_cmd_t;
+typedef ng_l2cap_cmd_t * ng_l2cap_cmd_p;
+
+#endif /* ndef _NETGRAPH_L2CAP_VAR_H_ */
+
diff --git a/sys/netgraph/bluetooth/socket/TODO b/sys/netgraph/bluetooth/socket/TODO
new file mode 100644
index 0000000..97ba9ed
--- /dev/null
+++ b/sys/netgraph/bluetooth/socket/TODO
@@ -0,0 +1,15 @@
+$Id: TODO,v 1.4 2002/09/06 21:03:56 max Exp $
+$FreeBSD$
+
+FIXME/TODO list
+
+1) Deal properly with "shutdown"s and hook "disconnect"s
+
+ How to let L2CAP node that user called "shutdown" on node or
+ have "disconnect"ed downstream hook. Should L2CAP node deal
+ with it?
+
+2) Locking
+
+ It is OK to use mutexes, but is there a better way?
+
diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket.c b/sys/netgraph/bluetooth/socket/ng_btsocket.c
new file mode 100644
index 0000000..f3eb8ff
--- /dev/null
+++ b/sys/netgraph/bluetooth/socket/ng_btsocket.c
@@ -0,0 +1,258 @@
+/*
+ * ng_btsocket.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_btsocket.c,v 1.20 2002/09/13 17:56:58 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <bitstring.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_btsocket.h"
+#include "ng_btsocket_hci_raw.h"
+#include "ng_btsocket_l2cap.h"
+
+static int ng_btsocket_modevent (module_t, int, void *);
+extern struct domain ng_btsocket_domain;
+
+/*
+ * Bluetooth raw HCI sockets
+ */
+
+static struct pr_usrreqs ng_btsocket_hci_raw_usrreqs = {
+ ng_btsocket_hci_raw_abort, /* abort */
+ pru_accept_notsupp, /* accept */
+ ng_btsocket_hci_raw_attach, /* attach */
+ ng_btsocket_hci_raw_bind, /* bind */
+ ng_btsocket_hci_raw_connect, /* connect */
+ pru_connect2_notsupp, /* connect2 */
+ ng_btsocket_hci_raw_control, /* control */
+ ng_btsocket_hci_raw_detach, /* detach */
+ ng_btsocket_hci_raw_disconnect, /* disconnect */
+ pru_listen_notsupp, /* listen */
+ ng_btsocket_hci_raw_peeraddr, /* peeraddr */
+ pru_rcvd_notsupp, /* rcvd */
+ pru_rcvoob_notsupp, /* rcvoob */
+ ng_btsocket_hci_raw_send, /* send */
+ pru_sense_null, /* send */
+ NULL, /* shutdown */
+ ng_btsocket_hci_raw_sockaddr, /* sockaddr */
+ sosend,
+ soreceive,
+ sopoll
+};
+
+/*
+ * Bluetooth raw L2CAP sockets
+ */
+
+static struct pr_usrreqs ng_btsocket_l2cap_raw_usrreqs = {
+ ng_btsocket_l2cap_raw_abort, /* abort */
+ pru_accept_notsupp, /* accept */
+ ng_btsocket_l2cap_raw_attach, /* attach */
+ ng_btsocket_l2cap_raw_bind, /* bind */
+ ng_btsocket_l2cap_raw_connect, /* connect */
+ pru_connect2_notsupp, /* connect2 */
+ ng_btsocket_l2cap_raw_control, /* control */
+ ng_btsocket_l2cap_raw_detach, /* detach */
+ ng_btsocket_l2cap_raw_disconnect, /* disconnect */
+ pru_listen_notsupp, /* listen */
+ ng_btsocket_l2cap_raw_peeraddr, /* peeraddr */
+ pru_rcvd_notsupp, /* rcvd */
+ pru_rcvoob_notsupp, /* rcvoob */
+ ng_btsocket_l2cap_raw_send, /* send */
+ pru_sense_null, /* send */
+ NULL, /* shutdown */
+ ng_btsocket_l2cap_raw_sockaddr, /* sockaddr */
+ sosend,
+ soreceive,
+ sopoll
+};
+
+/*
+ * Bluetooth SEQPACKET L2CAP sockets
+ */
+
+static struct pr_usrreqs ng_btsocket_l2cap_usrreqs = {
+ ng_btsocket_l2cap_abort, /* abort */
+ ng_btsocket_l2cap_accept, /* accept */
+ ng_btsocket_l2cap_attach, /* attach */
+ ng_btsocket_l2cap_bind, /* bind */
+ ng_btsocket_l2cap_connect, /* connect */
+ pru_connect2_notsupp, /* connect2 */
+ ng_btsocket_l2cap_control, /* control */
+ ng_btsocket_l2cap_detach, /* detach */
+ ng_btsocket_l2cap_disconnect, /* disconnect */
+ ng_btsocket_l2cap_listen, /* listen */
+ ng_btsocket_l2cap_peeraddr, /* peeraddr */
+ pru_rcvd_notsupp, /* rcvd */
+ pru_rcvoob_notsupp, /* rcvoob */
+ ng_btsocket_l2cap_send, /* send */
+ pru_sense_null, /* send */
+ NULL, /* shutdown */
+ ng_btsocket_l2cap_sockaddr, /* sockaddr */
+ sosend,
+ soreceive,
+ sopoll
+};
+
+/*
+ * Definitions of protocols supported in the BLUETOOTH domain
+ */
+
+static struct protosw ng_btsocket_protosw[] = {
+{
+ SOCK_RAW, /* protocol type */
+ &ng_btsocket_domain, /* backpointer to domain */
+ BLUETOOTH_PROTO_HCI, /* protocol */
+ PR_ATOMIC | PR_ADDR, /* flags */
+ NULL, NULL, NULL, /* input, output, ctlinput */
+ ng_btsocket_hci_raw_ctloutput, /* ctloutput */
+ NULL, /* ousrreq() */
+ ng_btsocket_hci_raw_init, /* init */
+ NULL, NULL, NULL, /* fasttimeo, slowtimo, drain */
+ &ng_btsocket_hci_raw_usrreqs, /* usrreq table (above) */
+ /* { NULL } */ /* pfh (protocol filter head?) */
+},
+{
+ SOCK_RAW, /* protocol type */
+ &ng_btsocket_domain, /* backpointer to domain */
+ BLUETOOTH_PROTO_L2CAP, /* protocol */
+ PR_ATOMIC | PR_ADDR, /* flags */
+ NULL, NULL, NULL, /* input, output, ctlinput */
+ NULL, /* ctloutput */
+ NULL, /* ousrreq() */
+ ng_btsocket_l2cap_raw_init, /* init */
+ NULL, NULL, NULL, /* fasttimeo, slowtimo, drain */
+ &ng_btsocket_l2cap_raw_usrreqs, /* usrreq table (above) */
+ /* { NULL } */ /* pfh (protocol filter head?) */
+},
+{
+ SOCK_SEQPACKET, /* protocol type */
+ &ng_btsocket_domain, /* backpointer to domain */
+ BLUETOOTH_PROTO_L2CAP, /* protocol */
+ PR_ATOMIC | PR_CONNREQUIRED, /* flags */
+ NULL, NULL, NULL, /* input, output, ctlinput */
+ ng_btsocket_l2cap_ctloutput, /* ctloutput */
+ NULL, /* ousrreq() */
+ ng_btsocket_l2cap_init, /* init */
+ NULL, NULL, NULL, /* fasttimeo, slowtimo, drain */
+ &ng_btsocket_l2cap_usrreqs, /* usrreq table (above) */
+ /* { NULL } */ /* pfh (protocol filter head?) */
+}
+};
+#define ng_btsocket_protosw_size \
+ (sizeof(ng_btsocket_protosw)/sizeof(ng_btsocket_protosw[0]))
+#define ng_btsocket_protosw_end \
+ &ng_btsocket_protosw[ng_btsocket_protosw_size]
+
+/*
+ * BLUETOOTH domain
+ */
+
+struct domain ng_btsocket_domain = {
+ AF_BLUETOOTH, /* family */
+ "bluetooth", /* domain name */
+ NULL, /* init() */
+ NULL, /* externalize() */
+ NULL, /* dispose() */
+ ng_btsocket_protosw, /* protosw entry */
+ ng_btsocket_protosw_end, /* end of protosw entries */
+ NULL, /* next domain in list */
+ NULL, /* rtattach() */
+ 0, /* arg to rtattach in bits */
+ 0 /* maxrtkey */
+};
+
+/*
+ * Socket sysctl tree
+ */
+
+SYSCTL_NODE(_net_bluetooth_hci, OID_AUTO, sockets, CTLFLAG_RW,
+ 0, "Bluetooth HCI sockets family");
+SYSCTL_NODE(_net_bluetooth_l2cap, OID_AUTO, sockets, CTLFLAG_RW,
+ 0, "Bluetooth L2CAP sockets family");
+
+/*
+ * Module
+ */
+
+static moduledata_t ng_btsocket_mod = {
+ "ng_btsocket",
+ ng_btsocket_modevent,
+ NULL
+};
+
+DECLARE_MODULE(ng_btsocket, ng_btsocket_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(ng_btsocket, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_btsocket, ng_bluetooth, NG_BLUETOOTH_VERSION,
+ NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_btsocket, netgraph, NG_ABI_VERSION,
+ NG_ABI_VERSION, NG_ABI_VERSION);
+
+/*
+ * Handle loading and unloading for this node type.
+ * This is to handle auxiliary linkages (e.g protocol domain addition).
+ */
+
+static int
+ng_btsocket_modevent(module_t mod, int event, void *data)
+{
+ int error = 0;
+
+ switch (event) {
+ case MOD_LOAD:
+ net_add_domain(&ng_btsocket_domain);
+ break;
+
+ case MOD_UNLOAD:
+ /* XXX can't unload protocol domain yet */
+ error = EBUSY;
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+} /* ng_btsocket_modevent */
+
diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c b/sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c
new file mode 100644
index 0000000..69fcbf0
--- /dev/null
+++ b/sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c
@@ -0,0 +1,1320 @@
+/*
+ * ng_btsocket_hci_raw.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_btsocket_hci_raw.c,v 1.3 2002/11/12 22:31:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domain.h>
+#include <sys/errno.h>
+#include <sys/filedesc.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <bitstring.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_btsocket.h"
+#include "ng_btsocket_hci_raw.h"
+
+/* MALLOC define */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_HCI_RAW, "netgraph_btsocks_hci_raw",
+ "Netgraph Bluetooth raw HCI sockets");
+#else
+#define M_NETGRAPH_BTSOCKET_HCI_RAW M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Netgraph node methods */
+static ng_constructor_t ng_btsocket_hci_raw_node_constructor;
+static ng_rcvmsg_t ng_btsocket_hci_raw_node_rcvmsg;
+static ng_shutdown_t ng_btsocket_hci_raw_node_shutdown;
+static ng_newhook_t ng_btsocket_hci_raw_node_newhook;
+static ng_connect_t ng_btsocket_hci_raw_node_connect;
+static ng_rcvdata_t ng_btsocket_hci_raw_node_rcvdata;
+static ng_disconnect_t ng_btsocket_hci_raw_node_disconnect;
+
+static void ng_btsocket_hci_raw_input (void *, int);
+static void ng_btsocket_hci_raw_output(node_p, hook_p, void *, int);
+static void ng_btsocket_hci_raw_savctl(ng_btsocket_hci_raw_pcb_p,
+ struct mbuf **,
+ struct mbuf *);
+
+/* Netgraph type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_BTSOCKET_HCI_RAW_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_btsocket_hci_raw_node_constructor, /* constructor */
+ ng_btsocket_hci_raw_node_rcvmsg, /* control message */
+ ng_btsocket_hci_raw_node_shutdown, /* destructor */
+ ng_btsocket_hci_raw_node_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_btsocket_hci_raw_node_connect, /* connect hook */
+ ng_btsocket_hci_raw_node_rcvdata, /* data */
+ ng_btsocket_hci_raw_node_disconnect, /* disconnect hook */
+ NULL /* node command list */
+};
+
+/* Globals */
+extern int ifqmaxlen;
+static u_int32_t ng_btsocket_hci_raw_debug_level;
+static u_int32_t ng_btsocket_hci_raw_ioctl_timeout;
+static node_p ng_btsocket_hci_raw_node;
+static struct ng_bt_itemq ng_btsocket_hci_raw_queue;
+static struct mtx ng_btsocket_hci_raw_queue_mtx;
+static struct task ng_btsocket_hci_raw_task;
+static LIST_HEAD(, ng_btsocket_hci_raw_pcb) ng_btsocket_hci_raw_sockets;
+static struct mtx ng_btsocket_hci_raw_sockets_mtx;
+static u_int32_t ng_btsocket_hci_raw_token;
+static struct mtx ng_btsocket_hci_raw_token_mtx;
+
+/* Sysctl tree */
+SYSCTL_DECL(_net_bluetooth_hci_sockets);
+SYSCTL_NODE(_net_bluetooth_hci_sockets, OID_AUTO, raw, CTLFLAG_RW,
+ 0, "Bluetooth raw HCI sockets family");
+SYSCTL_INT(_net_bluetooth_hci_sockets_raw, OID_AUTO, debug_level, CTLFLAG_RW,
+ &ng_btsocket_hci_raw_debug_level, NG_BTSOCKET_WARN_LEVEL,
+ "Bluetooth raw HCI sockets debug level");
+SYSCTL_INT(_net_bluetooth_hci_sockets_raw, OID_AUTO, ioctl_timeout, CTLFLAG_RW,
+ &ng_btsocket_hci_raw_ioctl_timeout, 5,
+ "Bluetooth raw HCI sockets ioctl timeout");
+SYSCTL_INT(_net_bluetooth_hci_sockets_raw, OID_AUTO, queue_len, CTLFLAG_RD,
+ &ng_btsocket_hci_raw_queue.len, 0,
+ "Bluetooth raw HCI sockets input queue length");
+SYSCTL_INT(_net_bluetooth_hci_sockets_raw, OID_AUTO, queue_maxlen, CTLFLAG_RD,
+ &ng_btsocket_hci_raw_queue.maxlen, 0,
+ "Bluetooth raw HCI sockets input queue max. length");
+SYSCTL_INT(_net_bluetooth_hci_sockets_raw, OID_AUTO, queue_drops, CTLFLAG_RD,
+ &ng_btsocket_hci_raw_queue.drops, 0,
+ "Bluetooth raw HCI sockets input queue drops");
+
+/* Debug */
+#define NG_BTSOCKET_HCI_RAW_INFO \
+ if (ng_btsocket_hci_raw_debug_level >= NG_BTSOCKET_INFO_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_HCI_RAW_WARN \
+ if (ng_btsocket_hci_raw_debug_level >= NG_BTSOCKET_WARN_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_HCI_RAW_ERR \
+ if (ng_btsocket_hci_raw_debug_level >= NG_BTSOCKET_ERR_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_HCI_RAW_ALERT \
+ if (ng_btsocket_hci_raw_debug_level >= NG_BTSOCKET_ALERT_LEVEL) \
+ printf
+
+/****************************************************************************
+ ****************************************************************************
+ ** Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+static int
+ng_btsocket_hci_raw_node_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_btsocket_hci_raw_node_constructor */
+
+/*
+ * Netgraph node destructor. Just let old node go and create new fresh one.
+ */
+
+static int
+ng_btsocket_hci_raw_node_shutdown(node_p node)
+{
+ int error = 0;
+
+ NG_NODE_UNREF(node);
+
+ error = ng_make_node_common(&typestruct, &ng_btsocket_hci_raw_node);
+ if (error != 0) {
+ NG_BTSOCKET_HCI_RAW_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_hci_raw_node = NULL;
+
+ return (ENOMEM);
+ }
+
+ error = ng_name_node(ng_btsocket_hci_raw_node,
+ NG_BTSOCKET_HCI_RAW_NODE_TYPE);
+ if (error != 0) {
+ NG_BTSOCKET_HCI_RAW_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_hci_raw_node);
+ ng_btsocket_hci_raw_node = NULL;
+
+ return (EINVAL);
+ }
+
+ return (0);
+} /* ng_btsocket_hci_raw_node_shutdown */
+
+/*
+ * Create new hook. Just say "yes"
+ */
+
+static int
+ng_btsocket_hci_raw_node_newhook(node_p node, hook_p hook, char const *name)
+{
+ return (0);
+} /* ng_btsocket_hci_raw_node_newhook */
+
+/*
+ * Connect hook. Just say "yes"
+ */
+
+static int
+ng_btsocket_hci_raw_node_connect(hook_p hook)
+{
+ return (0);
+} /* ng_btsocket_hci_raw_node_connect */
+
+/*
+ * Disconnect hook
+ */
+
+static int
+ng_btsocket_hci_raw_node_disconnect(hook_p hook)
+{
+ return (0);
+} /* ng_btsocket_hci_raw_node_disconnect */
+
+/*
+ * Receive control message.
+ * Make sure it is a message from HCI node and it is a response.
+ * Enqueue item and schedule input task.
+ */
+
+static int
+ng_btsocket_hci_raw_node_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ struct ng_mesg *msg = NGI_MSG(item); /* item still has message */
+ int error = 0;
+
+ if (msg != NULL &&
+ msg->header.typecookie == NGM_HCI_COOKIE &&
+ msg->header.flags & NGF_RESP) {
+ mtx_lock(&ng_btsocket_hci_raw_queue_mtx);
+ if (NG_BT_ITEMQ_FULL(&ng_btsocket_hci_raw_queue)) {
+ NG_BTSOCKET_HCI_RAW_ERR(
+"%s: Input queue is full\n", __func__);
+
+ NG_BT_ITEMQ_DROP(&ng_btsocket_hci_raw_queue);
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ } else {
+ NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_hci_raw_queue, item);
+ error = taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_hci_raw_task);
+ }
+ mtx_unlock(&ng_btsocket_hci_raw_queue_mtx);
+ } else {
+ NG_FREE_ITEM(item);
+ error = EINVAL;
+ }
+
+ return (error);
+} /* ng_btsocket_hci_raw_node_rcvmsg */
+
+/*
+ * Receive packet from the one of our hook.
+ * Prepend every packet with sockaddr_hci and record sender's node name.
+ * Enqueue item and schedule input task.
+ */
+
+static int
+ng_btsocket_hci_raw_node_rcvdata(hook_p hook, item_p item)
+{
+ struct mbuf *nam = NULL;
+ int error;
+
+ MGET(nam, M_DONTWAIT, MT_SONAME);
+ if (nam != NULL) {
+ struct sockaddr_hci *sa = mtod(nam, struct sockaddr_hci *);
+
+ nam->m_len = sizeof(struct sockaddr_hci);
+
+ sa->hci_len = sizeof(*sa);
+ sa->hci_family = AF_BLUETOOTH;
+ strncpy(sa->hci_node, NG_PEER_NODE_NAME(hook),
+ sizeof(sa->hci_node));
+ sa->hci_node[sizeof(sa->hci_node) - 1] = 0; /* sanity */
+
+ NGI_GET_M(item, nam->m_next);
+ NGI_M(item) = nam;
+
+ mtx_lock(&ng_btsocket_hci_raw_queue_mtx);
+ if (NG_BT_ITEMQ_FULL(&ng_btsocket_hci_raw_queue)) {
+ NG_BTSOCKET_HCI_RAW_ERR(
+"%s: Input queue is full\n", __func__);
+
+ NG_BT_ITEMQ_DROP(&ng_btsocket_hci_raw_queue);
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ } else {
+ NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_hci_raw_queue, item);
+ error = taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_hci_raw_task);
+ }
+ mtx_unlock(&ng_btsocket_hci_raw_queue_mtx);
+ } else {
+ NG_BTSOCKET_HCI_RAW_ERR(
+"%s: Failed to allocate address mbuf\n", __func__);
+
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ }
+
+ return (error);
+} /* ng_btsocket_hci_raw_node_rcvdata */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Sockets specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Get next token
+ */
+
+static void
+ng_btsocket_hci_raw_get_token(u_int32_t *token)
+{
+ mtx_lock(&ng_btsocket_hci_raw_token_mtx);
+
+ if (++ ng_btsocket_hci_raw_token == 0)
+ ng_btsocket_hci_raw_token = 1;
+
+ *token = ng_btsocket_hci_raw_token;
+
+ mtx_unlock(&ng_btsocket_hci_raw_token_mtx);
+} /* ng_btsocket_hci_raw_token */
+
+/*
+ * Send Netgraph message to the node - do not expect reply
+ */
+
+static int
+ng_btsocket_raw_send_ngmsg(char *path, int cmd, void *arg, int arglen)
+{
+ struct ng_mesg *msg = NULL;
+ int error = 0;
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, cmd, arglen, M_WAITOK);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ if (arg != NULL && arglen > 0)
+ bcopy(arg, msg->data, arglen);
+
+ NG_SEND_MSG_PATH(error, ng_btsocket_hci_raw_node, msg, path, NULL);
+
+ return (error);
+} /* ng_btsocket_raw_send_ngmsg */
+
+/*
+ * Send Netgraph message to the node (no data) and wait for reply
+ */
+
+static int
+ng_btsocket_raw_send_sync_ngmsg(ng_btsocket_hci_raw_pcb_p pcb, char *path,
+ int cmd, void *rsp, int rsplen)
+{
+ struct ng_mesg *msg = NULL;
+ int error = 0;
+
+ ng_btsocket_hci_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, cmd, 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ return (ENOMEM);
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_PATH(error, ng_btsocket_hci_raw_node, msg, path, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ return (error);
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "hcictl",
+ ng_btsocket_hci_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ return (error);
+ }
+
+ if (pcb->msg != NULL && pcb->msg->header.cmd == cmd)
+ bcopy(pcb->msg->data, rsp, rsplen);
+ else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+
+ return (0);
+} /* ng_btsocket_raw_send_sync_ngmsg */
+
+/*
+ * Create control information for the packet
+ */
+
+static void
+ng_btsocket_hci_raw_savctl(ng_btsocket_hci_raw_pcb_p pcb, struct mbuf **ctl,
+ struct mbuf *m)
+{
+ int dir;
+ struct timeval tv;
+
+ if (pcb->flags & NG_BTSOCKET_HCI_RAW_DIRECTION) {
+ dir = (m->m_flags & M_PROTO1)? 1 : 0;
+ *ctl = sbcreatecontrol((caddr_t) &dir, sizeof(dir),
+ SCM_HCI_RAW_DIRECTION, SOL_HCI_RAW);
+ if (*ctl != NULL)
+ ctl = &((*ctl)->m_next);
+ }
+
+ if (pcb->so->so_options & SO_TIMESTAMP) {
+ microtime(&tv);
+ *ctl = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*ctl != NULL)
+ ctl = &((*ctl)->m_next);
+ }
+} /* ng_btsocket_hci_raw_savctl */
+
+/*
+ * Raw HCI sockets data input routine
+ */
+
+static void
+ng_btsocket_hci_raw_data_input(struct mbuf *nam)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = NULL;
+ struct mbuf *m0 = NULL, *m = NULL;
+ struct sockaddr_hci *sa = NULL;
+ bitstr_t *mask = NULL;
+ int bit;
+
+ m0 = nam->m_next;
+ nam->m_next = NULL;
+
+ KASSERT((nam->m_type == MT_SONAME),
+ ("%s: m_type=%d\n", __func__, nam->m_type));
+ KASSERT((m0->m_flags & M_PKTHDR),
+ ("%s: m_flags=%#x\n", __func__, m0->m_flags));
+
+ sa = mtod(nam, struct sockaddr_hci *);
+
+ mtx_lock(&ng_btsocket_hci_raw_sockets_mtx);
+
+ LIST_FOREACH(pcb, &ng_btsocket_hci_raw_sockets, next) {
+ /*
+ * If socket was bound then check address and
+ * make sure it matches.
+ */
+
+ if (pcb->addr.hci_node[0] != 0 &&
+ strcmp(sa->hci_node, pcb->addr.hci_node) != 0)
+ continue;
+
+ /*
+ * Check packet agains socket filter
+ * XXX do we have to call m_pullup() here?
+ */
+
+ switch (*mtod(m0, u_int8_t *)) {
+ case NG_HCI_CMD_PKT:
+ case NG_HCI_ACL_DATA_PKT:
+ case NG_HCI_SCO_DATA_PKT:
+ mask = pcb->filter.packet_mask;
+ bit = *mtod(m0, u_int8_t *) - 1;
+ break;
+
+ case NG_HCI_EVENT_PKT:
+ mask = pcb->filter.event_mask;
+ bit = mtod(m0, ng_hci_event_pkt_t *)->event - 1;
+ break;
+
+ default:
+ KASSERT(0,
+("%s: invalid packet type=%#x\n", __func__, *mtod(m0, u_int8_t *)));
+
+ mask = NULL;
+ bit = 0;
+ break;
+ }
+
+ if (mask == NULL || !bit_test(mask, bit))
+ continue;
+
+ /*
+ * Make a copy of the packet, append to the socket's
+ * receive queue and wakeup socket. sbappendaddr()
+ * will check if socket has enough buffer space.
+ */
+
+ m = m_dup(m0, M_DONTWAIT);
+ if (m != NULL) {
+ struct mbuf *ctl = NULL;
+
+ ng_btsocket_hci_raw_savctl(pcb, &ctl, m);
+
+ if (sbappendaddr(&pcb->so->so_rcv,
+ (struct sockaddr *) sa, m, ctl))
+ sorwakeup(pcb->so);
+ else {
+ NG_BTSOCKET_HCI_RAW_WARN(
+"%s: sbappendadd() failed\n", __func__);
+
+ NG_FREE_M(m);
+ NG_FREE_M(ctl);
+ }
+ }
+ }
+
+ mtx_unlock(&ng_btsocket_hci_raw_sockets_mtx);
+
+ NG_FREE_M(nam);
+ NG_FREE_M(m0);
+} /* ng_btsocket_hci_raw_data_input */
+
+/*
+ * Raw HCI sockets message input routine
+ */
+
+static void
+ng_btsocket_hci_raw_msg_input(struct ng_mesg *msg)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = NULL;
+
+ if (msg->header.token != 0) {
+ mtx_lock(&ng_btsocket_hci_raw_sockets_mtx);
+
+ LIST_FOREACH(pcb, &ng_btsocket_hci_raw_sockets, next) {
+ if (msg->header.token == pcb->token) {
+ pcb->msg = msg;
+ msg = NULL;
+ wakeup(&pcb->msg);
+ break;
+ }
+ }
+
+ mtx_unlock(&ng_btsocket_hci_raw_sockets_mtx);
+ }
+
+ NG_FREE_MSG(msg); /* checks for != NULL */
+} /* ng_btsocket_hci_raw_msg_input */
+
+/*
+ * Raw HCI sockets input routines
+ */
+
+static void
+ng_btsocket_hci_raw_input(void *context, int pending)
+{
+ item_p item = NULL;
+
+ for (;;) {
+ mtx_lock(&ng_btsocket_hci_raw_queue_mtx);
+ NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_hci_raw_queue, item);
+ mtx_unlock(&ng_btsocket_hci_raw_queue_mtx);
+
+ if (item == NULL)
+ break;
+
+ switch(item->el_flags & NGQF_TYPE) {
+ case NGQF_DATA: {
+ struct mbuf *m = NULL;
+
+ NGI_GET_M(item, m);
+ ng_btsocket_hci_raw_data_input(m);
+ } break;
+
+ case NGQF_MESG: {
+ struct ng_mesg *msg = NULL;
+
+ NGI_GET_MSG(item, msg);
+ ng_btsocket_hci_raw_msg_input(msg);
+ } break;
+
+ default:
+ KASSERT(0,
+("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
+ break;
+ }
+
+ NG_FREE_ITEM(item);
+ }
+} /* ng_btsocket_hci_raw_input */
+
+/*
+ * Raw HCI sockets output routine
+ */
+
+static void
+ng_btsocket_hci_raw_output(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ struct mbuf *nam = (struct mbuf *) arg1, *m = NULL;
+ struct sockaddr_hci *sa = NULL;
+ int error;
+
+ m = nam->m_next;
+ nam->m_next = NULL;
+
+ KASSERT((nam->m_type == MT_SONAME),
+ ("%s: m_type=%d\n", __func__, nam->m_type));
+ KASSERT((m->m_flags & M_PKTHDR),
+ ("%s: m_flags=%#x\n", __func__, m->m_flags));
+
+ sa = mtod(nam, struct sockaddr_hci *);
+
+ /*
+ * Find downstream hook
+ * XXX For now access node hook list directly. Should be safe because
+ * we used ng_send_fn() and we should have exclusive lock on the node.
+ */
+
+ LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
+ if (hook == NULL || NG_HOOK_NOT_VALID(hook) ||
+ NG_NODE_NOT_VALID(NG_PEER_NODE(hook)))
+ continue;
+
+ if (strcmp(sa->hci_node, NG_PEER_NODE_NAME(hook)) == 0) {
+ NG_SEND_DATA_ONLY(error, hook, m); /* sets m to NULL */
+ break;
+ }
+ }
+
+ NG_FREE_M(nam); /* check for != NULL */
+ NG_FREE_M(m);
+} /* ng_btsocket_hci_raw_output */
+
+/*
+ * Initialize everything
+ */
+
+void
+ng_btsocket_hci_raw_init(void)
+{
+ int error = 0;
+
+ ng_btsocket_hci_raw_node = NULL;
+ ng_btsocket_hci_raw_debug_level = NG_BTSOCKET_WARN_LEVEL;
+ ng_btsocket_hci_raw_ioctl_timeout = 5;
+
+ /* Register Netgraph node type */
+ error = ng_newtype(&typestruct);
+ if (error != 0) {
+ NG_BTSOCKET_HCI_RAW_ALERT(
+"%s: Could not register Netgraph node type, error=%d\n", __func__, error);
+
+ return;
+ }
+
+ /* Create Netgrapg node */
+ error = ng_make_node_common(&typestruct, &ng_btsocket_hci_raw_node);
+ if (error != 0) {
+ NG_BTSOCKET_HCI_RAW_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_hci_raw_node = NULL;
+
+ return;
+ }
+
+ error = ng_name_node(ng_btsocket_hci_raw_node,
+ NG_BTSOCKET_HCI_RAW_NODE_TYPE);
+ if (error != 0) {
+ NG_BTSOCKET_HCI_RAW_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_hci_raw_node);
+ ng_btsocket_hci_raw_node = NULL;
+
+ return;
+ }
+
+ /* Create input queue */
+ NG_BT_ITEMQ_INIT(&ng_btsocket_hci_raw_queue, ifqmaxlen);
+ mtx_init(&ng_btsocket_hci_raw_queue_mtx,
+ "btsocks_hci_raw_queue_mtx", NULL, MTX_DEF);
+ TASK_INIT(&ng_btsocket_hci_raw_task, 0,
+ ng_btsocket_hci_raw_input, NULL);
+
+ /* Create list of sockets */
+ LIST_INIT(&ng_btsocket_hci_raw_sockets);
+ mtx_init(&ng_btsocket_hci_raw_sockets_mtx,
+ "btsocks_hci_raw_sockets_mtx", NULL, MTX_DEF);
+
+ /* Tokens */
+ ng_btsocket_hci_raw_token = 0;
+ mtx_init(&ng_btsocket_hci_raw_token_mtx,
+ "btsocks_hci_raw_token_mtx", NULL, MTX_DEF);
+} /* ng_btsocket_hci_raw_init */
+
+/*
+ * Abort connection on socket
+ */
+
+int
+ng_btsocket_hci_raw_abort(struct socket *so)
+{
+ soisdisconnected(so);
+
+ return (ng_btsocket_hci_raw_detach(so));
+} /* ng_btsocket_hci_raw_abort */
+
+/*
+ * Create new raw HCI socket
+ */
+
+int
+ng_btsocket_hci_raw_attach(struct socket *so, int proto, struct thread *td)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ int error = 0;
+
+ if (pcb != NULL)
+ return (EISCONN);
+
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EPROTONOSUPPORT);
+ if (proto != BLUETOOTH_PROTO_HCI)
+ return (EPROTONOSUPPORT);
+ if (so->so_type != SOCK_RAW)
+ return (ESOCKTNOSUPPORT);
+ if ((error = suser(td)) != 0)
+ return (error);
+
+ error = soreserve(so, NG_BTSOCKET_HCI_RAW_SENDSPACE,
+ NG_BTSOCKET_HCI_RAW_RECVSPACE);
+ if (error != 0)
+ return (error);
+
+ MALLOC(pcb, ng_btsocket_hci_raw_pcb_p, sizeof(*pcb),
+ M_NETGRAPH_BTSOCKET_HCI_RAW, M_WAITOK | M_ZERO);
+ if (pcb == NULL)
+ return (ENOMEM);
+
+ so->so_pcb = (caddr_t) pcb;
+ pcb->so = so;
+
+ /*
+ * Set default socket filter. By default socket only accepts HCI
+ * Command_Complete and Command_Status event packets.
+ */
+
+ bit_set(pcb->filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1);
+ bit_set(pcb->filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1);
+
+ mtx_lock(&ng_btsocket_hci_raw_sockets_mtx);
+ LIST_INSERT_HEAD(&ng_btsocket_hci_raw_sockets, pcb, next);
+ mtx_unlock(&ng_btsocket_hci_raw_sockets_mtx);
+
+ return (0);
+} /* ng_btsocket_hci_raw_attach */
+
+/*
+ * Bind raw HCI socket
+ */
+
+int
+ng_btsocket_hci_raw_bind(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ struct sockaddr_hci *sa = (struct sockaddr_hci *) nam;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->hci_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->hci_len != sizeof(*sa))
+ return (EINVAL);
+ if (sa->hci_node[0] == 0)
+ return (EINVAL);
+
+ bcopy(sa, &pcb->addr, sizeof(pcb->addr));
+
+ return (0);
+} /* ng_btsocket_hci_raw_bind */
+
+/*
+ * Connect raw HCI socket
+ */
+
+int
+ng_btsocket_hci_raw_connect(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ struct sockaddr_hci *sa = (struct sockaddr_hci *) nam;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->hci_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->hci_len != sizeof(*sa))
+ return (EINVAL);
+ if (sa->hci_node[0] == 0)
+ return (EDESTADDRREQ);
+ if (bcmp(sa, &pcb->addr, sizeof(pcb->addr)) != 0)
+ return (EADDRNOTAVAIL);
+
+ soisconnected(so);
+
+ return (0);
+} /* ng_btsocket_hci_raw_connect */
+
+/*
+ * Process ioctl on socket
+ */
+
+int
+ng_btsocket_hci_raw_control(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp, struct thread *td)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ char path[NG_NODELEN + 2],
+ *hci_node = (char *) data;
+ struct ng_mesg *msg = NULL;
+ int error = 0;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ /*
+ * Make sure caller has provided HCI node name, if not try to
+ * use addr from socket (if socket was bound)
+ */
+
+ if (hci_node[0] == 0) {
+ if (pcb->addr.hci_node[0] == 0)
+ return (EINVAL);
+
+ bzero(hci_node, sizeof(pcb->addr.hci_node));
+ strncpy(hci_node,pcb->addr.hci_node,sizeof(pcb->addr.hci_node));
+ }
+
+ snprintf(path, sizeof(path), "%s:", hci_node);
+
+ switch (cmd) {
+ case SIOC_HCI_RAW_NODE_GET_STATE: {
+ struct ng_btsocket_hci_raw_node_state *p =
+ (struct ng_btsocket_hci_raw_node_state *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_STATE,
+ &p->state, sizeof(p->state));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_INIT:
+ error = ng_btsocket_raw_send_ngmsg(path, NGM_HCI_NODE_INIT,
+ NULL, 0);
+ break;
+
+ case SIOC_HCI_RAW_NODE_GET_DEBUG: {
+ struct ng_btsocket_hci_raw_node_debug *p =
+ (struct ng_btsocket_hci_raw_node_debug *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_DEBUG,
+ &p->debug, sizeof(p->debug));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_SET_DEBUG: {
+ struct ng_btsocket_hci_raw_node_debug *p =
+ (struct ng_btsocket_hci_raw_node_debug *) data;
+
+ error = ng_btsocket_raw_send_ngmsg(path, NGM_HCI_NODE_SET_DEBUG,
+ &p->debug, sizeof(p->debug));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_BUFFER: {
+ struct ng_btsocket_hci_raw_node_buffer *p =
+ (struct ng_btsocket_hci_raw_node_buffer *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_BUFFER,
+ &p->buffer, sizeof(p->buffer));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_BDADDR: {
+ struct ng_btsocket_hci_raw_node_bdaddr *p =
+ (struct ng_btsocket_hci_raw_node_bdaddr *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_BDADDR,
+ &p->bdaddr, sizeof(p->bdaddr));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_FEATURES: {
+ struct ng_btsocket_hci_raw_node_features *p =
+ (struct ng_btsocket_hci_raw_node_features *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_FEATURES,
+ &p->features, sizeof(p->features));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_STAT: {
+ struct ng_btsocket_hci_raw_node_stat *p =
+ (struct ng_btsocket_hci_raw_node_stat *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_STAT,
+ &p->stat, sizeof(p->stat));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_RESET_STAT:
+ error = ng_btsocket_raw_send_ngmsg(path,
+ NGM_HCI_NODE_RESET_STAT, NULL, 0);
+ break;
+
+ case SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE:
+ error = ng_btsocket_raw_send_ngmsg(path,
+ NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE, NULL, 0);
+ break;
+
+ case SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE: {
+ struct ng_btsocket_hci_raw_node_neighbor_cache *p =
+ (struct ng_btsocket_hci_raw_node_neighbor_cache *) data;
+ ng_hci_node_get_neighbor_cache_ep *p1 = NULL;
+ ng_hci_node_neighbor_cache_entry_ep *p2 = NULL;
+
+ if (p->num_entries <= 0 ||
+ p->num_entries > NG_HCI_MAX_NEIGHBOR_NUM ||
+ p->entries == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ ng_btsocket_hci_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
+ NGM_HCI_NODE_GET_NEIGHBOR_CACHE, 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_PATH(error,ng_btsocket_hci_raw_node,msg,path,NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "hcictl",
+ ng_btsocket_hci_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_HCI_NODE_GET_NEIGHBOR_CACHE) {
+ /* Return data back to user space */
+ p1 = (ng_hci_node_get_neighbor_cache_ep *)
+ (pcb->msg->data);
+ p2 = (ng_hci_node_neighbor_cache_entry_ep *)
+ (p1 + 1);
+
+ p->num_entries = min(p->num_entries, p1->num_entries);
+ if (p->num_entries > 0)
+ error = copyout((caddr_t) p2,
+ (caddr_t) p->entries,
+ p->num_entries * sizeof(*p2));
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ }break;
+
+ case SIOC_HCI_RAW_NODE_GET_CON_LIST: {
+ struct ng_btsocket_hci_raw_con_list *p =
+ (struct ng_btsocket_hci_raw_con_list *) data;
+ ng_hci_node_con_list_ep *p1 = NULL;
+ ng_hci_node_con_ep *p2 = NULL;
+
+ if (p->num_connections == 0 ||
+ p->num_connections > NG_HCI_MAX_CON_NUM ||
+ p->connections == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ ng_btsocket_hci_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_NODE_GET_CON_LIST,
+ 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_PATH(error,ng_btsocket_hci_raw_node,msg,path,NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "hcictl",
+ ng_btsocket_hci_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_HCI_NODE_GET_CON_LIST) {
+ /* Return data back to user space */
+ p1 = (ng_hci_node_con_list_ep *)(pcb->msg->data);
+ p2 = (ng_hci_node_con_ep *)(p1 + 1);
+
+ p->num_connections = min(p->num_connections,
+ p1->num_connections);
+ if (p->num_connections > 0)
+ error = copyout((caddr_t) p2,
+ (caddr_t) p->connections,
+ p->num_connections * sizeof(*p2));
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK: {
+ struct ng_btsocket_hci_raw_node_link_policy_mask *p =
+ (struct ng_btsocket_hci_raw_node_link_policy_mask *)
+ data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK,
+ &p->policy_mask, sizeof(p->policy_mask));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK: {
+ struct ng_btsocket_hci_raw_node_link_policy_mask *p =
+ (struct ng_btsocket_hci_raw_node_link_policy_mask *)
+ data;
+
+ error = ng_btsocket_raw_send_ngmsg(path,
+ NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK,
+ &p->policy_mask, sizeof(p->policy_mask));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_GET_PACKET_MASK: {
+ struct ng_btsocket_hci_raw_node_packet_mask *p =
+ (struct ng_btsocket_hci_raw_node_packet_mask *) data;
+
+ error = ng_btsocket_raw_send_sync_ngmsg(pcb, path,
+ NGM_HCI_NODE_GET_PACKET_MASK,
+ &p->packet_mask, sizeof(p->packet_mask));
+ } break;
+
+ case SIOC_HCI_RAW_NODE_SET_PACKET_MASK: {
+ struct ng_btsocket_hci_raw_node_packet_mask *p =
+ (struct ng_btsocket_hci_raw_node_packet_mask *) data;
+
+ error = ng_btsocket_raw_send_ngmsg(path,
+ NGM_HCI_NODE_SET_PACKET_MASK,
+ &p->packet_mask, sizeof(p->packet_mask));
+
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ng_btsocket_hci_raw_control */
+
+/*
+ * Process getsockopt/setsockopt system calls
+ */
+
+int
+ng_btsocket_hci_raw_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ struct ng_btsocket_hci_raw_filter filter;
+ int error = 0, dir;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ if (sopt->sopt_level != SOL_HCI_RAW)
+ return (0);
+
+ switch (sopt->sopt_dir) {
+ case SOPT_GET:
+ switch (sopt->sopt_name) {
+ case SO_HCI_RAW_FILTER:
+ error = sooptcopyout(sopt, &pcb->filter,
+ sizeof(pcb->filter));
+ break;
+
+ case SO_HCI_RAW_DIRECTION:
+ dir = (pcb->flags & NG_BTSOCKET_HCI_RAW_DIRECTION)?1:0;
+ error = sooptcopyout(sopt, &dir, sizeof(dir));
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case SOPT_SET:
+ switch (sopt->sopt_name) {
+ case SO_HCI_RAW_FILTER:
+ error = sooptcopyin(sopt, &filter, sizeof(filter),
+ sizeof(filter));
+ if (error == 0)
+ bcopy(&filter, &pcb->filter,
+ sizeof(pcb->filter));
+ break;
+
+ case SO_HCI_RAW_DIRECTION:
+ error = sooptcopyin(sopt, &dir, sizeof(dir),
+ sizeof(dir));
+ if (error != 0)
+ break;
+
+ if (dir)
+ pcb->flags |= NG_BTSOCKET_HCI_RAW_DIRECTION;
+ else
+ pcb->flags &= ~NG_BTSOCKET_HCI_RAW_DIRECTION;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ng_btsocket_hci_raw_ctloutput */
+
+/*
+ * Detach raw HCI socket
+ */
+
+int
+ng_btsocket_hci_raw_detach(struct socket *so)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ so->so_pcb = NULL;
+ sotryfree(so);
+
+ mtx_lock(&ng_btsocket_hci_raw_sockets_mtx);
+ LIST_REMOVE(pcb, next);
+ mtx_unlock(&ng_btsocket_hci_raw_sockets_mtx);
+
+ bzero(pcb, sizeof(*pcb));
+ FREE(pcb, M_NETGRAPH_BTSOCKET_HCI_RAW);
+
+ return (0);
+} /* ng_btsocket_hci_raw_detach */
+
+/*
+ * Disconnect raw HCI socket
+ */
+
+int
+ng_btsocket_hci_raw_disconnect(struct socket *so)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ soisdisconnected(so);
+
+ return (0);
+} /* ng_btsocket_hci_raw_disconnect */
+
+/*
+ * Get socket peer's address
+ */
+
+int
+ng_btsocket_hci_raw_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+ return (EOPNOTSUPP);
+} /* ng_btsocket_hci_raw_peeraddr */
+
+/*
+ * Send data
+ */
+
+int
+ng_btsocket_hci_raw_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *sa, struct mbuf *control, struct thread *td)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ struct mbuf *nam = NULL;
+ int error = 0;
+
+ if (ng_btsocket_hci_raw_node == NULL) {
+ error = ENETDOWN;
+ goto drop;
+ }
+ if (pcb == NULL) {
+ error = EINVAL;
+ goto drop;
+ }
+ if (control != NULL) {
+ error = EINVAL;
+ goto drop;
+ }
+
+ if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t) ||
+ m->m_pkthdr.len > sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) {
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ if (sa == NULL) {
+ if (pcb->addr.hci_node[0] == 0) {
+ error = EDESTADDRREQ;
+ goto drop;
+ }
+
+ sa = (struct sockaddr *) &pcb->addr;
+ }
+
+ MGET(nam, M_WAITOK, MT_SONAME);
+ if (nam == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+
+ nam->m_len = sizeof(struct sockaddr_hci);
+ bcopy(sa,mtod(nam, struct sockaddr_hci *),sizeof(struct sockaddr_hci));
+
+ nam->m_next = m;
+ m = NULL;
+
+ return (ng_send_fn(ng_btsocket_hci_raw_node, NULL,
+ ng_btsocket_hci_raw_output, nam, 0));
+drop:
+ NG_FREE_M(control); /* NG_FREE_M checks for != NULL */
+ NG_FREE_M(nam);
+ NG_FREE_M(m);
+
+ return (error);
+} /* ng_btsocket_hci_raw_send */
+
+/*
+ * Get socket address
+ */
+
+int
+ng_btsocket_hci_raw_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+ ng_btsocket_hci_raw_pcb_p pcb = so2hci_raw_pcb(so);
+ struct sockaddr_hci sa;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_hci_raw_node == NULL)
+ return (EINVAL);
+
+ bzero(&sa, sizeof(sa));
+ sa.hci_len = sizeof(sa);
+ sa.hci_family = AF_BLUETOOTH;
+ strncpy(sa.hci_node, pcb->addr.hci_node, sizeof(sa.hci_node));
+
+ *nam = dup_sockaddr((struct sockaddr *) &sa, 0);
+
+ return ((*nam == NULL)? ENOMEM : 0);
+} /* ng_btsocket_hci_raw_sockaddr */
+
diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c
new file mode 100644
index 0000000..8360038
--- /dev/null
+++ b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c
@@ -0,0 +1,2717 @@
+/*
+ * ng_btsocket_l2cap.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_btsocket_l2cap.c,v 1.5 2002/10/26 03:34:37 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domain.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/filedesc.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <bitstring.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_btsocket.h"
+#include "ng_btsocket_l2cap.h"
+
+/* MALLOC define */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP, "netgraph_btsocks_l2cap",
+ "Netgraph Bluetooth L2CAP sockets");
+#else
+#define M_NETGRAPH_BTSOCKET_L2CAP M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Netgraph node methods */
+static ng_constructor_t ng_btsocket_l2cap_node_constructor;
+static ng_rcvmsg_t ng_btsocket_l2cap_node_rcvmsg;
+static ng_shutdown_t ng_btsocket_l2cap_node_shutdown;
+static ng_newhook_t ng_btsocket_l2cap_node_newhook;
+static ng_connect_t ng_btsocket_l2cap_node_connect;
+static ng_rcvdata_t ng_btsocket_l2cap_node_rcvdata;
+static ng_disconnect_t ng_btsocket_l2cap_node_disconnect;
+
+static void ng_btsocket_l2cap_input (void *, int);
+static void ng_btsocket_l2cap_rtclean (void *, int);
+
+/* Netgraph type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_BTSOCKET_L2CAP_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_btsocket_l2cap_node_constructor, /* constructor */
+ ng_btsocket_l2cap_node_rcvmsg, /* control message */
+ ng_btsocket_l2cap_node_shutdown, /* destructor */
+ ng_btsocket_l2cap_node_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_btsocket_l2cap_node_connect, /* connect hook */
+ ng_btsocket_l2cap_node_rcvdata, /* data */
+ ng_btsocket_l2cap_node_disconnect, /* disconnect hook */
+ NULL /* node command list */
+};
+
+/* Globals */
+extern int ifqmaxlen;
+static u_int32_t ng_btsocket_l2cap_debug_level;
+static u_int32_t ng_btsocket_l2cap_ioctl_timeout;
+static node_p ng_btsocket_l2cap_node;
+static struct ng_bt_itemq ng_btsocket_l2cap_queue;
+static struct mtx ng_btsocket_l2cap_queue_mtx;
+static struct task ng_btsocket_l2cap_queue_task;
+static LIST_HEAD(, ng_btsocket_l2cap_pcb) ng_btsocket_l2cap_sockets;
+static struct mtx ng_btsocket_l2cap_sockets_mtx;
+static u_int32_t ng_btsocket_l2cap_token;
+static struct mtx ng_btsocket_l2cap_token_mtx;
+static LIST_HEAD(, ng_btsocket_l2cap_rtentry) ng_btsocket_l2cap_rt;
+static struct mtx ng_btsocket_l2cap_rt_mtx;
+static struct task ng_btsocket_l2cap_rt_task;
+
+/* Sysctl tree */
+SYSCTL_DECL(_net_bluetooth_l2cap_sockets);
+SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, seq, CTLFLAG_RW,
+ 0, "Bluetooth SEQPACKET L2CAP sockets family");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, debug_level,
+ CTLFLAG_RW,
+ &ng_btsocket_l2cap_debug_level, NG_BTSOCKET_WARN_LEVEL,
+ "Bluetooth SEQPACKET L2CAP sockets debug level");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, ioctl_timeout,
+ CTLFLAG_RW,
+ &ng_btsocket_l2cap_ioctl_timeout, 5,
+ "Bluetooth SEQPACKET L2CAP sockets ioctl timeout");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_len,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_queue.len, 0,
+ "Bluetooth SEQPACKET L2CAP sockets input queue length");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_maxlen,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_queue.maxlen, 0,
+ "Bluetooth SEQPACKET L2CAP sockets input queue max. length");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_drops,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_queue.drops, 0,
+ "Bluetooth SEQPACKET L2CAP sockets input queue drops");
+
+/* Debug */
+#define NG_BTSOCKET_L2CAP_INFO \
+ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_INFO_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_WARN \
+ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_WARN_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_ERR \
+ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ERR_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_ALERT \
+ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ALERT_LEVEL) \
+ printf
+
+/*
+ * Netgraph message processing routines
+ */
+
+static int ng_btsocket_l2cap_process_l2ca_con_req_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+static int ng_btsocket_l2cap_process_l2ca_con_rsp_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+static int ng_btsocket_l2cap_process_l2ca_con_ind
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+
+static int ng_btsocket_l2cap_process_l2ca_cfg_req_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+static int ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+static int ng_btsocket_l2cap_process_l2ca_cfg_ind
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+
+static int ng_btsocket_l2cap_process_l2ca_discon_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+static int ng_btsocket_l2cap_process_l2ca_discon_ind
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+
+static int ng_btsocket_l2cap_process_l2ca_write_rsp
+ (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
+
+/*
+ * Send L2CA_xxx messages to the lower layer
+ */
+
+static int ng_btsocket_l2cap_send_l2ca_con_req
+ (ng_btsocket_l2cap_pcb_p);
+static int ng_btsocket_l2cap_send_l2ca_con_rsp_req
+ (u_int32_t, ng_btsocket_l2cap_rtentry_p, bdaddr_p, int, int, int);
+static int ng_btsocket_l2cap_send_l2ca_cfg_req
+ (ng_btsocket_l2cap_pcb_p);
+static int ng_btsocket_l2cap_send_l2ca_cfg_rsp
+ (ng_btsocket_l2cap_pcb_p);
+static int ng_btsocket_l2cap_send_l2ca_discon_req
+ (u_int32_t, ng_btsocket_l2cap_pcb_p);
+
+static int ng_btsocket_l2cap_send2
+ (ng_btsocket_l2cap_pcb_p);
+
+/*
+ * Timeout processing routines
+ */
+
+static void ng_btsocket_l2cap_timeout (ng_btsocket_l2cap_pcb_p);
+static void ng_btsocket_l2cap_untimeout (ng_btsocket_l2cap_pcb_p);
+static void ng_btsocket_l2cap_process_timeout (void *);
+
+/*
+ * Other stuff
+ */
+
+static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_addr(bdaddr_p, int);
+static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_token(u_int32_t);
+static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_cid (bdaddr_p, int);
+static void ng_btsocket_l2cap_get_token (u_int32_t *);
+static int ng_btsocket_l2cap_result2errno(int);
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph node interface
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+static int
+ng_btsocket_l2cap_node_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_btsocket_l2cap_node_constructor */
+
+/*
+ * Do local shutdown processing. Let old node go and create new fresh one.
+ */
+
+static int
+ng_btsocket_l2cap_node_shutdown(node_p node)
+{
+ int error = 0;
+
+ NG_NODE_UNREF(node);
+
+ /* Create new node */
+ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_l2cap_node = NULL;
+
+ return (error);
+ }
+
+ error = ng_name_node(ng_btsocket_l2cap_node,
+ NG_BTSOCKET_L2CAP_NODE_TYPE);
+ if (error != NULL) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_l2cap_node);
+ ng_btsocket_l2cap_node = NULL;
+
+ return (error);
+ }
+
+ return (0);
+} /* ng_btsocket_l2cap_node_shutdown */
+
+/*
+ * We allow any hook to be connected to the node.
+ */
+
+static int
+ng_btsocket_l2cap_node_newhook(node_p node, hook_p hook, char const *name)
+{
+ return (0);
+} /* ng_btsocket_l2cap_node_newhook */
+
+/*
+ * Just say "YEP, that's OK by me!"
+ */
+
+static int
+ng_btsocket_l2cap_node_connect(hook_p hook)
+{
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+ NG_HOOK_REF(hook); /* Keep extra reference to the hook */
+
+#if 0
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+ NG_HOOK_FORCE_QUEUE(hook);
+#endif
+
+ return (0);
+} /* ng_btsocket_l2cap_node_connect */
+
+/*
+ * Hook disconnection. Schedule route cleanup task
+ */
+
+static int
+ng_btsocket_l2cap_node_disconnect(hook_p hook)
+{
+ /*
+ * If hook has private information than we must have this hook in
+ * the routing table and must schedule cleaning for the routing table.
+ * Otherwise hook was connected but we never got "hook_info" message,
+ * so we have never added this hook to the routing table and it save
+ * to just delete it.
+ */
+
+ if (NG_HOOK_PRIVATE(hook) != NULL)
+ return (taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_l2cap_rt_task));
+
+ NG_HOOK_UNREF(hook); /* Remove extra reference */
+
+ return (0);
+} /* ng_btsocket_l2cap_node_disconnect */
+
+/*
+ * Process incoming messages
+ */
+
+static int
+ng_btsocket_l2cap_node_rcvmsg(node_p node, item_p item, hook_p hook)
+{
+ struct ng_mesg *msg = NGI_MSG(item); /* item still has message */
+ int error = 0;
+
+ if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) {
+ mtx_lock(&ng_btsocket_l2cap_queue_mtx);
+ if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Input queue is full (msg)\n", __func__);
+
+ NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ } else {
+ if (hook != NULL) {
+ NG_HOOK_REF(hook);
+ NGI_SET_HOOK(item, hook);
+ }
+
+ NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);
+ error = taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_l2cap_queue_task);
+ }
+ mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
+ } else {
+ NG_FREE_ITEM(item);
+ error = EINVAL;
+ }
+
+ return (error);
+} /* ng_btsocket_l2cap_node_rcvmsg */
+
+/*
+ * Receive data on a hook
+ */
+
+static int
+ng_btsocket_l2cap_node_rcvdata(hook_p hook, item_p item)
+{
+ int error = 0;
+
+ mtx_lock(&ng_btsocket_l2cap_queue_mtx);
+ if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Input queue is full (data)\n", __func__);
+
+ NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ } else {
+ NG_HOOK_REF(hook);
+ NGI_SET_HOOK(item, hook);
+
+ NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);
+ error = taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_l2cap_queue_task);
+ }
+ mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_node_rcvdata */
+
+/*
+ * Process L2CA_Connect respose. Socket layer must have initiated connection,
+ * so we have to have a socket associated with message token.
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_con_req_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_con_op *op = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+ int error = 0;
+
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_con_op *)(msg->data);
+
+ /* Look for the socket with the token */
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL)
+ return (ENOENT);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Connect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, status=%d, " \
+"state=%d\n", __func__, msg->header.token,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, op->lcid, op->result, op->status,
+ pcb->state);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ if (op->result == NG_L2CAP_PENDING) {
+ ng_btsocket_l2cap_timeout(pcb);
+ mtx_unlock(&pcb->pcb_mtx);
+ return (0);
+ }
+
+ if (op->result == NG_L2CAP_SUCCESS) {
+ /*
+ * Channel is now open, so update local channel ID and
+ * start configuration process. Source and destination
+ * addresses as well as route must be already set.
+ */
+
+ pcb->cid = op->lcid;
+
+ error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);
+ if (error != 0) {
+ /* Send disconnect request with "zero" token */
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ /* ... and close the socket */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ } else {
+ pcb->cfg_state = NG_BTSOCKET_L2CAP_CFG_IN_SENT;
+ pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;
+
+ ng_btsocket_l2cap_timeout(pcb);
+ }
+ } else {
+ /*
+ * We have failed to open connection, so convert result
+ * code to "errno" code and disconnect the socket. Channel
+ * already has been closed.
+ */
+
+ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_process_l2ca_con_req_rsp */
+
+/*
+ * Process L2CA_ConnectRsp response
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_con_rsp_op *op = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
+
+ /* Look for the socket with the token */
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL)
+ return (ENOENT);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_ConnectRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",
+ __func__, msg->header.token,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, op->result, pcb->state);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ /* Check the result and disconnect the socket on failure */
+ if (op->result != NG_L2CAP_SUCCESS) {
+ /* Close the socket - channel already closed */
+ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ } else {
+ /* Move to CONFIGURING state and wait for CONFIG_IND */
+ pcb->cfg_state = 0;
+ pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;
+ ng_btsocket_l2cap_timeout(pcb);
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (0);
+} /* ng_btsocket_process_l2ca_con_rsp_rsp */
+
+/*
+ * Process L2CA_Connect indicator. Find socket that listens on address
+ * and PSM. Find exact or closest match. Create new socket and initiate
+ * connection.
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_con_ind(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_con_ind_ip *ip = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL, *pcb1 = NULL;
+ int error = 0;
+ u_int32_t token = 0;
+ u_int16_t result = 0;
+
+ if (msg->header.arglen != sizeof(*ip))
+ return (EMSGSIZE);
+
+ ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Connect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, ident=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ ip->bdaddr.b[5], ip->bdaddr.b[4], ip->bdaddr.b[3],
+ ip->bdaddr.b[2], ip->bdaddr.b[1], ip->bdaddr.b[0],
+ ip->psm, ip->lcid, ip->ident);
+
+ pcb = ng_btsocket_l2cap_pcb_by_addr(&rt->src, ip->psm);
+ if (pcb != NULL) {
+ struct socket *so1 = NULL;
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ /*
+ * First check the pending connections queue and if we have
+ * space then create new socket and set proper source address.
+ */
+
+ if (pcb->so->so_qlen <= pcb->so->so_qlimit)
+ so1 = sonewconn(pcb->so, 0);
+
+ if (so1 == NULL) {
+ result = NG_L2CAP_NO_RESOURCES;
+ goto respond;
+ }
+
+ /*
+ * If we got here than we have created new socket. So complete
+ * connection. If we we listening on specific address then copy
+ * source address from listening socket, otherwise copy source
+ * address from hook's routing information.
+ */
+
+ pcb1 = so2l2cap_pcb(so1);
+ KASSERT((pcb1 != NULL),
+("%s: pcb1 == NULL\n", __func__));
+
+ mtx_lock(&pcb1->pcb_mtx);
+
+ if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0)
+ bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src));
+ else
+ bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src));
+
+ pcb1->flags &= ~NG_BTSOCKET_L2CAP_CLIENT;
+
+ bcopy(&ip->bdaddr, &pcb1->dst, sizeof(pcb1->dst));
+ pcb1->psm = ip->psm;
+ pcb1->cid = ip->lcid;
+ pcb1->rt = rt;
+
+ /* Copy socket settings */
+ pcb1->imtu = pcb->imtu;
+ bcopy(&pcb->oflow, &pcb1->oflow, sizeof(pcb1->oflow));
+ pcb1->flush_timo = pcb->flush_timo;
+
+ token = pcb1->token;
+ } else
+ /* Nobody listens on requested BDADDR/PSM */
+ result = NG_L2CAP_PSM_NOT_SUPPORTED;
+
+respond:
+ error = ng_btsocket_l2cap_send_l2ca_con_rsp_req(token, rt,
+ &ip->bdaddr, ip->ident, ip->lcid, result);
+ if (pcb1 != NULL) {
+ if (error != 0) {
+ pcb1->so->so_error = error;
+ pcb1->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb1->so);
+ } else {
+ pcb1->state = NG_BTSOCKET_L2CAP_CONNECTING;
+ soisconnecting(pcb1->so);
+
+ ng_btsocket_l2cap_timeout(pcb1);
+ }
+
+ mtx_unlock(&pcb1->pcb_mtx);
+ }
+
+ if (pcb != NULL)
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_process_l2ca_con_ind */
+
+/*
+ * Process L2CA_Config response
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_cfg_op *op = NULL;
+ ng_btsocket_l2cap_pcb_p pcb = NULL;
+
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
+
+ /*
+ * Socket must have issued a Configure request, so we must have a
+ * socket that wants to be configured. Use Netgraph message token
+ * to find it
+ */
+
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL) {
+ /*
+ * XXX FIXME what to do here? We could not find a
+ * socket with requested token. We even can not send
+ * Disconnect, because we do not know channel ID
+ */
+
+ return (ENOENT);
+ }
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Config response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \
+"cfg_state=%x\n",
+ __func__, msg->header.token,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ if (op->result == NG_L2CAP_SUCCESS) {
+ /*
+ * XXX FIXME Actually set flush and link timeout.
+ * Set QoS here if required. Resolve conficts (flush_timo).
+ * Save incoming MTU (peer's outgoing MTU) and outgoing flow
+ * spec.
+ */
+
+ pcb->imtu = op->imtu;
+ bcopy(&op->oflow, &pcb->oflow, sizeof(pcb->oflow));
+ pcb->flush_timo = op->flush_timo;
+
+ /*
+ * We have configured incoming side, so record it and check
+ * if configuration is complete. If complete then mark socket
+ * as connected, otherwise wait for the peer.
+ */
+
+ pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_IN_SENT;
+ pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN;
+
+ if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {
+ /* Configuration complete - mark socket as open */
+ ng_btsocket_l2cap_untimeout(pcb);
+ pcb->state = NG_BTSOCKET_L2CAP_OPEN;
+ soisconnected(pcb->so);
+ }
+ } else {
+ /*
+ * Something went wrong. Could be unacceptable parameters,
+ * reject or unknown option. That's too bad, but we will
+ * not negotiate. Send Disconnect and close the channel.
+ */
+
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ switch (op->result) {
+ case NG_L2CAP_UNACCEPTABLE_PARAMS:
+ case NG_L2CAP_UNKNOWN_OPTION:
+ pcb->so->so_error = EINVAL;
+ break;
+
+ default:
+ pcb->so->so_error = ECONNRESET;
+ break;
+ }
+
+ /* Send disconnect with "zero" token */
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ /* ... and close the socket */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_process_l2ca_cfg_req_rsp */
+
+/*
+ * Process L2CA_ConfigRsp response
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_cfg_rsp_op *op = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+ int error = 0;
+
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
+
+ /* Look for the socket with the token */
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL)
+ return (ENOENT);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_ConfigRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \
+"cfg_state=%x\n",
+ __func__, msg->header.token,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ /* Check the result and disconnect socket of failure */
+ if (op->result != NG_L2CAP_SUCCESS)
+ goto disconnect;
+
+ /*
+ * Now we done with remote side configuration. Configure local
+ * side if we have not done it yet.
+ */
+
+ pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_OUT_SENT;
+ pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT;
+
+ if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {
+ /* Configuration complete - mask socket as open */
+ ng_btsocket_l2cap_untimeout(pcb);
+ pcb->state = NG_BTSOCKET_L2CAP_OPEN;
+ soisconnected(pcb->so);
+ } else {
+ if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_IN_SENT)) {
+ /* Send L2CA_Config request - incoming path */
+ error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);
+ if (error != 0)
+ goto disconnect;
+
+ pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN_SENT;
+ }
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+
+disconnect:
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ /* Send disconnect with "zero" token */
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ /* ... and close the socket */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp */
+
+/*
+ * Process L2CA_Config indicator
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_cfg_ind(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_cfg_ind_ip *ip = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+ int error = 0;
+
+ if (msg->header.arglen != sizeof(*ip))
+ return (EMSGSIZE);
+
+ ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
+
+ /* Check for the open socket that has given channel ID */
+ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid);
+ if (pcb == NULL)
+ return (ENOENT);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Config indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d, cfg_state=%x\n",
+ __func__,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, pcb->state, pcb->cfg_state);
+
+ /* XXX FIXME re-configuration on open socket */
+ if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ /*
+ * XXX FIXME Actually set flush and link timeout. Set QoS here if
+ * required. Resolve conficts (flush_timo). Note outgoing MTU (peer's
+ * incoming MTU) and incoming flow spec.
+ */
+
+ pcb->omtu = ip->omtu;
+ bcopy(&ip->iflow, &pcb->iflow, sizeof(pcb->iflow));
+ pcb->flush_timo = ip->flush_timo;
+
+ /*
+ * Send L2CA_Config response to our peer and check for the errors,
+ * if any send disconnect to close the channel.
+ */
+
+ if (sbreserve(&pcb->so->so_snd, ip->omtu, pcb->so, curthread)) {
+ if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_OUT_SENT)) {
+ error = ng_btsocket_l2cap_send_l2ca_cfg_rsp(pcb);
+ if (error == 0)
+ pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT_SENT;
+ }
+ } else
+ error = ENOBUFS;
+
+ if (error != 0) {
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ pcb->so->so_error = error;
+
+ /* Send disconnect with "zero" token */
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ /* ... and close the socket */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_process_l2cap_cfg_ind */
+
+/*
+ * Process L2CA_Disconnect response
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_discon_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_discon_op *op = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_discon_op *)(msg->data);
+
+ /*
+ * Socket layer must have issued L2CA_Disconnect request, so there
+ * must be a socket that wants to be disconnected. Use Netgraph
+ * message token to find it.
+ */
+
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL)
+ return (0);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ /* XXX Close socket no matter what op->result says */
+ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Disconnect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",
+ __func__, msg->header.token,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, op->result, pcb->state);
+
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_process_l2ca_discon_rsp */
+
+/*
+ * Process L2CA_Disconnect indicator
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_discon_ind(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_discon_ind_ip *ip = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*ip))
+ return (EMSGSIZE);
+
+ ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
+
+ /* Look for the socket with given channel ID */
+ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid);
+ if (pcb == NULL)
+ return (0);
+
+ /*
+ * Channel has already been destroyed, so disconnect the socket
+ * and be done with it. If there was any pending request we can
+ * not do anything here anyway.
+ */
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Disconnect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d\n",
+ __func__,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, pcb->state);
+
+ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_process_l2ca_discon_ind */
+
+/*
+ * Process L2CA_Write response
+ */
+
+static int
+ng_btsocket_l2cap_process_l2ca_write_rsp(struct ng_mesg *msg,
+ ng_btsocket_l2cap_rtentry_p rt)
+{
+ ng_l2cap_l2ca_write_op *op = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+
+ /* Check message */
+ if (msg->header.arglen != sizeof(*op))
+ return (EMSGSIZE);
+
+ op = (ng_l2cap_l2ca_write_op *)(msg->data);
+
+ /* Look for the socket with given token */
+ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
+ if (pcb == NULL)
+ return (ENOENT);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CA_Write response, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, length=%d, " \
+"state=%d\n", __func__,
+ pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
+ pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
+ pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
+ pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
+ pcb->psm, pcb->cid, op->result, op->length,
+ pcb->state);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (ENOENT);
+ }
+
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ /*
+ * Check if we have more data to send
+ */
+
+ sbdroprecord(&pcb->so->so_snd);
+ if (pcb->so->so_snd.sb_cc > 0) {
+ if (ng_btsocket_l2cap_send2(pcb) == 0)
+ ng_btsocket_l2cap_timeout(pcb);
+ else
+ sbdroprecord(&pcb->so->so_snd); /* XXX */
+ }
+
+ /*
+ * Now set the result, drop packet from the socket send queue and
+ * ask for more (wakeup sender)
+ */
+
+ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
+ sowwakeup(pcb->so);
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_process_l2ca_write_rsp */
+
+/*
+ * Send L2CA_Connect request
+ */
+
+static int
+ng_btsocket_l2cap_send_l2ca_con_req(ng_btsocket_l2cap_pcb_p pcb)
+{
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_con_ip *ip = NULL;
+ int error = 0;
+
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->rt == NULL ||
+ pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
+ return (ENETDOWN);
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ msg->header.token = pcb->token;
+
+ ip = (ng_l2cap_l2ca_con_ip *)(msg->data);
+ bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr));
+ ip->psm = pcb->psm;
+
+ NG_SEND_MSG_HOOK(error,ng_btsocket_l2cap_node,msg,pcb->rt->hook,NULL);
+
+ return (error);
+} /* ng_btsocket_l2cap_send_l2ca_con_req */
+
+/*
+ * Send L2CA_Connect response
+ */
+
+static int
+ng_btsocket_l2cap_send_l2ca_con_rsp_req(u_int32_t token,
+ ng_btsocket_l2cap_rtentry_p rt, bdaddr_p dst, int ident,
+ int lcid, int result)
+{
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_con_rsp_ip *ip = NULL;
+ int error = 0;
+
+ if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
+ return (ENETDOWN);
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ msg->header.token = token;
+
+ ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
+ bcopy(dst, &ip->bdaddr, sizeof(ip->bdaddr));
+ ip->ident = ident;
+ ip->lcid = lcid;
+ ip->result = result;
+ ip->status = 0;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, rt->hook, NULL);
+
+ return (error);
+} /* ng_btsocket_l2cap_send_l2ca_con_rsp_req */
+
+/*
+ * Send L2CA_Config request
+ */
+
+static int
+ng_btsocket_l2cap_send_l2ca_cfg_req(ng_btsocket_l2cap_pcb_p pcb)
+{
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_cfg_ip *ip = NULL;
+ int error = 0;
+
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->rt == NULL ||
+ pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
+ return (ENETDOWN);
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ msg->header.token = pcb->token;
+
+ ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
+ ip->lcid = pcb->cid;
+ ip->imtu = pcb->imtu;
+ bcopy(&pcb->oflow, &ip->oflow, sizeof(ip->oflow));
+ ip->flush_timo = pcb->flush_timo;
+ ip->link_timo = pcb->link_timo;
+
+ NG_SEND_MSG_HOOK(error,ng_btsocket_l2cap_node,msg,pcb->rt->hook,NULL);
+
+ return (error);
+} /* ng_btsocket_l2cap_send_l2ca_cfg_req */
+
+/*
+ * Send L2CA_Config response
+ */
+
+static int
+ng_btsocket_l2cap_send_l2ca_cfg_rsp(ng_btsocket_l2cap_pcb_p pcb)
+{
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_cfg_rsp_ip *ip = NULL;
+ int error = 0;
+
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->rt == NULL ||
+ pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
+ return (ENETDOWN);
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ msg->header.token = pcb->token;
+
+ ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
+ ip->lcid = pcb->cid;
+ ip->omtu = pcb->omtu;
+ bcopy(&pcb->iflow, &ip->iflow, sizeof(ip->iflow));
+
+ NG_SEND_MSG_HOOK(error,ng_btsocket_l2cap_node,msg,pcb->rt->hook,NULL);
+
+ return (error);
+} /* ng_btsocket_l2cap_send_l2ca_cfg_rsp */
+
+/*
+ * Send L2CA_Disconnect request
+ */
+
+static int
+ng_btsocket_l2cap_send_l2ca_discon_req(u_int32_t token,
+ ng_btsocket_l2cap_pcb_p pcb)
+{
+ struct ng_mesg *msg = NULL;
+ ng_l2cap_l2ca_discon_ip *ip = NULL;
+ int error = 0;
+
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->rt == NULL ||
+ pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
+ return (ENETDOWN);
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
+ sizeof(*ip), M_NOWAIT);
+ if (msg == NULL)
+ return (ENOMEM);
+
+ msg->header.token = token;
+
+ ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
+ ip->lcid = pcb->cid;
+
+ NG_SEND_MSG_HOOK(error,ng_btsocket_l2cap_node,msg,pcb->rt->hook,NULL);
+
+ return (error);
+} /* ng_btsocket_l2cap_send_l2ca_discon_req */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Socket interface
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * L2CAP sockets data input routine
+ */
+
+static void
+ng_btsocket_l2cap_data_input(struct mbuf *m, hook_p hook)
+{
+ ng_l2cap_hdr_t *hdr = NULL;
+ ng_l2cap_clt_hdr_t *clt_hdr = NULL;
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+ ng_btsocket_l2cap_rtentry_t *rt = NULL;
+
+ if (hook == NULL) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Invalid source hook for L2CAP data packet\n", __func__);
+ goto drop;
+ }
+
+ rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);
+ if (rt == NULL) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not find out source bdaddr for L2CAP data packet\n", __func__);
+ goto drop;
+ }
+
+ /* Make sure we can access header */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: L2CAP data packet too small, len=%d\n", __func__, m->m_pkthdr.len);
+ goto drop;
+ }
+
+ if (m->m_len < sizeof(*hdr)) {
+ m = m_pullup(m, sizeof(*hdr));
+ if (m == NULL)
+ goto drop;
+ }
+
+ /* Strip L2CAP packet header and verify packet length */
+ hdr = mtod(m, ng_l2cap_hdr_t *);
+ m_adj(m, sizeof(*hdr));
+
+ if (hdr->length != m->m_pkthdr.len) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Bad L2CAP data packet length, len=%d, length=%d\n",
+ __func__, m->m_pkthdr.len, hdr->length);
+ goto drop;
+ }
+
+ /*
+ * Now process packet. Two cases:
+ *
+ * 1) Normal packet (cid != 2) then find connected socket and append
+ * mbuf to the socket queue. Wakeup socket.
+ *
+ * 2) Broadcast packet (cid == 2) then find all sockets that connected
+ * to the given PSM and have SO_BROADCAST bit set and append mbuf
+ * to the socket queue. Wakeup socket.
+ */
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Received L2CAP datat packet: src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dcid=%d, length=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->dcid, hdr->length);
+
+ if (NG_L2CAP_FIRST_CID <= hdr->dcid && hdr->dcid <= NG_L2CAP_LAST_CID) {
+ /* Normal packet: find connected socket */
+ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, hdr->dcid);
+ if (pcb == NULL)
+ goto drop;
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, " \
+"state=%d\n", __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->dcid, pcb->state);
+
+ mtx_unlock(&pcb->pcb_mtx);
+ goto drop;
+ }
+
+ /* Check packet size against socket's incoming MTU */
+ if (hdr->length > pcb->imtu) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: L2CAP data packet too big, src bdaddr=%x:%x:%x:%x:%x:%x, " \
+"dcid=%d, length=%d, imtu=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->dcid, hdr->length, pcb->imtu);
+
+ mtx_unlock(&pcb->pcb_mtx);
+ goto drop;
+ }
+
+ /* Check if we have enough space in socket receive queue */
+ if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
+
+ /*
+ * This is really bad. Receive queue on socket does
+ * not have enough space for the packet. We do not
+ * have any other choice but drop the packet. L2CAP
+ * does not provide any flow control.
+ */
+
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Not enough space in socket receive queue. Dropping L2CAP data packet, " \
+"src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, len=%d, space=%ld\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->dcid, m->m_pkthdr.len,
+ sbspace(&pcb->so->so_rcv));
+
+ mtx_unlock(&pcb->pcb_mtx);
+ goto drop;
+ }
+
+ /* Append packet to the socket receive queue and wakeup */
+ sbappendrecord(&pcb->so->so_rcv, m);
+ m = NULL;
+
+ sorwakeup(pcb->so);
+ mtx_unlock(&pcb->pcb_mtx);
+ } else if (hdr->dcid == NG_L2CAP_CLT_CID) {
+ /* Broadcast packet: give packet to all sockets */
+
+ /* Check packet size against connectionless MTU */
+ if (hdr->length > NG_L2CAP_MTU_DEFAULT) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Connectionless L2CAP data packet too big, " \
+"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->length);
+ goto drop;
+ }
+
+ /* Make sure we can access connectionless header */
+ if (m->m_pkthdr.len < sizeof(*clt_hdr)) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Can not get L2CAP connectionless packet header, " \
+"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ hdr->length);
+ goto drop;
+ }
+
+ if (m->m_len < sizeof(*clt_hdr)) {
+ m = m_pullup(m, sizeof(*clt_hdr));
+ if (m == NULL)
+ goto drop;
+ }
+
+ /* Strip connectionless header and deliver packet */
+ clt_hdr = mtod(m, ng_l2cap_clt_hdr_t *);
+ m_adj(m, sizeof(*clt_hdr));
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Got L2CAP connectionless data packet, " \
+"src bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, length=%d\n",
+ __func__,
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0],
+ clt_hdr->psm, hdr->length);
+
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) {
+ struct mbuf *copy = NULL;
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ if (bcmp(&rt->src, &pcb->src, sizeof(pcb->src)) != 0 ||
+ pcb->psm != clt_hdr->psm ||
+ pcb->state != NG_BTSOCKET_L2CAP_OPEN ||
+ (pcb->so->so_options & SO_BROADCAST) == 0 ||
+ m->m_pkthdr.len > sbspace(&pcb->so->so_rcv))
+ goto next;
+
+ /*
+ * Create a copy of the packet and append it to the
+ * socket's queue. If m_dup() failed - no big deal
+ * it is a broadcast traffic after all
+ */
+
+ copy = m_dup(m, M_NOWAIT);
+ if (copy != NULL) {
+ sbappendrecord(&pcb->so->so_rcv, copy);
+ sorwakeup(pcb->so);
+ }
+next:
+ mtx_unlock(&pcb->pcb_mtx);
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+ }
+drop:
+ NG_FREE_M(m); /* checks for m != NULL */
+} /* ng_btsocket_l2cap_data_input */
+
+/*
+ * L2CAP sockets default message input routine
+ */
+
+static void
+ng_btsocket_l2cap_default_msg_input(struct ng_mesg *msg, hook_p hook)
+{
+ switch (msg->header.cmd) {
+ case NGM_L2CAP_NODE_HOOK_INFO: {
+ ng_btsocket_l2cap_rtentry_t *rt = NULL;
+
+ if (hook == NULL || msg->header.arglen != sizeof(bdaddr_t))
+ break;
+
+ if (bcmp(msg->data, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
+ break;
+
+ rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);
+ if (rt == NULL) {
+ MALLOC(rt, ng_btsocket_l2cap_rtentry_p, sizeof(*rt),
+ M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT|M_ZERO);
+ if (rt == NULL)
+ break;
+
+ mtx_lock(&ng_btsocket_l2cap_rt_mtx);
+ LIST_INSERT_HEAD(&ng_btsocket_l2cap_rt, rt, next);
+ mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
+
+ NG_HOOK_SET_PRIVATE(hook, rt);
+ }
+
+ bcopy(msg->data, &rt->src, sizeof(rt->src));
+ rt->hook = hook;
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n",
+ __func__, NG_HOOK_NAME(hook),
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0]);
+ } break;
+
+ default:
+ NG_BTSOCKET_L2CAP_WARN(
+"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);
+ break;
+ }
+
+ NG_FREE_MSG(msg); /* Checks for msg != NULL */
+} /* ng_btsocket_l2cap_default_msg_input */
+
+/*
+ * L2CAP sockets L2CA message input routine
+ */
+
+static void
+ng_btsocket_l2cap_l2ca_msg_input(struct ng_mesg *msg, hook_p hook)
+{
+ ng_btsocket_l2cap_rtentry_p rt = NULL;
+
+ if (hook == NULL) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Invalid source hook for L2CA message\n", __func__);
+ goto drop;
+ }
+
+ rt = (ng_btsocket_l2cap_rtentry_p) NG_HOOK_PRIVATE(hook);
+ if (rt == NULL) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not find out source bdaddr for L2CA message\n", __func__);
+ goto drop;
+ }
+
+ switch (msg->header.cmd) {
+ case NGM_L2CAP_L2CA_CON: /* L2CA_Connect response */
+ ng_btsocket_l2cap_process_l2ca_con_req_rsp(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_CON_RSP: /* L2CA_ConnectRsp response */
+ ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_CON_IND: /* L2CA_Connect indicator */
+ ng_btsocket_l2cap_process_l2ca_con_ind(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_CFG: /* L2CA_Config response */
+ ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_CFG_RSP: /* L2CA_ConfigRsp response */
+ ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_CFG_IND: /* L2CA_Config indicator */
+ ng_btsocket_l2cap_process_l2ca_cfg_ind(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_DISCON: /* L2CA_Disconnect response */
+ ng_btsocket_l2cap_process_l2ca_discon_rsp(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_DISCON_IND: /* L2CA_Disconnect indicator */
+ ng_btsocket_l2cap_process_l2ca_discon_ind(msg, rt);
+ break;
+
+ case NGM_L2CAP_L2CA_WRITE: /* L2CA_Write response */
+ ng_btsocket_l2cap_process_l2ca_write_rsp(msg, rt);
+ break;
+
+ /* XXX FIXME add other L2CA messages */
+
+ default:
+ NG_BTSOCKET_L2CAP_WARN(
+"%s: Unknown L2CA message, cmd=%d\n", __func__, msg->header.cmd);
+ break;
+ }
+drop:
+ NG_FREE_MSG(msg);
+} /* ng_btsocket_l2cap_l2ca_msg_input */
+
+/*
+ * L2CAP sockets input routine
+ */
+
+static void
+ng_btsocket_l2cap_input(void *context, int pending)
+{
+ item_p item = NULL;
+ hook_p hook = NULL;
+
+ for (;;) {
+ mtx_lock(&ng_btsocket_l2cap_queue_mtx);
+ NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_queue, item);
+ mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
+
+ if (item == NULL)
+ break;
+
+ NGI_GET_HOOK(item, hook);
+ if (hook != NULL && NG_HOOK_NOT_VALID(hook))
+ goto drop;
+
+ switch(item->el_flags & NGQF_TYPE) {
+ case NGQF_DATA: {
+ struct mbuf *m = NULL;
+
+ NGI_GET_M(item, m);
+ ng_btsocket_l2cap_data_input(m, hook);
+ } break;
+
+ case NGQF_MESG: {
+ struct ng_mesg *msg = NULL;
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.cmd) {
+ case NGM_L2CAP_L2CA_CON:
+ case NGM_L2CAP_L2CA_CON_RSP:
+ case NGM_L2CAP_L2CA_CON_IND:
+ case NGM_L2CAP_L2CA_CFG:
+ case NGM_L2CAP_L2CA_CFG_RSP:
+ case NGM_L2CAP_L2CA_CFG_IND:
+ case NGM_L2CAP_L2CA_DISCON:
+ case NGM_L2CAP_L2CA_DISCON_IND:
+ case NGM_L2CAP_L2CA_WRITE:
+ /* XXX FIXME add other L2CA messages */
+ ng_btsocket_l2cap_l2ca_msg_input(msg, hook);
+ break;
+
+ default:
+ ng_btsocket_l2cap_default_msg_input(msg, hook);
+ break;
+ }
+ } break;
+
+ default:
+ KASSERT(0,
+("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
+ break;
+ }
+drop:
+ if (hook != NULL)
+ NG_HOOK_UNREF(hook);
+
+ NG_FREE_ITEM(item);
+ }
+} /* ng_btsocket_l2cap_input */
+
+/*
+ * Route cleanup task. Gets scheduled when hook is disconnected. Here we
+ * will find all sockets that use "invalid" hook and disconnect them.
+ */
+
+static void
+ng_btsocket_l2cap_rtclean(void *context, int pending)
+{
+ ng_btsocket_l2cap_pcb_p pcb = NULL;
+ ng_btsocket_l2cap_rtentry_p rt = NULL;
+
+ mtx_lock(&ng_btsocket_l2cap_rt_mtx);
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ /*
+ * First disconnect all sockets that use "invalid" hook
+ */
+
+ LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) {
+ mtx_lock(&pcb->pcb_mtx);
+
+ if (pcb->rt != NULL &&
+ pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {
+ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ pcb->so->so_error = ENETDOWN;
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+
+ pcb->token = 0;
+ pcb->rt = NULL;
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+ }
+
+ /*
+ * Now cleanup routing table
+ */
+
+ rt = LIST_FIRST(&ng_btsocket_l2cap_rt);
+ while (rt != NULL) {
+ ng_btsocket_l2cap_rtentry_p rt_next = LIST_NEXT(rt, next);
+
+ if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {
+ LIST_REMOVE(rt, next);
+
+ NG_HOOK_SET_PRIVATE(rt->hook, NULL);
+ NG_HOOK_UNREF(rt->hook); /* Remove extra reference */
+
+ bzero(rt, sizeof(*rt));
+ FREE(rt, M_NETGRAPH_BTSOCKET_L2CAP);
+ }
+
+ rt = rt_next;
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+ mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
+} /* ng_btsocket_l2cap_rtclean */
+
+/*
+ * Initialize everything
+ */
+
+void
+ng_btsocket_l2cap_init(void)
+{
+ int error = 0;
+
+ ng_btsocket_l2cap_node = NULL;
+ ng_btsocket_l2cap_debug_level = NG_BTSOCKET_WARN_LEVEL;
+ ng_btsocket_l2cap_ioctl_timeout = 5;
+
+ /* Register Netgraph node type */
+ error = ng_newtype(&typestruct);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not register Netgraph node type, error=%d\n", __func__, error);
+
+ return;
+ }
+
+ /* Create Netgrapg node */
+ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_l2cap_node = NULL;
+
+ return;
+ }
+
+ error = ng_name_node(ng_btsocket_l2cap_node,
+ NG_BTSOCKET_L2CAP_NODE_TYPE);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_l2cap_node);
+ ng_btsocket_l2cap_node = NULL;
+
+ return;
+ }
+
+ /* Create input queue */
+ NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_queue, ifqmaxlen);
+ mtx_init(&ng_btsocket_l2cap_queue_mtx,
+ "btsocks_l2cap_queue_mtx", NULL, MTX_DEF);
+ TASK_INIT(&ng_btsocket_l2cap_queue_task, 0,
+ ng_btsocket_l2cap_input, NULL);
+
+ /* Create list of sockets */
+ LIST_INIT(&ng_btsocket_l2cap_sockets);
+ mtx_init(&ng_btsocket_l2cap_sockets_mtx,
+ "btsocks_l2cap_sockets_mtx", NULL, MTX_DEF);
+
+ /* Tokens */
+ ng_btsocket_l2cap_token = 0;
+ mtx_init(&ng_btsocket_l2cap_token_mtx,
+ "btsocks_l2cap_token_mtx", NULL, MTX_DEF);
+
+ /* Routing table */
+ LIST_INIT(&ng_btsocket_l2cap_rt);
+ mtx_init(&ng_btsocket_l2cap_rt_mtx,
+ "btsocks_l2cap_rt_mtx", NULL, MTX_DEF);
+ TASK_INIT(&ng_btsocket_l2cap_rt_task, 0,
+ ng_btsocket_l2cap_rtclean, NULL);
+} /* ng_btsocket_l2cap_init */
+
+/*
+ * Abort connection on socket
+ */
+
+int
+ng_btsocket_l2cap_abort(struct socket *so)
+{
+ so->so_error = ECONNABORTED;
+
+ return (ng_btsocket_l2cap_detach(so));
+} /* ng_btsocket_l2cap_abort */
+
+/*
+ * Accept connection on socket. Nothing to do here, socket must be connected
+ * and ready, so just return peer address and be done with it.
+ */
+
+int
+ng_btsocket_l2cap_accept(struct socket *so, struct sockaddr **nam)
+{
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ return (ng_btsocket_l2cap_peeraddr(so, nam));
+} /* ng_btsocket_l2cap_accept */
+
+/*
+ * Create and attach new socket
+ */
+
+int
+ng_btsocket_l2cap_attach(struct socket *so, int proto, struct thread *td)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+ int error;
+
+ /* Check socket and protocol */
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EPROTONOSUPPORT);
+ if (so->so_type != SOCK_SEQPACKET)
+ return (ESOCKTNOSUPPORT);
+
+#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
+ if (proto != 0)
+ if (proto != BLUETOOTH_PROTO_L2CAP)
+ return (EPROTONOSUPPORT);
+#endif /* XXX */
+
+ if (pcb != NULL)
+ return (EISCONN);
+
+ /* Reserve send and receive space if it is not reserved yet */
+ if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
+ error = soreserve(so, NG_BTSOCKET_L2CAP_SENDSPACE,
+ NG_BTSOCKET_L2CAP_RECVSPACE);
+ if (error != 0)
+ return (error);
+ }
+
+ /* Allocate the PCB */
+ MALLOC(pcb, ng_btsocket_l2cap_pcb_p, sizeof(*pcb),
+ M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT | M_ZERO);
+ if (pcb == NULL)
+ return (ENOMEM);
+
+ /* Link the PCB and the socket */
+ so->so_pcb = (caddr_t) pcb;
+ pcb->so = so;
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+
+ /* Initialize PCB */
+ pcb->imtu = pcb->omtu = NG_L2CAP_MTU_DEFAULT;
+
+ /* Default flow */
+ pcb->iflow.flags = 0x0;
+ pcb->iflow.service_type = NG_HCI_SERVICE_TYPE_BEST_EFFORT;
+ pcb->iflow.token_rate = 0xffffffff; /* maximum */
+ pcb->iflow.token_bucket_size = 0xffffffff; /* maximum */
+ pcb->iflow.peak_bandwidth = 0x00000000; /* maximum */
+ pcb->iflow.latency = 0xffffffff; /* don't care */
+ pcb->iflow.delay_variation = 0xffffffff; /* don't care */
+
+ bcopy(&pcb->iflow, &pcb->oflow, sizeof(pcb->oflow));
+
+ pcb->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
+ pcb->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
+
+ ng_btsocket_l2cap_get_token(&pcb->token);
+
+ callout_handle_init(&pcb->timo);
+ mtx_init(&pcb->pcb_mtx, "btsocket_pcb_mtx", NULL, MTX_DEF);
+
+ /* Add the PCB to the list */
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+ LIST_INSERT_HEAD(&ng_btsocket_l2cap_sockets, pcb, next);
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_attach */
+
+/*
+ * Bind socket
+ */
+
+int
+ng_btsocket_l2cap_bind(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_l2cap_pcb_t *pcb = NULL;
+ struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam;
+ int psm, error = 0;
+
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ /* Verify address */
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->l2cap_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->l2cap_len != sizeof(*sa))
+ return (EINVAL);
+
+ psm = le16toh(sa->l2cap_psm);
+
+ /*
+ * Check if other socket has this address already (look for exact
+ * match PSM and bdaddr) and assign socket address if it's available.
+ *
+ * Note: socket can be bound to ANY PSM (zero) thus allowing several
+ * channels with the same PSM between the same pair of BD_ADDR'es.
+ */
+
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next)
+ if (psm != 0 && psm == pcb->psm &&
+ bcmp(&pcb->src, &sa->l2cap_bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ if (pcb == NULL) {
+ /* Set socket address */
+ pcb = so2l2cap_pcb(so);
+ if (pcb != NULL) {
+ bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src));
+ pcb->psm = psm;
+ } else
+ error = EINVAL;
+ } else
+ error = EADDRINUSE;
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_bind */
+
+/*
+ * Connect socket
+ */
+
+int
+ng_btsocket_l2cap_connect(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so);
+ struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam;
+ ng_btsocket_l2cap_rtentry_t *rt = NULL;
+ int have_src, error = 0;
+
+ /* Check socket */
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+ if (pcb->state == NG_BTSOCKET_L2CAP_CONNECTING)
+ return (EINPROGRESS);
+
+ /* Verify address */
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->l2cap_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->l2cap_len != sizeof(*sa))
+ return (EINVAL);
+ if (sa->l2cap_psm == 0 ||
+ bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
+ return (EDESTADDRREQ);
+ if (pcb->psm != 0 && pcb->psm != le16toh(sa->l2cap_psm))
+ return (EINVAL);
+
+ /* Send destination address and PSM */
+ bcopy(&sa->l2cap_bdaddr, &pcb->dst, sizeof(pcb->dst));
+ pcb->psm = le16toh(sa->l2cap_psm);
+
+ /*
+ * Routing. Socket should be bound to some source address. The source
+ * address can be ANY. Destination address must be set and it must not
+ * be ANY. If source address is ANY then find first rtentry that has
+ * src != dst.
+ */
+
+ pcb->rt = NULL;
+ have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src));
+
+ mtx_lock(&ng_btsocket_l2cap_rt_mtx);
+
+ LIST_FOREACH(rt, &ng_btsocket_l2cap_rt, next) {
+ if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
+ continue;
+
+ /* Match src and dst */
+ if (have_src) {
+ if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)
+ break;
+ } else {
+ if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)
+ break;
+ }
+ }
+
+ if (rt != NULL) {
+ pcb->rt = rt;
+
+ if (!have_src)
+ bcopy(&rt->src, &pcb->src, sizeof(pcb->src));
+ } else
+ error = EHOSTUNREACH;
+
+ mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
+
+ /*
+ * Send L2CA_Connect request
+ */
+
+ if (error == 0) {
+ mtx_lock(&pcb->pcb_mtx);
+ error = ng_btsocket_l2cap_send_l2ca_con_req(pcb);
+ if (error == 0) {
+ pcb->flags |= NG_BTSOCKET_L2CAP_CLIENT;
+ pcb->state = NG_BTSOCKET_L2CAP_CONNECTING;
+ soisconnecting(pcb->so);
+
+ ng_btsocket_l2cap_timeout(pcb);
+ }
+ mtx_unlock(&pcb->pcb_mtx);
+ }
+
+ return (error);
+} /* ng_btsocket_l2cap_connect */
+
+/*
+ * Process ioctl's calls on socket
+ */
+
+int
+ng_btsocket_l2cap_control(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp, struct thread *td)
+{
+ return (EINVAL);
+} /* ng_btsocket_l2cap_control */
+
+/*
+ * Process getsockopt/setsockopt system calls
+ */
+
+int
+ng_btsocket_l2cap_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+ int error = 0;
+ ng_l2cap_cfg_opt_val_t v;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ if (sopt->sopt_level != SOL_L2CAP)
+ return (0);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ switch (sopt->sopt_dir) {
+ case SOPT_GET:
+ switch (sopt->sopt_name) {
+ case SO_L2CAP_IMTU: /* get incoming MTU */
+ error = sooptcopyout(sopt, &pcb->imtu,
+ sizeof(pcb->imtu));
+ break;
+
+ case SO_L2CAP_OMTU: /* get outgoing (peer incoming) MTU */
+ error = sooptcopyout(sopt, &pcb->omtu,
+ sizeof(pcb->omtu));
+ break;
+
+ case SO_L2CAP_IFLOW: /* get incoming flow spec. */
+ error = sooptcopyout(sopt, &pcb->iflow,
+ sizeof(pcb->iflow));
+ break;
+
+ case SO_L2CAP_OFLOW: /* get outgoing flow spec. */
+ error = sooptcopyout(sopt, &pcb->oflow,
+ sizeof(pcb->oflow));
+ break;
+
+ case SO_L2CAP_FLUSH: /* get flush timeout */
+ error = sooptcopyout(sopt, &pcb->flush_timo,
+ sizeof(pcb->flush_timo));
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ case SOPT_SET:
+ /*
+ * We do not allow to change these parameters while
+ * socket is connected or we are in the process of
+ * creating a connection
+ *
+ * XXX may be this should indicate re-configuration of the
+ * open channel?
+ */
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED)
+ return (EACCES);
+
+ switch (sopt->sopt_name) {
+ case SO_L2CAP_IMTU: /* set incoming MTU */
+ error = sooptcopyin(sopt, &v, sizeof(v), sizeof(v.mtu));
+ if (error == 0)
+ pcb->imtu = v.mtu;
+ break;
+
+ case SO_L2CAP_OFLOW: /* set outgoing flow spec. */
+ error = sooptcopyin(sopt, &v, sizeof(v),sizeof(v.flow));
+ if (error == 0)
+ bcopy(&v.flow, &pcb->oflow, sizeof(pcb->oflow));
+ break;
+
+ case SO_L2CAP_FLUSH: /* set flush timeout */
+ error = sooptcopyin(sopt, &v, sizeof(v),
+ sizeof(v.flush_timo));
+ if (error == 0)
+ pcb->flush_timo = v.flush_timo;
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_ctloutput */
+
+/*
+ * Detach and destroy socket
+ */
+
+int
+ng_btsocket_l2cap_detach(struct socket *so)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ /* XXX what to do with pending request? */
+ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED &&
+ pcb->state != NG_BTSOCKET_L2CAP_DISCONNECTING)
+ /* Send disconnect request with "zero" token */
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(so);
+
+ so->so_pcb = NULL;
+ sotryfree(so);
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+ LIST_REMOVE(pcb, next);
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+
+ mtx_destroy(&pcb->pcb_mtx);
+ bzero(pcb, sizeof(*pcb));
+ FREE(pcb, M_NETGRAPH_BTSOCKET_L2CAP);
+
+ return (0);
+} /* ng_btsocket_l2cap_detach */
+
+/*
+ * Disconnect socket
+ */
+
+int
+ng_btsocket_l2cap_disconnect(struct socket *so)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+ int error = 0;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ if (pcb->state == NG_BTSOCKET_L2CAP_DISCONNECTING) {
+ mtx_unlock(&pcb->pcb_mtx);
+ return (EINPROGRESS);
+ }
+
+ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {
+ /* XXX FIXME what to do with pending request? */
+ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
+ ng_btsocket_l2cap_untimeout(pcb);
+
+ error = ng_btsocket_l2cap_send_l2ca_discon_req(pcb->token, pcb);
+ if (error == 0) {
+ pcb->state = NG_BTSOCKET_L2CAP_DISCONNECTING;
+ soisdisconnecting(so);
+
+ ng_btsocket_l2cap_timeout(pcb);
+ }
+
+ /* XXX FIXME what to do if error != 0 */
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+
+ return (error);
+} /* ng_btsocket_l2cap_disconnect */
+
+/*
+ * Listen on socket
+ */
+
+int
+ng_btsocket_l2cap_listen(struct socket *so, struct thread *td)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ return ((pcb->psm == 0)? EDESTADDRREQ : 0);
+} /* ng_btsocket_listen */
+
+/*
+ * Get peer address
+ */
+
+int
+ng_btsocket_l2cap_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+ struct sockaddr_l2cap sa;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ bcopy(&pcb->dst, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr));
+ sa.l2cap_psm = htole16(pcb->psm);
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+
+ *nam = dup_sockaddr((struct sockaddr *) &sa, 0);
+
+ return ((*nam == NULL)? ENOMEM : 0);
+} /* ng_btsocket_l2cap_peeraddr */
+
+/*
+ * Send data to socket
+ */
+
+int
+ng_btsocket_l2cap_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *nam, struct mbuf *control, struct thread *td)
+{
+ ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so);
+ int error = 0;
+
+ if (ng_btsocket_l2cap_node == NULL) {
+ error = ENETDOWN;
+ goto drop;
+ }
+
+ /* Check socket and input */
+ if (pcb == NULL || m == NULL || control != NULL) {
+ error = EINVAL;
+ goto drop;
+ }
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ /* Make sure socket is connected */
+ if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
+ mtx_unlock(&pcb->pcb_mtx);
+ error = ENOTCONN;
+ goto drop;
+ }
+
+ /* Check route */
+ if (pcb->rt == NULL ||
+ pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) {
+ mtx_unlock(&pcb->pcb_mtx);
+ error = ENETDOWN;
+ goto drop;
+ }
+
+ /* Check packet size agains outgoing (peer's incoming) MTU) */
+ if (m->m_pkthdr.len > pcb->omtu) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Packet too big, len=%d, omtu=%d\n", __func__, m->m_pkthdr.len, pcb->omtu);
+
+ mtx_unlock(&pcb->pcb_mtx);
+ error = EMSGSIZE;
+ goto drop;
+ }
+
+ /*
+ * First put packet on socket send queue. Then check if we have
+ * pending timeout. If we do not have timeout then we must send
+ * packet and schedule timeout. Otherwise do nothing and wait for
+ * L2CA_WRITE_RSP.
+ */
+
+ sbappendrecord(&pcb->so->so_snd, m);
+ m = NULL;
+
+ if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {
+ error = ng_btsocket_l2cap_send2(pcb);
+ if (error == 0)
+ ng_btsocket_l2cap_timeout(pcb);
+ else
+ sbdroprecord(&pcb->so->so_snd); /* XXX */
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+drop:
+ NG_FREE_M(m); /* checks for != NULL */
+ NG_FREE_M(control);
+
+ return (error);
+} /* ng_btsocket_l2cap_send */
+
+/*
+ * Send first packet in the socket queue to the L2CAP layer
+ */
+
+static int
+ng_btsocket_l2cap_send2(ng_btsocket_l2cap_pcb_p pcb)
+{
+ struct mbuf *m = NULL;
+ ng_l2cap_l2ca_hdr_t *hdr = NULL;
+ int error = 0;
+
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->so->so_snd.sb_cc == 0)
+ return (EINVAL); /* XXX */
+
+ m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT);
+ if (m == NULL)
+ return (ENOBUFS);
+
+ /* Create L2CA packet header */
+ M_PREPEND(m, sizeof(*hdr), M_NOWAIT);
+ if (m != NULL)
+ if (m->m_len < sizeof(*hdr))
+ m = m_pullup(m, sizeof(*hdr));
+
+ if (m == NULL) {
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Failed to create L2CA packet header\n", __func__);
+
+ return (ENOBUFS);
+ }
+
+ hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
+ hdr->token = pcb->token;
+ hdr->length = m->m_pkthdr.len - sizeof(*hdr);
+ hdr->lcid = pcb->cid;
+
+ NG_BTSOCKET_L2CAP_INFO(
+"%s: Sending packet: len=%d, length=%d, lcid=%d, token=%d, state=%d\n",
+ __func__, m->m_pkthdr.len, hdr->length, hdr->lcid,
+ hdr->token, pcb->state);
+
+ /*
+ * If we got here than we have successfuly creates new L2CAP
+ * data packet and now we can send it to the L2CAP layer
+ */
+
+ NG_SEND_DATA_ONLY(error, pcb->rt->hook, m);
+
+ return (error);
+} /* ng_btsocket_l2cap_send2 */
+
+/*
+ * Get socket address
+ */
+
+int
+ng_btsocket_l2cap_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+ ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);
+ struct sockaddr_l2cap sa;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_node == NULL)
+ return (EINVAL);
+
+ bcopy(&pcb->src, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr));
+ sa.l2cap_psm = htole16(pcb->psm);
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+
+ *nam = dup_sockaddr((struct sockaddr *) &sa, 0);
+
+ return ((*nam == NULL)? ENOMEM : 0);
+} /* ng_btsocket_l2cap_sockaddr */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Misc. functions
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Look for the socket that listens on given PSM and bdaddr. Returns exact or
+ * close match (if any).
+ */
+
+static ng_btsocket_l2cap_pcb_p
+ng_btsocket_l2cap_pcb_by_addr(bdaddr_p bdaddr, int psm)
+{
+ ng_btsocket_l2cap_pcb_p p = NULL, p1 = NULL;
+
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next) {
+ if (p->so == NULL || !(p->so->so_options & SO_ACCEPTCONN) ||
+ p->psm != psm)
+ continue;
+
+ if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0)
+ break;
+
+ if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0)
+ p1 = p;
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+
+ return ((p != NULL)? p : p1);
+} /* ng_btsocket_l2cap_pcb_by_addr */
+
+/*
+ * Look for the socket that has given token
+ */
+
+static ng_btsocket_l2cap_pcb_p
+ng_btsocket_l2cap_pcb_by_token(u_int32_t token)
+{
+ ng_btsocket_l2cap_pcb_p p = NULL;
+
+ if (token != 0) {
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next)
+ if (p->token == token)
+ break;
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+ }
+
+ return (p);
+} /* ng_btsocket_l2cap_pcb_by_token */
+
+/*
+ * Look for the socket that assigned to given source address and channel ID
+ */
+
+static ng_btsocket_l2cap_pcb_p
+ng_btsocket_l2cap_pcb_by_cid(bdaddr_p src, int cid)
+{
+ ng_btsocket_l2cap_pcb_p p = NULL;
+
+ mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
+
+ LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next)
+ if (p->cid == cid && bcmp(src, &p->src, sizeof(p->src)) == 0)
+ break;
+
+ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
+
+ return (p);
+} /* ng_btsocket_l2cap_pcb_by_cid */
+
+/*
+ * Set timeout on socket
+ */
+
+static void
+ng_btsocket_l2cap_timeout(ng_btsocket_l2cap_pcb_p pcb)
+{
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {
+ pcb->flags |= NG_BTSOCKET_L2CAP_TIMO;
+ pcb->timo = timeout(ng_btsocket_l2cap_process_timeout, pcb,
+ bluetooth_l2cap_ertx_timeout());
+ } else
+ KASSERT(0,
+("%s: Duplicated socket timeout?!\n", __func__));
+} /* ng_btsocket_l2cap_timeout */
+
+/*
+ * Unset timeout on socket
+ */
+
+static void
+ng_btsocket_l2cap_untimeout(ng_btsocket_l2cap_pcb_p pcb)
+{
+ mtx_assert(&pcb->pcb_mtx, MA_OWNED);
+
+ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) {
+ untimeout(ng_btsocket_l2cap_process_timeout, pcb, pcb->timo);
+ pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;
+ } else
+ KASSERT(0,
+("%s: No socket timeout?!\n", __func__));
+} /* ng_btsocket_l2cap_untimeout */
+
+/*
+ * Process timeout on socket
+ */
+
+static void
+ng_btsocket_l2cap_process_timeout(void *xpcb)
+{
+ ng_btsocket_l2cap_pcb_p pcb = (ng_btsocket_l2cap_pcb_p) xpcb;
+
+ mtx_lock(&pcb->pcb_mtx);
+
+ pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;
+ pcb->so->so_error = ETIMEDOUT;
+
+ switch (pcb->state) {
+ case NG_BTSOCKET_L2CAP_CONNECTING:
+ case NG_BTSOCKET_L2CAP_CONFIGURING:
+ /* Send disconnect request with "zero" token */
+ if (pcb->cid != 0)
+ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
+
+ /* ... and close the socket */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ break;
+
+ case NG_BTSOCKET_L2CAP_OPEN:
+ /* Send timeout - drop packet and wakeup sender */
+ sbdroprecord(&pcb->so->so_snd);
+ sowwakeup(pcb->so);
+ break;
+
+ case NG_BTSOCKET_L2CAP_DISCONNECTING:
+ /* Disconnect timeout - disconnect the socket anyway */
+ pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+ soisdisconnected(pcb->so);
+ break;
+
+ default:
+ NG_BTSOCKET_L2CAP_ERR(
+"%s: Invalid socket state=%d\n", __func__, pcb->state);
+ break;
+ }
+
+ mtx_unlock(&pcb->pcb_mtx);
+} /* ng_btsocket_l2cap_process_timeout */
+
+/*
+ * Get next token
+ */
+
+static void
+ng_btsocket_l2cap_get_token(u_int32_t *token)
+{
+ mtx_lock(&ng_btsocket_l2cap_token_mtx);
+
+ if (++ ng_btsocket_l2cap_token == 0)
+ ng_btsocket_l2cap_token = 1;
+
+ *token = ng_btsocket_l2cap_token;
+
+ mtx_unlock(&ng_btsocket_l2cap_token_mtx);
+} /* ng_btsocket_l2cap_get_token */
+
+/*
+ * Translate HCI/L2CAP error code into "errno" code
+ * XXX Note: Some L2CAP and HCI error codes have the same value, but
+ * different meaning
+ */
+
+static int
+ng_btsocket_l2cap_result2errno(int result)
+{
+ switch (result) {
+ case 0x00: /* No error */
+ return (0);
+
+ case 0x01: /* Unknown HCI command */
+ return (ENODEV);
+
+ case 0x02: /* No connection */
+ return (ENOTCONN);
+
+ case 0x03: /* Hardware failure */
+ return (EIO);
+
+ case 0x04: /* Page timeout */
+ return (EHOSTDOWN);
+
+ case 0x05: /* Authentication failure */
+ case 0x06: /* Key missing */
+ case 0x18: /* Pairing not allowed */
+ case 0x21: /* Role change not allowed */
+ case 0x24: /* LMP PSU not allowed */
+ case 0x25: /* Encryption mode not acceptable */
+ case 0x26: /* Unit key used */
+ return (EACCES);
+
+ case 0x07: /* Memory full */
+ return (ENOMEM);
+
+ case 0x08: /* Connection timeout */
+ case 0x10: /* Host timeout */
+ case 0x22: /* LMP response timeout */
+ case 0xee: /* HCI timeout */
+ case 0xeeee: /* L2CAP timeout */
+ return (ETIMEDOUT);
+
+ case 0x09: /* Max number of connections */
+ case 0x0a: /* Max number of SCO connections to a unit */
+ return (EMLINK);
+
+ case 0x0b: /* ACL connection already exists */
+ return (EEXIST);
+
+ case 0x0c: /* Command disallowed */
+ return (EBUSY);
+
+ case 0x0d: /* Host rejected due to limited resources */
+ case 0x0e: /* Host rejected due to securiity reasons */
+ case 0x0f: /* Host rejected due to remote unit is a personal unit */
+ case 0x1b: /* SCO offset rejected */
+ case 0x1c: /* SCO interval rejected */
+ case 0x1d: /* SCO air mode rejected */
+ return (ECONNREFUSED);
+
+ case 0x11: /* Unsupported feature or parameter value */
+ case 0x19: /* Unknown LMP PDU */
+ case 0x1a: /* Unsupported remote feature */
+ case 0x20: /* Unsupported LMP parameter value */
+ case 0x27: /* QoS is not supported */
+ case 0x29: /* Paring with unit key not supported */
+ return (EOPNOTSUPP);
+
+ case 0x12: /* Invalid HCI command parameter */
+ case 0x1e: /* Invalid LMP parameters */
+ return (EINVAL);
+
+ case 0x13: /* Other end terminated connection: User ended connection */
+ case 0x14: /* Other end terminated connection: Low resources */
+ case 0x15: /* Other end terminated connection: About to power off */
+ return (ECONNRESET);
+
+ case 0x16: /* Connection terminated by local host */
+ return (ECONNABORTED);
+
+#if 0 /* XXX not yet */
+ case 0x17: /* Repeated attempts */
+ case 0x1f: /* Unspecified error */
+ case 0x23: /* LMP error transaction collision */
+ case 0x28: /* Instant passed */
+#endif
+ }
+
+ return (ENOSYS);
+} /* ng_btsocket_l2cap_result2errno */
+
diff --git a/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c
new file mode 100644
index 0000000..3b24afd
--- /dev/null
+++ b/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c
@@ -0,0 +1,1222 @@
+/*
+ * ng_btsocket_l2cap_raw.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_btsocket_l2cap_raw.c,v 1.1 2002/09/04 21:44:00 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domain.h>
+#include <sys/errno.h>
+#include <sys/filedesc.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <bitstring.h>
+#include "ng_bluetooth.h"
+#include "ng_hci.h"
+#include "ng_l2cap.h"
+#include "ng_btsocket.h"
+#include "ng_btsocket_l2cap.h"
+
+/* MALLOC define */
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP_RAW, "netgraph_btsocks_l2cap_raw",
+ "Netgraph Bluetooth raw L2CAP sockets");
+#else
+#define M_NETGRAPH_BTSOCKET_L2CAP_RAW M_NETGRAPH
+#endif /* NG_SEPARATE_MALLOC */
+
+/* Netgraph node methods */
+static ng_constructor_t ng_btsocket_l2cap_raw_node_constructor;
+static ng_rcvmsg_t ng_btsocket_l2cap_raw_node_rcvmsg;
+static ng_shutdown_t ng_btsocket_l2cap_raw_node_shutdown;
+static ng_newhook_t ng_btsocket_l2cap_raw_node_newhook;
+static ng_connect_t ng_btsocket_l2cap_raw_node_connect;
+static ng_rcvdata_t ng_btsocket_l2cap_raw_node_rcvdata;
+static ng_disconnect_t ng_btsocket_l2cap_raw_node_disconnect;
+
+static void ng_btsocket_l2cap_raw_input (void *, int);
+static void ng_btsocket_l2cap_raw_rtclean (void *, int);
+static void ng_btsocket_l2cap_raw_get_token (u_int32_t *);
+
+/* Netgraph type descriptor */
+static struct ng_type typestruct = {
+ NG_ABI_VERSION,
+ NG_BTSOCKET_L2CAP_RAW_NODE_TYPE, /* typename */
+ NULL, /* modevent */
+ ng_btsocket_l2cap_raw_node_constructor, /* constructor */
+ ng_btsocket_l2cap_raw_node_rcvmsg, /* control message */
+ ng_btsocket_l2cap_raw_node_shutdown, /* destructor */
+ ng_btsocket_l2cap_raw_node_newhook, /* new hook */
+ NULL, /* find hook */
+ ng_btsocket_l2cap_raw_node_connect, /* connect hook */
+ ng_btsocket_l2cap_raw_node_rcvdata, /* data */
+ ng_btsocket_l2cap_raw_node_disconnect, /* disconnect hook */
+ NULL /* node command list */
+};
+
+/* Globals */
+extern int ifqmaxlen;
+static u_int32_t ng_btsocket_l2cap_raw_debug_level;
+static u_int32_t ng_btsocket_l2cap_raw_ioctl_timeout;
+static node_p ng_btsocket_l2cap_raw_node;
+static struct ng_bt_itemq ng_btsocket_l2cap_raw_queue;
+static struct mtx ng_btsocket_l2cap_raw_queue_mtx;
+static struct task ng_btsocket_l2cap_raw_queue_task;
+static LIST_HEAD(, ng_btsocket_l2cap_raw_pcb) ng_btsocket_l2cap_raw_sockets;
+static struct mtx ng_btsocket_l2cap_raw_sockets_mtx;
+static u_int32_t ng_btsocket_l2cap_raw_token;
+static struct mtx ng_btsocket_l2cap_raw_token_mtx;
+static LIST_HEAD(, ng_btsocket_l2cap_rtentry) ng_btsocket_l2cap_raw_rt;
+static struct mtx ng_btsocket_l2cap_raw_rt_mtx;
+static struct task ng_btsocket_l2cap_raw_rt_task;
+
+/* Sysctl tree */
+SYSCTL_DECL(_net_bluetooth_l2cap_sockets);
+SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, raw, CTLFLAG_RW,
+ 0, "Bluetooth raw L2CAP sockets family");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, debug_level,
+ CTLFLAG_RW,
+ &ng_btsocket_l2cap_raw_debug_level, NG_BTSOCKET_WARN_LEVEL,
+ "Bluetooth raw L2CAP sockets debug level");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, ioctl_timeout,
+ CTLFLAG_RW,
+ &ng_btsocket_l2cap_raw_ioctl_timeout, 5,
+ "Bluetooth raw L2CAP sockets ioctl timeout");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_len,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_raw_queue.len, 0,
+ "Bluetooth raw L2CAP sockets input queue length");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_maxlen,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_raw_queue.maxlen, 0,
+ "Bluetooth raw L2CAP sockets input queue max. length");
+SYSCTL_INT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_drops,
+ CTLFLAG_RD,
+ &ng_btsocket_l2cap_raw_queue.drops, 0,
+ "Bluetooth raw L2CAP sockets input queue drops");
+
+/* Debug */
+#define NG_BTSOCKET_L2CAP_RAW_INFO \
+ if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_INFO_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_RAW_WARN \
+ if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_WARN_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_RAW_ERR \
+ if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_ERR_LEVEL) \
+ printf
+
+#define NG_BTSOCKET_L2CAP_RAW_ALERT \
+ if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_ALERT_LEVEL) \
+ printf
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Netgraph node interface
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_btsocket_l2cap_raw_node_constructor */
+
+/*
+ * Do local shutdown processing. Let old node go and create new fresh one.
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_shutdown(node_p node)
+{
+ int error = 0;
+
+ NG_NODE_UNREF(node);
+
+ /* Create new node */
+ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_raw_node);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_RAW_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_l2cap_raw_node = NULL;
+
+ return (error);
+ }
+
+ error = ng_name_node(ng_btsocket_l2cap_raw_node,
+ NG_BTSOCKET_L2CAP_RAW_NODE_TYPE);
+ if (error != NULL) {
+ NG_BTSOCKET_L2CAP_RAW_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_l2cap_raw_node);
+ ng_btsocket_l2cap_raw_node = NULL;
+
+ return (error);
+ }
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_node_shutdown */
+
+/*
+ * We allow any hook to be connected to the node.
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_newhook(node_p node, hook_p hook, char const *name)
+{
+ return (0);
+} /* ng_btsocket_l2cap_raw_node_newhook */
+
+/*
+ * Just say "YEP, that's OK by me!"
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_connect(hook_p hook)
+{
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+ NG_HOOK_REF(hook); /* Keep extra reference to the hook */
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_node_connect */
+
+/*
+ * Hook disconnection. Schedule route cleanup task
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_disconnect(hook_p hook)
+{
+ /*
+ * If hook has private information than we must have this hook in
+ * the routing table and must schedule cleaning for the routing table.
+ * Otherwise hook was connected but we never got "hook_info" message,
+ * so we have never added this hook to the routing table and it save
+ * to just delete it.
+ */
+
+ if (NG_HOOK_PRIVATE(hook) != NULL)
+ return (taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_l2cap_raw_rt_task));
+
+ NG_HOOK_UNREF(hook); /* Remove extra reference */
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_node_disconnect */
+
+/*
+ * Process incoming messages
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_rcvmsg(node_p node, item_p item, hook_p hook)
+{
+ struct ng_mesg *msg = NGI_MSG(item); /* item still has message */
+ int error = 0;
+
+ if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) {
+ mtx_lock(&ng_btsocket_l2cap_raw_queue_mtx);
+ if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_raw_queue)) {
+ NG_BTSOCKET_L2CAP_RAW_ERR(
+"%s: Input queue is full\n", __func__);
+
+ NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_raw_queue);
+ NG_FREE_ITEM(item);
+ error = ENOBUFS;
+ } else {
+ if (hook != NULL) {
+ NG_HOOK_REF(hook);
+ NGI_SET_HOOK(item, hook);
+ }
+
+ NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_raw_queue, item);
+ error = taskqueue_enqueue(taskqueue_swi,
+ &ng_btsocket_l2cap_raw_queue_task);
+ }
+ mtx_unlock(&ng_btsocket_l2cap_raw_queue_mtx);
+ } else {
+ NG_FREE_ITEM(item);
+ error = EINVAL;
+ }
+
+ return (error);
+} /* ng_btsocket_l2cap_raw_node_rcvmsg */
+
+/*
+ * Receive data on a hook
+ */
+
+static int
+ng_btsocket_l2cap_raw_node_rcvdata(hook_p hook, item_p item)
+{
+ NG_FREE_ITEM(item);
+
+ return (EINVAL);
+} /* ng_btsocket_l2cap_raw_node_rcvdata */
+
+/*****************************************************************************
+ *****************************************************************************
+ ** Socket interface
+ *****************************************************************************
+ *****************************************************************************/
+
+/*
+ * L2CAP sockets input routine
+ */
+
+static void
+ng_btsocket_l2cap_raw_input(void *context, int pending)
+{
+ item_p item = NULL;
+ hook_p hook = NULL;
+ struct ng_mesg *msg = NULL;
+
+ for (;;) {
+ mtx_lock(&ng_btsocket_l2cap_raw_queue_mtx);
+ NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_raw_queue, item);
+ mtx_unlock(&ng_btsocket_l2cap_raw_queue_mtx);
+
+ if (item == NULL)
+ break;
+
+ KASSERT((item->el_flags & NGQF_TYPE) == NGQF_MESG,
+("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
+
+ NGI_GET_MSG(item, msg);
+ NGI_GET_HOOK(item, hook);
+ NG_FREE_ITEM(item);
+
+ switch (msg->header.cmd) {
+ case NGM_L2CAP_NODE_HOOK_INFO: {
+ ng_btsocket_l2cap_rtentry_t *rt = NULL;
+
+ if (hook == NULL || NG_HOOK_NOT_VALID(hook) ||
+ msg->header.arglen != sizeof(bdaddr_t))
+ break;
+
+ if (bcmp(msg->data, NG_HCI_BDADDR_ANY,
+ sizeof(bdaddr_t)) == 0)
+ break;
+
+ rt = (ng_btsocket_l2cap_rtentry_t *)
+ NG_HOOK_PRIVATE(hook);
+ if (rt == NULL) {
+ MALLOC(rt, ng_btsocket_l2cap_rtentry_p,
+ sizeof(*rt),
+ M_NETGRAPH_BTSOCKET_L2CAP_RAW,
+ M_NOWAIT|M_ZERO);
+ if (rt == NULL)
+ break;
+
+ mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
+ LIST_INSERT_HEAD(&ng_btsocket_l2cap_raw_rt,
+ rt, next);
+ mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
+
+ NG_HOOK_SET_PRIVATE(hook, rt);
+ }
+
+ bcopy(msg->data, &rt->src, sizeof(rt->src));
+ rt->hook = hook;
+
+ NG_BTSOCKET_L2CAP_RAW_INFO(
+"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n",
+ __func__, NG_HOOK_NAME(hook),
+ rt->src.b[5], rt->src.b[4], rt->src.b[3],
+ rt->src.b[2], rt->src.b[1], rt->src.b[0]);
+ } break;
+
+ case NGM_L2CAP_NODE_GET_FLAGS:
+ case NGM_L2CAP_NODE_GET_DEBUG:
+ case NGM_L2CAP_NODE_GET_CON_LIST:
+ case NGM_L2CAP_NODE_GET_CHAN_LIST:
+ case NGM_L2CAP_L2CA_PING:
+ case NGM_L2CAP_L2CA_GET_INFO: {
+ ng_btsocket_l2cap_raw_pcb_p pcb = NULL;
+
+ if (msg->header.token == 0 ||
+ !(msg->header.flags & NGF_RESP))
+ break;
+
+ mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
+
+ LIST_FOREACH(pcb, &ng_btsocket_l2cap_raw_sockets, next)
+ if (pcb->token == msg->header.token) {
+ pcb->msg = msg;
+ msg = NULL;
+ wakeup(&pcb->msg);
+ break;
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
+ } break;
+
+ default:
+ NG_BTSOCKET_L2CAP_RAW_WARN(
+"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);
+ break;
+ }
+
+ if (hook != NULL)
+ NG_HOOK_UNREF(hook); /* remove extra reference */
+
+ NG_FREE_MSG(msg); /* Checks for msg != NULL */
+ }
+} /* ng_btsocket_l2cap_raw_default_msg_input */
+
+/*
+ * Route cleanup task. Gets scheduled when hook is disconnected. Here we
+ * will find all sockets that use "invalid" hook and disconnect them.
+ */
+
+static void
+ng_btsocket_l2cap_raw_rtclean(void *context, int pending)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = NULL;
+ ng_btsocket_l2cap_rtentry_p rt = NULL;
+
+ mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
+ mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
+
+ /*
+ * First disconnect all sockets that use "invalid" hook
+ */
+
+ LIST_FOREACH(pcb, &ng_btsocket_l2cap_raw_sockets, next)
+ if (pcb->rt != NULL &&
+ pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {
+ if (pcb->so != NULL &&
+ pcb->so->so_state & SS_ISCONNECTED)
+ soisdisconnected(pcb->so);
+
+ pcb->rt = NULL;
+ }
+
+ /*
+ * Now cleanup routing table
+ */
+
+ rt = LIST_FIRST(&ng_btsocket_l2cap_raw_rt);
+ while (rt != NULL) {
+ ng_btsocket_l2cap_rtentry_p rt_next = LIST_NEXT(rt, next);
+
+ if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {
+ LIST_REMOVE(rt, next);
+
+ NG_HOOK_SET_PRIVATE(rt->hook, NULL);
+ NG_HOOK_UNREF(rt->hook); /* Remove extra reference */
+
+ bzero(rt, sizeof(*rt));
+ FREE(rt, M_NETGRAPH_BTSOCKET_L2CAP_RAW);
+ }
+
+ rt = rt_next;
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
+ mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
+} /* ng_btsocket_l2cap_raw_rtclean */
+
+/*
+ * Initialize everything
+ */
+
+void
+ng_btsocket_l2cap_raw_init(void)
+{
+ int error = 0;
+
+ ng_btsocket_l2cap_raw_node = NULL;
+ ng_btsocket_l2cap_raw_debug_level = NG_BTSOCKET_WARN_LEVEL;
+ ng_btsocket_l2cap_raw_ioctl_timeout = 5;
+
+ /* Register Netgraph node type */
+ error = ng_newtype(&typestruct);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_RAW_ALERT(
+"%s: Could not register Netgraph node type, error=%d\n", __func__, error);
+
+ return;
+ }
+
+ /* Create Netgrapg node */
+ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_raw_node);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_RAW_ALERT(
+"%s: Could not create Netgraph node, error=%d\n", __func__, error);
+
+ ng_btsocket_l2cap_raw_node = NULL;
+
+ return;
+ }
+
+ error = ng_name_node(ng_btsocket_l2cap_raw_node,
+ NG_BTSOCKET_L2CAP_RAW_NODE_TYPE);
+ if (error != 0) {
+ NG_BTSOCKET_L2CAP_RAW_ALERT(
+"%s: Could not name Netgraph node, error=%d\n", __func__, error);
+
+ NG_NODE_UNREF(ng_btsocket_l2cap_raw_node);
+ ng_btsocket_l2cap_raw_node = NULL;
+
+ return;
+ }
+
+ /* Create input queue */
+ NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_raw_queue, ifqmaxlen);
+ mtx_init(&ng_btsocket_l2cap_raw_queue_mtx,
+ "btsocks_l2cap_queue_mtx", NULL, MTX_DEF);
+ TASK_INIT(&ng_btsocket_l2cap_raw_queue_task, 0,
+ ng_btsocket_l2cap_raw_input, NULL);
+
+ /* Create list of sockets */
+ LIST_INIT(&ng_btsocket_l2cap_raw_sockets);
+ mtx_init(&ng_btsocket_l2cap_raw_sockets_mtx,
+ "btsocks_l2cap_sockets_mtx", NULL, MTX_DEF);
+
+ /* Tokens */
+ ng_btsocket_l2cap_raw_token = 0;
+ mtx_init(&ng_btsocket_l2cap_raw_token_mtx,
+ "btsocks_l2cap_token_mtx", NULL, MTX_DEF);
+
+ /* Routing table */
+ LIST_INIT(&ng_btsocket_l2cap_raw_rt);
+ mtx_init(&ng_btsocket_l2cap_raw_rt_mtx,
+ "btsocks_l2cap_rt_mtx", NULL, MTX_DEF);
+ TASK_INIT(&ng_btsocket_l2cap_raw_rt_task, 0,
+ ng_btsocket_l2cap_raw_rtclean, NULL);
+} /* ng_btsocket_l2cap_raw_init */
+
+/*
+ * Abort connection on socket
+ */
+
+int
+ng_btsocket_l2cap_raw_abort(struct socket *so)
+{
+ return (ng_btsocket_l2cap_raw_detach(so));
+} /* ng_btsocket_l2cap_raw_abort */
+
+/*
+ * Create and attach new socket
+ */
+
+int
+ng_btsocket_l2cap_raw_attach(struct socket *so, int proto, struct thread *td)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = so2l2cap_raw_pcb(so);
+ int error;
+
+ if (pcb != NULL)
+ return (EISCONN);
+
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EPROTONOSUPPORT);
+ if (so->so_type != SOCK_RAW)
+ return (ESOCKTNOSUPPORT);
+ if ((error = suser(td)) != 0)
+ return (error);
+
+ /* Reserve send and receive space if it is not reserved yet */
+ error = soreserve(so, NG_BTSOCKET_L2CAP_RAW_SENDSPACE,
+ NG_BTSOCKET_L2CAP_RAW_RECVSPACE);
+ if (error != 0)
+ return (error);
+
+ /* Allocate the PCB */
+ MALLOC(pcb, ng_btsocket_l2cap_raw_pcb_p, sizeof(*pcb),
+ M_NETGRAPH_BTSOCKET_L2CAP_RAW, M_WAITOK | M_ZERO);
+ if (pcb == NULL)
+ return (ENOMEM);
+
+ /* Link the PCB and the socket */
+ so->so_pcb = (caddr_t) pcb;
+ pcb->so = so;
+
+ /* Add the PCB to the list */
+ mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
+ LIST_INSERT_HEAD(&ng_btsocket_l2cap_raw_sockets, pcb, next);
+ mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_attach */
+
+/*
+ * Bind socket
+ */
+
+int
+ng_btsocket_l2cap_raw_bind(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_l2cap_raw_pcb_t *pcb = so2l2cap_raw_pcb(so);
+ struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->l2cap_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->l2cap_len != sizeof(*sa))
+ return (EINVAL);
+ if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
+ return (EINVAL);
+
+ bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src));
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_bind */
+
+/*
+ * Connect socket
+ */
+
+int
+ng_btsocket_l2cap_raw_connect(struct socket *so, struct sockaddr *nam,
+ struct thread *td)
+{
+ ng_btsocket_l2cap_raw_pcb_t *pcb = so2l2cap_raw_pcb(so);
+ struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam;
+ ng_btsocket_l2cap_rtentry_t *rt = NULL;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ if (sa == NULL)
+ return (EINVAL);
+ if (sa->l2cap_family != AF_BLUETOOTH)
+ return (EAFNOSUPPORT);
+ if (sa->l2cap_len != sizeof(*sa))
+ return (EINVAL);
+ if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
+ return (EINVAL);
+ if (bcmp(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src)) != 0)
+ return (EADDRNOTAVAIL);
+
+ /*
+ * Find hook with specified source address
+ */
+
+ pcb->rt = NULL;
+
+ mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
+
+ LIST_FOREACH(rt, &ng_btsocket_l2cap_raw_rt, next) {
+ if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
+ continue;
+
+ if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)
+ break;
+ }
+
+ if (rt != NULL) {
+ pcb->rt = rt;
+ soisconnected(so);
+ }
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
+
+ return ((pcb->rt != NULL)? 0 : ENETDOWN);
+} /* ng_btsocket_l2cap_raw_connect */
+
+/*
+ * Find hook that matches source address
+ */
+
+static ng_btsocket_l2cap_rtentry_p
+ng_btsocket_l2cap_raw_find_src_route(bdaddr_p src)
+{
+ ng_btsocket_l2cap_rtentry_p rt = NULL;
+
+ if (bcmp(src, NG_HCI_BDADDR_ANY, sizeof(*src)) != 0) {
+ mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
+
+ LIST_FOREACH(rt, &ng_btsocket_l2cap_raw_rt, next)
+ if (rt->hook != NULL && NG_HOOK_IS_VALID(rt->hook) &&
+ bcmp(src, &rt->src, sizeof(*src)) == 0)
+ break;
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
+ }
+
+ return (rt);
+} /* ng_btsocket_l2cap_raw_find_src_route */
+
+/*
+ * Find first hook does does not match destination address
+ */
+
+static ng_btsocket_l2cap_rtentry_p
+ng_btsocket_l2cap_raw_find_dst_route(bdaddr_p dst)
+{
+ ng_btsocket_l2cap_rtentry_p rt = NULL;
+
+ if (bcmp(dst, NG_HCI_BDADDR_ANY, sizeof(*dst)) != 0) {
+ mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
+
+ LIST_FOREACH(rt, &ng_btsocket_l2cap_raw_rt, next)
+ if (rt->hook != NULL && NG_HOOK_IS_VALID(rt->hook) &&
+ bcmp(dst, &rt->src, sizeof(*dst)) != 0)
+ break;
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
+ }
+
+ return (rt);
+} /* ng_btsocket_l2cap_raw_find_dst_route */
+
+/*
+ * Process ioctl's calls on socket
+ */
+
+int
+ng_btsocket_l2cap_raw_control(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp, struct thread *td)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = so2l2cap_raw_pcb(so);
+ bdaddr_t *src = (bdaddr_t *) data;
+ ng_btsocket_l2cap_rtentry_p rt = pcb->rt;
+ struct ng_mesg *msg = NULL;
+ int error = 0;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) {
+ if (cmd == SIOC_L2CAP_L2CA_PING ||
+ cmd == SIOC_L2CAP_L2CA_GET_INFO)
+ rt = ng_btsocket_l2cap_raw_find_dst_route(src + 1);
+ else
+ rt = ng_btsocket_l2cap_raw_find_src_route(src);
+
+ if (rt == NULL)
+ return (EHOSTUNREACH);
+ }
+
+ bcopy(&rt->src, src, sizeof(*src));
+
+ switch (cmd) {
+ case SIOC_L2CAP_NODE_GET_FLAGS: {
+ struct ng_btsocket_l2cap_raw_node_flags *p =
+ (struct ng_btsocket_l2cap_raw_node_flags *) data;
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_GET_FLAGS,
+ 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ ng_btsocket_l2cap_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_NODE_GET_FLAGS)
+ p->flags = *((ng_l2cap_node_flags_ep *)
+ (pcb->msg->data));
+ else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_L2CAP_NODE_GET_DEBUG: {
+ struct ng_btsocket_l2cap_raw_node_debug *p =
+ (struct ng_btsocket_l2cap_raw_node_debug *) data;
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_GET_DEBUG,
+ 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ ng_btsocket_l2cap_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_NODE_GET_DEBUG)
+ p->debug = *((ng_l2cap_node_debug_ep *)
+ (pcb->msg->data));
+ else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_L2CAP_NODE_SET_DEBUG: {
+ struct ng_btsocket_l2cap_raw_node_debug *p =
+ (struct ng_btsocket_l2cap_raw_node_debug *) data;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_SET_DEBUG,
+ sizeof(ng_l2cap_node_debug_ep), M_WAITOK);
+ if (msg == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_l2cap_node_debug_ep *)(msg->data)) = p->debug;
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node,
+ msg, rt->hook, NULL);
+ } break;
+
+ case SIOC_L2CAP_NODE_GET_CON_LIST: {
+ struct ng_btsocket_l2cap_raw_con_list *p =
+ (struct ng_btsocket_l2cap_raw_con_list *) data;
+ ng_l2cap_node_con_list_ep *p1 = NULL;
+ ng_l2cap_node_con_ep *p2 = NULL;
+
+ if (p->num_connections == 0 ||
+ p->num_connections > NG_L2CAP_MAX_CON_NUM ||
+ p->connections == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_GET_CON_LIST,
+ 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ ng_btsocket_l2cap_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_NODE_GET_CON_LIST) {
+ /* Return data back to user space */
+ p1 = (ng_l2cap_node_con_list_ep *)(pcb->msg->data);
+ p2 = (ng_l2cap_node_con_ep *)(p1 + 1);
+
+ p->num_connections = min(p->num_connections,
+ p1->num_connections);
+ if (p->num_connections > 0)
+ error = copyout((caddr_t) p2,
+ (caddr_t) p->connections,
+ p->num_connections * sizeof(*p2));
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_L2CAP_NODE_GET_CHAN_LIST: {
+ struct ng_btsocket_l2cap_raw_chan_list *p =
+ (struct ng_btsocket_l2cap_raw_chan_list *) data;
+ ng_l2cap_node_chan_list_ep *p1 = NULL;
+ ng_l2cap_node_chan_ep *p2 = NULL;
+
+ if (p->num_channels == 0 ||
+ p->num_channels > NG_L2CAP_MAX_CHAN_NUM ||
+ p->channels == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
+ NGM_L2CAP_NODE_GET_CHAN_LIST, 0, M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ ng_btsocket_l2cap_raw_ioctl_timeout * hz);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_NODE_GET_CHAN_LIST) {
+ /* Return data back to user space */
+ p1 = (ng_l2cap_node_chan_list_ep *)(pcb->msg->data);
+ p2 = (ng_l2cap_node_chan_ep *)(p1 + 1);
+
+ p->num_channels = min(p->num_channels,
+ p1->num_channels);
+ if (p->num_channels > 0)
+ error = copyout((caddr_t) p2,
+ (caddr_t) p->channels,
+ p->num_channels * sizeof(*p2));
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_L2CAP_L2CA_PING: {
+ struct ng_btsocket_l2cap_raw_ping *p =
+ (struct ng_btsocket_l2cap_raw_ping *) data;
+ ng_l2cap_l2ca_ping_ip *ip = NULL;
+ ng_l2cap_l2ca_ping_op *op = NULL;
+
+ if ((p->echo_size != 0 && p->echo_data == NULL) ||
+ p->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Loop back local ping */
+ if (bcmp(&p->echo_dst, &rt->src, sizeof(rt->src)) == 0) {
+ p->result = 0;
+ break;
+ }
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
+ NGM_L2CAP_L2CA_PING, sizeof(*ip) + p->echo_size,
+ M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
+ bcopy(&p->echo_dst, &ip->bdaddr, sizeof(ip->bdaddr));
+ ip->echo_size = p->echo_size;
+
+ if (ip->echo_size > 0) {
+ error = copyin(p->echo_data, ip + 1, p->echo_size);
+ if (error != 0) {
+ NG_FREE_MSG(msg);
+ pcb->token = 0;
+ break;
+ }
+ }
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ bluetooth_l2cap_rtx_timeout());
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_L2CA_PING) {
+ /* Return data back to the user space */
+ op = (ng_l2cap_l2ca_ping_op *)(pcb->msg->data);
+ p->result = op->result;
+ p->echo_size = min(p->echo_size, op->echo_size);
+
+ if (p->echo_size > 0)
+ error = copyout(op + 1, p->echo_data,
+ p->echo_size);
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ case SIOC_L2CAP_L2CA_GET_INFO: {
+ struct ng_btsocket_l2cap_raw_get_info *p =
+ (struct ng_btsocket_l2cap_raw_get_info *) data;
+ ng_l2cap_l2ca_get_info_ip *ip = NULL;
+ ng_l2cap_l2ca_get_info_op *op = NULL;
+
+ if ((p->info_size != 0 && p->info_data == NULL) ||
+ bcmp(&p->info_dst, &rt->src, sizeof(rt->src)) == 0) {
+ error = EINVAL;
+ break;
+ }
+
+ ng_btsocket_l2cap_raw_get_token(&pcb->token);
+ pcb->msg = NULL;
+
+ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
+ NGM_L2CAP_L2CA_GET_INFO, sizeof(*ip) + p->info_size,
+ M_WAITOK);
+ if (msg == NULL) {
+ pcb->token = 0;
+ error = ENOMEM;
+ break;
+ }
+ msg->header.token = pcb->token;
+
+ ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
+ bcopy(&p->info_dst, &ip->bdaddr, sizeof(ip->bdaddr));
+ ip->info_type = p->info_type;
+
+ NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
+ rt->hook, NULL);
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ error = tsleep(&pcb->msg, PZERO|PCATCH, "l2ctl",
+ bluetooth_l2cap_rtx_timeout());
+ if (error != 0) {
+ pcb->token = 0;
+ break;
+ }
+
+ if (pcb->msg != NULL &&
+ pcb->msg->header.cmd == NGM_L2CAP_L2CA_GET_INFO) {
+ /* Return data back to the user space */
+ op = (ng_l2cap_l2ca_get_info_op *)(pcb->msg->data);
+ p->result = op->result;
+ p->info_size = min(p->info_size, op->info_size);
+
+ if (p->info_size > 0)
+ error = copyout(op + 1, p->info_data,
+ p->info_size);
+ } else
+ error = EINVAL;
+
+ NG_FREE_MSG(pcb->msg); /* checks for != NULL */
+ pcb->token = 0;
+ } break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ng_btsocket_l2cap_raw_control */
+
+/*
+ * Detach and destroy socket
+ */
+
+int
+ng_btsocket_l2cap_raw_detach(struct socket *so)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = so2l2cap_raw_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ so->so_pcb = NULL;
+ sotryfree(so);
+
+ mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
+ LIST_REMOVE(pcb, next);
+ mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
+
+ bzero(pcb, sizeof(*pcb));
+ FREE(pcb, M_NETGRAPH_BTSOCKET_L2CAP_RAW);
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_detach */
+
+/*
+ * Disconnect socket
+ */
+
+int
+ng_btsocket_l2cap_raw_disconnect(struct socket *so)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = so2l2cap_raw_pcb(so);
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ pcb->rt = NULL;
+ soisdisconnected(so);
+
+ return (0);
+} /* ng_btsocket_l2cap_raw_disconnect */
+
+/*
+ * Get peer address
+ */
+
+int
+ng_btsocket_l2cap_raw_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+ return (EOPNOTSUPP);
+} /* ng_btsocket_l2cap_raw_peeraddr */
+
+/*
+ * Send data to socket
+ */
+
+int
+ng_btsocket_l2cap_raw_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *nam, struct mbuf *control, struct thread *td)
+{
+ NG_FREE_M(m); /* Checks for m != NULL */
+ NG_FREE_M(control);
+
+ return (EOPNOTSUPP);
+} /* ng_btsocket_l2cap_raw_send */
+
+/*
+ * Get socket address
+ */
+
+int
+ng_btsocket_l2cap_raw_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+ ng_btsocket_l2cap_raw_pcb_p pcb = so2l2cap_raw_pcb(so);
+ struct sockaddr_l2cap sa;
+
+ if (pcb == NULL)
+ return (EINVAL);
+ if (ng_btsocket_l2cap_raw_node == NULL)
+ return (EINVAL);
+
+ bcopy(&pcb->src, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr));
+ sa.l2cap_psm = 0;
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+
+ *nam = dup_sockaddr((struct sockaddr *) &sa, 0);
+
+ return ((*nam == NULL)? ENOMEM : 0);
+} /* ng_btsocket_l2cap_raw_sockaddr */
+
+/*
+ * Get next token
+ */
+
+static void
+ng_btsocket_l2cap_raw_get_token(u_int32_t *token)
+{
+ mtx_lock(&ng_btsocket_l2cap_raw_token_mtx);
+
+ if (++ ng_btsocket_l2cap_raw_token == 0)
+ ng_btsocket_l2cap_raw_token = 1;
+
+ *token = ng_btsocket_l2cap_raw_token;
+
+ mtx_unlock(&ng_btsocket_l2cap_raw_token_mtx);
+} /* ng_btsocket_l2cap_raw_get_token */
+
diff --git a/usr.bin/bluetooth/Makefile b/usr.bin/bluetooth/Makefile
new file mode 100644
index 0000000..1da9ef2
--- /dev/null
+++ b/usr.bin/bluetooth/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+SUBDIR= btsockstat
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.bin/bluetooth/btsockstat/Makefile b/usr.bin/bluetooth/btsockstat/Makefile
new file mode 100644
index 0000000..ea78892
--- /dev/null
+++ b/usr.bin/bluetooth/btsockstat/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.1.1.1 2002/09/09 16:12:49 max Exp $
+# $FreeBSD$
+
+PROG= btsockstat
+MAN1= btsockstat.1
+
+DESTDIR= /usr/bin/
+MANDIR= ../share/man/man
+
+WARNS?= 2
+CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include/
+
+SRCS= btsockstat.c
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/bluetooth/btsockstat/btsockstat.1 b/usr.bin/bluetooth/btsockstat/btsockstat.1
new file mode 100644
index 0000000..a1295cd
--- /dev/null
+++ b/usr.bin/bluetooth/btsockstat/btsockstat.1
@@ -0,0 +1,73 @@
+.\" btsockstat.1
+.\"
+.\" 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: btsockstat.1,v 1.2 2002/11/12 17:08:34 max Exp $
+.\" $FreeBSD$
+.Dd August 31, 2002
+.Dt BTSOCKSTAT 1
+.Os
+.Sh NAME
+.Nm btsockstat
+.Nd show Bluetooth sockets information
+.Sh SYNOPSIS
+.Nm
+.Op Fl p Ar protocol
+.Op Fl r
+.Op Fl M Ar core
+.Sh DESCRIPTION
+The
+.Nm
+command symbolically displays the contents of various Bluetooth sockets
+related data structures. There are few output formats, depending on the
+options for the information presented.
+.Nm
+will print results to the standard output and error messages to the
+standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl p Ar protocol
+Display a list of active sockets (protocol control blocks) for each
+specified protocol. Supported protocols are:
+.Cm hci_raw , l2cap_raw
+and
+.Cm l2cap .
+.It Fl r
+Display a list of active routing entries (if any) for specified protocol.
+.It Fl M Ar core
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem.
+.El
+.Sh BUGS
+Most likely. Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr ng_btsocket 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+
diff --git a/usr.bin/bluetooth/btsockstat/btsockstat.c b/usr.bin/bluetooth/btsockstat/btsockstat.c
new file mode 100644
index 0000000..d123502
--- /dev/null
+++ b/usr.bin/bluetooth/btsockstat/btsockstat.c
@@ -0,0 +1,484 @@
+/*
+ * btsockstat.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: btsockstat.c,v 1.2 2002/09/16 19:40:14 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/callout.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <bitstring.h>
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+#include <ng_btsocket_hci_raw.h>
+#include <ng_btsocket_l2cap.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void hcirawpr (kvm_t *kvmd, u_long addr);
+static void l2caprawpr (kvm_t *kvmd, u_long addr);
+static void l2cappr (kvm_t *kvmd, u_long addr);
+static void l2caprtpr (kvm_t *kvmd, u_long addr);
+
+static kvm_t * kopen (char const *memf);
+static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size);
+
+static void usage (void);
+
+/*
+ * List of symbols
+ */
+
+static struct nlist nl[] = {
+#define N_HCI_RAW 0
+ { "_ng_btsocket_hci_raw_sockets" },
+#define N_L2CAP_RAW 1
+ { "_ng_btsocket_l2cap_raw_sockets" },
+#define N_L2CAP 2
+ { "_ng_btsocket_l2cap_sockets" },
+#define N_L2CAP_RAW_RT 3
+ { "_ng_btsocket_l2cap_raw_rt" },
+#define N_L2CAP_RT 4
+ { "_ng_btsocket_l2cap_rt" },
+ { "" },
+};
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int opt, proto = -1, route = 0;
+ kvm_t *kvmd = NULL;
+ char *memf = NULL;
+
+ while ((opt = getopt(argc, argv, "hM:p:r")) != -1) {
+ switch (opt) {
+ case 'M':
+ memf = optarg;
+ break;
+
+ case 'p':
+ if (strcasecmp(optarg, "hci_raw") == 0)
+ proto = N_HCI_RAW;
+ else if (strcasecmp(optarg, "l2cap_raw") == 0)
+ proto = N_L2CAP_RAW;
+ else if (strcasecmp(optarg, "l2cap") == 0)
+ proto = N_L2CAP;
+ else
+ usage();
+ /* NOT REACHED */
+ break;
+
+ case 'r':
+ route = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ if (proto == N_HCI_RAW && route)
+ usage();
+ /* NOT REACHED */
+
+ kvmd = kopen(memf);
+ if (kvmd == NULL)
+ return (1);
+
+ switch (proto) {
+ case N_HCI_RAW:
+ hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
+ break;
+
+ case N_L2CAP_RAW:
+ if (route)
+ l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
+ else
+ l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
+ break;
+
+ case N_L2CAP:
+ if (route)
+ l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
+ else
+ l2cappr(kvmd, nl[N_L2CAP].n_value);
+ break;
+
+ default:
+ if (route) {
+ l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
+ l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
+ } else {
+ hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
+ l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
+ l2cappr(kvmd, nl[N_L2CAP].n_value);
+ }
+ break;
+ }
+
+ return (kvm_close(kvmd));
+} /* main */
+
+/*
+ * Print raw HCI sockets
+ */
+
+static void
+hcirawpr(kvm_t *kvmd, u_long addr)
+{
+ ng_btsocket_hci_raw_pcb_p this = NULL, next = NULL;
+ ng_btsocket_hci_raw_pcb_t pcb;
+ struct socket so;
+ int first = 1;
+
+ if (addr == 0)
+ return;
+
+ if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
+ return;
+
+ for ( ; this != NULL; this = next) {
+ if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
+ return;
+ if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
+ return;
+
+ next = LIST_NEXT(&pcb, next);
+
+ if (first) {
+ first = 0;
+ fprintf(stdout,
+"Active raw HCI sockets\n" \
+"%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n",
+ "Socket",
+ "PCB",
+ "Flags",
+ "Recv-Q",
+ "Send-Q",
+ "Local address");
+ }
+
+ if (pcb.addr.hci_node[0] == 0) {
+ pcb.addr.hci_node[0] = '*';
+ pcb.addr.hci_node[1] = 0;
+ }
+
+ fprintf(stdout,
+"%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n",
+ (int) pcb.so,
+ (int) this,
+ pcb.flags,
+ so.so_rcv.sb_cc,
+ so.so_snd.sb_cc,
+ pcb.addr.hci_node);
+ }
+} /* hcirawpr */
+
+/*
+ * Print raw L2CAP sockets
+ */
+
+static void
+l2caprawpr(kvm_t *kvmd, u_long addr)
+{
+ ng_btsocket_l2cap_raw_pcb_p this = NULL, next = NULL;
+ ng_btsocket_l2cap_raw_pcb_t pcb;
+ struct socket so;
+ int first = 1;
+ char bdaddr[32];
+
+ if (addr == 0)
+ return;
+
+ if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
+ return;
+
+ for ( ; this != NULL; this = next) {
+ if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
+ return;
+ if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
+ return;
+
+ next = LIST_NEXT(&pcb, next);
+
+ if (first) {
+ first = 0;
+ fprintf(stdout,
+"Active raw L2CAP sockets\n" \
+"%-8.8s %-8.8s %-6.6s %-6.6s %-18.18s\n",
+ "Socket",
+ "PCB",
+ "Recv-Q",
+ "Send-Q",
+ "Local address");
+ }
+
+ if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0) {
+ bdaddr[0] = '*';
+ bdaddr[1] = 0;
+ } else
+ snprintf(bdaddr, sizeof(bdaddr),
+"%02x:%02x:%02x:%02x:%02x:%02x",
+ pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
+ pcb.src.b[2], pcb.src.b[1], pcb.src.b[0]);
+
+ fprintf(stdout,
+"%-8.8x %-8.8x %6d %6d %-18.18s\n",
+ (int) pcb.so,
+ (int) this,
+ so.so_rcv.sb_cc,
+ so.so_snd.sb_cc,
+ bdaddr);
+ }
+} /* l2caprawpr */
+
+/*
+ * Print L2CAP sockets
+ */
+
+static void
+l2cappr(kvm_t *kvmd, u_long addr)
+{
+ static char const * const states[] = {
+ /* NG_BTSOCKET_L2CAP_CLOSED */ "CLOSED",
+ /* NG_BTSOCKET_L2CAP_CONNECTING */ "CON",
+ /* NG_BTSOCKET_L2CAP_CONFIGURING */ "CONFIG",
+ /* NG_BTSOCKET_L2CAP_OPEN */ "OPEN",
+ /* NG_BTSOCKET_L2CAP_DISCONNECTING */ "DISCON"
+ };
+#define state2str(x) \
+ (((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)])
+
+ ng_btsocket_l2cap_pcb_p this = NULL, next = NULL;
+ ng_btsocket_l2cap_pcb_t pcb;
+ struct socket so;
+ int first = 1;
+ char local[32], remote[32];
+
+
+ if (addr == 0)
+ return;
+
+ if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
+ return;
+
+ for ( ; this != NULL; this = next) {
+ if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
+ return;
+ if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
+ return;
+
+ next = LIST_NEXT(&pcb, next);
+
+ if (first) {
+ first = 0;
+ fprintf(stdout,
+"Active L2CAP sockets\n" \
+"%-8.8s %-6.6s %-6.6s %-24.24s %-18.18s %-5.5s %s\n",
+ "PCB",
+ "Recv-Q",
+ "Send-Q",
+ "Local address/PSM",
+ "Foreign address",
+ "CID",
+ "State");
+ }
+
+ if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0)
+ snprintf(local, sizeof(local), "*/%d", pcb.psm);
+ else
+ snprintf(local, sizeof(local),
+"%02x:%02x:%02x:%02x:%02x:%02x/%d",
+ pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
+ pcb.src.b[2], pcb.src.b[1], pcb.src.b[0],
+ pcb.psm);
+
+ if (memcmp(&pcb.dst, NG_HCI_BDADDR_ANY, sizeof(pcb.dst)) == 0) {
+ remote[0] = '*';
+ remote[1] = 0;
+ } else
+ snprintf(remote, sizeof(remote),
+"%02x:%02x:%02x:%02x:%02x:%02x",
+ pcb.dst.b[5], pcb.dst.b[4], pcb.dst.b[3],
+ pcb.dst.b[2], pcb.dst.b[1], pcb.dst.b[0]);
+
+ fprintf(stdout,
+"%-8.8x %6d %6d %-24.24s %-18.18s %-5d %s\n",
+ (int) this,
+ so.so_rcv.sb_cc,
+ so.so_snd.sb_cc,
+ local,
+ remote,
+ pcb.cid,
+ (so.so_options & SO_ACCEPTCONN)?
+ "LISTEN" : state2str(pcb.state));
+ }
+} /* l2cappr */
+
+/*
+ * Print L2CAP routing table
+ */
+
+static void
+l2caprtpr(kvm_t *kvmd, u_long addr)
+{
+ ng_btsocket_l2cap_rtentry_p this = NULL, next = NULL;
+ ng_btsocket_l2cap_rtentry_t rt;
+ int first = 1;
+ char bdaddr[32];
+
+ if (addr == 0)
+ return;
+
+ if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
+ return;
+
+ for ( ; this != NULL; this = next) {
+ if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0)
+ return;
+
+ next = LIST_NEXT(&rt, next);
+
+ if (first) {
+ first = 0;
+ fprintf(stdout,
+"Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)? "raw " : "");
+ fprintf(stdout,
+"%-8.8s %-8.8s %-18.18s\n", "RTentry",
+ "Hook",
+ "BD_ADDR");
+ }
+
+ if (memcmp(&rt.src, NG_HCI_BDADDR_ANY, sizeof(rt.src)) == 0) {
+ bdaddr[0] = '-';
+ bdaddr[1] = 0;
+ } else
+ snprintf(bdaddr, sizeof(bdaddr),
+"%02x:%02x:%02x:%02x:%02x:%02x", rt.src.b[5], rt.src.b[4], rt.src.b[3],
+ rt.src.b[2], rt.src.b[1], rt.src.b[0]);
+
+ fprintf(stdout,
+"%-8.8x %-8.8x %-18.18s\n",
+ (int) this,
+ (int) rt.hook,
+ bdaddr);
+ }
+} /* l2caprtpr */
+
+/*
+ * Open kvm
+ */
+
+static kvm_t *
+kopen(char const *memf)
+{
+ kvm_t *kvmd = NULL;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ /*
+ * Discard setgid privileges if not the running kernel so that
+ * bad guys can't print interesting stuff from kernel memory.
+ */
+
+ if (memf != NULL)
+ setgid(getgid());
+
+ kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
+ if (kvmd == NULL) {
+ warnx("kvm_openfiles: %s", errbuf);
+ return (NULL);
+ }
+
+ if (kvm_nlist(kvmd, nl) < 0) {
+ warnx("kvm_nlist: %s", kvm_geterr(kvmd));
+ goto fail;
+ }
+
+ if (nl[0].n_type == 0) {
+ warnx("kvm_nlist: no namelist");
+ goto fail;
+ }
+
+ return (kvmd);
+fail:
+ kvm_close(kvmd);
+
+ return (NULL);
+} /* kopen */
+
+/*
+ * Read kvm
+ */
+
+static int
+kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
+{
+ if (kvmd == NULL || buffer == NULL)
+ return (-1);
+
+ if (kvm_read(kvmd, addr, buffer, size) != size) {
+ warnx("kvm_read: %s", kvm_geterr(kvmd));
+ return (-1);
+ }
+
+ return (0);
+} /* kread */
+
+/*
+ * Print usage and exit
+ */
+
+static void
+usage(void)
+{
+ fprintf(stdout, "Usage: btsockstat [-M core ] [-p proto] [-r]\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/Makefile b/usr.sbin/bluetooth/Makefile
new file mode 100644
index 0000000..821a003
--- /dev/null
+++ b/usr.sbin/bluetooth/Makefile
@@ -0,0 +1,12 @@
+# $Id$
+# $FreeBSD$
+
+SUBDIR= \
+ bt3cfw \
+ hccontrol \
+ hcseriald \
+ l2control \
+ l2ping
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/bluetooth/bt3cfw/Makefile b/usr.sbin/bluetooth/bt3cfw/Makefile
new file mode 100644
index 0000000..05ae1ab
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/Makefile
@@ -0,0 +1,15 @@
+# $Id: Makefile,v 1.1.1.1 2002/11/12 00:39:18 max Exp $
+# $FreeBSD$
+
+DESTDIR= /usr/sbin/
+MANDIR= ../share/man/man
+PROG= bt3cfw
+MAN8= bt3cfw.8
+WARNS?= 2
+CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include
+SRCS= bt3cfw.c
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.8 b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8
new file mode 100644
index 0000000..e85cb16
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8
@@ -0,0 +1,69 @@
+.\" bt3cfw.1
+.\"
+.\" 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: bt3cfw.8,v 1.1.1.1 2002/11/12 00:39:18 max Exp $
+.\" $FreeBSD$
+.Dd November 11, 2002
+.Dt BT3CFW 8
+.Os
+.Sh NAME
+.Nm BT3CFW
+.Nd Firmware download utility for 3Com Bluetooth PC card driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar Netgraph node name
+.Op Fl f Ar Firmware file name
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the specified Netgraph driver node of type
+.Dv BTCCC
+and downloads specified firmware file.
+.Pp
+Due to copyright issues I will no longer provide firmware with the card
+driver. The firmware can be obtained from the Windows driver package that
+can be downloaded from the 3COM web site at no charge. The firmware name
+is BT3CPCC.BIN. I'm using original firmware that came with the card on CD-ROM.
+.Bd -literal -offset indent
+MD5 (BT3CPCC.BIN) = 36170fda56ea9fdbf1702c966f8a97f1
+.Ed
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl n Ar Netgraph node name
+Connect to the specified Netgraph driver node of type
+.Dv BTCCC .
+.It Fl f Ar Firmware file name
+Specify firmware file name for download.
+.El
+.Sh BUGS
+Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr ng_bt3c 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.c b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c
new file mode 100644
index 0000000..09252ac
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c
@@ -0,0 +1,227 @@
+/*
+ * bt3cfw.c
+ *
+ * Copyright (c) 2001 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: bt3cfw.c,v 1.1.1.1 2002/11/12 00:39:18 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <netgraph.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "ng_bt3c.h"
+
+#define BT3CFW_IDENT "bt3cfw"
+#define BT3CFW_MAX_FIRMWARE_SIZE 0xffff
+
+/* Convert hex ASCII to int4 */
+static int
+hexa2int4(const char *a)
+{
+ if ('0' <= *a && *a <= '9')
+ return (*a - '0');
+
+ if ('A' <= *a && *a <= 'F')
+ return (*a - 'A' + 0xa);
+
+ if ('a' <= *a && *a <= 'f')
+ return (*a - 'a' + 0xa);
+
+ syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a);
+ exit(255);
+}
+
+/* Convert hex ASCII to int8 */
+static int
+hexa2int8(const char *a)
+{
+ return ((hexa2int4(a) << 4) | hexa2int4(a + 1));
+}
+
+/* Convert hex ASCII to int16 */
+static int
+hexa2int16(const char *a)
+{
+ return ((hexa2int8(a) << 8) | hexa2int8(a + 2));
+}
+
+/* Convert hex ASCII to int32 */
+static int
+hexa2int32(const char *a)
+{
+ return ((hexa2int16(a) << 16) | hexa2int16(a + 4));
+}
+
+/* Display usage() and exit */
+static void
+usage(void)
+{
+ syslog(LOG_ERR, "Usage: %s -f FirmwareFile -n NodeName", BT3CFW_IDENT);
+ exit(255);
+}
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ FILE *firmware_file = NULL;
+ char buffer[80], path[NG_PATHLEN + 1],
+ *firmware_filename = NULL;
+ u_int8_t *firmware = NULL;
+ int firmware_size, opt, cs, ds;
+
+ memset(path, 0, sizeof(path));
+ openlog(BT3CFW_IDENT, LOG_NDELAY|LOG_PID|LOG_PERROR, LOG_USER);
+
+ while ((opt = getopt(argc, argv, "f:hn:")) != -1) {
+ switch (opt) {
+ case 'f':
+ firmware_filename = optarg;
+ break;
+
+ case 'n':
+ snprintf(path, sizeof(path), "%s:", optarg);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ if (firmware_filename == NULL || path[0] == 0)
+ usage();
+ /* NOT REACHED */
+
+ firmware = (u_int8_t *) calloc(BT3CFW_MAX_FIRMWARE_SIZE,
+ sizeof(u_int8_t));
+ if (firmware == NULL) {
+ syslog(LOG_ERR, "Could not allocate firmware buffer");
+ exit(255);
+ }
+
+ if ((firmware_file = fopen(firmware_filename, "r")) == NULL) {
+ syslog(LOG_ERR, "Could not open BT3C firmware file %s. %s (%d)",
+ firmware_filename, strerror(errno), errno);
+ exit(255);
+ }
+
+ firmware_size = 0;
+
+ while (fgets(buffer, sizeof(buffer), firmware_file)) {
+ int i, size, address, cs, fcs;
+
+ size = hexa2int8(buffer + 2);
+ address = hexa2int32(buffer + 4);
+ fcs = hexa2int8(buffer + 2 + size * 2);
+
+ if (buffer[1] == '3') {
+ ng_bt3c_firmware_block_ep *block = NULL;
+ u_int16_t *data = NULL;
+
+ block = (ng_bt3c_firmware_block_ep *)
+ (firmware + firmware_size);
+
+ firmware_size += sizeof(*block);
+ if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) {
+ syslog(LOG_ERR, "Could not add new firmware " \
+ "block. Firmware file %s is " \
+ "too big, firmware_size=%d",
+ firmware_filename,
+ firmware_size);
+ exit(255);
+ }
+
+ block->block_address = address;
+ block->block_size = (size - 4) / 2;
+ block->block_alignment = (block->block_size * 2) % 3;
+ if (block->block_alignment != 0)
+ block->block_alignment = 3 - block->block_alignment;
+
+ firmware_size += (block->block_size * 2);
+ firmware_size += block->block_alignment;
+ if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) {
+ syslog(LOG_ERR, "Could not add new firmware " \
+ "data. Firmware file %s is " \
+ "too big, firmware_size=%d",
+ firmware_filename,
+ firmware_size);
+ exit(255);
+ }
+
+ /* First part of the cheksum: size and address */
+ cs = 0;
+ for (i = 0; i < 5; i++)
+ cs += hexa2int8(buffer + 2 + i * 2);
+
+ /* Data + second part of the cheksum: data */
+ data = (u_int16_t *)(block + 1);
+ for (i = 0; i < block->block_size; i++) {
+ data[i] = hexa2int16(buffer + (i * 4) + 12);
+ cs += (((data[i] & 0xff00) >> 8) & 0xff);
+ cs += (data[i] & 0x00ff);
+ }
+ } else
+ for (cs = 0, i = 0; i < size; i++)
+ cs += hexa2int8(buffer + 2 + i * 2);
+
+ if (((cs + fcs) & 0xff) != 0xff) {
+ syslog(LOG_ERR, "Invalid firmware file %s. Checksum " \
+ "error, cs=%#x, fcs=%#x, checksum=%#x",
+ firmware_filename, (cs & 0xff), fcs,
+ ((cs + fcs) & 0xff));
+ exit(255);
+ }
+ }
+
+ /* Send firmware to the card */
+ if (NgMkSockNode(NULL, &cs, &ds) < 0) {
+ syslog(LOG_ERR, "Could not create Netgraph socket. %s (%d)",
+ strerror(errno), errno);
+ exit(255);
+ }
+
+ if (NgSendMsg(cs, path, NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_DOWNLOAD_FIRMWARE,
+ (void const *) firmware, firmware_size) < 0) {
+ syslog(LOG_ERR, "Could not send Netgraph message. %s (%d)",
+ strerror(errno), errno);
+ exit(255);
+ }
+
+ free(firmware);
+ firmware = NULL;
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/hccontrol/Makefile b/usr.sbin/bluetooth/hccontrol/Makefile
new file mode 100644
index 0000000..c816c3c
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.6 2002/09/06 18:52:41 max Exp $
+# $FreeBSD$
+
+DESTDIR= /usr/sbin/
+MANDIR= ../share/man/man
+PROG= hccontrol
+MAN8= hccontrol.8
+WARNS?= 2
+CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include
+SRCS= send_recv.c link_policy.c link_control.c \
+ host_controller_baseband.c info.c status.c node.c hccontrol.c \
+ util.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.8 b/usr.sbin/bluetooth/hccontrol/hccontrol.8
new file mode 100644
index 0000000..555cf32
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.8
@@ -0,0 +1,163 @@
+.\" hccontrol.8
+.\"
+.\" 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: hccontrol.8,v 1.8 2002/11/12 22:33:17 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt HCCONTROL 8
+.Os
+.Sh NAME
+.Nm hccontrol
+.Nd HCI configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar HCI node name
+.Op Ar command
+.Op Ar parameters ...
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the specified Netgraph node of type
+.Em HCI
+and attempts to send specified command to the HCI Netgraph node or to the
+associated Bluetooth device.
+.Nm
+will print results to the standard output and error messages to
+the standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl n Ar HCI node name
+Connect to the specified HCI Netgraph node.
+.It command
+One of the supported commands (see below). Special command
+.Dq help
+can be used to obtain the list of all supported commands. To get more
+information about specific command use
+.Dq help command .
+.It parameters
+One or more optional space separated command parameters.
+.El
+.Sh COMMANDS
+The currently supported HCI commands in
+.Nm
+are:
+.Pp
+.Bd -literal -offset indent -compact
+Inquiry
+Create_Connection
+Disconnect
+Add_SCO_Connection
+Change_Connection_Packet_Type
+Remote_Name_Request
+Read_Remote_Supported_Features
+Read_Remote_Version_Information
+Read_Clock_Offset
+Role_Discovery
+Switch_Role
+Read_Link_Policy_Settings
+Write_Link_Policy_Settings
+Reset
+Read_Pin_Type
+Write_Pin_Type
+Read_Stored_Link_Key
+Write_Stored_Link_Key
+Delete_Stored_Link_Key
+Change_Local_Name
+Read_Local_Name
+Read_Connection_Accept_Timeout
+Write_Connection_Accept_Timeout
+Read_Page_Timeout
+Write_Page_Timeout
+Read_Scan_Enable
+Write_Scan_Enable
+Read_Page_Scan_Activity
+Write_Page_Scan_Activity
+Read_Inquiry_Scan_Activity
+Write_Inquiry_Scan_Activity
+Read_Authentication_Enable
+Write_Authentication_Enable
+Read_Encryption_Mode
+Write_Encryption_Mode
+Read_Class_Of_Device
+Write_Class_Of_Device
+Read_Voice_Settings
+Write_Voice_Settings
+Read_Number_Broadcast_Retransmissions
+Write_Number_Broadcast_Retransmissions
+Read_Hold_Mode_Activity
+Write_Hold_Mode_Activity
+Read_SCO_Flow_Control_Enable
+Write_SCO_Flow_Control_Enable
+Read_Link_Supervision_Timeout
+Write_Link_Supervision_Timeout
+Read_Local_Version_Information
+Read_Local_Supported_Features
+Read_Buffer_Size
+Read_Country_Code
+Read_BD_ADDR
+Read_Failed_Contact_Counter
+Reset_Failed_Contact_Counter
+Get_Link_Quality
+Read_RSSI
+.Ed
+.Pp
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bd -literal -offset indent -compact
+Read_Node_State
+Initialize
+Read_Debug_Level
+Write_Debug_Level
+Read_Command_Timeout
+Write_Command_Timeout
+Read_Node_Buffer_Size
+Read_Node_BD_ADDR
+Read_Node_Features
+Read_Node_Stat
+Reset_Node_Stat
+Flush_Neighbor_Cache
+Read_Neighbor_Cache
+Read_Connection_List
+Read_Node_Link_Policy_Settings_Mask
+Write_Node_Link_Policy_Settings_Mask
+Read_Node_Packet_Mask
+Write_Node_Packet_Mask
+.Ed
+.Pp
+.Sh BUGS
+Most likely. Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_hci 4 ,
+.Xr hcseriald 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c
new file mode 100644
index 0000000..12b70eb
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c
@@ -0,0 +1,274 @@
+/*
+ * hccontrol.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: hccontrol.c,v 1.11 2002/09/12 18:19:43 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <assert.h>
+#include <bitstring.h>
+#include <err.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "hccontrol.h"
+
+/* Prototypes */
+static int do_hci_command (char const *, int, char **);
+static struct hci_command * find_hci_command (char const *, struct hci_command *);
+static void print_hci_command (struct hci_command *);
+static void usage (void);
+
+/* Globals */
+int verbose = 0;
+int timeout;
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ char *node = NULL;
+ int n;
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "n:v")) != -1) {
+ switch (n) {
+ case 'n':
+ node = optarg;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ n = do_hci_command(node, argc, argv);
+
+ return (n);
+} /* main */
+
+/* Create socket and bind it */
+static int
+socket_open(char const *node)
+{
+ struct sockaddr_hci addr;
+ struct ng_btsocket_hci_raw_filter filter;
+ int s, mib[4];
+ size_t size;
+
+ if (node == NULL)
+ usage();
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+ if (s < 0)
+ err(1, "Could not create socket");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_len = sizeof(addr);
+ addr.hci_family = AF_BLUETOOTH;
+ strncpy(addr.hci_node, node, sizeof(addr.hci_node));
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ err(2, "Could not bind socket, node=%s", node);
+
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ err(3, "Could not connect socket, node=%s", node);
+
+ memset(&filter, 0, sizeof(filter));
+ bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1);
+
+ if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
+ (void * const) &filter, sizeof(filter)) < 0)
+ err(4, "Could not setsockopt()");
+
+ size = (sizeof(mib)/sizeof(mib[0]));
+ if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0)
+ err(5, "Could not sysctlnametomib()");
+
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
+ (void *) &timeout, &size, NULL, 0) < 0)
+ err(6, "Could not sysctl()");
+
+ timeout ++;
+
+ return (s);
+} /* socket_open */
+
+/* Execute commands */
+static int
+do_hci_command(char const *node, int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct hci_command *c = NULL;
+ int s, e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_hci_command(link_control_commands);
+ print_hci_command(link_policy_commands);
+ print_hci_command(host_controller_baseband_commands);
+ print_hci_command(info_commands);
+ print_hci_command(status_commands);
+ print_hci_command(node_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_hci_command(cmd, link_control_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, link_policy_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, host_controller_baseband_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, info_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, status_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, node_commands);
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+execute:
+ if (!help) {
+ s = socket_open(node);
+ e = (c->handler)(s, -- argc, ++ argv);
+ close(s);
+ } else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(errno));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+
+ return (e);
+} /* do_hci_command */
+
+/* Try to find command in specified category */
+static struct hci_command *
+find_hci_command(char const *command, struct hci_command *category)
+{
+ struct hci_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_hci_command */
+
+/* Try to find command in specified category */
+static void
+print_hci_command(struct hci_command *category)
+{
+ struct hci_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_hci_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stdout, "Usage: hccontrol -n HCI_node_name cmd [p1] [..]]\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.h b/usr.sbin/bluetooth/hccontrol/hccontrol.h
new file mode 100644
index 0000000..8bcf764
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.h
@@ -0,0 +1,75 @@
+/*
+ * hccontrol.h
+ *
+ * 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: hccontrol.h,v 1.8 2002/09/12 18:19:43 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _HCCONTROL_H_
+#define _HCCONTROL_H_
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+struct hci_command {
+ char const *command;
+ char const *description;
+ int (*handler)(int, int, char **);
+};
+
+extern int timeout;
+extern int verbose;
+extern struct hci_command link_control_commands[];
+extern struct hci_command link_policy_commands[];
+extern struct hci_command host_controller_baseband_commands[];
+extern struct hci_command info_commands[];
+extern struct hci_command status_commands[];
+extern struct hci_command node_commands[];
+
+int hci_request (int, int, char const *, int, char *, int *);
+int hci_simple_request (int, int, char *, int *);
+int hci_send (int, char const *, int);
+int hci_recv (int, char *, int *);
+
+char const * const hci_link2str (int);
+char const * const hci_pin2str (int);
+char const * const hci_scan2str (int);
+char const * const hci_encrypt2str (int, int);
+char const * const hci_coding2str (int);
+char const * const hci_vdata2str (int);
+char const * const hci_hmode2str (int, char *, int);
+char const * const hci_ver2str (int);
+char const * const hci_manufacturer2str(int);
+char const * const hci_features2str (u_int8_t *, char *, int);
+char const * const hci_cc2str (int);
+char const * const hci_con_state2str (int);
+char const * const hci_status2str (int);
+
+#endif /* _HCCONTROL_H_ */
+
diff --git a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c
new file mode 100644
index 0000000..3aa1571
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c
@@ -0,0 +1,1713 @@
+/*
+ * host_controller_baseband.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: host_controller_baseband.c,v 1.12 2002/11/19 18:34:06 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Convert hex ASCII to int4 */
+static int
+hci_hexa2int4(const char *a)
+{
+ if ('0' <= *a && *a <= '9')
+ return (*a - '0');
+
+ if ('A' <= *a && *a <= 'F')
+ return (*a - 'A' + 0xa);
+
+ if ('a' <= *a && *a <= 'f')
+ return (*a - 'a' + 0xa);
+
+ return (-1);
+}
+
+/* Convert hex ASCII to int8 */
+static int
+hci_hexa2int8(const char *a)
+{
+ int hi = hci_hexa2int4(a);
+ int lo = hci_hexa2int4(a + 1);
+
+ if (hi < 0 || lo < 0)
+ return (-1);
+
+ return ((hi << 4) | lo);
+}
+
+/* Convert ascii hex string to the u_int8_t[] */
+static int
+hci_hexstring2array(char const *s, u_int8_t *a, int asize)
+{
+ int i, l, b;
+
+ l = strlen(s) / 2;
+ if (l > asize)
+ l = asize;
+
+ for (i = 0; i < l; i++) {
+ b = hci_hexa2int8(s + i * 2);
+ if (b < 0)
+ return (-1);
+
+ a[i] = (b & 0xff);
+ }
+
+ return (0);
+}
+
+/* Send RESET to the unit */
+static int
+hci_reset(int s, int argc, char **argv)
+{
+ ng_hci_status_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_RESET), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_reset */
+
+/* Send Read_PIN_Type command to the unit */
+static int
+hci_read_pin_type(int s, int argc, char **argv)
+{
+ ng_hci_read_pin_type_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PIN_TYPE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "PIN type: %s [%#02x]\n",
+ hci_pin2str(rp.pin_type), rp.pin_type);
+
+ return (OK);
+} /* hci_read_pin_type */
+
+/* Send Write_PIN_Type command to the unit */
+static int
+hci_write_pin_type(int s, int argc, char **argv)
+{
+ ng_hci_write_pin_type_cp cp;
+ ng_hci_write_pin_type_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.pin_type = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PIN_TYPE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp , &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_pin_type */
+
+/* Send Read_Stored_Link_Key command to the unit */
+static int
+hci_read_stored_link_key(int s, int argc, char **argv)
+{
+ struct {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_read_stored_link_key_cp cp;
+ } __attribute__ ((packed)) cmd;
+
+ struct {
+ ng_hci_event_pkt_t hdr;
+ union {
+ ng_hci_command_compl_ep cc;
+ ng_hci_return_link_keys_ep key;
+ u_int8_t b[NG_HCI_EVENT_PKT_SIZE];
+ } ep;
+ } __attribute__ ((packed)) event;
+
+ int n,a0,a1,a2,a3,a4,a5;
+
+ /* Send command */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.type = NG_HCI_CMD_PKT;
+ cmd.hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_STORED_LINK_KEY));
+ cmd.hdr.length = sizeof(cmd.cp);
+
+ switch (argc) {
+ case 1:
+ /* parse BD_ADDR */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &a5, &a4, &a3, &a2,
+ &a1, &a0) != 6)
+ return (USAGE);
+
+ cmd.cp.bdaddr.b[0] = (a0 & 0xff);
+ cmd.cp.bdaddr.b[1] = (a1 & 0xff);
+ cmd.cp.bdaddr.b[2] = (a2 & 0xff);
+ cmd.cp.bdaddr.b[3] = (a3 & 0xff);
+ cmd.cp.bdaddr.b[4] = (a4 & 0xff);
+ cmd.cp.bdaddr.b[5] = (a5 & 0xff);
+ break;
+
+ default:
+ cmd.cp.read_all = 1;
+ break;
+ }
+
+ if (hci_send(s, (char const *) &cmd, sizeof(cmd)) != OK)
+ return (ERROR);
+
+ /* Receive events */
+again:
+ memset(&event, 0, sizeof(event));
+ n = sizeof(event);
+ if (hci_recv(s, (char *) &event, &n) != OK)
+ return (ERROR);
+
+ if (n <= sizeof(event.hdr)) {
+ errno = EMSGSIZE;
+ return (ERROR);
+ }
+
+ if (event.hdr.type != NG_HCI_EVENT_PKT) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ /* Parse event */
+ switch (event.hdr.event) {
+ case NG_HCI_EVENT_COMMAND_COMPL: {
+ ng_hci_read_stored_link_key_rp *rp = NULL;
+
+ if (event.ep.cc.opcode == 0x0000 ||
+ event.ep.cc.opcode != cmd.hdr.opcode)
+ goto again;
+
+ rp = (ng_hci_read_stored_link_key_rp *)(event.ep.b +
+ sizeof(event.ep.cc));
+
+ fprintf(stdout, "Complete: Status: %s [%#x]\n",
+ hci_status2str(rp->status), rp->status);
+ fprintf(stdout, "Maximum Number of keys: %d\n",
+ le16toh(rp->max_num_keys));
+ fprintf(stdout, "Number of keys read: %d\n",
+ le16toh(rp->num_keys_read));
+ } break;
+
+ case NG_HCI_EVENT_RETURN_LINK_KEYS: {
+ struct _key {
+ bdaddr_t bdaddr;
+ u_int8_t key[NG_HCI_KEY_SIZE];
+ } __attribute__ ((packed)) *k = NULL;
+
+ fprintf(stdout, "Event: Number of keys: %d\n",
+ event.ep.key.num_keys);
+
+ k = (struct _key *)(event.ep.b + sizeof(event.ep.key));
+ for (n = 0; n < event.ep.key.num_keys; n++) {
+ fprintf(stdout, "\t%d: %02x:%02x:%02x:%02x:%02x:%02x ",
+ n + 1,
+ k->bdaddr.b[5], k->bdaddr.b[4], k->bdaddr.b[3],
+ k->bdaddr.b[2], k->bdaddr.b[1], k->bdaddr.b[0]);
+
+ for (a0 = 0; a0 < sizeof(k->key); a0++)
+ fprintf(stdout, "%02x", k->key[a0]);
+ fprintf(stdout, "\n");
+
+ k ++;
+ }
+
+ goto again;
+
+ } break;
+
+ default:
+ goto again;
+ }
+
+ return (OK);
+} /* hci_read_store_link_key */
+
+/* Send Write_Stored_Link_Key command to the unit */
+static int
+hci_write_stored_link_key(int s, int argc, char **argv)
+{
+ struct {
+ ng_hci_write_stored_link_key_cp p;
+ bdaddr_t bdaddr;
+ u_int8_t key[NG_HCI_KEY_SIZE];
+ } cp;
+ ng_hci_write_stored_link_key_rp rp;
+ int32_t n, a0, a1, a2, a3, a4, a5;
+
+ memset(&cp, 0, sizeof(cp));
+
+ switch (argc) {
+ case 2:
+ cp.p.num_keys_write = 1;
+
+ /* parse BD_ADDR */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x",
+ &a5, &a4, &a3, &a2, &a1, &a0) != 6)
+ return (USAGE);
+
+ cp.bdaddr.b[0] = (a0 & 0xff);
+ cp.bdaddr.b[1] = (a1 & 0xff);
+ cp.bdaddr.b[2] = (a2 & 0xff);
+ cp.bdaddr.b[3] = (a3 & 0xff);
+ cp.bdaddr.b[4] = (a4 & 0xff);
+ cp.bdaddr.b[5] = (a5 & 0xff);
+
+ /* parse key */
+ if (hci_hexstring2array(argv[1], cp.key, sizeof(cp.key)) < 0)
+ return (USAGE);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_STORED_LINK_KEY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of keys written: %d\n", rp.num_keys_written);
+
+ return (OK);
+} /* hci_write_stored_link_key */
+
+
+/* Send Delete_Stored_Link_Key command to the unit */
+static int
+hci_delete_stored_link_key(int s, int argc, char **argv)
+{
+ ng_hci_delete_stored_link_key_cp cp;
+ ng_hci_delete_stored_link_key_rp rp;
+ int32_t n, a0, a1, a2, a3, a4, a5;
+
+ memset(&cp, 0, sizeof(cp));
+
+ switch (argc) {
+ case 1:
+ /* parse BD_ADDR */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x",
+ &a5, &a4, &a3, &a2, &a1, &a0) != 6)
+ return (USAGE);
+
+ cp.bdaddr.b[0] = (a0 & 0xff);
+ cp.bdaddr.b[1] = (a1 & 0xff);
+ cp.bdaddr.b[2] = (a2 & 0xff);
+ cp.bdaddr.b[3] = (a3 & 0xff);
+ cp.bdaddr.b[4] = (a4 & 0xff);
+ cp.bdaddr.b[5] = (a5 & 0xff);
+ break;
+
+ default:
+ cp.delete_all = 1;
+ break;
+ }
+
+ /* send command */
+ n = sizeof(cp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_DELETE_STORED_LINK_KEY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of keys deleted: %d\n", rp.num_keys_deleted);
+
+ return (OK);
+} /* hci_delete_stored_link_key */
+
+/* Send Change_Local_Name command to the unit */
+static int
+hci_change_local_name(int s, int argc, char **argv)
+{
+ ng_hci_change_local_name_cp cp;
+ ng_hci_change_local_name_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ snprintf(cp.name, sizeof(cp.name), "%s", argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_CHANGE_LOCAL_NAME),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_change_local_name */
+
+/* Send Read_Local_Name command to the unit */
+static int
+hci_read_local_name(int s, int argc, char **argv)
+{
+ ng_hci_read_local_name_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_LOCAL_NAME),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Local name: %s\n", rp.name);
+
+ return (OK);
+} /* hci_read_local_name */
+
+/* Send Read_Connection_Accept_Timeout to the unit */
+static int
+hci_read_connection_accept_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_con_accept_timo_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_CON_ACCEPT_TIMO),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+ fprintf(stdout, "Connection accept timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_connection_accept_timeout */
+
+/* Send Write_Connection_Accept_Timeout to the unit */
+static int
+hci_write_connection_accept_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_con_accept_timo_cp cp;
+ ng_hci_write_con_accept_timo_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xb540)
+ return (USAGE);
+
+ cp.timeout = (u_int16_t) n;
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_connection_accept_timeout */
+
+/* Send Read_Page_Timeout command to the unit */
+static int
+hci_read_page_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_page_timo_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_TIMO),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+ fprintf(stdout, "Page timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_page_timeoout */
+
+/* Send Write_Page_Timeout command to the unit */
+static int
+hci_write_page_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_page_timo_cp cp;
+ ng_hci_write_page_timo_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xffff)
+ return (USAGE);
+
+ cp.timeout = (u_int16_t) n;
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_timeout */
+
+/* Send Read_Scan_Enable command to the unit */
+static int
+hci_read_scan_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_scan_enable_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_SCAN_ENABLE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Scan enable: %s [%#02x]\n",
+ hci_scan2str(rp.scan_enable), rp.scan_enable);
+
+ return (OK);
+} /* hci_read_scan_enable */
+
+/* Send Write_Scan_Enable command to the unit */
+static int
+hci_write_scan_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_scan_enable_cp cp;
+ ng_hci_write_scan_enable_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3)
+ return (USAGE);
+
+ cp.scan_enable = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_SCAN_ENABLE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_scan_enable */
+
+/* Send Read_Page_Scan_Activity command to the unit */
+static int
+hci_read_page_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_page_scan_activity_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.page_scan_interval = le16toh(rp.page_scan_interval);
+ rp.page_scan_window = le16toh(rp.page_scan_window);
+
+ fprintf(stdout, "Page Scan Interval: %.2f msec [%d slots]\n",
+ rp.page_scan_interval * 0.625, rp.page_scan_interval);
+ fprintf(stdout, "Page Scan Window: %.2f msec [%d slots]\n",
+ rp.page_scan_window * 0.625, rp.page_scan_window);
+
+ return (OK);
+} /* hci_read_page_scan_activity */
+
+/* Send Write_Page_Scan_Activity command to the unit */
+static int
+hci_write_page_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_page_scan_activity_cp cp;
+ ng_hci_write_page_scan_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* page scan interval */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.page_scan_interval = (u_int16_t) n;
+
+ /* page scan window */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.page_scan_window = (u_int16_t) n;
+
+ if (cp.page_scan_window > cp.page_scan_interval)
+ return (USAGE);
+
+ cp.page_scan_interval = htole16(cp.page_scan_interval);
+ cp.page_scan_window = htole16(cp.page_scan_window);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_scan_activity */
+
+/* Send Read_Inquiry_Scan_Activity command to the unit */
+static int
+hci_read_inquiry_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_inquiry_scan_activity_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.inquiry_scan_interval = le16toh(rp.inquiry_scan_interval);
+ rp.inquiry_scan_window = le16toh(rp.inquiry_scan_window);
+
+ fprintf(stdout, "Inquiry Scan Interval: %.2f msec [%d slots]\n",
+ rp.inquiry_scan_interval * 0.625, rp.inquiry_scan_interval);
+ fprintf(stdout, "Inquiry Scan Window: %.2f msec [%d slots]\n",
+ rp.inquiry_scan_window * 0.625, rp.inquiry_scan_interval);
+
+ return (OK);
+} /* hci_read_inquiry_scan_activity */
+
+/* Send Write_Inquiry_Scan_Activity command to the unit */
+static int
+hci_write_inquiry_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_inquiry_scan_activity_cp cp;
+ ng_hci_write_inquiry_scan_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* inquiry scan interval */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.inquiry_scan_interval = (u_int16_t) n;
+
+ /* inquiry scan window */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.inquiry_scan_window = (u_int16_t) n;
+
+ if (cp.inquiry_scan_window > cp.inquiry_scan_interval)
+ return (USAGE);
+
+ cp.inquiry_scan_interval =
+ htole16(cp.inquiry_scan_interval);
+ cp.inquiry_scan_window = htole16(cp.inquiry_scan_window);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_inquiry_scan_activity */
+
+/* Send Read_Authentication_Enable command to the unit */
+static int
+hci_read_authentication_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_auth_enable_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_AUTH_ENABLE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Authentication Enable: %s [%d]\n",
+ rp.auth_enable? "Enabled" : "Disabled", rp.auth_enable);
+
+ return (OK);
+} /* hci_read_authentication_enable */
+
+/* Send Write_Authentication_Enable command to the unit */
+static int
+hci_write_authentication_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_auth_enable_cp cp;
+ ng_hci_write_auth_enable_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.auth_enable = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_AUTH_ENABLE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_authentication_enable */
+
+/* Send Read_Encryption_Mode command to the unit */
+static int
+hci_read_encryption_mode(int s, int argc, char **argv)
+{
+ ng_hci_read_encryption_mode_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_ENCRYPTION_MODE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Encryption mode: %s [%#02x]\n",
+ hci_encrypt2str(rp.encryption_mode, 0), rp.encryption_mode);
+
+ return (OK);
+} /* hci_read_encryption_mode */
+
+/* Send Write_Encryption_Mode command to the unit */
+static int
+hci_write_encryption_mode(int s, int argc, char **argv)
+{
+ ng_hci_write_encryption_mode_cp cp;
+ ng_hci_write_encryption_mode_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2)
+ return (USAGE);
+
+ cp.encryption_mode = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_ENCRYPTION_MODE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_encryption_mode */
+
+/* Send Read_Class_Of_Device command to the unit */
+static int
+hci_read_class_of_device(int s, int argc, char **argv)
+{
+ ng_hci_read_unit_class_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_UNIT_CLASS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Class: %02x:%02x:%02x\n",
+ rp.uclass[2], rp.uclass[1], rp.uclass[0]);
+
+ return (0);
+} /* hci_read_class_of_device */
+
+/* Send Write_Class_Of_Device command to the unit */
+static int
+hci_write_class_of_device(int s, int argc, char **argv)
+{
+ ng_hci_write_unit_class_cp cp;
+ ng_hci_write_unit_class_rp rp;
+ int n0, n1, n2;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3)
+ return (USAGE);
+
+ cp.uclass[0] = (n0 & 0xff);
+ cp.uclass[1] = (n1 & 0xff);
+ cp.uclass[2] = (n2 & 0xff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n0 = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_UNIT_CLASS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n0) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_class_of_device */
+
+/* Send Read_Voice_Settings command to the unit */
+static int
+hci_read_voice_settings(int s, int argc, char **argv)
+{
+ ng_hci_read_voice_settings_rp rp;
+ int n,
+ input_coding,
+ input_data_format,
+ input_sample_size;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_VOICE_SETTINGS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.settings = le16toh(rp.settings);
+
+ input_coding = (rp.settings & 0x0300) >> 8;
+ input_data_format = (rp.settings & 0x00c0) >> 6;
+ input_sample_size = (rp.settings & 0x0020) >> 5;
+
+ fprintf(stdout, "Voice settings: %#04x\n", rp.settings);
+ fprintf(stdout, "Input coding: %s [%d]\n",
+ hci_coding2str(input_coding), input_coding);
+ fprintf(stdout, "Input data format: %s [%d]\n",
+ hci_vdata2str(input_data_format), input_data_format);
+
+ if (input_coding == 0x00) /* Only for Linear PCM */
+ fprintf(stdout, "Input sample size: %d bit [%d]\n",
+ input_sample_size? 16 : 8, input_sample_size);
+
+ return (OK);
+} /* hci_read_voice_settings */
+
+/* Send Write_Voice_Settings command to the unit */
+static int
+hci_write_voice_settings(int s, int argc, char **argv)
+{
+ ng_hci_write_voice_settings_cp cp;
+ ng_hci_write_voice_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.settings = (u_int16_t) n;
+ cp.settings = htole16(cp.settings);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_VOICE_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_voice_settings */
+
+/* Send Read_Number_Broadcast_Restransmissions */
+static int
+hci_read_number_broadcast_retransmissions(int s, int argc, char **argv)
+{
+ ng_hci_read_num_broadcast_retrans_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of broadcast retransmissions: %d\n",
+ rp.counter);
+
+ return (OK);
+} /* hci_read_number_broadcast_retransmissions */
+
+/* Send Write_Number_Broadcast_Restransmissions */
+static int
+hci_write_number_broadcast_retransmissions(int s, int argc, char **argv)
+{
+ ng_hci_write_num_broadcast_retrans_cp cp;
+ ng_hci_write_num_broadcast_retrans_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0xff)
+ return (USAGE);
+
+ cp.counter = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_number_broadcast_retransmissions */
+
+/* Send Read_Hold_Mode_Activity command to the unit */
+static int
+hci_read_hold_mode_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_hold_mode_activity_rp rp;
+ int n;
+ char buffer[1024];
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Hold Mode Activities: %#02x\n", rp.hold_mode_activity);
+ if (rp.hold_mode_activity == 0)
+ fprintf(stdout, "Maintain current Power State");
+ else
+ fprintf(stdout, "%s", hci_hmode2str(rp.hold_mode_activity,
+ buffer, sizeof(buffer)));
+
+ fprintf(stdout, "\n");
+
+ return (OK);
+} /* hci_read_hold_mode_activity */
+
+/* Send Write_Hold_Mode_Activity command to the unit */
+static int
+hci_write_hold_mode_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_hold_mode_activity_cp cp;
+ ng_hci_write_hold_mode_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 4)
+ return (USAGE);
+
+ cp.hold_mode_activity = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_hold_mode_activity */
+
+/* Send Read_SCO_Flow_Control_Enable command to the unit */
+static int
+hci_read_sco_flow_control_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_sco_flow_control_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_SCO_FLOW_CONTROL),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "SCO flow control %s [%d]\n",
+ rp.flow_control? "enabled" : "disabled", rp.flow_control);
+
+ return (OK);
+} /* hci_read_sco_flow_control_enable */
+
+/* Send Write_SCO_Flow_Control_Enable command to the unit */
+static int
+hci_write_sco_flow_control_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_sco_flow_control_cp cp;
+ ng_hci_write_sco_flow_control_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.flow_control = (u_int8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_sco_flow_control_enable */
+
+/* Send Read_Link_Supervision_Timeout command to the unit */
+static int
+hci_read_link_supervision_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_link_supervision_timo_cp cp;
+ ng_hci_read_link_supervision_timo_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link supervision timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_link_supervision_timeout */
+
+/* Send Write_Link_Supervision_Timeout command to the unit */
+static int
+hci_write_link_supervision_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_link_supervision_timo_cp cp;
+ ng_hci_write_link_supervision_timo_rp rp;
+ int n;
+
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* link supervision timeout */
+ if (sscanf(argv[1], "%d", &n) != 1 || n < 0 || n > 0xeff)
+ return (USAGE);
+
+ cp.timeout = (u_int16_t) (n & 0x0fff);
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_link_supervision_timeout */
+
+struct hci_command host_controller_baseband_commands[] = {
+{
+"reset",
+"\nThe Reset command will reset the Host Controller and the Link Manager.\n" \
+"After the reset is completed, the current operational state will be lost,\n" \
+"the Bluetooth unit will enter standby mode and the Host Controller will\n" \
+"automatically revert to the default values for the parameters for which\n" \
+"default values are defined in the specification.",
+&hci_reset
+},
+{
+"read_pin_type",
+"\nThe Read_PIN_Type command is used for the Host to read whether the Link\n" \
+"Manager assumes that the Host supports variable PIN codes only a fixed PIN\n" \
+"code.",
+&hci_read_pin_type
+},
+{
+"write_pin_type <pin_type>",
+"\nThe Write_PIN_Type command is used for the Host to write to the Host\n" \
+"Controller whether the Host supports variable PIN codes or only a fixed PIN\n"\
+"code.\n\n" \
+"\t<pin_type> - dd; 0 - Variable; 1 - Fixed",
+&hci_write_pin_type
+},
+{
+"read_stored_link_key [<bdaddr>]",
+"\nThe Read_Stored_Link_Key command provides the ability to read one or\n" \
+"more link keys stored in the Bluetooth Host Controller. The Bluetooth Host\n" \
+"Controller can store a limited number of link keys for other Bluetooth\n" \
+"devices.\n\n" \
+"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR",
+&hci_read_stored_link_key
+},
+{
+"write_stored_link_key <bdaddr> <key>",
+"\nThe Write_Stored_Link_Key command provides the ability to write one\n" \
+"or more link keys to be stored in the Bluetooth Host Controller. The\n" \
+"Bluetooth Host Controller can store a limited number of link keys for other\n"\
+"Bluetooth devices. If no additional space is available in the Bluetooth\n"\
+"Host Controller then no additional link keys will be stored.\n\n" \
+"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR\n" \
+"\t<key> - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx up to 16 bytes link key",
+&hci_write_stored_link_key
+},
+{
+"delete_stored_link_key [<bdaddr>]",
+"\nThe Delete_Stored_Link_Key command provides the ability to remove one\n" \
+"or more of the link keys stored in the Bluetooth Host Controller. The\n" \
+"Bluetooth Host Controller can store a limited number of link keys for other\n"\
+"Bluetooth devices.\n\n" \
+"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR",
+&hci_delete_stored_link_key
+},
+{
+"change_local_name <name>",
+"\nThe Change_Local_Name command provides the ability to modify the user\n" \
+"friendly name for the Bluetooth unit.\n\n" \
+"\t<name> - string",
+&hci_change_local_name
+},
+{
+"read_local_name",
+"\nThe Read_Local_Name command provides the ability to read the\n" \
+"stored user-friendly name for the Bluetooth unit.",
+&hci_read_local_name
+},
+{
+"read_connection_accept_timeout",
+"\nThis command will read the value for the Connection_Accept_Timeout\n" \
+"configuration parameter. The Connection_Accept_Timeout configuration\n" \
+"parameter allows the Bluetooth hardware to automatically deny a\n" \
+"connection request after a specified time period has occurred and\n" \
+"the new connection is not accepted. Connection Accept Timeout\n" \
+"measured in Number of Baseband slots.",
+&hci_read_connection_accept_timeout
+},
+{
+"write_connection_accept_timeout <timeout>",
+"\nThis command will write the value for the Connection_Accept_Timeout\n" \
+"configuration parameter.\n\n" \
+"\t<timeout> - dddd; measured in number of baseband slots.",
+&hci_write_connection_accept_timeout
+},
+{
+"read_page_timeout",
+"\nThis command will read the value for the Page_Timeout configuration\n" \
+"parameter. The Page_Timeout configuration parameter defines the\n" \
+"maximum time the local Link Manager will wait for a baseband page\n" \
+"response from the remote unit at a locally initiated connection\n" \
+"attempt. Page Timeout measured in Number of Baseband slots.",
+&hci_read_page_timeout
+},
+{
+"write_page_timeout <timeout>",
+"\nThis command will write the value for the Page_Timeout configuration\n" \
+"parameter.\n\n" \
+"\t<timeout> - dddd; measured in number of baseband slots.",
+&hci_write_page_timeout
+},
+{
+"read_scan_enable",
+"\nThis command will read the value for the Scan_Enable parameter. The\n" \
+"Scan_Enable parameter controls whether or not the Bluetooth uint\n" \
+"will periodically scan for page attempts and/or inquiry requests\n" \
+"from other Bluetooth unit.\n\n" \
+"\t0x00 - No Scans enabled.\n" \
+"\t0x01 - Inquiry Scan enabled. Page Scan disabled.\n" \
+"\t0x02 - Inquiry Scan disabled. Page Scan enabled.\n" \
+"\t0x03 - Inquiry Scan enabled. Page Scan enabled.",
+&hci_read_scan_enable
+},
+{
+"write_scan_enable <scan_enable>",
+"\nThis command will write the value for the Scan_Enable parameter.\n" \
+"The Scan_Enable parameter controls whether or not the Bluetooth\n" \
+"unit will periodically scan for page attempts and/or inquiry\n" \
+"requests from other Bluetooth unit.\n\n" \
+"\t<scan_enable> - dd;\n" \
+"\t0 - No Scans enabled.\n" \
+"\t1 - Inquiry Scan enabled. Page Scan disabled.\n" \
+"\t2 - Inquiry Scan disabled. Page Scan enabled.\n" \
+"\t3 - Inquiry Scan enabled. Page Scan enabled.",
+&hci_write_scan_enable
+},
+{
+"read_page_scan_activity",
+"\nThis command will read the value for Page_Scan_Activity configuration\n" \
+"parameters. The Page_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive page scans. This time interval is \n" \
+"defined from when the Host Controller started its last page scan until\n" \
+"it begins the next page scan. The Page_Scan_Window configuration parameter\n" \
+"defines the amount of time for the duration of the page scan. The\n" \
+"Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.",
+&hci_read_page_scan_activity
+},
+{
+"write_page_scan_activity interval(dddd) window(dddd)",
+"\nThis command will write the value for Page_Scan_Activity configuration\n" \
+"parameter. The Page_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive page scans. This is defined as the time\n" \
+"interval from when the Host Controller started its last page scan until it\n" \
+"begins the next page scan. The Page_Scan_Window configuration parameter\n" \
+"defines the amount of time for the duration of the page scan. \n" \
+"The Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.\n\n" \
+"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \
+"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen",
+&hci_write_page_scan_activity
+},
+{
+"read_inquiry_scan_activity",
+"\nThis command will read the value for Inquiry_Scan_Activity configuration\n" \
+"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive inquiry scans. This is defined as the\n" \
+"time interval from when the Host Controller started its last inquiry scan\n" \
+"until it begins the next inquiry scan.",
+&hci_read_inquiry_scan_activity
+},
+{
+"write_inquiry_scan_activity interval(dddd) window(dddd)",
+"\nThis command will write the value for Inquiry_Scan_Activity configuration\n"\
+"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive inquiry scans. This is defined as the\n" \
+"time interval from when the Host Controller started its last inquiry scan\n" \
+"until it begins the next inquiry scan. The Inquiry_Scan_Window configuration\n" \
+"parameter defines the amount of time for the duration of the inquiry scan.\n" \
+"The Inquiry_Scan_Window can only be less than or equal to the Inquiry_Scan_Interval.\n\n" \
+"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \
+"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen",
+&hci_write_inquiry_scan_activity
+},
+{
+"read_authentication_enable",
+"\nThis command will read the value for the Authentication_Enable parameter.\n"\
+"The Authentication_Enable parameter controls if the local unit requires\n"\
+"to authenticate the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n"\
+"and the corresponding Connection Complete event). At connection setup, only\n"\
+"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\
+"authenticate the other unit.",
+&hci_read_authentication_enable
+},
+{
+"write_authentication_enable enable(0|1)",
+"\nThis command will write the value for the Authentication_Enable parameter.\n"\
+"The Authentication_Enable parameter controls if the local unit requires to\n"\
+"authenticate the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n" \
+"and the corresponding Connection Complete event). At connection setup, only\n"\
+"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\
+"authenticate the other unit.",
+&hci_write_authentication_enable
+},
+{
+"read_encryption_mode",
+"\nThis command will read the value for the Encryption_Mode parameter. The\n" \
+"Encryption_Mode parameter controls if the local unit requires encryption\n" \
+"to the remote unit at connection setup (between the Create_Connection\n" \
+"command or acceptance of an incoming ACL connection and the corresponding\n" \
+"Connection Complete event). At connection setup, only the unit(s) with\n" \
+"the Authentication_Enable parameter enabled and Encryption_Mode parameter\n" \
+"enabled will try to encrypt the connection to the other unit.\n\n" \
+"\t<encryption_mode>:\n" \
+"\t0x00 - Encryption disabled.\n" \
+"\t0x01 - Encryption only for point-to-point packets.\n" \
+"\t0x02 - Encryption for both point-to-point and broadcast packets.",
+&hci_read_encryption_mode
+},
+{
+"write_encryption_mode mode(0|1|2)",
+"\tThis command will write the value for the Encryption_Mode parameter.\n" \
+"The Encryption_Mode parameter controls if the local unit requires\n" \
+"encryption to the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n" \
+"and the corresponding Connection Complete event). At connection setup,\n" \
+"only the unit(s) with the Authentication_Enable parameter enabled and\n" \
+"Encryption_Mode parameter enabled will try to encrypt the connection to\n" \
+"the other unit.\n\n" \
+"\t<encryption_mode> (dd)\n" \
+"\t0 - Encryption disabled.\n" \
+"\t1 - Encryption only for point-to-point packets.\n" \
+"\t2 - Encryption for both point-to-point and broadcast packets.",
+&hci_write_encryption_mode
+},
+{
+"read_class_of_device",
+"\nThis command will read the value for the Class_of_Device parameter.\n" \
+"The Class_of_Device parameter is used to indicate the capabilities of\n" \
+"the local unit to other units.",
+&hci_read_class_of_device
+},
+{
+"write_class_of_device class(xx:xx:xx)",
+"\nThis command will write the value for the Class_of_Device parameter.\n" \
+"The Class_of_Device parameter is used to indicate the capabilities of \n" \
+"the local unit to other units.\n\n" \
+"\t<class> (xx:xx:xx) - class of device",
+&hci_write_class_of_device
+},
+{
+"read_voice_settings",
+"\nThis command will read the values for the Voice_Setting parameter.\n" \
+"The Voice_Setting parameter controls all the various settings for voice\n" \
+"connections. These settings apply to all voice connections, and cannot be\n" \
+"set for individual voice connections. The Voice_Setting parameter controls\n" \
+"the configuration for voice connections: Input Coding, Air coding format,\n" \
+"input data format, Input sample size, and linear PCM parameter.",
+&hci_read_voice_settings
+},
+{
+"write_voice_settings settings(xxxx)",
+"\nThis command will write the values for the Voice_Setting parameter.\n" \
+"The Voice_Setting parameter controls all the various settings for voice\n" \
+"connections. These settings apply to all voice connections, and cannot be\n" \
+"set for individual voice connections. The Voice_Setting parameter controls\n" \
+"the configuration for voice connections: Input Coding, Air coding format,\n" \
+"input data format, Input sample size, and linear PCM parameter.\n\n" \
+"\t<voice_settings> (xxxx) - voice settings",
+&hci_write_voice_settings
+},
+{
+"read_number_broadcast_retransmissions",
+"\nThis command will read the unit's parameter value for the Number of\n" \
+"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \
+"unreliable.",
+&hci_read_number_broadcast_retransmissions
+},
+{
+"write_number_broadcast_retransmissions count(dd)",
+"\nThis command will write the unit's parameter value for the Number of\n" \
+"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \
+"unreliable.\n\n" \
+"\t<count> (dd) - number of broadcast retransimissions",
+&hci_write_number_broadcast_retransmissions
+},
+{
+"read_hold_mode_activity",
+"\nThis command will read the value for the Hold_Mode_Activity parameter.\n" \
+"The Hold_Mode_Activity value is used to determine what activities should\n" \
+"be suspended when the unit is in hold mode.",
+&hci_read_hold_mode_activity
+},
+{
+"write_hold_mode_activity settings(0|1|2|4)",
+"\nThis command will write the value for the Hold_Mode_Activity parameter.\n" \
+"The Hold_Mode_Activity value is used to determine what activities should\n" \
+"be suspended when the unit is in hold mode.\n\n" \
+"\t<settings> (dd) - bit mask:\n" \
+"\t0 - Maintain current Power State. Default\n" \
+"\t1 - Suspend Page Scan.\n" \
+"\t2 - Suspend Inquiry Scan.\n" \
+"\t4 - Suspend Periodic Inquiries.",
+&hci_write_hold_mode_activity
+},
+{
+"read_sco_flow_control_enable",
+"\nThe Read_SCO_Flow_Control_Enable command provides the ability to read\n" \
+"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \
+"decide if the Host Controller will send Number Of Completed Packets events\n" \
+"for SCO Connection Handles. This setting allows the Host to enable and\n" \
+"disable SCO flow control.",
+&hci_read_sco_flow_control_enable
+},
+{
+"write_sco_flow_control_enable enable(0|1)",
+"\nThe Write_SCO_Flow_Control_Enable command provides the ability to write\n" \
+"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \
+"decide if the Host Controller will send Number Of Completed Packets events\n" \
+"for SCO Connection Handles. This setting allows the Host to enable and\n" \
+"disable SCO flow control. The SCO_Flow_Control_Enable setting can only be\n" \
+"changed if no connections exist.",
+&hci_write_sco_flow_control_enable
+},
+{
+"read_link_supervision_timeout <connection_handle>",
+"\nThis command will read the value for the Link_Supervision_Timeout\n" \
+"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \
+"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \
+"reason, no Baseband packets are received from that Connection Handle for a\n" \
+"duration longer than the Link_Supervision_Timeout, the connection is\n"
+"disconnected.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n",
+&hci_read_link_supervision_timeout
+},
+{
+"write_link_supervision_timeout <connection_handle> <timeout>",
+"\nThis command will write the value for the Link_Supervision_Timeout\n" \
+"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \
+"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \
+"reason, no Baseband packets are received from that connection handle for a\n" \
+"duration longer than the Link_Supervision_Timeout, the connection is\n" \
+"disconnected.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<timeout> - dddd; timeout measured in number of baseband slots\n",
+&hci_write_link_supervision_timeout
+},
+{ NULL, }
+};
+
diff --git a/usr.sbin/bluetooth/hccontrol/info.c b/usr.sbin/bluetooth/hccontrol/info.c
new file mode 100644
index 0000000..447a493
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/info.c
@@ -0,0 +1,219 @@
+/*
+ * info.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: info.c,v 1.7 2002/09/06 18:52:41 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Send Read_Local_Version_Information command to the unit */
+static int
+hci_read_local_version_information(int s, int argc, char **argv)
+{
+ ng_hci_read_local_ver_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_LOCAL_VER), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.manufacturer = le16toh(rp.manufacturer);
+
+ fprintf(stdout, "HCI version: %s [%#02x]\n",
+ hci_ver2str(rp.hci_version), rp.hci_version);
+ fprintf(stdout, "HCI revision: %#04x\n",
+ le16toh(rp.hci_revision));
+ fprintf(stdout, "LMP version: %#02x\n", rp.lmp_version);
+ fprintf(stdout, "LMP sub-version: %#04x\n",
+ le16toh(rp.lmp_subversion));
+ fprintf(stdout, "Manufacturer: %s [%#04x]\n",
+ hci_manufacturer2str(rp.manufacturer), rp.manufacturer);
+
+ return (OK);
+} /* hci_read_local_version_information */
+
+/* Send Read_Local_Supported_Features command to the unit */
+static int
+hci_read_local_supported_features(int s, int argc, char **argv)
+{
+ ng_hci_read_local_features_rp rp;
+ int n;
+ char buffer[1024];
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_LOCAL_FEATURES),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Features: ");
+ for (n = 0; n < sizeof(rp.features); n++)
+ fprintf(stdout, "%#02x ", rp.features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(rp.features,
+ buffer, sizeof(buffer)));
+
+ return (OK);
+} /* hci_read_local_supported_features */
+
+/* Sent Read_Buffer_Size command to the unit */
+static int
+hci_read_buffer_size(int s, int argc, char **argv)
+{
+ ng_hci_read_buffer_size_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_BUFFER_SIZE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Max. ACL packet size: %d bytes\n",
+ le16toh(rp.max_acl_size));
+ fprintf(stdout, "Number of ACL packets: %d\n",
+ le16toh(rp.num_acl_pkt));
+ fprintf(stdout, "Max. SCO packet size: %d bytes\n",
+ rp.max_sco_size);
+ fprintf(stdout, "Number of SCO packets: %d\n",
+ le16toh(rp.num_sco_pkt));
+
+ return (OK);
+} /* hci_read_buffer_size */
+
+/* Send Read_Country_Code command to the unit */
+static int
+hci_read_country_code(int s, int argc, char **argv)
+{
+ ng_hci_read_country_code_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_COUNTRY_CODE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Country code: %s [%#02x]\n",
+ hci_cc2str(rp.country_code), rp.country_code);
+
+ return (OK);
+} /* hci_read_country_code */
+
+/* Send Read_BD_ADDR command to the unit */
+static int
+hci_read_bd_addr(int s, int argc, char **argv)
+{
+ ng_hci_read_bdaddr_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_BDADDR), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ rp.bdaddr.b[5], rp.bdaddr.b[4], rp.bdaddr.b[3],
+ rp.bdaddr.b[2], rp.bdaddr.b[1], rp.bdaddr.b[0]);
+
+ return (OK);
+} /* hci_read_bd_addr */
+
+struct hci_command info_commands[] = {
+{
+"read_local_version_information",
+"\nThis command will read the values for the version information for the\n" \
+"local Bluetooth unit.",
+&hci_read_local_version_information
+},
+{
+"read_local_supported_features",
+"\nThis command requests a list of the supported features for the local\n" \
+"unit. This command will return a list of the LMP features.",
+&hci_read_local_supported_features
+},
+{
+"read_buffer_size",
+"\nThe Read_Buffer_Size command is used to read the maximum size of the\n" \
+"data portion of HCI ACL and SCO Data Packets sent from the Host to the\n" \
+"Host Controller.",
+&hci_read_buffer_size
+},
+{
+"read_country_code",
+"\nThis command will read the value for the Country_Code return parameter.\n" \
+"The Country_Code defines which range of frequency band of the ISM 2.4 GHz\n" \
+"band will be used by the unit.",
+&hci_read_country_code
+},
+{
+"read_bd_addr",
+"\nThis command will read the value for the BD_ADDR parameter. The BD_ADDR\n" \
+"is a 48-bit unique identifier for a Bluetooth unit.",
+&hci_read_bd_addr
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/link_control.c b/usr.sbin/bluetooth/hccontrol/link_control.c
new file mode 100644
index 0000000..68bf35f
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/link_control.c
@@ -0,0 +1,973 @@
+/*
+ * link_control.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: link_control.c,v 1.12 2002/09/17 16:36:46 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+static void hci_inquiry_response (int n, u_int8_t **b);
+
+/* Send Inquiry command to the unit */
+static int
+hci_inquiry(int s, int argc, char **argv)
+{
+ int n0, n1, n2, timo;
+ u_int8_t b[512];
+ ng_hci_inquiry_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* set defaults */
+ cp.lap[2] = 0x9e;
+ cp.lap[1] = 0x8b;
+ cp.lap[0] = 0x33;
+ cp.inquiry_length = 5;
+ cp.num_responses = 8;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 3:
+ /* LAP */
+ if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3)
+ return (USAGE);
+
+ cp.lap[0] = (n0 & 0xff);
+ cp.lap[1] = (n1 & 0xff);
+ cp.lap[2] = (n2 & 0xff);
+
+ /* inquiry length (N * 1.28) sec, range 0x01 - 0x30 */
+ case 2:
+ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x1 || n0 > 0x30)
+ return (USAGE);
+
+ cp.inquiry_length = (n0 & 0xff);
+
+ /* number of responses, range 0x00 - 0xff */
+ case 1:
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 > 0xff)
+ return (USAGE);
+
+ cp.num_responses = (n0 & 0xff);
+
+ /* use defaults */
+ case 0:
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status back */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_INQUIRY), (char const *) &cp, sizeof(cp),
+ b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ timo = timeout;
+ timeout = cp.inquiry_length * 1.28 + 1;
+
+wait_for_more:
+ /* wait for inquiry events */
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR) {
+ timeout = timo;
+ return (ERROR);
+ }
+
+ if (n0 < sizeof(*e)) {
+ timeout = timo;
+ errno = EIO;
+ return (ERROR);
+ }
+
+ switch (e->event) {
+ case NG_HCI_EVENT_INQUIRY_RESULT: {
+ ng_hci_inquiry_result_ep *ir =
+ (ng_hci_inquiry_result_ep *)(e + 1);
+ u_int8_t *r = (u_int8_t *)(ir + 1);
+
+ fprintf(stdout, "Inquiry result, num_responses=%d\n",
+ ir->num_responses);
+
+ for (n0 = 0; n0 < ir->num_responses; n0++)
+ hci_inquiry_response(n0, &r);
+
+ goto wait_for_more;
+ }
+
+ case NG_HCI_EVENT_INQUIRY_COMPL:
+ fprintf(stdout, "Inquiry complete. Status: %s [%#02x]\n",
+ hci_status2str(*(b + sizeof(*e))), *(b + sizeof(*e)));
+ break;
+
+ default:
+ goto wait_for_more;
+ }
+
+ timeout = timo;
+
+ return (OK);
+} /* hci_inquiry */
+
+/* Print Inquiry_Result event */
+static void
+hci_inquiry_response(int n, u_int8_t **b)
+{
+ struct inquiry_response {
+ bdaddr_t bdaddr;
+ u_int8_t page_scan_rep_mode;
+ u_int8_t page_scan_period_mode;
+ u_int8_t page_scan_mode;
+ u_int8_t class[NG_HCI_CLASS_SIZE];
+ u_int16_t clock_offset;
+ } *ir = (struct inquiry_response *)(*b);
+
+ fprintf(stdout, "Inquiry result #%d\n", n);
+ fprintf(stdout, "\tBD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ir->bdaddr.b[5], ir->bdaddr.b[4], ir->bdaddr.b[3],
+ ir->bdaddr.b[2], ir->bdaddr.b[1], ir->bdaddr.b[0]);
+ fprintf(stdout, "\tPage Scan Rep. Mode: %#02x\n",
+ ir->page_scan_rep_mode);
+ fprintf(stdout, "\tPage Scan Period Mode: %#02x\n",
+ ir->page_scan_period_mode);
+ fprintf(stdout, "\tPage Scan Mode: %#02x\n",
+ ir->page_scan_mode);
+ fprintf(stdout, "\tClass: %02x:%02x:%02x\n",
+ ir->class[2], ir->class[1], ir->class[0]);
+ fprintf(stdout, "\tClock offset: %#04x\n",
+ le16toh(ir->clock_offset));
+
+ *b += sizeof(*ir);
+} /* hci_inquiry_response */
+
+/* Send Create_Connection command to the unit */
+static int
+hci_create_connection(int s, int argc, char **argv)
+{
+ int n0, n1, n2, n3, n4, n5;
+ char b[512];
+ ng_hci_create_con_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.pkt_type = htole16( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 |
+ NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 |
+ NG_HCI_PKT_DM5);
+ cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0;
+ cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE;
+ cp.clock_offset = 0;
+ cp.accept_role_switch = 1;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 6:
+ /* accept role switch */
+ if (sscanf(argv[2], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.accept_role_switch = n0 ? 1 : 0;
+
+ case 5:
+ /* clock offset */
+ if (sscanf(argv[2], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.clock_offset = (n0 & 0xffff);
+ cp.clock_offset = htole16(cp.clock_offset);
+
+ case 4:
+ /* page scan mode */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 3)
+ return (USAGE);
+
+ cp.page_scan_mode = (n0 & 0xff);
+
+ case 3:
+ /* page scan rep mode */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 2)
+ return (USAGE);
+
+ cp.page_scan_rep_mode = (n0 & 0xff);
+
+ case 2:
+ /* packet type */
+ if (sscanf(argv[1], "%x", &n0) != 1)
+ return (USAGE);
+
+ n0 &= ( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 |
+ NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 |
+ NG_HCI_PKT_DM5);
+ if (n0 == 0)
+ return (USAGE);
+
+ cp.pkt_type = (n0 & 0xffff);
+ cp.pkt_type = htole16(cp.pkt_type);
+
+ case 1:
+ /* BD_ADDR */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x",
+ &n5, &n4, &n3, &n2, &n1, &n0) != 6)
+ return (USAGE);
+
+ cp.bdaddr.b[0] = (n0 & 0xff);
+ cp.bdaddr.b[1] = (n1 & 0xff);
+ cp.bdaddr.b[2] = (n2 & 0xff);
+ cp.bdaddr.b[3] = (n3 & 0xff);
+ cp.bdaddr.b[4] = (n4 & 0xff);
+ cp.bdaddr.b[5] = (n5 & 0xff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_CREATE_CON),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_COMPL) {
+ ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Encryption mode: %s [%d]\n",
+ hci_encrypt2str(ep->encryption_mode, 0),
+ ep->encryption_mode);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_create_connection */
+
+/* Send Disconnect command to the unit */
+static int
+hci_disconnect(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_discon_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.reason = 0x13;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* reason */
+ if (sscanf(argv[1], "%d", &n) != 1 || n <= 0x00 || n > 0xff)
+ return (USAGE);
+
+ cp.reason = (u_int8_t) (n & 0xff);
+
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_DISCON),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_DISCON_COMPL) {
+ ng_hci_discon_compl_ep *ep = (ng_hci_discon_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Reason: %s [%#02x]\n",
+ hci_status2str(ep->reason), ep->reason);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_diconnect */
+
+/* Send Add_SCO_Connection command to the unit */
+static int
+hci_add_sco_connection(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_add_sco_con_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.pkt_type = htole16(NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3);
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* packet type */
+ if (sscanf(argv[0], "%x", &n) != 1)
+ return (USAGE);
+
+ n &= (NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3);
+ if (n == 0)
+ return (USAGE);
+
+ cp.pkt_type = (u_int16_t) (n & 0x0fff);
+ cp.pkt_type = htole16(cp.pkt_type);
+
+ case 1:
+ /* acl connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_ADD_SCO_CON),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_COMPL) {
+ ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Encryption mode: %s [%d]\n",
+ hci_encrypt2str(ep->encryption_mode, 0),
+ ep->encryption_mode);
+ } else
+ goto again;
+
+ return (OK);
+} /* Add_SCO_Connection */
+
+/* Send Change_Connection_Packet_Type command to the unit */
+static int
+hci_change_connection_packet_type(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_change_con_pkt_type_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* packet type */
+ if (sscanf(argv[1], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.pkt_type = (u_int16_t) (n & 0xffff);
+ cp.pkt_type = htole16(cp.pkt_type);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_CHANGE_CON_PKT_TYPE),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_PKT_TYPE_CHANGED) {
+ ng_hci_con_pkt_type_changed_ep *ep =
+ (ng_hci_con_pkt_type_changed_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Packet type: %#04x\n",
+ le16toh(ep->pkt_type));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_change_connection_packet_type */
+
+/* Send Remote_Name_Request command to the unit */
+static int
+hci_remote_name_request(int s, int argc, char **argv)
+{
+ int n0, n1, n2, n3, n4, n5;
+ char b[512];
+ ng_hci_remote_name_req_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 4:
+ /* BD_ADDR */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x",
+ &n5, &n4, &n3, &n2, &n1, &n0) != 6)
+ return (USAGE);
+
+ cp.bdaddr.b[0] = (n0 & 0xff);
+ cp.bdaddr.b[1] = (n1 & 0xff);
+ cp.bdaddr.b[2] = (n2 & 0xff);
+ cp.bdaddr.b[3] = (n3 & 0xff);
+ cp.bdaddr.b[4] = (n4 & 0xff);
+ cp.bdaddr.b[5] = (n5 & 0xff);
+
+ /* page_scan_rep_mode */
+ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x02)
+ return (USAGE);
+
+ cp.page_scan_rep_mode = (n0 & 0xff);
+
+ /* page_scan_mode */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x03)
+ return (USAGE);
+
+ cp.page_scan_mode = (n0 & 0xff);
+
+ /* clock_offset */
+ if (sscanf(argv[3], "%x", &n0) != 1)
+ return (USAGE);
+
+ cp.clock_offset = (n0 & 0xffff);
+ cp.clock_offset = htole16(cp.clock_offset);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_REMOTE_NAME_REQ),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL) {
+ ng_hci_remote_name_req_compl_ep *ep =
+ (ng_hci_remote_name_req_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+ fprintf(stdout, "Name: %s\n", ep->name);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_remote_name_request */
+
+/* Send Read_Remote_Supported_Features command to the unit */
+static int
+hci_read_remote_supported_features(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_remote_features_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+ char buffer[1024];
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_REMOTE_FEATURES),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL) {
+ ng_hci_read_remote_features_compl_ep *ep =
+ (ng_hci_read_remote_features_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Features: ");
+ for (n = 0; n < sizeof(ep->features); n++)
+ fprintf(stdout, "%#02x ", ep->features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(ep->features,
+ buffer, sizeof(buffer)));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_remote_supported_features */
+
+/* Send Read_Remote_Version_Information command to the unit */
+static int
+hci_read_remote_version_information(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_remote_ver_info_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_REMOTE_VER_INFO),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL) {
+ ng_hci_read_remote_ver_info_compl_ep *ep =
+ (ng_hci_read_remote_ver_info_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ ep->manufacturer = le16toh(ep->manufacturer);
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "LMP version: %#02x\n", ep->lmp_version);
+ fprintf(stdout, "LMP sub-version: %#04x\n",
+ le16toh(ep->lmp_subversion));
+ fprintf(stdout, "Manufacturer: %s [%#04x]\n",
+ hci_manufacturer2str(ep->manufacturer),
+ ep->manufacturer);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_remote_version_information */
+
+/* Send Read_Clock_Offset command to the unit */
+static int
+hci_read_clock_offset(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_clock_offset_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_CLOCK_OFFSET),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL) {
+ ng_hci_read_clock_offset_compl_ep *ep =
+ (ng_hci_read_clock_offset_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Clock offset: %#04x\n",
+ le16toh(ep->clock_offset));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_clock_offset */
+
+struct hci_command link_control_commands[] = {
+{
+"inquiry <LAP> <inquiry_length> <num_reponses>",
+"\nThis command will cause the Bluetooth unit to enter Inquiry Mode.\n" \
+"Inquiry Mode is used to discover other nearby Bluetooth units. The LAP\n" \
+"input parameter contains the LAP from which the inquiry access code shall\n" \
+"be derived when the inquiry procedure is made. The Inquiry_Length parameter\n"\
+"specifies the total duration of the Inquiry Mode and, when this time\n" \
+"expires, Inquiry will be halted. The Num_Responses parameter specifies the\n" \
+"number of responses that can be received before the Inquiry is halted.\n\n" \
+"\t<LAP> - xx:xx:xx; 9e:8b:33 (GIAC), 93:8b:00 (LDIAC)\n" \
+"\t<inquiry_length> - dd; total length == dd * 1.28 sec\n" \
+"\t<num_responses> - dd",
+&hci_inquiry
+},
+{
+"create_connection <BD_ADDR> <pkt> <rep_mode> <ps_mode> <clck_off> <role_sw>",
+"" \
+"\t<BD_ADDR> - remote unit address\n\n" \
+"\t<pkt> - xxxx; packet type\n" \
+"" \
+"\t\tACL packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0008 DM1\n" \
+"\t\t0x0010 DH1\n" \
+"\t\t0x0400 DM3\n" \
+"\t\t0x0800 DH3\n" \
+"\t\t0x4000 DM5\n" \
+"\t\t0x8000 DH5\n\n" \
+"" \
+"\trep_mode - d; page scan repetition mode\n" \
+"" \
+"\t\tPage scan repetition modes\n" \
+"\t\t--------------------------\n" \
+"\t\t0 Page scan repetition mode 0\n" \
+"\t\t1 Page scan repetition mode 1\n" \
+"\t\t2 Page scan repetition mode 2\n" \
+"\n" \
+"\tps_mode - d; Page scan mode\n" \
+"" \
+"\t\tPage scan modes\n" \
+"\t\t---------------\n" \
+"\t\t0 Mandatory page scan mode\n" \
+"\t\t1 Optional page scan mode1\n" \
+"\t\t2 Optional page scan mode2\n" \
+"\t\t3 Optional page scan mode3\n" \
+"\n" \
+"\tclck_off - dddd; clock offset. Use 0 if unknown\n\n" \
+"\trole_sw - d; allow (1) or deny role switch\n",
+&hci_create_connection
+},
+{
+"disconnect <connection_handle> <reason>",
+"\nThe Disconnection command is used to terminate an existing connection.\n" \
+"The connection handle command parameter indicates which connection is to\n" \
+"be disconnected. The Reason command parameter indicates the reason for\n" \
+"ending the connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<reason> - dd; reason; usually 19 (0x13) - user ended;\n" \
+"\t also 0x05, 0x13-0x15, 0x1A, 0x29",
+&hci_disconnect
+},
+{
+"add_sco_connection <acl connection handle> <packet type>",
+"This command will cause the link manager to create a SCO connection using\n" \
+"the ACL connection specified by the connection handle command parameter.\n" \
+"The Link Manager will determine how the new connection is established. This\n"\
+"connection is determined by the current state of the device, its piconet,\n" \
+"and the state of the device to be connected. The packet type command parameter\n" \
+"specifies which packet types the Link Manager should use for the connection.\n"\
+"The Link Manager must only use the packet type(s) specified by the packet\n" \
+"type command parameter for sending HCI SCO data packets. Multiple packet\n" \
+"types may be specified for the packet type command parameter by performing\n" \
+"a bitwise OR operation of the different packet types. Note: An SCO connection\n" \
+"can only be created when an ACL connection already exists and when it is\n" \
+"not put in park mode.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n" \
+"\t<packet_type> - xxxx; packet type\n" \
+"" \
+"\t\tSCO packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0020 HV1\n" \
+"\t\t0x0040 HV2\n" \
+"\t\t0x0080 HV3\n",
+&hci_add_sco_connection
+},
+{
+"change_connection_packet_type <connection_hande> <packet_type>",
+"The Change_Connection_Packet_Type command is used to change which packet\n" \
+"types can be used for a connection that is currently established. This\n" \
+"allows current connections to be dynamically modified to support different\n" \
+"types of user data. The Packet_Type command parameter specifies which\n" \
+"packet types the Link Manager can use for the connection. Multiple packet\n" \
+"types may be specified for the Packet_Type command parameter by bitwise OR\n" \
+"operation of the different packet types.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<packet_type> - xxxx; packet type mask\n" \
+"" \
+"\t\tACL packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0008 DM1\n" \
+"\t\t0x0010 DH1\n" \
+"\t\t0x0400 DM3\n" \
+"\t\t0x0800 DH3\n" \
+"\t\t0x4000 DM5\n" \
+"\t\t0x8000 DH5\n\n" \
+"" \
+"\t\tSCO packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0020 HV1\n" \
+"\t\t0x0040 HV2\n" \
+"\t\t0x0080 HV3\n" \
+"",
+&hci_change_connection_packet_type
+},
+{
+"remote_name_request <bdaddr> <ps_rep_mode> <ps_mode> <clock_offset>",
+"\nThe Remote_Name_Request command is used to obtain the user-friendly\n" \
+"name of another Bluetooth unit.\n\n" \
+"\t<bdaddr> - xx:xx:xx:xx:xx:xx remote unit BD_ADDR\n" \
+"\t<ps_rep_mode> - dd; page scan repetition mode [0-2]\n" \
+"\t<ps_mode> - dd; page scan mode [0-3]\n" \
+"\t<clock_offset> - xxxx; clock offset [0 - 0xffff]",
+&hci_remote_name_request
+},
+{
+"read_remote_supported_features <connection_handle>",
+"\nThis command requests a list of the supported features for the remote\n" \
+"unit identified by the connection handle parameter. The connection handle\n" \
+"must be a connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_remote_supported_features
+},
+{
+"read_remote_version_information <connection_handle>",
+"\nThis command will obtain the values for the version information for the\n" \
+"remote Bluetooth unit identified by the connection handle parameter. The\n" \
+"connection handle must be a connection handle for an ACL connection.\n\n" \
+"\t<conneciton_handle> - dddd; connection handle",
+&hci_read_remote_version_information
+},
+{
+"read_clock_offset <connection_handle>",
+"\nThis command allows the Host to read clock offset to remote unit.\n" \
+"\t<conneciton_handle> - dddd; connection handle",
+&hci_read_clock_offset
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/link_policy.c b/usr.sbin/bluetooth/hccontrol/link_policy.c
new file mode 100644
index 0000000..dec9259
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/link_policy.c
@@ -0,0 +1,310 @@
+/*
+ * link_policy.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: link_policy.c,v 1.3 2002/09/17 16:33:44 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <stdio.h>
+#include "hccontrol.h"
+
+/* Send Role Discovery to the unit */
+static int
+hci_role_discovery(int s, int argc, char **argv)
+{
+ ng_hci_role_discovery_cp cp;
+ ng_hci_role_discovery_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_ROLE_DISCOVERY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Role: %s [%#x]\n",
+ (rp.role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", rp.role);
+
+ return (OK);
+} /* hci_role_discovery */
+
+/* Send Swith Role to the unit */
+static int
+hci_switch_role(int s, int argc, char **argv)
+{
+ int n0, n1, n2, n3, n4, n5;
+ char b[512];
+ ng_hci_switch_role_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* bdaddr */
+ if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x",
+ &n5, &n4, &n3, &n2, &n1, &n0) != 6)
+ return (USAGE);
+
+ cp.bdaddr.b[0] = n0 & 0xff;
+ cp.bdaddr.b[1] = n1 & 0xff;
+ cp.bdaddr.b[2] = n2 & 0xff;
+ cp.bdaddr.b[3] = n3 & 0xff;
+ cp.bdaddr.b[4] = n4 & 0xff;
+ cp.bdaddr.b[5] = n5 & 0xff;
+
+ /* role */
+ if (sscanf(argv[1], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.role = n0? NG_HCI_ROLE_SLAVE : NG_HCI_ROLE_MASTER;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_SWITCH_ROLE),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_ROLE_CHANGE) {
+ ng_hci_role_change_ep *ep = (ng_hci_role_change_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
+ ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
+ fprintf(stdout, "Role: %s [%#x]\n",
+ (ep->role == NG_HCI_ROLE_MASTER)? "Master" : "Slave",
+ ep->role);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_switch_role */
+
+/* Send Read_Link_Policy_Settings command to the unit */
+static int
+hci_read_link_policy_settings(int s, int argc, char **argv)
+{
+ ng_hci_read_link_policy_settings_cp cp;
+ ng_hci_read_link_policy_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_READ_LINK_POLICY_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link policy settings: %#x\n", le16toh(rp.settings));
+
+ return (OK);
+} /* hci_read_link_policy_settings */
+
+/* Send Write_Link_Policy_Settings command to the unit */
+static int
+hci_write_link_policy_settings(int s, int argc, char **argv)
+{
+ ng_hci_write_link_policy_settings_cp cp;
+ ng_hci_write_link_policy_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* link policy settings */
+ if (sscanf(argv[1], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.settings = (u_int16_t) (n & 0x0ffff);
+ cp.settings = htole16(cp.settings);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_link_policy_settings */
+
+struct hci_command link_policy_commands[] = {
+{
+"role_discovery <conection_handle>",
+"\nThe Role_Discovery command is used for a Bluetooth device to determine\n" \
+"which role the device is performing for a particular Connection Handle.\n" \
+"The connection handle must be a connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_role_discovery
+},
+{
+"switch_role <bdaddr> <role>",
+"\nThe Switch_Role command is used for a Bluetooth device to switch the\n" \
+"current role the device is performing for a particular connection with\n" \
+"another specified Bluetooth device. The BD_ADDR command parameter indicates\n"\
+"for which connection the role switch is to be performed. The Role indicates\n"\
+"the requested new role that the local device performs. Note: the BD_ADDR\n" \
+"command parameter must specify a Bluetooth device for which a connection\n"
+"already exists.\n\n" \
+"\t<bdaddr> - xx:xx:xx:xx:xx:xx; device bdaddr\n" \
+"\t<role> - dd; role; 0 - Master, 1 - Slave",
+&hci_switch_role
+},
+{
+"read_link_policy_settings <connection_handle>",
+"\nThis command will read the Link Policy setting for the specified connection\n"\
+"handle. The link policy settings parameter determines the behavior of the\n" \
+"local Link Manager when it receives a request from a remote device or it\n" \
+"determines itself to change the master-slave role or to enter the hold,\n" \
+"sniff, or park mode. The local Link Manager will automatically accept or\n" \
+"reject such a request from the remote device, and may even autonomously\n" \
+"request itself, depending on the value of the link policy settings parameter\n"\
+"for the corresponding connection handle. The connection handle must be a\n" \
+"connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_link_policy_settings
+},
+{
+"write_link_policy_settings <connection_handle> <settings>",
+"\nThis command will write the Link Policy setting for the specified connection\n"\
+"handle. The link policy settings parameter determines the behavior of the\n" \
+"local Link Manager when it receives a request from a remote device or it\n" \
+"determines itself to change the master-slave role or to enter the hold,\n" \
+"sniff, or park mode. The local Link Manager will automatically accept or\n" \
+"reject such a request from the remote device, and may even autonomously\n" \
+"request itself, depending on the value of the link policy settings parameter\n"\
+"for the corresponding connection handle. The connection handle must be a\n" \
+"connection handle for an ACL connection. Multiple Link Manager policies may\n"\
+"be specified for the link policy settings parameter by performing a bitwise\n"\
+"OR operation of the different activity types.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<settings> - xxxx; settings\n" \
+"\t\t0x0000 - Disable All LM Modes (Default)\n" \
+"\t\t0x0001 - Enable Master Slave Switch\n" \
+"\t\t0x0002 - Enable Hold Mode\n" \
+"\t\t0x0004 - Enable Sniff Mode\n" \
+"\t\t0x0008 - Enable Park Mode\n",
+&hci_write_link_policy_settings
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/node.c b/usr.sbin/bluetooth/hccontrol/node.c
new file mode 100644
index 0000000..b7ff33a
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/node.c
@@ -0,0 +1,518 @@
+/*
+ * node.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: node.c,v 1.8 2002/11/12 22:33:17 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <bitstring.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Send Read_Node_State command to the node */
+static int
+hci_read_node_state(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_state r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\nState: %#x\n", r.hci_node, r.state);
+
+ return (OK);
+} /* hci_read_node_state */
+
+/* Send Intitialize command to the node */
+static int
+hci_node_initialize(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_init r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_INIT, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_node_initialize */
+
+/* Send Read_Debug_Level command to the node */
+static int
+hci_read_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\nDebug level: %d\n", r.hci_node, r.debug);
+
+ return (OK);
+} /* hci_read_debug_level */
+
+/* Send Write_Debug_Level command to the node */
+static int
+hci_write_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ switch (argc) {
+ case 1:
+ r.debug = atoi(argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_debug_level */
+
+/* Send Read_Node_Buffer_Size command to the node */
+static int
+hci_read_node_buffer_size(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_buffer r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\n",
+ r.hci_node);
+ fprintf(stdout, "Number of free command buffers: %d\n",
+ r.buffer.cmd_free);
+ fprintf(stdout, "Max. ACL packet size: %d\n",
+ r.buffer.acl_size);
+ fprintf(stdout, "Numbef of free ACL buffers: %d\n",
+ r.buffer.acl_free);
+ fprintf(stdout, "Total number of ACL buffers: %d\n",
+ r.buffer.acl_pkts);
+ fprintf(stdout, "Max. SCO packet size: %d\n",
+ r.buffer.sco_size);
+ fprintf(stdout, "Numbef of free SCO buffers: %d\n",
+ r.buffer.sco_free);
+ fprintf(stdout, "Total number of SCO buffers: %d\n",
+ r.buffer.sco_pkts);
+
+ return (OK);
+} /* hci_read_node_buffer_size */
+
+/* Send Read_Node_BD_ADDR command to the node */
+static int
+hci_read_node_bd_addr(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_bdaddr r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\n", r.hci_node);
+ fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ r.bdaddr.b[5], r.bdaddr.b[4], r.bdaddr.b[3],
+ r.bdaddr.b[2], r.bdaddr.b[1], r.bdaddr.b[0]);
+
+ return (OK);
+} /* hci_read_node_bd_addr */
+
+/* Send Read_Node_Features command to the node */
+static int
+hci_read_node_features(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_features r;
+ int n;
+ char buffer[1024];
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\nFeatures: ", r.hci_node);
+ for (n = 0; n < sizeof(r.features)/sizeof(r.features[0]); n++)
+ fprintf(stdout, "%#02x ", r.features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(r.features,
+ buffer, sizeof(buffer)));
+
+ return (OK);
+} /* hci_read_node_features */
+
+/* Send Read_Node_Stat command to the node */
+static int
+hci_read_node_stat(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_stat r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\n", r.hci_node);
+ fprintf(stdout, "Commands sent: %d\n", r.stat.cmd_sent);
+ fprintf(stdout, "Events received: %d\n", r.stat.evnt_recv);
+ fprintf(stdout, "ACL packets received: %d\n", r.stat.acl_recv);
+ fprintf(stdout, "ACL packets sent: %d\n", r.stat.acl_sent);
+ fprintf(stdout, "SCO packets received: %d\n", r.stat.sco_recv);
+ fprintf(stdout, "SCO packets sent: %d\n", r.stat.sco_sent);
+ fprintf(stdout, "Bytes received: %d\n", r.stat.bytes_recv);
+ fprintf(stdout, "Bytes sent: %d\n", r.stat.bytes_sent);
+
+ return (OK);
+} /* hci_read_node_stat */
+
+/* Send Reset_Node_Stat command to the node */
+static int
+hci_reset_node_stat(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_reset_stat r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_RESET_STAT, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_reset_node_stat */
+
+/* Send Flush_Neighbor_Cache command to the node */
+static int
+hci_flush_neighbor_cache(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_flush_neighbor_cache r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE,
+ &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_flush_neighbor_cache */
+
+/* Send Read_Neighbor_Cache command to the node */
+static int
+hci_read_neighbor_cache(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_neighbor_cache r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_entries = NG_HCI_MAX_NEIGHBOR_NUM;
+ r.entries = calloc(NG_HCI_MAX_NEIGHBOR_NUM,
+ sizeof(ng_hci_node_neighbor_cache_entry_ep));
+ if (r.entries == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE, &r,
+ sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "Neighbor cache for the node: %s\n", r.hci_node);
+ fprintf(stdout,
+"BD_ADDR " \
+"Features " \
+"Clock offset " \
+"Page scan " \
+"Rep. scan\n");
+
+ for (n = 0; n < r.num_entries; n++) {
+ fprintf(stdout,
+"%02x:%02x:%02x:%02x:%02x:%02x " \
+"%02x %02x %02x %02x %02x %02x %02x %02x " \
+"%#12x " \
+"%#9x " \
+"%#9x\n",
+ r.entries[n].bdaddr.b[5], r.entries[n].bdaddr.b[4],
+ r.entries[n].bdaddr.b[3], r.entries[n].bdaddr.b[2],
+ r.entries[n].bdaddr.b[1], r.entries[n].bdaddr.b[0],
+ r.entries[n].features[0], r.entries[n].features[1],
+ r.entries[n].features[2], r.entries[n].features[3],
+ r.entries[n].features[4], r.entries[n].features[5],
+ r.entries[n].features[6], r.entries[n].features[7],
+ r.entries[n].clock_offset, r.entries[n].page_scan_mode,
+ r.entries[n].page_scan_rep_mode);
+ }
+out:
+ free(r.entries);
+
+ return (error);
+} /* hci_read_neightbor_cache */
+
+/* Send Read_Connection_List command to the node */
+static int
+hci_read_connection_list(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_con_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_connections = NG_HCI_MAX_CON_NUM;
+ r.connections = calloc(NG_HCI_MAX_CON_NUM, sizeof(ng_hci_node_con_ep));
+ if (r.connections == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "Connections list for the node: %s\n", r.hci_node);
+ fprintf(stdout,
+"Remote BD_ADDR " \
+"Handle " \
+"Type " \
+"Mode " \
+"Role " \
+"Encrypt " \
+"Pending " \
+"Queue " \
+"State\n");
+
+ for (n = 0; n < r.num_connections; n++) {
+ fprintf(stdout,
+"%02x:%02x:%02x:%02x:%02x:%02x " \
+"%6d " \
+"%4.4s " \
+"%4d " \
+"%4.4s " \
+"%7.7s " \
+"%7d " \
+"%5d " \
+"%s\n",
+ r.connections[n].bdaddr.b[5],
+ r.connections[n].bdaddr.b[4],
+ r.connections[n].bdaddr.b[3],
+ r.connections[n].bdaddr.b[2],
+ r.connections[n].bdaddr.b[1],
+ r.connections[n].bdaddr.b[0],
+ r.connections[n].con_handle,
+ (r.connections[n].link_type == NG_HCI_LINK_ACL)?
+ "ACL" : "SCO",
+ r.connections[n].mode,
+ (r.connections[n].role == NG_HCI_ROLE_MASTER)?
+ "MAST" : "SLAV",
+ hci_encrypt2str(r.connections[n].encryption_mode, 1),
+ r.connections[n].pending,
+ r.connections[n].queue_len,
+ hci_con_state2str(r.connections[n].state));
+ }
+out:
+ free(r.connections);
+
+ return (error);
+} /* hci_read_connection_list */
+
+/* Send Read_Link_Policy_Settings_Mask command to the node */
+int
+hci_read_link_policy_settings_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_link_policy_mask r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\nLink Policy Settings mask: %#04x\n",
+ r.hci_node, r.policy_mask);
+
+ return (OK);
+} /* hci_read_link_policy_settings_mask */
+
+/* Send Write_Link_Policy_Settings_Mask command to the node */
+int
+hci_write_link_policy_settings_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_link_policy_mask r;
+ int m;
+
+ memset(&r, 0, sizeof(r));
+
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &m) != 1)
+ return (USAGE);
+
+ r.policy_mask = (m & 0xffff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_link_policy_settings_mask */
+
+/* Send Read_Packet_Mask command to the node */
+int
+hci_read_packet_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_packet_mask r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Node: %s\nPacket mask: %#04x\n",
+ r.hci_node, r.packet_mask);
+
+ return (OK);
+} /* hci_read_packet_mask */
+
+/* Send Write_Packet_Mask command to the node */
+int
+hci_write_packet_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_packet_mask r;
+ int m;
+
+ memset(&r, 0, sizeof(r));
+
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &m) != 1)
+ return (USAGE);
+
+ r.packet_mask = (m & 0xffff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_PACKET_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_packet_mask */
+
+struct hci_command node_commands[] = {
+{
+"read_node_state",
+"Get HCI node state",
+&hci_read_node_state
+},
+{
+"initialize",
+"Initialize HCI node",
+&hci_node_initialize
+},
+{
+"read_debug_level",
+"Read HCI node debug level",
+&hci_read_debug_level
+},
+{
+"write_debug_level <level>",
+"Write HCI node debug level",
+&hci_write_debug_level
+},
+{
+"read_node_buffer_size",
+"Read HCI node buffer information",
+&hci_read_node_buffer_size
+},
+{
+"read_node_bd_addr",
+"Read HCI node BD_ADDR",
+&hci_read_node_bd_addr
+},
+{
+"read_node_features",
+"Read HCI node features",
+&hci_read_node_features
+},
+{
+"read_node_stat",
+"Read HCI node statistic information",
+&hci_read_node_stat
+},
+{
+"reset_node_stat",
+"Reset HCI node statistic information",
+&hci_reset_node_stat
+},
+{
+"flush_neighbor_cache",
+"Flush HCI node neighbor cache",
+&hci_flush_neighbor_cache
+},
+{
+"read_neighbor_cache",
+"Read HCI node neighbor cache",
+&hci_read_neighbor_cache
+},
+{
+"read_connection_list",
+"Read connection list",
+&hci_read_connection_list
+},
+{
+"read_node_link_policy_settings_mask",
+"Read Link Policy Settinngs mask for the node",
+&hci_read_link_policy_settings_mask
+},
+{
+"write_node_link_policy_settings_mask <policy_mask>",
+"Write Link Policy Settinngs mask for the node. Policy mask - xxxx",
+&hci_write_link_policy_settings_mask
+},
+{
+"read_node_packet_mask",
+"Read Packet mask for the node",
+&hci_read_packet_mask
+},
+{
+"write_node_packet_mask <packet_mask>",
+"Write Packet mask for the node. Packet mask - xxxx",
+&hci_write_packet_mask
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/send_recv.c b/usr.sbin/bluetooth/hccontrol/send_recv.c
new file mode 100644
index 0000000..aff6f93
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/send_recv.c
@@ -0,0 +1,184 @@
+/*
+ * send_recv.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: send_recv.c,v 1.4 2002/09/04 21:31:30 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/endian.h>
+#include <assert.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <string.h>
+#include <unistd.h>
+#include "hccontrol.h"
+
+/* Send HCI request to the unit */
+int
+hci_request(int s, int opcode, char const *cp, int cp_size, char *rp, int *rp_size)
+{
+ char buffer[512];
+ int n;
+ ng_hci_cmd_pkt_t *c = (ng_hci_cmd_pkt_t *) buffer;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) buffer;
+
+ assert(rp != NULL);
+ assert(*rp_size != NULL);
+ assert(*rp_size > 0);
+
+ c->type = NG_HCI_CMD_PKT;
+ c->opcode = (u_int16_t) opcode;
+ c->opcode = htole16(c->opcode);
+
+ if (cp != NULL) {
+ assert(0 < cp_size && cp_size <= NG_HCI_CMD_PKT_SIZE);
+
+ c->length = (u_int8_t) cp_size;
+ memcpy(buffer + sizeof(*c), cp, cp_size);
+ } else
+ c->length = 0;
+
+ if (hci_send(s, buffer, sizeof(*c) + cp_size) == ERROR)
+ return (ERROR);
+
+again:
+ n = sizeof(buffer);
+ if (hci_recv(s, buffer, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EMSGSIZE;
+ return (ERROR);
+ }
+
+ if (e->type != NG_HCI_EVENT_PKT) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ switch (e->event) {
+ case NG_HCI_EVENT_COMMAND_COMPL: {
+ ng_hci_command_compl_ep *cc =
+ (ng_hci_command_compl_ep *)(e + 1);
+
+ cc->opcode = le16toh(cc->opcode);
+
+ if (cc->opcode == 0x0000 || cc->opcode != opcode)
+ goto again;
+
+ n -= (sizeof(*e) + sizeof(*cc));
+ if (n < *rp_size)
+ *rp_size = n;
+
+ memcpy(rp, buffer + sizeof(*e) + sizeof(*cc), *rp_size);
+ } break;
+
+ case NG_HCI_EVENT_COMMAND_STATUS: {
+ ng_hci_command_status_ep *cs =
+ (ng_hci_command_status_ep *)(e + 1);
+
+ cs->opcode = le16toh(cs->opcode);
+
+ if (cs->opcode == 0x0000 || cs->opcode != opcode)
+ goto again;
+
+ *rp_size = 1;
+ *rp = cs->status;
+ } break;
+
+ default:
+ goto again;
+ }
+
+ return (OK);
+} /* hci_request */
+
+/* Send simple HCI request - Just HCI command packet (no parameters) */
+int
+hci_simple_request(int s, int opcode, char *rp, int *rp_size)
+{
+ return (hci_request(s, opcode, NULL, 0, rp, rp_size));
+} /* hci_simple_request */
+
+/* Send HCI data to the unit */
+int
+hci_send(int s, char const *buffer, int size)
+{
+ assert(buffer != NULL);
+ assert(size >= sizeof(ng_hci_cmd_pkt_t));
+ assert(size <= sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE);
+
+ if (send(s, buffer, size, 0) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_send */
+
+/* Receive HCI data from the unit */
+int
+hci_recv(int s, char *buffer, int *size)
+{
+ struct timeval tv;
+ fd_set rfd;
+ int n;
+
+ assert(buffer != NULL);
+ assert(size != NULL);
+ assert(*size > sizeof(ng_hci_event_pkt_t));
+
+again:
+ FD_ZERO(&rfd);
+ FD_SET(s, &rfd);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ n = select(s + 1, &rfd, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ } else
+ errno = ETIMEDOUT;
+
+ return (ERROR);
+ }
+
+ assert(FD_ISSET(s, &rfd));
+
+ n = recv(s, buffer, *size, 0);
+ if (n < 0)
+ return (ERROR);
+
+ *size = n;
+
+ return (OK);
+} /* hci_recv */
+
diff --git a/usr.sbin/bluetooth/hccontrol/status.c b/usr.sbin/bluetooth/hccontrol/status.c
new file mode 100644
index 0000000..c857349
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/status.c
@@ -0,0 +1,245 @@
+/*
+ * status.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: status.c,v 1.2 2002/09/06 18:52:41 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <stdio.h>
+#include "hccontrol.h"
+
+/* Send Read_Failed_Contact_Counter command to the unit */
+static int
+hci_read_failed_contact_counter(int s, int argc, char **argv)
+{
+ ng_hci_read_failed_contact_cntr_cp cp;
+ ng_hci_read_failed_contact_cntr_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_READ_FAILED_CONTACT_CNTR),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Failed contact counter: %d\n", le16toh(rp.counter));
+
+ return (OK);
+} /* hci_read_failed_contact_counter */
+
+/* Send Reset_Failed_Contact_Counter command to the unit */
+static int
+hci_reset_failed_contact_counter(int s, int argc, char **argv)
+{
+ ng_hci_reset_failed_contact_cntr_cp cp;
+ ng_hci_reset_failed_contact_cntr_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_reset_failed_contact_counter */
+
+/* Sent Get_Link_Quality command to the unit */
+static int
+hci_get_link_quality(int s, int argc, char **argv)
+{
+ ng_hci_get_link_quality_cp cp;
+ ng_hci_get_link_quality_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_GET_LINK_QUALITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link quality: %d\n", le16toh(rp.quality));
+
+ return (OK);
+} /* hci_get_link_quality */
+
+/* Send Read_RSSI command to the unit */
+static int
+hci_read_rssi(int s, int argc, char **argv)
+{
+ ng_hci_read_rssi_cp cp;
+ ng_hci_read_rssi_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (u_int16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_READ_RSSI),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "RSSI: %d dB\n", (int) rp.rssi);
+
+ return (OK);
+} /* hci_read_rssi */
+
+struct hci_command status_commands[] = {
+{
+"read_failed_contact_counter <connection_handle>",
+"\nThis command will read the value for the Failed_Contact_Counter\n" \
+"parameter for a particular ACL connection to another device.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_read_failed_contact_counter
+},
+{
+"reset_failed_contact_counter <connection_handle>",
+"\nThis command will reset the value for the Failed_Contact_Counter\n" \
+"parameter for a particular ACL connection to another device.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_reset_failed_contact_counter
+},
+{
+"get_link_quality <connection_handle>",
+"\nThis command will return the value for the Link_Quality for the\n" \
+"specified ACL connection handle. This command will return a Link_Quality\n" \
+"value from 0-255, which represents the quality of the link between two\n" \
+"Bluetooth devices. The higher the value, the better the link quality is.\n" \
+"Each Bluetooth module vendor will determine how to measure the link quality." \
+"\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_get_link_quality
+},
+{
+"read_rssi <connection_handle>",
+"\nThis command will read the value for the difference between the\n" \
+"measured Received Signal Strength Indication (RSSI) and the limits of\n" \
+"the Golden Receive Power Range for a ACL connection handle to another\n" \
+"Bluetooth device. Any positive RSSI value returned by the Host Controller\n" \
+"indicates how many dB the RSSI is above the upper limit, any negative\n" \
+"value indicates how many dB the RSSI is below the lower limit. The value\n" \
+"zero indicates that the RSSI is inside the Golden Receive Power Range.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_read_rssi
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/util.c b/usr.sbin/bluetooth/hccontrol/util.c
new file mode 100644
index 0000000..51868b4
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/util.c
@@ -0,0 +1,350 @@
+/*
+ * util.c
+ *
+ * Copyright (c) 2001 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: util.c,v 1.2 2002/09/12 18:19:43 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#define SIZE(x) (sizeof((x))/sizeof((x)[0]))
+
+char const * const
+hci_link2str(int link_type)
+{
+ static char const * const t[] = {
+ /* NG_HCI_LINK_SCO */ "SCO",
+ /* NG_HCI_LINK_ACL */ "ACL"
+ };
+
+ return (link_type >= SIZE(t)? "?" : t[link_type]);
+} /* hci_link2str */
+
+char const * const
+hci_pin2str(int type)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Variable PIN",
+ /* 0x01 */ "Fixed PIN"
+ };
+
+ return (type >= SIZE(t)? "?" : t[type]);
+} /* hci_pin2str */
+
+char const * const
+hci_scan2str(int scan)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "No Scan enabled",
+ /* 0x01 */ "Inquiry Scan enabled. Page Scan disabled",
+ /* 0x02 */ "Inquiry Scan disabled. Page Scan enabled",
+ /* 0x03 */ "Inquiry Scan enabled. Page Scan enabled"
+ };
+
+ return (scan >= SIZE(t)? "?" : t[scan]);
+} /* hci_scan2str */
+
+char const * const
+hci_encrypt2str(int encrypt, int brief)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Disabled",
+ /* 0x01 */ "Only for point-to-point packets",
+ /* 0x02 */ "Both point-to-point and broadcast packets"
+ };
+
+ static char const * const t1[] = {
+ /* NG_HCI_ENCRYPTION_MODE_NONE */ "NONE",
+ /* NG_HCI_ENCRYPTION_MODE_P2P */ "P2P",
+ /* NG_HCI_ENCRYPTION_MODE_ALL */ "ALL",
+ };
+
+ if (brief)
+ return (encrypt >= SIZE(t1)? "?" : t1[encrypt]);
+
+ return (encrypt >= SIZE(t)? "?" : t[encrypt]);
+} /* hci_encrypt2str */
+
+char const * const
+hci_coding2str(int coding)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Linear",
+ /* 0x01 */ "u-law",
+ /* 0x02 */ "A-law",
+ /* 0x03 */ "Reserved"
+ };
+
+ return (coding >= SIZE(t)? "?" : t[coding]);
+} /* hci_coding2str */
+
+char const * const
+hci_vdata2str(int data)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "1's complement",
+ /* 0x01 */ "2's complement",
+ /* 0x02 */ "Sign-Magnitude",
+ /* 0x03 */ "Reserved"
+ };
+
+ return (data >= SIZE(t)? "?" : t[data]);
+} /* hci_vdata2str */
+
+char const * const
+hci_hmode2str(int mode, char *buffer, int size)
+{
+ static char const * const t[] = {
+ /* 0x01 */ "Suspend Page Scan ",
+ /* 0x02 */ "Suspend Inquiry Scan ",
+ /* 0x04 */ "Suspend Periodic Inquiries "
+ };
+
+ if (buffer != NULL && size > 0) {
+ int n;
+
+ memset(buffer, 0, size);
+ for (n = 0; n < SIZE(t); n++) {
+ int len = strlen(buffer);
+
+ if (len >= size)
+ break;
+ if (mode & (1 << n))
+ strncat(buffer, t[n], size - len);
+ }
+ }
+
+ return (buffer);
+} /* hci_hmode2str */
+
+char const * const
+hci_ver2str(int ver)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "v1.0B",
+ /* 0x01 */ "v1.1"
+ };
+
+ return (ver >= SIZE(t)? "?" : t[ver]);
+} /* hci_ver2str */
+
+char const * const
+hci_manufacturer2str(int m)
+{
+ static char const * const t[] = {
+ /* 0000 */ "Ericsson Mobile Comunications",
+ /* 0001 */ "Nokia Mobile Phones",
+ /* 0002 */ "Intel Corp.",
+ /* 0003 */ "IBM Corp.",
+ /* 0004 */ "Toshiba Corp.",
+ /* 0005 */ "3Com",
+ /* 0006 */ "Microsoft",
+ /* 0007 */ "Lucent",
+ /* 0008 */ "Motorola",
+ /* 0009 */ "Infineon Technologies AG",
+ /* 0010 */ "Cambridge Silicon Radio",
+ /* 0011 */ "Silicon Wave",
+ /* 0012 */ "Digianswer A/S",
+ /* 0013 */ "Texas Instruments Inc.",
+ /* 0014 */ "Parthus Technologies Inc.",
+ /* 0015 */ "Broadcom Corporation",
+ /* 0016 */ "Mitel Semiconductor",
+ /* 0017 */ "Widcomm, Inc.",
+ /* 0018 */ "Telencomm Inc.",
+ /* 0019 */ "Atmel Corporation",
+ /* 0020 */ "Mitsubishi Electric Corporation",
+ /* 0021 */ "RTX Telecom A/S",
+ /* 0022 */ "KC Technology Inc.",
+ /* 0023 */ "Newlogic",
+ /* 0024 */ "Transilica, Inc.",
+ /* 0025 */ "Rohde & Schwartz GmbH & Co. KG",
+ /* 0026 */ "TTPCom Limited",
+ /* 0027 */ "Signia Technologies, Inc.",
+ /* 0028 */ "Conexant Systems Inc.",
+ /* 0029 */ "Qualcomm",
+ /* 0030 */ "Inventel",
+ /* 0031 */ "AVM Berlin",
+ /* 0032 */ "BandSpeed, Inc.",
+ /* 0033 */ "Mansella Ltd",
+ /* 0034 */ "NEC Corporation",
+ /* 0035 */ "WavePlus Technology Co., Ltd.",
+ /* 0036 */ "Alcatel",
+ /* 0037 */ "Philips Semiconductors",
+ /* 0038 */ "C Technologies",
+ /* 0039 */ "Open Interface",
+ /* 0040 */ "R F Micro Devices",
+ /* 0041 */ "Hitachi Ltd",
+ /* 0042 */ "Symbol Technologies, Inc.",
+ /* 0043 */ "Tenovis",
+ /* 0044 */ "Macronix International Co. Ltd.",
+ /* 0045 */ "GCT Semiconductor",
+ /* 0046 */ "Norwood Systems",
+ /* 0047 */ "MewTel Technology Inc."
+ };
+
+ return (m >= SIZE(t)? "?" : t[m]);
+} /* hci_manufacturer2str */
+
+char const * const
+hci_features2str(u_int8_t *features, char *buffer, int size)
+{
+ static char const * const t[][8] = {
+ { /* byte 0 */
+ /* 0 */ "<3-Slot> ",
+ /* 1 */ "<5-Slot> ",
+ /* 2 */ "<Encryption> ",
+ /* 3 */ "<Slot offset> ",
+ /* 4 */ "<Timing accuracy> ",
+ /* 5 */ "<Switch> ",
+ /* 6 */ "<Hold mode> ",
+ /* 7 */ "<Sniff mode> "
+ },
+ { /* byte 1 */
+ /* 0 */ "<Park mode> ",
+ /* 1 */ "<RSSI> ",
+ /* 2 */ "<Channel quality> ",
+ /* 3 */ "<SCO link> ",
+ /* 4 */ "<HV2 packets> ",
+ /* 5 */ "<HV3 packets> ",
+ /* 6 */ "<u-law log> ",
+ /* 7 */ "<A-law log> "
+ },
+ { /* byte 2 */
+ /* 0 */ "<CVSD> ",
+ /* 1 */ "<Paging scheme> ",
+ /* 2 */ "<Power control> ",
+ /* 3 */ "<Transparent SCO data> ",
+ /* 4 */ "<Flow control lag (bit0)> ",
+ /* 5 */ "<Flow control lag (bit1)> ",
+ /* 6 */ "<Flow control lag (bit2)> ",
+ /* 7 */ "<Unknown2.7> "
+ }};
+
+ if (buffer != NULL && size > 0) {
+ int n, i, len0, len1;
+
+ memset(buffer, 0, size);
+ len1 = 0;
+
+ for (n = 0; n < SIZE(t); n++) {
+ for (i = 0; i < SIZE(t[n]); i++) {
+ len0 = strlen(buffer);
+ if (len0 >= size)
+ goto done;
+
+ if (features[n] & (1 << i)) {
+ if (len1 + strlen(t[n][i]) > 60) {
+ len1 = 0;
+ buffer[len0 - 1] = '\n';
+ }
+
+ len1 += strlen(t[n][i]);
+ strncat(buffer, t[n][i], size - len0);
+ }
+ }
+ }
+ }
+done:
+ return (buffer);
+} /* hci_features2str */
+
+char const * const
+hci_cc2str(int cc)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "North America, Europe, Japan",
+ /* 0x01 */ "France"
+ };
+
+ return (cc >= SIZE(t)? "?" : t[cc]);
+} /* hci_cc2str */
+
+char const * const
+hci_con_state2str(int state)
+{
+ static char const * const t[] = {
+ /* NG_HCI_CON_CLOSED */ "CLOSED",
+ /* NG_HCI_CON_W4_LP_CON_RSP */ "W4_LP_CON_RSP",
+ /* NG_HCI_CON_W4_CONN_COMPLETE */ "W4_CONN_COMPLETE",
+ /* NG_HCI_CON_OPEN */ "OPEN"
+ };
+
+ return (state >= SIZE(t)? "UNKNOWN" : t[state]);
+} /* hci_con_state2str */
+
+char const * const
+hci_status2str(int status)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "No error",
+ /* 0x01 */ "Unknown HCI command",
+ /* 0x02 */ "No connection",
+ /* 0x03 */ "Hardware failure",
+ /* 0x04 */ "Page timeout",
+ /* 0x05 */ "Authentication failure",
+ /* 0x06 */ "Key missing",
+ /* 0x07 */ "Memory full",
+ /* 0x08 */ "Connection timeout",
+ /* 0x09 */ "Max number of connections",
+ /* 0x0a */ "Max number of SCO connections to a unit",
+ /* 0x0b */ "ACL connection already exists",
+ /* 0x0c */ "Command disallowed",
+ /* 0x0d */ "Host rejected due to limited resources",
+ /* 0x0e */ "Host rejected due to securiity reasons",
+ /* 0x0f */ "Host rejected due to remote unit is a personal unit",
+ /* 0x10 */ "Host timeout",
+ /* 0x11 */ "Unsupported feature or parameter value",
+ /* 0x12 */ "Invalid HCI command parameter",
+ /* 0x13 */ "Other end terminated connection: User ended connection",
+ /* 0x14 */ "Other end terminated connection: Low resources",
+ /* 0x15 */ "Other end terminated connection: About to power off",
+ /* 0x16 */ "Connection terminated by local host",
+ /* 0x17 */ "Repeated attempts",
+ /* 0x18 */ "Pairing not allowed",
+ /* 0x19 */ "Unknown LMP PDU",
+ /* 0x1a */ "Unsupported remote feature",
+ /* 0x1b */ "SCO offset rejected",
+ /* 0x1c */ "SCO interval rejected",
+ /* 0x1d */ "SCO air mode rejected",
+ /* 0x1e */ "Invalid LMP parameters",
+ /* 0x1f */ "Unspecified error",
+ /* 0x20 */ "Unsupported LMP parameter value",
+ /* 0x21 */ "Role change not allowed",
+ /* 0x22 */ "LMP response timeout",
+ /* 0x23 */ "LMP error transaction collision",
+ /* 0x24 */ "LMP PSU not allowed",
+ /* 0x25 */ "Encryption mode not acceptable",
+ /* 0x26 */ "Unit key used",
+ /* 0x27 */ "QoS is not supported",
+ /* 0x28 */ "Instant passed",
+ /* 0x29 */ "Paring with unit key not supported"
+ };
+
+ return (status >= SIZE(t)? "Unknown error" : t[status]);
+} /* hci_status2str */
+
diff --git a/usr.sbin/bluetooth/hcseriald/Makefile b/usr.sbin/bluetooth/hcseriald/Makefile
new file mode 100644
index 0000000..4153cd6
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.4 2002/09/04 21:29:58 max Exp $
+# $FreeBSD$
+
+DESTDIR= /usr/sbin/
+MANDIR= ../share/man/man
+
+PROG= hcseriald
+MAN8= hcseriald.8
+
+WARNS?= 2
+CFLAGS+= -Wall -O2 -I../../../sys/netgraph/bluetooth/include
+SRCS= hcseriald.c
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.8 b/usr.sbin/bluetooth/hcseriald/hcseriald.8
new file mode 100644
index 0000000..d4b641d
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/hcseriald.8
@@ -0,0 +1,81 @@
+.\" hcseriald.8
+.\"
+.\" 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: hcseriald.8,v 1.3 2002/11/09 19:16:50 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt HCSERIALD 8
+.Os
+.Sh NAME
+.Nm hcseriald
+.Nd supervise serial Bluetooth devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Op Fl n Ar node name
+.Op Fl s Ar speed
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+handles serial Bluetooth devices. It does one simple thing. It opens
+specified serial device, sets device parameters and pushes
+.Em H4
+line discipline.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar device
+Callout device name. Example:
+.Fl f
+.Pa /dev/cuaa0 .
+.It Fl n Ar node name
+Set H4 Netgraph node name. Example:
+.Fl n Ar sio0 .
+.It Fl s Ar speed
+Set serial device speed to
+.Em speed .
+Example:
+.Fl s Ar 115200 .
+.It Fl d
+Do not disassociate from the controlling terminal, i.e. run in foreground.
+.El
+.Sh FILES
+.Bl -tag -width /dev/consolectl -compact
+.It Pa /var/run/hcserial.*.pid
+process id of the currently running
+.Nm
+daemon. Where
+.Dq *
+is a H4 Netgraph node name.
+.El
+.Sh SEE ALSO
+.Xr tty 4 ,
+.Xr ng_h4 4 ,
+.Xr ng_hci 4 ,
+.Xr hccontrol 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.c b/usr.sbin/bluetooth/hcseriald/hcseriald.c
new file mode 100644
index 0000000..bb0e819
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/hcseriald.c
@@ -0,0 +1,279 @@
+/*
+ * hcseriald.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: hcseriald.c,v 1.4 2002/09/04 21:29:58 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph.h>
+#include <ng_h4.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* Prototypes */
+static int open_device (char const *, speed_t, char const *);
+static void sighandler (int);
+static void usage ();
+
+static char const * const hcseriald = "hcseriald";
+static int done = 0;
+
+int
+main(int argc, char *argv[])
+{
+ char *device = NULL, *name = NULL;
+ speed_t speed = 115200;
+ int n, detach = 1;
+ char p[FILENAME_MAX];
+ FILE *f = NULL;
+ struct sigaction sa;
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "df:n:s:")) != -1) {
+ switch (n) {
+ case 'd':
+ detach = 0;
+ break;
+
+ case 'f':
+ device = optarg;
+ break;
+
+ case 'n':
+ name = optarg;
+ break;
+
+ case 's':
+ speed = atoi(optarg);
+ if (speed < 0)
+ usage(argv[0]);
+ break;
+
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (device == NULL || name == NULL)
+ usage(argv[0]);
+
+ openlog(hcseriald, LOG_PID | LOG_NDELAY, LOG_USER);
+
+ /* Open device */
+ n = open_device(device, speed, name);
+
+ if (detach) {
+ pid_t pid = fork();
+
+ if (pid == (pid_t) -1) {
+ syslog(LOG_ERR, "Could not fork(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (pid != 0)
+ exit(0);
+
+ if (daemon(0, 0) < 0) {
+ syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ }
+
+ /* Write PID file */
+ snprintf(p, sizeof(p), "/var/run/%s.%s.pid", hcseriald, name);
+ f = fopen(p, "w");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Could not fopen(%s). %s (%d)",
+ p, strerror(errno), errno);
+ exit(1);
+ }
+ fprintf(f, "%d", getpid());
+ fclose(f);
+
+ /* Install signal handler */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighandler;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGINT, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Keep running */
+ while (!done)
+ select(0, NULL, NULL, NULL, NULL);
+
+ /* Remove PID file and close device */
+ unlink(p);
+ close(n);
+ closelog();
+
+ return (0);
+} /* main */
+
+/* Open terminal, set settings, push H4 line discipline and set node name */
+static int
+open_device(char const *device, speed_t speed, char const *name)
+{
+ int fd, disc, cs, ds;
+ struct termios t;
+ struct nodeinfo ni;
+ struct ngm_name n;
+ char p[NG_NODELEN + 1];
+
+ /* Open terminal device and setup H4 line discipline */
+ fd = open(device, O_RDWR|O_NOCTTY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcgetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ cfmakeraw(&t);
+
+ t.c_cflag |= CLOCAL; /* clocal */
+ t.c_cflag &= ~CSIZE; /* cs8 */
+ t.c_cflag |= CS8; /* cs8 */
+ t.c_cflag &= ~PARENB; /* -parenb */
+ t.c_cflag &= ~CSTOPB; /* -cstopb */
+ t.c_cflag |= CRTSCTS; /* crtscts */
+
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (cfsetspeed(&t, speed) < 0) {
+ syslog(LOG_ERR, "Could not cfsetspeed(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ disc = H4DISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ syslog(LOG_ERR, "Could not ioctl(%s, TIOCSETD, %d). %s (%d)",
+ device, disc, strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Get default name of the Netgraph node */
+ memset(&ni, 0, sizeof(ni));
+ if (ioctl(fd, NGIOCGINFO, &ni) < 0) {
+ syslog(LOG_ERR, "Could not ioctl(%d, NGIOGINFO). %s (%d)",
+ fd, strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Assign new name to the Netgraph node */
+ snprintf(p, sizeof(p), "%s:", ni.name);
+ snprintf(n.name, sizeof(n.name), "%s", name);
+
+ if (NgMkSockNode(NULL, &cs, &ds) < 0) {
+ syslog(LOG_ERR, "Could not NgMkSockNode(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (NgSendMsg(cs, p, NGM_GENERIC_COOKIE, NGM_NAME, &n, sizeof(n)) < 0) {
+ syslog(LOG_ERR, "Could not NgSendMsg(%d, %s, NGM_NAME, %s). " \
+ "%s (%d)", cs, p, n.name, strerror(errno), errno);
+ exit(1);
+ }
+
+ close(cs);
+ close(ds);
+
+ return (fd);
+} /* open_device */
+
+/* Signal handler */
+static void
+sighandler(int s)
+{
+ done = 1;
+} /* sighandler */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s -f device -n node_name [-s speed -d]\n" \
+ "Where:\n" \
+ "\t-f device tty device name, ex. /dev/cuaa1\n" \
+ "\t-n node_name set Netgraph node name to node_name\n" \
+ "\t-s speed set tty speed, ex. 115200\n" \
+ "\t-d run in foreground\n",
+ hcseriald);
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/l2control/Makefile b/usr.sbin/bluetooth/l2control/Makefile
new file mode 100644
index 0000000..b098874
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.3 2002/09/04 21:30:40 max Exp $
+# $FreeBSD$
+
+DESTDIR= /usr/sbin/
+MANDIR= ../share/man/man
+PROG= l2control
+MAN8= l2control.8
+WARNS?= 2
+CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include
+SRCS= l2cap.c l2control.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/l2control/l2cap.c b/usr.sbin/bluetooth/l2control/l2cap.c
new file mode 100644
index 0000000..a0f2d24
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2cap.c
@@ -0,0 +1,256 @@
+/*
+ * l2cap.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: l2cap.c,v 1.6 2002/09/04 21:30:40 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <bitstring.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "l2control.h"
+
+#define SIZE(x) (sizeof((x))/sizeof((x)[0]))
+
+/* Send read_node_flags command to the node */
+static int
+l2cap_read_node_flags(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_flags r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n",
+ r.src.b[5], r.src.b[4], r.src.b[3],
+ r.src.b[2], r.src.b[1], r.src.b[0]);
+ fprintf(stdout, "Connectionless traffic flags:\n");
+ fprintf(stdout, "\tSDP: %s\n",
+ (r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled");
+ fprintf(stdout, "\tRFCOMM: %s\n",
+ (r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled");
+ fprintf(stdout, "\tTCP: %s\n",
+ (r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled");
+
+ return (OK);
+} /* l2cap_read_node_flags */
+
+/* Send read_debug_level command to the node */
+static int
+l2cap_read_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n",
+ r.src.b[5], r.src.b[4], r.src.b[3],
+ r.src.b[2], r.src.b[1], r.src.b[0]);
+ fprintf(stdout, "Debug level: %d\n", r.debug);
+
+ return (OK);
+} /* l2cap_read_debug_level */
+
+/* Send write_debug_level command to the node */
+static int
+l2cap_write_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ switch (argc) {
+ case 1:
+ r.debug = atoi(argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* l2cap_write_debug_level */
+
+/* Send read_connection_list command to the node */
+static int
+l2cap_read_connection_list(int s, int argc, char **argv)
+{
+ static char const * const state[] = {
+ /* NG_L2CAP_CON_CLOSED */ "CLOSED",
+ /* NG_L2CAP_W4_LP_CON_CFM */ "W4_LP_CON_CFM",
+ /* NG_L2CAP_CON_OPEN */ "OPEN"
+ };
+#define con_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
+
+ struct ng_btsocket_l2cap_raw_con_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_connections = NG_L2CAP_MAX_CON_NUM;
+ r.connections = calloc(NG_L2CAP_MAX_CON_NUM,
+ sizeof(ng_l2cap_node_con_ep));
+ if (r.connections == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n",
+ r.src.b[5], r.src.b[4], r.src.b[3],
+ r.src.b[2], r.src.b[1], r.src.b[0]);
+ fprintf(stdout, "L2CAP connections:\n");
+ fprintf(stdout,
+"Remote BD_ADDR Handle Flags Pending State\n");
+ for (n = 0; n < r.num_connections; n++) {
+ fprintf(stdout,
+ "%02x:%02x:%02x:%02x:%02x:%02x " \
+ " %5d " \
+ "%2.2s %2.2s " \
+ "%7d " \
+ "%s\n",
+ r.connections[n].remote.b[5],
+ r.connections[n].remote.b[4],
+ r.connections[n].remote.b[3],
+ r.connections[n].remote.b[2],
+ r.connections[n].remote.b[1],
+ r.connections[n].remote.b[0],
+ r.connections[n].con_handle,
+ ((r.connections[n].flags & NG_L2CAP_CON_TX)? "TX" : ""),
+ ((r.connections[n].flags & NG_L2CAP_CON_RX)? "RX" : ""),
+ r.connections[n].pending,
+ con_state2str(r.connections[n].state));
+ }
+out:
+ free(r.connections);
+
+ return (error);
+} /* l2cap_read_connection_list */
+
+/* Send read_channel_list command to the node */
+static int
+l2cap_read_channel_list(int s, int argc, char **argv)
+{
+ static char const * const state[] = {
+ /* NG_L2CAP_CLOSED */ "CLOSED",
+ /* NG_L2CAP_W4_L2CAP_CON_RSP */ "W4_L2CAP_CON_RSP",
+ /* NG_L2CAP_W4_L2CA_CON_RSP */ "W4_L2CA_CON_RSP",
+ /* NG_L2CAP_CONFIG */ "CONFIG",
+ /* NG_L2CAP_OPEN */ "OPEN",
+ /* NG_L2CAP_W4_L2CAP_DISCON_RSP */ "W4_L2CAP_DISCON_RSP",
+ /* NG_L2CAP_W4_L2CA_DISCON_RSP */ "W4_L2CA_DISCON_RSP"
+ };
+#define ch_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
+
+ struct ng_btsocket_l2cap_raw_chan_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_channels = NG_L2CAP_MAX_CHAN_NUM;
+ r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM,
+ sizeof(ng_l2cap_node_chan_ep));
+ if (r.channels == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n",
+ r.src.b[5], r.src.b[4], r.src.b[3],
+ r.src.b[2], r.src.b[1], r.src.b[0]);
+ fprintf(stdout, "L2CAP channels:\n");
+ fprintf(stdout,
+"Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State\n");
+ for (n = 0; n < r.num_channels; n++) {
+ fprintf(stdout,
+ "%02x:%02x:%02x:%02x:%02x:%02x " \
+ "%5d/%5d %5d " \
+ "%5d/%5d " \
+ "%s\n",
+ r.channels[n].remote.b[5], r.channels[n].remote.b[4],
+ r.channels[n].remote.b[3], r.channels[n].remote.b[2],
+ r.channels[n].remote.b[1], r.channels[n].remote.b[0],
+ r.channels[n].scid, r.channels[n].dcid,
+ r.channels[n].psm, r.channels[n].imtu,
+ r.channels[n].omtu,
+ ch_state2str(r.channels[n].state));
+ }
+out:
+ free(r.channels);
+
+ return (error);
+} /* l2cap_read_channel_list */
+
+struct l2cap_command l2cap_commands[] = {
+{
+"read_node_flags",
+"Get L2CAP node flags",
+&l2cap_read_node_flags
+},
+{
+"read_debug_level",
+"Get L2CAP node debug level",
+&l2cap_read_debug_level
+},
+{
+"write_debug_level <level>",
+"Set L2CAP node debug level",
+&l2cap_write_debug_level
+},
+{
+"read_connection_list",
+"Read list of the L2CAP connections",
+&l2cap_read_connection_list
+},
+{
+"read_channel_list",
+"Read list of the L2CAP channels",
+&l2cap_read_channel_list
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/l2control/l2control.8 b/usr.sbin/bluetooth/l2control/l2control.8
new file mode 100644
index 0000000..9efa251
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.8
@@ -0,0 +1,84 @@
+.\" l2control.8
+.\"
+.\" 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: l2control.8,v 1.4 2002/11/09 19:18:16 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt L2CONTROL 8
+.Os
+.Sh NAME
+.Nm l2control
+.Nd L2CAP configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar local BD_ADDR
+.Op Ar command
+.Op Ar parameters ...
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the local device with specified BD_ADDR and attempts
+to send specified command.
+.Nm
+will print results to the standard output and error messages to
+the standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar local BD_ADDR
+Connect to the local device with specified BD_ADDR. Example:
+.Fl a Ar 00:01:02:03:04:05 .
+.It command
+One of the supported commands (see below). Special command
+.Dq help
+can be used to obtain the list of all supported commands. To get more
+information about specific command use
+.Dq help command .
+.It parameters
+One or more optional space separated command parameters.
+.El
+.Sh COMMANDS
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bd -literal -offset indent -compact
+Read_Node_Flags
+Read_Debug_Level
+Write_Debug_Level
+Read_Connection_List
+Read_Channel_List
+.Ed
+.Pp
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_l2cap 4 ,
+.Xr l2ping 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/l2control/l2control.c b/usr.sbin/bluetooth/l2control/l2control.c
new file mode 100644
index 0000000..ef65826
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.c
@@ -0,0 +1,226 @@
+/*
+ * l2control.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: l2control.c,v 1.5 2002/09/04 21:30:40 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <bitstring.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "l2control.h"
+
+/* Prototypes */
+static int do_l2cap_command (bdaddr_p, int, char **);
+static struct l2cap_command * find_l2cap_command (char const *,
+ struct l2cap_command *);
+static void print_l2cap_command (struct l2cap_command *);
+static void usage (void);
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ int n;
+ bdaddr_t bdaddr;
+
+ memset(&bdaddr, 0, sizeof(bdaddr));
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "a:")) != -1) {
+ switch (n) {
+ case 'a': {
+ int a0, a1, a2, a3, a4, a5;
+
+ if (sscanf(optarg, "%x:%x:%x:%x:%x:%x",
+ &a5, &a4, &a3, &a2, &a1, &a0) != 6) {
+ usage();
+ break;
+ }
+
+ bdaddr.b[0] = (a0 & 0xff);
+ bdaddr.b[1] = (a1 & 0xff);
+ bdaddr.b[2] = (a2 & 0xff);
+ bdaddr.b[3] = (a3 & 0xff);
+ bdaddr.b[4] = (a4 & 0xff);
+ bdaddr.b[5] = (a5 & 0xff);
+ } break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ return (do_l2cap_command(&bdaddr, argc, argv));
+} /* main */
+
+/* Execute commands */
+static int
+do_l2cap_command(bdaddr_p bdaddr, int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct l2cap_command *c = NULL;
+ struct sockaddr_l2cap sa;
+ int s, e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_l2cap_command(l2cap_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_l2cap_command(cmd, l2cap_commands);
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+
+ if (!help) {
+ if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0)
+ usage();
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+ memcpy(&sa.l2cap_bdaddr, bdaddr, sizeof(sa.l2cap_bdaddr));
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP);
+ if (s < 0)
+ err(1, "Could not create socket");
+
+ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(2,
+"Could not bind socket, bdaddr=%x:%x:%x:%x:%x:%x",
+ sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
+ sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
+ sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
+
+ if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(2,
+"Could not connect socket, bdaddr=%x:%x:%x:%x:%x:%x",
+ sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
+ sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
+ sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
+
+ e = 0x0ffff;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &e, sizeof(e)) < 0)
+ err(3, "Coult not setsockopt(RCVBUF, %d)", e);
+
+ e = (c->handler)(s, -- argc, ++ argv);
+
+ close(s);
+ } else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(errno));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+ return (e);
+} /* do_l2cap_command */
+
+/* Try to find command in specified category */
+static struct l2cap_command *
+find_l2cap_command(char const *command, struct l2cap_command *category)
+{
+ struct l2cap_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_l2cap_command */
+
+/* Try to find command in specified category */
+static void
+print_l2cap_command(struct l2cap_command *category)
+{
+ struct l2cap_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_l2cap_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stdout, "Usage: l2control -a BD_ADDR cmd [p1] [..]]\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/l2control/l2control.h b/usr.sbin/bluetooth/l2control/l2control.h
new file mode 100644
index 0000000..0f6e5dc
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.h
@@ -0,0 +1,49 @@
+/*
+ * l2control.h
+ *
+ * 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: l2control.h,v 1.2 2002/09/04 21:30:40 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _L2CONTROL_H_
+#define _L2CONTROL_H_
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+struct l2cap_command {
+ char const *command;
+ char const *description;
+ int (*handler)(int, int, char **);
+};
+
+extern struct l2cap_command l2cap_commands[];
+
+#endif /* _L2CONTROL_H_ */
+
diff --git a/usr.sbin/bluetooth/l2ping/Makefile b/usr.sbin/bluetooth/l2ping/Makefile
new file mode 100644
index 0000000..f2dad31
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.4 2002/09/04 21:28:05 max Exp $
+# $FreeBSD$
+
+DESTDIR= /usr/sbin/
+MANDIR= ../share/man/man
+PROG= l2ping
+MAN8= l2ping.8
+SRCS= l2ping.c
+WARNS?= 2
+CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/l2ping/l2ping.8 b/usr.sbin/bluetooth/l2ping/l2ping.8
new file mode 100644
index 0000000..aa79ad9
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.8
@@ -0,0 +1,87 @@
+.\" l2ping.8
+.\"
+.\" 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: l2ping.8,v 1.4 2002/11/09 19:20:09 max Exp $
+.\" $FreeBSD$
+.Dd June 14, 2002
+.Dt L2PING 8
+.Os
+.Sh NAME
+.Nm l2ping
+.Nd send L2CAP ECHO_REQUEST to remote devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar remote BD_ADDR
+.Op Fl S Ar source BD_ADDR
+.Op Fl c Ar count
+.Op Fl f
+.Op Fl i Ar delay
+.Op Fl s Ar size
+.Sh DESCRIPTION
+The
+.Nm
+uses L2CAP ECHO_REQUEST datagram to elicit a L2CAP ECHO_RESPONSE from a
+remote device.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar remote BD_ADDR
+Address of remote device to ping. Example:
+.Fl a Ar 00:01:02:03:04:05 .
+.It Fl S Ar source BD_ADDR
+Send L2CAP ECHO_REQUEST from local device that has BD_ADDR. Example:
+.Fl S Ar 00:05:04:03:02:01 .
+.It Fl c Ar count
+Number of packets to send. If this option is not specified,
+.Nm
+will operate until interrupted.
+.It Fl f
+.Dq Flood
+ping, i.e. no delay between packets.
+.It Fl i Ar wait
+Wait
+.Em wait
+seconds between sending each packet. The default is to wait for one
+second between each packet. This option is ignored if
+.Fl f
+has been specified.
+.It Fl s Ar size
+Specify the number of payload bytes to be sent. The default is 64. The
+maximum size is 65531. Use this option with caution. Some implementations
+may not like large sizes and may hang or even crash.
+.El
+.Sh BUGS
+Could collect more statistic. Could check for duplicated, corrupted
+and lost packets.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_l2cap 4 ,
+.Xr l2control 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/l2ping/l2ping.c b/usr.sbin/bluetooth/l2ping/l2ping.c
new file mode 100644
index 0000000..a7ffd37
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.c
@@ -0,0 +1,279 @@
+/*
+ * l2ping.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: l2ping.c,v 1.9 2002/09/04 21:28:05 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <bitstring.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ng_hci.h>
+#include <ng_l2cap.h>
+#include <ng_btsocket.h>
+
+static void usage (void);
+static void tv_sub (struct timeval *, struct timeval const *);
+static double tv2msec (struct timeval const *);
+
+#undef min
+#define min(x, y) (((x) > (y))? (y) : (x))
+
+static char const pattern[] = "1234567890-";
+#define PATTERN_SIZE (sizeof(pattern) - 1)
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char *argv[])
+{
+ struct ng_btsocket_l2cap_raw_ping r;
+ int n, s, count, wait, flood, fail;
+ struct timeval a, b;
+
+ /* Set defaults */
+ memset(&r, 0, sizeof(r));
+ r.echo_data = calloc(NG_L2CAP_MAX_ECHO_SIZE, sizeof(u_int8_t));
+ if (r.echo_data == NULL) {
+ fprintf(stderr, "Failed to allocate echo data buffer");
+ exit(1);
+ }
+
+ r.echo_size = 64; /* bytes */
+ count = -1; /* unlimited */
+ wait = 1; /* sec */
+ flood = 0;
+
+ /* Parse command line arguments */
+ while ((n = getopt(argc, argv, "a:c:fi:n:s:S:")) != -1) {
+ switch (n) {
+ case 'a':
+ case 'S': {
+ int a0, a1, a2, a3, a4, a5;
+
+ if (sscanf(optarg, "%x:%x:%x:%x:%x:%x",
+ &a5, &a4, &a3, &a2, &a1, &a0) != 6)
+ usage();
+
+ if (n == 'a') {
+ /* destination bdaddr */
+ r.echo_dst.b[0] = (a0 & 0xff);
+ r.echo_dst.b[1] = (a1 & 0xff);
+ r.echo_dst.b[2] = (a2 & 0xff);
+ r.echo_dst.b[3] = (a3 & 0xff);
+ r.echo_dst.b[4] = (a4 & 0xff);
+ r.echo_dst.b[5] = (a5 & 0xff);
+ } else {
+ /* source bdaddr */
+ r.echo_src.b[0] = (a0 & 0xff);
+ r.echo_src.b[1] = (a1 & 0xff);
+ r.echo_src.b[2] = (a2 & 0xff);
+ r.echo_src.b[3] = (a3 & 0xff);
+ r.echo_src.b[4] = (a4 & 0xff);
+ r.echo_src.b[5] = (a5 & 0xff);
+ }
+ } break;
+
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+
+ case 'f':
+ flood = 1;
+ break;
+
+ case 'i':
+ wait = atoi(optarg);
+ if (wait <= 0)
+ usage();
+ break;
+
+ case 's':
+ r.echo_size = atoi(optarg);
+ if ((int) r.echo_size < sizeof(int))
+ usage();
+
+ if (r.echo_size > NG_L2CAP_MAX_ECHO_SIZE)
+ r.echo_size = NG_L2CAP_MAX_ECHO_SIZE;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (memcmp(&r.echo_dst, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
+ usage();
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP);
+ if (s < 0)
+ err(2, "Could not create socket");
+
+ if (memcmp(&r.echo_src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) != 0) {
+ struct sockaddr_l2cap sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+ memcpy(&sa.l2cap_bdaddr, &r.echo_src, sizeof(bdaddr_t));
+
+ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(3,
+"Could not bind socket, src bdaddr=%x:%x:%x:%x:%x:%x",
+ sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
+ sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
+ sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
+
+ if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(4,
+"Could not connect socket, src bdaddr=%x:%x:%x:%x:%x:%x",
+ sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
+ sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
+ sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
+ }
+
+ /* Fill pattern */
+ for (n = 0; n < r.echo_size; ) {
+ int avail = min(r.echo_size - n, PATTERN_SIZE);
+
+ memcpy(r.echo_data + n, pattern, avail);
+ n += avail;
+ }
+
+ /* Start ping'ing */
+ for (n = 0; count == -1 || count > 0; n ++) {
+ if (gettimeofday(&a, NULL) < 0)
+ err(5, "Could not gettimeofday(a)");
+
+ fail = 0;
+ *((int *)(r.echo_data)) = htonl(n);
+ if (ioctl(s, SIOC_L2CAP_L2CA_PING, &r, sizeof(r)) < 0) {
+ r.result = errno;
+ fail = 1;
+/*
+ warn("Could not ping, dst bdaddr=%x:%x:%x:%x:%x:%x",
+ r.echo_dst.b[5], r.echo_dst.b[4],
+ r.echo_dst.b[3], r.echo_dst.b[2],
+ r.echo_dst.b[1], r.echo_dst.b[0]);
+*/
+ }
+
+ if (gettimeofday(&b, NULL) < 0)
+ err(7, "Could not gettimeofday(b)");
+
+ tv_sub(&b, &a);
+
+ fprintf(stdout,
+"%d bytes from %x:%x:%x:%x:%x:%x seq_no=%d time=%.3f ms result=%#x %s\n",
+ r.echo_size,
+ r.echo_dst.b[5], r.echo_dst.b[4],
+ r.echo_dst.b[3], r.echo_dst.b[2],
+ r.echo_dst.b[1], r.echo_dst.b[0],
+ ntohl(*((int *)(r.echo_data))),
+ tv2msec(&b), r.result,
+ ((fail == 0)? "" : strerror(errno)));
+
+ if (!flood) {
+ /* Wait */
+ a.tv_sec = wait;
+ a.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &a);
+ }
+
+ if (count != -1)
+ count --;
+ }
+
+ free(r.echo_data);
+ close(s);
+
+ return (0);
+} /* main */
+
+/*
+ * a -= b, for timevals
+ */
+
+static void
+tv_sub(struct timeval *a, struct timeval const *b)
+{
+ if (a->tv_usec < b->tv_usec) {
+ a->tv_usec += 1000000;
+ a->tv_sec -= 1;
+ }
+
+ a->tv_usec -= b->tv_usec;
+ a->tv_sec -= b->tv_sec;
+} /* tv_sub */
+
+/*
+ * convert tv to msec
+ */
+
+static double
+tv2msec(struct timeval const *tvp)
+{
+ return(((double)tvp->tv_usec)/1000.0 + ((double)tvp->tv_sec)*1000.0);
+} /* tv2msec */
+
+/*
+ * Usage
+ */
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: l2ping -a bd_addr " \
+ "[-S bd_addr -c count -i wait -s size]\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, "\t-S bd_addr - Source BD_ADDR\n");
+ fprintf(stderr, "\t-a bd_addr - Remote BD_ADDR to ping\n");
+ fprintf(stderr, "\t-c count - Number of packets to send\n");
+ fprintf(stderr, "\t-f - No delay (soft of flood)\n");
+ fprintf(stderr, "\t-i wait - Delay between packets (sec)\n");
+ fprintf(stderr, "\t-s size - Packet size (bytes), " \
+ "between %d and %d\n", sizeof(int), NG_L2CAP_MAX_ECHO_SIZE);
+
+ exit(255);
+} /* usage */
+
OpenPOWER on IntegriCloud