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 + * 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 + * 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 +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 +__RCSID("$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#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)); +}