Kernel part of bluetooth stack ported by Dmitry Komissaroff. Very much work
authorHasso Tepper <hasso@dragonflybsd.org>
Sun, 30 Dec 2007 20:02:57 +0000 (20:02 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Sun, 30 Dec 2007 20:02:57 +0000 (20:02 +0000)
in progress.

Obtained-from: NetBSD via OpenBSD

37 files changed:
etc/mtree/BSD.include.dist
include/Makefile
sys/Makefile
sys/Makefile.modules
sys/bus/usb/usbhid.h
sys/dev/usbmisc/Makefile
sys/dev/usbmisc/ubt/Makefile [new file with mode: 0644]
sys/dev/usbmisc/ubt/ubt.c [new file with mode: 0644]
sys/net/netisr.h
sys/netbt/Makefile [new file with mode: 0644]
sys/netbt/bluetooth.h [new file with mode: 0644]
sys/netbt/bt_input.c [new file with mode: 0644]
sys/netbt/bt_proto.c [new file with mode: 0644]
sys/netbt/hci.h [new file with mode: 0644]
sys/netbt/hci_event.c [new file with mode: 0644]
sys/netbt/hci_ioctl.c [new file with mode: 0644]
sys/netbt/hci_link.c [new file with mode: 0644]
sys/netbt/hci_misc.c [new file with mode: 0644]
sys/netbt/hci_socket.c [new file with mode: 0644]
sys/netbt/hci_unit.c [new file with mode: 0644]
sys/netbt/l2cap.h [new file with mode: 0644]
sys/netbt/l2cap_lower.c [new file with mode: 0644]
sys/netbt/l2cap_misc.c [new file with mode: 0644]
sys/netbt/l2cap_signal.c [new file with mode: 0644]
sys/netbt/l2cap_socket.c [new file with mode: 0644]
sys/netbt/l2cap_upper.c [new file with mode: 0644]
sys/netbt/rfcomm.h [new file with mode: 0644]
sys/netbt/rfcomm_dlc.c [new file with mode: 0644]
sys/netbt/rfcomm_session.c [new file with mode: 0644]
sys/netbt/rfcomm_socket.c [new file with mode: 0644]
sys/netbt/rfcomm_upper.c [new file with mode: 0644]
sys/netbt/sco.h [new file with mode: 0644]
sys/netbt/sco_socket.c [new file with mode: 0644]
sys/netbt/sco_upper.c [new file with mode: 0644]
sys/sys/mount.h
sys/sys/socket.h
sys/sys/ttycom.h

index 8b03ef5..e0f94b4 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/etc/mtree/BSD.include.dist,v 1.32.2.14 2003/01/24 09:47:07 roam Exp $
-# $DragonFly: src/etc/mtree/BSD.include.dist,v 1.25 2007/12/12 23:47:30 dillon Exp $
+# $DragonFly: src/etc/mtree/BSD.include.dist,v 1.26 2007/12/30 20:02:56 hasso Exp $
 #
 # Please see the file src/etc/mtree/README before making changes to this file.
 #
     ..
     kadm5
     ..
+    netbt
+    ..
     netinet
     ..
     netinet6
index 06ee085..b8634fe 100644 (file)
@@ -1,6 +1,6 @@
 #      @(#)Makefile    8.2 (Berkeley) 1/4/94
 # $FreeBSD: src/include/Makefile,v 1.109.2.27 2003/01/24 05:12:29 sam Exp $
-# $DragonFly: src/include/Makefile,v 1.37 2007/12/12 23:47:32 dillon Exp $
+# $DragonFly: src/include/Makefile,v 1.38 2007/12/30 20:02:56 hasso Exp $
 #
 # Doing a make install builds /usr/include
 #
@@ -36,7 +36,7 @@ LHDRS=        aio.h errno.h fcntl.h linker_set.h mqueue.h poll.h sched.h \
 # Other directories, like 'bus' and 'netproto', are created using mtree.
 #
 # XXX allow these directories to not contain header files.
-LDIRS= net netgraph netinet netinet6 sys vm
+LDIRS= net netbt netgraph netinet netinet6 sys vm
 
 # Subdirectories containing header files to copy.  In symlink mode 
 # the subdirectory will be symlinked.  Care must be taken to adjust
index df1802a..572b368 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/Makefile,v 1.20.2.1 2000/07/10 08:22:34 obrien Exp $
-# $DragonFly: src/sys/Makefile,v 1.15 2006/11/07 06:43:18 dillon Exp $
+# $DragonFly: src/sys/Makefile,v 1.16 2007/12/30 20:02:56 hasso Exp $
 
 # This is the old aout only boot loader.
 .if    exists(${.CURDIR}/boot) && ${MACHINE_ARCH} == "i386" && ${OBJFORMAT} == "elf"
@@ -12,7 +12,7 @@ SUBDIR=       boot
 .if defined(MODULES_OVERRIDE)
 SUBDIR+=${MODULES_OVERRIDE}
 .else
-SUBDIR+=bus crypto emulation dev kern net netgraph netproto vfs
+SUBDIR+=bus crypto emulation dev kern net netbt netgraph netproto vfs
 SUBDIR+=libiconv
 .endif
 .endif
index aa2c70a..798f875 100644 (file)
@@ -1,14 +1,14 @@
 # Makefile.modules - build the modules (executed from the modules: target
 # in Makefile).
 #
-# $DragonFly: src/sys/Makefile.modules,v 1.6 2007/02/01 08:42:18 corecode Exp $
+# $DragonFly: src/sys/Makefile.modules,v 1.7 2007/12/30 20:02:56 hasso Exp $
 .if defined(MODULES_OVERRIDE)
 SUBDIR=${MODULES_OVERRIDE}
 .else
 .if ${MACHINE_PLATFORM} != "vkernel"
 SUBDIR=bus
 .endif
-SUBDIR+=crypto emulation dev kern net netgraph netproto vfs
+SUBDIR+=crypto emulation dev kern net netbt netgraph netproto vfs
 SUBDIR+=libiconv
 .endif
 
index 22c8485..08d294d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $
  * $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.13 2002/01/02 20:16:53 joe Exp $
- * $DragonFly: src/sys/bus/usb/usbhid.h,v 1.3 2003/12/30 01:01:44 dillon Exp $
+ * $DragonFly: src/sys/bus/usb/usbhid.h,v 1.4 2007/12/30 20:02:56 hasso Exp $
  */
 
 /*
@@ -167,6 +167,9 @@ typedef struct usb_hid_descriptor {
 #define HUD_ERASER             0x0045
 #define HUD_TABLET_PICK                0x0046
 
+/* Usages, Consumer */
+#define HUC_AC_PAN             0x0238
+
 #define HID_USAGE2(p,u) (((p) << 16) | u)
 
 #define UHID_INPUT_REPORT 0x01
index 6b7c2b3..7c9e7d4 100644 (file)
@@ -1,7 +1,8 @@
-# $DragonFly: src/sys/dev/usbmisc/Makefile,v 1.8 2007/11/07 09:07:44 hasso Exp $
+# $DragonFly: src/sys/dev/usbmisc/Makefile,v 1.9 2007/12/30 20:02:56 hasso Exp $
 #
 
-SUBDIR= moscom uark ubsa uchcom ucom ufm uftdi ugen uhid ukbd ulpt umass \
-       umodem ums umsm uplcom uscanner uslcom uticom uvisor uvscom umct
+SUBDIR= moscom uark ubsa ubt uchcom ucom ufm uftdi ugen uhid ukbd ulpt \
+       umass umct umodem ums umsm uplcom uscanner uslcom uticom uvisor \
+       uvscom umct
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/usbmisc/ubt/Makefile b/sys/dev/usbmisc/ubt/Makefile
new file mode 100644 (file)
index 0000000..c6928fb
--- /dev/null
@@ -0,0 +1,9 @@
+# $DragonFly: src/sys/dev/usbmisc/ubt/Makefile,v 1.1 2007/12/30 20:02:56 hasso Exp $
+
+KMOD=  ubt
+SRCS=  ubt.c opt_usb.h bus_if.h device_if.h
+CFALGS+ = -I${WRKSRC}
+NOMAN=
+KMODDEPS = netbt
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/usbmisc/ubt/ubt.c b/sys/dev/usbmisc/ubt/ubt.c
new file mode 100644 (file)
index 0000000..259abd7
--- /dev/null
@@ -0,0 +1,1545 @@
+/* $OpenBSD: ubt.c,v 1.9 2007/10/11 18:33:14 deraadt Exp $ */
+/* $DragonFly: src/sys/dev/usbmisc/ubt/ubt.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2006 Itronix Inc.
+ * All rights reserved.
+ *
+ * Written by Iain Hibbert for Itronix Inc.
+ *
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+/*
+ * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) and
+ * David Sainty (David.Sainty@dtsp.co.nz).
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+/*
+ * This driver originally written by Lennart Augustsson and David Sainty,
+ * but was mostly rewritten for the NetBSD Bluetooth protocol stack by
+ * Iain Hibbert for Itronix, Inc using the FreeBSD ng_ubt.c driver as a
+ * reference.
+ */
+
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
+#include <bus/usb/usbdivar.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+/*******************************************************************************
+ *
+ *     debugging stuff
+ */
+#undef DPRINTF
+#undef DPRINTFN
+
+#ifdef UBT_DEBUG
+int    ubt_debug = UBT_DEBUG;
+
+#define DPRINTF(fmt, args...)          do {            \
+       if (ubt_debug)                                  \
+               kprintf("%s: "fmt, __func__ , ##args);  \
+} while (/* CONSTCOND */0)
+
+#define DPRINTFN(n, fmt, args...)      do {            \
+       if (ubt_debug > (n))                            \
+               kprintf("%s: "fmt, __func__ , ##args);  \
+} while (/* CONSTCOND */0)
+
+#else
+#define DPRINTF(...)
+#define DPRINTFN(...)
+#endif
+
+/*******************************************************************************
+ *
+ *     ubt softc structure
+ *
+ */
+
+/* buffer sizes */
+/*
+ * NB: although ACL packets can extend to 65535 bytes, most devices
+ * have max_acl_size at much less (largest I have seen is 384)
+ */
+#define UBT_BUFSIZ_CMD         (HCI_CMD_PKT_SIZE - 1)
+#define UBT_BUFSIZ_ACL         (2048 - 1)
+#define UBT_BUFSIZ_EVENT       (HCI_EVENT_PKT_SIZE - 1)
+
+/* Transmit timeouts */
+
+#define UBT_CMD_TIMEOUT                100
+#define UBT_ACL_TIMEOUT                100
+
+/*
+ * Specification says, that is must be 1ms, but it causes kernel panic.
+ * Setting interval to USBD_DEFAULT_INTERVAL is not working for some of
+ * us either.
+ * XXX: Make it sysctl.
+ */
+#define UBT_INTR_TIMEOUT               USBD_DEFAULT_INTERVAL
+
+/*
+ * ISOC transfers
+ *
+ * xfer buffer size depends on the frame size, and the number
+ * of frames per transfer is fixed, as each frame should be
+ * 1ms worth of data. This keeps the rate that xfers complete
+ * fairly constant. We use multiple xfers to keep the hardware
+ * busy
+ */
+#define UBT_NXFERS             3       /* max xfers to queue */
+#define UBT_NFRAMES            10      /* frames per xfer */
+
+struct ubt_isoc_xfer {
+       struct ubt_softc        *softc;
+       usbd_xfer_handle         xfer;
+       uint8_t                 *buf;
+       uint16_t                 size[UBT_NFRAMES];
+       int                      busy;
+};
+
+struct ubt_softc {
+       device_t                 sc_dev;
+       usbd_device_handle       sc_udev;
+       int                      sc_refcnt;
+       int                      sc_dying;
+
+       /* Control Interface */
+       usbd_interface_handle    sc_iface0;
+
+       /* Commands (control) */
+       usbd_xfer_handle         sc_cmd_xfer;
+       u_char                  *sc_cmd_buf;
+
+       /* Events (interrupt) */
+       int                      sc_evt_addr;   /* endpoint address */
+       usbd_pipe_handle         sc_evt_pipe;
+       u_char                  *sc_evt_buf;
+
+
+       /* ACL data (in) */
+       int                      sc_aclrd_addr; /* endpoint address */
+       usbd_pipe_handle         sc_aclrd_pipe; /* read pipe */
+       usbd_xfer_handle         sc_aclrd_xfer; /* read xfer */
+       u_char                  *sc_aclrd_buf;  /* read buffer */
+       int                      sc_aclrd_busy; /* reading */
+
+       /* ACL data (out) */
+       int                      sc_aclwr_addr; /* endpoint address */
+       usbd_pipe_handle         sc_aclwr_pipe; /* write pipe */
+       usbd_xfer_handle         sc_aclwr_xfer; /* write xfer */
+       u_char                  *sc_aclwr_buf;  /* write buffer */
+
+       /* ISOC interface */
+       usbd_interface_handle    sc_iface1;     /* ISOC interface */
+       struct sysctllog        *sc_log;        /* sysctl log */
+       int                      sc_config;     /* current config no */
+       int                      sc_alt_config; /* no of alternates */
+
+       /* SCO data (in) */
+       int                      sc_scord_addr; /* endpoint address */
+       usbd_pipe_handle         sc_scord_pipe; /* read pipe */
+       int                      sc_scord_size; /* frame length */
+       struct ubt_isoc_xfer     sc_scord[UBT_NXFERS];
+       struct mbuf             *sc_scord_mbuf; /* current packet */
+
+       /* SCO data (out) */
+       int                      sc_scowr_addr; /* endpoint address */
+       usbd_pipe_handle         sc_scowr_pipe; /* write pipe */
+       int                      sc_scowr_size; /* frame length */
+       struct ubt_isoc_xfer     sc_scowr[UBT_NXFERS];
+       struct mbuf             *sc_scowr_mbuf; /* current packet */
+
+       /* Protocol structure */
+       struct hci_unit          sc_unit;
+
+       /* Successfully attached */
+       int                      sc_ok;
+};
+
+/*
+ * Bluetooth unit/USB callback routines
+ */
+int ubt_enable(struct hci_unit *);
+void ubt_disable(struct hci_unit *);
+
+void ubt_xmit_cmd_start(struct hci_unit *);
+void ubt_xmit_cmd_complete(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+void ubt_xmit_acl_start(struct hci_unit *);
+void ubt_xmit_acl_complete(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+void ubt_xmit_sco_start(struct hci_unit *);
+void ubt_xmit_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
+void ubt_xmit_sco_complete(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+void ubt_recv_event(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+void ubt_recv_acl_start(struct ubt_softc *);
+void ubt_recv_acl_complete(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+void ubt_recv_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
+void ubt_recv_sco_complete(usbd_xfer_handle,
+                               usbd_private_handle, usbd_status);
+
+static device_probe_t ubt_match;
+static device_attach_t ubt_attach;
+static device_detach_t ubt_detach;
+
+static devclass_t ubt_devclass;
+
+static device_method_t ubt_methods[] = {
+       DEVMETHOD(device_probe, ubt_match),
+       DEVMETHOD(device_attach, ubt_attach),
+       DEVMETHOD(device_detach, ubt_detach),
+       {0,0},
+       {0,0}
+};
+
+static driver_t ubt_driver = {
+       "ubt",
+       ubt_methods,
+       sizeof(struct ubt_softc)
+};
+
+DRIVER_MODULE(ubt, uhub, ubt_driver, ubt_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(ubt, netbt, 1, 1, 1);
+#if 0 /* not yet */
+MODULE_DEPEND(ubt, bthub, 1, 1, 1);
+#endif
+MODULE_DEPEND(ubt, usb, 1, 1, 1);
+
+static int ubt_set_isoc_config(struct ubt_softc *);
+static void ubt_abortdealloc(struct ubt_softc *);
+
+/*
+ * Match against the whole device, since we want to take
+ * both interfaces. If a device should be ignored then add
+ *
+ *     { VendorID, ProductID }
+ *
+ * to the ubt_ignore list.
+ */
+static const struct usb_devno ubt_ignore[] = {
+       { USB_DEVICE(0x0a5c, 0x2000) }, /* Braodcom BCM2033 */
+       { USB_DEVICE(0x0a5c, 0x2033) }, /* Broadcom BCM2033 (no fw) */
+       { 0, 0 }                        /* end of list */
+};
+
+static int
+ubt_match(device_t self)
+{
+
+       struct usb_attach_arg *uaa = device_get_ivars(self);
+       usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
+
+       DPRINTFN(50, "ubt_match\n");
+
+       if (usb_lookup(ubt_ignore, uaa->vendor, uaa->product))
+               return UMATCH_NONE;
+
+       if (dd->bDeviceClass == UDCLASS_WIRELESS
+           && dd->bDeviceSubClass == UDSUBCLASS_RF
+           && dd->bDeviceProtocol == UDPROTO_BLUETOOTH)
+               return UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO;
+       return UMATCH_NONE;
+}
+
+static int
+ubt_attach(device_t self)
+{
+       struct ubt_softc *sc = device_get_softc(self);
+       struct usb_attach_arg *uaa = device_get_ivars(self);
+
+       usb_config_descriptor_t *cd;
+       usb_endpoint_descriptor_t *ed;
+       int err;
+       uint8_t count, i;
+
+       DPRINTFN(50, "ubt_attach: sc=%p\n", sc);
+       
+       sc->sc_udev = uaa->device;
+       sc->sc_dev = self;
+
+       /*
+        * Move the device into the configured state
+        */
+       err = usbd_set_config_index(sc->sc_udev, 0, 1);
+       if (err) {
+               kprintf("%s: failed to set configuration idx 0: %s\n",
+                   device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+
+               return ENXIO;
+       }
+
+       /*
+        * Interface 0 must have 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
+        */
+        
+       err = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
+       if (err) {
+               kprintf("%s: Could not get interface 0 handle %s (%d)\n",
+                               device_get_nameunit(sc->sc_dev), usbd_errstr(err), err);
+
+               return ENXIO;
+       }
+
+       sc->sc_evt_addr = -1;
+       sc->sc_aclrd_addr = -1;
+       sc->sc_aclwr_addr = -1;
+
+       count = 0;
+       (void)usbd_endpoint_count(sc->sc_iface0, &count);
+
+       for (i = 0 ; i < count ; i++) {
+               int dir, type;
+
+               ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
+               if (ed == NULL) {
+                       kprintf("%s: could not read endpoint descriptor %d\n",
+                           device_get_nameunit(sc->sc_dev), i);
+
+                       return ENXIO;
+               }
+
+               dir = UE_GET_DIR(ed->bEndpointAddress);
+               type = UE_GET_XFERTYPE(ed->bmAttributes);
+
+               if (dir == UE_DIR_IN && type == UE_INTERRUPT)
+                       sc->sc_evt_addr = ed->bEndpointAddress;
+               else if (dir == UE_DIR_IN && type == UE_BULK)
+                       sc->sc_aclrd_addr = ed->bEndpointAddress;
+               else if (dir == UE_DIR_OUT && type == UE_BULK)
+                       sc->sc_aclwr_addr = ed->bEndpointAddress;
+       }
+
+       if (sc->sc_evt_addr == -1) {
+               kprintf("%s: missing INTERRUPT endpoint on interface 0\n",
+                               device_get_nameunit(sc->sc_dev));
+
+               return ENXIO;
+       }
+       if (sc->sc_aclrd_addr == -1) {
+               kprintf("%s: missing BULK IN endpoint on interface 0\n",
+                               device_get_nameunit(sc->sc_dev));
+
+               return ENXIO;
+       }
+       if (sc->sc_aclwr_addr == -1) {
+               kprintf("%s: missing BULK OUT endpoint on interface 0\n",
+                               device_get_nameunit(sc->sc_dev));
+
+               return ENXIO;
+       }
+
+       /*
+        * Interface 1 must have 2 endpoints
+        *      1) Isochronous IN endpoint to receive SCO data
+        *      2) Isochronous OUT endpoint to send SCO data
+        *
+        * and will have several configurations, which can be selected
+        * via a sysctl variable. We select config 0 to start, which
+        * means that no SCO data will be available.
+        */
+        
+       err = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
+       if (err) {
+               kprintf("%s: Could not get interface 1 handle %s (%d)\n",
+                               device_get_nameunit(sc->sc_dev), usbd_errstr(err), err);
+
+               return ENXIO;
+       }
+
+       cd = usbd_get_config_descriptor(sc->sc_udev);
+       if (cd == NULL) {
+               kprintf("%s: could not get config descriptor\n",
+                       device_get_nameunit(sc->sc_dev));
+
+               return ENXIO;
+       }
+
+       sc->sc_alt_config = usbd_get_no_alts(cd, 1);
+
+       /* set initial config */
+       
+       err = ubt_set_isoc_config(sc);
+       if (err) {
+               kprintf("%s: ISOC config failed\n",
+                       device_get_nameunit(sc->sc_dev));
+
+               return ENXIO;
+       }
+
+       /* Attach HCI host's software */
+       
+       /* Fill HCI part of struct with data */
+       sc->sc_unit.hci_softc = self;
+       sc->sc_unit.hci_devname = device_get_nameunit(sc->sc_dev);
+       sc->sc_unit.hci_enable = ubt_enable;
+       sc->sc_unit.hci_disable = ubt_disable;
+       sc->sc_unit.hci_start_cmd = ubt_xmit_cmd_start;
+       sc->sc_unit.hci_start_acl = ubt_xmit_acl_start;
+       sc->sc_unit.hci_start_sco = ubt_xmit_sco_start;
+       
+       /* Attach to HCI software stack */
+       
+       hci_attach(&sc->sc_unit);
+
+       usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+                          sc->sc_dev);
+       sc->sc_ok = 1;
+
+       return 0;
+}
+
+static int
+ubt_detach(device_t self)
+{
+
+       struct ubt_softc *sc = device_get_softc(self);
+
+       DPRINTF("sc=%p \n", sc);
+
+       sc->sc_dying = 1;
+
+       if (!sc->sc_ok)
+               return 0;
+
+       /* Detach HCI interface */
+
+       hci_detach(&sc->sc_unit);
+
+       /*
+        * Abort all pipes. Causes processes waiting for transfer to wake.
+        *
+        * Actually, hci_detach() above will call ubt_disable() which may
+        * call ubt_abortdealloc(), but lets be sure since doing it twice
+        * wont cause an error.
+        */
+
+       ubt_abortdealloc(sc);
+
+       /* wait for all processes to finish */
+       
+       if (sc->sc_refcnt-- > 0)
+               usb_detach_wait(sc->sc_dev);
+
+       usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+                          sc->sc_dev);
+
+       DPRINTFN(1, "driver detached\n");
+
+       return 0;
+}
+
+/* set ISOC configuration */
+int
+ubt_set_isoc_config(struct ubt_softc *sc)
+{
+       usb_endpoint_descriptor_t *ed;
+       int rd_addr, wr_addr, rd_size, wr_size;
+       uint8_t count, i;
+       int err;
+
+       err = usbd_set_interface(sc->sc_iface1, sc->sc_config);
+       if (err != USBD_NORMAL_COMPLETION) {
+               kprintf(
+                   "%s: Could not set config %d on ISOC interface. %s (%d)\n",
+                   device_get_nameunit(sc->sc_dev), sc->sc_config, usbd_errstr(err), err);
+
+               return err == USBD_IN_USE ? EBUSY : EIO;
+       }
+
+       /*
+        * We wont get past the above if there are any pipes open, so no
+        * need to worry about buf/xfer/pipe deallocation. If we get an
+        * error after this, the frame quantities will be 0 and no SCO
+        * data will be possible.
+        */
+
+       sc->sc_scord_size = rd_size = 0;
+       sc->sc_scord_addr = rd_addr = -1;
+
+       sc->sc_scowr_size = wr_size = 0;
+       sc->sc_scowr_addr = wr_addr = -1;
+
+       count = 0;
+       (void)usbd_endpoint_count(sc->sc_iface1, &count);
+
+       for (i = 0 ; i < count ; i++) {
+               ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
+               if (ed == NULL) {
+                       kprintf("%s: could not read endpoint descriptor %d\n",
+                           device_get_nameunit(sc->sc_dev), i);
+
+                       return EIO;
+               }
+
+               DPRINTFN(5, "%s: endpoint type %02x (%02x) addr %02x (%s)\n",
+                       device_get_nameunit(sc->sc_dev),
+                       UE_GET_XFERTYPE(ed->bmAttributes),
+                       UE_GET_ISO_TYPE(ed->bmAttributes),
+                       ed->bEndpointAddress,
+                       UE_GET_DIR(ed->bEndpointAddress) ? "in" : "out");
+
+               if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+                       continue;
+
+               if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+                       rd_addr = ed->bEndpointAddress;
+                       rd_size = UGETW(ed->wMaxPacketSize);
+               } else {
+                       wr_addr = ed->bEndpointAddress;
+                       wr_size = UGETW(ed->wMaxPacketSize);
+               }
+       }
+
+       if (rd_addr == -1) {
+               kprintf(
+                   "%s: missing ISOC IN endpoint on interface config %d\n",
+                   device_get_nameunit(sc->sc_dev), sc->sc_config);
+
+               return ENOENT;
+       }
+       if (wr_addr == -1) {
+               kprintf(
+                   "%s: missing ISOC OUT endpoint on interface config %d\n",
+                   device_get_nameunit(sc->sc_dev), sc->sc_config);
+
+               return ENOENT;
+       }
+
+#ifdef DIAGNOSTIC
+       if (rd_size > MLEN) {
+               kprintf("%s: rd_size=%d exceeds MLEN\n",
+                   device_get_nameunit(sc->sc_dev), rd_size);
+
+               return EOVERFLOW;
+       }
+
+       if (wr_size > MLEN) {
+               kprintf("%s: wr_size=%d exceeds MLEN\n",
+                   device_get_nameunit(sc->sc_dev), wr_size);
+
+               return EOVERFLOW;
+       }
+#endif
+
+       sc->sc_scord_size = rd_size;
+       sc->sc_scord_addr = rd_addr;
+
+       sc->sc_scowr_size = wr_size;
+       sc->sc_scowr_addr = wr_addr;
+
+       return 0;
+}
+
+void
+ubt_abortdealloc(struct ubt_softc *sc)
+{
+       int i;
+
+       DPRINTFN(1, "sc=%p\n", sc);
+       crit_enter();
+       /* Abort all pipes */
+       if (sc->sc_evt_pipe != NULL) {
+               usbd_abort_pipe(sc->sc_evt_pipe);
+               usbd_close_pipe(sc->sc_evt_pipe);
+               sc->sc_evt_pipe = NULL;
+       }
+
+       if (sc->sc_aclrd_pipe != NULL) {
+               usbd_abort_pipe(sc->sc_aclrd_pipe);
+               usbd_close_pipe(sc->sc_aclrd_pipe);
+               sc->sc_aclrd_pipe = NULL;
+       }
+
+       if (sc->sc_aclwr_pipe != NULL) {
+               usbd_abort_pipe(sc->sc_aclwr_pipe);
+               usbd_close_pipe(sc->sc_aclwr_pipe);
+               sc->sc_aclwr_pipe = NULL;
+       }
+
+       if (sc->sc_scord_pipe != NULL) {
+               usbd_abort_pipe(sc->sc_scord_pipe);
+               usbd_close_pipe(sc->sc_scord_pipe);
+               sc->sc_scord_pipe = NULL;
+       }
+
+       if (sc->sc_scowr_pipe != NULL) {
+               usbd_abort_pipe(sc->sc_scowr_pipe);
+               usbd_close_pipe(sc->sc_scowr_pipe);
+               sc->sc_scowr_pipe = NULL;
+       }
+
+       /* Free event buffer */
+       if (sc->sc_evt_buf != NULL) {
+               kfree(sc->sc_evt_buf, M_USBDEV);
+               sc->sc_evt_buf = NULL;
+       }
+
+       /* Free all xfers and xfer buffers (implicit) */
+       if (sc->sc_cmd_xfer != NULL) {
+               usbd_free_xfer(sc->sc_cmd_xfer);
+               sc->sc_cmd_xfer = NULL;
+               sc->sc_cmd_buf = NULL;
+       }
+
+       if (sc->sc_aclrd_xfer != NULL) {
+               usbd_free_xfer(sc->sc_aclrd_xfer);
+               sc->sc_aclrd_xfer = NULL;
+               sc->sc_aclrd_buf = NULL;
+       }
+
+       if (sc->sc_aclwr_xfer != NULL) {
+               usbd_free_xfer(sc->sc_aclwr_xfer);
+               sc->sc_aclwr_xfer = NULL;
+               sc->sc_aclwr_buf = NULL;
+       }
+
+       for (i = 0 ; i < UBT_NXFERS ; i++) {
+               if (sc->sc_scord[i].xfer != NULL) {
+                       usbd_free_xfer(sc->sc_scord[i].xfer);
+                       sc->sc_scord[i].xfer = NULL;
+                       sc->sc_scord[i].buf = NULL;
+               }
+
+               if (sc->sc_scowr[i].xfer != NULL) {
+                       usbd_free_xfer(sc->sc_scowr[i].xfer);
+                       sc->sc_scowr[i].xfer = NULL;
+                       sc->sc_scowr[i].buf = NULL;
+               }
+       }
+
+       /* Free partial SCO packets */
+       if (sc->sc_scord_mbuf != NULL) {
+               m_freem(sc->sc_scord_mbuf);
+               sc->sc_scord_mbuf = NULL;
+       }
+
+       if (sc->sc_scowr_mbuf != NULL) {
+               m_freem(sc->sc_scowr_mbuf);
+               sc->sc_scowr_mbuf = NULL;
+       }
+       crit_exit();    
+}
+
+/*******************************************************************************
+ *
+ * Bluetooth Unit/USB callbacks
+ *
+ * All of this will be called at the IPL_ we specified above
+ */
+int
+ubt_enable(struct hci_unit *unit)
+{
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       usbd_status err;
+       int i, error;
+
+       DPRINTFN(1, "%s: sc=%p\n", __func__, sc);
+
+       if (unit->hci_flags & BTF_RUNNING)
+               return 0;
+
+       /* Events */
+       
+       sc->sc_evt_buf = kmalloc(UBT_BUFSIZ_EVENT, M_USBDEV, M_NOWAIT);
+       if (sc->sc_evt_buf == NULL) {
+               error = ENOMEM;
+               goto bad;
+       }
+
+       err = usbd_open_pipe_intr(sc->sc_iface0,
+                                 sc->sc_evt_addr,
+                                 USBD_SHORT_XFER_OK,
+                                 &sc->sc_evt_pipe,
+                                 sc,
+                                 sc->sc_evt_buf,
+                                 UBT_BUFSIZ_EVENT,
+                                 ubt_recv_event,
+                                 UBT_INTR_TIMEOUT);
+       if (err != USBD_NORMAL_COMPLETION) {
+               error = EIO;
+               kprintf("can't open events pipe_intr\n");
+               goto bad;
+       }
+
+       /* Commands */
+       
+       sc->sc_cmd_xfer = usbd_alloc_xfer(sc->sc_udev);
+       if (sc->sc_cmd_xfer == NULL) {
+               kprintf("can't allocate cmd_xfer\n");
+               error = ENOMEM;
+               goto bad;
+       }
+       sc->sc_cmd_buf = usbd_alloc_buffer(sc->sc_cmd_xfer, UBT_BUFSIZ_CMD);
+       if (sc->sc_cmd_buf == NULL) {
+               kprintf("can't allocate cmd_buf\n");
+               error = ENOMEM;
+               goto bad;
+       }
+
+       /* ACL read */
+       
+       err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclrd_addr,
+                               USBD_EXCLUSIVE_USE, &sc->sc_aclrd_pipe);
+       if (err != USBD_NORMAL_COMPLETION) {
+               kprintf("can't open aclrd pipe\n");
+               error = EIO;
+               goto bad;
+       }
+       sc->sc_aclrd_xfer = usbd_alloc_xfer(sc->sc_udev);
+       if (sc->sc_aclrd_xfer == NULL) {
+               kprintf("can't allocate aclrd_xfer\n");
+               error = ENOMEM;
+               goto bad;
+       }
+       sc->sc_aclrd_buf = usbd_alloc_buffer(sc->sc_aclrd_xfer, UBT_BUFSIZ_ACL);
+       if (sc->sc_aclrd_buf == NULL) {
+               kprintf("can't allocate aclrd_buf\n");
+               error = ENOMEM;
+               goto bad;
+       }
+       sc->sc_aclrd_busy = 0;
+       ubt_recv_acl_start(sc);
+
+       /* ACL write */
+       
+       err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclwr_addr,
+                               USBD_EXCLUSIVE_USE, &sc->sc_aclwr_pipe);
+       if (err != USBD_NORMAL_COMPLETION) {
+               kprintf("can't open aclwr pipe\n");
+               error = EIO;
+               goto bad;
+       }
+       sc->sc_aclwr_xfer = usbd_alloc_xfer(sc->sc_udev);
+       if (sc->sc_aclwr_xfer == NULL) {
+               kprintf("can't allocate aclwr_xfer\n");
+               error = ENOMEM;
+               goto bad;
+       }
+       sc->sc_aclwr_buf = usbd_alloc_buffer(sc->sc_aclwr_xfer, UBT_BUFSIZ_ACL);
+       if (sc->sc_aclwr_buf == NULL) {
+               kprintf("can't allocate aclwr_buf\n");
+               error = ENOMEM;
+               goto bad;
+       }
+
+       /* SCO read */
+       
+       if (sc->sc_scord_size > 0) {
+               err = usbd_open_pipe(sc->sc_iface1, sc->sc_scord_addr,
+                                       USBD_EXCLUSIVE_USE, &sc->sc_scord_pipe);
+               if (err != USBD_NORMAL_COMPLETION) {
+                       error = EIO;
+                       goto bad;
+               }
+
+               for (i = 0 ; i < UBT_NXFERS ; i++) {
+                       sc->sc_scord[i].xfer = usbd_alloc_xfer(sc->sc_udev);
+                       if (sc->sc_scord[i].xfer == NULL) {
+                               error = ENOMEM;
+                               goto bad;
+                       }
+                       sc->sc_scord[i].buf = usbd_alloc_buffer(sc->sc_scord[i].xfer,
+                                               sc->sc_scord_size * UBT_NFRAMES);
+                       if (sc->sc_scord[i].buf == NULL) {
+                               error = ENOMEM;
+                               goto bad;
+                       }
+                       sc->sc_scord[i].softc = sc;
+                       sc->sc_scord[i].busy = 0;
+                       ubt_recv_sco_start1(sc, &sc->sc_scord[i]);
+               }
+       }
+
+       /* SCO write */
+       
+       if (sc->sc_scowr_size > 0) {
+               err = usbd_open_pipe(sc->sc_iface1, sc->sc_scowr_addr,
+                                       USBD_EXCLUSIVE_USE, &sc->sc_scowr_pipe);
+               if (err != USBD_NORMAL_COMPLETION) {
+                       error = EIO;
+                       goto bad;
+               }
+
+               for (i = 0 ; i < UBT_NXFERS ; i++) {
+                       sc->sc_scowr[i].xfer = usbd_alloc_xfer(sc->sc_udev);
+                       if (sc->sc_scowr[i].xfer == NULL) {
+                               error = ENOMEM;
+                               goto bad;
+                       }
+                       sc->sc_scowr[i].buf = usbd_alloc_buffer(sc->sc_scowr[i].xfer,
+                                               sc->sc_scowr_size * UBT_NFRAMES);
+                       if (sc->sc_scowr[i].buf == NULL) {
+                               error = ENOMEM;
+                               goto bad;
+                       }
+                       sc->sc_scowr[i].softc = sc;
+                       sc->sc_scowr[i].busy = 0;
+               }
+       }
+
+       unit->hci_flags &= ~BTF_XMIT;
+       unit->hci_flags |= BTF_RUNNING;
+       
+       return 0;
+
+bad:
+       kprintf("ubt_enable: something going wrong... :( \n");
+       ubt_abortdealloc(sc);
+       return error;
+}
+
+void
+ubt_disable(struct hci_unit *unit)
+{
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       
+       DPRINTFN(1, "sc=%p\n", sc);
+
+       if ((unit->hci_flags & BTF_RUNNING) == 0)
+               return;
+
+       ubt_abortdealloc(sc);
+
+       unit->hci_flags &= ~BTF_RUNNING;
+}
+
+void
+ubt_xmit_cmd_start(struct hci_unit *unit)
+{
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       usb_device_request_t req;
+       usbd_status status;
+       struct mbuf *m;
+       int len;
+
+       if (sc->sc_dying)
+               return;
+
+
+
+       if (IF_QEMPTY(&unit->hci_cmdq)) 
+               return;
+       IF_DEQUEUE(&unit->hci_cmdq, m);
+       DPRINTFN(15, " %s: xmit CMD packet (%d bytes)\n",
+                       unit->hci_devname, m->m_pkthdr.len);
+
+       sc->sc_refcnt++;
+
+       unit->hci_flags |= BTF_XMIT_CMD;
+
+       len = m->m_pkthdr.len - 1;
+       m_copydata(m, 1, len, sc->sc_cmd_buf);
+       m_freem(m);
+
+       memset(&req, 0, sizeof(req));
+       req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+       USETW(req.wLength, len);
+
+       usbd_setup_default_xfer(sc->sc_cmd_xfer,
+                               sc->sc_udev,
+                               unit,
+                               UBT_CMD_TIMEOUT,
+                               &req,
+                               sc->sc_cmd_buf,
+                               len,
+                               USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+                               ubt_xmit_cmd_complete);
+
+       status = usbd_transfer(sc->sc_cmd_xfer);
+
+       KKASSERT(status != USBD_NORMAL_COMPLETION);
+
+       if (status != USBD_IN_PROGRESS) {
+               DPRINTF("usbd_transfer status=%s (%d)\n",
+                       usbd_errstr(status), status);
+               sc->sc_refcnt--;
+               unit->hci_flags &= ~BTF_XMIT_CMD;
+       }
+       
+}
+
+void
+ubt_xmit_cmd_complete(usbd_xfer_handle xfer,
+                       usbd_private_handle h, usbd_status status)
+{
+       struct hci_unit *unit = h;
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       uint32_t count;
+
+       DPRINTFN(15, " %s: CMD complete status=%s (%d)\n",
+                       unit->hci_devname, usbd_errstr(status), status);
+
+       unit->hci_flags &= ~BTF_XMIT_CMD;
+
+       if (--sc->sc_refcnt < 0) {
+               DPRINTF("sc_refcnt=%d\n", sc->sc_refcnt);
+               usb_detach_wakeup(sc->sc_dev);
+               return;
+       }
+
+       if (sc->sc_dying) {
+               DPRINTF("sc_dying\n");
+               return;
+       }
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("status=%s (%d)\n",
+                       usbd_errstr(status), status);
+               unit->hci_stats.err_tx++;
+               return;
+       }
+
+       usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+
+       unit->hci_stats.cmd_tx++;
+       unit->hci_stats.byte_tx += count;
+
+       ubt_xmit_cmd_start(unit);
+}
+
+void
+ubt_xmit_acl_start(struct hci_unit *unit)
+{
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       struct mbuf *m;
+       usbd_status status;
+       int len;
+
+       if (sc->sc_dying)
+               return;
+
+
+       if (IF_QEMPTY(&unit->hci_acltxq)) 
+               return;
+
+       sc->sc_refcnt++;
+       unit->hci_flags |= BTF_XMIT_ACL;
+
+       IF_DEQUEUE(&unit->hci_acltxq, m);
+
+       DPRINTFN(15, "%s: xmit ACL packet (%d bytes)\n",
+                       unit->hci_devname, m->m_pkthdr.len);
+
+       len = m->m_pkthdr.len - 1;
+       if (len > UBT_BUFSIZ_ACL) {
+               DPRINTF("%s: truncating ACL packet (%d => %d)!\n",
+                       unit->hci_devname, len, UBT_BUFSIZ_ACL);
+
+               len = UBT_BUFSIZ_ACL;
+       }
+
+       m_copydata(m, 1, len, sc->sc_aclwr_buf);
+       m_freem(m);
+
+       unit->hci_stats.acl_tx++;
+       unit->hci_stats.byte_tx += len;
+
+       usbd_setup_xfer(sc->sc_aclwr_xfer,
+                       sc->sc_aclwr_pipe,
+                       unit,
+                       sc->sc_aclwr_buf,
+                       len,
+                       USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+                       UBT_ACL_TIMEOUT,
+                       ubt_xmit_acl_complete);
+
+       status = usbd_transfer(sc->sc_aclwr_xfer);
+
+       KKASSERT(status != USBD_NORMAL_COMPLETION);
+
+       if (status != USBD_IN_PROGRESS) {
+               DPRINTF("usbd_transfer status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               sc->sc_refcnt--;
+               unit->hci_flags &= ~BTF_XMIT_ACL;
+       }
+       
+}
+
+void
+ubt_xmit_acl_complete(usbd_xfer_handle xfer,
+               usbd_private_handle h, usbd_status status)
+{
+       struct hci_unit *unit = h;
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+
+       DPRINTFN(15, "%s: ACL complete status=%s (%d)\n",
+               unit->hci_devname, usbd_errstr(status), status);
+
+       unit->hci_flags &= ~BTF_XMIT_ACL;
+
+       if (--sc->sc_refcnt < 0) {
+               usb_detach_wakeup(sc->sc_dev);
+               return;
+       }
+
+       if (sc->sc_dying)
+               return;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               unit->hci_stats.err_tx++;
+
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->sc_aclwr_pipe);
+               else
+                       return;
+       }
+
+       ubt_xmit_acl_start(unit);
+       
+}
+
+void
+ubt_xmit_sco_start(struct hci_unit *unit)
+{
+       struct ubt_softc *sc = device_get_softc(unit->hci_softc);
+       int i;
+
+       if (sc->sc_dying || sc->sc_scowr_size == 0)
+               return;
+
+       for (i = 0 ; i < UBT_NXFERS ; i++) {
+               if (sc->sc_scowr[i].busy)
+                       continue;
+
+               ubt_xmit_sco_start1(sc, &sc->sc_scowr[i]);
+       }
+}
+
+void
+ubt_xmit_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
+{
+       struct mbuf *m;
+       uint8_t *buf;
+       int num, len, size, space;
+
+       if (sc->sc_dying)
+               return;
+
+       space = sc->sc_scowr_size * UBT_NFRAMES;
+       buf = isoc->buf;
+       len = 0;
+
+       /*
+        * Fill the request buffer with data from the queue,
+        * keeping any leftover packet on our private hook.
+        *
+        * Complete packets are passed back up to the stack
+        * for disposal, since we can't rely on the controller
+        * to tell us when it has finished with them.
+        */
+
+       m = sc->sc_scowr_mbuf;
+       while (space > 0) {
+               if (m == NULL) {
+                       crit_enter();
+                       IF_DEQUEUE(&sc->sc_unit.hci_scotxq, m);
+                       crit_exit();
+                       if (m == NULL)
+                               break;
+
+                       m_adj(m, 1);    /* packet type */
+
+               }
+
+               if (m->m_pkthdr.len > 0) {
+                       size = MIN(m->m_pkthdr.len, space);
+
+                       m_copydata(m, 0, size, buf);
+                       m_adj(m, size);
+
+                       buf += size;
+                       len += size;
+                       space -= size;
+               }
+
+               if (m->m_pkthdr.len == 0) {
+                       sc->sc_unit.hci_stats.sco_tx++;
+                       hci_complete_sco(&sc->sc_unit, m);
+                       m = NULL;
+               }
+       }
+       sc->sc_scowr_mbuf = m;
+
+       DPRINTFN(15, "isoc=%p, len=%d, space=%d\n", isoc, len, space);
+
+       if (len == 0)   /* nothing to send */
+       
+               return;
+
+       sc->sc_refcnt++;
+       sc->sc_unit.hci_flags |= BTF_XMIT_SCO;
+       sc->sc_unit.hci_stats.byte_tx += len;
+       isoc->busy = 1;
+
+       /*
+        * calculate number of isoc frames and sizes
+        */
+
+       for (num = 0 ; len > 0 ; num++) {
+               size = MIN(sc->sc_scowr_size, len);
+
+               isoc->size[num] = size;
+               len -= size;
+       }
+
+       usbd_setup_isoc_xfer(isoc->xfer,
+                            sc->sc_scowr_pipe,
+                            isoc,
+                            isoc->size,
+                            num,
+                            USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+                            ubt_xmit_sco_complete);
+
+       usbd_transfer(isoc->xfer);
+}
+
+void
+ubt_xmit_sco_complete(usbd_xfer_handle xfer,
+               usbd_private_handle h, usbd_status status)
+{
+       struct ubt_isoc_xfer *isoc = h;
+       struct ubt_softc *sc;
+       int i;
+
+       KKASSERT(xfer == isoc->xfer);
+       sc = isoc->softc;
+
+       DPRINTFN(15, "isoc=%p, status=%s (%d)\n",
+               isoc, usbd_errstr(status), status);
+
+       isoc->busy = 0;
+
+       for (i = 0 ; ; i++) {
+               if (i == UBT_NXFERS) {
+                       sc->sc_unit.hci_flags &= ~BTF_XMIT_SCO;
+                       break;
+               }
+
+               if (sc->sc_scowr[i].busy)
+                       break;
+       }
+
+       if (--sc->sc_refcnt < 0) {
+               usb_detach_wakeup(sc->sc_dev);
+               return;
+       }
+
+       if (sc->sc_dying)
+               return;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               sc->sc_unit.hci_stats.err_tx++;
+
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->sc_scowr_pipe);
+               else
+                       return;
+       }
+
+       ubt_xmit_sco_start(&sc->sc_unit);
+}
+
+/*
+ * Load incoming data into an mbuf with leading type byte.
+ */
+static struct mbuf *
+ubt_mbufload(uint8_t *buf, int count, uint8_t type)
+{
+       struct mbuf *m;
+       MGETHDR(m, MB_DONTWAIT, MT_DATA);
+       if (m == NULL) {kprintf(" MGETHDR return NULL\n");
+               return NULL; }
+       *mtod(m, uint8_t *) = type;
+       m->m_pkthdr.len = m->m_len = MHLEN;
+       m_copyback(m, 1, count, buf);   /* (extends if necessary)*/
+       if (m->m_pkthdr.len != MAX(MHLEN, count + 1)) {
+               m_free(m);
+               kprintf(" m->m_pkthdr.len != MAX() \n");
+               return NULL;
+       }
+       m->m_pkthdr.len = count + 1;
+       m->m_len = MIN(MHLEN, m->m_pkthdr.len);
+       return m;
+}
+
+void
+ubt_recv_event(usbd_xfer_handle xfer, usbd_private_handle h, usbd_status status)
+{
+       struct ubt_softc *sc = h;
+       struct mbuf *m;
+       uint32_t count;
+       void *buf;
+
+       DPRINTFN(15, "sc=%p status=%s (%d)\n",
+                   sc, usbd_errstr(status), status);
+
+       if (status != USBD_NORMAL_COMPLETION || sc->sc_dying)
+               return;
+
+       usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
+
+       if (count < sizeof(hci_event_hdr_t) - 1) {
+               DPRINTF("dumped undersized event (count = %d)\n", count);
+               sc->sc_unit.hci_stats.err_rx++;
+               return;
+       }
+
+       sc->sc_unit.hci_stats.evt_rx++;
+       sc->sc_unit.hci_stats.byte_rx += count;
+
+       m = ubt_mbufload(buf, count, HCI_EVENT_PKT);
+       if (m != NULL){
+               hci_input_event(&sc->sc_unit, m);
+               }
+       else
+               sc->sc_unit.hci_stats.err_rx++; 
+}
+
+void
+ubt_recv_acl_start(struct ubt_softc *sc)
+{
+       usbd_status status;
+
+       DPRINTFN(15, "sc=%p\n", sc);
+
+       if (sc->sc_aclrd_busy || sc->sc_dying) {
+               DPRINTF("sc_aclrd_busy=%d, sc_dying=%d\n",
+                       sc->sc_aclrd_busy,
+                       sc->sc_dying);
+
+               return;
+       }
+
+       sc->sc_refcnt++;
+       sc->sc_aclrd_busy = 1;
+
+       usbd_setup_xfer(sc->sc_aclrd_xfer,
+                       sc->sc_aclrd_pipe,
+                       sc,
+                       sc->sc_aclrd_buf,
+                       UBT_BUFSIZ_ACL,
+                       USBD_NO_COPY | USBD_SHORT_XFER_OK,
+                       USBD_NO_TIMEOUT,
+                       ubt_recv_acl_complete);
+
+       status = usbd_transfer(sc->sc_aclrd_xfer);
+
+       KKASSERT(status != USBD_NORMAL_COMPLETION);
+
+       if (status != USBD_IN_PROGRESS) {
+               DPRINTF("usbd_transfer status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               sc->sc_refcnt--;
+               sc->sc_aclrd_busy = 0;
+       }
+}
+
+void
+ubt_recv_acl_complete(usbd_xfer_handle xfer,
+               usbd_private_handle h, usbd_status status)
+{
+       struct ubt_softc *sc = h;
+       struct mbuf *m;
+       uint32_t count;
+       void *buf;
+
+       DPRINTFN(15, "sc=%p status=%s (%d)\n",
+                       sc, usbd_errstr(status), status);
+
+       sc->sc_aclrd_busy = 0;
+
+       if (--sc->sc_refcnt < 0) {
+               DPRINTF("refcnt = %d\n", sc->sc_refcnt);
+               usb_detach_wakeup(sc->sc_dev);
+               return;
+       }
+
+       if (sc->sc_dying) {
+               DPRINTF("sc_dying\n");
+               return;
+       }
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               sc->sc_unit.hci_stats.err_rx++;
+
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->sc_aclrd_pipe);
+               else
+                       return;
+       } else {
+               usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
+
+               if (count < sizeof(hci_acldata_hdr_t) - 1) {
+                       DPRINTF("dumped undersized packet (%d)\n", count);
+                       sc->sc_unit.hci_stats.err_rx++;
+               } else {
+                       sc->sc_unit.hci_stats.acl_rx++;
+                       sc->sc_unit.hci_stats.byte_rx += count;
+
+                       m = ubt_mbufload(buf, count, HCI_ACL_DATA_PKT);
+                       if (m != NULL)
+                               hci_input_acl(&sc->sc_unit, m);
+                       else
+                               sc->sc_unit.hci_stats.err_rx++;
+               }
+       }
+
+       /* and restart */
+       ubt_recv_acl_start(sc);
+}
+
+void
+ubt_recv_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
+{
+       int i;
+
+       DPRINTFN(15, "sc=%p, isoc=%p\n", sc, isoc);
+
+       if (isoc->busy || sc->sc_dying || sc->sc_scord_size == 0) {
+               DPRINTF("%s%s%s\n",
+                       isoc->busy ? " busy" : "",
+                       sc->sc_dying ? " dying" : "",
+                       sc->sc_scord_size == 0 ? " size=0" : "");
+
+               return;
+       }
+
+       sc->sc_refcnt++;
+       isoc->busy = 1;
+
+       for (i = 0 ; i < UBT_NFRAMES ; i++)
+               isoc->size[i] = sc->sc_scord_size;
+
+       usbd_setup_isoc_xfer(isoc->xfer,
+                            sc->sc_scord_pipe,
+                            isoc,
+                            isoc->size,
+                            UBT_NFRAMES,
+                            USBD_NO_COPY | USBD_SHORT_XFER_OK,
+                            ubt_recv_sco_complete);
+
+       usbd_transfer(isoc->xfer);
+}
+
+void
+ubt_recv_sco_complete(usbd_xfer_handle xfer,
+               usbd_private_handle h, usbd_status status)
+{
+       struct ubt_isoc_xfer *isoc = h;
+       struct ubt_softc *sc;
+       struct mbuf *m;
+       uint32_t count;
+       uint8_t *ptr, *frame;
+       int i, size, got, want;
+
+       KKASSERT(isoc != NULL);
+       KKASSERT(isoc->xfer == xfer);
+
+       sc = isoc->softc;
+       isoc->busy = 0;
+
+       if (--sc->sc_refcnt < 0) {
+               DPRINTF("refcnt=%d\n", sc->sc_refcnt);
+               usb_detach_wakeup(sc->sc_dev);
+               return;
+       }
+
+       if (sc->sc_dying) {
+               DPRINTF("sc_dying\n");
+               return;
+       }
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("status=%s (%d)\n",
+                       usbd_errstr(status), status);
+
+               sc->sc_unit.hci_stats.err_rx++;
+
+               if (status == USBD_STALLED) {
+                       usbd_clear_endpoint_stall_async(sc->sc_scord_pipe);
+                       goto restart;
+               }
+
+               return;
+       }
+
+       usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+       if (count == 0)
+               goto restart;
+
+       DPRINTFN(15, "sc=%p, isoc=%p, count=%u\n",
+                       sc, isoc, count);
+
+       sc->sc_unit.hci_stats.byte_rx += count;
+
+       /*
+        * Extract SCO packets from ISOC frames. The way we have it,
+        * no SCO packet can be bigger than MHLEN. This is unlikely
+        * to actually happen, but if we ran out of mbufs and lost
+        * sync then we may get spurious data that makes it seem that
+        * way, so we discard data that wont fit. This doesnt really
+        * help with the lost sync situation alas.
+        */
+
+       m = sc->sc_scord_mbuf;
+       if (m != NULL) {
+               sc->sc_scord_mbuf = NULL;
+               ptr = mtod(m, uint8_t *) + m->m_pkthdr.len;
+               got = m->m_pkthdr.len;
+               want = sizeof(hci_scodata_hdr_t);
+               if (got >= want)
+                       want += mtod(m, hci_scodata_hdr_t *)->length ;
+       } else {
+               ptr = NULL;
+               got = 0;
+               want = 0;
+       }
+
+       for (i = 0 ; i < UBT_NFRAMES ; i++) {
+               frame = isoc->buf + (i * sc->sc_scord_size);
+
+               while (isoc->size[i] > 0) {
+                       size = isoc->size[i];
+
+                       if (m == NULL) {
+                               MGETHDR(m, MB_DONTWAIT, MT_DATA);
+                               if (m == NULL) {
+                                       kprintf("%s: out of memory (xfer halted)\n",
+                                               device_get_nameunit(sc->sc_dev));
+
+                                       sc->sc_unit.hci_stats.err_rx++;
+                                       return;         /* lost sync */
+                               }
+
+                               ptr = mtod(m, uint8_t *);
+                               *ptr++ = HCI_SCO_DATA_PKT;
+                               got = 1;
+                               want = sizeof(hci_scodata_hdr_t);
+                       }
+
+                       if (got + size > want)
+                               size = want - got;
+
+                       if (got + size > MHLEN)
+                               memcpy(ptr, frame, MHLEN - got);
+                       else
+                               memcpy(ptr, frame, size);
+
+                       ptr += size;
+                       got += size;
+                       frame += size;
+
+                       if (got == want) {
+                               /*
+                                * If we only got a header, add the packet
+                                * length to our want count. Send complete
+                                * packets up to protocol stack.
+                                */
+                               if (want == sizeof(hci_scodata_hdr_t))
+                                       want += mtod(m, hci_scodata_hdr_t *)->length;
+
+                               if (got == want) {
+                                       m->m_pkthdr.len = m->m_len = got;
+                                       sc->sc_unit.hci_stats.sco_rx++;
+                                       hci_input_sco(&sc->sc_unit, m);
+                                       m = NULL;
+                               }
+                       }
+
+                       isoc->size[i] -= size;
+               }
+       }
+
+       if (m != NULL) {
+               m->m_pkthdr.len = m->m_len = got;
+               sc->sc_scord_mbuf = m;
+       }
+
+restart: /* and restart */
+       ubt_recv_sco_start1(sc, isoc);
+}
index 440fc8c..46affa9 100644 (file)
@@ -65,7 +65,7 @@
  *
  *     @(#)netisr.h    8.1 (Berkeley) 6/10/93
  * $FreeBSD: src/sys/net/netisr.h,v 1.21.2.5 2002/02/09 23:02:39 luigi Exp $
- * $DragonFly: src/sys/net/netisr.h,v 1.28 2007/09/30 04:37:27 sephe Exp $
+ * $DragonFly: src/sys/net/netisr.h,v 1.29 2007/12/30 20:02:56 hasso Exp $
  */
 
 #ifndef _NET_NETISR_H_
@@ -99,6 +99,7 @@
 #define        NETISR_IPV6     28              /* same as AF_INET6 */
 #define        NETISR_NATM     29              /* same as AF_NATM */
 #define        NETISR_NETGRAPH 30              /* same as AF_NETGRAPH */
+#define        NETISR_BLUETOOTH 31
 
 #define        NETISR_MAX      32
 
diff --git a/sys/netbt/Makefile b/sys/netbt/Makefile
new file mode 100644 (file)
index 0000000..bdadc9a
--- /dev/null
@@ -0,0 +1,13 @@
+# $DragonFly: src/sys/netbt/Makefile,v 1.1 2007/12/30 20:02:56 hasso Exp $
+
+KMOD   = netbt
+SRCS   = sco_socket.c sco_upper.c bt_input.c bt_proto.c hci_event.c hci_ioctl.c
+SRCS   += hci_link.c hci_misc.c hci_socket.c hci_unit.c l2cap_lower.c
+SRCS   += l2cap_misc.c l2cap_signal.c l2cap_socket.c l2cap_upper.c
+SRCS   += rfcomm_dlc.c rfcomm_session.c rfcomm_socket.c rfcomm_upper.c
+SRCS   += bluetooth.h hci.h l2cap.h rfcomm.h sco.h 
+SRCS   += opt_usb.h device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
+
+
diff --git a/sys/netbt/bluetooth.h b/sys/netbt/bluetooth.h
new file mode 100644 (file)
index 0000000..e27e766
--- /dev/null
@@ -0,0 +1,157 @@
+/* $OpenBSD: bluetooth.h,v 1.4 2007/05/30 03:42:53 uwe Exp $ */
+/* $NetBSD: bluetooth.h,v 1.5 2007/04/21 06:15:22 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/bluetooth.h,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#ifndef _NETBT_BLUETOOTH_H_
+#define _NETBT_BLUETOOTH_H_
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+/*
+ * Bluetooth Address Family Protocol Numbers
+ */
+#define BTPROTO_HCI    1
+#define BTPROTO_L2CAP  2
+#define BTPROTO_RFCOMM 3
+#define BTPROTO_SCO    4
+
+/* All sizes are in bytes */
+#define BLUETOOTH_BDADDR_SIZE  6
+
+/*
+ * Bluetooth device address
+ */
+typedef struct {
+       uint8_t b[BLUETOOTH_BDADDR_SIZE];
+} __attribute__ ((packed)) bdaddr_t;
+
+/*
+ * bdaddr utility functions
+ */
+static __inline int
+bdaddr_same(const bdaddr_t *a, const bdaddr_t *b)
+{
+
+       return (a->b[0] == b->b[0] && a->b[1] == b->b[1]
+               && a->b[2] == b->b[2] && a->b[3] == b->b[3]
+               && a->b[4] == b->b[4] && a->b[5] == b->b[5]);
+}
+
+static __inline int
+bdaddr_any(const bdaddr_t *a)
+{
+
+       return (a->b[0] == 0 && a->b[1] == 0 && a->b[2] == 0
+               && a->b[3] == 0 && a->b[4] == 0 && a->b[5] == 0);
+}
+
+static __inline void
+bdaddr_copy(bdaddr_t *d, const bdaddr_t *s)
+{
+
+       d->b[0] = s->b[0];
+       d->b[1] = s->b[1];
+       d->b[2] = s->b[2];
+       d->b[3] = s->b[3];
+       d->b[4] = s->b[4];
+       d->b[5] = s->b[5];
+}
+
+/*
+ * Socket address used by Bluetooth protocols
+ */
+struct sockaddr_bt {
+       uint8_t         bt_len;
+       sa_family_t     bt_family;
+       bdaddr_t        bt_bdaddr;
+       uint16_t        bt_psm;
+       uint8_t         bt_channel;
+       uint8_t         bt_zero[5];
+};
+
+/* Note: this is actually 6 bytes including terminator */
+#define BDADDR_ANY     ((const bdaddr_t *) "\000\000\000\000\000")
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/bus.h>
+
+MALLOC_DECLARE(M_BLUETOOTH);
+
+/*
+ * Bluetooth Protocol API callback methods
+ */
+struct mbuf;
+struct btproto {
+       void (*connecting)(void *);
+       void (*connected)(void *);
+       void (*disconnected)(void *, int);
+       void *(*newconn)(void *, struct sockaddr_bt *, struct sockaddr_bt *);
+       void (*complete)(void *, int);
+       void (*linkmode)(void *, int);
+       void (*input)(void *, struct mbuf *);
+};
+
+/*
+ * Debugging stuff
+ */
+#ifdef BLUETOOTH_DEBUG
+extern int bluetooth_debug;
+# define DPRINTF(fmt, args...) do {                    \
+       if (bluetooth_debug)                            \
+               kprintf("%s: "fmt, __func__ , ##args);  \
+} while (/* CONSTCOND */0)
+
+# define DPRINTFN(n, fmt, args...)     do {            \
+       if (bluetooth_debug > (n))                      \
+               kprintf("%s: "fmt, __func__ , ##args);  \
+} while (/* CONSTCOND */0)
+
+# define UNKNOWN(value)                        \
+               kprintf("%s: %s = %d unknown!\n", __func__, #value, (value));
+#else
+# define DPRINTF(...)
+# define DPRINTFN(...)
+# define UNKNOWN(x)
+#endif /* BLUETOOTH_DEBUG */
+
+#define sbspace(sb) \
+    ((long) imin((int)((sb)->ssb_hiwat - (sb)->ssb_cc), \
+         (int)((sb)->ssb_mbmax - (sb)->ssb_mbcnt)))
+#endif /* _KERNEL */
+
+#define letoh16(x)              le16toh(x)
+#define letoh32(x)              le32toh(x)
+
+#endif /* _NETBT_BLUETOOTH_H_ */
diff --git a/sys/netbt/bt_input.c b/sys/netbt/bt_input.c
new file mode 100644 (file)
index 0000000..7a24f09
--- /dev/null
@@ -0,0 +1,37 @@
+/* $OpenBSD: bt_input.c,v 1.5 2007/06/24 20:55:27 uwe Exp $ */
+/* $DragonFly: src/sys/netbt/bt_input.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*
+ * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+
+#include <net/netisr.h>
+
+#include <netbt/hci.h>
+
+void
+btintr(struct netmsg *msg)
+{
+       struct hci_unit *unit;
+
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               hci_intr(unit);
+       }
+}
diff --git a/sys/netbt/bt_proto.c b/sys/netbt/bt_proto.c
new file mode 100644 (file)
index 0000000..b8c287d
--- /dev/null
@@ -0,0 +1,190 @@
+/* $OpenBSD: bt_proto.c,v 1.4 2007/06/24 20:55:27 uwe Exp $ */
+/* $DragonFly: src/sys/netbt/bt_proto.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*
+ * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <net/if.h>
+#include <net/pf/pfvar.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+#include <netbt/l2cap.h>
+#include <netbt/rfcomm.h>
+#include <netbt/sco.h>
+
+MALLOC_DEFINE(M_BLUETOOTH, "Bluetooth", "Bluetooth system memory");
+
+extern struct pr_usrreqs hci_usrreqs;
+
+static int
+netbt_modevent(module_t mod, int type, void *data)
+{
+       switch (type) {
+       case MOD_LOAD:
+               break;
+       case MOD_UNLOAD:
+               return EBUSY;
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static moduledata_t netbt_mod = {
+       "netbt",
+       netbt_modevent,
+       NULL
+};
+
+DECLARE_MODULE(netbt, netbt_mod, SI_SUB_EXEC, SI_ORDER_ANY);
+
+struct domain btdomain;
+
+struct protosw btsw[] = {
+       { /* raw HCI commands */
+               .pr_type = SOCK_RAW,
+               .pr_domain = &btdomain,
+               .pr_protocol = BTPROTO_HCI,
+               .pr_flags = (PR_ADDR | PR_ATOMIC),
+               .pr_input = 0,
+               .pr_output = 0,
+               .pr_ctlinput = 0,
+               .pr_ctloutput = hci_ctloutput,
+               .pr_mport = cpu0_soport,
+               .pr_init = 0,
+               .pr_fasttimo =  0,
+               .pr_slowtimo = 0,
+               .pr_drain = 0,
+               .pr_usrreqs = &hci_usrreqs
+       },
+       { /* HCI SCO data (audio) */
+               .pr_type = SOCK_SEQPACKET,
+               .pr_domain = &btdomain,
+               .pr_protocol = BTPROTO_SCO,
+               .pr_flags = (PR_CONNREQUIRED | PR_ATOMIC ),
+               .pr_input = 0,
+               .pr_output = 0,
+               .pr_ctlinput = 0,
+               .pr_ctloutput = sco_ctloutput,
+               .pr_mport = cpu0_soport,
+               .pr_init = 0,
+               .pr_fasttimo =  0,
+               .pr_slowtimo = 0,
+               .pr_drain = 0,
+               .pr_usrreqs = &sco_usrreqs
+
+       },
+       { /* L2CAP Connection Oriented */
+               .pr_type = SOCK_SEQPACKET,
+               .pr_domain = &btdomain,
+               .pr_protocol = BTPROTO_L2CAP,
+               .pr_flags = (PR_CONNREQUIRED | PR_ATOMIC ),
+               .pr_input = 0,
+               .pr_output = 0,
+               .pr_ctlinput = 0,
+               .pr_ctloutput = l2cap_ctloutput,
+               .pr_mport = cpu0_soport,
+               .pr_init = 0,
+               .pr_fasttimo =  0,
+               .pr_slowtimo = 0,
+               .pr_drain = 0,
+               .pr_usrreqs = &l2cap_usrreqs
+       },
+       { /* RFCOMM */
+               .pr_type = SOCK_STREAM,
+               .pr_domain = &btdomain,
+               .pr_protocol = BTPROTO_RFCOMM,
+               .pr_flags = (PR_CONNREQUIRED | PR_WANTRCVD),
+               .pr_input = 0,
+               .pr_output = 0,
+               .pr_ctlinput = 0,
+               .pr_ctloutput = rfcomm_ctloutput,
+               .pr_mport = cpu0_soport,
+               .pr_init = 0,
+               .pr_fasttimo =  0,
+               .pr_slowtimo = 0,
+               .pr_drain = 0,
+               .pr_usrreqs = &rfcomm_usrreqs
+       },
+};
+
+static void
+netbt_dispose(struct mbuf* m)
+{
+       ZONE_DESTROY(l2cap_pdu_pool);
+       ZONE_DESTROY(l2cap_req_pool);
+       ZONE_DESTROY(rfcomm_credit_pool);
+}
+
+static void
+netbt_init(void)
+{
+       int error = 1;
+       do {
+               ZONE_CREATE(l2cap_pdu_pool, struct l2cap_pdu, "l2cap_pdu");
+               ZONE_CREATE(l2cap_req_pool, struct l2cap_req, "l2cap_req");
+               ZONE_CREATE(rfcomm_credit_pool, struct rfcomm_credit,
+                   "rfcomm_credit");
+               error = 0;
+       } while(0); 
+
+       if (error) {
+               netbt_dispose(NULL);
+               panic("Can't create vm_zones");
+       }
+}
+
+struct domain btdomain = {
+       .dom_family = AF_BLUETOOTH,
+       .dom_name = "bluetooth",
+       .dom_init = netbt_init,
+       .dom_externalize = NULL,
+       .dom_dispose = netbt_dispose,
+       .dom_protosw = btsw,
+       .dom_protoswNPROTOSW = &btsw[sizeof(btsw)/sizeof(btsw[0])],
+       .dom_next = SLIST_ENTRY_INITIALIZER,
+       .dom_rtattach = 0,
+       .dom_rtoffset = 32,
+       .dom_maxrtkey = sizeof(struct sockaddr_bt),
+       .dom_ifattach = 0,
+       .dom_ifdetach = 0,
+};
+
+DOMAIN_SET(bt);
+SYSCTL_NODE(_net, PF_BLUETOOTH, bt, CTLFLAG_RW, 0, "Bluetooth protocol");
+SYSCTL_NODE(_net_bt, BTPROTO_HCI, hci_unit, CTLFLAG_RW, 0,
+    "Bluetooth HCI protocol");
+
+static void
+netisr_netbt_setup(void *dummy __unused)
+{
+       netisr_register(NETISR_BLUETOOTH, cpu0_portfn, btintr);
+}
+
+SYSINIT(netbt_setup, SI_BOOT2_KLD, SI_ORDER_ANY, netisr_netbt_setup, NULL);
diff --git a/sys/netbt/hci.h b/sys/netbt/hci.h
new file mode 100644 (file)
index 0000000..0dbc400
--- /dev/null
@@ -0,0 +1,2236 @@
+/* $OpenBSD: hci.h,v 1.9 2007/07/22 21:05:00 gwk Exp $ */
+/* $NetBSD: hci.h,v 1.10 2007/04/21 06:15:23 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/hci.h,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+/*-
+ * 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.
+ *
+ * $FreeBSD: src/sys/netgraph/bluetooth/include/ng_hci.h,v 1.6 2005/01/07 01:45:43 imp Exp $
+ */
+
+/*
+ * This file contains everything that applications need to know from
+ * Host Controller Interface (HCI). Information taken from Bluetooth
+ * Core Specifications (v1.1 and v2.0)
+ *
+ * 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 _NETBT_HCI_H_
+#define _NETBT_HCI_H_
+
+#include <netbt/bluetooth.h>
+
+/**************************************************************************
+ **************************************************************************
+ **                   Common defines and types (HCI)
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_LAP_SIZE                   3   /* unit LAP */
+#define HCI_KEY_SIZE                   16  /* link key */
+#define HCI_PIN_SIZE                   16  /* link PIN */
+#define HCI_EVENT_MASK_SIZE            8   /* event mask */
+#define HCI_CLASS_SIZE                 3   /* unit class */
+#define HCI_FEATURES_SIZE              8   /* LMP features */
+#define HCI_UNIT_NAME_SIZE             248 /* unit name size */
+#define HCI_DEVNAME_SIZE               16  /* same as dv_xname */
+
+/* HCI specification */
+#define HCI_SPEC_V10                   0x00 /* v1.0 */
+#define HCI_SPEC_V11                   0x01 /* v1.1 */
+#define HCI_SPEC_V12                   0x02 /* v1.2 */
+#define HCI_SPEC_V20                   0x03 /* v2.0 */
+/* 0x02 - 0xFF - reserved for future use */
+
+/* LMP features (and page 0 of extended features) */
+/* ------------------- byte 0 --------------------*/
+#define HCI_LMP_3SLOT                  0x01
+#define HCI_LMP_5SLOT                  0x02
+#define HCI_LMP_ENCRYPTION             0x04
+#define HCI_LMP_SLOT_OFFSET            0x08
+#define HCI_LMP_TIMIACCURACY           0x10
+#define HCI_LMP_ROLE_SWITCH            0x20
+#define HCI_LMP_HOLD_MODE              0x40
+#define HCI_LMP_SNIFF_MODE             0x80
+/* ------------------- byte 1 --------------------*/
+#define HCI_LMP_PARK_MODE              0x01
+#define HCI_LMP_RSSI                   0x02
+#define HCI_LMP_CHANNEL_QUALITY                0x04
+#define HCI_LMP_SCO_LINK               0x08
+#define HCI_LMP_HV2_PKT                        0x10
+#define HCI_LMP_HV3_PKT                        0x20
+#define HCI_LMP_ULAW_LOG               0x40
+#define HCI_LMP_ALAW_LOG               0x80
+/* ------------------- byte 2 --------------------*/
+#define HCI_LMP_CVSD                   0x01
+#define HCI_LMP_PAGISCHEME             0x02
+#define HCI_LMP_POWER_CONTROL          0x04
+#define HCI_LMP_TRANSPARENT_SCO                0x08
+#define HCI_LMP_FLOW_CONTROL_LAG0      0x10
+#define HCI_LMP_FLOW_CONTROL_LAG1      0x20
+#define HCI_LMP_FLOW_CONTROL_LAG2      0x40
+#define HCI_LMP_BC_ENCRYPTION          0x80
+/* ------------------- byte 3 --------------------*/
+/* reserved                            0x01 */
+#define HCI_LMP_EDR_ACL_2MBPS          0x02
+#define HCI_LMP_EDR_ACL_3MBPS          0x04
+#define HCI_LMP_ENHANCED_ISCAN         0x08
+#define HCI_LMP_INTERLACED_ISCAN       0x10
+#define HCI_LMP_INTERLACED_PSCAN       0x20
+#define HCI_LMP_RSSI_INQUIRY           0x40
+#define HCI_LMP_EV3_PKT                        0x80
+/* ------------------- byte 4 --------------------*/
+#define HCI_LMP_EV4_PKT                        0x01
+#define HCI_LMP_EV5_PKT                        0x02
+/* reserved                            0x04 */
+#define HCI_LMP_AFH_CAPABLE_SLAVE      0x08
+#define HCI_LMP_AFH_CLASS_SLAVE                0x10
+/* reserved                            0x20 */
+/* reserved                            0x40 */
+#define HCI_LMP_3SLOT_EDR_ACL          0x80
+/* ------------------- byte 5 --------------------*/
+#define HCI_LMP_5SLOT_EDR_ACL          0x01
+/* reserved                            0x02 */
+/* reserved                            0x04 */
+#define HCI_LMP_AFH_CAPABLE_MASTER     0x08
+#define HCI_LMP_AFH_CLASS_MASTER       0x10
+#define HCI_LMP_EDR_eSCO_2MBPS         0x20
+#define HCI_LMP_EDR_eSCO_3MBPS         0x40
+#define HCI_LMP_3SLOT_EDR_eSCO         0x80
+/* ------------------- byte 6 --------------------*/
+/* reserved                                      */
+/* ------------------- byte 7 --------------------*/
+#define HCI_LMP_EXTENDED_FEATURES      0x80
+
+/* Link types */
+#define HCI_LINK_SCO                   0x00 /* Voice */
+#define HCI_LINK_ACL                   0x01 /* Data */
+#define HCI_LINK_eSCO                  0x02 /* eSCO */
+/* 0x03 - 0xFF - reserved for future use */
+
+/*
+ * ACL/SCO packet type bits are set to enable the
+ * packet type, except for 2MBPS and 3MBPS when they
+ * are unset to enable the packet type.
+ */
+/* ACL Packet types for "Create Connection" */
+#define HCI_PKT_2MBPS_DH1      0x0002
+#define HCI_PKT_3MBPS_DH1      0x0004
+#define HCI_PKT_DM1            0x0008
+#define HCI_PKT_DH1            0x0010
+#define HCI_PKT_2MBPS_DH3      0x0100
+#define HCI_PKT_3MBPS_DH3      0x0200
+#define HCI_PKT_DM3            0x0400
+#define HCI_PKT_DH3            0x0800
+#define HCI_PKT_2MBPS_DH5      0x1000
+#define HCI_PKT_3MBPS_DH5      0x2000
+#define HCI_PKT_DM5            0x4000
+#define HCI_PKT_DH5            0x8000
+
+/* SCO Packet types for "Setup Synchronous Connection" */
+#define HCI_PKT_HV1            0x0001
+#define HCI_PKT_HV2            0x0002
+#define HCI_PKT_HV3            0x0004
+#define HCI_PKT_EV3            0x0008
+#define HCI_PKT_EV4            0x0010
+#define HCI_PKT_EV5            0x0020
+#define HCI_PKT_2MBPS_EV3      0x0040
+#define HCI_PKT_3MBPS_EV3      0x0080
+#define HCI_PKT_2MBPS_EV5      0x0100
+#define HCI_PKT_3MBPS_EV5      0x0200
+
+/*
+ * 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 :)
+ */
+
+/* Page scan modes (are deprecated) */
+#define HCI_MANDATORY_PAGE_SCAN_MODE           0x00
+#define HCI_OPTIONAL_PAGE_SCAN_MODE1           0x01
+#define HCI_OPTIONAL_PAGE_SCAN_MODE2           0x02
+#define HCI_OPTIONAL_PAGE_SCAN_MODE3           0x03
+/* 0x04 - 0xFF - reserved for future use */
+
+/* Page scan repetition modes */
+#define HCI_SCAN_REP_MODE0                     0x00
+#define HCI_SCAN_REP_MODE1                     0x01
+#define HCI_SCAN_REP_MODE2                     0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Page scan period modes */
+#define HCI_PAGE_SCAN_PERIOD_MODE0             0x00
+#define HCI_PAGE_SCAN_PERIOD_MODE1             0x01
+#define HCI_PAGE_SCAN_PERIOD_MODE2             0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Scan enable */
+#define HCI_NO_SCAN_ENABLE                     0x00
+#define HCI_INQUIRY_SCAN_ENABLE                        0x01
+#define HCI_PAGE_SCAN_ENABLE                   0x02
+/* 0x04 - 0xFF - reserved for future use */
+
+/* Hold mode activities */
+#define HCI_HOLD_MODE_NO_CHANGE                        0x00
+#define HCI_HOLD_MODE_SUSPEND_PAGE_SCAN                0x01
+#define HCI_HOLD_MODE_SUSPEND_INQUIRY_SCAN     0x02
+#define HCI_HOLD_MODE_SUSPEND_PERIOD_INQUIRY   0x04
+/* 0x08 - 0x80 - reserved for future use */
+
+/* Connection roles */
+#define HCI_ROLE_MASTER                                0x00
+#define HCI_ROLE_SLAVE                         0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Key flags */
+#define HCI_USE_SEMI_PERMANENT_LINK_KEYS       0x00
+#define HCI_USE_TEMPORARY_LINK_KEY             0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Pin types */
+#define HCI_PIN_TYPE_VARIABLE                  0x00
+#define HCI_PIN_TYPE_FIXED                     0x01
+
+/* Link key types */
+#define HCI_LINK_KEY_TYPE_COMBINATION_KEY      0x00
+#define HCI_LINK_KEY_TYPE_LOCAL_UNIT_KEY       0x01
+#define HCI_LINK_KEY_TYPE_REMOTE_UNIT_KEY      0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Encryption modes */
+#define HCI_ENCRYPTION_MODE_NONE               0x00
+#define HCI_ENCRYPTION_MODE_P2P                        0x01
+#define HCI_ENCRYPTION_MODE_ALL                        0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Quality of service types */
+#define HCI_SERVICE_TYPE_NO_TRAFFIC            0x00
+#define HCI_SERVICE_TYPE_BEST_EFFORT           0x01
+#define HCI_SERVICE_TYPE_GUARANTEED            0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Link policy settings */
+#define HCI_LINK_POLICY_DISABLE_ALL_LM_MODES   0x0000
+#define HCI_LINK_POLICY_ENABLE_ROLE_SWITCH     0x0001 /* Master/Slave switch */
+#define HCI_LINK_POLICY_ENABLE_HOLD_MODE       0x0002
+#define HCI_LINK_POLICY_ENABLE_SNIFF_MODE      0x0004
+#define HCI_LINK_POLICY_ENABLE_PARK_MODE       0x0008
+/* 0x0010 - 0x8000 - reserved for future use */
+
+/* Event masks */
+#define HCI_EVMSK_ALL                          0x00000000ffffffff
+#define HCI_EVMSK_NONE                         0x0000000000000000
+#define HCI_EVMSK_INQUIRY_COMPL                        0x0000000000000001
+#define HCI_EVMSK_INQUIRY_RESULT               0x0000000000000002
+#define HCI_EVMSK_CON_COMPL                    0x0000000000000004
+#define HCI_EVMSK_CON_REQ                      0x0000000000000008
+#define HCI_EVMSK_DISCON_COMPL                 0x0000000000000010
+#define HCI_EVMSK_AUTH_COMPL                   0x0000000000000020
+#define HCI_EVMSK_REMOTE_NAME_REQ_COMPL                0x0000000000000040
+#define HCI_EVMSK_ENCRYPTION_CHANGE            0x0000000000000080
+#define HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL    0x0000000000000100
+#define HCI_EVMSK_MASTER_LINK_KEY_COMPL                0x0000000000000200
+#define HCI_EVMSK_READ_REMOTE_FEATURES_COMPL   0x0000000000000400
+#define HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL   0x0000000000000800
+#define HCI_EVMSK_QOS_SETUP_COMPL              0x0000000000001000
+#define HCI_EVMSK_COMMAND_COMPL                        0x0000000000002000
+#define HCI_EVMSK_COMMAND_STATUS               0x0000000000004000
+#define HCI_EVMSK_HARDWARE_ERROR               0x0000000000008000
+#define HCI_EVMSK_FLUSH_OCCUR                  0x0000000000010000
+#define HCI_EVMSK_ROLE_CHANGE                  0x0000000000020000
+#define HCI_EVMSK_NUM_COMPL_PKTS               0x0000000000040000
+#define HCI_EVMSK_MODE_CHANGE                  0x0000000000080000
+#define HCI_EVMSK_RETURN_LINK_KEYS             0x0000000000100000
+#define HCI_EVMSK_PIN_CODE_REQ                 0x0000000000200000
+#define HCI_EVMSK_LINK_KEY_REQ                 0x0000000000400000
+#define HCI_EVMSK_LINK_KEY_NOTIFICATION                0x0000000000800000
+#define HCI_EVMSK_LOOPBACK_COMMAND             0x0000000001000000
+#define HCI_EVMSK_DATA_BUFFER_OVERFLOW         0x0000000002000000
+#define HCI_EVMSK_MAX_SLOT_CHANGE              0x0000000004000000
+#define HCI_EVMSK_READ_CLOCK_OFFSET_COMLETE    0x0000000008000000
+#define HCI_EVMSK_CON_PKT_TYPE_CHANGED         0x0000000010000000
+#define HCI_EVMSK_QOS_VIOLATION                        0x0000000020000000
+#define HCI_EVMSK_PAGE_SCAN_MODE_CHANGE                0x0000000040000000
+#define HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE    0x0000000080000000
+/* 0x0000000100000000 - 0x8000000000000000 - reserved for future use */
+
+/* Filter types */
+#define HCI_FILTER_TYPE_NONE                   0x00
+#define HCI_FILTER_TYPE_INQUIRY_RESULT         0x01
+#define HCI_FILTER_TYPE_CON_SETUP              0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Filter condition types for HCI_FILTER_TYPE_INQUIRY_RESULT */
+#define HCI_FILTER_COND_INQUIRY_NEW_UNIT       0x00
+#define HCI_FILTER_COND_INQUIRY_UNIT_CLASS     0x01
+#define HCI_FILTER_COND_INQUIRY_BDADDR         0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Filter condition types for HCI_FILTER_TYPE_CON_SETUP */
+#define HCI_FILTER_COND_CON_ANY_UNIT           0x00
+#define HCI_FILTER_COND_CON_UNIT_CLASS         0x01
+#define HCI_FILTER_COND_CON_BDADDR             0x02
+/* 0x03 - 0xFF - reserved for future use */
+
+/* Xmit level types */
+#define HCI_XMIT_LEVEL_CURRENT                 0x00
+#define HCI_XMIT_LEVEL_MAXIMUM                 0x01
+/* 0x02 - 0xFF - reserved for future use */
+
+/* Host Controller to Host flow control */
+#define HCI_HC2H_FLOW_CONTROL_NONE             0x00
+#define HCI_HC2H_FLOW_CONTROL_ACL              0x01
+#define HCI_HC2H_FLOW_CONTROL_SCO              0x02
+#define HCI_HC2H_FLOW_CONTROL_BOTH             0x03
+/* 0x04 - 0xFF - reserved future use */
+
+/* Loopback modes */
+#define HCI_LOOPBACK_NONE                      0x00
+#define HCI_LOOPBACK_LOCAL                     0x01
+#define 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 HCI_OPCODE(gf,cf)              ((((gf) & 0x3f) << 10) | ((cf) & 0x3ff))
+#define HCI_OCF(op)                    ((op) & 0x3ff)
+#define HCI_OGF(op)                    (((op) >> 10) & 0x3f)
+
+/*
+ * Macro(s) to extract/combine connection handle, BC (Broadcast) and
+ * PB (Packet boundary) flags.
+ */
+
+#define HCI_CON_HANDLE(h)              ((h) & 0x0fff)
+#define HCI_PB_FLAG(h)                 (((h) & 0x3000) >> 12)
+#define HCI_BC_FLAG(h)                 (((h) & 0xc000) >> 14)
+#define HCI_MK_CON_HANDLE(h, pb, bc) \
+       (((h) & 0x0fff) | (((pb) & 3) << 12) | (((bc) & 3) << 14))
+
+/* PB flag values */
+                                       /* 00 - reserved for future use */
+#define        HCI_PACKET_FRAGMENT             0x1
+#define        HCI_PACKET_START                0x2
+                                       /* 11 - reserved for future use */
+
+/* BC flag values */
+#define HCI_POINT2POINT                        0x0 /* only Host controller to Host */
+#define HCI_BROADCAST_ACTIVE           0x1 /* both directions */
+#define HCI_BROADCAST_PICONET          0x2 /* both directions */
+                                       /* 11 - reserved for future use */
+
+/* HCI command packet header */
+typedef struct {
+       uint8_t         type;   /* MUST be 0x01 */
+       uint16_t        opcode; /* OpCode */
+       uint8_t         length; /* parameter(s) length in bytes */
+} __attribute__ ((__packed__)) hci_cmd_hdr_t;
+
+#define HCI_CMD_PKT                    0x01
+#define HCI_CMD_PKT_SIZE               (sizeof(hci_cmd_hdr_t) + 0xff)
+
+/* ACL data packet header */
+typedef struct {
+       uint8_t         type;        /* MUST be 0x02 */
+       uint16_t        con_handle;  /* connection handle + PB + BC flags */
+       uint16_t        length;      /* payload length in bytes */
+} __attribute__ ((__packed__)) hci_acldata_hdr_t;
+
+#define HCI_ACL_DATA_PKT               0x02
+#define HCI_ACL_PKT_SIZE               (sizeof(hci_acldata_hdr_t) + 0xffff)
+
+/* SCO data packet header */
+typedef struct {
+       uint8_t         type;       /* MUST be 0x03 */
+       uint16_t        con_handle; /* connection handle + reserved bits */
+       uint8_t         length;     /* payload length in bytes */
+} __attribute__ ((__packed__)) hci_scodata_hdr_t;
+
+#define HCI_SCO_DATA_PKT               0x03
+#define HCI_SCO_PKT_SIZE               (sizeof(hci_scodata_hdr_t) + 0xff)
+
+/* HCI event packet header */
+typedef struct {
+       uint8_t         type;   /* MUST be 0x04 */
+       uint8_t         event;  /* event */
+       uint8_t         length; /* parameter(s) length in bytes */
+} __attribute__ ((__packed__)) hci_event_hdr_t;
+
+#define HCI_EVENT_PKT                  0x04
+#define HCI_EVENT_PKT_SIZE             (sizeof(hci_event_hdr_t) + 0xff)
+
+/* HCI status return parameter */
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+} __attribute__ ((__packed__)) hci_status_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x01   Link control commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_LINK_CONTROL                   0x01
+
+#define HCI_OCF_INQUIRY                                        0x0001
+#define HCI_CMD_INQUIRY                                        0x0401
+typedef struct {
+       uint8_t         lap[HCI_LAP_SIZE]; /* LAP */
+       uint8_t         inquiry_length;    /* (N x 1.28) sec */
+       uint8_t         num_responses;     /* Max. # of responses */
+} __attribute__ ((__packed__)) hci_inquiry_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_INQUIRY_CANCEL                         0x0002
+#define HCI_CMD_INQUIRY_CANCEL                         0x0402
+/* No command parameter(s) */
+typedef hci_status_rp  hci_inquiry_cancel_rp;
+
+#define HCI_OCF_PERIODIC_INQUIRY                       0x0003
+#define HCI_CMD_PERIODIC_INQUIRY                       0x0403
+typedef struct {
+       uint16_t        max_period_length; /* Max. and min. amount of time */
+       uint16_t        min_period_length; /* between consecutive inquiries */
+       uint8_t         lap[HCI_LAP_SIZE]; /* LAP */
+       uint8_t         inquiry_length;    /* (inquiry_length * 1.28) sec */
+       uint8_t         num_responses;     /* Max. # of responses */
+} __attribute__ ((__packed__)) hci_periodic_inquiry_cp;
+
+typedef hci_status_rp  hci_periodic_inquiry_rp;
+
+#define HCI_OCF_EXIT_PERIODIC_INQUIRY                  0x0004
+#define HCI_CMD_EXIT_PERIODIC_INQUIRY                  0x0404
+/* No command parameter(s) */
+typedef hci_status_rp  hci_exit_periodic_inquiry_rp;
+
+#define HCI_OCF_CREATE_CON                             0x0005
+#define HCI_CMD_CREATE_CON                             0x0405
+typedef struct {
+       bdaddr_t        bdaddr;             /* destination address */
+       uint16_t        pkt_type;           /* packet type */
+       uint8_t         page_scan_rep_mode; /* page scan repetition mode */
+       uint8_t         page_scan_mode;     /* reserved - set to 0x00 */
+       uint16_t        clock_offset;       /* clock offset */
+       uint8_t         accept_role_switch; /* accept role switch? 0x00 == No */
+} __attribute__ ((__packed__)) hci_create_con_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_DISCONNECT                             0x0006
+#define HCI_CMD_DISCONNECT                             0x0406
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         reason;     /* reason to disconnect */
+} __attribute__ ((__packed__)) hci_discon_cp;
+/* No return parameter(s) */
+
+/* Add SCO Connection is deprecated */
+#define HCI_OCF_ADD_SCO_CON                            0x0007
+#define HCI_CMD_ADD_SCO_CON                            0x0407
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        pkt_type;   /* packet type */
+} __attribute__ ((__packed__)) hci_add_sco_con_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_CREATE_CON_CANCEL                      0x0008
+#define HCI_CMD_CREATE_CON_CANCEL                      0x0408
+typedef struct {
+       bdaddr_t        bdaddr;         /* destination address */
+} __attribute__ ((__packed__)) hci_create_con_cancel_cp;
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       bdaddr_t        bdaddr;         /* destination address */
+} __attribute__ ((__packed__)) hci_create_con_cancel_rp;
+
+#define HCI_OCF_ACCEPT_CON                             0x0009
+#define HCI_CMD_ACCEPT_CON                             0x0409
+typedef struct {
+       bdaddr_t        bdaddr; /* address of unit to be connected */
+       uint8_t         role;   /* connection role */
+} __attribute__ ((__packed__)) hci_accept_con_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_REJECT_CON                             0x000a
+#define HCI_CMD_REJECT_CON                             0x040A
+typedef struct {
+       bdaddr_t        bdaddr; /* remote address */
+       uint8_t         reason; /* reason to reject */
+} __attribute__ ((__packed__)) hci_reject_con_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_LINK_KEY_REP                           0x000b
+#define HCI_CMD_LINK_KEY_REP                           0x040B
+typedef struct {
+       bdaddr_t        bdaddr;            /* remote address */
+       uint8_t         key[HCI_KEY_SIZE]; /* key */
+} __attribute__ ((__packed__)) hci_link_key_rep_cp;
+
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* unit address */
+} __attribute__ ((__packed__)) hci_link_key_rep_rp;
+
+#define HCI_OCF_LINK_KEY_NEG_REP                       0x000c
+#define HCI_CMD_LINK_KEY_NEG_REP                       0x040C
+typedef struct {
+       bdaddr_t        bdaddr; /* remote address */
+} __attribute__ ((__packed__)) hci_link_key_neg_rep_cp;
+
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* unit address */
+} __attribute__ ((__packed__)) hci_link_key_neg_rep_rp;
+
+#define HCI_OCF_PIN_CODE_REP                           0x000d
+#define HCI_CMD_PIN_CODE_REP                           0x040D
+typedef struct {
+       bdaddr_t        bdaddr;               /* remote address */
+       uint8_t         pin_size;             /* pin code length (in bytes) */
+       uint8_t         pin[HCI_PIN_SIZE];    /* pin code */
+} __attribute__ ((__packed__)) hci_pin_code_rep_cp;
+
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* unit address */
+} __attribute__ ((__packed__)) hci_pin_code_rep_rp;
+
+#define HCI_OCF_PIN_CODE_NEG_REP                       0x000e
+#define HCI_CMD_PIN_CODE_NEG_REP                       0x040E
+typedef struct {
+       bdaddr_t        bdaddr; /* remote address */
+} __attribute__ ((__packed__)) hci_pin_code_neg_rep_cp;
+
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* unit address */
+} __attribute__ ((__packed__)) hci_pin_code_neg_rep_rp;
+
+#define HCI_OCF_CHANGE_CON_PACKET_TYPE                 0x000f
+#define HCI_CMD_CHANGE_CON_PACKET_TYPE                 0x040F
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        pkt_type;   /* packet type */
+} __attribute__ ((__packed__)) hci_change_con_pkt_type_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_AUTH_REQ                               0x0011
+#define HCI_CMD_AUTH_REQ                               0x0411
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_auth_req_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_SET_CON_ENCRYPTION                     0x0013
+#define HCI_CMD_SET_CON_ENCRYPTION                     0x0413
+typedef struct {
+       uint16_t        con_handle;        /* connection handle */
+       uint8_t         encryption_enable; /* 0x00 - disable, 0x01 - enable */
+} __attribute__ ((__packed__)) hci_set_con_encryption_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_CHANGE_CON_LINK_KEY                    0x0015
+#define HCI_CMD_CHANGE_CON_LINK_KEY                    0x0415
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_change_con_link_key_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_MASTER_LINK_KEY                                0x0017
+#define HCI_CMD_MASTER_LINK_KEY                                0x0417
+typedef struct {
+       uint8_t         key_flag; /* key flag */
+} __attribute__ ((__packed__)) hci_master_link_key_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_REMOTE_NAME_REQ                                0x0019
+#define HCI_CMD_REMOTE_NAME_REQ                                0x0419
+typedef struct {
+       bdaddr_t        bdaddr;             /* remote address */
+       uint8_t         page_scan_rep_mode; /* page scan repetition mode */
+       uint8_t         page_scan_mode;     /* page scan mode */
+       uint16_t        clock_offset;       /* clock offset */
+} __attribute__ ((__packed__)) hci_remote_name_req_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_REMOTE_NAME_REQ_CANCEL                 0x001a
+#define HCI_CMD_REMOTE_NAME_REQ_CANCEL                 0x041A
+typedef struct {
+       bdaddr_t        bdaddr;         /* remote address */
+} __attribute__ ((__packed__)) hci_remote_name_req_cancel_cp;
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       bdaddr_t        bdaddr;         /* remote address */
+} __attribute__ ((__packed__)) hci_remote_name_req_cancel_rp;
+
+#define HCI_OCF_READ_REMOTE_FEATURES                   0x001b
+#define HCI_CMD_READ_REMOTE_FEATURES                   0x041B
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_remote_features_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_REMOTE_EXTENDED_FEATURES          0x001c
+#define HCI_CMD_READ_REMOTE_EXTENDED_FEATURES          0x041C
+typedef struct {
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         page;           /* page number */
+} __attribute__ ((__packed__)) hci_read_remote_extended_features_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_REMOTE_VER_INFO                   0x001d
+#define HCI_CMD_READ_REMOTE_VER_INFO                   0x041D
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_remote_ver_info_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_CLOCK_OFFSET                      0x001f
+#define HCI_CMD_READ_CLOCK_OFFSET                      0x041F
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_clock_offset_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_LMP_HANDLE                                0x0020
+#define HCI_CMD_READ_LMP_HANDLE                                0x0420
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_lmp_handle_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         lmp_handle; /* LMP handle */
+       uint32_t        reserved;   /* reserved */
+} __attribute__ ((__packed__)) hci_read_lmp_handle_rp;
+
+#define HCI_OCF_SETUP_SCO_CON                          0x0028
+#define HCI_CMD_SETUP_SCO_CON                          0x0428
+typedef struct {
+       uint16_t        con_handle;     /* connection handle */
+       uint32_t        tx_bandwidth;   /* transmit bandwidth */
+       uint32_t        rx_bandwidth;   /* receive bandwidth */
+       uint16_t        latency;        /* maximum latency */
+       uint16_t        voice;          /* voice setting */
+       uint8_t         rt_effort;      /* retransmission effort */
+       uint16_t        pkt_type;       /* packet types */
+} __attribute__ ((__packed__)) hci_setup_sco_con_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_ACCEPT_SCO_CON_REQ                     0x0029
+#define HCI_CMD_ACCEPT_SCO_CON_REQ                     0x0429
+typedef struct {
+       bdaddr_t        bdaddr;         /* remote address */
+       uint32_t        tx_bandwidth;   /* transmit bandwidth */
+       uint32_t        rx_bandwidth;   /* receive bandwidth */
+       uint16_t        latency;        /* maximum latency */
+       uint16_t        content;        /* voice setting */
+       uint8_t         rt_effort;      /* retransmission effort */
+       uint16_t        pkt_type;       /* packet types */
+} __attribute__ ((__packed__)) hci_accept_sco_con_req_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_REJECT_SCO_CON_REQ                     0x002a
+#define HCI_CMD_REJECT_SCO_CON_REQ                     0x042a
+typedef struct {
+       bdaddr_t        bdaddr;         /* remote address */
+       uint8_t         reason;         /* reject error code */
+} __attribute__ ((__packed__)) hci_reject_sco_con_req_cp;
+/* No return parameter(s) */
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x02   Link policy commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_LINK_POLICY                    0x02
+
+#define HCI_OCF_HOLD_MODE                              0x0001
+#define HCI_CMD_HOLD_MODE                              0x0801
+typedef struct {
+       uint16_t        con_handle;   /* connection handle */
+       uint16_t        max_interval; /* (max_interval * 0.625) msec */
+       uint16_t        min_interval; /* (max_interval * 0.625) msec */
+} __attribute__ ((__packed__)) hci_hold_mode_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_SNIFF_MODE                             0x0003
+#define HCI_CMD_SNIFF_MODE                             0x0803
+typedef struct {
+       uint16_t        con_handle;   /* connection handle */
+       uint16_t        max_interval; /* (max_interval * 0.625) msec */
+       uint16_t        min_interval; /* (max_interval * 0.625) msec */
+       uint16_t        attempt;      /* (2 * attempt - 1) * 0.625 msec */
+       uint16_t        timeout;      /* (2 * attempt - 1) * 0.625 msec */
+} __attribute__ ((__packed__)) hci_sniff_mode_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_EXIT_SNIFF_MODE                                0x0004
+#define HCI_CMD_EXIT_SNIFF_MODE                                0x0804
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_exit_sniff_mode_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_PARK_MODE                              0x0005
+#define HCI_CMD_PARK_MODE                              0x0805
+typedef struct {
+       uint16_t        con_handle;   /* connection handle */
+       uint16_t        max_interval; /* (max_interval * 0.625) msec */
+       uint16_t        min_interval; /* (max_interval * 0.625) msec */
+} __attribute__ ((__packed__)) hci_park_mode_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_EXIT_PARK_MODE                         0x0006
+#define HCI_CMD_EXIT_PARK_MODE                         0x0806
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_exit_park_mode_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_QOS_SETUP                              0x0007
+#define HCI_CMD_QOS_SETUP                              0x0807
+typedef struct {
+       uint16_t        con_handle;      /* connection handle */
+       uint8_t         flags;           /* reserved for future use */
+       uint8_t         service_type;    /* service type */
+       uint32_t        token_rate;      /* bytes per second */
+       uint32_t        peak_bandwidth;  /* bytes per second */
+       uint32_t        latency;         /* microseconds */
+       uint32_t        delay_variation; /* microseconds */
+} __attribute__ ((__packed__)) hci_qos_setup_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_ROLE_DISCOVERY                         0x0009
+#define HCI_CMD_ROLE_DISCOVERY                         0x0809
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_role_discovery_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         role;       /* role for the connection handle */
+} __attribute__ ((__packed__)) hci_role_discovery_rp;
+
+#define HCI_OCF_SWITCH_ROLE                            0x000b
+#define HCI_CMD_SWITCH_ROLE                            0x080B
+typedef struct {
+       bdaddr_t        bdaddr; /* remote address */
+       uint8_t         role;   /* new local role */
+} __attribute__ ((__packed__)) hci_switch_role_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_LINK_POLICY_SETTINGS              0x000c
+#define HCI_CMD_READ_LINK_POLICY_SETTINGS              0x080C
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_link_policy_settings_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        settings;   /* link policy settings */
+} __attribute__ ((__packed__)) hci_read_link_policy_settings_rp;
+
+#define HCI_OCF_WRITE_LINK_POLICY_SETTINGS             0x000d
+#define HCI_CMD_WRITE_LINK_POLICY_SETTINGS             0x080D
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        settings;   /* link policy settings */
+} __attribute__ ((__packed__)) hci_write_link_policy_settings_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_write_link_policy_settings_rp;
+
+#define HCI_OCF_READ_DEFAULT_LINK_POLICY_SETTINGS      0x000e
+#define HCI_CMD_READ_DEFAULT_LINK_POLICY_SETTINGS      0x080E
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        settings;   /* link policy settings */
+} __attribute__ ((__packed__)) hci_read_default_link_policy_settings_rp;
+
+#define HCI_OCF_WRITE_DEFAULT_LINK_POLICY_SETTINGS     0x000f
+#define HCI_CMD_WRITE_DEFAULT_LINK_POLICY_SETTINGS     0x080F
+typedef struct {
+       uint16_t        settings;   /* link policy settings */
+} __attribute__ ((__packed__)) hci_write_default_link_policy_settings_cp;
+
+typedef hci_status_rp  hci_write_default_link_policy_settings_rp;
+
+#define HCI_OCF_FLOW_SPECIFICATION                     0x0010
+#define HCI_CMD_FLOW_SPECIFICATION                     0x0810
+typedef struct {
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         flags;          /* reserved */
+       uint8_t         flow_direction;
+       uint8_t         service_type;
+       uint32_t        token_rate;
+       uint32_t        token_bucket;
+       uint32_t        peak_bandwidth;
+       uint32_t        latency;
+} __attribute__ ((__packed__)) hci_flow_specification_cp;
+/* No return parameter(s) */
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x03   Host Controller and Baseband commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_HC_BASEBAND                    0x03
+
+#define HCI_OCF_SET_EVENT_MASK                         0x0001
+#define HCI_CMD_SET_EVENT_MASK                         0x0C01
+typedef struct {
+       uint8_t         event_mask[HCI_EVENT_MASK_SIZE]; /* event_mask */
+} __attribute__ ((__packed__)) hci_set_event_mask_cp;
+
+typedef hci_status_rp  hci_set_event_mask_rp;
+
+#define HCI_OCF_RESET                                  0x0003
+#define HCI_CMD_RESET                                  0x0C03
+/* No command parameter(s) */
+typedef hci_status_rp  hci_reset_rp;
+
+#define HCI_OCF_SET_EVENT_FILTER                       0x0005
+#define HCI_CMD_SET_EVENT_FILTER                       0x0C05
+typedef struct {
+       uint8_t         filter_type;           /* filter type */
+       uint8_t         filter_condition_type; /* filter condition type */
+/* variable size condition
+       uint8_t         condition[]; -- conditions */
+} __attribute__ ((__packed__)) hci_set_event_filter_cp;
+
+typedef hci_status_rp  hci_set_event_filter_rp;
+
+#define HCI_OCF_FLUSH                                  0x0008
+#define HCI_CMD_FLUSH                                  0x0C08
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_flush_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_flush_rp;
+
+#define HCI_OCF_READ_PIN_TYPE                          0x0009
+#define HCI_CMD_READ_PIN_TYPE                          0x0C09
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;   /* 0x00 - success */
+       uint8_t         pin_type; /* PIN type */
+} __attribute__ ((__packed__)) hci_read_pin_type_rp;
+
+#define HCI_OCF_WRITE_PIN_TYPE                         0x000a
+#define HCI_CMD_WRITE_PIN_TYPE                         0x0C0A
+typedef struct {
+       uint8_t         pin_type; /* PIN type */
+} __attribute__ ((__packed__)) hci_write_pin_type_cp;
+
+typedef hci_status_rp  hci_write_pin_type_rp;
+
+#define HCI_OCF_CREATE_NEW_UNIT_KEY                    0x000b
+#define HCI_CMD_CREATE_NEW_UNIT_KEY                    0x0C0B
+/* No command parameter(s) */
+typedef hci_status_rp  hci_create_new_unit_key_rp;
+
+#define HCI_OCF_READ_STORED_LINK_KEY                   0x000d
+#define HCI_CMD_READ_STORED_LINK_KEY                   0x0C0D
+typedef struct {
+       bdaddr_t        bdaddr;   /* address */
+       uint8_t         read_all; /* read all keys? 0x01 - yes */
+} __attribute__ ((__packed__)) hci_read_stored_link_key_cp;
+
+typedef struct {
+       uint8_t         status;        /* 0x00 - success */
+       uint16_t        max_num_keys;  /* Max. number of keys */
+       uint16_t        num_keys_read; /* Number of stored keys */
+} __attribute__ ((__packed__)) hci_read_stored_link_key_rp;
+
+#define HCI_OCF_WRITE_STORED_LINK_KEY                  0x0011
+#define HCI_CMD_WRITE_STORED_LINK_KEY                  0x0C11
+typedef struct {
+       uint8_t         num_keys_write; /* # of keys to write */
+/* these are repeated "num_keys_write" times
+       bdaddr_t        bdaddr;             --- remote address(es)
+       uint8_t         key[HCI_KEY_SIZE];  --- key(s) */
+} __attribute__ ((__packed__)) hci_write_stored_link_key_cp;
+
+typedef struct {
+       uint8_t         status;           /* 0x00 - success */
+       uint8_t         num_keys_written; /* # of keys successfully written */
+} __attribute__ ((__packed__)) hci_write_stored_link_key_rp;
+
+#define HCI_OCF_DELETE_STORED_LINK_KEY                 0x0012
+#define HCI_CMD_DELETE_STORED_LINK_KEY                 0x0C12
+typedef struct {
+       bdaddr_t        bdaddr;     /* address */
+       uint8_t         delete_all; /* delete all keys? 0x01 - yes */
+} __attribute__ ((__packed__)) hci_delete_stored_link_key_cp;
+
+typedef struct {
+       uint8_t         status;           /* 0x00 - success */
+       uint16_t        num_keys_deleted; /* Number of keys deleted */
+} __attribute__ ((__packed__)) hci_delete_stored_link_key_rp;
+
+#define HCI_OCF_WRITE_LOCAL_NAME                       0x0013
+#define HCI_CMD_WRITE_LOCAL_NAME                       0x0C13
+typedef struct {
+       char            name[HCI_UNIT_NAME_SIZE]; /* new unit name */
+} __attribute__ ((__packed__)) hci_write_local_name_cp;
+
+typedef hci_status_rp  hci_write_local_name_rp;
+
+#define HCI_OCF_READ_LOCAL_NAME                                0x0014
+#define HCI_CMD_READ_LOCAL_NAME                                0x0C14
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;                   /* 0x00 - success */
+       char            name[HCI_UNIT_NAME_SIZE]; /* unit name */
+} __attribute__ ((__packed__)) hci_read_local_name_rp;
+
+#define HCI_OCF_READ_CON_ACCEPT_TIMEOUT                        0x0015
+#define HCI_CMD_READ_CON_ACCEPT_TIMEOUT                        0x0C15
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;  /* 0x00 - success */
+       uint16_t        timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((__packed__)) hci_read_con_accept_timeout_rp;
+
+#define HCI_OCF_WRITE_CON_ACCEPT_TIMEOUT               0x0016
+#define HCI_CMD_WRITE_CON_ACCEPT_TIMEOUT               0x0C16
+typedef struct {
+       uint16_t        timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((__packed__)) hci_write_con_accept_timeout_cp;
+
+typedef hci_status_rp  hci_write_con_accept_timeout_rp;
+
+#define HCI_OCF_READ_PAGE_TIMEOUT                      0x0017
+#define HCI_CMD_READ_PAGE_TIMEOUT                      0x0C17
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;  /* 0x00 - success */
+       uint16_t        timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((__packed__)) hci_read_page_timeout_rp;
+
+#define HCI_OCF_WRITE_PAGE_TIMEOUT                     0x0018
+#define HCI_CMD_WRITE_PAGE_TIMEOUT                     0x0C18
+typedef struct {
+       uint16_t        timeout; /* (timeout * 0.625) msec */
+} __attribute__ ((__packed__)) hci_write_page_timeout_cp;
+
+typedef hci_status_rp  hci_write_page_timeout_rp;
+
+#define HCI_OCF_READ_SCAN_ENABLE                       0x0019
+#define HCI_CMD_READ_SCAN_ENABLE                       0x0C19
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;      /* 0x00 - success */
+       uint8_t         scan_enable; /* Scan enable */
+} __attribute__ ((__packed__)) hci_read_scan_enable_rp;
+
+#define HCI_OCF_WRITE_SCAN_ENABLE                      0x001a
+#define HCI_CMD_WRITE_SCAN_ENABLE                      0x0C1A
+typedef struct {
+       uint8_t         scan_enable; /* Scan enable */
+} __attribute__ ((__packed__)) hci_write_scan_enable_cp;
+
+typedef hci_status_rp  hci_write_scan_enable_rp;
+
+#define HCI_OCF_READ_PAGE_SCAN_ACTIVITY                        0x001b
+#define HCI_CMD_READ_PAGE_SCAN_ACTIVITY                        0x0C1B
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;             /* 0x00 - success */
+       uint16_t        page_scan_interval; /* interval * 0.625 msec */
+       uint16_t        page_scan_window;   /* window * 0.625 msec */
+} __attribute__ ((__packed__)) hci_read_page_scan_activity_rp;
+
+#define HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY               0x001c
+#define HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY               0x0C1C
+typedef struct {
+       uint16_t        page_scan_interval; /* interval * 0.625 msec */
+       uint16_t        page_scan_window;   /* window * 0.625 msec */
+} __attribute__ ((__packed__)) hci_write_page_scan_activity_cp;
+
+typedef hci_status_rp  hci_write_page_scan_activity_rp;
+
+#define HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY             0x001d
+#define HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY             0x0C1D
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;                /* 0x00 - success */
+       uint16_t        inquiry_scan_interval; /* interval * 0.625 msec */
+       uint16_t        inquiry_scan_window;   /* window * 0.625 msec */
+} __attribute__ ((__packed__)) hci_read_inquiry_scan_activity_rp;
+
+#define HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY            0x001e
+#define HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY            0x0C1E
+typedef struct {
+       uint16_t        inquiry_scan_interval; /* interval * 0.625 msec */
+       uint16_t        inquiry_scan_window;   /* window * 0.625 msec */
+} __attribute__ ((__packed__)) hci_write_inquiry_scan_activity_cp;
+
+typedef hci_status_rp  hci_write_inquiry_scan_activity_rp;
+
+#define HCI_OCF_READ_AUTH_ENABLE                       0x001f
+#define HCI_CMD_READ_AUTH_ENABLE                       0x0C1F
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;      /* 0x00 - success */
+       uint8_t         auth_enable; /* 0x01 - enabled */
+} __attribute__ ((__packed__)) hci_read_auth_enable_rp;
+
+#define HCI_OCF_WRITE_AUTH_ENABLE                      0x0020
+#define HCI_CMD_WRITE_AUTH_ENABLE                      0x0C20
+typedef struct {
+       uint8_t         auth_enable; /* 0x01 - enabled */
+} __attribute__ ((__packed__)) hci_write_auth_enable_cp;
+
+typedef hci_status_rp  hci_write_auth_enable_rp;
+
+#define HCI_OCF_READ_ENCRYPTION_MODE                   0x0021
+#define HCI_CMD_READ_ENCRYPTION_MODE                   0x0C21
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;          /* 0x00 - success */
+       uint8_t         encryption_mode; /* encryption mode */
+} __attribute__ ((__packed__)) hci_read_encryption_mode_rp;
+
+#define HCI_OCF_WRITE_ENCRYPTION_MODE                  0x0022
+#define HCI_CMD_WRITE_ENCRYPTION_MODE                  0x0C22
+typedef struct {
+       uint8_t         encryption_mode; /* encryption mode */
+} __attribute__ ((__packed__)) hci_write_encryption_mode_cp;
+
+typedef hci_status_rp  hci_write_encryption_mode_rp;
+
+#define HCI_OCF_READ_UNIT_CLASS                                0x0023
+#define HCI_CMD_READ_UNIT_CLASS                                0x0C23
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         uclass[HCI_CLASS_SIZE]; /* unit class */
+} __attribute__ ((__packed__)) hci_read_unit_class_rp;
+
+#define HCI_OCF_WRITE_UNIT_CLASS                       0x0024
+#define HCI_CMD_WRITE_UNIT_CLASS                       0x0C24
+typedef struct {
+       uint8_t         uclass[HCI_CLASS_SIZE]; /* unit class */
+} __attribute__ ((__packed__)) hci_write_unit_class_cp;
+
+typedef hci_status_rp  hci_write_unit_class_rp;
+
+#define HCI_OCF_READ_VOICE_SETTING                     0x0025
+#define HCI_CMD_READ_VOICE_SETTING                     0x0C25
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;   /* 0x00 - success */
+       uint16_t        settings; /* voice settings */
+} __attribute__ ((__packed__)) hci_read_voice_setting_rp;
+
+#define HCI_OCF_WRITE_VOICE_SETTING                    0x0026
+#define HCI_CMD_WRITE_VOICE_SETTING                    0x0C26
+typedef struct {
+       uint16_t        settings; /* voice settings */
+} __attribute__ ((__packed__)) hci_write_voice_setting_cp;
+
+typedef hci_status_rp  hci_write_voice_setting_rp;
+
+#define HCI_OCF_READ_AUTO_FLUSH_TIMEOUT                        0x0027
+#define HCI_CMD_READ_AUTO_FLUSH_TIMEOUT                        0x0C27
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_auto_flush_timeout_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        timeout;    /* 0x00 - no flush, timeout * 0.625 msec */
+} __attribute__ ((__packed__)) hci_read_auto_flush_timeout_rp;
+
+#define HCI_OCF_WRITE_AUTO_FLUSH_TIMEOUT               0x0028
+#define HCI_CMD_WRITE_AUTO_FLUSH_TIMEOUT               0x0C28
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        timeout;    /* 0x00 - no flush, timeout * 0.625 msec */
+} __attribute__ ((__packed__)) hci_write_auto_flush_timeout_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_write_auto_flush_timeout_rp;
+
+#define HCI_OCF_READ_NUM_BROADCAST_RETRANS             0x0029
+#define HCI_CMD_READ_NUM_BROADCAST_RETRANS             0x0C29
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;  /* 0x00 - success */
+       uint8_t         counter; /* number of broadcast retransmissions */
+} __attribute__ ((__packed__)) hci_read_num_broadcast_retrans_rp;
+
+#define HCI_OCF_WRITE_NUM_BROADCAST_RETRANS            0x002a
+#define HCI_CMD_WRITE_NUM_BROADCAST_RETRANS            0x0C2A
+typedef struct {
+       uint8_t         counter; /* number of broadcast retransmissions */
+} __attribute__ ((__packed__)) hci_write_num_broadcast_retrans_cp;
+
+typedef hci_status_rp  hci_write_num_broadcast_retrans_rp;
+
+#define HCI_OCF_READ_HOLD_MODE_ACTIVITY                        0x002b
+#define HCI_CMD_READ_HOLD_MODE_ACTIVITY                        0x0C2B
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;             /* 0x00 - success */
+       uint8_t         hold_mode_activity; /* Hold mode activities */
+} __attribute__ ((__packed__)) hci_read_hold_mode_activity_rp;
+
+#define HCI_OCF_WRITE_HOLD_MODE_ACTIVITY               0x002c
+#define HCI_CMD_WRITE_HOLD_MODE_ACTIVITY               0x0C2C
+typedef struct {
+       uint8_t         hold_mode_activity; /* Hold mode activities */
+} __attribute__ ((__packed__)) hci_write_hold_mode_activity_cp;
+
+typedef hci_status_rp  hci_write_hold_mode_activity_rp;
+
+#define HCI_OCF_READ_XMIT_LEVEL                                0x002d
+#define HCI_CMD_READ_XMIT_LEVEL                                0x0C2D
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         type;       /* Xmit level type */
+} __attribute__ ((__packed__)) hci_read_xmit_level_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       char            level;      /* -30 <= level <= 30 dBm */
+} __attribute__ ((__packed__)) hci_read_xmit_level_rp;
+
+#define HCI_OCF_READ_SCO_FLOW_CONTROL                  0x002e
+#define HCI_CMD_READ_SCO_FLOW_CONTROL                  0x0C2E
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;       /* 0x00 - success */
+       uint8_t         flow_control; /* 0x00 - disabled */
+} __attribute__ ((__packed__)) hci_read_sco_flow_control_rp;
+
+#define HCI_OCF_WRITE_SCO_FLOW_CONTROL                 0x002f
+#define HCI_CMD_WRITE_SCO_FLOW_CONTROL                 0x0C2F
+typedef struct {
+       uint8_t         flow_control; /* 0x00 - disabled */
+} __attribute__ ((__packed__)) hci_write_sco_flow_control_cp;
+
+typedef hci_status_rp  hci_write_sco_flow_control_rp;
+
+#define HCI_OCF_HC2H_FLOW_CONTROL                      0x0031
+#define HCI_CMD_HC2H_FLOW_CONTROL                      0x0C31
+typedef struct {
+       uint8_t         hc2h_flow; /* Host Controller to Host flow control */
+} __attribute__ ((__packed__)) hci_hc2h_flow_control_cp;
+
+typedef hci_status_rp  hci_h2hc_flow_control_rp;
+
+#define HCI_OCF_HOST_BUFFER_SIZE                       0x0033
+#define HCI_CMD_HOST_BUFFER_SIZE                       0x0C33
+typedef struct {
+       uint16_t        max_acl_size; /* Max. size of ACL packet (bytes) */
+       uint8_t         max_sco_size; /* Max. size of SCO packet (bytes) */
+       uint16_t        num_acl_pkts;  /* Max. number of ACL packets */
+       uint16_t        num_sco_pkts;  /* Max. number of SCO packets */
+} __attribute__ ((__packed__)) hci_host_buffer_size_cp;
+
+typedef hci_status_rp  hci_host_buffer_size_rp;
+
+#define HCI_OCF_HOST_NUM_COMPL_PKTS                    0x0035
+#define HCI_CMD_HOST_NUM_COMPL_PKTS                    0x0C35
+typedef struct {
+       uint8_t         nu_con_handles; /* # of connection handles */
+/* these are repeated "num_con_handles" times
+       uint16_t        con_handle;    --- connection handle(s)
+       uint16_t        compl_pkts;    --- # of completed packets */
+} __attribute__ ((__packed__)) hci_host_num_compl_pkts_cp;
+/* No return parameter(s) */
+
+#define HCI_OCF_READ_LINK_SUPERVISION_TIMEOUT          0x0036
+#define HCI_CMD_READ_LINK_SUPERVISION_TIMEOUT          0x0C36
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_link_supervision_timeout_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        timeout;    /* Link supervision timeout * 0.625 msec */
+} __attribute__ ((__packed__)) hci_read_link_supervision_timeout_rp;
+
+#define HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT         0x0037
+#define HCI_CMD_WRITE_LINK_SUPERVISION_TIMEOUT         0x0C37
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        timeout;    /* Link supervision timeout * 0.625 msec */
+} __attribute__ ((__packed__)) hci_write_link_supervision_timeout_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_write_link_supervision_timeout_rp;
+
+#define HCI_OCF_READ_NUM_SUPPORTED_IAC                 0x0038
+#define HCI_CMD_READ_NUM_SUPPORTED_IAC                 0x0C38
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;  /* 0x00 - success */
+       uint8_t         num_iac; /* # of supported IAC during scan */
+} __attribute__ ((__packed__)) hci_read_num_supported_iac_rp;
+
+#define HCI_OCF_READ_IAC_LAP                           0x0039
+#define HCI_CMD_READ_IAC_LAP                           0x0C39
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;  /* 0x00 - success */
+       uint8_t         num_iac; /* # of IAC */
+/* these are repeated "num_iac" times
+       uint8_t         laps[HCI_LAP_SIZE]; --- LAPs */
+} __attribute__ ((__packed__)) hci_read_iac_lap_rp;
+
+#define HCI_OCF_WRITE_IAC_LAP                          0x003a
+#define HCI_CMD_WRITE_IAC_LAP                          0x0C3A
+typedef struct {
+       uint8_t         num_iac; /* # of IAC */
+/* these are repeated "num_iac" times
+       uint8_t         laps[HCI_LAP_SIZE]; --- LAPs */
+} __attribute__ ((__packed__)) hci_write_iac_lap_cp;
+
+typedef hci_status_rp  hci_write_iac_lap_rp;
+
+#define HCI_OCF_READ_PAGE_SCAN_PERIOD                  0x003b
+#define HCI_CMD_READ_PAGE_SCAN_PERIOD                  0x0C3B
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;                /* 0x00 - success */
+       uint8_t         page_scan_period_mode; /* Page scan period mode */
+} __attribute__ ((__packed__)) hci_read_page_scan_period_rp;
+
+#define HCI_OCF_WRITE_PAGE_SCAN_PERIOD                 0x003c
+#define HCI_CMD_WRITE_PAGE_SCAN_PERIOD                 0x0C3C
+typedef struct {
+       uint8_t         page_scan_period_mode; /* Page scan period mode */
+} __attribute__ ((__packed__)) hci_write_page_scan_period_cp;
+
+typedef hci_status_rp  hci_write_page_scan_period_rp;
+
+/* Read Page Scan Mode is deprecated */
+#define HCI_OCF_READ_PAGE_SCAN                         0x003d
+#define HCI_CMD_READ_PAGE_SCAN                         0x0C3D
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         page_scan_mode; /* Page scan mode */
+} __attribute__ ((__packed__)) hci_read_page_scan_rp;
+
+/* Write Page Scan Mode is deprecated */
+#define HCI_OCF_WRITE_PAGE_SCAN                                0x003e
+#define HCI_CMD_WRITE_PAGE_SCAN                                0x0C3E
+typedef struct {
+       uint8_t         page_scan_mode; /* Page scan mode */
+} __attribute__ ((__packed__)) hci_write_page_scan_cp;
+
+typedef hci_status_rp  hci_write_page_scan_rp;
+
+#define HCI_OCF_SET_AFH_CLASSIFICATION                 0x003f
+#define HCI_CMD_SET_AFH_CLASSIFICATION                 0x0C3F
+typedef struct {
+       uint8_t         classification[10];
+} __attribute__ ((__packed__)) hci_set_afh_classification_cp;
+
+typedef hci_status_rp  hci_set_afh_classification_rp;
+
+#define HCI_OCF_READ_INQUIRY_SCAN_TYPE                 0x0042
+#define HCI_CMD_READ_INQUIRY_SCAN_TYPE                 0x0C42
+/* No command parameter(s) */
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         type;           /* inquiry scan type */
+} __attribute__ ((__packed__)) hci_read_inquiry_scan_type_rp;
+
+#define HCI_OCF_WRITE_INQUIRY_SCAN_TYPE                        0x0043
+#define HCI_CMD_WRITE_INQUIRY_SCAN_TYPE                        0x0C43
+typedef struct {
+       uint8_t         type;           /* inquiry scan type */
+} __attribute__ ((__packed__)) hci_write_inquiry_scan_type_cp;
+
+typedef hci_status_rp  hci_write_inquiry_scan_type_rp;
+
+#define HCI_OCF_READ_INQUIRY_MODE                      0x0044
+#define HCI_CMD_READ_INQUIRY_MODE                      0x0C44
+/* No command parameter(s) */
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         mode;           /* inquiry mode */
+} __attribute__ ((__packed__)) hci_read_inquiry_mode_rp;
+
+#define HCI_OCF_WRITE_INQUIRY_MODE                     0x0045
+#define HCI_CMD_WRITE_INQUIRY_MODE                     0x0C45
+typedef struct {
+       uint8_t         mode;           /* inquiry mode */
+} __attribute__ ((__packed__)) hci_write_inquiry_mode_cp;
+
+typedef hci_status_rp  hci_write_inquiry_mode_rp;
+
+#define HCI_OCF_READ_PAGE_SCAN_TYPE                    0x0046
+#define HCI_CMD_READ_PAGE_SCAN_TYPE                    0x0C46
+/* No command parameter(s) */
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         type;           /* page scan type */
+} __attribute__ ((__packed__)) hci_read_page_scan_type_rp;
+
+#define HCI_OCF_WRITE_PAGE_SCAN_TYPE                   0x0047
+#define HCI_CMD_WRITE_PAGE_SCAN_TYPE                   0x0C47
+typedef struct {
+       uint8_t         type;           /* page scan type */
+} __attribute__ ((__packed__)) hci_write_page_scan_type_cp;
+
+typedef hci_status_rp  hci_write_page_scan_type_rp;
+
+#define HCI_OCF_READ_AFH_ASSESSMENT                    0x0048
+#define HCI_CMD_READ_AFH_ASSESSMENT                    0x0C48
+/* No command parameter(s) */
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         mode;           /* assessment mode */
+} __attribute__ ((__packed__)) hci_read_afh_assessment_rp;
+
+#define HCI_OCF_WRITE_AFH_ASSESSMENT                   0x0049
+#define HCI_CMD_WRITE_AFH_ASSESSMENT                   0x0C49
+typedef struct {
+       uint8_t         mode;           /* assessment mode */
+} __attribute__ ((__packed__)) hci_write_afh_assessment_cp;
+
+typedef hci_status_rp  hci_write_afh_assessment_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x04   Informational commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_INFO                           0x04
+
+#define HCI_OCF_READ_LOCAL_VER                         0x0001
+#define HCI_CMD_READ_LOCAL_VER                         0x1001
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         hci_version;    /* HCI version */
+       uint16_t        hci_revision;   /* HCI revision */
+       uint8_t         lmp_version;    /* LMP version */
+       uint16_t        manufacturer;   /* Hardware manufacturer name */
+       uint16_t        lmp_subversion; /* LMP sub-version */
+} __attribute__ ((__packed__)) hci_read_local_ver_rp;
+
+#define HCI_OCF_READ_LOCAL_COMMANDS                    0x0002
+#define HCI_CMD_READ_LOCAL_COMMANDS                    0x1002
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         commands[64];   /* opcode bitmask */
+} __attribute__ ((__packed__)) hci_read_local_commands_rp;
+
+#define HCI_OCF_READ_LOCAL_FEATURES                    0x0003
+#define HCI_CMD_READ_LOCAL_FEATURES                    0x1003
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;                      /* 0x00 - success */
+       uint8_t         features[HCI_FEATURES_SIZE]; /* LMP features bitmsk*/
+} __attribute__ ((__packed__)) hci_read_local_features_rp;
+
+#define HCI_OCF_READ_LOCAL_EXTENDED_FEATURES           0x0004
+#define HCI_CMD_READ_LOCAL_EXTENDED_FEATURES           0x1004
+typedef struct {
+       uint8_t         page;           /* page number */
+} __attribute__ ((__packed__)) hci_read_local_extended_features_cp;
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint8_t         page;           /* page number */
+       uint8_t         max_page;       /* maximum page number */
+       uint8_t         features[HCI_FEATURES_SIZE];    /* LMP features */
+} __attribute__ ((__packed__)) hci_read_local_extended_features_rp;
+
+#define HCI_OCF_READ_BUFFER_SIZE                       0x0005
+#define HCI_CMD_READ_BUFFER_SIZE                       0x1005
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;       /* 0x00 - success */
+       uint16_t        max_acl_size; /* Max. size of ACL packet (bytes) */
+       uint8_t         max_sco_size; /* Max. size of SCO packet (bytes) */
+       uint16_t        num_acl_pkts;  /* Max. number of ACL packets */
+       uint16_t        num_sco_pkts;  /* Max. number of SCO packets */
+} __attribute__ ((__packed__)) hci_read_buffer_size_rp;
+
+/* Read Country Code is deprecated */
+#define HCI_OCF_READ_COUNTRY_CODE                      0x0007
+#define HCI_CMD_READ_COUNTRY_CODE                      0x1007
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status;       /* 0x00 - success */
+       uint8_t         country_code; /* 0x00 - NAM, EUR, JP; 0x01 - France */
+} __attribute__ ((__packed__)) hci_read_country_code_rp;
+
+#define HCI_OCF_READ_BDADDR                            0x0009
+#define HCI_CMD_READ_BDADDR                            0x1009
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* unit address */
+} __attribute__ ((__packed__)) hci_read_bdaddr_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x05   Status commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_STATUS                         0x05
+
+#define HCI_OCF_READ_FAILED_CONTACT_CNTR               0x0001
+#define HCI_CMD_READ_FAILED_CONTACT_CNTR               0x1401
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_failed_contact_cntr_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        counter;    /* number of consecutive failed contacts */
+} __attribute__ ((__packed__)) hci_read_failed_contact_cntr_rp;
+
+#define HCI_OCF_RESET_FAILED_CONTACT_CNTR              0x0002
+#define HCI_CMD_RESET_FAILED_CONTACT_CNTR              0x1402
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_reset_failed_contact_cntr_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_reset_failed_contact_cntr_rp;
+
+#define HCI_OCF_READ_LINK_QUALITY                      0x0003
+#define HCI_CMD_READ_LINK_QUALITY                      0x1403
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_link_quality_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         quality;    /* higher value means better quality */
+} __attribute__ ((__packed__)) hci_read_link_quality_rp;
+
+#define HCI_OCF_READ_RSSI                              0x0005
+#define HCI_CMD_READ_RSSI                              0x1405
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_rssi_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       char            rssi;       /* -127 <= rssi <= 127 dB */
+} __attribute__ ((__packed__)) hci_read_rssi_rp;
+
+#define HCI_OCF_READ_AFH_CHANNEL_MAP                   0x0006
+#define HCI_CMD_READ_AFH_CHANNEL_MAP                   0x1406
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_read_afh_channel_map_cp;
+
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         mode;       /* AFH mode */
+       uint8_t         map[10];    /* AFH Channel Map */
+} __attribute__ ((__packed__)) hci_read_afh_channel_map_rp;
+
+#define HCI_OCF_READ_CLOCK                             0x0007
+#define HCI_CMD_READ_CLOCK                             0x1407
+typedef struct {
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         clock;          /* which clock */
+} __attribute__ ((__packed__)) hci_read_clock_cp;
+
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* connection handle */
+       uint32_t        clock;          /* clock value */
+       uint16_t        accuracy;       /* clock accuracy */
+} __attribute__ ((__packed__)) hci_read_clock_rp;
+
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x06   Testing commands and return parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_TESTING                                0x06
+
+#define HCI_OCF_READ_LOOPBACK_MODE                     0x0001
+#define HCI_CMD_READ_LOOPBACK_MODE                     0x1801
+/* No command parameter(s) */
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       uint8_t         lbmode; /* loopback mode */
+} __attribute__ ((__packed__)) hci_read_loopback_mode_rp;
+
+#define HCI_OCF_WRITE_LOOPBACK_MODE                    0x0002
+#define HCI_CMD_WRITE_LOOPBACK_MODE                    0x1802
+typedef struct {
+       uint8_t         lbmode; /* loopback mode */
+} __attribute__ ((__packed__)) hci_write_loopback_mode_cp;
+
+typedef hci_status_rp  hci_write_loopback_mode_rp;
+
+#define HCI_OCF_ENABLE_UNIT_UNDER_TEST                 0x0003
+#define HCI_CMD_ENABLE_UNIT_UNDER_TEST                 0x1803
+/* No command parameter(s) */
+typedef hci_status_rp  hci_enable_unit_under_test_rp;
+
+/**************************************************************************
+ **************************************************************************
+ ** OGF 0x3e   Bluetooth Logo Testing
+ ** OGF 0x3f   Vendor Specific
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_OGF_BT_LOGO                                0x3e
+#define HCI_OGF_VENDOR                         0x3f
+
+/* Ericsson specific FC */
+#define HCI_CMD_ERICSSON_WRITE_PCM_SETTINGS            0xFC07
+#define HCI_CMD_ERICSSON_SET_UART_BAUD_RATE            0xFC09
+#define HCI_CMD_ERICSSON_SET_SCO_DATA_PATH             0xFC1D
+
+/* Cambridge Silicon Radio specific FC */
+#define HCI_CMD_CSR_EXTN                               0xFC00
+
+
+/**************************************************************************
+ **************************************************************************
+ **                         Events and event parameters
+ **************************************************************************
+ **************************************************************************/
+
+#define HCI_EVENT_INQUIRY_COMPL                        0x01
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+} __attribute__ ((__packed__)) hci_inquiry_compl_ep;
+
+#define HCI_EVENT_INQUIRY_RESULT               0x02
+typedef struct {
+       uint8_t         num_responses;      /* number of responses */
+/*     hci_inquiry_response[num_responses]   -- see below */
+} __attribute__ ((__packed__)) hci_inquiry_result_ep;
+
+typedef struct {
+       bdaddr_t        bdaddr;                   /* unit address */
+       uint8_t         page_scan_rep_mode;       /* page scan rep. mode */
+       uint8_t         page_scan_period_mode;    /* page scan period mode */
+       uint8_t         page_scan_mode;           /* page scan mode */
+       uint8_t         uclass[HCI_CLASS_SIZE];   /* unit class */
+       uint16_t        clock_offset;             /* clock offset */
+} __attribute__ ((__packed__)) hci_inquiry_response;
+
+#define HCI_EVENT_CON_COMPL                    0x03
+typedef struct {
+       uint8_t         status;          /* 0x00 - success */
+       uint16_t        con_handle;      /* Connection handle */
+       bdaddr_t        bdaddr;          /* remote unit address */
+       uint8_t         link_type;       /* Link type */
+       uint8_t         encryption_mode; /* Encryption mode */
+} __attribute__ ((__packed__)) hci_con_compl_ep;
+
+#define HCI_EVENT_CON_REQ                      0x04
+typedef struct {
+       bdaddr_t        bdaddr;         /* remote unit address */
+       uint8_t         uclass[HCI_CLASS_SIZE]; /* remote unit class */
+       uint8_t         link_type;      /* link type */
+} __attribute__ ((__packed__)) hci_con_req_ep;
+
+#define HCI_EVENT_DISCON_COMPL                 0x05
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         reason;     /* reason to disconnect */
+} __attribute__ ((__packed__)) hci_discon_compl_ep;
+
+#define HCI_EVENT_AUTH_COMPL                   0x06
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_auth_compl_ep;
+
+#define HCI_EVENT_REMOTE_NAME_REQ_COMPL                0x07
+typedef struct {
+       uint8_t         status;                   /* 0x00 - success */
+       bdaddr_t        bdaddr;                   /* remote unit address */
+       char            name[HCI_UNIT_NAME_SIZE]; /* remote unit name */
+} __attribute__ ((__packed__)) hci_remote_name_req_compl_ep;
+
+#define HCI_EVENT_ENCRYPTION_CHANGE            0x08
+typedef struct {
+       uint8_t         status;                   /* 0x00 - success */
+       uint16_t        con_handle;               /* Connection handle */
+       uint8_t         encryption_enable;        /* 0x00 - disable */
+} __attribute__ ((__packed__)) hci_encryption_change_ep;
+
+#define HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL    0x09
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* Connection handle */
+} __attribute__ ((__packed__)) hci_change_con_link_key_compl_ep;
+
+#define HCI_EVENT_MASTER_LINK_KEY_COMPL                0x0a
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* Connection handle */
+       uint8_t         key_flag;   /* Key flag */
+} __attribute__ ((__packed__)) hci_master_link_key_compl_ep;
+
+#define HCI_EVENT_READ_REMOTE_FEATURES_COMPL   0x0b
+typedef struct {
+       uint8_t         status;                      /* 0x00 - success */
+       uint16_t        con_handle;                  /* Connection handle */
+       uint8_t         features[HCI_FEATURES_SIZE]; /* LMP features bitmsk*/
+} __attribute__ ((__packed__)) hci_read_remote_features_compl_ep;
+
+#define HCI_EVENT_READ_REMOTE_VER_INFO_COMPL   0x0c
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* Connection handle */
+       uint8_t         lmp_version;    /* LMP version */
+       uint16_t        manufacturer;   /* Hardware manufacturer name */
+       uint16_t        lmp_subversion; /* LMP sub-version */
+} __attribute__ ((__packed__)) hci_read_remote_ver_info_compl_ep;
+
+#define HCI_EVENT_QOS_SETUP_COMPL              0x0d
+typedef struct {
+       uint8_t         status;          /* 0x00 - success */
+       uint16_t        con_handle;      /* connection handle */
+       uint8_t         flags;           /* reserved for future use */
+       uint8_t         service_type;    /* service type */
+       uint32_t        token_rate;      /* bytes per second */
+       uint32_t        peak_bandwidth;  /* bytes per second */
+       uint32_t        latency;         /* microseconds */
+       uint32_t        delay_variation; /* microseconds */
+} __attribute__ ((__packed__)) hci_qos_setup_compl_ep;
+
+#define HCI_EVENT_COMMAND_COMPL                        0x0e
+typedef struct {
+       uint8_t         num_cmd_pkts; /* # of HCI command packets */
+       uint16_t        opcode;       /* command OpCode */
+       /* command return parameters (if any) */
+} __attribute__ ((__packed__)) hci_command_compl_ep;
+
+#define HCI_EVENT_COMMAND_STATUS               0x0f
+typedef struct {
+       uint8_t         status;       /* 0x00 - pending */
+       uint8_t         num_cmd_pkts; /* # of HCI command packets */
+       uint16_t        opcode;       /* command OpCode */
+} __attribute__ ((__packed__)) hci_command_status_ep;
+
+#define HCI_EVENT_HARDWARE_ERROR               0x10
+typedef struct {
+       uint8_t         hardware_code; /* hardware error code */
+} __attribute__ ((__packed__)) hci_hardware_error_ep;
+
+#define HCI_EVENT_FLUSH_OCCUR                  0x11
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_flush_occur_ep;
+
+#define HCI_EVENT_ROLE_CHANGE                  0x12
+typedef struct {
+       uint8_t         status; /* 0x00 - success */
+       bdaddr_t        bdaddr; /* address of remote unit */
+       uint8_t         role;   /* new connection role */
+} __attribute__ ((__packed__)) hci_role_change_ep;
+
+#define HCI_EVENT_NUM_COMPL_PKTS               0x13
+typedef struct {
+       uint8_t         num_con_handles; /* # of connection handles */
+/* these are repeated "num_con_handles" times
+       uint16_t        con_handle; --- connection handle(s)
+       uint16_t        compl_pkts; --- # of completed packets */
+} __attribute__ ((__packed__)) hci_num_compl_pkts_ep;
+
+#define HCI_EVENT_MODE_CHANGE                  0x14
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint8_t         unit_mode;  /* remote unit mode */
+       uint16_t        interval;   /* interval * 0.625 msec */
+} __attribute__ ((__packed__)) hci_mode_change_ep;
+
+#define HCI_EVENT_RETURN_LINK_KEYS             0x15
+typedef struct {
+       uint8_t         num_keys; /* # of keys */
+/* these are repeated "num_keys" times
+       bdaddr_t        bdaddr;               --- remote address(es)
+       uint8_t         key[HCI_KEY_SIZE]; --- key(s) */
+} __attribute__ ((__packed__)) hci_return_link_keys_ep;
+
+#define HCI_EVENT_PIN_CODE_REQ                 0x16
+typedef struct {
+       bdaddr_t        bdaddr; /* remote unit address */
+} __attribute__ ((__packed__)) hci_pin_code_req_ep;
+
+#define HCI_EVENT_LINK_KEY_REQ                 0x17
+typedef struct {
+       bdaddr_t        bdaddr; /* remote unit address */
+} __attribute__ ((__packed__)) hci_link_key_req_ep;
+
+#define HCI_EVENT_LINK_KEY_NOTIFICATION                0x18
+typedef struct {
+       bdaddr_t        bdaddr;            /* remote unit address */
+       uint8_t         key[HCI_KEY_SIZE]; /* link key */
+       uint8_t         key_type;          /* type of the key */
+} __attribute__ ((__packed__)) hci_link_key_notification_ep;
+
+#define HCI_EVENT_LOOPBACK_COMMAND             0x19
+typedef hci_cmd_hdr_t  hci_loopback_command_ep;
+
+#define HCI_EVENT_DATA_BUFFER_OVERFLOW         0x1a
+typedef struct {
+       uint8_t         link_type; /* Link type */
+} __attribute__ ((__packed__)) hci_data_buffer_overflow_ep;
+
+#define HCI_EVENT_MAX_SLOT_CHANGE              0x1b
+typedef struct {
+       uint16_t        con_handle;    /* connection handle */
+       uint8_t         lmp_max_slots; /* Max. # of slots allowed */
+} __attribute__ ((__packed__)) hci_max_slot_change_ep;
+
+#define HCI_EVENT_READ_CLOCK_OFFSET_COMPL      0x1c
+typedef struct {
+       uint8_t         status;       /* 0x00 - success */
+       uint16_t        con_handle;   /* Connection handle */
+       uint16_t        clock_offset; /* Clock offset */
+} __attribute__ ((__packed__)) hci_read_clock_offset_compl_ep;
+
+#define HCI_EVENT_CON_PKT_TYPE_CHANGED         0x1d
+typedef struct {
+       uint8_t         status;     /* 0x00 - success */
+       uint16_t        con_handle; /* connection handle */
+       uint16_t        pkt_type;   /* packet type */
+} __attribute__ ((__packed__)) hci_con_pkt_type_changed_ep;
+
+#define HCI_EVENT_QOS_VIOLATION                        0x1e
+typedef struct {
+       uint16_t        con_handle; /* connection handle */
+} __attribute__ ((__packed__)) hci_qos_violation_ep;
+
+/* Page Scan Mode Change Event is deprecated */
+#define HCI_EVENT_PAGE_SCAN_MODE_CHANGE                0x1f
+typedef struct {
+       bdaddr_t        bdaddr;         /* destination address */
+       uint8_t         page_scan_mode; /* page scan mode */
+} __attribute__ ((__packed__)) hci_page_scan_mode_change_ep;
+
+#define HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE    0x20
+typedef struct {
+       bdaddr_t        bdaddr;             /* destination address */
+       uint8_t         page_scan_rep_mode; /* page scan repetition mode */
+} __attribute__ ((__packed__)) hci_page_scan_rep_mode_change_ep;
+
+#define HCI_EVENT_FLOW_SPECIFICATION_COMPL     0x21
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         flags;          /* reserved */
+       uint8_t         direction;      /* flow direction */
+       uint8_t         type;           /* service type */
+       uint32_t        token_rate;     /* token rate */
+       uint32_t        bucket_size;    /* token bucket size */
+       uint32_t        peak_bandwidth; /* peak bandwidth */
+       uint32_t        latency;        /* access latency */
+} __attribute__ ((__packed__)) hci_flow_specification_compl_ep;
+
+#define HCI_EVENT_RSSI_RESULT                  0x22
+typedef struct {
+       uint8_t         num_responses;      /* number of responses */
+/*     hci_rssi_response[num_responses]   -- see below */
+} __attribute__ ((__packed__)) hci_rssi_result_ep;
+
+typedef struct {
+       bdaddr_t        bdaddr;                 /* unit address */
+       uint8_t         page_scan_rep_mode;     /* page scan rep. mode */
+       uint8_t         blank;                  /* reserved */
+       uint8_t         uclass[HCI_CLASS_SIZE]; /* unit class */
+       uint16_t        clock_offset;           /* clock offset */
+       int8_t          rssi;                   /* rssi */
+} __attribute__ ((__packed__)) hci_rssi_response_ep;
+
+#define HCI_EVENT_READ_REMOTE_EXTENDED_FEATURES        0x23
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         page;           /* page number */
+       uint8_t         max;            /* max page number */
+       uint8_t         features[HCI_FEATURES_SIZE]; /* LMP features bitmsk*/
+} __attribute__ ((__packed__)) hci_read_remote_extended_features_ep;
+
+#define HCI_EVENT_SCO_CON_COMPL                        0x2c
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* connection handle */
+       bdaddr_t        bdaddr;         /* unit address */
+       uint8_t         link_type;      /* link type */
+       uint8_t         interval;       /* transmission interval */
+       uint8_t         window;         /* retransmission window */
+       uint16_t        rxlen;          /* rx packet length */
+       uint16_t        txlen;          /* tx packet length */
+       uint8_t         mode;           /* air mode */
+} __attribute__ ((__packed__)) hci_sco_con_compl_ep;
+
+#define HCI_EVENT_SCO_CON_CHANGED              0x2d
+typedef struct {
+       uint8_t         status;         /* 0x00 - success */
+       uint16_t        con_handle;     /* connection handle */
+       uint8_t         interval;       /* transmission interval */
+       uint8_t         window;         /* retransmission window */
+       uint16_t        rxlen;          /* rx packet length */
+       uint16_t        txlen;          /* tx packet length */
+} __attribute__ ((__packed__)) hci_sco_con_changed_ep;
+
+#define HCI_EVENT_BT_LOGO                      0xfe
+
+#define HCI_EVENT_VENDOR                       0xff
+
+/**************************************************************************
+ **************************************************************************
+ **                 HCI Socket Definitions
+ **************************************************************************
+ **************************************************************************/
+
+/* HCI socket options */
+#define SO_HCI_EVT_FILTER              1       /* get/set event filter */
+#define SO_HCI_PKT_FILTER              2       /* get/set packet filter */
+#define SO_HCI_DIRECTION               3       /* packet direction indicator */
+
+/* Control Messages */
+#define SCM_HCI_DIRECTION              SO_HCI_DIRECTION
+
+/*
+ * HCI socket filter and get/set routines
+ *
+ * for ease of use, we filter 256 possible events/packets
+ */
+struct hci_filter {
+       uint32_t        mask[8];        /* 256 bits */
+};
+
+static __inline void
+hci_filter_set(uint8_t bit, struct hci_filter *filter)
+{
+       uint8_t off = bit - 1;
+
+       off >>= 5;
+       filter->mask[off] |= (1 << ((bit - 1) & 0x1f));
+}
+
+static __inline void
+hci_filter_clr(uint8_t bit, struct hci_filter *filter)
+{
+       uint8_t off = bit - 1;
+
+       off >>= 5;
+       filter->mask[off] &= ~(1 << ((bit - 1) & 0x1f));
+}
+
+static __inline int
+hci_filter_test(uint8_t bit, struct hci_filter *filter)
+{
+       uint8_t off = bit - 1;
+
+       off >>= 5;
+       return (filter->mask[off] & (1 << ((bit - 1) & 0x1f)));
+}
+
+/*
+ * HCI socket ioctl's
+ *
+ * Apart from GBTINFOA, these are all indexed on the unit name
+ */
+
+#define SIOCGBTINFO    _IOWR('b',  5, struct btreq) /* get unit info */
+#define SIOCGBTINFOA   _IOWR('b',  6, struct btreq) /* get info by address */
+#define SIOCNBTINFO    _IOWR('b',  7, struct btreq) /* next unit info */
+
+#define SIOCSBTFLAGS   _IOWR('b',  8, struct btreq) /* set unit flags */
+#define SIOCSBTPOLICY  _IOWR('b',  9, struct btreq) /* set unit link policy */
+#define SIOCSBTPTYPE   _IOWR('b', 10, struct btreq) /* set unit packet type */
+
+#define SIOCGBTSTATS   _IOWR('b', 11, struct btreq) /* get unit statistics */
+#define SIOCZBTSTATS   _IOWR('b', 12, struct btreq) /* zero unit statistics */
+
+#define SIOCBTDUMP      _IOW('b', 13, struct btreq) /* print debug info */
+#define SIOCSBTSCOMTU  _IOWR('b', 17, struct btreq) /* set sco_mtu value */
+
+struct bt_stats {
+       uint32_t        err_tx;
+       uint32_t        err_rx;
+       uint32_t        cmd_tx;
+       uint32_t        evt_rx;
+       uint32_t        acl_tx;
+       uint32_t        acl_rx;
+       uint32_t        sco_tx;
+       uint32_t        sco_rx;
+       uint32_t        byte_tx;
+       uint32_t        byte_rx;
+};
+
+struct btreq {
+       char    btr_name[HCI_DEVNAME_SIZE];     /* device name */
+
+       union {
+           struct {
+               bdaddr_t btri_bdaddr;           /* device bdaddr */
+               uint16_t btri_flags;            /* flags */
+               uint16_t btri_num_cmd;          /* # of free cmd buffers */
+               uint16_t btri_num_acl;          /* # of free ACL buffers */
+               uint16_t btri_num_sco;          /* # of free SCO buffers */
+               uint16_t btri_acl_mtu;          /* ACL mtu */
+               uint16_t btri_sco_mtu;          /* SCO mtu */
+               uint16_t btri_link_policy;      /* Link Policy */
+               uint16_t btri_packet_type;      /* Packet Type */
+           } btri;
+           struct bt_stats btrs;   /* unit stats */
+       } btru;
+};
+
+#define btr_flags      btru.btri.btri_flags
+#define btr_bdaddr     btru.btri.btri_bdaddr
+#define btr_num_cmd    btru.btri.btri_num_cmd
+#define btr_num_acl    btru.btri.btri_num_acl
+#define btr_num_sco    btru.btri.btri_num_sco
+#define btr_acl_mtu    btru.btri.btri_acl_mtu
+#define btr_sco_mtu    btru.btri.btri_sco_mtu
+#define btr_link_policy btru.btri.btri_link_policy
+#define btr_packet_type btru.btri.btri_packet_type
+#define btr_uclass     btru.btri.btri_uclass
+#define btr_stats      btru.btrs
+
+/* hci_unit & btr_flags */
+#define BTF_UP                 (1<<0)  /* unit is up */
+#define BTF_RUNNING            (1<<1)  /* unit is running */
+#define BTF_XMIT_CMD           (1<<2)  /* unit is transmitting CMD packets */
+#define BTF_XMIT_ACL           (1<<3)  /* unit is transmitting ACL packets */
+#define BTF_XMIT_SCO           (1<<4)  /* unit is transmitting SCO packets */
+#define BTF_XMIT               (BTF_XMIT_CMD | BTF_XMIT_ACL | BTF_XMIT_SCO)
+#define BTF_INIT_BDADDR                (1<<5)  /* waiting for bdaddr */
+#define BTF_INIT_BUFFER_SIZE   (1<<6)  /* waiting for buffer size */
+#define BTF_INIT_FEATURES      (1<<7)  /* waiting for features */
+#define BTF_INIT               (BTF_INIT_BDADDR | BTF_INIT_BUFFER_SIZE | BTF_INIT_FEATURES)
+
+/**************************************************************************
+ **************************************************************************
+ **                 HCI Kernel Definitions
+ **************************************************************************
+ **************************************************************************/
+
+#ifdef _KERNEL
+
+#include <net/if.h>            /* for struct ifqueue */
+
+struct l2cap_channel;
+struct mbuf;
+struct sco_pcb;
+struct socket;
+
+/* global HCI kernel variables */
+
+/* sysctl variables */
+extern int hci_memo_expiry;
+extern int hci_acl_expiry;
+extern int hci_sendspace, hci_recvspace;
+extern int hci_eventq_max, hci_aclrxq_max, hci_scorxq_max;
+
+/*
+ * HCI Connection Information
+ */
+struct hci_link {
+       struct hci_unit         *hl_unit;       /* our unit */
+       TAILQ_ENTRY(hci_link)    hl_next;       /* next link on unit */
+
+       /* common info */
+       uint16_t                 hl_state;      /* connection state */
+       uint16_t                 hl_flags;      /* link flags */
+       bdaddr_t                 hl_bdaddr;     /* dest address */
+       uint16_t                 hl_handle;     /* connection handle */
+       uint8_t                  hl_type;       /* link type */
+
+       /* ACL link info */
+       uint8_t                  hl_lastid;     /* last id used */
+       uint16_t                 hl_refcnt;     /* reference count */
+       uint16_t                 hl_mtu;        /* signalling mtu for link */
+       uint16_t                 hl_flush;      /* flush timeout */
+
+       TAILQ_HEAD(,l2cap_pdu)   hl_txq;        /* queue of outgoing PDUs */
+       int                      hl_txqlen;     /* number of fragments */
+       struct mbuf             *hl_rxp;        /* incoming PDU (accumulating)*/
+       struct callout           hl_expire;     /* connection expiry timer */
+       TAILQ_HEAD(,l2cap_req)   hl_reqs;       /* pending requests */
+
+       /* SCO link info */
+       struct hci_link         *hl_link;       /* SCO ACL link */
+       struct sco_pcb          *hl_sco;        /* SCO pcb */
+       struct ifqueue           hl_data;       /* SCO outgoing data */
+};
+
+/* hci_link state */
+#define HCI_LINK_CLOSED                0  /* closed */
+#define HCI_LINK_WAIT_CONNECT  1  /* waiting to connect */
+#define HCI_LINK_WAIT_AUTH     2  /* waiting for auth */
+#define HCI_LINK_WAIT_ENCRYPT  3  /* waiting for encrypt */
+#define HCI_LINK_WAIT_SECURE   4  /* waiting for secure */
+#define HCI_LINK_OPEN          5  /* ready and willing */
+#define HCI_LINK_BLOCK         6  /* open but blocking (see hci_acl_start) */
+
+/* hci_link flags */
+#define HCI_LINK_AUTH_REQ      (1<<0)  /* authentication requested */
+#define HCI_LINK_ENCRYPT_REQ   (1<<1)  /* encryption requested */
+#define HCI_LINK_SECURE_REQ    (1<<2)  /* secure link requested */
+#define HCI_LINK_AUTH          (1<<3)  /* link is authenticated */
+#define HCI_LINK_ENCRYPT       (1<<4)  /* link is encrypted */
+#define HCI_LINK_SECURE                (1<<5)  /* link is secured */
+
+/*
+ * Bluetooth Memo
+ *     cached device information for remote devices that this unit has seen
+ */
+struct hci_memo {
+       struct timeval          time;           /* time of last response */
+       hci_inquiry_response    response;       /* inquiry response */
+       LIST_ENTRY(hci_memo)    next;
+};
+
+/*
+ * The Bluetooth HCI device unit structure
+ */
+struct hci_unit {
+       device_t        hci_softc;              /* ptr to device softc */
+#if 0 /* not yet */
+       device_t        hci_bthub;              /* bthub(4) handle */
+#endif
+
+       /* device info */
+       char            *hci_devname;           /* device name */
+       bdaddr_t         hci_bdaddr;            /* device address */
+       uint16_t         hci_flags;             /* see BTF_ above */
+
+       uint16_t         hci_packet_type;       /* packet types */
+       uint16_t         hci_acl_mask;          /* ACL packet capabilities */
+       uint16_t         hci_sco_mask;          /* SCO packet capabilities */
+
+       uint16_t         hci_link_policy;       /* link policy */
+       uint16_t         hci_lmp_mask;          /* link policy capabilities */
+
+       /* flow control */
+       uint16_t         hci_max_acl_size;      /* ACL payload mtu */
+       uint16_t         hci_num_acl_pkts;      /* free ACL packet buffers */
+       uint8_t          hci_num_cmd_pkts;      /* free CMD packet buffers */
+       uint8_t          hci_max_sco_size;      /* SCO payload mtu */
+       uint16_t         hci_num_sco_pkts;      /* free SCO packet buffers */
+
+       TAILQ_HEAD(,hci_link)   hci_links;      /* list of ACL/SCO links */
+       LIST_HEAD(,hci_memo)    hci_memos;      /* cached memo list */
+
+       /*
+        * h/w driver callbacks
+        *
+        * the device driver must supply these.
+        */
+       int     (*hci_enable)           /* enable device */
+               (struct hci_unit *);
+       void    (*hci_disable)          /* disable device */
+               (struct hci_unit *);
+       void    (*hci_start_cmd)        /* initiate cmd output routine */
+               (struct hci_unit *);
+       void    (*hci_start_acl)        /* initiate acl output routine */
+               (struct hci_unit *);
+       void    (*hci_start_sco)        /* initiate sco output routine */
+               (struct hci_unit *);
+       int hci_ipl;            /* to block queue operations */
+
+       /* input queues */
+       struct ifqueue           hci_eventq;    /* Event queue */
+       struct ifqueue           hci_aclrxq;    /* ACL rx queue */
+       struct ifqueue           hci_scorxq;    /* SCO rx queue */
+       uint16_t                 hci_eventqlen; /* Event queue length */
+       uint16_t                 hci_aclrxqlen; /* ACL rx queue length */
+       uint16_t                 hci_scorxqlen; /* SCO rx queue length */
+
+       /* output queues */
+       struct ifqueue           hci_cmdwait;   /* pending commands */
+       struct ifqueue           hci_cmdq;      /* Command queue */
+       struct ifqueue           hci_acltxq;    /* ACL tx queue */
+       struct ifqueue           hci_scotxq;    /* SCO tx queue */
+       struct ifqueue           hci_scodone;   /* SCO done queue */
+
+       struct bt_stats          hci_stats;     /* unit statistics */
+
+       TAILQ_ENTRY(hci_unit) hci_next;
+};
+
+extern TAILQ_HEAD(hci_unit_list, hci_unit) hci_unit_list;
+
+/*
+ * HCI layer function prototypes
+ */
+
+/* hci_event.c */
+void hci_event(struct mbuf *, struct hci_unit *);
+
+/* hci_ioctl.c */
+int hci_ioctl(unsigned long, void *, struct proc *);
+
+/* hci_link.c */
+struct hci_link *hci_acl_open(struct hci_unit *, bdaddr_t *);
+struct hci_link *hci_acl_newconn(struct hci_unit *, bdaddr_t *);
+void hci_acl_close(struct hci_link *, int);
+void hci_acl_timeout(void *);
+int hci_acl_setmode(struct hci_link *);
+void hci_acl_linkmode(struct hci_link *);
+void hci_acl_recv(struct mbuf *, struct hci_unit *);
+int hci_acl_send(struct mbuf *, struct hci_link *, struct l2cap_channel *);
+void hci_acl_start(struct hci_link *);
+void hci_acl_complete(struct hci_link *, int);
+struct hci_link *hci_sco_newconn(struct hci_unit *, bdaddr_t *);
+void hci_sco_recv(struct mbuf *, struct hci_unit *);
+void hci_sco_start(struct hci_link *);
+void hci_sco_complete(struct hci_link *, int);
+struct hci_link *hci_link_alloc(struct hci_unit *);
+void hci_link_free(struct hci_link *, int);
+struct hci_link *hci_link_lookup_state(struct hci_unit *, uint16_t, uint16_t);
+struct hci_link *hci_link_lookup_bdaddr(struct hci_unit *, bdaddr_t *, uint16_t);
+struct hci_link *hci_link_lookup_handle(struct hci_unit *, uint16_t);
+
+/* hci_misc.c */
+int hci_route_lookup(bdaddr_t *, bdaddr_t *);
+struct hci_memo *hci_memo_find(struct hci_unit *, bdaddr_t *);
+void hci_memo_free(struct hci_memo *);
+
+/* hci_socket.c */
+void hci_drop(void *);
+int hci_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *);
+int hci_ctloutput(struct socket *so, struct sockopt *sopt);
+void hci_mtap(struct mbuf *, struct hci_unit *);
+
+/* hci_unit.c */
+void hci_attach(struct hci_unit *);
+void hci_detach(struct hci_unit *);
+int hci_enable(struct hci_unit *);
+void hci_disable(struct hci_unit *);
+struct hci_unit *hci_unit_lookup(bdaddr_t *);
+int hci_send_cmd(struct hci_unit *, uint16_t, void *, uint8_t);
+void hci_input_event(struct hci_unit *, struct mbuf *);
+void hci_input_acl(struct hci_unit *, struct mbuf *);
+void hci_input_sco(struct hci_unit *, struct mbuf *);
+void hci_complete_sco(struct hci_unit *, struct mbuf *);
+void hci_output_cmd(struct hci_unit *, struct mbuf *);
+void hci_output_acl(struct hci_unit *, struct mbuf *);
+void hci_output_sco(struct hci_unit *, struct mbuf *);
+void hci_intr(void *);
+void btintr(struct netmsg *msg);
+
+/* XXX mimic NetBSD for now, although we don't have these interfaces */
+#define M_GETCTX(m, t) ((t)(m)->m_pkthdr.rcvif)
+#define M_SETCTX(m, c) ((m)->m_pkthdr.rcvif = (void *)(c))
+#define EPASSTHROUGH ENOTTY    /* XXX */
+
+#endif /* _KERNEL */
+
+#endif /* _NETBT_HCI_H_ */
diff --git a/sys/netbt/hci_event.c b/sys/netbt/hci_event.c
new file mode 100644 (file)
index 0000000..5b0981f
--- /dev/null
@@ -0,0 +1,948 @@
+/* $OpenBSD: hci_event.c,v 1.6 2007/10/01 16:39:30 krw Exp $ */
+/* $NetBSD: hci_event.c,v 1.6 2007/04/21 06:15:23 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/hci_event.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/bus.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+#include <netbt/sco.h>
+
+static void hci_event_inquiry_result(struct hci_unit *, struct mbuf *);
+static void hci_event_command_status(struct hci_unit *, struct mbuf *);
+static void hci_event_command_compl(struct hci_unit *, struct mbuf *);
+static void hci_event_con_compl(struct hci_unit *, struct mbuf *);
+static void hci_event_discon_compl(struct hci_unit *, struct mbuf *);
+static void hci_event_con_req(struct hci_unit *, struct mbuf *);
+static void hci_event_num_compl_pkts(struct hci_unit *, struct mbuf *);
+static void hci_event_auth_compl(struct hci_unit *, struct mbuf *);
+static void hci_event_encryption_change(struct hci_unit *, struct mbuf *);
+static void hci_event_change_con_link_key_compl(struct hci_unit *, struct mbuf *);
+static void hci_cmd_read_bdaddr(struct hci_unit *, struct mbuf *);
+static void hci_cmd_read_buffer_size(struct hci_unit *, struct mbuf *);
+static void hci_cmd_read_local_features(struct hci_unit *, struct mbuf *);
+static void hci_cmd_reset(struct hci_unit *, struct mbuf *);
+
+#ifdef BLUETOOTH_DEBUG
+int bluetooth_debug = BLUETOOTH_DEBUG;
+
+static const char *hci_eventnames[] = {
+/* 0x00 */ "NULL",
+/* 0x01 */ "INQUIRY COMPLETE",
+/* 0x02 */ "INQUIRY RESULT",
+/* 0x03 */ "CONN COMPLETE",
+/* 0x04 */ "CONN REQ",
+/* 0x05 */ "DISCONN COMPLETE",
+/* 0x06 */ "AUTH COMPLETE",
+/* 0x07 */ "REMOTE NAME REQ COMPLETE",
+/* 0x08 */ "ENCRYPTION CHANGE",
+/* 0x09 */ "CHANGE CONN LINK KEY COMPLETE",
+/* 0x0a */ "MASTER LINK KEY COMPLETE",
+/* 0x0b */ "READ REMOTE FEATURES COMPLETE",
+/* 0x0c */ "READ REMOTE VERSION INFO COMPLETE",
+/* 0x0d */ "QoS SETUP COMPLETE",
+/* 0x0e */ "COMMAND COMPLETE",
+/* 0x0f */ "COMMAND STATUS",
+/* 0x10 */ "HARDWARE ERROR",
+/* 0x11 */ "FLUSH OCCUR",
+/* 0x12 */ "ROLE CHANGE",
+/* 0x13 */ "NUM COMPLETED PACKETS",
+/* 0x14 */ "MODE CHANGE",
+/* 0x15 */ "RETURN LINK KEYS",
+/* 0x16 */ "PIN CODE REQ",
+/* 0x17 */ "LINK KEY REQ",
+/* 0x18 */ "LINK KEY NOTIFICATION",
+/* 0x19 */ "LOOPBACK COMMAND",
+/* 0x1a */ "DATA BUFFER OVERFLOW",
+/* 0x1b */ "MAX SLOT CHANGE",
+/* 0x1c */ "READ CLOCK OFFSET COMPLETE",
+/* 0x1d */ "CONN PKT TYPE CHANGED",
+/* 0x1e */ "QOS VIOLATION",
+/* 0x1f */ "PAGE SCAN MODE CHANGE",
+/* 0x20 */ "PAGE SCAN REP MODE CHANGE",
+/* 0x21 */ "FLOW SPECIFICATION COMPLETE",
+/* 0x22 */ "RSSI RESULT",
+/* 0x23 */ "READ REMOTE EXT FEATURES"
+};
+
+static const char *
+hci_eventstr(unsigned int event)
+{
+
+       if (event < (sizeof(hci_eventnames) / sizeof(*hci_eventnames)))
+               return hci_eventnames[event];
+
+       switch (event) {
+       case HCI_EVENT_SCO_CON_COMPL:   /* 0x2c */
+               return "SCO CON COMPLETE";
+
+       case HCI_EVENT_SCO_CON_CHANGED: /* 0x2d */
+               return "SCO CON CHANGED";
+
+       case HCI_EVENT_BT_LOGO:         /* 0xfe */
+               return "BT_LOGO";
+
+       case HCI_EVENT_VENDOR:          /* 0xff */
+               return "VENDOR";
+       }
+
+       return "UNRECOGNISED";
+}
+#endif /* BLUETOOTH_DEBUG */
+
+/*
+ * process HCI Events
+ *
+ * We will free the mbuf at the end, no need for any sub
+ * functions to handle that. We kind of assume that the
+ * device sends us valid events.
+ * XXX "kind of"? This needs to be fixed.
+ */
+void
+hci_event(struct mbuf *m, struct hci_unit *unit)
+{
+       hci_event_hdr_t hdr;
+
+       KKASSERT(m->m_flags & M_PKTHDR);
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(hdr));
+       m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr);
+       m_adj(m, sizeof(hdr));
+
+       KKASSERT(hdr.type == HCI_EVENT_PKT);
+
+       DPRINTFN(1, "(%s) event %s\n", unit->hci_devname, hci_eventstr(hdr.event));
+
+       switch(hdr.event) {
+       case HCI_EVENT_COMMAND_STATUS:
+               hci_event_command_status(unit, m);
+               break;
+
+       case HCI_EVENT_COMMAND_COMPL:
+               hci_event_command_compl(unit, m);
+               break;
+
+       case HCI_EVENT_NUM_COMPL_PKTS:
+               hci_event_num_compl_pkts(unit, m);
+               break;
+
+       case HCI_EVENT_INQUIRY_RESULT:
+               hci_event_inquiry_result(unit, m);
+               break;
+
+       case HCI_EVENT_CON_COMPL:
+               hci_event_con_compl(unit, m);
+               break;
+
+       case HCI_EVENT_DISCON_COMPL:
+               hci_event_discon_compl(unit, m);
+               break;
+
+       case HCI_EVENT_CON_REQ:
+               hci_event_con_req(unit, m);
+               break;
+
+       case HCI_EVENT_AUTH_COMPL:
+               hci_event_auth_compl(unit, m);
+               break;
+
+       case HCI_EVENT_ENCRYPTION_CHANGE:
+               hci_event_encryption_change(unit, m);
+               break;
+
+       case HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:
+               hci_event_change_con_link_key_compl(unit, m);
+               break;
+
+       case HCI_EVENT_SCO_CON_COMPL:
+       case HCI_EVENT_INQUIRY_COMPL:
+       case HCI_EVENT_REMOTE_NAME_REQ_COMPL:
+       case HCI_EVENT_MASTER_LINK_KEY_COMPL:
+       case HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
+       case HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:
+       case HCI_EVENT_QOS_SETUP_COMPL:
+       case HCI_EVENT_HARDWARE_ERROR:
+       case HCI_EVENT_FLUSH_OCCUR:
+       case HCI_EVENT_ROLE_CHANGE:
+       case HCI_EVENT_MODE_CHANGE:
+       case HCI_EVENT_RETURN_LINK_KEYS:
+       case HCI_EVENT_PIN_CODE_REQ:
+       case HCI_EVENT_LINK_KEY_REQ:
+       case HCI_EVENT_LINK_KEY_NOTIFICATION:
+       case HCI_EVENT_LOOPBACK_COMMAND:
+       case HCI_EVENT_DATA_BUFFER_OVERFLOW:
+       case HCI_EVENT_MAX_SLOT_CHANGE:
+       case HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
+       case HCI_EVENT_CON_PKT_TYPE_CHANGED:
+       case HCI_EVENT_QOS_VIOLATION:
+       case HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
+       case HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
+       case HCI_EVENT_FLOW_SPECIFICATION_COMPL:
+       case HCI_EVENT_RSSI_RESULT:
+       case HCI_EVENT_READ_REMOTE_EXTENDED_FEATURES:
+       case HCI_EVENT_SCO_CON_CHANGED:
+       case HCI_EVENT_BT_LOGO:
+       case HCI_EVENT_VENDOR:
+               break;
+
+       default:
+               UNKNOWN(hdr.event);
+               break;
+       }
+
+       m_freem(m);
+}
+
+/*
+ * Command Status
+ *
+ * Update our record of num_cmd_pkts then post-process any pending commands
+ * and optionally restart cmd output on the unit.
+ */
+static void
+hci_event_command_status(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_command_status_ep ep;
+       struct hci_link *link;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       DPRINTFN(1, "(%s) opcode (%03x|%04x) status = 0x%x num_cmd_pkts = %d\n",
+               unit->hci_devname,
+               HCI_OGF(letoh16(ep.opcode)), HCI_OCF(letoh16(ep.opcode)),
+               ep.status,
+               ep.num_cmd_pkts);
+
+       unit->hci_num_cmd_pkts = ep.num_cmd_pkts;
+
+       /*
+        * post processing of pending commands
+        */
+       switch(letoh16(ep.opcode)) {
+       case HCI_CMD_CREATE_CON:
+               switch (ep.status) {
+               case 0x12:      /* Invalid HCI command parameters */
+                       DPRINTF("(%s) Invalid HCI command parameters\n",
+                           unit->hci_devname);
+                       while ((link = hci_link_lookup_state(unit,
+                           HCI_LINK_ACL, HCI_LINK_WAIT_CONNECT)) != NULL)
+                               hci_link_free(link, ECONNABORTED);
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       while (unit->hci_num_cmd_pkts > 0 && !IF_QEMPTY(&unit->hci_cmdwait)) {
+               IF_DEQUEUE(&unit->hci_cmdwait, m);
+               hci_output_cmd(unit, m);
+       }
+}
+
+/*
+ * Command Complete
+ *
+ * Update our record of num_cmd_pkts then handle the completed command,
+ * and optionally restart cmd output on the unit.
+ */
+static void
+hci_event_command_compl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_command_compl_ep ep;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       DPRINTFN(1, "(%s) opcode (%03x|%04x) num_cmd_pkts = %d\n",
+               unit->hci_devname,
+               HCI_OGF(letoh16(ep.opcode)), HCI_OCF(letoh16(ep.opcode)),
+               ep.num_cmd_pkts);
+
+       unit->hci_num_cmd_pkts = ep.num_cmd_pkts;
+
+       /*
+        * post processing of completed commands
+        */
+       switch(letoh16(ep.opcode)) {
+       case HCI_CMD_READ_BDADDR:
+               hci_cmd_read_bdaddr(unit, m);
+               break;
+
+       case HCI_CMD_READ_BUFFER_SIZE:
+               hci_cmd_read_buffer_size(unit, m);
+               break;
+
+       case HCI_CMD_READ_LOCAL_FEATURES:
+               hci_cmd_read_local_features(unit, m);
+               break;
+
+       case HCI_CMD_RESET:
+               hci_cmd_reset(unit, m);
+               break;
+
+       default:
+               break;
+       }
+
+       while (unit->hci_num_cmd_pkts > 0 && !IF_QEMPTY(&unit->hci_cmdwait)) {
+               IF_DEQUEUE(&unit->hci_cmdwait, m);
+               hci_output_cmd(unit, m);
+       }
+}
+
+/*
+ * Number of Completed Packets
+ *
+ * This is sent periodically by the Controller telling us how many
+ * buffers are now freed up and which handle was using them. From
+ * this we determine which type of buffer it was and add the qty
+ * back into the relevant packet counter, then restart output on
+ * links that have halted.
+ */
+static void
+hci_event_num_compl_pkts(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_num_compl_pkts_ep ep;
+       struct hci_link *link, *next;
+       uint16_t handle, num;
+       int num_acl = 0, num_sco = 0;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       while (ep.num_con_handles--) {
+               m_copydata(m, 0, sizeof(handle), (caddr_t)&handle);
+               m_adj(m, sizeof(handle));
+               handle = letoh16(handle);
+
+               m_copydata(m, 0, sizeof(num), (caddr_t)&num);
+               m_adj(m, sizeof(num));
+               num = letoh16(num);
+
+               link = hci_link_lookup_handle(unit, handle);
+               if (link) {
+                       if (link->hl_type == HCI_LINK_ACL) {
+                               num_acl += num;
+                               hci_acl_complete(link, num);
+                       } else {
+                               num_sco += num;
+                               hci_sco_complete(link, num);
+                       }
+               } else {
+                       /* XXX need to issue Read_Buffer_Size or Reset? */
+                       kprintf("%s: unknown handle %d! "
+                               "(losing track of %d packet buffer%s)\n",
+                               unit->hci_devname, handle,
+                               num, (num == 1 ? "" : "s"));
+               }
+       }
+
+       /*
+        * Move up any queued packets. When a link has sent data, it will move
+        * to the back of the queue - technically then if a link had something
+        * to send and there were still buffers available it could get started
+        * twice but it seemed more important to to handle higher loads fairly
+        * than worry about wasting cycles when we are not busy.
+        */
+
+       unit->hci_num_acl_pkts += num_acl;
+       unit->hci_num_sco_pkts += num_sco;
+
+       link = TAILQ_FIRST(&unit->hci_links);
+       while (link && (unit->hci_num_acl_pkts > 0 || unit->hci_num_sco_pkts > 0)) {
+               next = TAILQ_NEXT(link, hl_next);
+
+               if (link->hl_type == HCI_LINK_ACL) {
+                       if (unit->hci_num_acl_pkts > 0 && link->hl_txqlen > 0)
+                               hci_acl_start(link);
+               } else {
+                       if (unit->hci_num_sco_pkts > 0 && link->hl_txqlen > 0)
+                               hci_sco_start(link);
+               }
+
+               link = next;
+       }
+}
+
+/*
+ * Inquiry Result
+ *
+ * keep a note of devices seen, so we know which unit to use
+ * on outgoing connections
+ */
+static void
+hci_event_inquiry_result(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_inquiry_result_ep ep;
+       struct hci_memo *memo;
+       bdaddr_t bdaddr;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       DPRINTFN(1, "%d response%s\n", ep.num_responses,
+                               (ep.num_responses == 1 ? "" : "s"));
+
+       while(ep.num_responses--) {
+               m_copydata(m, 0, sizeof(bdaddr_t), (caddr_t)&bdaddr);
+
+               DPRINTFN(1, "bdaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
+                       bdaddr.b[5], bdaddr.b[4], bdaddr.b[3],
+                       bdaddr.b[2], bdaddr.b[1], bdaddr.b[0]);
+
+               memo = hci_memo_find(unit, &bdaddr);
+               if (memo == NULL) {
+                       memo = kmalloc(sizeof(*memo), M_BLUETOOTH,
+                           M_NOWAIT | M_ZERO);
+                       if (memo == NULL) {
+                               DPRINTFN(0, "out of memo memory!\n");
+                               break;
+                       }
+
+                       LIST_INSERT_HEAD(&unit->hci_memos, memo, next);
+               }
+
+               microtime(&memo->time);
+               m_copydata(m, 0, sizeof(hci_inquiry_response),
+                       (caddr_t)&memo->response);
+               m_adj(m, sizeof(hci_inquiry_response));
+
+               memo->response.clock_offset =
+                   letoh16(memo->response.clock_offset);
+       }
+}
+
+/*
+ * Connection Complete
+ *
+ * Sent to us when a connection is made. If there is no link
+ * structure already allocated for this, we must have changed
+ * our mind, so just disconnect.
+ */
+static void
+hci_event_con_compl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_con_compl_ep ep;
+       hci_write_link_policy_settings_cp cp;
+       struct hci_link *link;
+       int err;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       DPRINTFN(1, "(%s) %s connection complete for "
+               "%02x:%02x:%02x:%02x:%02x:%02x status %#x\n",
+               unit->hci_devname,
+               (ep.link_type == HCI_LINK_ACL ? "ACL" : "SCO"),
+               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.status);
+
+       link = hci_link_lookup_bdaddr(unit, &ep.bdaddr, ep.link_type);
+
+       if (ep.status) {
+               if (link != NULL) {
+                       switch (ep.status) {
+                       case 0x04: /* "Page Timeout" */
+                               err = EHOSTDOWN;
+                               break;
+
+                       case 0x08: /* "Connection Timed Out" */
+                       case 0x10: /* "Connection Accept Timeout Exceeded" */
+                               err = ETIMEDOUT;
+                               break;
+
+                       case 0x16: /* "Connection Terminated by Local Host" */
+                               err = 0;
+                               break;
+
+                       default:
+                               err = ECONNREFUSED;
+                               break;
+                       }
+
+                       hci_link_free(link, err);
+               }
+
+               return;
+       }
+
+       if (link == NULL) {
+               hci_discon_cp dp;
+
+               dp.con_handle = ep.con_handle;
+               dp.reason = 0x13; /* "Remote User Terminated Connection" */
+
+               hci_send_cmd(unit, HCI_CMD_DISCONNECT, &dp, sizeof(dp));
+               return;
+       }
+
+       /* XXX could check auth_enable here */
+
+       if (ep.encryption_mode)
+               link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT);
+
+       link->hl_state = HCI_LINK_OPEN;
+       link->hl_handle = HCI_CON_HANDLE(letoh16(ep.con_handle));
+
+       if (ep.link_type == HCI_LINK_ACL) {
+               cp.con_handle = ep.con_handle;
+               cp.settings = htole16(unit->hci_link_policy);
+               err = hci_send_cmd(unit, HCI_CMD_WRITE_LINK_POLICY_SETTINGS,
+                                               &cp, sizeof(cp));
+               if (err)
+                       kprintf("%s: Warning, could not write link policy\n",
+                               unit->hci_devname);
+
+               err = hci_acl_setmode(link);
+               if (err == EINPROGRESS)
+                       return;
+
+               hci_acl_linkmode(link);
+       } else {
+               (*link->hl_sco->sp_proto->connected)(link->hl_sco->sp_upper);
+       }
+}
+
+/*
+ * Disconnection Complete
+ *
+ * This is sent in response to a disconnection request, but also if
+ * the remote device goes out of range.
+ */
+static void
+hci_event_discon_compl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_discon_compl_ep ep;
+       struct hci_link *link;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       ep.con_handle = letoh16(ep.con_handle);
+
+       DPRINTFN(1, "handle #%d, status=0x%x\n", ep.con_handle, ep.status);
+
+       link = hci_link_lookup_handle(unit, HCI_CON_HANDLE(ep.con_handle));
+       if (link)
+               hci_link_free(link, ENOENT); /* XXX NetBSD used ENOLINK here */
+}
+
+/*
+ * Connect Request
+ *
+ * We check upstream for appropriate listeners and accept connections
+ * that are wanted.
+ */
+static void
+hci_event_con_req(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_con_req_ep ep;
+       hci_accept_con_cp ap;
+       hci_reject_con_cp rp;
+       struct hci_link *link;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       DPRINTFN(1, "bdaddr %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+               "class %2.2x%2.2x%2.2x type %s\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],
+               ep.uclass[0], ep.uclass[1], ep.uclass[2],
+               ep.link_type == HCI_LINK_ACL ? "ACL" : "SCO");
+
+       if (ep.link_type == HCI_LINK_ACL)
+               link = hci_acl_newconn(unit, &ep.bdaddr);
+       else
+               link = hci_sco_newconn(unit, &ep.bdaddr);
+
+       if (link == NULL) {
+               memset(&rp, 0, sizeof(rp));
+               bdaddr_copy(&rp.bdaddr, &ep.bdaddr);
+               rp.reason = 0x0f;       /* Unacceptable BD_ADDR */
+
+               hci_send_cmd(unit, HCI_CMD_REJECT_CON, &rp, sizeof(rp));
+       } else {
+               memset(&ap, 0, sizeof(ap));
+               bdaddr_copy(&ap.bdaddr, &ep.bdaddr);
+               if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH)
+                       ap.role = HCI_ROLE_MASTER;
+               else
+                       ap.role = HCI_ROLE_SLAVE;
+
+               hci_send_cmd(unit, HCI_CMD_ACCEPT_CON, &ap, sizeof(ap));
+       }
+}
+
+/*
+ * Auth Complete
+ *
+ * Authentication has been completed on an ACL link. We can notify the
+ * upper layer protocols unless further mode changes are pending.
+ */
+static void
+hci_event_auth_compl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_auth_compl_ep ep;
+       struct hci_link *link;
+       int err;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       ep.con_handle = HCI_CON_HANDLE(letoh16(ep.con_handle));
+
+       DPRINTFN(1, "handle #%d, status=0x%x\n", ep.con_handle, ep.status);
+
+       link = hci_link_lookup_handle(unit, ep.con_handle);
+       if (link == NULL || link->hl_type != HCI_LINK_ACL)
+               return;
+
+       if (ep.status == 0) {
+               link->hl_flags |= HCI_LINK_AUTH;
+
+               if (link->hl_state == HCI_LINK_WAIT_AUTH)
+                       link->hl_state = HCI_LINK_OPEN;
+
+               err = hci_acl_setmode(link);
+               if (err == EINPROGRESS)
+                       return;
+       }
+
+       hci_acl_linkmode(link);
+}
+
+/*
+ * Encryption Change
+ *
+ * The encryption status has changed. Basically, we note the change
+ * then notify the upper layer protocol unless further mode changes
+ * are pending.
+ * Note that if encryption gets disabled when it has been requested,
+ * we will attempt to enable it again.. (its a feature not a bug :)
+ */
+static void
+hci_event_encryption_change(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_encryption_change_ep ep;
+       struct hci_link *link;
+       int err;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       ep.con_handle = HCI_CON_HANDLE(letoh16(ep.con_handle));
+
+       DPRINTFN(1, "handle #%d, status=0x%x, encryption_enable=0x%x\n",
+                ep.con_handle, ep.status, ep.encryption_enable);
+
+       link = hci_link_lookup_handle(unit, ep.con_handle);
+       if (link == NULL || link->hl_type != HCI_LINK_ACL)
+               return;
+
+       if (ep.status == 0) {
+               if (ep.encryption_enable == 0)
+                       link->hl_flags &= ~HCI_LINK_ENCRYPT;
+               else
+                       link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT);
+
+               if (link->hl_state == HCI_LINK_WAIT_ENCRYPT)
+                       link->hl_state = HCI_LINK_OPEN;
+
+               err = hci_acl_setmode(link);
+               if (err == EINPROGRESS)
+                       return;
+       }
+
+       hci_acl_linkmode(link);
+}
+
+/*
+ * Change Connection Link Key Complete
+ *
+ * Link keys are handled in userland but if we are waiting to secure
+ * this link, we should notify the upper protocols. A SECURE request
+ * only needs a single key change, so we can cancel the request.
+ */
+static void
+hci_event_change_con_link_key_compl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_change_con_link_key_compl_ep ep;
+       struct hci_link *link;
+       int err;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(ep));
+       m_copydata(m, 0, sizeof(ep), (caddr_t)&ep);
+       m_adj(m, sizeof(ep));
+
+       ep.con_handle = HCI_CON_HANDLE(letoh16(ep.con_handle));
+
+       DPRINTFN(1, "handle #%d, status=0x%x\n", ep.con_handle, ep.status);
+
+       link = hci_link_lookup_handle(unit, ep.con_handle);
+       if (link == NULL || link->hl_type != HCI_LINK_ACL)
+               return;
+
+       link->hl_flags &= ~HCI_LINK_SECURE_REQ;
+
+       if (ep.status == 0) {
+               link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_SECURE);
+
+               if (link->hl_state == HCI_LINK_WAIT_SECURE)
+                       link->hl_state = HCI_LINK_OPEN;
+
+               err = hci_acl_setmode(link);
+               if (err == EINPROGRESS)
+                       return;
+       }
+
+       hci_acl_linkmode(link);
+}
+
+/*
+ * process results of read_bdaddr command_complete event
+ */
+static void
+hci_cmd_read_bdaddr(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_read_bdaddr_rp rp;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(rp));
+       m_copydata(m, 0, sizeof(rp), (caddr_t)&rp);
+       m_adj(m, sizeof(rp));
+
+       if (rp.status > 0)
+               return;
+
+       if ((unit->hci_flags & BTF_INIT_BDADDR) == 0)
+               return;
+
+       bdaddr_copy(&unit->hci_bdaddr, &rp.bdaddr);
+
+       crit_enter();
+       unit->hci_flags &= ~BTF_INIT_BDADDR;
+       crit_exit();
+
+       wakeup(unit);
+}
+
+/*
+ * process results of read_buffer_size command_complete event
+ */
+static void
+hci_cmd_read_buffer_size(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_read_buffer_size_rp rp;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(rp));
+       m_copydata(m, 0, sizeof(rp), (caddr_t)&rp);
+       m_adj(m, sizeof(rp));
+
+       if (rp.status > 0)
+               return;
+
+       if ((unit->hci_flags & BTF_INIT_BUFFER_SIZE) == 0)
+               return;
+
+       unit->hci_max_acl_size = letoh16(rp.max_acl_size);
+       unit->hci_num_acl_pkts = letoh16(rp.num_acl_pkts);
+       unit->hci_max_sco_size = rp.max_sco_size;
+       unit->hci_num_sco_pkts = letoh16(rp.num_sco_pkts);
+
+       crit_enter();
+       unit->hci_flags &= ~BTF_INIT_BUFFER_SIZE;
+       crit_exit();
+
+       wakeup(unit);
+}
+
+/*
+ * process results of read_local_features command_complete event
+ */
+static void
+hci_cmd_read_local_features(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_read_local_features_rp rp;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(rp));
+       m_copydata(m, 0, sizeof(rp), (caddr_t)&rp);
+       m_adj(m, sizeof(rp));
+
+       if (rp.status > 0)
+               return;
+
+       if ((unit->hci_flags & BTF_INIT_FEATURES) == 0)
+               return;
+
+       unit->hci_lmp_mask = 0;
+
+       if (rp.features[0] & HCI_LMP_ROLE_SWITCH)
+               unit->hci_lmp_mask |= HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
+
+       if (rp.features[0] & HCI_LMP_HOLD_MODE)
+               unit->hci_lmp_mask |= HCI_LINK_POLICY_ENABLE_HOLD_MODE;
+
+       if (rp.features[0] & HCI_LMP_SNIFF_MODE)
+               unit->hci_lmp_mask |= HCI_LINK_POLICY_ENABLE_SNIFF_MODE;
+
+       if (rp.features[1] & HCI_LMP_PARK_MODE)
+               unit->hci_lmp_mask |= HCI_LINK_POLICY_ENABLE_PARK_MODE;
+
+       /* ACL packet mask */
+       unit->hci_acl_mask = HCI_PKT_DM1 | HCI_PKT_DH1;
+
+       if (rp.features[0] & HCI_LMP_3SLOT)
+               unit->hci_acl_mask |= HCI_PKT_DM3 | HCI_PKT_DH3;
+
+       if (rp.features[0] & HCI_LMP_5SLOT)
+               unit->hci_acl_mask |= HCI_PKT_DM5 | HCI_PKT_DH5;
+
+       if ((rp.features[3] & HCI_LMP_EDR_ACL_2MBPS) == 0)
+               unit->hci_acl_mask |= HCI_PKT_2MBPS_DH1
+                                   | HCI_PKT_2MBPS_DH3
+                                   | HCI_PKT_2MBPS_DH5;
+
+       if ((rp.features[3] & HCI_LMP_EDR_ACL_3MBPS) == 0)
+               unit->hci_acl_mask |= HCI_PKT_3MBPS_DH1
+                                   | HCI_PKT_3MBPS_DH3
+                                   | HCI_PKT_3MBPS_DH5;
+
+       if ((rp.features[4] & HCI_LMP_3SLOT_EDR_ACL) == 0)
+               unit->hci_acl_mask |= HCI_PKT_2MBPS_DH3
+                                   | HCI_PKT_3MBPS_DH3;
+
+       if ((rp.features[5] & HCI_LMP_5SLOT_EDR_ACL) == 0)
+               unit->hci_acl_mask |= HCI_PKT_2MBPS_DH5
+                                   | HCI_PKT_3MBPS_DH5;
+
+       unit->hci_packet_type = unit->hci_acl_mask;
+
+       /* SCO packet mask */
+       unit->hci_sco_mask = 0;
+       if (rp.features[1] & HCI_LMP_SCO_LINK)
+               unit->hci_sco_mask |= HCI_PKT_HV1;
+
+       if (rp.features[1] & HCI_LMP_HV2_PKT)
+               unit->hci_sco_mask |= HCI_PKT_HV2;
+
+       if (rp.features[1] & HCI_LMP_HV3_PKT)
+               unit->hci_sco_mask |= HCI_PKT_HV3;
+
+       if (rp.features[3] & HCI_LMP_EV3_PKT)
+               unit->hci_sco_mask |= HCI_PKT_EV3;
+
+       if (rp.features[4] & HCI_LMP_EV4_PKT)
+               unit->hci_sco_mask |= HCI_PKT_EV4;
+
+       if (rp.features[4] & HCI_LMP_EV5_PKT)
+               unit->hci_sco_mask |= HCI_PKT_EV5;
+
+       /* XXX what do 2MBPS/3MBPS/3SLOT eSCO mean? */
+
+       crit_enter();
+       unit->hci_flags &= ~BTF_INIT_FEATURES;
+       crit_exit();
+
+       wakeup(unit);
+
+       DPRINTFN(1, "%s: lmp_mask %4.4x, acl_mask %4.4x, sco_mask %4.4x\n",
+               unit->hci_devname, unit->hci_lmp_mask,
+               unit->hci_acl_mask, unit->hci_sco_mask);
+}
+
+/*
+ * process results of reset command_complete event
+ *
+ * This has killed all the connections, so close down anything we have left,
+ * and reinitialise the unit.
+ */
+static void
+hci_cmd_reset(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_reset_rp rp;
+       struct hci_link *link, *next;
+       int acl;
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(rp));
+       m_copydata(m, 0, sizeof(rp), (caddr_t)&rp);
+       m_adj(m, sizeof(rp));
+
+       if (rp.status != 0)
+               return;
+
+       /*
+        * release SCO links first, since they may be holding
+        * an ACL link reference.
+        */
+       for (acl = 0 ; acl < 2 ; acl++) {
+               next = TAILQ_FIRST(&unit->hci_links);
+               while ((link = next) != NULL) {
+                       next = TAILQ_NEXT(link, hl_next);
+                       if (acl || link->hl_type != HCI_LINK_ACL)
+                               hci_link_free(link, ECONNABORTED);
+               }
+       }
+
+       unit->hci_num_acl_pkts = 0;
+       unit->hci_num_sco_pkts = 0;
+
+       if (hci_send_cmd(unit, HCI_CMD_READ_BDADDR, NULL, 0))
+               return;
+
+       if (hci_send_cmd(unit, HCI_CMD_READ_BUFFER_SIZE, NULL, 0))
+               return;
+
+       if (hci_send_cmd(unit, HCI_CMD_READ_LOCAL_FEATURES, NULL, 0))
+               return;
+}
diff --git a/sys/netbt/hci_ioctl.c b/sys/netbt/hci_ioctl.c
new file mode 100644 (file)
index 0000000..b1d0751
--- /dev/null
@@ -0,0 +1,314 @@
+/* $OpenBSD: hci_ioctl.c,v 1.1 2007/06/01 02:46:11 uwe Exp $ */
+/* $NetBSD: hci_ioctl.c,v 1.5 2007/01/04 19:07:03 elad Exp $ */
+/* $DragonFly: src/sys/netbt/hci_ioctl.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/domain.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/thread2.h>
+#include <sys/bus.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+#include <netbt/l2cap.h>
+#include <netbt/rfcomm.h>
+
+#ifdef BLUETOOTH_DEBUG
+#define BDADDR(bd)     (bd).b[5], (bd).b[4], (bd).b[3],        \
+                       (bd).b[2], (bd).b[1], (bd).b[0]
+
+static void
+hci_dump(void)
+{
+       struct hci_unit *unit;
+       struct hci_link *link;
+       struct l2cap_channel *chan;
+       struct rfcomm_session *rs;
+       struct rfcomm_dlc *dlc;
+
+       kprintf("HCI:\n");
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               kprintf("UNIT %s: flags 0x%4.4x, "
+                       "num_cmd=%d, num_acl=%d, num_sco=%d\n",
+                       unit->hci_devname, unit->hci_flags,
+                       unit->hci_num_cmd_pkts,
+                       unit->hci_num_acl_pkts,
+                       unit->hci_num_sco_pkts);
+               TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+                       kprintf("+HANDLE #%d: %s "
+                           "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+                           "state %d, refcnt %d\n",
+                           link->hl_handle,
+                           (link->hl_type == HCI_LINK_ACL ? "ACL":"SCO"),
+                           BDADDR(link->hl_bdaddr),
+                           link->hl_state, link->hl_refcnt);
+               }
+       }
+
+       kprintf("L2CAP:\n");
+       LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
+               kprintf("CID #%d state %d, psm=0x%4.4x, "
+                   "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+                   "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+                   chan->lc_lcid, chan->lc_state, chan->lc_raddr.bt_psm,
+                   BDADDR(chan->lc_laddr.bt_bdaddr),
+                   BDADDR(chan->lc_raddr.bt_bdaddr));
+       }
+
+       LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) {
+               kprintf("LISTEN psm=0x%4.4x, "
+                   "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+                   chan->lc_laddr.bt_psm,
+                   BDADDR(chan->lc_laddr.bt_bdaddr));
+       }
+
+       kprintf("RFCOMM:\n");
+       LIST_FOREACH(rs, &rfcomm_session_active, rs_next) {
+               chan = rs->rs_l2cap;
+               kprintf("SESSION: state=%d, flags=0x%4.4x, psm 0x%4.4x "
+                   "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+                   "raddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+                   rs->rs_state, rs->rs_flags, chan->lc_raddr.bt_psm,
+                   BDADDR(chan->lc_laddr.bt_bdaddr),
+                   BDADDR(chan->lc_raddr.bt_bdaddr));
+               LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
+                       kprintf("+DLC channel=%d, dlci=%d, "
+                           "state=%d, flags=0x%4.4x, rxcred=%d, rxsize=%ld, "
+                           "txcred=%d, pending=%d, txqlen=%d\n",
+                           dlc->rd_raddr.bt_channel, dlc->rd_dlci,
+                           dlc->rd_state, dlc->rd_flags,
+                           dlc->rd_rxcred, (unsigned long)dlc->rd_rxsize,
+                           dlc->rd_txcred, dlc->rd_pending,
+                           (dlc->rd_txbuf ? dlc->rd_txbuf->m_pkthdr.len : 0));
+               }
+       }
+
+       LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) {
+               chan = rs->rs_l2cap;
+               kprintf("LISTEN: psm 0x%4.4x, "
+                   "laddr=%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+                   chan->lc_laddr.bt_psm,
+                   BDADDR(chan->lc_laddr.bt_bdaddr));
+               LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next)
+                       kprintf("+DLC channel=%d\n", dlc->rd_laddr.bt_channel);
+       }
+}
+
+#undef BDADDR
+#endif
+
+int
+hci_ioctl(unsigned long cmd, void *data, struct proc *p)
+{
+       struct btreq *btr = data;
+       struct thread *td = curthread; 
+       struct hci_unit *unit;
+       int err = 0;
+
+       DPRINTFN(1, "cmd %#lx\n", cmd);
+
+       switch(cmd) {
+#ifdef BLUETOOTH_DEBUG
+       case SIOCBTDUMP:
+               hci_dump();
+               return 0;
+#endif
+       /*
+        * Get unit info based on address rather than name
+        */
+       case SIOCGBTINFOA:
+               unit = hci_unit_lookup(&btr->btr_bdaddr);
+               if (unit == NULL)
+                       return ENXIO;
+
+               break;
+
+       /*
+        * The remaining ioctl's all use the same btreq structure and
+        * index on the name of the device, so we look that up first.
+        */
+       case SIOCNBTINFO:
+               /* empty name means give the first unit */
+               if (btr->btr_name[0] == '\0') {
+                       unit = NULL;
+                       break;
+               }
+
+               /* else fall through and look it up */
+       case SIOCGBTINFO:
+       case SIOCSBTFLAGS:
+       case SIOCSBTPOLICY:
+       case SIOCSBTPTYPE:
+       case SIOCGBTSTATS:
+       case SIOCZBTSTATS:
+       case SIOCSBTSCOMTU:
+               TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+                       if (strncmp(unit->hci_devname, btr->btr_name,
+                           HCI_DEVNAME_SIZE) == 0)
+                               break;
+               }
+
+               if (unit == NULL)
+                       return ENXIO;
+
+               break;
+
+       default:        /* not one of mine */
+               return EPASSTHROUGH;
+       }
+
+       switch(cmd) {
+       case SIOCNBTINFO:       /* get next info */
+               if (unit)
+                       unit = TAILQ_NEXT(unit, hci_next);
+               else
+                       unit = TAILQ_FIRST(&hci_unit_list);
+
+               if (unit == NULL) {
+                       err = ENXIO;
+                       break;
+               }
+
+               /* and fall through to */
+       case SIOCGBTINFO:       /* get unit info */
+       case SIOCGBTINFOA:      /* get info by address */
+               memset(btr, 0, sizeof(struct btreq));
+               strlcpy(btr->btr_name, unit->hci_devname, HCI_DEVNAME_SIZE);
+               bdaddr_copy(&btr->btr_bdaddr, &unit->hci_bdaddr);
+
+               btr->btr_flags = unit->hci_flags;
+
+               btr->btr_num_cmd = unit->hci_num_cmd_pkts;
+               btr->btr_num_acl = unit->hci_num_acl_pkts;
+               btr->btr_num_sco = unit->hci_num_sco_pkts;
+               btr->btr_acl_mtu = unit->hci_max_acl_size;
+               btr->btr_sco_mtu = unit->hci_max_sco_size;
+
+               btr->btr_packet_type = unit->hci_packet_type;
+               btr->btr_link_policy = unit->hci_link_policy;
+               break;
+
+       case SIOCSBTFLAGS:      /* set unit flags (privileged) */
+               err = suser(td);
+               if (err)
+                       break;
+
+               if ((unit->hci_flags & BTF_UP)
+                   && (btr->btr_flags & BTF_UP) == 0) {
+                       hci_disable(unit);
+                       unit->hci_flags &= ~BTF_UP;
+               }
+
+               crit_enter();
+               unit->hci_flags |= (btr->btr_flags & BTF_INIT);
+               crit_exit();
+
+               if ((unit->hci_flags & BTF_UP) == 0
+                   && (btr->btr_flags & BTF_UP)) {
+                       err = hci_enable(unit);
+                       if (err)
+                               break;
+
+                       crit_enter();
+                       unit->hci_flags |= BTF_UP;
+                       crit_exit();
+               }
+
+               btr->btr_flags = unit->hci_flags;
+               break;
+
+       case SIOCSBTPOLICY:     /* set unit link policy (privileged) */
+               err = suser(td);
+               if (err)
+                       break;
+
+               unit->hci_link_policy = btr->btr_link_policy;
+               unit->hci_link_policy &= unit->hci_lmp_mask;
+               btr->btr_link_policy = unit->hci_link_policy;
+               break;
+
+       case SIOCSBTPTYPE:      /* set unit packet types (privileged) */
+               err = suser(td);
+               if (err)
+                       break;
+
+               unit->hci_packet_type = btr->btr_packet_type;
+               unit->hci_packet_type &= unit->hci_acl_mask;
+               btr->btr_packet_type = unit->hci_packet_type;
+               break;
+
+       case SIOCGBTSTATS:      /* get unit statistics */
+               crit_enter();
+               memcpy(&btr->btr_stats, &unit->hci_stats,
+                       sizeof(struct bt_stats));
+               crit_exit();
+               break;
+
+       case SIOCZBTSTATS:      /* get & reset unit statistics */
+               err = suser(td);
+               if (err)
+                       break;
+
+               crit_enter();
+               memcpy(&btr->btr_stats, &unit->hci_stats,
+                       sizeof(struct bt_stats));
+               memset(&unit->hci_stats, 0, sizeof(struct bt_stats));
+               crit_exit();
+
+               break;
+
+       case SIOCSBTSCOMTU:     /* set sco_mtu value for unit */
+               /*
+                * This is a temporary ioctl and may not be supported
+                * in the future. The need is that if SCO packets are
+                * sent to USB bluetooth controllers that are not an
+                * integer number of frame sizes, the USB bus locks up.
+                */
+               err = suser(td);
+               if (err)
+                       break;
+
+               unit->hci_max_sco_size = btr->btr_sco_mtu;
+               break;
+
+       default:
+               err = EFAULT;
+               break;
+       }
+
+       return err;
+}
diff --git a/sys/netbt/hci_link.c b/sys/netbt/hci_link.c
new file mode 100644 (file)
index 0000000..d462bc4
--- /dev/null
@@ -0,0 +1,1061 @@
+/* $OpenBSD: hci_link.c,v 1.6 2007/09/17 01:33:33 krw Exp $ */
+/* $NetBSD: hci_link.c,v 1.11 2007/04/21 06:15:23 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/hci_link.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/callout.h>
+#include <net/if.h>
+#include <net/pf/pfvar.h>
+#include <sys/bus.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+#include <netbt/l2cap.h>
+#include <netbt/sco.h>
+
+/*******************************************************************************
+ *
+ *     HCI ACL Connections
+ */
+
+/*
+ * Automatically expire unused ACL connections after this number of
+ * seconds (if zero, do not expire unused connections) [sysctl]
+ */
+int hci_acl_expiry = 10;       /* seconds */
+
+/*
+ * hci_acl_open(unit, bdaddr)
+ *
+ * open ACL connection to remote bdaddr. Only one ACL connection is permitted
+ * between any two Bluetooth devices, so we look for an existing one before
+ * trying to start a new one.
+ */
+struct hci_link *
+hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr)
+{
+       struct hci_link *link;
+       struct hci_memo *memo;
+       hci_create_con_cp cp;
+       int err;
+
+       KKASSERT(unit != NULL);
+       KKASSERT(bdaddr != NULL);
+
+       link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL);
+       if (link == NULL) {
+               link = hci_link_alloc(unit);
+               if (link == NULL)
+                       return NULL;
+
+               link->hl_type = HCI_LINK_ACL;
+               bdaddr_copy(&link->hl_bdaddr, bdaddr);
+       }
+
+       switch(link->hl_state) {
+       case HCI_LINK_CLOSED:
+               /*
+                * open connection to remote device
+                */
+               memset(&cp, 0, sizeof(cp));
+               bdaddr_copy(&cp.bdaddr, bdaddr);
+               cp.pkt_type = htole16(unit->hci_packet_type);
+
+               memo = hci_memo_find(unit, bdaddr);
+               if (memo != NULL) {
+                       cp.page_scan_rep_mode = memo->response.page_scan_rep_mode;
+                       cp.page_scan_mode = memo->response.page_scan_mode;
+                       cp.clock_offset = htole16(memo->response.clock_offset);
+               }
+
+               if (unit->hci_link_policy & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH)
+                       cp.accept_role_switch = 1;
+
+               err = hci_send_cmd(unit, HCI_CMD_CREATE_CON, &cp, sizeof(cp));
+               if (err) {
+                       hci_link_free(link, err);
+                       return NULL;
+               }
+
+               link->hl_state = HCI_LINK_WAIT_CONNECT;
+               break;
+
+       case HCI_LINK_WAIT_CONNECT:
+       case HCI_LINK_WAIT_AUTH:
+       case HCI_LINK_WAIT_ENCRYPT:
+       case HCI_LINK_WAIT_SECURE:
+               /*
+                * somebody else already trying to connect, we just
+                * sit on the bench with them..
+                */
+               break;
+
+       case HCI_LINK_OPEN:
+               /*
+                * If already open, halt any expiry callouts. We dont need
+                * to care about already invoking callouts since refcnt >0
+                * will keep the link alive.
+                */
+               callout_stop(&link->hl_expire);
+               break;
+
+       default:
+               UNKNOWN(link->hl_state);
+               return NULL;
+       }
+
+       /* open */
+       link->hl_refcnt++;
+
+       return link;
+}
+
+/*
+ * Close ACL connection. When there are no more references to this link,
+ * we can either close it down or schedule a delayed closedown.
+ */
+void
+hci_acl_close(struct hci_link *link, int err)
+{
+       KKASSERT(link != NULL);
+
+       if (--link->hl_refcnt == 0) {
+               if (link->hl_state == HCI_LINK_CLOSED)
+                       hci_link_free(link, err);
+               else if (hci_acl_expiry > 0)
+                       callout_reset(&link->hl_expire, hci_acl_expiry * hz,
+                           hci_acl_timeout, link);
+       }
+}
+
+/*
+ * Incoming ACL connection.
+ *
+ * For now, we accept all connections but it would be better to check
+ * the L2CAP listen list and only accept when there is a listener
+ * available.
+ *
+ * There should not be a link to the same bdaddr already, we check
+ * anyway though its left unhandled for now.
+ */
+struct hci_link *
+hci_acl_newconn(struct hci_unit *unit, bdaddr_t *bdaddr)
+{
+       struct hci_link *link;
+
+       link = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL);
+       if (link != NULL)
+               return NULL;
+
+       link = hci_link_alloc(unit);
+       if (link != NULL) {
+               link->hl_state = HCI_LINK_WAIT_CONNECT;
+               link->hl_type = HCI_LINK_ACL;
+               bdaddr_copy(&link->hl_bdaddr, bdaddr);
+
+               if (hci_acl_expiry > 0)
+                       callout_reset(&link->hl_expire, hci_acl_expiry * hz,
+                           hci_acl_timeout, link);
+       }
+
+       return link;
+}
+
+void
+hci_acl_timeout(void *arg)
+{
+       struct hci_link *link = arg;
+       hci_discon_cp cp;
+       int err;
+
+       crit_enter();
+
+       if (link->hl_refcnt > 0) 
+               goto out;
+
+       DPRINTF("link #%d expired\n", link->hl_handle);
+
+       switch (link->hl_state) {
+       case HCI_LINK_CLOSED:
+       case HCI_LINK_WAIT_CONNECT:
+               hci_link_free(link, ECONNRESET);
+               break;
+
+       case HCI_LINK_WAIT_AUTH:
+       case HCI_LINK_WAIT_ENCRYPT:
+       case HCI_LINK_WAIT_SECURE:
+       case HCI_LINK_OPEN:
+               cp.con_handle = htole16(link->hl_handle);
+               cp.reason = 0x13; /* "Remote User Terminated Connection" */
+
+               err = hci_send_cmd(link->hl_unit, HCI_CMD_DISCONNECT,
+                                       &cp, sizeof(cp));
+
+               if (err) {
+                       DPRINTF("error %d sending HCI_CMD_DISCONNECT\n",
+                           err);
+               }
+
+               break;
+
+       default:
+               UNKNOWN(link->hl_state);
+               break;
+       }
+
+out:
+       crit_exit(); 
+}
+
+/*
+ * Initiate any Link Mode change requests.
+ */
+int
+hci_acl_setmode(struct hci_link *link)
+{
+       int err;
+
+       KKASSERT(link != NULL);
+       KKASSERT(link->hl_unit != NULL);
+
+       if (link->hl_state != HCI_LINK_OPEN)
+               return EINPROGRESS;
+
+       if ((link->hl_flags & HCI_LINK_AUTH_REQ)
+           && !(link->hl_flags & HCI_LINK_AUTH)) {
+               hci_auth_req_cp cp;
+
+               DPRINTF("requesting auth for handle #%d\n",
+                       link->hl_handle);
+
+               link->hl_state = HCI_LINK_WAIT_AUTH;
+               cp.con_handle = htole16(link->hl_handle);
+               err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ,
+                                  &cp, sizeof(cp));
+
+               return (err == 0 ? EINPROGRESS : err);
+       }
+
+       if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ)
+           && !(link->hl_flags & HCI_LINK_ENCRYPT)) {
+               hci_set_con_encryption_cp cp;
+
+               /* XXX we should check features for encryption capability */
+
+               DPRINTF("requesting encryption for handle #%d\n",
+                       link->hl_handle);
+
+               link->hl_state = HCI_LINK_WAIT_ENCRYPT;
+               cp.con_handle = htole16(link->hl_handle);
+               cp.encryption_enable = 0x01;
+
+               err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION,
+                                  &cp, sizeof(cp));
+
+               return (err == 0 ? EINPROGRESS : err);
+       }
+
+       if ((link->hl_flags & HCI_LINK_SECURE_REQ)) {
+               hci_change_con_link_key_cp cp;
+
+               /* always change link key for SECURE requests */
+               link->hl_flags &= ~HCI_LINK_SECURE;
+
+               DPRINTF("changing link key for handle #%d\n",
+                       link->hl_handle);
+
+               link->hl_state = HCI_LINK_WAIT_SECURE;
+               cp.con_handle = htole16(link->hl_handle);
+
+               err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY,
+                                  &cp, sizeof(cp));
+
+               return (err == 0 ? EINPROGRESS : err);
+       }
+
+       return 0;
+}
+
+/*
+ * Link Mode changed.
+ *
+ * This is called from event handlers when the mode change
+ * is complete. We notify upstream and restart the link.
+ */
+void
+hci_acl_linkmode(struct hci_link *link)
+{
+       struct l2cap_channel *chan, *next;
+       int err, mode = 0;
+
+       DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n",
+               link->hl_handle,
+               (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"),
+               (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"),
+               (link->hl_flags & HCI_LINK_SECURE ? "on" : "off"));
+
+       if (link->hl_flags & HCI_LINK_AUTH)
+               mode |= L2CAP_LM_AUTH;
+
+       if (link->hl_flags & HCI_LINK_ENCRYPT)
+               mode |= L2CAP_LM_ENCRYPT;
+
+       if (link->hl_flags & HCI_LINK_SECURE)
+               mode |= L2CAP_LM_SECURE;
+
+       /*
+        * The link state will only be OPEN here if the mode change
+        * was successful. So, we can proceed with L2CAP connections,
+        * or notify already establshed channels, to allow any that
+        * are dissatisfied to disconnect before we restart.
+        */
+       next = LIST_FIRST(&l2cap_active_list);
+       while ((chan = next) != NULL) {
+               next = LIST_NEXT(chan, lc_ncid);
+
+               if (chan->lc_link != link)
+                       continue;
+
+               switch(chan->lc_state) {
+               case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */
+                       if ((mode & chan->lc_mode) != chan->lc_mode) {
+                               l2cap_close(chan, ECONNABORTED);
+                               break;
+                       }
+
+                       chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP;
+                       err = l2cap_send_connect_req(chan);
+                       if (err) {
+                               l2cap_close(chan, err);
+                               break;
+                       }
+                       break;
+
+               case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */
+                       if ((mode & chan->lc_mode) != chan->lc_mode) {
+                               l2cap_send_connect_rsp(link, chan->lc_ident,
+                                                       0, chan->lc_rcid,
+                                                       L2CAP_SECURITY_BLOCK);
+
+                               l2cap_close(chan, ECONNABORTED);
+                               break;
+                       }
+
+                       l2cap_send_connect_rsp(link, chan->lc_ident,
+                                               chan->lc_lcid, chan->lc_rcid,
+                                               L2CAP_SUCCESS);
+
+                       chan->lc_state = L2CAP_WAIT_CONFIG;
+                       chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ);
+                       err = l2cap_send_config_req(chan);
+                       if (err) {
+                               l2cap_close(chan, err);
+                               break;
+                       }
+                       break;
+
+               case L2CAP_WAIT_RECV_CONNECT_RSP:
+               case L2CAP_WAIT_CONFIG:
+               case L2CAP_OPEN: /* already established */
+                       (*chan->lc_proto->linkmode)(chan->lc_upper, mode);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       link->hl_state = HCI_LINK_OPEN;
+       hci_acl_start(link);
+}
+
+/*
+ * Receive ACL Data
+ *
+ * we accumulate packet fragments on the hci_link structure
+ * until a full L2CAP frame is ready, then send it on.
+ */
+void
+hci_acl_recv(struct mbuf *m, struct hci_unit *unit)
+{
+       struct hci_link *link;
+       hci_acldata_hdr_t hdr;
+       uint16_t handle, want;
+       int pb, got;
+
+       KKASSERT(m != NULL);
+       KKASSERT(unit != NULL);
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(hdr));
+       m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr);
+       m_adj(m, sizeof(hdr));
+
+#ifdef DIAGNOSTIC
+       if (hdr.type != HCI_ACL_DATA_PKT) {
+               kprintf("%s: bad ACL packet type\n", unit->hci_devname);
+               goto bad;
+       }
+
+       if (m->m_pkthdr.len != letoh16(hdr.length)) {
+               kprintf("%s: bad ACL packet length (%d != %d)\n",
+                       unit->hci_devname, m->m_pkthdr.len, letoh16(hdr.length));
+               goto bad;
+       }
+#endif
+
+       hdr.length = letoh16(hdr.length);
+       hdr.con_handle = letoh16(hdr.con_handle);
+       handle = HCI_CON_HANDLE(hdr.con_handle);
+       pb = HCI_PB_FLAG(hdr.con_handle);
+
+       link = hci_link_lookup_handle(unit, handle);
+       if (link == NULL) {
+               hci_discon_cp cp;
+
+               DPRINTF("%s: dumping packet for unknown handle #%d\n",
+                       unit->hci_devname, handle);
+
+               /*
+                * There is no way to find out what this connection handle is
+                * for, just get rid of it. This may happen, if a USB dongle
+                * is plugged into a self powered hub and does not reset when
+                * the system is shut down.
+                */
+               cp.con_handle = htole16(handle);
+               cp.reason = 0x13; /* "Remote User Terminated Connection" */
+               hci_send_cmd(unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp));
+               goto bad;
+       }
+
+       switch (pb) {
+       case HCI_PACKET_START:
+               if (link->hl_rxp != NULL)
+                       kprintf("%s: dropped incomplete ACL packet\n",
+                               unit->hci_devname);
+
+               if (m->m_pkthdr.len < sizeof(l2cap_hdr_t)) {
+                       kprintf("%s: short ACL packet\n",
+                               unit->hci_devname);
+
+                       goto bad;
+               }
+
+               link->hl_rxp = m;
+               got = m->m_pkthdr.len;
+               break;
+
+       case HCI_PACKET_FRAGMENT:
+               if (link->hl_rxp == NULL) {
+                       kprintf("%s: unexpected packet fragment\n",
+                               unit->hci_devname);
+
+                       goto bad;
+               }
+
+               got = m->m_pkthdr.len + link->hl_rxp->m_pkthdr.len;
+               m_cat(link->hl_rxp, m);
+               m = link->hl_rxp;
+               m->m_pkthdr.len = got;
+               break;
+
+       default:
+               kprintf("%s: unknown packet type\n",
+                       unit->hci_devname);
+
+               goto bad;
+       }
+
+       m_copydata(m, 0, sizeof(want), (caddr_t)&want);
+       want = letoh16(want) + sizeof(l2cap_hdr_t) - got;
+
+       if (want > 0)
+               return;
+
+       link->hl_rxp = NULL;
+
+       if (want == 0) {
+               l2cap_recv_frame(m, link);
+               return;
+       }
+
+bad:
+       m_freem(m);
+}
+
+/*
+ * Send ACL data on link
+ *
+ * We must fragment packets into chunks of less than unit->hci_max_acl_size and
+ * prepend a relevant ACL header to each fragment. We keep a PDU structure
+ * attached to the link, so that completed fragments can be marked off and
+ * more data requested from above once the PDU is sent.
+ */
+int
+hci_acl_send(struct mbuf *m, struct hci_link *link,
+               struct l2cap_channel *chan)
+{
+       struct l2cap_pdu *pdu;
+       struct mbuf *n = NULL;
+       int plen, mlen, num = 0;
+
+       KKASSERT(link != NULL);
+       KKASSERT(m != NULL);
+       KKASSERT(m->m_flags & M_PKTHDR);
+       KKASSERT(m->m_pkthdr.len > 0);
+
+       if (link->hl_state == HCI_LINK_CLOSED) {
+               m_freem(m);
+               return ENETDOWN;
+       }
+
+       pdu = pool_get(&l2cap_pdu_pool, PR_NOWAIT);
+       if (pdu == NULL)
+               goto nomem;
+
+       bzero(pdu, sizeof *pdu);
+       pdu->lp_chan = chan;
+       pdu->lp_pending = 0;
+
+       plen = m->m_pkthdr.len;
+       mlen = link->hl_unit->hci_max_acl_size;
+
+       DPRINTFN(5, "%s: handle #%d, plen = %d, max = %d\n",
+               link->hl_unit->hci_devname, link->hl_handle, plen, mlen);
+
+       while (plen > 0) {
+               if (plen > mlen) {
+                       n = m_split(m, mlen, MB_DONTWAIT);
+                       if (n == NULL)
+                               goto nomem;
+               } else {
+                       mlen = plen;
+               }
+
+               if (num++ == 0)
+                       m->m_flags |= M_PROTO1; /* tag first fragment */
+
+               DPRINTFN(10, "chunk of %d (plen = %d) bytes\n", mlen, plen);
+               IF_ENQUEUE(&pdu->lp_data, m);
+               m = n;
+               plen -= mlen;
+       }
+
+       TAILQ_INSERT_TAIL(&link->hl_txq, pdu, lp_next);
+       link->hl_txqlen += num;
+
+       hci_acl_start(link);
+
+       return 0;
+
+nomem:
+       if (m) m_freem(m);
+       if (pdu) {
+               IF_DRAIN(&pdu->lp_data);
+               pool_put(&l2cap_pdu_pool, pdu);
+       }
+
+       return ENOMEM;
+}
+
+/*
+ * Start sending ACL data on link.
+ *
+ *     This is called when the queue may need restarting: as new data
+ * is queued, after link mode changes have completed, or when device
+ * buffers have cleared.
+ *
+ *     We may use all the available packet slots. The reason that we add
+ * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP
+ * signal packets may be queued before the handle is given to us..
+ */
+void
+hci_acl_start(struct hci_link *link)
+{
+       struct hci_unit *unit;
+       hci_acldata_hdr_t *hdr;
+       struct l2cap_pdu *pdu;
+       struct mbuf *m;
+       uint16_t handle;
+
+       KKASSERT(link != NULL);
+
+       unit = link->hl_unit;
+       KKASSERT(unit != NULL);
+
+       /* this is mainly to block ourselves (below) */
+       if (link->hl_state != HCI_LINK_OPEN)
+               return;
+
+       if (link->hl_txqlen == 0 || unit->hci_num_acl_pkts == 0)
+               return;
+
+       /* find first PDU with data to send */
+       pdu = TAILQ_FIRST(&link->hl_txq);
+       for (;;) {
+               if (pdu == NULL)
+                       return;
+
+               if (!IF_QEMPTY(&pdu->lp_data))
+                       break;
+
+               pdu = TAILQ_NEXT(pdu, lp_next);
+       }
+
+       while (unit->hci_num_acl_pkts > 0) {
+               IF_DEQUEUE(&pdu->lp_data, m);
+               KKASSERT(m != NULL);
+
+               if (m->m_flags & M_PROTO1)
+                       handle = HCI_MK_CON_HANDLE(link->hl_handle,
+                                               HCI_PACKET_START, 0);
+               else
+                       handle = HCI_MK_CON_HANDLE(link->hl_handle,
+                                               HCI_PACKET_FRAGMENT, 0);
+
+               M_PREPEND(m, sizeof(*hdr), MB_DONTWAIT);
+               if (m == NULL)
+                       break;
+
+               hdr = mtod(m, hci_acldata_hdr_t *);
+               hdr->type = HCI_ACL_DATA_PKT;
+               hdr->con_handle = htole16(handle);
+               hdr->length = htole16(m->m_pkthdr.len - sizeof(*hdr));
+
+               link->hl_txqlen--;
+               pdu->lp_pending++;
+
+               hci_output_acl(unit, m);
+
+               if (IF_QEMPTY(&pdu->lp_data)) {
+                       if (pdu->lp_chan) {
+                               /*
+                                * This should enable streaming of PDUs - when
+                                * we have placed all the fragments on the acl
+                                * output queue, we trigger the L2CAP layer to
+                                * send us down one more. Use a false state so
+                                * we dont run into ourselves coming back from
+                                * the future..
+                                */
+                               link->hl_state = HCI_LINK_BLOCK;
+                               l2cap_start(pdu->lp_chan);
+                               link->hl_state = HCI_LINK_OPEN;
+                       }
+
+                       pdu = TAILQ_NEXT(pdu, lp_next);
+                       if (pdu == NULL)
+                               break;
+               }
+       }
+
+       /*
+        * We had our turn now, move to the back of the queue to let
+        * other links have a go at the output buffers..
+        */
+       if (TAILQ_NEXT(link, hl_next)) {
+               TAILQ_REMOVE(&unit->hci_links, link, hl_next);
+               TAILQ_INSERT_TAIL(&unit->hci_links, link, hl_next);
+       }
+}
+
+/*
+ * Confirm ACL packets cleared from Controller buffers. We scan our PDU
+ * list to clear pending fragments and signal upstream for more data
+ * when a PDU is complete.
+ */
+void
+hci_acl_complete(struct hci_link *link, int num)
+{
+       struct l2cap_pdu *pdu;
+       struct l2cap_channel *chan;
+
+       DPRINTFN(5, "handle #%d (%d)\n", link->hl_handle, num);
+
+       while (num > 0) {
+               pdu = TAILQ_FIRST(&link->hl_txq);
+               if (pdu == NULL) {
+                       kprintf("%s: %d packets completed on handle #%x "
+                               "but none pending!\n",
+                               link->hl_unit->hci_devname, num,
+                               link->hl_handle);
+                       return;
+               }
+
+               if (num >= pdu->lp_pending) {
+                       num -= pdu->lp_pending;
+                       pdu->lp_pending = 0;
+
+                       if (IF_QEMPTY(&pdu->lp_data)) {
+                               TAILQ_REMOVE(&link->hl_txq, pdu, lp_next);
+                               chan = pdu->lp_chan;
+                               if (chan != NULL) {
+                                       chan->lc_pending--;
+                                       (*chan->lc_proto->complete)
+                                                       (chan->lc_upper, 1);
+
+                                       if (chan->lc_pending == 0)
+                                               l2cap_start(chan);
+                               }
+
+                               pool_put(&l2cap_pdu_pool, pdu);
+                       }
+               } else {
+                       pdu->lp_pending -= num;
+                       num = 0;
+               }
+       }
+}
+
+/*******************************************************************************
+ *
+ *     HCI SCO Connections
+ */
+
+/*
+ * Incoming SCO Connection. We check the list for anybody willing
+ * to take it.
+ */
+struct hci_link *
+hci_sco_newconn(struct hci_unit *unit, bdaddr_t *bdaddr)
+{
+       struct sockaddr_bt laddr, raddr;
+       struct sco_pcb *pcb, *new;
+       struct hci_link *sco, *acl;
+
+       memset(&laddr, 0, sizeof(laddr));
+       laddr.bt_len = sizeof(laddr);
+       laddr.bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&laddr.bt_bdaddr, &unit->hci_bdaddr);
+
+       memset(&raddr, 0, sizeof(raddr));
+       raddr.bt_len = sizeof(raddr);
+       raddr.bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&raddr.bt_bdaddr, bdaddr);
+
+       /*
+        * There should already be an ACL link up and running before
+        * the controller sends us SCO connection requests, but you
+        * never know..
+        */
+       acl = hci_link_lookup_bdaddr(unit, bdaddr, HCI_LINK_ACL);
+       if (acl == NULL || acl->hl_state != HCI_LINK_OPEN)
+               return NULL;
+
+       LIST_FOREACH(pcb, &sco_pcb, sp_next) {
+               if ((pcb->sp_flags & SP_LISTENING) == 0)
+                       continue;
+
+               new = (*pcb->sp_proto->newconn)(pcb->sp_upper, &laddr, &raddr);
+               if (new == NULL)
+                       continue;
+
+               /*
+                * Ok, got new pcb so we can start a new link and fill
+                * in all the details.
+                */
+               bdaddr_copy(&new->sp_laddr, &unit->hci_bdaddr);
+               bdaddr_copy(&new->sp_raddr, bdaddr);
+
+               sco = hci_link_alloc(unit);
+               if (sco == NULL) {
+                       sco_detach(&new);
+                       return NULL;
+               }
+
+               sco->hl_type = HCI_LINK_SCO;
+               bdaddr_copy(&sco->hl_bdaddr, bdaddr);
+
+               sco->hl_link = hci_acl_open(unit, bdaddr);
+               KKASSERT(sco->hl_link == acl);
+
+               sco->hl_sco = new;
+               new->sp_link = sco;
+
+               new->sp_mtu = unit->hci_max_sco_size;
+               return sco;
+       }
+
+       return NULL;
+}
+
+/*
+ * receive SCO packet, we only need to strip the header and send
+ * it to the right handler
+ */
+void
+hci_sco_recv(struct mbuf *m, struct hci_unit *unit)
+{
+       struct hci_link *link;
+       hci_scodata_hdr_t hdr;
+       uint16_t handle;
+
+       KKASSERT(m != NULL);
+       KKASSERT(unit != NULL);
+
+       KKASSERT(m->m_pkthdr.len >= sizeof(hdr));
+       m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr);
+       m_adj(m, sizeof(hdr));
+
+#ifdef DIAGNOSTIC
+       if (hdr.type != HCI_SCO_DATA_PKT) {
+               kprintf("%s: bad SCO packet type\n", unit->hci_devname);
+               goto bad;
+       }
+
+       if (m->m_pkthdr.len != hdr.length) {
+               kprintf("%s: bad SCO packet length (%d != %d)\n",
+                   unit->hci_devname, m->m_pkthdr.len, hdr.length);
+               goto bad;
+       }
+#endif
+
+       hdr.con_handle = letoh16(hdr.con_handle);
+       handle = HCI_CON_HANDLE(hdr.con_handle);
+
+       link = hci_link_lookup_handle(unit, handle);
+       if (link == NULL || link->hl_type == HCI_LINK_ACL) {
+               DPRINTF("%s: dumping packet for unknown handle #%d\n",
+                       unit->hci_devname, handle);
+
+               goto bad;
+       }
+
+       (*link->hl_sco->sp_proto->input)(link->hl_sco->sp_upper, m);
+       return;
+
+bad:
+       m_freem(m);
+}
+
+void
+hci_sco_start(struct hci_link *link)
+{
+}
+
+/*
+ * SCO packets have completed at the controller, so we can
+ * signal up to free the buffer space.
+ */
+void
+hci_sco_complete(struct hci_link *link, int num)
+{
+
+       DPRINTFN(5, "handle #%d (num=%d)\n", link->hl_handle, num);
+       link->hl_sco->sp_pending--;
+       (*link->hl_sco->sp_proto->complete)(link->hl_sco->sp_upper, num);
+}
+
+/*******************************************************************************
+ *
+ *     Generic HCI Connection alloc/free/lookup etc
+ */
+
+struct hci_link *
+hci_link_alloc(struct hci_unit *unit)
+{
+       struct hci_link *link;
+
+       KKASSERT(unit != NULL);
+
+       link = kmalloc(sizeof *link, M_BLUETOOTH, M_NOWAIT | M_ZERO);
+       if (link == NULL)
+               return NULL;
+
+       link->hl_unit = unit;
+       link->hl_state = HCI_LINK_CLOSED;
+
+       /* init ACL portion */
+       callout_init(&link->hl_expire);
+
+       crit_enter();
+       TAILQ_INIT(&link->hl_txq);      /* outgoing packets */
+       TAILQ_INIT(&link->hl_reqs);     /* request queue */
+
+       link->hl_mtu = L2CAP_MTU_DEFAULT;               /* L2CAP signal mtu */
+       link->hl_flush = L2CAP_FLUSH_TIMO_DEFAULT;      /* flush timeout */
+
+       /* attach to unit */
+       TAILQ_INSERT_HEAD(&unit->hci_links, link, hl_next);
+       crit_exit();    
+       return link;
+}
+
+void
+hci_link_free(struct hci_link *link, int err)
+{
+       struct l2cap_req *req;
+       struct l2cap_pdu *pdu;
+       struct l2cap_channel *chan, *next;
+
+       KKASSERT(link != NULL);
+
+       DPRINTF("#%d, type = %d, state = %d, refcnt = %d\n",
+               link->hl_handle, link->hl_type,
+               link->hl_state, link->hl_refcnt);
+
+       /* ACL reference count */
+       if (link->hl_refcnt > 0) {
+               next = LIST_FIRST(&l2cap_active_list);
+               while ((chan = next) != NULL) {
+                       next = LIST_NEXT(chan, lc_ncid);
+                       if (chan->lc_link == link)
+                               l2cap_close(chan, err);
+               }
+       }
+       KKASSERT(link->hl_refcnt == 0);
+
+       /* ACL L2CAP requests.. */
+       while ((req = TAILQ_FIRST(&link->hl_reqs)) != NULL)
+               l2cap_request_free(req);
+
+       KKASSERT(TAILQ_EMPTY(&link->hl_reqs));
+
+       /* ACL outgoing data queue */
+       while ((pdu = TAILQ_FIRST(&link->hl_txq)) != NULL) {
+               TAILQ_REMOVE(&link->hl_txq, pdu, lp_next);
+               IF_DRAIN(&pdu->lp_data);
+               if (pdu->lp_pending)
+                       link->hl_unit->hci_num_acl_pkts += pdu->lp_pending;
+
+               pool_put(&l2cap_pdu_pool, pdu);
+       }
+
+       KKASSERT(TAILQ_EMPTY(&link->hl_txq));
+
+       /* ACL incoming data packet */
+       if (link->hl_rxp != NULL) {
+               m_freem(link->hl_rxp);
+               link->hl_rxp = NULL;
+       }
+
+       /* SCO master ACL link */
+       if (link->hl_link != NULL) {
+               hci_acl_close(link->hl_link, err);
+               link->hl_link = NULL;
+       }
+
+       /* SCO pcb */
+       if (link->hl_sco != NULL) {
+               struct sco_pcb *pcb;
+
+               pcb = link->hl_sco;
+               pcb->sp_link = NULL;
+               link->hl_sco = NULL;
+               (*pcb->sp_proto->disconnected)(pcb->sp_upper, err);
+       }
+
+       /* flush any SCO data */
+       crit_enter();   
+       IF_DRAIN(&link->hl_data);
+       crit_exit();
+       /*
+        * Halt the timeout - if its already running we cannot free the
+        * link structure but the timeout function will call us back in
+        * any case.
+        */
+       link->hl_state = HCI_LINK_CLOSED;
+       callout_stop(&link->hl_expire);
+       if (callout_active(&link->hl_expire))
+               return;
+
+       crit_enter();
+       TAILQ_REMOVE(&link->hl_unit->hci_links, link, hl_next);
+       crit_exit();    
+       kfree(link, M_BLUETOOTH);
+}
+
+/*
+ * Lookup HCI link by type and state.
+ */
+struct hci_link *
+hci_link_lookup_state(struct hci_unit *unit, uint16_t type, uint16_t state)
+{
+       struct hci_link *link;
+
+       TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+               if (link->hl_type == type && link->hl_state == state)
+                       break;
+       }
+
+       return link;
+}
+
+/*
+ * Lookup HCI link by address and type. Note that for SCO links there may
+ * be more than one link per address, so we only return links with no
+ * handle (ie new links)
+ */
+struct hci_link *
+hci_link_lookup_bdaddr(struct hci_unit *unit, bdaddr_t *bdaddr, uint16_t type)
+{
+       struct hci_link *link;
+
+       KKASSERT(unit != NULL);
+       KKASSERT(bdaddr != NULL);
+
+       TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+               if (link->hl_type != type)
+                       continue;
+
+               if (type == HCI_LINK_SCO && link->hl_handle != 0)
+                       continue;
+
+               if (bdaddr_same(&link->hl_bdaddr, bdaddr))
+                       break;
+       }
+
+       return link;
+}
+
+struct hci_link *
+hci_link_lookup_handle(struct hci_unit *unit, uint16_t handle)
+{
+       struct hci_link *link;
+
+       KKASSERT(unit != NULL);
+
+       TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+               if (handle == link->hl_handle)
+                       break;
+       }
+
+       return link;
+}
diff --git a/sys/netbt/hci_misc.c b/sys/netbt/hci_misc.c
new file mode 100644 (file)
index 0000000..7a11155
--- /dev/null
@@ -0,0 +1,149 @@
+/* $OpenBSD: hci_misc.c,v 1.1 2007/05/30 03:42:53 uwe Exp $ */
+/* $NetBSD: hci_misc.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */
+/* $DragonFly: src/sys/netbt/hci_misc.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+/*
+ * cache Inquiry Responses for this number of seconds for routing
+ * purposes [sysctl]
+ */
+int hci_memo_expiry = 600;
+
+/*
+ * set 'src' address for routing to 'dest'
+ */
+int
+hci_route_lookup(bdaddr_t *src, bdaddr_t *dest)
+{
+       struct hci_unit *unit;
+       struct hci_link *link;
+       struct hci_memo *memo;
+
+       /*
+        * Walk the ACL connections, if we have a connection
+        * to 'dest' already then thats best..
+        */
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               if ((unit->hci_flags & BTF_UP) == 0)
+                       continue;
+
+               TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+                       if (link->hl_type != HCI_LINK_ACL)
+                               continue;
+
+                       if (bdaddr_same(&link->hl_bdaddr, dest))
+                               goto found;
+               }
+       }
+
+       /*
+        * Now check all the memos to see if there has been an
+        * inquiry repsonse..
+        */
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               if ((unit->hci_flags & BTF_UP) == 0)
+                       continue;
+
+               memo = hci_memo_find(unit, dest);
+               if (memo)
+                       goto found;
+       }
+
+       /*
+        * Last ditch effort, lets use the first unit we find
+        * thats up and running. (XXX settable default route?)
+        */
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               if ((unit->hci_flags & BTF_UP) == 0)
+                       continue;
+
+               goto found;
+       }
+
+       return EHOSTUNREACH;
+
+found:
+       bdaddr_copy(src, &unit->hci_bdaddr);
+       return 0;
+}
+
+/*
+ * find unit memo from bdaddr
+ */
+struct hci_memo *
+hci_memo_find(struct hci_unit *unit, bdaddr_t *bdaddr)
+{
+       struct hci_memo *memo, *m0;
+       struct timeval now;
+
+       microtime(&now);
+
+       m0 = LIST_FIRST(&unit->hci_memos);
+       while ((memo = m0) != NULL) {
+               m0 = LIST_NEXT(memo, next);
+
+               if (now.tv_sec > memo->time.tv_sec + hci_memo_expiry) {
+                       DPRINTF("memo %p too old (expiring)\n", memo);
+                       hci_memo_free(memo);
+                       continue;
+               }
+
+               if (bdaddr_same(bdaddr, &memo->response.bdaddr)) {
+                       DPRINTF("memo %p found\n", memo);
+                       return memo;
+               }
+       }
+
+       DPRINTF("no memo found\n");
+       return NULL;
+}
+
+void
+hci_memo_free(struct hci_memo *memo)
+{
+
+       LIST_REMOVE(memo, next);
+       kfree(memo, M_BLUETOOTH);
+}
diff --git a/sys/netbt/hci_socket.c b/sys/netbt/hci_socket.c
new file mode 100644 (file)
index 0000000..2e5ccfe
--- /dev/null
@@ -0,0 +1,770 @@
+/* $OpenBSD: hci_socket.c,v 1.4 2007/09/17 01:33:33 krw Exp $ */
+/* $NetBSD: hci_socket.c,v 1.10 2007/03/31 18:17:13 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/hci_socket.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/cdefs.h>
+
+/* load symbolic names */
+#ifdef BLUETOOTH_DEBUG
+#define PRUREQUESTS
+#define PRCOREQUESTS
+#endif
+
+#include <sys/param.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <sys/sysctl.h>
+#include <sys/thread2.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+/*******************************************************************************
+ *
+ * HCI SOCK_RAW Sockets - for control of Bluetooth Devices
+ *
+ */
+
+/*
+ * the raw HCI protocol control block
+ */
+struct hci_pcb {
+       struct socket           *hp_socket;     /* socket */
+       unsigned int            hp_flags;       /* flags */
+       bdaddr_t                hp_laddr;       /* local address */
+       bdaddr_t                hp_raddr;       /* remote address */
+       struct hci_filter       hp_efilter;     /* user event filter */
+       struct hci_filter       hp_pfilter;     /* user packet filter */
+       LIST_ENTRY(hci_pcb)     hp_next;        /* next HCI pcb */
+};
+
+/* hp_flags */
+#define HCI_PRIVILEGED         (1<<0)  /* no security filter for root */
+#define HCI_DIRECTION          (1<<1)  /* direction control messages */
+#define HCI_PROMISCUOUS                (1<<2)  /* listen to all units */
+
+LIST_HEAD(hci_pcb_list, hci_pcb) hci_pcb = LIST_HEAD_INITIALIZER(hci_pcb);
+
+/* sysctl defaults */
+int hci_sendspace = HCI_CMD_PKT_SIZE;
+int hci_recvspace = 4096;
+
+extern struct pr_usrreqs hci_usrreqs;
+
+/* Prototypes for usrreqs methods. */
+static int hci_sabort (struct socket *so);
+static int hci_sdetach(struct socket *so);
+static int hci_sdisconnect (struct socket *so);
+static int hci_scontrol (struct socket *so, u_long cmd, caddr_t data,
+                                    struct ifnet *ifp, struct thread *td);
+static int hci_sattach (struct socket *so, int proto,
+                               struct pru_attach_info *ai);
+static int hci_sbind (struct socket *so, struct sockaddr *nam,
+                                 struct thread *td);
+static int hci_sconnect (struct socket *so, struct sockaddr *nam,
+                                    struct thread *td);
+static int hci_speeraddr (struct socket *so, struct sockaddr **nam);
+static int hci_ssockaddr (struct socket *so, struct sockaddr **nam);
+static int hci_sshutdown (struct socket *so);
+static int hci_ssend (struct socket *so, int flags, struct mbuf *m,
+                                 struct sockaddr *addr, struct mbuf *control,
+                                 struct thread *td);
+
+/*
+ * Security filter routines for unprivileged users.
+ *     Allow all but a few critical events, and only permit read commands.
+ */
+
+static int
+hci_security_check_opcode(uint16_t opcode)
+{
+
+       switch (opcode) {
+       /* Link control */
+       case HCI_CMD_INQUIRY:
+               return sizeof(hci_inquiry_cp);
+       case HCI_CMD_REMOTE_NAME_REQ:
+               return sizeof(hci_remote_name_req_cp);
+       case HCI_CMD_READ_REMOTE_FEATURES:
+               return sizeof(hci_read_remote_features_cp);
+       case HCI_CMD_READ_REMOTE_EXTENDED_FEATURES:
+               return sizeof(hci_read_remote_extended_features_cp);
+       case HCI_CMD_READ_REMOTE_VER_INFO:
+               return sizeof(hci_read_remote_ver_info_cp);
+       case HCI_CMD_READ_CLOCK_OFFSET:
+               return sizeof(hci_read_clock_offset_cp);
+       case HCI_CMD_READ_LMP_HANDLE:
+               return sizeof(hci_read_lmp_handle_cp);
+
+       /* Link policy */
+       case HCI_CMD_ROLE_DISCOVERY:
+               return sizeof(hci_role_discovery_cp);
+       case HCI_CMD_READ_LINK_POLICY_SETTINGS:
+               return sizeof(hci_read_link_policy_settings_cp);
+       case HCI_CMD_READ_DEFAULT_LINK_POLICY_SETTINGS:
+               return 0;       /* No command parameters */
+
+       /* Host controller and baseband */
+       case HCI_CMD_READ_PIN_TYPE:
+       case HCI_CMD_READ_LOCAL_NAME:
+       case HCI_CMD_READ_CON_ACCEPT_TIMEOUT:
+       case HCI_CMD_READ_PAGE_TIMEOUT:
+       case HCI_CMD_READ_SCAN_ENABLE:
+       case HCI_CMD_READ_PAGE_SCAN_ACTIVITY:
+       case HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY:
+       case HCI_CMD_READ_AUTH_ENABLE:
+       case HCI_CMD_READ_ENCRYPTION_MODE:
+       case HCI_CMD_READ_UNIT_CLASS:
+       case HCI_CMD_READ_VOICE_SETTING:
+               return 0;       /* No command parameters */
+       case HCI_CMD_READ_AUTO_FLUSH_TIMEOUT:
+               return sizeof(hci_read_auto_flush_timeout_cp);
+       case HCI_CMD_READ_NUM_BROADCAST_RETRANS:
+       case HCI_CMD_READ_HOLD_MODE_ACTIVITY:
+               return 0;       /* No command parameters */
+       case HCI_CMD_READ_XMIT_LEVEL:
+               return sizeof(hci_read_xmit_level_cp);
+       case HCI_CMD_READ_SCO_FLOW_CONTROL:
+               return 0;       /* No command parameters */
+       case HCI_CMD_READ_LINK_SUPERVISION_TIMEOUT:
+               return sizeof(hci_read_link_supervision_timeout_cp);
+       case HCI_CMD_READ_NUM_SUPPORTED_IAC:
+       case HCI_CMD_READ_IAC_LAP:
+       case HCI_CMD_READ_PAGE_SCAN_PERIOD:
+       case HCI_CMD_READ_PAGE_SCAN:
+       case HCI_CMD_READ_INQUIRY_SCAN_TYPE:
+       case HCI_CMD_READ_INQUIRY_MODE:
+       case HCI_CMD_READ_PAGE_SCAN_TYPE:
+       case HCI_CMD_READ_AFH_ASSESSMENT:
+               return 0;       /* No command parameters */
+
+       /* Informational */
+       case HCI_CMD_READ_LOCAL_VER:
+       case HCI_CMD_READ_LOCAL_COMMANDS:
+       case HCI_CMD_READ_LOCAL_FEATURES:
+               return 0;       /* No command parameters */
+       case HCI_CMD_READ_LOCAL_EXTENDED_FEATURES:
+               return sizeof(hci_read_local_extended_features_cp);
+       case HCI_CMD_READ_BUFFER_SIZE:
+       case HCI_CMD_READ_COUNTRY_CODE:
+       case HCI_CMD_READ_BDADDR:
+               return 0;       /* No command parameters */
+
+       /* Status */
+       case HCI_CMD_READ_FAILED_CONTACT_CNTR:
+               return sizeof(hci_read_failed_contact_cntr_cp);
+       case HCI_CMD_READ_LINK_QUALITY:
+               return sizeof(hci_read_link_quality_cp);
+       case HCI_CMD_READ_RSSI:
+               return sizeof(hci_read_rssi_cp);
+       case HCI_CMD_READ_AFH_CHANNEL_MAP:
+               return sizeof(hci_read_afh_channel_map_cp);
+       case HCI_CMD_READ_CLOCK:
+               return sizeof(hci_read_clock_cp);
+
+       /* Testing */
+       case HCI_CMD_READ_LOOPBACK_MODE:
+               return 0;       /* No command parameters */
+       }
+
+       return -1;      /* disallowed */
+}
+
+static int
+hci_security_check_event(uint8_t event)
+{
+
+       switch (event) {
+       case HCI_EVENT_RETURN_LINK_KEYS:
+       case HCI_EVENT_LINK_KEY_NOTIFICATION:
+       case HCI_EVENT_VENDOR:
+               return -1;      /* disallowed */
+       }
+
+       return 0;       /* ok */
+}
+
+/*
+ * When command packet reaches the device, we can drop
+ * it from the socket buffer (called from hci_output_acl)
+ */
+void
+hci_drop(void *arg)
+{
+       struct socket *so = arg;
+
+       sbdroprecord(&so->so_snd.sb);
+       sowwakeup(so);
+}
+
+/*
+ * HCI socket is going away and has some pending packets. We let them
+ * go by design, but remove the context pointer as it will be invalid
+ * and we no longer need to be notified.
+ */
+static void
+hci_cmdwait_flush(struct socket *so)
+{
+       struct hci_unit *unit;
+       struct socket *ctx;
+       struct mbuf *m;
+
+       DPRINTF("flushing %p\n", so);
+
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               IF_POLL(&unit->hci_cmdwait, m);
+               while (m != NULL) {
+                       ctx = M_GETCTX(m, struct socket *);
+                       if (ctx == so)
+                               M_SETCTX(m, NULL);
+
+                       m = m->m_nextpkt;
+               }
+       }
+}
+
+/*
+ * HCI send packet
+ *     This came from userland, so check it out.
+ */
+static int
+hci_send(struct hci_pcb *pcb, struct mbuf *m, bdaddr_t *addr)
+{
+       struct hci_unit *unit;
+       struct mbuf *m0;
+       hci_cmd_hdr_t hdr;
+       int err;
+
+       KKASSERT(m != NULL);
+       KKASSERT(addr != NULL);
+
+       /* wants at least a header to start with */
+       if (m->m_pkthdr.len < sizeof(hdr)) {
+               err = EMSGSIZE;
+               goto bad;
+       }
+       m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr);
+
+       /* only allows CMD packets to be sent */
+       if (hdr.type != HCI_CMD_PKT) {
+               err = EINVAL;
+               goto bad;
+       }
+
+       /* validates packet length */
+       if (m->m_pkthdr.len != sizeof(hdr) + hdr.length) {
+               err = EMSGSIZE;
+               goto bad;
+       }
+
+       /* security checks for unprivileged users */
+       if ((pcb->hp_flags & HCI_PRIVILEGED) == 0
+           && hci_security_check_opcode(letoh16(hdr.opcode)) != hdr.length) {
+               err = EPERM;
+               goto bad;
+       }
+
+       /* finds destination */
+       unit = hci_unit_lookup(addr);
+       if (unit == NULL) {
+               err = ENETDOWN;
+               goto bad;
+       }
+
+       /* makes a copy for precious to keep */
+       m0 = m_copym(m, 0, M_COPYALL, MB_DONTWAIT);
+       if (m0 == NULL) {
+               err = ENOMEM;
+               goto bad;
+       }
+       sbappendrecord(&pcb->hp_socket->so_snd.sb, m0);
+       M_SETCTX(m, pcb->hp_socket);    /* enable drop callback */
+
+       DPRINTFN(2, "(%s) opcode (%03x|%04x)\n", unit->hci_devname,
+               HCI_OGF(letoh16(hdr.opcode)), HCI_OCF(letoh16(hdr.opcode)));
+
+       /* Sendss it */
+       if (unit->hci_num_cmd_pkts == 0) {
+               IF_ENQUEUE(&unit->hci_cmdwait, m);
+       } else
+               hci_output_cmd(unit, m);
+
+       return 0;
+
+bad:
+       DPRINTF("packet (%d bytes) not sent (error %d)\n",
+                       m->m_pkthdr.len, err);
+       if (m) m_freem(m);
+       return err;
+}
+
+/*
+ * Implementation of usrreqs.
+ */
+static int
+hci_sabort (struct socket *so)
+{
+       /* struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;  */
+
+       soisdisconnected(so);
+       return hci_sdetach(so);
+}
+
+static int
+hci_sdetach(struct socket *so)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;     
+       
+       if (pcb == NULL)
+               return EINVAL;
+       if (so->so_snd.ssb_mb != NULL)
+               hci_cmdwait_flush(so);
+
+       so->so_pcb = NULL;
+       LIST_REMOVE(pcb, hp_next);
+       kfree(pcb, M_PCB);
+       
+       return 0;
+}
+
+static int
+hci_sdisconnect (struct socket *so)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;     
+
+       if (pcb==NULL)
+               return EINVAL;
+
+       bdaddr_copy(&pcb->hp_raddr, BDADDR_ANY);
+       /*
+        * XXX We cannot call soisdisconnected() here, as it sets
+        * SS_CANTRCVMORE and SS_CANTSENDMORE. The problem is that
+        * soisconnected() does not clear these and if you try to reconnect
+        * this socket (which is permitted) you get a broken pipe when you
+        * try to write any data.
+        */
+       so->so_state &= ~SS_ISCONNECTED;
+       
+       return 0;
+}
+
+static int
+hci_scontrol (struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
+    struct thread *td)
+{
+       return hci_ioctl(cmd, (void*)data, NULL);
+}
+
+static int
+hci_sattach (struct socket *so, int proto, struct pru_attach_info *ai)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       int err = 0;
+
+       if (pcb)
+               return EINVAL;
+
+       err = soreserve(so, hci_sendspace, hci_recvspace,NULL);
+       if (err) 
+               return err;
+
+       pcb = kmalloc(sizeof *pcb, M_PCB, M_NOWAIT | M_ZERO);
+       if (pcb == NULL) 
+               return ENOMEM;
+
+       so->so_pcb = pcb;
+       pcb->hp_socket = so;
+
+       if (curproc == NULL || suser(curthread) == 0)
+               pcb->hp_flags |= HCI_PRIVILEGED;
+
+       /*
+        * Set default user filter. By default, socket only passes
+        * Command_Complete and Command_Status Events.
+        */
+       hci_filter_set(HCI_EVENT_COMMAND_COMPL, &pcb->hp_efilter);
+       hci_filter_set(HCI_EVENT_COMMAND_STATUS, &pcb->hp_efilter);
+       hci_filter_set(HCI_EVENT_PKT, &pcb->hp_pfilter);
+
+       crit_enter();
+       LIST_INSERT_HEAD(&hci_pcb, pcb, hp_next);
+       crit_exit();
+
+       return 0;
+}
+
+static int
+hci_sbind (struct socket *so, struct sockaddr *nam,
+                                struct thread *td)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       struct sockaddr_bt *sa;
+
+       KKASSERT(nam != NULL);
+       sa = (struct sockaddr_bt *)nam;
+
+       if (sa->bt_len != sizeof(struct sockaddr_bt))
+               return EINVAL;
+
+       if (sa->bt_family != AF_BLUETOOTH)
+               return EAFNOSUPPORT;
+
+       bdaddr_copy(&pcb->hp_laddr, &sa->bt_bdaddr);
+
+       if (bdaddr_any(&sa->bt_bdaddr))
+               pcb->hp_flags |= HCI_PROMISCUOUS;
+       else
+               pcb->hp_flags &= ~HCI_PROMISCUOUS;
+
+       return 0;
+}
+
+static int
+hci_sconnect (struct socket *so, struct sockaddr *nam,
+                                   struct thread *td)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       struct sockaddr_bt *sa;
+       KKASSERT(nam != NULL);
+       sa = (struct sockaddr_bt *)nam;
+
+       if (sa->bt_len != sizeof(struct sockaddr_bt))
+               return EINVAL;
+
+       if (sa->bt_family != AF_BLUETOOTH)
+               return EAFNOSUPPORT;
+
+       if (hci_unit_lookup(&sa->bt_bdaddr) == NULL)
+               return EADDRNOTAVAIL;
+
+       bdaddr_copy(&pcb->hp_raddr, &sa->bt_bdaddr);
+       soisconnected(so);
+
+       return 0;
+}
+
+static int
+hci_speeraddr (struct socket *so, struct sockaddr **nam)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       struct sockaddr_bt *sa;
+
+       KKASSERT(nam != NULL);
+       sa = (struct sockaddr_bt *)nam;
+
+       memset(sa, 0, sizeof(struct sockaddr_bt));
+       sa->bt_len = sizeof(struct sockaddr_bt);
+       sa->bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&sa->bt_bdaddr, &pcb->hp_raddr);
+
+       return 0;
+}
+
+static int
+hci_ssockaddr (struct socket *so, struct sockaddr **nam)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       struct sockaddr_bt *sa;
+       
+       KKASSERT(nam != NULL);
+       sa = (struct sockaddr_bt *)nam;
+
+       memset(sa, 0, sizeof(struct sockaddr_bt));
+       sa->bt_len = sizeof(struct sockaddr_bt);
+       sa->bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&sa->bt_bdaddr, &pcb->hp_laddr);
+
+       return 0;
+}
+
+static int
+hci_sshutdown (struct socket *so)
+{
+       socantsendmore(so);
+       return 0;
+}
+
+static int
+hci_ssend (struct socket *so, int flags, struct mbuf *m, 
+                                struct sockaddr *addr, struct mbuf *control,
+                                struct thread *td)
+{
+       int err = 0;
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       struct sockaddr_bt *sa;
+
+       sa = NULL;
+       if (addr) {
+               sa = (struct sockaddr_bt *)addr;
+
+               if (sa->bt_len != sizeof(struct sockaddr_bt)) {
+                       err = EINVAL;
+                       if (m) m_freem(m);
+                       if (control) m_freem(control);
+                       return err;
+               }
+
+               if (sa->bt_family != AF_BLUETOOTH) {
+                       err = EAFNOSUPPORT;
+                       if (m) m_freem(m);
+                       if (control) m_freem(control);
+                       return err;
+               }
+       }
+
+       if (control) /* have no use for this */
+               m_freem(control);
+
+       return hci_send(pcb, m, (sa ? &sa->bt_bdaddr : &pcb->hp_raddr));
+}
+
+/*
+ * get/set socket options
+ */
+int
+hci_ctloutput (struct socket *so, struct sockopt *sopt)
+{
+       struct hci_pcb *pcb = (struct hci_pcb *)so->so_pcb;
+       int idir = 0;
+       int err = 0;
+
+#ifdef notyet                  /* XXX */
+       DPRINTFN(2, "req %s\n", prcorequests[req]);
+#endif
+
+       if (pcb == NULL)
+               return EINVAL;
+
+       if (sopt->sopt_level != BTPROTO_HCI)
+               return ENOPROTOOPT;
+
+       switch(sopt->sopt_dir) {
+       case PRCO_GETOPT:
+               switch (sopt->sopt_name) {
+               case SO_HCI_EVT_FILTER:
+                       err = sooptcopyout(sopt, &pcb->hp_efilter,
+                           sizeof(struct hci_filter));
+                       break;
+
+               case SO_HCI_PKT_FILTER:
+                        err = sooptcopyout(sopt, &pcb->hp_pfilter,
+                           sizeof(struct hci_filter));
+                       break;
+
+               case SO_HCI_DIRECTION:
+                       if (pcb->hp_flags & HCI_DIRECTION)
+                               idir = 1;
+                       else
+                               idir = 0;
+                       err = sooptcopyout(sopt, &idir, sizeof(int));
+                       break;
+
+               default:
+                       err = ENOPROTOOPT;
+                       break;
+               }
+               break;
+
+       case PRCO_SETOPT:
+               switch (sopt->sopt_name) {
+               case SO_HCI_EVT_FILTER: /* set event filter */
+                       err = sooptcopyin(sopt, &pcb->hp_efilter,
+                           sizeof(struct hci_filter),
+                           sizeof(struct hci_filter)); 
+                       break;
+
+               case SO_HCI_PKT_FILTER: /* set packet filter */
+                       err = sooptcopyin(sopt, &pcb->hp_pfilter,
+                           sizeof(struct hci_filter),
+                           sizeof(struct hci_filter)); 
+                       break;
+
+               case SO_HCI_DIRECTION:  /* request direction ctl messages */
+                       err = sooptcopyin(sopt, &idir, sizeof(int),
+                           sizeof(int)); 
+                       if (err) break;
+                       if (idir)
+                               pcb->hp_flags |= HCI_DIRECTION;
+                       else
+                               pcb->hp_flags &= ~HCI_DIRECTION;
+                       break;
+
+               default:
+                       err = ENOPROTOOPT;
+                       break;
+               }
+               break;
+
+       default:
+               err = ENOPROTOOPT;
+               break;
+       }
+
+       return err;
+}
+
+/*
+ * HCI mbuf tap routine
+ *
+ * copy packets to any raw HCI sockets that wish (and are
+ * permitted) to see them
+ */
+void
+hci_mtap(struct mbuf *m, struct hci_unit *unit)
+{
+       struct hci_pcb *pcb;
+       struct mbuf *m0, *ctlmsg, **ctl;
+       struct sockaddr_bt sa;
+       uint8_t type;
+       uint8_t event;
+       uint16_t opcode;
+
+       KKASSERT(m->m_len >= sizeof(type));
+
+       type = *mtod(m, uint8_t *);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.bt_len = sizeof(struct sockaddr_bt);
+       sa.bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&sa.bt_bdaddr, &unit->hci_bdaddr);
+
+       LIST_FOREACH(pcb, &hci_pcb, hp_next) {
+               /*
+                * filter according to source address
+                */
+               if ((pcb->hp_flags & HCI_PROMISCUOUS) == 0
+                   && bdaddr_same(&pcb->hp_laddr, &sa.bt_bdaddr) == 0)
+                       continue;
+
+               /*
+                * filter according to packet type filter
+                */
+               if (hci_filter_test(type, &pcb->hp_pfilter) == 0)
+                       continue;
+
+               /*
+                * filter according to event/security filters
+                */
+               switch(type) {
+               case HCI_EVENT_PKT:
+                       KKASSERT(m->m_len >= sizeof(hci_event_hdr_t));
+
+                       event = mtod(m, hci_event_hdr_t *)->event;
+
+                       if (hci_filter_test(event, &pcb->hp_efilter) == 0)
+                               continue;
+
+                       if ((pcb->hp_flags & HCI_PRIVILEGED) == 0
+                           && hci_security_check_event(event) == -1)
+                               continue;
+                       break;
+
+               case HCI_CMD_PKT:
+                       KKASSERT(m->m_len >= sizeof(hci_cmd_hdr_t));
+
+                       opcode = letoh16(mtod(m, hci_cmd_hdr_t *)->opcode);
+
+                       if ((pcb->hp_flags & HCI_PRIVILEGED) == 0
+                           && hci_security_check_opcode(opcode) == -1)
+                               continue;
+                       break;
+
+               case HCI_ACL_DATA_PKT:
+               case HCI_SCO_DATA_PKT:
+               default:
+                       if ((pcb->hp_flags & HCI_PRIVILEGED) == 0)
+                               continue;
+
+                       break;
+               }
+
+               /*
+                * create control messages
+                */
+               ctlmsg = NULL;
+               ctl = &ctlmsg;
+               if (pcb->hp_flags & HCI_DIRECTION) {
+                       int dir = m->m_flags & IFF_LINK0 ? 1 : 0;
+
+                       *ctl = sbcreatecontrol((void *)&dir, sizeof(dir),
+                           SCM_HCI_DIRECTION, BTPROTO_HCI);
+
+                       if (*ctl != NULL)
+                               ctl = &((*ctl)->m_next);
+               }
+
+               /*
+                * copy to socket
+                */
+               m0 = m_copym(m, 0, M_COPYALL, MB_DONTWAIT);
+               if (m0 && sbappendaddr(&pcb->hp_socket->so_rcv.sb,
+                               (struct sockaddr *)&sa, m0, ctlmsg)) {
+                       sorwakeup(pcb->hp_socket);
+               } else {
+                       m_freem(ctlmsg);
+                       m_freem(m0);
+               }
+       }
+}
+
+struct pr_usrreqs hci_usrreqs = {
+        .pru_abort = hci_sabort,
+        .pru_accept = pru_accept_notsupp,
+        .pru_attach = hci_sattach,
+        .pru_bind = hci_sbind,
+        .pru_connect = hci_sconnect,
+        .pru_connect2 = pru_connect2_notsupp,
+        .pru_control = hci_scontrol,
+        .pru_detach = hci_sdetach,
+        .pru_disconnect = hci_sdisconnect,
+        .pru_listen = pru_listen_notsupp,
+        .pru_peeraddr = hci_speeraddr,
+        .pru_rcvd = pru_rcvd_notsupp,
+        .pru_rcvoob = pru_rcvoob_notsupp,
+        .pru_send = hci_ssend,
+        .pru_sense = pru_sense_null,
+        .pru_shutdown = hci_sshutdown,
+        .pru_sockaddr = hci_ssockaddr,
+        .pru_sosend = sosend,
+        .pru_soreceive = soreceive,
+        .pru_sopoll = sopoll
+};
diff --git a/sys/netbt/hci_unit.c b/sys/netbt/hci_unit.c
new file mode 100644 (file)
index 0000000..0a63fb1
--- /dev/null
@@ -0,0 +1,504 @@
+/* $OpenBSD: hci_unit.c,v 1.7 2007/06/24 20:55:27 uwe Exp $ */
+/* $NetBSD: hci_unit.c,v 1.4 2007/03/30 20:47:03 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/hci_unit.c,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/bus.h>
+
+#include <net/netisr.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+struct hci_unit_list hci_unit_list = TAILQ_HEAD_INITIALIZER(hci_unit_list);
+
+/*
+ * HCI Input Queue max lengths.
+ */
+int hci_eventq_max = 20;
+int hci_aclrxq_max = 50;
+int hci_scorxq_max = 50;
+
+/*
+ * bluetooth unit functions
+ */
+
+void
+hci_attach(struct hci_unit *unit)
+{
+       KKASSERT(unit->hci_softc != NULL);
+       KKASSERT(unit->hci_devname != NULL);
+       KKASSERT(unit->hci_enable != NULL);
+       KKASSERT(unit->hci_disable != NULL);
+       KKASSERT(unit->hci_start_cmd != NULL);
+       KKASSERT(unit->hci_start_acl != NULL);
+       KKASSERT(unit->hci_start_sco != NULL);
+
+       unit->hci_eventq.ifq_maxlen = hci_eventq_max;
+       unit->hci_aclrxq.ifq_maxlen = hci_aclrxq_max;
+       unit->hci_scorxq.ifq_maxlen = hci_scorxq_max;
+
+       TAILQ_INIT(&unit->hci_links);
+       LIST_INIT(&unit->hci_memos);
+
+       TAILQ_INSERT_TAIL(&hci_unit_list, unit, hci_next);
+}
+
+void
+hci_detach(struct hci_unit *unit)
+{
+       hci_disable(unit);
+
+       TAILQ_REMOVE(&hci_unit_list, unit, hci_next);
+}
+
+int
+hci_enable(struct hci_unit *unit)
+{
+       int err;
+
+       /*
+        * Bluetooth spec says that a device can accept one
+        * command on power up until they send a Command Status
+        * or Command Complete event with more information, but
+        * it seems that some devices cant and prefer to send a
+        * No-op Command Status packet when they are ready, so
+        * we set this here and allow the driver (bt3c) to zero
+        * it.
+        */
+       unit->hci_num_cmd_pkts = 1;
+       unit->hci_num_acl_pkts = 0;
+       unit->hci_num_sco_pkts = 0;
+
+       /*
+        * only allow the basic packet types until
+        * the features report is in
+        */
+       unit->hci_acl_mask = HCI_PKT_DM1 | HCI_PKT_DH1;
+       unit->hci_packet_type = unit->hci_acl_mask;
+       
+       err = (*unit->hci_enable)(unit);
+       if (err)
+               goto bad1;
+
+       /*
+        * Reset the device, this will trigger initialisation
+        * and wake us up.
+        */
+       crit_enter(); 
+       unit->hci_flags |= BTF_INIT;
+       crit_exit();
+
+       err = hci_send_cmd(unit, HCI_CMD_RESET, NULL, 0);
+       if (err)
+               goto bad2;
+               
+       while (unit->hci_flags & BTF_INIT) {
+               err = tsleep(unit, PCATCH, "hciena", 5 * hz);
+               if (err)
+                       goto bad2;
+                       
+               /* XXX
+                * "What If", while we were sleeping, the device
+                * was removed and detached? Ho Hum.
+                */
+       }
+
+#if 0 /* not yet */
+       /*
+        * Attach Bluetooth Device Hub
+        */
+       unit->hci_bthub = NULL;
+       
+       unit->hci_bthub = device_add_child(unit->hci_softc, "bthub", -1);
+       if (!unit->hci_bthub) {
+               device_printf(unit->hci_softc, "Device creation failed\n");
+               goto bad2;
+       }
+
+       DPRINTFN(10, "%s is added as child to %s\n",
+           device_get_nameunit(unit->hci_bthub),
+           device_get_nameunit(unit->hci_softc));
+
+       device_set_desc(unit->hci_bthub,"Bluetooth Device Hub");
+
+       device_set_ivars(unit->hci_bthub, &unit->hci_bdaddr);
+
+       device_probe_and_attach(unit->hci_bthub);
+#endif
+       return 0;
+
+bad2:
+       (*unit->hci_disable)(unit);
+
+bad1:
+       return err;
+}
+
+void
+hci_disable(struct hci_unit *unit)
+{
+       struct hci_link *link, *next;
+       struct hci_memo *memo;
+       int acl;
+
+#if 0 /* not yet */
+       if (unit->hci_bthub) {
+               device_delete_child(unit->hci_softc, unit->hci_bthub);
+               unit->hci_bthub = NULL;
+       }
+#endif
+
+       (*unit->hci_disable)(unit);
+
+       /*
+        * close down any links, take care to close SCO first since
+        * they may depend on ACL links.
+        */
+       for (acl = 0 ; acl < 2 ; acl++) {
+               next = TAILQ_FIRST(&unit->hci_links);
+               while ((link = next) != NULL) {
+                       next = TAILQ_NEXT(link, hl_next);
+                       if (acl || link->hl_type != HCI_LINK_ACL)
+                               hci_link_free(link, ECONNABORTED);
+               }
+       }
+
+       while ((memo = LIST_FIRST(&unit->hci_memos)) != NULL)
+               hci_memo_free(memo);
+
+       IF_DRAIN(&unit->hci_eventq);
+       unit->hci_eventqlen = 0;
+
+       IF_DRAIN(&unit->hci_aclrxq);
+       unit->hci_aclrxqlen = 0;
+
+       IF_DRAIN(&unit->hci_scorxq);
+       unit->hci_scorxqlen = 0;
+
+       IF_DRAIN(&unit->hci_cmdq);
+       IF_DRAIN(&unit->hci_cmdwait);
+       IF_DRAIN(&unit->hci_acltxq);
+       IF_DRAIN(&unit->hci_scotxq);
+       IF_DRAIN(&unit->hci_scodone);
+}
+
+struct hci_unit *
+hci_unit_lookup(bdaddr_t *addr)
+{
+       struct hci_unit *unit;
+
+       TAILQ_FOREACH(unit, &hci_unit_list, hci_next) {
+               if ((unit->hci_flags & BTF_UP) == 0)
+                       continue;
+
+               if (bdaddr_same(&unit->hci_bdaddr, addr))
+                       break;
+       }
+
+       return unit;
+}
+
+/*
+ * construct and queue a HCI command packet
+ */
+int
+hci_send_cmd(struct hci_unit *unit, uint16_t opcode, void *buf, uint8_t len)
+{
+
+       struct mbuf *m;
+       hci_cmd_hdr_t *p;
+
+       KKASSERT(unit != NULL);
+
+       m = m_gethdr(MB_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               return ENOMEM;
+
+       p = mtod(m, hci_cmd_hdr_t *);
+       p->type = HCI_CMD_PKT;
+       p->opcode = htole16(opcode);
+       p->length = len;
+       m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);
+       M_SETCTX(m, NULL);
+
+       if (len) {
+               KKASSERT(buf != NULL);
+
+               m_copyback(m, sizeof(hci_cmd_hdr_t), len, buf);
+               if (m->m_pkthdr.len != (sizeof(hci_cmd_hdr_t) + len)) {
+                       m_freem(m);
+                       return ENOMEM;
+               }
+       }
+
+       DPRINTFN(2, "(%s) opcode (%3.3x|%4.4x)\n", unit->hci_devname,
+               HCI_OGF(opcode), HCI_OCF(opcode));
+
+       /* and send it on */
+       if (unit->hci_num_cmd_pkts == 0) {
+               IF_ENQUEUE(&unit->hci_cmdwait, m);
+       } else
+               hci_output_cmd(unit, m);
+
+       return 0;
+}
+
+/*
+ * Incoming packet processing. Since the code is single threaded
+ * in any case (IPL_SOFTNET), we handle it all in one interrupt function
+ * picking our way through more important packets first so that hopefully
+ * we will never get clogged up with bulk data.
+ */
+void
+hci_intr(void *arg)
+{
+       struct hci_unit *unit = arg;
+       struct mbuf *m;
+
+another:
+       crit_enter();
+
+       if (unit->hci_eventqlen > 0) {
+               IF_DEQUEUE(&unit->hci_eventq, m);
+               unit->hci_eventqlen--;
+               crit_exit();
+               KKASSERT(m != NULL);
+
+               DPRINTFN(10, "(%s) recv event, len = %d\n",
+                               unit->hci_devname, m->m_pkthdr.len);
+
+               m->m_flags |= IFF_LINK0;        /* mark incoming packet */
+               hci_mtap(m, unit);
+               hci_event(m, unit);
+
+               goto another;
+       }
+
+       if (unit->hci_scorxqlen > 0) {
+               IF_DEQUEUE(&unit->hci_scorxq, m);
+               unit->hci_scorxqlen--;
+               crit_exit();
+               KKASSERT(m != NULL);
+
+               DPRINTFN(10, "(%s) recv SCO, len = %d\n",
+                               unit->hci_devname, m->m_pkthdr.len);
+
+               m->m_flags |= IFF_LINK0;        /* mark incoming packet */
+               hci_mtap(m, unit);
+               hci_sco_recv(m, unit);
+               
+               goto another;
+       }
+
+       if (unit->hci_aclrxqlen > 0) {
+               IF_DEQUEUE(&unit->hci_aclrxq, m);
+               unit->hci_aclrxqlen--;
+               crit_exit();
+               KKASSERT(m != NULL);
+               
+               DPRINTFN(10, "(%s) recv ACL, len = %d\n",
+                               unit->hci_devname, m->m_pkthdr.len);
+
+               m->m_flags |= IFF_LINK0;        /* mark incoming packet */
+               hci_mtap(m, unit);
+               hci_acl_recv(m, unit);
+
+               goto another;
+       }
+
+       IF_DEQUEUE(&unit->hci_scodone, m);
+       if (m != NULL) {
+               struct hci_link *link;
+               crit_exit();
+
+               DPRINTFN(11, "(%s) complete SCO\n",
+                               unit->hci_devname);
+
+               TAILQ_FOREACH(link, &unit->hci_links, hl_next) {
+                       if (link == M_GETCTX(m, struct hci_link *)) {
+                               hci_sco_complete(link, 1);
+                               break;
+                       }
+               }
+
+               unit->hci_num_sco_pkts++;
+               m_freem(m);
+
+               goto another;
+       }
+
+       crit_exit();
+
+       DPRINTFN(10, "done\n");
+}
+
+/**********************************************************************
+ *
+ * IO routines
+ *
+ * input & complete routines will be called from device driver
+ * (at unit->hci_ipl)
+ */
+
+void
+hci_input_event(struct hci_unit *unit, struct mbuf *m)
+{
+       if (unit->hci_eventqlen > hci_eventq_max) {
+               DPRINTF("(%s) dropped event packet.\n", unit->hci_devname);
+               unit->hci_stats.err_rx++;
+               m_freem(m);
+       } else {
+               unit->hci_eventqlen++;
+               crit_enter();
+               IF_ENQUEUE(&unit->hci_eventq, m);
+               crit_exit();
+               netisr_queue(NETISR_BLUETOOTH, m);
+       }
+}
+
+void
+hci_input_acl(struct hci_unit *unit, struct mbuf *m)
+{
+       if (unit->hci_aclrxqlen > hci_aclrxq_max) {
+               DPRINTF("(%s) dropped ACL packet.\n", unit->hci_devname);
+               unit->hci_stats.err_rx++;
+               m_freem(m);
+       } else {
+               unit->hci_aclrxqlen++;
+               crit_enter();
+               IF_ENQUEUE(&unit->hci_aclrxq, m);
+               crit_exit();
+               netisr_queue(NETISR_BLUETOOTH,m);
+       }
+}
+
+void
+hci_input_sco(struct hci_unit *unit, struct mbuf *m)
+{
+       if (unit->hci_scorxqlen > hci_scorxq_max) {
+               DPRINTF("(%s) dropped SCO packet.\n", unit->hci_devname);
+               unit->hci_stats.err_rx++;
+               m_freem(m);
+       } else {
+               unit->hci_scorxqlen++;
+               crit_enter();
+               IF_ENQUEUE(&unit->hci_scorxq, m);
+               crit_exit();
+               netisr_queue(NETISR_BLUETOOTH,m);
+       }
+}
+
+void
+hci_output_cmd(struct hci_unit *unit, struct mbuf *m)
+{
+       void *arg;
+
+       hci_mtap(m, unit);
+
+       DPRINTFN(10, "(%s) num_cmd_pkts=%d\n", unit->hci_devname,
+                                              unit->hci_num_cmd_pkts);
+
+       unit->hci_num_cmd_pkts--;
+
+       /*
+        * If context is set, this was from a HCI raw socket
+        * and a record needs to be dropped from the sockbuf.
+        */
+       arg = M_GETCTX(m, void *);
+       if (arg != NULL)
+               hci_drop(arg);
+
+       crit_enter();
+       IF_ENQUEUE(&unit->hci_cmdq, m);
+       crit_exit();
+       if ((unit->hci_flags & BTF_XMIT_CMD) == 0)
+               (*unit->hci_start_cmd)(unit);
+}
+
+void
+hci_output_acl(struct hci_unit *unit, struct mbuf *m)
+{
+       hci_mtap(m, unit);
+
+       DPRINTFN(10, "(%s) num_acl_pkts=%d\n", unit->hci_devname,
+                                              unit->hci_num_acl_pkts);
+
+       unit->hci_num_acl_pkts--;
+
+       crit_enter();
+       IF_ENQUEUE(&unit->hci_acltxq, m);
+       crit_exit();
+
+       if ((unit->hci_flags & BTF_XMIT_ACL) == 0)
+               (*unit->hci_start_acl)(unit);
+}
+
+void
+hci_output_sco(struct hci_unit *unit, struct mbuf *m)
+{
+
+       hci_mtap(m, unit);
+
+       DPRINTFN(10, "(%s) num_sco_pkts=%d\n", unit->hci_devname,
+                                              unit->hci_num_sco_pkts);
+
+       unit->hci_num_sco_pkts--;
+
+       crit_enter();
+       IF_ENQUEUE(&unit->hci_scotxq, m);
+       crit_exit();
+       if ((unit->hci_flags & BTF_XMIT_SCO) == 0)
+               (*unit->hci_start_sco)(unit);
+
+}
+
+void
+hci_complete_sco(struct hci_unit *unit, struct mbuf *m)
+{
+       IF_ENQUEUE(&unit->hci_scodone, m);
+       crit_enter();
+       netisr_queue(NETISR_BLUETOOTH,m);
+       crit_exit();
+}
diff --git a/sys/netbt/l2cap.h b/sys/netbt/l2cap.h
new file mode 100644 (file)
index 0000000..4391f12
--- /dev/null
@@ -0,0 +1,491 @@
+/* $OpenBSD: l2cap.h,v 1.4 2007/06/01 02:46:11 uwe Exp $ */
+/* $NetBSD: l2cap.h,v 1.5 2007/04/21 06:15:23 plunky Exp $ */
+/* $DragonFly: src/sys/netbt/l2cap.h,v 1.1 2007/12/30 20:02:56 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain Hibbert.
+ * Copyright (c) 2006 Itronix Inc.
+ * 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.
+ * 3. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+/*-
+ * 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.
+ *
+ * $FreeBSD: src/sys/netgraph/bluetooth/include/l2cap.h,v 1.4 2005/08/31 18:13:23 emax Exp $
+ */
+
+/*
+ * This file contains everything that application needs to know about
+ * Link Layer Control and Adaptation Protocol (L2CAP). All information
+ * was obtained from Bluetooth Specification Books (v1.1 and up)
+ *
+ * This file can be included by both kernel and userland applications.
+ */
+
+#ifndef _NETBT_L2CAP_H_
+#define _NETBT_L2CAP_H_
+
+#include <sys/types.h>
+
+/**************************************************************************
+ **************************************************************************
+ **                   Common defines and types (L2CAP)
+ **************************************************************************
+ **************************************************************************/
+
+/*
+ * Channel IDs are assigned per machine. So the total number of channels that
+ * a machine can have open at the same time is 0xffff - 0x0040 = 0xffbf (65471).
+ * This number does not depend on number of HCI connections.
+ */
+
+#define L2CAP_NULL_CID                 0x0000  /* DO NOT USE THIS CID */
+#define L2CAP_SIGNAL_CID               0x0001  /* signaling channel ID */
+#define L2CAP_CLT_CID                  0x0002  /* connectionless channel ID */
+       /* 0x0003 - 0x003f Reserved */
+#define L2CAP_FIRST_CID                        0x0040  /* dynamically alloc. (start) */
+#define L2CAP_LAST_CID                 0xffff  /* dynamically alloc. (end) */
+
+/* L2CAP MTU */
+#define L2CAP_MTU_MINIMUM              48
+#define L2CAP_MTU_DEFAULT              672
+#define L2CAP_MTU_MAXIMUM              0xffff
+
+/* L2CAP flush and link timeouts */
+#define L2CAP_FLUSH_TIMO_DEFAULT       0xffff /* always retransmit */
+#define L2CAP_LINK_TIMO_DEFAULT                0xffff
+
+/* L2CAP Command Reject reasons */
+#define L2CAP_REJ_NOT_UNDERSTOOD       0x0000
+#define L2CAP_REJ_MTU_EXCEEDED         0x0001
+#define L2CAP_REJ_INVALID_CID          0x0002
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* Protocol/Service Multiplexor (PSM) values */
+#define L2CAP_PSM_ANY                  0x0000  /* Any/Invalid PSM */
+#define L2CAP_PSM_SDP                  0x0001  /* Service Discovery Protocol */
+#define L2CAP_PSM_RFCOMM               0x0003  /* RFCOMM protocol */
+#define L2CAP_PSM_TCP                  0x0005  /* Telephony Control Protocol */
+#define L2CAP_PSM_TCS                  0x0007  /* TCS cordless */
+#define L2CAP_PSM_BNEP                 0x000f  /* Bluetooth Network */
+                                               /*      Encapsulation Protocol*/
+#define L2CAP_PSM_HID_CNTL             0x0011  /* HID Control */
+#define L2CAP_PSM_HID_INTR             0x0013  /* HID Interrupt */
+#define L2CAP_PSM_ESDP                 0x0015  /* Extended Service */
+                                               /*      Discovery Profile */
+#define L2CAP_PSM_AVCTP                        0x0017  /* Audio/Visual Control */
+                                               /*      Transport Protocol */
+#define L2CAP_PSM_AVDTP                        0x0019  /* Audio/Visual Distribution */
+                                               /*      Transport Protocol */
+/* 0x0019 - 0x1000 - reserved for future use */
+
+#define L2CAP_PSM_INVALID(psm)         (((psm) & 0x0101) != 0x0001)
+
+/* L2CAP Connection response command result codes */
+#define L2CAP_SUCCESS                  0x0000
+#define L2CAP_PENDING                  0x0001
+#define L2CAP_PSM_NOT_SUPPORTED                0x0002
+#define L2CAP_SECURITY_BLOCK           0x0003
+#define L2CAP_NO_RESOURCES             0x0004
+#define L2CAP_TIMEOUT                  0xeeee
+#define L2CAP_UNKNOWN                  0xffff
+/* 0x0005 - 0xffff - reserved for future use */
+
+/* L2CAP Connection response status codes */
+#define L2CAP_NO_INFO                  0x0000
+#define L2CAP_AUTH_PENDING             0x0001
+#define L2CAP_AUTZ_PENDING             0x0002
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* L2CAP Configuration response result codes */
+#define L2CAP_UNACCEPTABLE_PARAMS      0x0001
+#define L2CAP_REJECT                   0x0002
+#define L2CAP_UNKNOWN_OPTION           0x0003
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* L2CAP Configuration options */
+#define L2CAP_OPT_CFLAG_BIT            0x0001
+#define L2CAP_OPT_CFLAG(flags)         ((flags) & L2CAP_OPT_CFLAG_BIT)
+#define L2CAP_OPT_HINT_BIT             0x80
+#define L2CAP_OPT_HINT(type)           ((type) & L2CAP_OPT_HINT_BIT)
+#define L2CAP_OPT_HINT_MASK            0x7f
+#define L2CAP_OPT_MTU                  0x01
+#define L2CAP_OPT_MTU_SIZE             sizeof(uint16_t)
+#define L2CAP_OPT_FLUSH_TIMO           0x02
+#define L2CAP_OPT_FLUSH_TIMO_SIZE      sizeof(uint16_t)
+#define L2CAP_OPT_QOS                  0x03
+#define L2CAP_OPT_QOS_SIZE             sizeof(l2cap_qos_t)
+#define L2CAP_OPT_RFC                  0x04
+#define L2CAP_OPT_RFC_SIZE             sizeof(l2cap_rfc_t)
+/* 0x05 - 0xff - reserved for future use */
+
+/* L2CAP Information request type codes */
+#define L2CAP_CONNLESS_MTU             0x0001
+#define L2CAP_EXTENDED_FEATURES                0x0002
+/* 0x0003 - 0xffff - reserved for future use */
+
+/* L2CAP Information response codes */
+#define L2CAP_NOT_SUPPORTED            0x0001
+/* 0x0002 - 0xffff - reserved for future use */
+
+/* L2CAP Quality of Service option */
+typedef struct {
+       uint8_t  flags;                 /* reserved for future use */
+       uint8_t  service_type;          /* service type */
+       uint32_t token_rate;            /* bytes per second */
+       uint32_t token_bucket_size;     /* bytes */
+       uint32_t peak_bandwidth;        /* bytes per second */
+       uint32_t latency;               /* microseconds */
+       uint32_t delay_variation;       /* microseconds */
+} __attribute__ ((__packed__)) l2cap_qos_t;
+
+/* L2CAP QoS type */
+#define L2CAP_QOS_NO_TRAFFIC   0x00
+#define L2CAP_QOS_BEST_EFFORT  0x01       /* (default) */
+#define L2CAP_QOS_GUARANTEED   0x02
+/* 0x03 - 0xff - reserved for future use */
+
+/* L2CAP Retransmission & Flow Control option */
+typedef struct {
+       uint8_t mode;              /* RFC mode */
+       uint8_t window_size;       /* bytes */
+       uint8_t max_transmit;      /* max retransmissions */
+       uint16_t        retransmit_timo;   /* milliseconds */
+       uint16_t        monitor_timo;      /* milliseconds */
+       uint16_t        max_pdu_size;      /* bytes */
+} __attribute__ ((__packed__)) l2cap_rfc_t;
+
+/* L2CAP RFC mode */
+#define L2CAP_RFC_BASIC                0x00       /* (default) */
+#define L2CAP_RFC_RETRANSMIT   0x01
+#define L2CAP_RFC_FLOW         0x02
+/* 0x03 - 0xff - reserved for future use */
+
+/**************************************************************************
+ **************************************************************************
+ **                 Link level defines, headers and types
+ **************************************************************************
+ **************************************************************************/
+
+/* L2CAP header */
+typedef struct {
+       uint16_t        length; /* payload size */
+       uint16_t        dcid;   /* destination channel ID */
+} __attribute__ ((__packed__)) l2cap_hdr_t;
+
+/* L2CAP ConnectionLess Traffic                (dcid == L2CAP_CLT_CID) */
+typedef struct {
+       uint16_t        psm; /* Protocol/Service Multiplexor */
+} __attribute__ ((__packed__)) l2cap_clt_hdr_t;
+
+#define L2CAP_CLT_MTU_MAXIMUM \
+       (L2CAP_MTU_MAXIMUM - sizeof(l2cap_clt_hdr_t))
+
+/* L2CAP Command header                        (dcid == L2CAP_SIGNAL_CID) */
+typedef struct {
+       uint8_t code;   /* command OpCode */
+       uint8_t ident;  /* identifier to match request and response */
+       uint16_t        length; /* command parameters length */
+} __attribute__ ((__packed__)) l2cap_cmd_hdr_t;
+
+/* L2CAP Command Reject */
+#define L2CAP_COMMAND_REJ                      0x01
+typedef struct {
+       uint16_t        reason; /* reason to reject command */
+       uint16_t        data[2];/* optional data */
+} __attribute__ ((__packed__)) l2cap_cmd_rej_cp;
+
+/* L2CAP Connection Request */
+#define L2CAP_CONNECT_REQ                      0x02
+typedef struct {
+       uint16_t        psm;  /* Protocol/Service Multiplexor (PSM) */
+       uint16_t        scid; /* source channel ID */
+} __attribute__ ((__packed__)) l2cap_con_req_cp;
+
+/* L2CAP Connection Response */
+#define L2CAP_CONNECT_RSP                      0x03
+typedef struct {
+       uint16_t        dcid;   /* destination channel ID */
+       uint16_t        scid;   /* source channel ID */
+       uint16_t        result; /* 0x00 - success */
+       uint16_t        status; /* more info if result != 0x00 */
+} __attribute__ ((__packed__)) l2cap_con_rsp_cp;
+
+/* L2CAP Configuration Request */
+#define L2CAP_CONFIG_REQ                       0x04
+typedef struct {
+       uint16_t        dcid;  /* destination channel ID */
+       uint16_t        flags; /* flags */
+/*     uint8_t options[] --  options */
+} __attribute__ ((__packed__)) l2cap_cfg_req_cp;
+
+/* L2CAP Configuration Response */
+#define L2CAP_CONFIG_RSP                       0x05
+typedef struct {
+       uint16_t        scid;   /* source channel ID */
+       uint16_t        flags;  /* flags */
+       uint16_t        result; /* 0x00 - success */
+/*     uint8_t options[] -- options */
+} __attribute__ ((__packed__)) l2cap_cfg_rsp_cp;
+
+/* L2CAP configuration option */
+typedef struct {
+       uint8_t type;
+       uint8_t length;
+/*     uint8_t value[] -- option value (depends on type) */
+} __attribute__ ((__packed__)) l2cap_cfg_opt_t;
+
+/* L2CAP configuration option value */
+typedef union {
+       uint16_t                mtu;            /* L2CAP_OPT_MTU */
+       uint16_t                flush_timo;     /* L2CAP_OPT_FLUSH_TIMO */
+       l2cap_qos_t             qos;            /* L2CAP_OPT_QOS */
+       l2cap_rfc_t             rfc;            /* L2CAP_OPT_RFC */
+} l2cap_cfg_opt_val_t;
+
+/* L2CAP Disconnect Request */
+#define L2CAP_DISCONNECT_REQ                   0x06
+typedef struct {
+       uint16_t        dcid; /* destination channel ID */
+       uint16_t        scid; /* source channel ID */
+} __attribute__ ((__packed__)) l2cap_discon_req_cp;
+
+/* L2CAP Disconnect Response */
+#define L2CAP_DISCONNECT_RSP                   0x07
+typedef l2cap_discon_req_cp    l2cap_discon_rsp_cp;
+
+/* L2CAP Echo Request */
+#define L2CAP_ECHO_REQ                         0x08
+/* No command parameters, only optional data */
+
+/* L2CAP Echo Response */
+#define L2CAP_ECHO_RSP                         0x09
+#define L2CAP_MAX_ECHO_SIZE \
+       (L2CAP_MTU_MAXIMUM - sizeof(l2cap_cmd_hdr_t))
+/* No command parameters, only optional data */
+
+/* L2CAP Information Request */
+#define L2CAP_INFO_REQ                         0x0a
+typedef struct {
+       uint16_t        type; /* requested information type */
+} __attribute__ ((__packed__)) l2cap_info_req_cp;
+
+/* L2CAP Information Response */
+#define L2CAP_INFO_RSP                         0x0b
+typedef struct {
+       uint16_t        type;   /* requested information type */
+       uint16_t        result; /* 0x00 - success */
+/*     uint8_t info[]  -- info data (depends on type)
+ *
+ * L2CAP_CONNLESS_MTU - 2 bytes connectionless MTU
+ */
+} __attribute__ ((__packed__)) l2cap_info_rsp_cp;
+
+typedef union {
+       /* L2CAP_CONNLESS_MTU */
+       struct {
+               uint16_t        mtu;
+       } __attribute__ ((__packed__)) mtu;
+} l2cap_info_rsp_data_t;
+
+/**************************************************************************
+ **************************************************************************
+ **            L2CAP Socket Definitions
+ **************************************************************************
+ **************************************************************************/
+
+/* Socket options */
+#define SO_L2CAP_IMTU          1       /* incoming MTU */
+#define SO_L2CAP_OMTU          2       /* outgoing MTU */
+#define SO_L2CAP_IQOS          3       /* incoming QoS */
+#define SO_L2CAP_OQOS          4       /* outgoing QoS */
+#define SO_L2CAP_FLUSH         5       /* flush timeout */
+#define SO_L2CAP_LM            6       /* link mode */
+
+/* L2CAP link mode flags */
+#define L2CAP_LM_AUTH          (1<<0)  /* want authentication */
+#define L2CAP_LM_ENCRYPT       (1<<1)  /* want encryption */
+#define L2CAP_LM_SECURE                (1<<2)  /* want secured link */
+
+#ifdef _KERNEL
+#include <vm/vm_zone.h>
+
+#include <net/if.h>            /* for struct ifqueue */
+
+LIST_HEAD(l2cap_channel_list, l2cap_channel);
+
+/* global variables */
+extern struct l2cap_channel_list l2cap_active_list;
+extern struct l2cap_channel_list l2cap_listen_list;
+extern vm_zone_t l2cap_pdu_pool;
+extern vm_zone_t l2cap_req_pool;
+extern const l2cap_qos_t l2cap_default_qos;
+
+/* sysctl variables */
+extern int l2cap_response_timeout;
+extern int l2cap_response_extended_timeout;
+extern int l2cap_sendspace, l2cap_recvspace;
+
+/*
+ * L2CAP Channel
+ */
+struct l2cap_channel {
+       struct hci_link         *lc_link;       /* ACL connection (down) */
+       uint16_t                 lc_state;      /* channel state */
+       uint16_t                 lc_flags;      /* channel flags */
+       uint8_t                  lc_ident;      /* cached request id */
+
+       uint16_t                 lc_lcid;       /* local channel ID */
+       struct sockaddr_bt       lc_laddr;      /* local address */
+
+       uint16_t                 lc_rcid;       /* remote channel ID */
+       struct sockaddr_bt       lc_raddr;      /* remote address */
+
+       int                      lc_mode;       /* link mode */
+       uint16_t                 lc_imtu;       /* incoming mtu */
+       uint16_t                 lc_omtu;       /* outgoing mtu */
+       uint16_t                 lc_flush;      /* flush timeout */
+       l2cap_qos_t              lc_iqos;       /* incoming QoS flow control */
+       l2cap_qos_t              lc_oqos;       /* outgoing Qos flow control */
+
+       uint8_t                  lc_pending;    /* num of pending PDUs */
+       struct ifqueue           lc_txq;        /* transmit queue */
+
+       const struct btproto    *lc_proto;      /* upper layer callbacks */
+       void                    *lc_upper;      /* upper layer argument */
+
+       LIST_ENTRY(l2cap_channel)lc_ncid;       /* next channel (ascending CID) */
+};
+
+/* l2cap_channel state */
+#define L2CAP_CLOSED                   0 /* closed */
+#define L2CAP_WAIT_SEND_CONNECT_REQ    1 /* waiting to send connect request */
+#define L2CAP_WAIT_RECV_CONNECT_RSP    2 /* waiting to recv connect response */
+#define L2CAP_WAIT_SEND_CONNECT_RSP    3 /* waiting to send connect response */
+#define L2CAP_WAIT_CONFIG              4 /* waiting for configuration */
+#define L2CAP_OPEN                     5 /* user data transfer state */
+#define L2CAP_WAIT_DISCONNECT          6 /* have sent disconnect request */
+
+/* l2cap_channel flags */
+#define L2CAP_SHUTDOWN         (1<<0)  /* channel is closing */
+#define L2CAP_WAIT_CONFIG_REQ  (1<<1)  /* waiting for config request */
+#define L2CAP_WAIT_CONFIG_RSP  (1<<2)  /* waiting for config response */
+
+/*
+ * L2CAP Request
+ */
+struct l2cap_req {
+       struct hci_link         *lr_link;       /* ACL connection */
+       struct l2cap_channel    *lr_chan;       /* channel pointer */
+       uint8_t                  lr_code;       /* request code */
+       uint8_t                  lr_id;         /* request id */
+       struct callout           lr_rtx;        /* response timer */
+       TAILQ_ENTRY(l2cap_req)   lr_next;       /* next request on link */
+};
+
+/*
+ * L2CAP Protocol Data Unit
+ */
+struct l2cap_pdu {
+       struct l2cap_channel    *lp_chan;       /* PDU owner */
+       struct ifqueue           lp_data;       /* PDU data */
+       TAILQ_ENTRY(l2cap_pdu)   lp_next;       /* next PDU on link */
+       int                      lp_pending;    /* # of fragments pending */
+};
+
+/*
+ * L2CAP function prototypes
+ */
+
+extern struct pr_usrreqs l2cap_usrreqs;
+struct socket;
+struct mbuf;
+
+/* l2cap_lower.c */
+void l2cap_close(struct l2cap_channel *, int);
+void l2cap_recv_frame(struct mbuf *, struct hci_link *);
+int l2cap_start(struct l2cap_channel *);
+
+/* l2cap_misc.c */
+void l2cap_init(void);
+int l2cap_setmode(struct l2cap_channel *);
+int l2cap_cid_alloc(struct l2cap_channel *);
+struct l2cap_channel *l2cap_cid_lookup(uint16_t);
+int l2cap_request_alloc(struct l2cap_channel *, uint8_t);
+struct l2cap_req *l2cap_request_lookup(struct hci_link *, uint8_t);
+void l2cap_request_free(struct l2cap_req *);
+void l2cap_rtx(void *);
+
+/* l2cap_signal.c */
+void l2cap_recv_signal(struct mbuf *, struct hci_link *);
+int l2cap_send_connect_req(struct l2cap_channel *);
+int l2cap_send_config_req(struct l2cap_channel *);