Index: bluetoothdevices.config
===================================================================
RCS file: /cvsroot/src/sys/dev/bluetooth/bluetoothdevices.config,v
retrieving revision 1.1
diff -u -p -r1.1 bluetoothdevices.config
--- src/sys/dev/bluetooth/bluetoothdevices.config       20 Jul 2011 22:42:59 -0000      1.1
+++ src/sys/dev/bluetooth/bluetoothdevices.config       10 Aug 2017 09:01:27 -0000
@@ -44,3 +44,4 @@ btsco* at bthub?
# Bluetooth pseudo devices
pseudo-device  bcsp                    # BlueCore Serial Protocol
pseudo-device  btuart                  # Bluetooth HCI UART (H4)
+pseudo-device  bthfive                 # Bluetooth HCI UART (H5)
Index: files.bluetooth
===================================================================
RCS file: /cvsroot/src/sys/dev/bluetooth/files.bluetooth,v
retrieving revision 1.14
diff -u -p -r1.14 files.bluetooth
--- src/sys/dev/bluetooth/files.bluetooth       22 May 2010 18:56:01 -0000      1.14
+++ src/sys/dev/bluetooth/files.bluetooth       10 Aug 2017 09:01:27 -0000
@@ -44,3 +44,7 @@ file dev/bluetooth/btuart.c           btuart
# BlueCore Serial Protocol
defpseudodev bcsp: btbus, bluetooth
file dev/bluetooth/bcsp.c              bcsp
+
+# Bluetooth HCI UART (H5)
+defpseudodev bthfive: btbus, bluetooth
+file dev/bluetooth/bth5.c              bthfive
--- /dev/null   2017-08-10 18:51:00.000000000 +1000
+++ src/sys/dev/bluetooth/bth5.h        2017-08-10 18:50:54.000000000 +1000
@@ -0,0 +1,128 @@
+/*     $NetBSD$        */
+/*
+ * Copyright (c) 2017 Nathanial Sloss <[email protected]>
+ * All rights reserved.
+ *
+ * Copyright (c) 2007 KIYOHARA Takashi
+ * 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 ``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 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 _DEV_BLUETOOTH_BTH5_H_
+#define _DEV_BLUETOOTH_BTH5_H_
+
+/*
+ * BT UART H5 (3-wire) serial protocol definitions.
+ */
+
+/* BTH5 packet header */
+typedef struct {
+       uint8_t flags;
+#if BYTE_ORDER == BIG_ENDIAN
+       uint8_t plen1 :4;               /* Payload Length (bits 0-3) */
+       uint8_t ident :4;               /* Protocol Identifier */
+#else
+       uint8_t ident :4;               /* Protocol Identifier */
+       uint8_t plen1 :4;               /* Payload Length (bits 0-3) */
+#endif
+       uint8_t plen2;                  /* Payload Length (bits 4-11) */
+       uint8_t csum;                   /* Checksum */
+       u_char payload[0];
+} __packed bth5_hdr_t;
+
+#define BTH5_FLAGS_SEQ_SHIFT   0
+#define BTH5_FLAGS_SEQ_MASK    0x07
+#define BTH5_FLAGS_SEQ(n) \
+       (((n) & BTH5_FLAGS_SEQ_MASK) >> BTH5_FLAGS_SEQ_SHIFT)
+#define BTH5_FLAGS_ACK_SHIFT   3
+#define BTH5_FLAGS_ACK_MASK    0x38
+#define BTH5_FLAGS_ACK(n) \
+       (((n) & BTH5_FLAGS_ACK_MASK) >> BTH5_FLAGS_ACK_SHIFT)
+#define BTH5_FLAGS_CRC_PRESENT 0x40
+#define BTH5_FLAGS_PROTOCOL_TYPE 0x80
+#define BTH5_FLAGS_PROTOCOL_REL        0x80
+
+#define BTH5_SET_PLEN(hdrp, n)                         \
+       do {                                            \
+               (hdrp)->plen1 = ((n) & 0x00f);          \
+               (hdrp)->plen2 = ((n) >> 4);             \
+       } while (0)
+#define BTH5_GET_PLEN(hdrp)    ((hdrp)->plen1 | ((hdrp)->plen2 << 4))
+
+#define BTH5_GET_CSUM(hdrp)                                            \
+       (0xff - (uint8_t)((hdrp)->flags + ((hdrp)->plen1 << 4) +        \
+       (hdrp)->ident + (hdrp)->plen2))
+#define BTH5_SET_CSUM(hdrp)    ((hdrp)->csum = BTH5_GET_CSUM(hdrp))
+
+
+#define BTH5_IDENT_ACKPKT      0       /* Used by MUX Layer */
+/* Other Protocol Identifier values described to bcore-sp-007P */
+
+
+/* definitions of SLIP Layer */
+#define BTH5_SLIP_PKTSTART     0xc0
+#define BTH5_SLIP_PKTEND       BTH5_SLIP_PKTSTART
+#define BTH5_SLIP_XON          0x11
+#define BTH5_SLIP_XOFF         0x13
+#define BTH5_SLIP_ESCAPE       0xdb
+#define BTH5_SLIP_ESCAPE_PKTEND        0xdc
+#define BTH5_SLIP_ESCAPE_ESCAPE        0xdd
+#define BTH5_SLIP_ESCAPE_XON   0xde
+#define BTH5_SLIP_ESCAPE_XOFF  0xdf
+
+
+/* definitions of Sequencing Layer */
+#define BTH5_SEQ_TX_TIMEOUT    (hz / 4)        /* 250 msec */
+#define BTH5_SEQ_TX_WINSIZE    7
+#define BTH5_SEQ_TX_RETRY_LIMIT        20
+
+
+/*
+ * Reference to bcore-sp-007p.
+ *   Channel Allocation
+ */
+#define BTH5_CHANNEL_HCI_CMD   1       /* HCI Command and Event */
+#define BTH5_CHANNEL_HCI_EVT   4       /* HCI Command and Event */
+#define BTH5_CHANNEL_HCI_ACL   2       /* HCI ACL data */
+#define BTH5_CHANNEL_HCI_SCO   3       /* HCI SCO data */
+#define BTH5_CHANNEL_LE                15      /* Link Establishment */
+
+
+/*
+ *   Link Establishment Protocol
+ */
+typedef enum {
+       le_state_shy,
+       le_state_curious,
+       le_state_garrulous
+} bth5_le_state_t;
+
+#define BTH5_LE_SYNC           { 0x01, 0x7e };
+#define BTH5_LE_SYNCRESP       { 0x02, 0x7d };
+#define BTH5_LE_CONF           { 0x03, 0xfc, 0x0f };
+#define BTH5_LE_CONFRESP       { 0x04, 0x7b, 0x0f };
+
+#define BTH5_LE_TSHY_TIMEOUT   hz      /* XXXX: 1sec ? */
+#define BTH5_LE_TCONF_TIMEOUT  hz      /* XXXX: 1sec ? */
+
+#endif /* !_DEV_BLUETOOTH_BTH5_H_ */
--- /dev/null   2017-08-10 18:51:00.000000000 +1000
+++ src/sys/dev/bluetooth/bth5.c        2017-08-10 18:49:56.000000000 +1000
@@ -0,0 +1,1830 @@
+/*     $NetBSD$        */
+/*
+ * Copyright (c) 2017 Nathanial Sloss <[email protected]>
+ * All rights reserved.
+ *
+ * Copyright (c) 2007 KIYOHARA Takashi
+ * 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 ``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 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>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/kauth.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/syslimits.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+#include <dev/bluetooth/bth5.h>
+
+#include "ioconf.h"
+
+#ifdef BTH5_DEBUG
+#ifdef DPRINTF
+#undef DPRINTF
+#endif
+#ifdef DPRINTFN
+#undef DPRINTFN
+#endif
+
+#define DPRINTF(x)     printf x
+#define DPRINTFN(n, x) do { if (bth5_debug > (n)) printf x; } while (0)
+int bth5_debug = 3;
+#else
+#undef DPRINTF
+#undef DPRINTFN
+
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+struct bth5_softc {
+       device_t sc_dev;
+
+       struct tty *sc_tp;
+       struct hci_unit *sc_unit;               /* Bluetooth HCI Unit */
+       struct bt_stats sc_stats;
+
+       int sc_flags;
+
+       /* output queues */
+       MBUFQ_HEAD()    sc_cmdq;
+       MBUFQ_HEAD()    sc_aclq;
+       MBUFQ_HEAD()    sc_scoq;
+
+       int sc_baud;
+       int sc_init_baud;
+
+       /* variables of SLIP Layer */
+       struct mbuf *sc_txp;                    /* outgoing packet */
+       struct mbuf *sc_rxp;                    /* incoming packet */
+       int sc_slip_txrsv;                      /* reserved byte data */
+       int sc_slip_rxexp;                      /* expected byte data */
+       void (*sc_transmit_callback)(struct bth5_softc *, struct mbuf *);
+
+       /* variables of Packet Integrity Layer */
+       int sc_pi_txcrc;                        /* use CRC, if true */
+
+       /* variables of MUX Layer */
+       bool sc_mux_send_ack;                   /* flag for send_ack */
+       bool sc_mux_choke;                      /* Choke signal */
+       struct timeval sc_mux_lastrx;           /* Last Rx Pkt Time */
+
+       /* variables of Sequencing Layer */
+       MBUFQ_HEAD() sc_seqq;                   /* Sequencing Layer queue */
+       MBUFQ_HEAD() sc_seq_retryq;             /* retry queue */
+       uint32_t sc_seq_txseq;
+       uint32_t sc_seq_txack;
+       uint32_t sc_seq_expected_rxseq;
+       uint32_t sc_seq_winspace;
+       uint32_t sc_seq_retries;
+       callout_t sc_seq_timer;
+       uint32_t sc_seq_timeout;
+       uint32_t sc_seq_winsize;
+       uint32_t sc_seq_retry_limit;
+
+       /* variables of Datagram Queue Layer */
+       MBUFQ_HEAD() sc_dgq;                    /* Datagram Queue Layer queue */
+
+       /* variables of BTH5 Link Establishment Protocol */
+       bool sc_le_muzzled;
+       bth5_le_state_t sc_le_state;
+       callout_t sc_le_timer;
+
+       struct sysctllog *sc_log;               /* sysctl log */
+};
+
+/* sc_flags */
+#define        BTH5_XMIT       (1 << 0)        /* transmit active */
+#define        BTH5_ENABLED    (1 << 1)        /* is enabled */
+
+static int bthfive_match(device_t, cfdata_t, void *);
+static void bthfive_attach(device_t, device_t, void *);
+static int bthfive_detach(device_t, int);
+
+/* tty functions */
+static int bth5open(dev_t, struct tty *);
+static int bth5close(struct tty *, int);
+static int bth5ioctl(struct tty *, u_long, void *, int, struct lwp *);
+
+static int bth5_slip_transmit(struct tty *);
+static int bth5_slip_receive(int, struct tty *);
+
+static void bth5_pktintegrity_transmit(struct bth5_softc *);
+static void bth5_pktintegrity_receive(struct bth5_softc *, struct mbuf *);
+static void bth5_crc_update(uint16_t *, uint8_t);
+static uint16_t bth5_crc_reverse(uint16_t);
+
+static void bth5_mux_transmit(struct bth5_softc *sc);
+static void bth5_mux_receive(struct bth5_softc *sc, struct mbuf *m);
+static __inline void bth5_send_ack_command(struct bth5_softc *sc);
+static __inline struct mbuf *bth5_create_ackpkt(void);
+static __inline void bth5_set_choke(struct bth5_softc *, bool);
+
+static void bth5_sequencing_receive(struct bth5_softc *, struct mbuf *);
+static bool bth5_tx_reliable_pkt(struct bth5_softc *, struct mbuf *, u_int);
+static __inline u_int bth5_get_txack(struct bth5_softc *);
+static void bth5_signal_rxack(struct bth5_softc *, uint32_t);
+static void bth5_reliabletx_callback(struct bth5_softc *, struct mbuf *);
+static void bth5_timer_timeout(void *);
+static void bth5_sequencing_reset(struct bth5_softc *);
+
+static void bth5_datagramq_receive(struct bth5_softc *, struct mbuf *);
+static bool bth5_tx_unreliable_pkt(struct bth5_softc *, struct mbuf *, u_int);
+static void bth5_unreliabletx_callback(struct bth5_softc *, struct mbuf *);
+
+static int bth5_start_le(struct bth5_softc *);
+static void bth5_terminate_le(struct bth5_softc *);
+static void bth5_input_le(struct bth5_softc *, struct mbuf *);
+static void bth5_le_timeout(void *);
+
+static void bth5_start(struct bth5_softc *);
+
+/* bluetooth hci functions */
+static int bth5_enable(device_t);
+static void bth5_disable(device_t);
+static void bth5_output_cmd(device_t, struct mbuf *);
+static void bth5_output_acl(device_t, struct mbuf *);
+static void bth5_output_sco(device_t, struct mbuf *);
+static void bth5_stats(device_t, struct bt_stats *, int);
+
+#ifdef BTH5_DEBUG
+static void bth5_packet_print(struct mbuf *m);
+#endif
+
+
+/*
+ * It doesn't need to be exported, as only bth5attach() uses it,
+ * but there's no "official" way to make it static.
+ */
+CFATTACH_DECL_NEW(bthfive, sizeof(struct bth5_softc),
+    bthfive_match, bthfive_attach, bthfive_detach, NULL);
+
+static struct linesw bth5_disc = {
+       .l_name = "bth5",
+       .l_open = bth5open,
+       .l_close = bth5close,
+       .l_read = ttyerrio,
+       .l_write = ttyerrio,
+       .l_ioctl = bth5ioctl,
+       .l_rint = bth5_slip_receive,
+       .l_start = bth5_slip_transmit,
+       .l_modem = ttymodem,
+       .l_poll = ttyerrpoll
+};
+
+static const struct hci_if bth5_hci = {
+       .enable = bth5_enable,
+       .disable = bth5_disable,
+       .output_cmd = bth5_output_cmd,
+       .output_acl = bth5_output_acl,
+       .output_sco = bth5_output_sco,
+       .get_stats = bth5_stats,
+       .ipl = IPL_TTY,
+};
+
+/* ARGSUSED */
+void
+bthfiveattach(int num __unused)
+{
+       int error;
+
+       error = ttyldisc_attach(&bth5_disc);
+       if (error) {
+               aprint_error("%s: unable to register line discipline, "
+                   "error = %d\n", bthfive_cd.cd_name, error);
+               return;
+       }
+
+       error = config_cfattach_attach(bthfive_cd.cd_name, &bthfive_ca);
+       if (error) {
+               aprint_error("%s: unable to register cfattach, error = %d\n",
+                   bthfive_cd.cd_name, error);
+               config_cfdriver_detach(&bthfive_cd);
+               (void) ttyldisc_detach(&bth5_disc);
+       }
+}
+
+/*
+ * Autoconf match routine.
+ *
+ * XXX: unused: config_attach_pseudo(9) does not call ca_match.
+ */
+/* ARGSUSED */
+static int
+bthfive_match(device_t self __unused, cfdata_t cfdata __unused,
+          void *arg __unused)
+{
+
+       /* pseudo-device; always present */
+       return 1;
+}
+
+/*
+ * Autoconf attach routine.  Called by config_attach_pseudo(9) when we
+ * open the line discipline.
+ */
+/* ARGSUSED */
+static void
+bthfive_attach(device_t parent __unused, device_t self, void *aux __unused)
+{
+       struct bth5_softc *sc = device_private(self);
+       const struct sysctlnode *node;
+       int rc, bth5_node_num;
+
+       aprint_normal("\n");
+       aprint_naive("\n");
+
+       sc->sc_dev = self;
+       callout_init(&sc->sc_seq_timer, 0);
+       callout_setfunc(&sc->sc_seq_timer, bth5_timer_timeout, sc);
+       callout_init(&sc->sc_le_timer, 0);
+       callout_setfunc(&sc->sc_le_timer, bth5_le_timeout, sc);
+       sc->sc_seq_timeout = BTH5_SEQ_TX_TIMEOUT;
+       sc->sc_seq_winsize = BTH5_SEQ_TX_WINSIZE;
+       sc->sc_seq_retry_limit = BTH5_SEQ_TX_RETRY_LIMIT;
+       MBUFQ_INIT(&sc->sc_seqq);
+       MBUFQ_INIT(&sc->sc_seq_retryq);
+       MBUFQ_INIT(&sc->sc_dgq);
+       MBUFQ_INIT(&sc->sc_cmdq);
+       MBUFQ_INIT(&sc->sc_aclq);
+       MBUFQ_INIT(&sc->sc_scoq);
+
+       /* Attach Bluetooth unit */
+       sc->sc_unit = hci_attach_pcb(&bth5_hci, self, 0);
+
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           0, CTLTYPE_NODE, device_xname(self),
+           SYSCTL_DESCR("bth5 controls"),
+           NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       bth5_node_num = node->sysctl_num;
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           CTLFLAG_READWRITE, CTLTYPE_BOOL,
+           "muzzled", SYSCTL_DESCR("muzzled for Link-establishment Layer"),
+           NULL, 0, &sc->sc_le_muzzled,
+           0, CTL_HW, bth5_node_num, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           CTLFLAG_READWRITE, CTLTYPE_INT,
+           "txcrc", SYSCTL_DESCR("txcrc for Packet Integrity Layer"),
+           NULL, 0, &sc->sc_pi_txcrc,
+           0, CTL_HW, bth5_node_num, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           CTLFLAG_READWRITE, CTLTYPE_INT,
+           "timeout", SYSCTL_DESCR("timeout for Sequencing Layer"),
+           NULL, 0, &sc->sc_seq_timeout,
+           0, CTL_HW, bth5_node_num, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           CTLFLAG_READWRITE, CTLTYPE_INT,
+           "winsize", SYSCTL_DESCR("winsize for Sequencing Layer"),
+           NULL, 0, &sc->sc_seq_winsize,
+           0, CTL_HW, bth5_node_num, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+           CTLFLAG_READWRITE, CTLTYPE_INT,
+           "retry_limit", SYSCTL_DESCR("retry limit for Sequencing Layer"),
+           NULL, 0, &sc->sc_seq_retry_limit,
+           0, CTL_HW, bth5_node_num, CTL_CREATE, CTL_EOL)) != 0) {
+               goto err;
+       }
+       return;
+
+err:
+       aprint_error_dev(self, "sysctl_createv failed (rc = %d)\n", rc);
+}
+
+/*
+ * Autoconf detach routine.  Called when we close the line discipline.
+ */
+/* ARGSUSED */
+static int
+bthfive_detach(device_t self, int flags __unused)
+{
+       struct bth5_softc *sc = device_private(self);
+
+       if (sc->sc_unit != NULL) {
+               hci_detach_pcb(sc->sc_unit);
+               sc->sc_unit = NULL;
+       }
+
+       callout_halt(&sc->sc_seq_timer, NULL);
+       callout_destroy(&sc->sc_seq_timer);
+
+       callout_halt(&sc->sc_le_timer, NULL);
+       callout_destroy(&sc->sc_le_timer);
+
+       return 0;
+}
+
+
+/*
+ * Line discipline functions.
+ */
+/* ARGSUSED */
+static int
+bth5open(dev_t device __unused, struct tty *tp)
+{
+       struct bth5_softc *sc;
+       device_t dev;
+       cfdata_t cfdata;
+       struct lwp *l = curlwp;         /* XXX */
+       int error, unit, s;
+       static char name[] = "bthfive";
+
+       error = kauth_authorize_device(l->l_cred, KAUTH_DEVICE_BLUETOOTH_BCSP,
+           KAUTH_ARG(KAUTH_REQ_DEVICE_BLUETOOTH_BCSP_ADD), NULL, NULL, NULL);
+       if (error)
+               return (error);
+
+       s = spltty();
+
+       if (tp->t_linesw == &bth5_disc) {
+               sc = tp->t_sc;
+               if (sc != NULL) {
+                       splx(s);
+                       return EBUSY;
+               }
+       }
+
+       KASSERT(tp->t_oproc != NULL);
+
+       cfdata = malloc(sizeof(struct cfdata), M_DEVBUF, M_WAITOK);
+       for (unit = 0; unit < bthfive_cd.cd_ndevs; unit++)
+               if (device_lookup(&bthfive_cd, unit) == NULL)
+                       break;
+       cfdata->cf_name = name;
+       cfdata->cf_atname = name;
+       cfdata->cf_unit = unit;
+       cfdata->cf_fstate = FSTATE_STAR;
+
+       aprint_normal("%s%d at tty major %llu minor %llu",
+           name, unit, (unsigned long long)major(tp->t_dev),
+           (unsigned long long)minor(tp->t_dev));
+       dev = config_attach_pseudo(cfdata);
+       if (dev == NULL) {
+               splx(s);
+               return EIO;
+       }
+       sc = device_private(dev);
+
+       mutex_spin_enter(&tty_lock);
+       tp->t_sc = sc;
+       sc->sc_tp = tp;
+       ttyflush(tp, FREAD | FWRITE);
+       mutex_spin_exit(&tty_lock);
+
+       splx(s);
+
+       sc->sc_slip_txrsv = BTH5_SLIP_PKTSTART;
+       bth5_sequencing_reset(sc);
+
+       /* start link-establishment */
+       bth5_start_le(sc);
+
+       return 0;
+}
+
+/* ARGSUSED */
+static int
+bth5close(struct tty *tp, int flag __unused)
+{
+       struct bth5_softc *sc = tp->t_sc;
+       cfdata_t cfdata;
+       int s;
+
+       /* terminate link-establishment */
+       bth5_terminate_le(sc);
+
+       s = spltty();
+
+       MBUFQ_DRAIN(&sc->sc_dgq);
+       bth5_sequencing_reset(sc);
+
+       mutex_spin_enter(&tty_lock);
+       ttyflush(tp, FREAD | FWRITE);
+       mutex_spin_exit(&tty_lock);     /* XXX */
+       ttyldisc_release(tp->t_linesw);
+       tp->t_linesw = ttyldisc_default();
+       if (sc != NULL) {
+               tp->t_sc = NULL;
+               if (sc->sc_tp == tp) {
+                       cfdata = device_cfdata(sc->sc_dev);
+                       config_detach(sc->sc_dev, 0);
+                       free(cfdata, M_DEVBUF);
+               }
+
+       }
+       splx(s);
+       return 0;
+}
+
+/* ARGSUSED */
+static int
+bth5ioctl(struct tty *tp, u_long cmd, void *data, int flag __unused,
+         struct lwp *l __unused)
+{
+       struct bth5_softc *sc = tp->t_sc;
+       int error;
+
+       if (sc == NULL || tp != sc->sc_tp)
+               return EPASSTHROUGH;
+
+       error = 0;
+       switch (cmd) {
+       default:
+               error = EPASSTHROUGH;
+               break;
+       }
+
+       return error;
+}
+
+
+/*
+ * UART Driver Layer is supported by com-driver.
+ */
+
+/*
+ * BTH5 SLIP Layer functions:
+ *   Supports to transmit/receive a byte stream.
+ *   SLIP protocol described in Internet standard RFC 1055.
+ */
+static int
+bth5_slip_transmit(struct tty *tp)
+{
+       struct bth5_softc *sc = tp->t_sc;
+       struct mbuf *m;
+       int count, rlen;
+       uint8_t *rptr;
+
+       m = sc->sc_txp;
+       if (m == NULL) {
+               sc->sc_flags &= ~BTH5_XMIT;
+               bth5_mux_transmit(sc);
+               return 0;
+       }
+
+       count = 0;
+       rlen = 0;
+       rptr = mtod(m, uint8_t *);
+
+       if (sc->sc_slip_txrsv != 0) {
+#ifdef BTH5_DEBUG
+               if (sc->sc_slip_txrsv == BTH5_SLIP_PKTSTART)
+                       DPRINTFN(4, ("%s: slip transmit start\n",
+                           device_xname(sc->sc_dev)));
+               else
+                       DPRINTFN(4, ("0x%02x ", sc->sc_slip_txrsv));
+#endif
+
+               if (putc(sc->sc_slip_txrsv, &tp->t_outq) < 0)
+                       return 0;
+               count++;
+
+               if (sc->sc_slip_txrsv == BTH5_SLIP_ESCAPE_PKTEND ||
+                   sc->sc_slip_txrsv == BTH5_SLIP_ESCAPE_XON ||
+                   sc->sc_slip_txrsv == BTH5_SLIP_ESCAPE_XOFF ||
+                   sc->sc_slip_txrsv == BTH5_SLIP_ESCAPE_ESCAPE) {
+                       rlen++;
+                       rptr++;
+               }
+               sc->sc_slip_txrsv = 0;
+       }
+
+       for(;;) {
+               if (rlen >= m->m_len) {
+                       m = m->m_next;
+                       if (m == NULL) {
+                               if (putc(BTH5_SLIP_PKTEND, &tp->t_outq) < 0)
+                                       break;
+
+                               DPRINTFN(4, ("\n%s: slip transmit end\n",
+                                   device_xname(sc->sc_dev)));
+
+                               m = sc->sc_txp;
+                               sc->sc_txp = NULL;
+                               sc->sc_slip_txrsv = BTH5_SLIP_PKTSTART;
+
+                               sc->sc_transmit_callback(sc, m);
+                               m = NULL;
+                               break;
+                       }
+
+                       rlen = 0;
+                       rptr = mtod(m, uint8_t *);
+                       continue;
+               }
+
+               if (*rptr == BTH5_SLIP_PKTEND) {
+                       if (putc(BTH5_SLIP_ESCAPE, &tp->t_outq) < 0)
+                               break;
+                       count++;
+                       DPRINTFN(4, (" esc "));
+
+                       if (putc(BTH5_SLIP_ESCAPE_PKTEND, &tp->t_outq) < 0) {
+                               sc->sc_slip_txrsv = BTH5_SLIP_ESCAPE_PKTEND;
+                               break;
+                       }
+                       DPRINTFN(4, ("0x%02x ", BTH5_SLIP_ESCAPE_PKTEND));
+                       rptr++;
+               } else if (*rptr == BTH5_SLIP_XON) {
+                       if (putc(BTH5_SLIP_ESCAPE, &tp->t_outq) < 0)
+                               break;
+                       count++;
+                       DPRINTFN(4, (" esc "));
+
+                       if (putc(BTH5_SLIP_ESCAPE_XON, &tp->t_outq) < 0) {
+                               sc->sc_slip_txrsv = BTH5_SLIP_ESCAPE_XON;
+                               break;
+                       }
+                       DPRINTFN(4, ("0x%02x ", BTH5_SLIP_ESCAPE_XON));
+                       rptr++;
+               } else if (*rptr == BTH5_SLIP_XOFF) {
+                       if (putc(BTH5_SLIP_ESCAPE, &tp->t_outq) < 0)
+                               break;
+                       count++;
+                       DPRINTFN(4, (" esc "));
+
+                       if (putc(BTH5_SLIP_ESCAPE_XOFF, &tp->t_outq) < 0) {
+                               sc->sc_slip_txrsv = BTH5_SLIP_ESCAPE_XOFF;
+                               break;
+                       }
+                       DPRINTFN(4, ("0x%02x ", BTH5_SLIP_ESCAPE_XOFF));
+                       rptr++;
+               } else if (*rptr == BTH5_SLIP_ESCAPE) {
+                       if (putc(BTH5_SLIP_ESCAPE, &tp->t_outq) < 0)
+                               break;
+                       count++;
+                       DPRINTFN(4, (" esc "));
+
+                       if (putc(BTH5_SLIP_ESCAPE_ESCAPE, &tp->t_outq) < 0) {
+                               sc->sc_slip_txrsv = BTH5_SLIP_ESCAPE_ESCAPE;
+                               break;
+                       }
+                       DPRINTFN(4, ("0x%02x ", BTH5_SLIP_ESCAPE_ESCAPE));
+                       rptr++;
+               } else {
+                       if (putc(*rptr++, &tp->t_outq) < 0)
+                               break;
+                       DPRINTFN(4, ("0x%02x ", *(rptr - 1)));
+               }
+               rlen++;
+               count++;
+       }
+       if (m != NULL)
+               m_adj(m, rlen);
+
+       sc->sc_stats.byte_tx += count;
+
+       if (tp->t_outq.c_cc != 0)
+               (*tp->t_oproc)(tp);
+
+       return 0;
+}
+
+static int
+bth5_slip_receive(int c, struct tty *tp)
+{
+       struct bth5_softc *sc = tp->t_sc;
+       struct mbuf *m = sc->sc_rxp;
+       int discard = 0;
+       const char *errstr;
+
+       c &= TTY_CHARMASK;
+
+       /* If we already started a packet, find the trailing end of it. */
+       if (m) {
+               while (m->m_next)
+                       m = m->m_next;
+
+               if (M_TRAILINGSPACE(m) == 0) {
+                       /* extend mbuf */
+                       MGET(m->m_next, M_DONTWAIT, MT_DATA);
+                       if (m->m_next == NULL) {
+                               aprint_error_dev(sc->sc_dev,
+                                   "out of memory\n");
+                               sc->sc_stats.err_rx++;
+                               return 0;       /* (lost sync) */
+                       }
+
+                       m = m->m_next;
+                       m->m_len = 0;
+               }
+       } else
+               if (c != BTH5_SLIP_PKTSTART) {
+                       discard = 1;
+                       errstr = "not sync";
+                       goto discarded;
+               }
+
+       switch (c) {
+       case BTH5_SLIP_PKTSTART /* or _PKTEND */:
+               if (m == NULL) {
+                       /* BTH5_SLIP_PKTSTART */
+
+                       DPRINTFN(4, ("%s: slip receive start\n",
+                           device_xname(sc->sc_dev)));
+
+                       /* new packet */
+                       MGETHDR(m, M_DONTWAIT, MT_DATA);
+                       if (m == NULL) {
+                               aprint_error_dev(sc->sc_dev,
+                                   "out of memory\n");
+                               sc->sc_stats.err_rx++;
+                               return 0;       /* (lost sync) */
+                       }
+
+                       sc->sc_rxp = m;
+                       m->m_pkthdr.len = m->m_len = 0;
+                       sc->sc_slip_rxexp = 0;
+               } else {
+                       /* BTH5_SLIP_PKTEND */
+
+                       if (m == sc->sc_rxp && m->m_len == 0) {
+                               DPRINTFN(4, ("%s: resynchronises\n",
+                                   device_xname(sc->sc_dev)));
+
+                               sc->sc_stats.byte_rx++;
+                               return 0;
+                       }
+
+                       DPRINTFN(4, ("%s%s: slip receive end\n",
+                           (m->m_len % 16 != 0) ? "\n" :  "",
+                           device_xname(sc->sc_dev)));
+
+                       bth5_pktintegrity_receive(sc, sc->sc_rxp);
+                       sc->sc_rxp = NULL;
+                       sc->sc_slip_rxexp = BTH5_SLIP_PKTSTART;
+               }
+               sc->sc_stats.byte_rx++;
+               return 0;
+
+       case BTH5_SLIP_ESCAPE:
+
+               DPRINTFN(4, ("  esc"));
+
+               if (sc->sc_slip_rxexp == BTH5_SLIP_ESCAPE) {
+                       discard = 1;
+                       errstr = "waiting 0xdc or 0xdb or 0xde of 0xdf";
+               } else
+                       sc->sc_slip_rxexp = BTH5_SLIP_ESCAPE;
+               break;
+
+       default:
+               DPRINTFN(4, (" 0x%02x%s",
+                   c, (m->m_len % 16 == 15) ? "\n" :  ""));
+
+               switch (sc->sc_slip_rxexp) {
+               case BTH5_SLIP_PKTSTART:
+                       discard = 1;
+                       errstr = "waiting 0xc0";
+                       break;
+
+               case BTH5_SLIP_ESCAPE:
+                       if (c == BTH5_SLIP_ESCAPE_PKTEND)
+                               mtod(m, uint8_t *)[m->m_len++] =
+                                   BTH5_SLIP_PKTEND;
+                       else if (c == BTH5_SLIP_ESCAPE_XON)
+                               mtod(m, uint8_t *)[m->m_len++] =
+                                   BTH5_SLIP_XON;
+                       else if (c == BTH5_SLIP_ESCAPE_XOFF)
+                               mtod(m, uint8_t *)[m->m_len++] =
+                                   BTH5_SLIP_XOFF;
+                       else if (c == BTH5_SLIP_ESCAPE_ESCAPE)
+                               mtod(m, uint8_t *)[m->m_len++] =
+                                   BTH5_SLIP_ESCAPE;
+                       else {
+                               discard = 1;
+                               errstr = "unknown escape";
+                       }
+                       sc->sc_slip_rxexp = 0;
+                       break;
+
+               default:
+                       mtod(m, uint8_t *)[m->m_len++] = c;
+               }
+               sc->sc_rxp->m_pkthdr.len++;
+       }
+       if (discard) {
+discarded:
+#ifdef BTH5_DEBUG
+               DPRINTFN(4, ("%s: receives unexpected byte 0x%02x: %s\n",
+                   device_xname(sc->sc_dev), c, errstr));
+#else
+               __USE(errstr);
+#endif
+       }
+       sc->sc_stats.byte_rx++;
+
+       return 0;
+}
+
+
+/*
+ * BTH5 Packet Integrity Layer functions:
+ *   handling Payload Length, Checksum, CRC.
+ */
+static void
+bth5_pktintegrity_transmit(struct bth5_softc *sc)
+{
+       struct mbuf *m = sc->sc_txp;
+       bth5_hdr_t *hdrp = mtod(m, bth5_hdr_t *);
+       int pldlen;
+
+       DPRINTFN(3, ("%s: pi transmit\n", device_xname(sc->sc_dev)));
+
+       pldlen = m->m_pkthdr.len - sizeof(bth5_hdr_t);
+
+       if (sc->sc_pi_txcrc)
+               hdrp->flags |= BTH5_FLAGS_CRC_PRESENT;
+
+       BTH5_SET_PLEN(hdrp, pldlen);
+       BTH5_SET_CSUM(hdrp);
+
+       if (sc->sc_pi_txcrc) {
+               struct mbuf *_m;
+               int n = 0;
+               uint16_t crc = 0xffff;
+               uint8_t *buf;
+
+               for (_m = m; _m != NULL; _m = _m->m_next) {
+                       buf = mtod(_m, uint8_t *);
+                       for (n = 0; n < _m->m_len; n++)
+                               bth5_crc_update(&crc, *(buf + n));
+               }
+               crc = htobe16(bth5_crc_reverse(crc));
+               m_copyback(m, m->m_pkthdr.len, sizeof(crc), &crc);
+       }
+
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 3)
+               bth5_packet_print(m);
+#endif
+
+       bth5_slip_transmit(sc->sc_tp);
+}
+
+static void
+bth5_pktintegrity_receive(struct bth5_softc *sc, struct mbuf *m)
+{
+       bth5_hdr_t *hdrp = mtod(m, bth5_hdr_t *);
+       u_int pldlen;
+       int discard = 0;
+       uint16_t crc = 0xffff;
+       const char *errstr;
+
+       DPRINTFN(3, ("%s: pi receive\n", device_xname(sc->sc_dev)));
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 4)
+               bth5_packet_print(m);
+#endif
+
+       KASSERT(m->m_len >= sizeof(bth5_hdr_t));
+
+       pldlen = m->m_pkthdr.len - sizeof(bth5_hdr_t) -
+           ((hdrp->flags & BTH5_FLAGS_CRC_PRESENT) ? sizeof(crc) : 0);
+       if (pldlen > 0xfff) {
+               discard = 1;
+               errstr = "Payload Length";
+               goto discarded;
+       }
+       if (hdrp->csum != BTH5_GET_CSUM(hdrp)) {
+               discard = 1;
+               errstr = "Checksum";
+               goto discarded;
+       }
+       if (BTH5_GET_PLEN(hdrp) != pldlen) {
+               discard = 1;
+               errstr = "Payload Length";
+               goto discarded;
+       }
+       if (hdrp->flags & BTH5_FLAGS_CRC_PRESENT) {
+               struct mbuf *_m;
+               int i, n;
+               uint16_t crc0;
+               uint8_t *buf;
+
+               i = 0;
+               n = 0;
+               for (_m = m; _m != NULL; _m = _m->m_next) {
+                       buf = mtod(m, uint8_t *);
+                       for (n = 0;
+                           n < _m->m_len && i < sizeof(bth5_hdr_t) + pldlen;
+                           n++, i++)
+                               bth5_crc_update(&crc, *(buf + n));
+               }
+
+               m_copydata(_m, n, sizeof(crc0), &crc0);
+               if (be16toh(crc0) != bth5_crc_reverse(crc)) {
+                       discard = 1;
+                       errstr = "CRC";
+               } else
+                       /* Shaves CRC */
+                       m_adj(m, (int)(0 - sizeof(crc)));
+       }
+
+       if (discard) {
+discarded:
+#ifdef BTH5_DEBUG
+               DPRINTFN(3, ("%s: receives unexpected packet: %s\n",
+                   device_xname(sc->sc_dev), errstr));
+#else
+               __USE(errstr);
+#endif
+               m_freem(m);
+       } else
+               bth5_mux_receive(sc, m);
+}
+
+static const uint16_t crctbl[] = {
+       0x0000, 0x1081, 0x2102, 0x3183,
+       0x4204, 0x5285, 0x6306, 0x7387,
+       0x8408, 0x9489, 0xa50a, 0xb58b,
+       0xc60c, 0xd68d, 0xe70e, 0xf78f,
+};
+
+static void
+bth5_crc_update(uint16_t *crc, uint8_t d)
+{
+       uint16_t reg = *crc;
+
+       reg = (reg >> 4) ^ crctbl[(reg ^ d) & 0x000f];
+       reg = (reg >> 4) ^ crctbl[(reg ^ (d >> 4)) & 0x000f];
+
+       *crc = reg;
+}
+
+static uint16_t
+bth5_crc_reverse(uint16_t crc)
+{
+       uint16_t b, rev;
+
+       for (b = 0, rev = 0; b < 16; b++) {
+               rev = rev << 1;
+               rev |= (crc & 1);
+               crc = crc >> 1;
+       }
+
+       return rev;
+}
+
+
+/*
+ * BTH5 MUX Layer functions
+ */
+static void
+bth5_mux_transmit(struct bth5_softc *sc)
+{
+       struct mbuf *m;
+       bth5_hdr_t *hdrp;
+
+       DPRINTFN(2, ("%s: mux transmit: sc_flags=0x%x, choke=%d",
+           device_xname(sc->sc_dev), sc->sc_flags, sc->sc_mux_choke));
+
+       if (sc->sc_mux_choke) {
+               struct mbuf *_m = NULL;
+
+               /* In this case, send only Link Establishment packet */
+               for (m = MBUFQ_FIRST(&sc->sc_dgq); m != NULL;
+                   _m = m, m = MBUFQ_NEXT(m)) {
+                       hdrp = mtod(m, bth5_hdr_t *);
+                       if (hdrp->ident == BTH5_CHANNEL_LE) {
+                               if (m == MBUFQ_FIRST(&sc->sc_dgq))
+                                       MBUFQ_DEQUEUE(&sc->sc_dgq, m);
+                               else {
+                                       if (m->m_nextpkt == NULL)
+                                               sc->sc_dgq.mq_last =
+                                                   &_m->m_nextpkt;
+                                       _m->m_nextpkt = m->m_nextpkt;
+                                       m->m_nextpkt = NULL;
+                               }
+                               goto transmit;
+                       }
+               }
+               DPRINTFN(2, ("\n"));
+               return;
+       }
+
+       /*
+        * The MUX Layer always gives priority to packets from the Datagram
+        * Queue Layer over the Sequencing Layer.
+        */
+       if (MBUFQ_FIRST(&sc->sc_dgq)) {
+               MBUFQ_DEQUEUE(&sc->sc_dgq, m);
+               goto transmit;
+       }
+       if (MBUFQ_FIRST(&sc->sc_seqq)) {
+               MBUFQ_DEQUEUE(&sc->sc_seqq, m);
+               hdrp = mtod(m, bth5_hdr_t *);
+               hdrp->flags |= BTH5_FLAGS_PROTOCOL_REL;         /* Reliable */
+               goto transmit;
+       }
+       bth5_start(sc);
+       if (sc->sc_mux_send_ack == true) {
+               m = bth5_create_ackpkt();
+               if (m != NULL)
+                       goto transmit;
+               aprint_error_dev(sc->sc_dev, "out of memory\n");
+               sc->sc_stats.err_tx++;
+       }
+
+       /* Nothing to send */
+       DPRINTFN(2, ("\n"));
+       return;
+
+transmit:
+       DPRINTFN(2, (", txack=%d, send_ack=%d\n",
+           bth5_get_txack(sc), sc->sc_mux_send_ack));
+
+       hdrp = mtod(m, bth5_hdr_t *);
+       hdrp->flags |=
+           (bth5_get_txack(sc) << BTH5_FLAGS_ACK_SHIFT) & BTH5_FLAGS_ACK_MASK;
+       if (sc->sc_mux_send_ack == true)
+               sc->sc_mux_send_ack = false;
+
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 3)
+               bth5_packet_print(m);
+#endif
+
+       sc->sc_txp = m;
+       bth5_pktintegrity_transmit(sc);
+}
+
+static void
+bth5_mux_receive(struct bth5_softc *sc, struct mbuf *m)
+{
+       bth5_hdr_t *hdrp = mtod(m, bth5_hdr_t *);
+       const u_int rxack = BTH5_FLAGS_ACK(hdrp->flags);
+
+       DPRINTFN(2, ("%s: mux receive: flags=0x%x, ident=%d, rxack=%d\n",
+           device_xname(sc->sc_dev), hdrp->flags, hdrp->ident, rxack));
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 3)
+               bth5_packet_print(m);
+#endif
+
+       bth5_signal_rxack(sc, rxack);
+
+       microtime(&sc->sc_mux_lastrx);
+
+       /* if the Ack Packet received then discard */
+       if (BTH5_FLAGS_SEQ(hdrp->flags) == 0 &&
+           hdrp->ident == BTH5_IDENT_ACKPKT &&
+           BTH5_GET_PLEN(hdrp) == 0) {
+               m_freem(m);
+               return;
+       }
+
+       if (hdrp->flags & BTH5_FLAGS_PROTOCOL_REL)
+               bth5_sequencing_receive(sc, m);
+       else
+               bth5_datagramq_receive(sc, m);
+}
+
+static __inline void
+bth5_send_ack_command(struct bth5_softc *sc)
+{
+
+       DPRINTFN(2, ("%s: mux send_ack_command\n", device_xname(sc->sc_dev)));
+
+       sc->sc_mux_send_ack = true;
+}
+
+static __inline struct mbuf *
+bth5_create_ackpkt(void)
+{
+       struct mbuf *m;
+       bth5_hdr_t *hdrp;
+
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m != NULL) {
+               m->m_pkthdr.len = m->m_len = sizeof(bth5_hdr_t);
+               hdrp = mtod(m, bth5_hdr_t *);
+               /*
+                * An Ack Packet has the following fields:
+                *      Ack Field:                      txack (not set yet)
+                *      Seq Field:                      0
+                *      Protocol Identifier Field:      0
+                *      Protocol Type Field:            Any value
+                *      Payload Length Field:           0
+                */
+               memset(hdrp, 0, sizeof(bth5_hdr_t));
+       }
+       return m;
+}
+
+static __inline void
+bth5_set_choke(struct bth5_softc *sc, bool choke)
+{
+
+       DPRINTFN(2, ("%s: mux set choke=%d\n", device_xname(sc->sc_dev), choke));
+
+       sc->sc_mux_choke = choke;
+}
+
+
+/*
+ * BTH5 Sequencing Layer functions
+ */
+static void
+bth5_sequencing_receive(struct bth5_softc *sc, struct mbuf *m)
+{
+       bth5_hdr_t hdr;
+       uint32_t rxseq;
+
+       m_copydata(m, 0, sizeof(bth5_hdr_t), &hdr);
+       rxseq = BTH5_FLAGS_SEQ(hdr.flags);
+
+       DPRINTFN(1, ("%s: seq receive: rxseq=%d, expected %d\n",
+           device_xname(sc->sc_dev), rxseq, sc->sc_seq_expected_rxseq));
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 2)
+               bth5_packet_print(m);
+#endif
+
+       /*
+        * We remove the header of BTH5 and add the 'uint8_t type' of
+        * hci_*_hdr_t to the head.
+        */
+       m_adj(m, sizeof(bth5_hdr_t) - sizeof(uint8_t));
+
+       if (rxseq != sc->sc_seq_expected_rxseq) {
+               m_freem(m);
+
+               /* send ack packet, if needly */
+               bth5_mux_transmit(sc);
+
+               return;
+       }
+
+       switch (hdr.ident) {
+       case BTH5_CHANNEL_HCI_CMD:
+               *(mtod(m, uint8_t *)) = HCI_CMD_PKT;
+               if (!hci_input_event(sc->sc_unit, m))
+                       sc->sc_stats.err_rx++;
+
+               sc->sc_stats.evt_rx++;
+               break;
+
+       case BTH5_CHANNEL_HCI_EVT:
+               *(mtod(m, uint8_t *)) = HCI_EVENT_PKT;
+               if (!hci_input_event(sc->sc_unit, m))
+                       sc->sc_stats.err_rx++;
+
+               sc->sc_stats.evt_rx++;
+               break;
+
+       case BTH5_CHANNEL_HCI_ACL:
+               *(mtod(m, uint8_t *)) = HCI_ACL_DATA_PKT;
+               if (!hci_input_acl(sc->sc_unit, m))
+                       sc->sc_stats.err_rx++;
+
+               sc->sc_stats.acl_rx++;
+               break;
+
+       case BTH5_CHANNEL_HCI_SCO:
+               *(mtod(m, uint8_t *)) = HCI_SCO_DATA_PKT;
+               if (!hci_input_sco(sc->sc_unit, m))
+                       sc->sc_stats.err_rx++;
+
+               sc->sc_stats.sco_rx++;
+               break;
+
+       default:
+               aprint_error_dev(sc->sc_dev,
+                   "received reliable packet with not support channel %d\n",
+                   hdr.ident);
+               m_freem(m);
+               break;
+       }
+
+       sc->sc_seq_expected_rxseq =
+           (sc->sc_seq_expected_rxseq + 1) & BTH5_FLAGS_SEQ_MASK;
+       sc->sc_seq_txack = sc->sc_seq_expected_rxseq;
+       bth5_send_ack_command(sc);
+}
+
+static bool
+bth5_tx_reliable_pkt(struct bth5_softc *sc, struct mbuf *m, u_int protocol_id)
+{
+       bth5_hdr_t *hdrp;
+       struct mbuf *_m;
+       struct mbuf *_retrans;
+       u_int pldlen;
+       int s;
+
+       DPRINTFN(1, ("%s: seq transmit:"
+           "protocol_id=%d, winspace=%d, txseq=%d\n", device_xname(sc->sc_dev),
+           protocol_id, sc->sc_seq_winspace, sc->sc_seq_txseq));
+
+       for (pldlen = 0, _m = m; _m != NULL; _m = _m->m_next) {
+               if (_m->m_len < 0)
+                       goto out;
+               pldlen += _m->m_len;
+       }
+       if (pldlen > 0xfff)
+               goto out;
+       if (protocol_id == BTH5_IDENT_ACKPKT || protocol_id > 15)
+               goto out;
+
+       if (sc->sc_seq_winspace == 0)
+               goto out;
+
+       M_PREPEND(m, sizeof(bth5_hdr_t), M_DONTWAIT);
+       if (m == NULL) {
+               aprint_error_dev(sc->sc_dev, "out of memory\n");
+               return false;
+       }
+       KASSERT(m->m_len >= sizeof(bth5_hdr_t));
+
+       hdrp = mtod(m, bth5_hdr_t *);
+       memset(hdrp, 0, sizeof(bth5_hdr_t));
+       hdrp->flags |= sc->sc_seq_txseq;
+       hdrp->ident = protocol_id;
+
+       callout_schedule(&sc->sc_seq_timer, sc->sc_seq_timeout);
+
+       s = splserial();
+       MBUFQ_ENQUEUE(&sc->sc_seqq, m);
+       splx(s);
+       sc->sc_transmit_callback = bth5_reliabletx_callback;
+
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 2)
+               bth5_packet_print(m);
+#endif
+
+       sc->sc_seq_txseq = (sc->sc_seq_txseq + 1) & BTH5_FLAGS_SEQ_MASK;
+       sc->sc_seq_winspace--;
+       _retrans = m_copym(m, 0, M_COPYALL, M_WAIT);
+       if (_retrans == NULL) {
+               aprint_error_dev(sc->sc_dev, "out of memory\n");
+               goto out;
+       }
+       MBUFQ_ENQUEUE(&sc->sc_seq_retryq, _retrans);
+       bth5_mux_transmit(sc);
+
+       return true;
+out:
+       m_freem(m);
+       return false;
+}
+
+static __inline u_int
+bth5_get_txack(struct bth5_softc *sc)
+{
+
+       return sc->sc_seq_txack;
+}
+
+static void
+bth5_signal_rxack(struct bth5_softc *sc, uint32_t rxack)
+{
+       bth5_hdr_t *hdrp;
+       struct mbuf *m;
+       uint32_t seqno = (rxack - 1) & BTH5_FLAGS_SEQ_MASK;
+       int s;
+
+       DPRINTFN(1, ("%s: seq signal rxack: rxack=%d\n",
+           device_xname(sc->sc_dev), rxack));
+
+       s = splserial();
+       m = MBUFQ_FIRST(&sc->sc_seq_retryq);
+       while (m != NULL) {
+               hdrp = mtod(m, bth5_hdr_t *);
+               if (BTH5_FLAGS_SEQ(hdrp->flags) == seqno) {
+                       struct mbuf *m0;
+
+                       for (m0 = MBUFQ_FIRST(&sc->sc_seq_retryq);
+                           m0 != MBUFQ_NEXT(m);
+                           m0 = MBUFQ_FIRST(&sc->sc_seq_retryq)) {
+                               MBUFQ_DEQUEUE(&sc->sc_seq_retryq, m0);
+                               m_freem(m0);
+                               sc->sc_seq_winspace++;
+                       }
+                       break;
+               }
+               m = MBUFQ_NEXT(m);
+       }
+       splx(s);
+       sc->sc_seq_retries = 0;
+
+       if (sc->sc_seq_winspace == sc->sc_seq_winsize)
+               callout_stop(&sc->sc_seq_timer);
+       else
+               callout_schedule(&sc->sc_seq_timer, sc->sc_seq_timeout);
+}
+
+static void
+bth5_reliabletx_callback(struct bth5_softc *sc, struct mbuf *m)
+{
+
+       m_freem(m);
+}
+
+static void
+bth5_timer_timeout(void *arg)
+{
+       struct bth5_softc *sc = arg;
+       struct mbuf *m, *_m;
+       int s, i = 0;
+
+       DPRINTFN(1, ("%s: seq timeout: retries=%d\n",
+           device_xname(sc->sc_dev), sc->sc_seq_retries));
+
+       s = splserial();
+       for (m = MBUFQ_FIRST(&sc->sc_seq_retryq); m != NULL;
+           m = MBUFQ_NEXT(m)) {
+               _m = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
+               if (_m == NULL) {
+                       aprint_error_dev(sc->sc_dev, "out of memory\n");
+                       return;
+               }
+               MBUFQ_ENQUEUE(&sc->sc_seqq, _m);
+               i++;
+       }
+       splx(s);
+
+       if (i != 0) {
+               if (++sc->sc_seq_retries < sc->sc_seq_retry_limit)
+                       callout_schedule(&sc->sc_seq_timer, sc->sc_seq_timeout);
+               else {
+                       aprint_error_dev(sc->sc_dev,
+                           "reached the retry limit."
+                           " restart the link-establishment\n");
+                       bth5_sequencing_reset(sc);
+                       bth5_start_le(sc);
+                       return;
+               }
+       }
+       bth5_mux_transmit(sc);
+}
+
+static void
+bth5_sequencing_reset(struct bth5_softc *sc)
+{
+       int s;
+
+       s = splserial();
+       MBUFQ_DRAIN(&sc->sc_seqq);
+       MBUFQ_DRAIN(&sc->sc_seq_retryq);
+       splx(s);
+
+
+       sc->sc_seq_txseq = 0;
+       sc->sc_seq_txack = 0;
+       sc->sc_seq_winspace = sc->sc_seq_winsize;
+       sc->sc_seq_retries = 0;
+       callout_stop(&sc->sc_seq_timer);
+
+       sc->sc_mux_send_ack = false;
+
+       /* XXXX: expected_rxseq should be set by MUX Layer */
+       sc->sc_seq_expected_rxseq = 0;
+}
+
+
+/*
+ * BTH5 Datagram Queue Layer functions
+ */
+static void
+bth5_datagramq_receive(struct bth5_softc *sc, struct mbuf *m)
+{
+       bth5_hdr_t hdr;
+
+       DPRINTFN(1, ("%s: dgq receive\n", device_xname(sc->sc_dev)));
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 2)
+               bth5_packet_print(m);
+#endif
+
+       m_copydata(m, 0, sizeof(bth5_hdr_t), &hdr);
+
+       switch (hdr.ident) {
+       case BTH5_CHANNEL_LE:
+               m_adj(m, sizeof(bth5_hdr_t));
+               bth5_input_le(sc, m);
+               break;
+
+       case BTH5_CHANNEL_HCI_SCO:
+               /*
+                * We remove the header of BTH5 and add the 'uint8_t type' of
+                * hci_scodata_hdr_t to the head.
+                */
+               m_adj(m, sizeof(bth5_hdr_t) - sizeof(uint8_t));
+               *(mtod(m, uint8_t *)) = HCI_SCO_DATA_PKT;
+               if (!hci_input_sco(sc->sc_unit, m))
+                       sc->sc_stats.err_rx++;
+
+               sc->sc_stats.sco_rx++;
+               break;
+
+       default:
+               aprint_error_dev(sc->sc_dev,
+                   "received unreliable packet with not support channel %d\n",
+                   hdr.ident);
+               m_freem(m);
+               break;
+       }
+}
+
+static bool
+bth5_tx_unreliable_pkt(struct bth5_softc *sc, struct mbuf *m, u_int protocol_id)
+{
+       bth5_hdr_t *hdrp;
+       struct mbuf *_m;
+       u_int pldlen;
+       int s;
+
+       DPRINTFN(1, ("%s: dgq transmit: protocol_id=%d,",
+           device_xname(sc->sc_dev), protocol_id));
+
+       for (pldlen = 0, _m = m; _m != NULL; _m = m->m_next) {
+               if (_m->m_len < 0)
+                       goto out;
+               pldlen += _m->m_len;
+       }
+       DPRINTFN(1, (" pldlen=%d\n", pldlen));
+       if (pldlen > 0xfff)
+               goto out;
+       if (protocol_id == BTH5_IDENT_ACKPKT || protocol_id > 15)
+               goto out;
+
+       M_PREPEND(m, sizeof(bth5_hdr_t), M_DONTWAIT);
+       if (m == NULL) {
+               aprint_error_dev(sc->sc_dev, "out of memory\n");
+               return false;
+       }
+       KASSERT(m->m_len >= sizeof(bth5_hdr_t));
+
+       hdrp = mtod(m, bth5_hdr_t *);
+       memset(hdrp, 0, sizeof(bth5_hdr_t));
+       hdrp->ident = protocol_id;
+
+       s = splserial();
+       MBUFQ_ENQUEUE(&sc->sc_dgq, m);
+       splx(s);
+       sc->sc_transmit_callback = bth5_unreliabletx_callback;
+
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 2)
+               bth5_packet_print(m);
+#endif
+
+       bth5_mux_transmit(sc);
+
+       return true;
+out:
+       m_freem(m);
+       return false;
+}
+
+static void
+bth5_unreliabletx_callback(struct bth5_softc *sc, struct mbuf *m)
+{
+
+       if (M_GETCTX(m, void *) == NULL)
+               m_freem(m);
+       else if (!hci_complete_sco(sc->sc_unit, m))
+               sc->sc_stats.err_tx++;
+}
+
+
+/*
+ * BTUART H5 Link Establishment Protocol functions
+ */
+static const uint8_t sync[] = BTH5_LE_SYNC;
+static const uint8_t syncresp[] = BTH5_LE_SYNCRESP;
+static const uint8_t conf[] = BTH5_LE_CONF;
+static const uint8_t confresp[] = BTH5_LE_CONFRESP;
+
+static int
+bth5_start_le(struct bth5_softc *sc)
+{
+
+       DPRINTF(("%s: start link-establish\n", device_xname(sc->sc_dev)));
+
+       bth5_set_choke(sc, true);
+
+       if (!sc->sc_le_muzzled) {
+               struct mbuf *m;
+
+               m = m_gethdr(M_WAIT, MT_DATA);
+               m->m_pkthdr.len = m->m_len = 0;
+               m_copyback(m, 0, sizeof(sync), sync);
+               if (!bth5_tx_unreliable_pkt(sc, m, BTH5_CHANNEL_LE)) {
+                       aprint_error_dev(sc->sc_dev,
+                           "le-packet transmit failed\n");
+                       return EINVAL;
+               }
+       }
+       callout_schedule(&sc->sc_le_timer, BTH5_LE_TSHY_TIMEOUT);
+
+       sc->sc_le_state = le_state_shy;
+       return 0;
+}
+
+static void
+bth5_terminate_le(struct bth5_softc *sc)
+{
+       struct mbuf *m;
+
+       /* terminate link-establishment */
+       callout_stop(&sc->sc_le_timer);
+       bth5_set_choke(sc, true);
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               aprint_error_dev(sc->sc_dev, "out of memory\n");
+       else {
+               /* length of le packets is 4 */
+               m->m_pkthdr.len = m->m_len = 0;
+               m_copyback(m, 0, sizeof(sync), sync);
+               if (!bth5_tx_unreliable_pkt(sc, m, BTH5_CHANNEL_LE))
+                       aprint_error_dev(sc->sc_dev,
+                           "link-establishment terminations failed\n");
+       }
+}
+
+static void
+bth5_input_le(struct bth5_softc *sc, struct mbuf *m)
+{
+       uint16_t *rcvpkt;
+       int i;
+       const uint8_t *rplypkt;
+       static struct {
+               const char *type;
+               const uint8_t *datap;
+       } pkt[] = {
+               { "sync",       sync },
+               { "sync-resp",  syncresp },
+               { "conf",       conf },
+               { "conf-resp",  confresp },
+
+               { NULL, 0 }
+       };
+
+       DPRINTFN(0, ("%s: le input: state %d, muzzled %d\n",
+           device_xname(sc->sc_dev), sc->sc_le_state, sc->sc_le_muzzled));
+#ifdef BTH5_DEBUG
+       if (bth5_debug == 1)
+               bth5_packet_print(m);
+#endif
+
+       rcvpkt = mtod(m, uint16_t *);
+       i = 0;
+
+       /* length of le packets is 2 */
+       if (m->m_len == sizeof(uint16_t))
+               for (i = 0; pkt[i].type != NULL; i++)
+                       if (*(const uint16_t *)pkt[i].datap == *rcvpkt)
+                               break;
+       if (m->m_len < sizeof(uint16_t) || pkt[i].type == NULL) {
+               aprint_error_dev(sc->sc_dev, "received unknown packet\n");
+               m_freem(m);
+               return;
+       }
+
+       rplypkt = NULL;
+       switch (sc->sc_le_state) {
+       case le_state_shy:
+               if (*rcvpkt == *(const uint16_t *)sync) {
+                       sc->sc_le_muzzled = false;
+                       rplypkt = syncresp;
+               } else if (*rcvpkt == *(const uint16_t *)syncresp) {
+                       DPRINTF(("%s: state change to curious\n",
+                           device_xname(sc->sc_dev)));
+
+                       callout_schedule(&sc->sc_le_timer,
+                           BTH5_LE_TCONF_TIMEOUT);
+                       sc->sc_le_state = le_state_curious;
+               } else
+                       aprint_error_dev(sc->sc_dev,
+                           "received an unknown packet at shy\n");
+               break;
+
+       case le_state_curious:
+               if (*rcvpkt == *(const uint16_t *)sync)
+                       rplypkt = syncresp;
+               else if (*rcvpkt == *(const uint16_t *)conf)
+                       rplypkt = confresp;
+               else if (*rcvpkt == *(const uint16_t *)confresp) {
+                       DPRINTF(("%s: state change to garrulous:\n",
+                           device_xname(sc->sc_dev)));
+
+                       bth5_set_choke(sc, false);
+                       callout_stop(&sc->sc_le_timer);
+                       sc->sc_le_state = le_state_garrulous;
+               } else
+                       aprint_error_dev(sc->sc_dev,
+                           "received unknown packet at curious\n");
+               break;
+
+       case le_state_garrulous:
+               if (*rcvpkt == *(const uint16_t *)conf)
+                       rplypkt = confresp;
+               else if (*rcvpkt == *(const uint16_t *)sync) {
+                       /* XXXXX */
+                       aprint_error_dev(sc->sc_dev,
+                           "received sync! peer to reset?\n");
+
+                       bth5_sequencing_reset(sc);
+                       rplypkt = sync;
+                       sc->sc_le_state = le_state_shy;
+               } else
+                       aprint_error_dev(sc->sc_dev,
+                           "received unknown packet at garrulous\n");
+               break;
+       }
+
+       int len = m->m_len;
+       m_freem(m);
+
+       if (rplypkt != NULL) {
+               MGETHDR(m, M_DONTWAIT, MT_DATA);
+               if (m == NULL)
+                       aprint_error_dev(sc->sc_dev, "out of memory\n");
+               else {
+                       /* length of le packets is 2 */
+                       m->m_pkthdr.len = m->m_len = 0;
+                       if (rplypkt == confresp || rplypkt == conf)
+                               m_copyback(m, 0, len, rplypkt);
+                       else
+                               m_copyback(m, 0, 2, rplypkt);
+                       if (!bth5_tx_unreliable_pkt(sc, m, BTH5_CHANNEL_LE))
+                               aprint_error_dev(sc->sc_dev,
+                                   "le-packet transmit failed\n");
+               }
+       }
+}
+
+static void
+bth5_le_timeout(void *arg)
+{
+       struct bth5_softc *sc = arg;
+       struct mbuf *m;
+       int timeout;
+       const uint8_t *sndpkt = NULL;
+
+       DPRINTFN(0, ("%s: le timeout: state %d, muzzled %d\n",
+           device_xname(sc->sc_dev), sc->sc_le_state, sc->sc_le_muzzled));
+
+       switch (sc->sc_le_state) {
+       case le_state_shy:
+               if (!sc->sc_le_muzzled)
+                       sndpkt = sync;
+               timeout = BTH5_LE_TSHY_TIMEOUT;
+               break;
+
+       case le_state_curious:
+               sndpkt = conf;
+               timeout = BTH5_LE_TCONF_TIMEOUT;
+               break;
+
+       default:
+               aprint_error_dev(sc->sc_dev,
+                   "timeout happen at unknown state %d\n", sc->sc_le_state);
+               return;
+       }
+
+       if (sndpkt != NULL) {
+               MGETHDR(m, M_DONTWAIT, MT_DATA);
+               if (m == NULL)
+                       aprint_error_dev(sc->sc_dev, "out of memory\n");
+               else {
+                       /* length of le packets is 4 */
+                       m->m_pkthdr.len = m->m_len = 0;
+                       if (sndpkt == conf || sndpkt == confresp)
+                               m_copyback(m, 0, 3, sndpkt);
+                       else
+                               m_copyback(m, 0, 2, sndpkt);
+                       if (!bth5_tx_unreliable_pkt(sc, m, BTH5_CHANNEL_LE))
+                               aprint_error_dev(sc->sc_dev,
+                                   "le-packet transmit failed\n");
+               }
+       }
+
+       callout_schedule(&sc->sc_le_timer, timeout);
+}
+
+
+/*
+ * BTUART H5 Serial Protocol functions.
+ */
+static int
+bth5_enable(device_t self)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       if (sc->sc_flags & BTH5_ENABLED)
+               return 0;
+
+       s = spltty();
+
+       sc->sc_flags |= BTH5_ENABLED;
+       sc->sc_flags &= ~BTH5_XMIT;
+
+       splx(s);
+
+       return 0;
+}
+
+static void
+bth5_disable(device_t self)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       if ((sc->sc_flags & BTH5_ENABLED) == 0)
+               return;
+
+       s = spltty();
+
+       if (sc->sc_rxp) {
+               m_freem(sc->sc_rxp);
+               sc->sc_rxp = NULL;
+       }
+
+       if (sc->sc_txp) {
+               m_freem(sc->sc_txp);
+               sc->sc_txp = NULL;
+       }
+
+       MBUFQ_DRAIN(&sc->sc_cmdq);
+       MBUFQ_DRAIN(&sc->sc_aclq);
+       MBUFQ_DRAIN(&sc->sc_scoq);
+
+       sc->sc_flags &= ~BTH5_ENABLED;
+       splx(s);
+}
+
+static void
+bth5_start(struct bth5_softc *sc)
+{
+       struct mbuf *m;
+
+       KASSERT((sc->sc_flags & BTH5_XMIT) == 0);
+       KASSERT(sc->sc_txp == NULL);
+
+       if (MBUFQ_FIRST(&sc->sc_aclq)) {
+               MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+               sc->sc_stats.acl_tx++;
+               sc->sc_flags |= BTH5_XMIT;
+               bth5_tx_reliable_pkt(sc, m, BTH5_CHANNEL_HCI_ACL);
+       }
+
+       if (MBUFQ_FIRST(&sc->sc_cmdq)) {
+               MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+               sc->sc_stats.cmd_tx++;
+               sc->sc_flags |= BTH5_XMIT;
+               bth5_tx_reliable_pkt(sc, m, BTH5_CHANNEL_HCI_CMD);
+       }
+
+       if (MBUFQ_FIRST(&sc->sc_scoq)) {
+               MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+               sc->sc_stats.sco_tx++;
+               /* XXXX: We can transmit with reliable */
+               sc->sc_flags |= BTH5_XMIT;
+               bth5_tx_unreliable_pkt(sc, m, BTH5_CHANNEL_HCI_SCO);
+       }
+
+       return;
+}
+
+static void
+bth5_output_cmd(device_t self, struct mbuf *m)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       KASSERT(sc->sc_flags & BTH5_ENABLED);
+
+       m_adj(m, sizeof(uint8_t));
+       M_SETCTX(m, NULL);
+
+       s = spltty();
+       MBUFQ_ENQUEUE(&sc->sc_cmdq, m);
+       if ((sc->sc_flags & BTH5_XMIT) == 0)
+               bth5_start(sc);
+
+       splx(s);
+}
+
+static void
+bth5_output_acl(device_t self, struct mbuf *m)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       KASSERT(sc->sc_flags & BTH5_ENABLED);
+
+       m_adj(m, sizeof(uint8_t));
+       M_SETCTX(m, NULL);
+
+       s = spltty();
+       MBUFQ_ENQUEUE(&sc->sc_aclq, m);
+       if ((sc->sc_flags & BTH5_XMIT) == 0)
+               bth5_start(sc);
+
+       splx(s);
+}
+
+static void
+bth5_output_sco(device_t self, struct mbuf *m)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       KASSERT(sc->sc_flags & BTH5_ENABLED);
+
+       m_adj(m, sizeof(uint8_t));
+
+       s = spltty();
+       MBUFQ_ENQUEUE(&sc->sc_scoq, m);
+       if ((sc->sc_flags & BTH5_XMIT) == 0)
+               bth5_start(sc);
+
+       splx(s);
+}
+
+static void
+bth5_stats(device_t self, struct bt_stats *dest, int flush)
+{
+       struct bth5_softc *sc = device_private(self);
+       int s;
+
+       s = spltty();
+       memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats));
+
+       if (flush)
+               memset(&sc->sc_stats, 0, sizeof(struct bt_stats));
+
+       splx(s);
+}
+
+
+#ifdef BTH5_DEBUG
+static void
+bth5_packet_print(struct mbuf *m)
+{
+       int i;
+       uint8_t *p;
+
+       for ( ; m != NULL; m = m->m_next) {
+               p = mtod(m, uint8_t *);
+               for (i = 0; i < m->m_len; i++) {
+                       if (i % 16 == 0)
+                               printf(" ");
+                       printf(" %02x", *(p + i));
+                       if (i % 16 == 15)
+                               printf("\n");
+               }
+               printf("\n");
+       }
+}
+#endif
Index: com.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/com.c,v
retrieving revision 1.341
diff -u -p -r1.341 com.c
--- sys/dev/ic/com.c    31 Jul 2017 23:53:25 -0000      1.341
+++ sys/dev/ic/com.c    10 Aug 2017 09:02:39 -0000
@@ -477,7 +477,7 @@ com_attach_subr(struct com_softc *sc)
               goto fifodelay;

       case COM_TYPE_BCMAUXUART:
