/* $NetBSD: bcsp.c,v 1.33 2022/10/26 23:43:21 riastradh Exp $ */ /* * 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: bcsp.c,v 1.33 2022/10/26 23:43:21 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ioconf.h" #ifdef BCSP_DEBUG #ifdef DPRINTF #undef DPRINTF #endif #ifdef DPRINTFN #undef DPRINTFN #endif #define DPRINTF(x) printf x #define DPRINTFN(n, x) do { if (bcsp_debug > (n)) printf x; } while (0) int bcsp_debug = 3; #else #undef DPRINTF #undef DPRINTFN #define DPRINTF(x) #define DPRINTFN(n, x) #endif struct bcsp_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 bcsp_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 BCSP Link Establishment Protocol */ bool sc_le_muzzled; bcsp_le_state_t sc_le_state; callout_t sc_le_timer; struct sysctllog *sc_log; /* sysctl log */ }; /* sc_flags */ #define BCSP_XMIT (1 << 0) /* transmit active */ #define BCSP_ENABLED (1 << 1) /* is enabled */ static int bcsp_match(device_t, cfdata_t, void *); static void bcsp_attach(device_t, device_t, void *); static int bcsp_detach(device_t, int); /* tty functions */ static int bcspopen(dev_t, struct tty *); static int bcspclose(struct tty *, int); static int bcspioctl(struct tty *, u_long, void *, int, struct lwp *); static int bcsp_slip_transmit(struct tty *); static int bcsp_slip_receive(int, struct tty *); static void bcsp_pktintegrity_transmit(struct bcsp_softc *); static void bcsp_pktintegrity_receive(struct bcsp_softc *, struct mbuf *); static void bcsp_crc_update(uint16_t *, uint8_t); static uint16_t bcsp_crc_reverse(uint16_t); static void bcsp_mux_transmit(struct bcsp_softc *sc); static void bcsp_mux_receive(struct bcsp_softc *sc, struct mbuf *m); static __inline void bcsp_send_ack_command(struct bcsp_softc *sc); static __inline struct mbuf *bcsp_create_ackpkt(void); static __inline void bcsp_set_choke(struct bcsp_softc *, bool); static void bcsp_sequencing_receive(struct bcsp_softc *, struct mbuf *); static bool bcsp_tx_reliable_pkt(struct bcsp_softc *, struct mbuf *, u_int); static __inline u_int bcsp_get_txack(struct bcsp_softc *); static void bcsp_signal_rxack(struct bcsp_softc *, uint32_t); static void bcsp_reliabletx_callback(struct bcsp_softc *, struct mbuf *); static void bcsp_timer_timeout(void *); static void bcsp_sequencing_reset(struct bcsp_softc *); static void bcsp_datagramq_receive(struct bcsp_softc *, struct mbuf *); static bool bcsp_tx_unreliable_pkt(struct bcsp_softc *, struct mbuf *, u_int); static void bcsp_unreliabletx_callback(struct bcsp_softc *, struct mbuf *); static int bcsp_start_le(struct bcsp_softc *); static void bcsp_terminate_le(struct bcsp_softc *); static void bcsp_input_le(struct bcsp_softc *, struct mbuf *); static void bcsp_le_timeout(void *); static void bcsp_start(struct bcsp_softc *); /* bluetooth hci functions */ static int bcsp_enable(device_t); static void bcsp_disable(device_t); static void bcsp_output_cmd(device_t, struct mbuf *); static void bcsp_output_acl(device_t, struct mbuf *); static void bcsp_output_sco(device_t, struct mbuf *); static void bcsp_stats(device_t, struct bt_stats *, int); #ifdef BCSP_DEBUG static void bcsp_packet_print(struct mbuf *m); #endif /* * It doesn't need to be exported, as only bcspattach() uses it, * but there's no "official" way to make it static. */ CFATTACH_DECL_NEW(bcsp, sizeof(struct bcsp_softc), bcsp_match, bcsp_attach, bcsp_detach, NULL); static struct linesw bcsp_disc = { .l_name = "bcsp", .l_open = bcspopen, .l_close = bcspclose, .l_read = ttyerrio, .l_write = ttyerrio, .l_ioctl = bcspioctl, .l_rint = bcsp_slip_receive, .l_start = bcsp_slip_transmit, .l_modem = ttymodem, .l_poll = ttyerrpoll }; static const struct hci_if bcsp_hci = { .enable = bcsp_enable, .disable = bcsp_disable, .output_cmd = bcsp_output_cmd, .output_acl = bcsp_output_acl, .output_sco = bcsp_output_sco, .get_stats = bcsp_stats, .ipl = IPL_TTY, }; /* ARGSUSED */ void bcspattach(int num __unused) { int error; error = ttyldisc_attach(&bcsp_disc); if (error) { aprint_error("%s: unable to register line discipline, " "error = %d\n", bcsp_cd.cd_name, error); return; } error = config_cfattach_attach(bcsp_cd.cd_name, &bcsp_ca); if (error) { aprint_error("%s: unable to register cfattach, error = %d\n", bcsp_cd.cd_name, error); config_cfdriver_detach(&bcsp_cd); (void) ttyldisc_detach(&bcsp_disc); } } /* * Autoconf match routine. * * XXX: unused: config_attach_pseudo(9) does not call ca_match. */ /* ARGSUSED */ static int bcsp_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 bcsp_attach(device_t parent __unused, device_t self, void *aux __unused) { struct bcsp_softc *sc = device_private(self); const struct sysctlnode *node; int rc, bcsp_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, bcsp_timer_timeout, sc); callout_init(&sc->sc_le_timer, 0); callout_setfunc(&sc->sc_le_timer, bcsp_le_timeout, sc); sc->sc_seq_timeout = BCSP_SEQ_TX_TIMEOUT; sc->sc_seq_winsize = BCSP_SEQ_TX_WINSIZE; sc->sc_seq_retry_limit = BCSP_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(&bcsp_hci, self, 0); if ((rc = sysctl_createv(&sc->sc_log, 0, NULL, &node, 0, CTLTYPE_NODE, device_xname(self), SYSCTL_DESCR("bcsp controls"), NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) { goto err; } bcsp_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, bcsp_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, bcsp_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, bcsp_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, bcsp_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, bcsp_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 bcsp_detach(device_t self, int flags __unused) { struct bcsp_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 bcspopen(dev_t device __unused, struct tty *tp) { struct bcsp_softc *sc; device_t dev; cfdata_t cfdata; struct lwp *l = curlwp; /* XXX */ int error, unit, s; static char name[] = "bcsp"; 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 == &bcsp_disc) { sc = tp->t_sc; if (sc != NULL) { splx(s); return EBUSY; } } cfdata = malloc(sizeof(struct cfdata), M_DEVBUF, M_WAITOK); for (unit = 0; unit < bcsp_cd.cd_ndevs; unit++) if (device_lookup(&bcsp_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); ttylock(tp); tp->t_sc = sc; sc->sc_tp = tp; ttyflush(tp, FREAD | FWRITE); ttyunlock(tp); splx(s); sc->sc_slip_txrsv = BCSP_SLIP_PKTSTART; bcsp_sequencing_reset(sc); /* start link-establishment */ bcsp_start_le(sc); return 0; } /* ARGSUSED */ static int bcspclose(struct tty *tp, int flag __unused) { struct bcsp_softc *sc = tp->t_sc; cfdata_t cfdata; int s; /* terminate link-establishment */ bcsp_terminate_le(sc); s = spltty(); MBUFQ_DRAIN(&sc->sc_dgq); bcsp_sequencing_reset(sc); ttylock(tp); ttyflush(tp, FREAD | FWRITE); ttyunlock(tp); /* 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 bcspioctl(struct tty *tp, u_long cmd, void *data, int flag __unused, struct lwp *l __unused) { struct bcsp_softc *sc = tp->t_sc; int error; /* * XXX * This function can be called without KERNEL_LOCK when caller's * struct cdevsw is set D_MPSAFE. Is KERNEL_LOCK required? */ 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. */ /* * BCSP SLIP Layer functions: * Supports to transmit/receive a byte stream. * SLIP protocol described in Internet standard RFC 1055. */ static int bcsp_slip_transmit(struct tty *tp) { struct bcsp_softc *sc = tp->t_sc; struct mbuf *m; int count, rlen; uint8_t *rptr; m = sc->sc_txp; if (m == NULL) { sc->sc_flags &= ~BCSP_XMIT; bcsp_mux_transmit(sc); return 0; } count = 0; rlen = 0; rptr = mtod(m, uint8_t *); if (sc->sc_slip_txrsv != 0) { #ifdef BCSP_DEBUG if (sc->sc_slip_txrsv == BCSP_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 == BCSP_SLIP_ESCAPE_PKTEND || sc->sc_slip_txrsv == BCSP_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(BCSP_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 = BCSP_SLIP_PKTSTART; sc->sc_transmit_callback(sc, m); m = NULL; break; } rlen = 0; rptr = mtod(m, uint8_t *); continue; } if (*rptr == BCSP_SLIP_PKTEND) { if (putc(BCSP_SLIP_ESCAPE, &tp->t_outq) < 0) break; count++; DPRINTFN(4, (" esc ")); if (putc(BCSP_SLIP_ESCAPE_PKTEND, &tp->t_outq) < 0) { sc->sc_slip_txrsv = BCSP_SLIP_ESCAPE_PKTEND; break; } DPRINTFN(4, ("0x%02x ", BCSP_SLIP_ESCAPE_PKTEND)); rptr++; } else if (*rptr == BCSP_SLIP_ESCAPE) { if (putc(BCSP_SLIP_ESCAPE, &tp->t_outq) < 0) break; count++; DPRINTFN(4, (" esc ")); if (putc(BCSP_SLIP_ESCAPE_ESCAPE, &tp->t_outq) < 0) { sc->sc_slip_txrsv = BCSP_SLIP_ESCAPE_ESCAPE; break; } DPRINTFN(4, ("0x%02x ", BCSP_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 != NULL) (*tp->t_oproc)(tp); return 0; } static int bcsp_slip_receive(int c, struct tty *tp) { struct bcsp_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 != BCSP_SLIP_PKTSTART) { discard = 1; errstr = "not sync"; goto discarded; } switch (c) { case BCSP_SLIP_PKTSTART /* or _PKTEND */: if (m == NULL) { /* BCSP_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 { /* BCSP_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))); bcsp_pktintegrity_receive(sc, sc->sc_rxp); sc->sc_rxp = NULL; sc->sc_slip_rxexp = BCSP_SLIP_PKTSTART; } sc->sc_stats.byte_rx++; return 0; case BCSP_SLIP_ESCAPE: DPRINTFN(4, (" esc")); if (sc->sc_slip_rxexp == BCSP_SLIP_ESCAPE) { discard = 1; errstr = "waiting 0xdc or 0xdb"; } else sc->sc_slip_rxexp = BCSP_SLIP_ESCAPE; break; default: DPRINTFN(4, (" 0x%02x%s", c, (m->m_len % 16 == 15) ? "\n" : "")); switch (sc->sc_slip_rxexp) { case BCSP_SLIP_PKTSTART: discard = 1; errstr = "waiting 0xc0"; break; case BCSP_SLIP_ESCAPE: if (c == BCSP_SLIP_ESCAPE_PKTEND) mtod(m, uint8_t *)[m->m_len++] = BCSP_SLIP_PKTEND; else if (c == BCSP_SLIP_ESCAPE_ESCAPE) mtod(m, uint8_t *)[m->m_len++] = BCSP_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 BCSP_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; } /* * BCSP Packet Integrity Layer functions: * handling Payload Length, Checksum, CRC. */ static void bcsp_pktintegrity_transmit(struct bcsp_softc *sc) { struct mbuf *m = sc->sc_txp; bcsp_hdr_t *hdrp = mtod(m, bcsp_hdr_t *); int pldlen; DPRINTFN(3, ("%s: pi transmit\n", device_xname(sc->sc_dev))); pldlen = m->m_pkthdr.len - sizeof(bcsp_hdr_t); if (sc->sc_pi_txcrc) hdrp->flags |= BCSP_FLAGS_CRC_PRESENT; BCSP_SET_PLEN(hdrp, pldlen); BCSP_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++) bcsp_crc_update(&crc, *(buf + n)); } crc = htobe16(bcsp_crc_reverse(crc)); m_copyback(m, m->m_pkthdr.len, sizeof(crc), &crc); } #ifdef BCSP_DEBUG if (bcsp_debug == 4) bcsp_packet_print(m); #endif bcsp_slip_transmit(sc->sc_tp); } static void bcsp_pktintegrity_receive(struct bcsp_softc *sc, struct mbuf *m) { bcsp_hdr_t *hdrp = mtod(m, bcsp_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 BCSP_DEBUG if (bcsp_debug == 4) bcsp_packet_print(m); #endif KASSERT(m->m_len >= sizeof(bcsp_hdr_t)); pldlen = m->m_pkthdr.len - sizeof(bcsp_hdr_t) - ((hdrp->flags & BCSP_FLAGS_CRC_PRESENT) ? sizeof(crc) : 0); if (pldlen > 0xfff) { discard = 1; errstr = "Payload Length"; goto discarded; } if (hdrp->csum != BCSP_GET_CSUM(hdrp)) { discard = 1; errstr = "Checksum"; goto discarded; } if (BCSP_GET_PLEN(hdrp) != pldlen) { discard = 1; errstr = "Payload Length"; goto discarded; } if (hdrp->flags & BCSP_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(bcsp_hdr_t) + pldlen; n++, i++) bcsp_crc_update(&crc, *(buf + n)); } m_copydata(_m, n, sizeof(crc0), &crc0); if (be16toh(crc0) != bcsp_crc_reverse(crc)) { discard = 1; errstr = "CRC"; } else /* Shaves CRC */ m_adj(m, (int)(0 - sizeof(crc))); } if (discard) { discarded: #ifdef BCSP_DEBUG DPRINTFN(3, ("%s: receives unexpected packet: %s\n", device_xname(sc->sc_dev), errstr)); #else __USE(errstr); #endif m_freem(m); } else bcsp_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 bcsp_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 bcsp_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; } /* * BCSP MUX Layer functions */ static void bcsp_mux_transmit(struct bcsp_softc *sc) { struct mbuf *m; bcsp_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, bcsp_hdr_t *); if (hdrp->ident == BCSP_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, bcsp_hdr_t *); hdrp->flags |= BCSP_FLAGS_PROTOCOL_REL; /* Reliable */ goto transmit; } bcsp_start(sc); if (sc->sc_mux_send_ack == true) { m = bcsp_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", bcsp_get_txack(sc), sc->sc_mux_send_ack)); hdrp = mtod(m, bcsp_hdr_t *); hdrp->flags |= (bcsp_get_txack(sc) << BCSP_FLAGS_ACK_SHIFT) & BCSP_FLAGS_ACK_MASK; if (sc->sc_mux_send_ack == true) sc->sc_mux_send_ack = false; #ifdef BCSP_DEBUG if (bcsp_debug == 3) bcsp_packet_print(m); #endif sc->sc_txp = m; bcsp_pktintegrity_transmit(sc); } static void bcsp_mux_receive(struct bcsp_softc *sc, struct mbuf *m) { bcsp_hdr_t *hdrp = mtod(m, bcsp_hdr_t *); const u_int rxack = BCSP_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 BCSP_DEBUG if (bcsp_debug == 3) bcsp_packet_print(m); #endif bcsp_signal_rxack(sc, rxack); microtime(&sc->sc_mux_lastrx); /* if the Ack Packet received then discard */ if (BCSP_FLAGS_SEQ(hdrp->flags) == 0 && hdrp->ident == BCSP_IDENT_ACKPKT && BCSP_GET_PLEN(hdrp) == 0) { m_freem(m); return; } if (hdrp->flags & BCSP_FLAGS_PROTOCOL_REL) bcsp_sequencing_receive(sc, m); else bcsp_datagramq_receive(sc, m); } static __inline void bcsp_send_ack_command(struct bcsp_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 * bcsp_create_ackpkt(void) { struct mbuf *m; bcsp_hdr_t *hdrp; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m != NULL) { m->m_pkthdr.len = m->m_len = sizeof(bcsp_hdr_t); hdrp = mtod(m, bcsp_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(bcsp_hdr_t)); } return m; } static __inline void bcsp_set_choke(struct bcsp_softc *sc, bool choke) { DPRINTFN(2, ("%s: mux set choke=%d\n", device_xname(sc->sc_dev), choke)); sc->sc_mux_choke = choke; } /* * BCSP Sequencing Layer functions */ static void bcsp_sequencing_receive(struct bcsp_softc *sc, struct mbuf *m) { bcsp_hdr_t hdr; uint32_t rxseq; m_copydata(m, 0, sizeof(bcsp_hdr_t), &hdr); rxseq = BCSP_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 BCSP_DEBUG if (bcsp_debug == 2) bcsp_packet_print(m); #endif /* * We remove the header of BCSP and add the 'uint8_t type' of * hci_*_hdr_t to the head. */ m_adj(m, sizeof(bcsp_hdr_t) - sizeof(uint8_t)); if (rxseq != sc->sc_seq_expected_rxseq) { m_freem(m); /* send ack packet, if needly */ bcsp_mux_transmit(sc); return; } switch (hdr.ident) { case BCSP_CHANNEL_HCI_CMDEVT: *(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 BCSP_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 BCSP_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; case BCSP_CHANNEL_HQ: case BCSP_CHANNEL_DEVMGT: case BCSP_CHANNEL_L2CAP: case BCSP_CHANNEL_RFCOMM: case BCSP_CHANNEL_SDP: case BCSP_CHANNEL_DFU: case BCSP_CHANNEL_VM: 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) & BCSP_FLAGS_SEQ_MASK; sc->sc_seq_txack = sc->sc_seq_expected_rxseq; bcsp_send_ack_command(sc); } static bool bcsp_tx_reliable_pkt(struct bcsp_softc *sc, struct mbuf *m, u_int protocol_id) { bcsp_hdr_t *hdrp; struct mbuf *_m; 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 == BCSP_IDENT_ACKPKT || protocol_id > 15) goto out; if (sc->sc_seq_winspace == 0) goto out; M_PREPEND(m, sizeof(bcsp_hdr_t), M_DONTWAIT); if (m == NULL) { aprint_error_dev(sc->sc_dev, "out of memory\n"); return false; } KASSERT(m->m_len >= sizeof(bcsp_hdr_t)); hdrp = mtod(m, bcsp_hdr_t *); memset(hdrp, 0, sizeof(bcsp_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 = bcsp_reliabletx_callback; #ifdef BCSP_DEBUG if (bcsp_debug == 2) bcsp_packet_print(m); #endif sc->sc_seq_txseq = (sc->sc_seq_txseq + 1) & BCSP_FLAGS_SEQ_MASK; sc->sc_seq_winspace--; _m = m_copym(m, 0, M_COPYALL, M_DONTWAIT); if (_m == NULL) { aprint_error_dev(sc->sc_dev, "out of memory\n"); goto out; } MBUFQ_ENQUEUE(&sc->sc_seq_retryq, _m); bcsp_mux_transmit(sc); return true; out: m_freem(m); return false; } #if 0 static bool bcsp_rx_reliable_pkt(struct bcsp_softc *sc, struct mbuf *m, u_int protocol_id) { return false; } /* XXXX: I can't understand meaning this function... */ static __inline void bcsp_link_failed(struct bcsp_softc *sc) { return (sc->sc_seq_retries >= sc->sc_seq_retry_limit); } #endif static __inline u_int bcsp_get_txack(struct bcsp_softc *sc) { return sc->sc_seq_txack; } static void bcsp_signal_rxack(struct bcsp_softc *sc, uint32_t rxack) { bcsp_hdr_t *hdrp; struct mbuf *m; uint32_t seqno = (rxack - 1) & BCSP_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, bcsp_hdr_t *); if (BCSP_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 bcsp_reliabletx_callback(struct bcsp_softc *sc, struct mbuf *m) { m_freem(m); } static void bcsp_timer_timeout(void *arg) { struct bcsp_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"); bcsp_sequencing_reset(sc); bcsp_start_le(sc); return; } } bcsp_mux_transmit(sc); } static void bcsp_sequencing_reset(struct bcsp_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; } /* * BCSP Datagram Queue Layer functions */ static void bcsp_datagramq_receive(struct bcsp_softc *sc, struct mbuf *m) { bcsp_hdr_t hdr; DPRINTFN(1, ("%s: dgq receive\n", device_xname(sc->sc_dev))); #ifdef BCSP_DEBUG if (bcsp_debug == 2) bcsp_packet_print(m); #endif m_copydata(m, 0, sizeof(bcsp_hdr_t), &hdr); switch (hdr.ident) { case BCSP_CHANNEL_LE: m_adj(m, sizeof(bcsp_hdr_t)); bcsp_input_le(sc, m); break; case BCSP_CHANNEL_HCI_SCO: /* * We remove the header of BCSP and add the 'uint8_t type' of * hci_scodata_hdr_t to the head. */ m_adj(m, sizeof(bcsp_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 bcsp_tx_unreliable_pkt(struct bcsp_softc *sc, struct mbuf *m, u_int protocol_id) { bcsp_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 == BCSP_IDENT_ACKPKT || protocol_id > 15) goto out; M_PREPEND(m, sizeof(bcsp_hdr_t), M_DONTWAIT); if (m == NULL) { aprint_error_dev(sc->sc_dev, "out of memory\n"); return false; } KASSERT(m->m_len >= sizeof(bcsp_hdr_t)); hdrp = mtod(m, bcsp_hdr_t *); memset(hdrp, 0, sizeof(bcsp_hdr_t)); hdrp->ident = protocol_id; s = splserial(); MBUFQ_ENQUEUE(&sc->sc_dgq, m); splx(s); sc->sc_transmit_callback = bcsp_unreliabletx_callback; #ifdef BCSP_DEBUG if (bcsp_debug == 2) bcsp_packet_print(m); #endif bcsp_mux_transmit(sc); return true; out: m_freem(m); return false; } #if 0 static bool bcsp_rx_unreliable_pkt(struct bcsp_softc *sc, struct mbuf *m, u_int protocol_id) { return false; } #endif static void bcsp_unreliabletx_callback(struct bcsp_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++; } /* * BlueCore Link Establishment Protocol functions */ static const uint8_t sync[] = BCSP_LE_SYNC; static const uint8_t syncresp[] = BCSP_LE_SYNCRESP; static const uint8_t conf[] = BCSP_LE_CONF; static const uint8_t confresp[] = BCSP_LE_CONFRESP; static int bcsp_start_le(struct bcsp_softc *sc) { DPRINTF(("%s: start link-establish\n", device_xname(sc->sc_dev))); bcsp_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 (!bcsp_tx_unreliable_pkt(sc, m, BCSP_CHANNEL_LE)) { aprint_error_dev(sc->sc_dev, "le-packet transmit failed\n"); return EINVAL; } } callout_schedule(&sc->sc_le_timer, BCSP_LE_TSHY_TIMEOUT); sc->sc_le_state = le_state_shy; return 0; } static void bcsp_terminate_le(struct bcsp_softc *sc) { struct mbuf *m; /* terminate link-establishment */ callout_stop(&sc->sc_le_timer); bcsp_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 (!bcsp_tx_unreliable_pkt(sc, m, BCSP_CHANNEL_LE)) aprint_error_dev(sc->sc_dev, "link-establishment terminations failed\n"); } } static void bcsp_input_le(struct bcsp_softc *sc, struct mbuf *m) { uint32_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 BCSP_DEBUG if (bcsp_debug == 1) bcsp_packet_print(m); #endif rcvpkt = mtod(m, uint32_t *); i = 0; /* length of le packets is 4 */ if (m->m_len == sizeof(uint32_t)) for (i = 0; pkt[i].type != NULL; i++) if (*(const uint32_t *)pkt[i].datap == *rcvpkt) break; if (m->m_len != sizeof(uint32_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 uint32_t *)sync) { sc->sc_le_muzzled = false; rplypkt = syncresp; } else if (*rcvpkt == *(const uint32_t *)syncresp) { DPRINTF(("%s: state change to curious\n", device_xname(sc->sc_dev))); rplypkt = conf; callout_schedule(&sc->sc_le_timer, BCSP_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 uint32_t *)sync) rplypkt = syncresp; else if (*rcvpkt == *(const uint32_t *)conf) rplypkt = confresp; else if (*rcvpkt == *(const uint32_t *)confresp) { DPRINTF(("%s: state change to garrulous:\n", device_xname(sc->sc_dev))); bcsp_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 uint32_t *)conf) rplypkt = confresp; else if (*rcvpkt == *(const uint32_t *)sync) { /* XXXXX */ aprint_error_dev(sc->sc_dev, "received sync! peer to reset?\n"); bcsp_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; } 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 4 */ m->m_pkthdr.len = m->m_len = 0; m_copyback(m, 0, 4, rplypkt); if (!bcsp_tx_unreliable_pkt(sc, m, BCSP_CHANNEL_LE)) aprint_error_dev(sc->sc_dev, "le-packet transmit failed\n"); } } } static void bcsp_le_timeout(void *arg) { struct bcsp_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 = BCSP_LE_TSHY_TIMEOUT; break; case le_state_curious: sndpkt = conf; timeout = BCSP_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; m_copyback(m, 0, 4, sndpkt); if (!bcsp_tx_unreliable_pkt(sc, m, BCSP_CHANNEL_LE)) aprint_error_dev(sc->sc_dev, "le-packet transmit failed\n"); } } callout_schedule(&sc->sc_le_timer, timeout); } /* * BlueCore Serial Protocol functions. */ static int bcsp_enable(device_t self) { struct bcsp_softc *sc = device_private(self); int s; if (sc->sc_flags & BCSP_ENABLED) return 0; s = spltty(); sc->sc_flags |= BCSP_ENABLED; sc->sc_flags &= ~BCSP_XMIT; splx(s); return 0; } static void bcsp_disable(device_t self) { struct bcsp_softc *sc = device_private(self); int s; if ((sc->sc_flags & BCSP_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 &= ~BCSP_ENABLED; splx(s); } static void bcsp_start(struct bcsp_softc *sc) { struct mbuf *m; KASSERT((sc->sc_flags & BCSP_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 |= BCSP_XMIT; bcsp_tx_reliable_pkt(sc, m, BCSP_CHANNEL_HCI_ACL); } if (MBUFQ_FIRST(&sc->sc_cmdq)) { MBUFQ_DEQUEUE(&sc->sc_cmdq, m); sc->sc_stats.cmd_tx++; sc->sc_flags |= BCSP_XMIT; bcsp_tx_reliable_pkt(sc, m, BCSP_CHANNEL_HCI_CMDEVT); } 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 |= BCSP_XMIT; bcsp_tx_unreliable_pkt(sc, m, BCSP_CHANNEL_HCI_SCO); } return; } static void bcsp_output_cmd(device_t self, struct mbuf *m) { struct bcsp_softc *sc = device_private(self); int s; KASSERT(sc->sc_flags & BCSP_ENABLED); m_adj(m, sizeof(uint8_t)); M_SETCTX(m, NULL); s = spltty(); MBUFQ_ENQUEUE(&sc->sc_cmdq, m); if ((sc->sc_flags & BCSP_XMIT) == 0) bcsp_start(sc); splx(s); } static void bcsp_output_acl(device_t self, struct mbuf *m) { struct bcsp_softc *sc = device_private(self); int s; KASSERT(sc->sc_flags & BCSP_ENABLED); m_adj(m, sizeof(uint8_t)); M_SETCTX(m, NULL); s = spltty(); MBUFQ_ENQUEUE(&sc->sc_aclq, m); if ((sc->sc_flags & BCSP_XMIT) == 0) bcsp_start(sc); splx(s); } static void bcsp_output_sco(device_t self, struct mbuf *m) { struct bcsp_softc *sc = device_private(self); int s; KASSERT(sc->sc_flags & BCSP_ENABLED); m_adj(m, sizeof(uint8_t)); s = spltty(); MBUFQ_ENQUEUE(&sc->sc_scoq, m); if ((sc->sc_flags & BCSP_XMIT) == 0) bcsp_start(sc); splx(s); } static void bcsp_stats(device_t self, struct bt_stats *dest, int flush) { struct bcsp_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 BCSP_DEBUG static void bcsp_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