diff options
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 */ + |