summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>2002-11-20 23:01:59 +0000
committerjulian <julian@FreeBSD.org>2002-11-20 23:01:59 +0000
commitd72cb748a8219735d80767b8847995b1ff103f9a (patch)
treec38e3c48c390af16003a1f451848e93558978422
parent97980d0d57dd5c2944c5f3306726610a3a1b6e51 (diff)
downloadFreeBSD-src-d72cb748a8219735d80767b8847995b1ff103f9a.zip
FreeBSD-src-d72cb748a8219735d80767b8847995b1ff103f9a.tar.gz
The second try a committing the bluetooth code
Has been seen to work on several cards and communicating with several mobile phones to use them as modems etc. We are still talking with 3com to try get them to allow us to include the firmware for their pccard in the driver but the driver is here.. In the mean time it can be downloaded from the 3com website and loaded using the utility bt3cfw(8) (supplied) (instructions in the man page) Not yet linked to the build Submitted by: Maksim Yevmenkin <myevmenk@exodus.net> Approved by: re
-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