/*- * Copyright (c) 2016 Andriy Voskoboinyk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef RTWN_WITHOUT_UCODE void r12a_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { #if __FreeBSD_version >= 1200012 struct ieee80211_ratectl_tx_status txs; #endif struct r12a_c2h_tx_rpt *rpt; struct ieee80211_node *ni; int ntries; /* Skip Rx descriptor / report id / sequence fields. */ buf += sizeof(struct r92c_rx_stat) + 2; len -= sizeof(struct r92c_rx_stat) + 2; rpt = (struct r12a_c2h_tx_rpt *)buf; if (len != sizeof(*rpt)) { device_printf(sc->sc_dev, "%s: wrong report size (%d, must be %zu)\n", __func__, len, sizeof(*rpt)); return; } RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: ccx report dump: 0: %02X, id: %02X, 2: %02X, queue time: " "low %02X, high %02X, final ridx: %02X, rsvd: %04X\n", __func__, rpt->txrptb0, rpt->macid, rpt->txrptb2, rpt->queue_time_low, rpt->queue_time_high, rpt->final_rate, rpt->reserved); if (rpt->macid > sc->macid_limit) { device_printf(sc->sc_dev, "macid %u is too big; increase MACID_MAX limit\n", rpt->macid); return; } ntries = MS(rpt->txrptb2, R12A_TXRPTB2_RETRY_CNT); ni = sc->node_list[rpt->macid]; if (ni != NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: frame for macid %u was" "%s sent (%d retries)\n", __func__, rpt->macid, (rpt->txrptb0 & (R12A_TXRPTB0_RETRY_OVER | R12A_TXRPTB0_LIFE_EXPIRE)) ? " not" : "", ntries); #if __FreeBSD_version >= 1200012 txs.flags = IEEE80211_RATECTL_STATUS_LONG_RETRY | IEEE80211_RATECTL_STATUS_FINAL_RATE; txs.long_retries = ntries; if (rpt->final_rate > RTWN_RIDX_OFDM54) { /* MCS */ txs.final_rate = (rpt->final_rate - 12) | IEEE80211_RATE_MCS; } else txs.final_rate = ridx2rate[rpt->final_rate]; if (rpt->txrptb0 & R12A_TXRPTB0_RETRY_OVER) txs.status = IEEE80211_RATECTL_TX_FAIL_LONG; else if (rpt->txrptb0 & R12A_TXRPTB0_LIFE_EXPIRE) txs.status = IEEE80211_RATECTL_TX_FAIL_EXPIRED; else txs.status = IEEE80211_RATECTL_TX_SUCCESS; ieee80211_ratectl_tx_complete(ni, &txs); #else struct ieee80211vap *vap = ni->ni_vap; if (rpt->txrptb0 & R12A_TXRPTB0_RETRY_OVER) { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ntries, NULL); } else { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ntries, NULL); } #endif } else { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: macid %u, ni is NULL\n", __func__, rpt->macid); } } void r12a_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) { struct r12a_softc *rs = sc->sc_priv; /* Skip Rx descriptor. */ buf += sizeof(struct r92c_rx_stat); len -= sizeof(struct r92c_rx_stat); if (len < 2) { device_printf(sc->sc_dev, "C2H report too short (len %d)\n", len); return; } len -= 2; switch (buf[0]) { /* command id */ case R12A_C2H_TX_REPORT: /* NOTREACHED */ KASSERT(0, ("use handle_tx_report() instead of %s\n", __func__)); break; case R12A_C2H_IQK_FINISHED: RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "FW IQ calibration finished\n"); rs->rs_flags &= ~R12A_IQK_RUNNING; break; default: device_printf(sc->sc_dev, "%s: C2H report %u was not handled\n", __func__, buf[0]); } } #else void r12a_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { /* Should not happen. */ device_printf(sc->sc_dev, "%s: called\n", __func__); } void r12a_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) { /* Should not happen. */ device_printf(sc->sc_dev, "%s: called\n", __func__); } #endif int r12a_check_frame_checksum(struct rtwn_softc *sc, struct mbuf *m) { struct r12a_softc *rs = sc->sc_priv; struct r92c_rx_stat *stat; uint32_t rxdw1; stat = mtod(m, struct r92c_rx_stat *); rxdw1 = le32toh(stat->rxdw1); if (rxdw1 & R12A_RXDW1_CKSUM) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: %s/%s checksum is %s\n", __func__, (rxdw1 & R12A_RXDW1_UDP) ? "UDP" : "TCP", (rxdw1 & R12A_RXDW1_IPV6) ? "IPv6" : "IP", (rxdw1 & R12A_RXDW1_CKSUM_ERR) ? "invalid" : "valid"); if (rxdw1 & R12A_RXDW1_CKSUM_ERR) return (-1); if ((rxdw1 & R12A_RXDW1_IPV6) ? (rs->rs_flags & R12A_RXCKSUM6_EN) : (rs->rs_flags & R12A_RXCKSUM_EN)) { m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } return (0); } uint8_t r12a_rx_radiotap_flags(const void *buf) { const struct r92c_rx_stat *stat = buf; uint8_t flags, rate; if (!(stat->rxdw4 & htole32(R12A_RXDW4_SPLCP))) return (0); rate = MS(le32toh(stat->rxdw3), R92C_RXDW3_RATE); if (RTWN_RATE_IS_CCK(rate)) flags = IEEE80211_RADIOTAP_F_SHORTPRE; else flags = IEEE80211_RADIOTAP_F_SHORTGI; return (flags); }