-               sc->sc_fifolen = 8;
+               sc->sc_fifolen = 1;
               fifo_msg = "BCM AUX UART, working fifo";
               SET(sc->sc_hwflags, COM_HW_FIFO);
               CSR_WRITE_1(regsp, COM_REG_FIFO,
Index: Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/btattach/Makefile,v
retrieving revision 1.2
diff -u -p -r1.2 Makefile
--- src/usr.sbin/btattach/Makefile      6 Dec 2009 12:55:46 -0000       1.2
+++ src/usr.sbin/btattach/Makefile      10 Aug 2017 09:17:49 -0000
@@ -3,7 +3,8 @@
PROG=  btattach
MAN=   btattach.8
SRCS=  btattach.c init_bcm2035.c init_bgb2xx.c init_csr.c init_digi.c \
-       init_ericsson.c init_st.c init_stlc2500.c init_swave.c init_unistone.c
+       init_ericsson.c init_st.c init_stlc2500.c init_swave.c init_unistone.c \
+       init_bcm43xx.c

DPADD+=        ${LIBBLUETOOTH} ${LIBUTIL}
LDADD+=        -lbluetooth -lutil
Index: btattach.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/btattach/btattach.c,v
retrieving revision 1.13
diff -u -p -r1.13 btattach.c
--- src/usr.sbin/btattach/btattach.c    16 Jun 2015 23:04:14 -0000      1.13
+++ src/usr.sbin/btattach/btattach.c    10 Aug 2017 09:17:49 -0000
@@ -62,6 +62,13 @@ static const struct devtype types[] = {
       .speed = B115200,
    },
    {
+       .name = "bcm43xx",
+       .line = "bth5",
+       .descr = "Broadcom BCM43xx",
+       .init = &init_bcm43xx,
+       .speed = B115200,
+    },
+    {
       .name = "bcsp",
       .line = "bcsp",
       .descr = "Generic BlueCore Serial Protocol",
Index: btattach.h
===================================================================
RCS file: /cvsroot/src/usr.sbin/btattach/btattach.h,v
retrieving revision 1.3
diff -u -p -r1.3 btattach.h
--- src/usr.sbin/btattach/btattach.h    6 Dec 2009 12:55:46 -0000       1.3
+++ src/usr.sbin/btattach/btattach.h    10 Aug 2017 09:17:49 -0000
@@ -40,6 +40,7 @@ struct devtype {
};

devinit_t init_bcm2035;
+devinit_t init_bcm43xx;
devinit_t init_bgb2xx;
devinit_t init_csr;
devinit_t init_digi;
--- /dev/null   2017-08-10 18:51:00.000000000 +1000
+++ src/usr.sbin/btattach/init_bcm43xx.c        2017-08-10 19:10:31.000000000 +1000
@@ -0,0 +1,110 @@
+/*     $NetBSD$        */
+
+/*-
+ * Copyright (c) 2017 Iain Hibbert
+ * All rights reserved.
+ *
+ * Copyright (c) 2008 Iain Hibbert
+ * 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 ``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 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.
+ */
+
+/*
+ * init information in this file gleaned from hciattach(8)
+ * command from BlueZ for Linux - see http://www.bluez.org/
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "btattach.h"
+
+#define HCI_CMD_BCM43XX_SET_UART_BAUD_RATE     \
+       HCI_OPCODE(HCI_OGF_VENDOR, 0x018)
+
+#define HCI_CMD_BCM43XX_SET_BDADDR             \
+       HCI_OPCODE(HCI_OGF_VENDOR, 0x006)
+
+#define HCI_CMD_43XXFWDN                       \
+       HCI_OPCODE(HCI_OGF_VENDOR, 0x02e)
+
+void
+init_bcm43xx(int fd, unsigned int speed)
+{
+       uint8_t rate[6];
+       uint8_t fw_buf[1024];
+       char fw[] = "./BCM43430A1.hcd";
+       int nr, fwfd, fw_len;
+       uint8_t resp[7];
+       uint8_t name[20];
+       uint16_t fw_cmd;
+
+       memset(rate, 0, sizeof(rate));
+
+       uart_send_cmd(fd, HCI_CMD_RESET, NULL, 0);
+       uart_recv_cc(fd, HCI_CMD_RESET, &resp, sizeof(resp));
+       /* assume it succeeded? */
+
+       fwfd = open(fw, O_RDONLY);
+       if (fwfd < 0) {
+               fprintf(stderr, "Unable to open firmware: %s\n", fw);
+               return;
+       }
+
+       uart_send_cmd(fd, HCI_CMD_43XXFWDN, NULL, 0);
+       uart_recv_cc(fd, HCI_CMD_43XXFWDN, &resp, sizeof(resp));
+               sleep(1);
+
+       while (read(fwfd, &fw_buf[1], 3) == 3) {
+               fw_buf[0] = HCI_CMD_PKT;
+               fw_len = fw_buf[3];
+               if (read(fwfd, &fw_buf[4], fw_len) != fw_len)
+                       break;
+               fw_cmd = fw_buf[2] << 8 | fw_buf[1];
+               uart_send_cmd(fd, fw_cmd, &fw_buf[4], fw_len);
+               uart_recv_cc(fd, fw_cmd, &resp, sizeof(resp));
+       }
+
+       close(fwfd);
+
+       sleep(4);
+       uart_send_cmd(fd, HCI_CMD_RESET, NULL, 0);
+       uart_recv_cc(fd, HCI_CMD_RESET, &resp, sizeof(resp));
+       /* assume it succeeded? */
+
+       rate[2] = speed;
+       rate[3] = speed >> 8;
+       rate[4] = speed >> 16;
+       rate[5] = speed >> 24;
+
+       uart_send_cmd(fd, HCI_CMD_BCM43XX_SET_UART_BAUD_RATE, &rate, sizeof(rate));
+       uart_recv_cc(fd, HCI_CMD_BCM43XX_SET_UART_BAUD_RATE, &resp, sizeof(resp));
+}