| /* |
| * Copyright 2010 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * Copyright (c) 2008 Atheros Communications Inc. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <sys/byteorder.h> |
| |
| #include "arn_core.h" |
| |
| /* |
| * Setup and link descriptors. |
| * |
| * 11N: we can no longer afford to self link the last descriptor. |
| * MAC acknowledges BA status as long as it copies frames to host |
| * buffer (or rx fifo). This can incorrectly acknowledge packets |
| * to a sender if last desc is self-linked. |
| */ |
| void |
| arn_rx_buf_link(struct arn_softc *sc, struct ath_buf *bf) |
| { |
| struct ath_desc *ds; |
| |
| ds = bf->bf_desc; |
| ds->ds_link = 0; |
| ds->ds_data = bf->bf_dma.cookie.dmac_address; |
| |
| /* virtual addr of the beginning of the buffer. */ |
| ds->ds_vdata = bf->bf_dma.mem_va; |
| |
| /* |
| * setup rx descriptors. The bf_dma.alength here tells the H/W |
| * how much data it can DMA to us and that we are prepared |
| * to process |
| */ |
| (void) ath9k_hw_setuprxdesc(sc->sc_ah, ds, |
| bf->bf_dma.alength, /* buffer size */ |
| 0); |
| |
| if (sc->sc_rxlink == NULL) |
| ath9k_hw_putrxbuf(sc->sc_ah, bf->bf_daddr); |
| else |
| *sc->sc_rxlink = bf->bf_daddr; |
| |
| sc->sc_rxlink = &ds->ds_link; |
| ath9k_hw_rxena(sc->sc_ah); |
| } |
| |
| void |
| arn_setdefantenna(struct arn_softc *sc, uint32_t antenna) |
| { |
| /* XXX block beacon interrupts */ |
| ath9k_hw_setantenna(sc->sc_ah, antenna); |
| sc->sc_defant = (uint8_t)antenna; /* LINT */ |
| sc->sc_rxotherant = 0; |
| } |
| |
| /* |
| * Extend 15-bit time stamp from rx descriptor to |
| * a full 64-bit TSF using the current h/w TSF. |
| */ |
| |
| static uint64_t |
| arn_extend_tsf(struct arn_softc *sc, uint32_t rstamp) |
| { |
| uint64_t tsf; |
| |
| tsf = ath9k_hw_gettsf64(sc->sc_ah); |
| if ((tsf & 0x7fff) < rstamp) |
| tsf -= 0x8000; |
| return ((tsf & ~0x7fff) | rstamp); |
| } |
| |
| static int |
| arn_rx_prepare(struct ath_desc *ds, struct arn_softc *sc) |
| { |
| uint8_t phyerr; |
| |
| if (ds->ds_rxstat.rs_more) { |
| /* |
| * Frame spans multiple descriptors; this cannot happen yet |
| * as we don't support jumbograms. If not in monitor mode, |
| * discard the frame. Enable this if you want to see |
| * error frames in Monitor mode. |
| */ |
| if (sc->sc_ah->ah_opmode != ATH9K_M_MONITOR) |
| goto rx_next; |
| } else if (ds->ds_rxstat.rs_status != 0) { |
| if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) { |
| sc->sc_stats.ast_rx_crcerr++; |
| goto rx_next; /* should ignore? */ |
| } |
| |
| if (ds->ds_rxstat.rs_status & ATH9K_RXERR_FIFO) { |
| sc->sc_stats.ast_rx_fifoerr++; |
| } |
| |
| if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) { |
| sc->sc_stats.ast_rx_phyerr++; |
| phyerr = ds->ds_rxstat.rs_phyerr & 0x1f; |
| sc->sc_stats.ast_rx_phy[phyerr]++; |
| goto rx_next; |
| } |
| |
| if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) { |
| sc->sc_stats.ast_rx_badcrypt++; |
| } |
| |
| /* |
| * Reject error frames with the exception of |
| * decryption and MIC failures. For monitor mode, |
| * we also ignore the CRC error. |
| */ |
| if (sc->sc_ah->ah_opmode == ATH9K_M_MONITOR) { |
| if (ds->ds_rxstat.rs_status & |
| ~(ATH9K_RXERR_DECRYPT | |
| ATH9K_RXERR_MIC | |
| ATH9K_RXERR_CRC)) |
| goto rx_next; |
| } else { |
| if (ds->ds_rxstat.rs_status & |
| ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { |
| goto rx_next; |
| } |
| } |
| } |
| |
| return (1); |
| rx_next: |
| return (0); |
| } |
| |
| |
| static void |
| arn_opmode_init(struct arn_softc *sc) |
| { |
| struct ath_hal *ah = sc->sc_ah; |
| uint32_t rfilt; |
| uint32_t mfilt[2]; |
| ieee80211com_t *ic = (ieee80211com_t *)sc; |
| |
| /* configure rx filter */ |
| rfilt = arn_calcrxfilter(sc); |
| ath9k_hw_setrxfilter(ah, rfilt); |
| |
| /* configure bssid mask */ |
| if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) |
| (void) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); |
| |
| /* configure operational mode */ |
| ath9k_hw_setopmode(ah); |
| |
| /* Handle any link-level address change. */ |
| (void) ath9k_hw_setmac(ah, sc->sc_myaddr); |
| |
| /* calculate and install multicast filter */ |
| mfilt[0] = ~((uint32_t)0); /* LINT */ |
| mfilt[1] = ~((uint32_t)0); /* LINT */ |
| |
| ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); |
| |
| ARN_DBG((ARN_DBG_RECV, "arn: arn_opmode_init(): " |
| "mode = %d RX filter 0x%x, MC filter %08x:%08x\n", |
| ic->ic_opmode, rfilt, mfilt[0], mfilt[1])); |
| } |
| |
| /* |
| * Calculate the receive filter according to the |
| * operating mode and state: |
| * |
| * o always accept unicast, broadcast, and multicast traffic |
| * o maintain current state of phy error reception (the hal |
| * may enable phy error frames for noise immunity work) |
| * o probe request frames are accepted only when operating in |
| * hostap, adhoc, or monitor modes |
| * o enable promiscuous mode according to the interface state |
| * o accept beacons: |
| * - when operating in adhoc mode so the 802.11 layer creates |
| * node table entries for peers, |
| * - when operating in station mode for collecting rssi data when |
| * the station is otherwise quiet, or |
| * - when operating as a repeater so we see repeater-sta beacons |
| * - when scanning |
| */ |
| |
| uint32_t |
| arn_calcrxfilter(struct arn_softc *sc) |
| { |
| #define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | \ |
| ATH9K_RX_FILTER_PHYRADAR) |
| |
| uint32_t rfilt; |
| |
| rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE) | |
| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | |
| ATH9K_RX_FILTER_MCAST; |
| |
| /* If not a STA, enable processing of Probe Requests */ |
| if (sc->sc_ah->ah_opmode != ATH9K_M_STA) |
| rfilt |= ATH9K_RX_FILTER_PROBEREQ; |
| |
| /* Can't set HOSTAP into promiscous mode */ |
| if (((sc->sc_ah->ah_opmode != ATH9K_M_HOSTAP) && |
| (sc->sc_promisc)) || |
| (sc->sc_ah->ah_opmode == ATH9K_M_MONITOR)) { |
| rfilt |= ATH9K_RX_FILTER_PROM; |
| /* ??? To prevent from sending ACK */ |
| rfilt &= ~ATH9K_RX_FILTER_UCAST; |
| } |
| |
| if (sc->sc_ah->ah_opmode == ATH9K_M_STA || |
| sc->sc_ah->ah_opmode == ATH9K_M_IBSS) |
| rfilt |= ATH9K_RX_FILTER_BEACON; |
| |
| /* |
| * If in HOSTAP mode, want to enable reception of PSPOLL |
| * frames & beacon frames |
| */ |
| if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) |
| rfilt |= (ATH9K_RX_FILTER_BEACON | ATH9K_RX_FILTER_PSPOLL); |
| |
| return (rfilt); |
| |
| #undef RX_FILTER_PRESERVE |
| } |
| |
| /* |
| * When block ACK agreement has been set up between station and AP, |
| * Net80211 module will call this function to inform hardware about |
| * informations of this BA agreement. |
| * When AP wants to delete BA agreement that was originated by it, |
| * Net80211 modele will call this function to clean up relevant |
| * information in hardware. |
| */ |
| |
| void |
| arn_ampdu_recv_action(struct ieee80211_node *in, |
| const uint8_t *frm, |
| const uint8_t *efrm) |
| { |
| struct ieee80211com *ic; |
| struct arn_softc *sc; |
| |
| if ((in == NULL) || (frm == NULL) || (ic = in->in_ic) == NULL) { |
| ARN_DBG((ARN_DBG_FATAL, |
| "Unknown AMPDU action or NULL node index\n")); |
| return; |
| } |
| |
| sc = (struct arn_softc *)ic; |
| |
| if (!(sc->sc_flags & SC_OP_RXAGGR)) |
| return; |
| else |
| sc->sc_recv_action(in, frm, efrm); |
| } |
| |
| int |
| arn_startrecv(struct arn_softc *sc) |
| { |
| struct ath_hal *ah = sc->sc_ah; |
| struct ath_buf *bf; |
| |
| /* rx descriptor link set up */ |
| mutex_enter(&sc->sc_rxbuflock); |
| if (list_empty(&sc->sc_rxbuf_list)) |
| goto start_recv; |
| |
| /* clean up rx link firstly */ |
| sc->sc_rxlink = NULL; |
| |
| bf = list_head(&sc->sc_rxbuf_list); |
| while (bf != NULL) { |
| arn_rx_buf_link(sc, bf); |
| bf = list_next(&sc->sc_rxbuf_list, bf); |
| } |
| |
| |
| /* We could have deleted elements so the list may be empty now */ |
| if (list_empty(&sc->sc_rxbuf_list)) |
| goto start_recv; |
| |
| bf = list_head(&sc->sc_rxbuf_list); |
| |
| ath9k_hw_putrxbuf(ah, bf->bf_daddr); |
| ath9k_hw_rxena(ah); |
| |
| start_recv: |
| mutex_exit(&sc->sc_rxbuflock); |
| arn_opmode_init(sc); |
| ath9k_hw_startpcureceive(ah); |
| |
| return (0); |
| } |
| |
| boolean_t |
| arn_stoprecv(struct arn_softc *sc) |
| { |
| struct ath_hal *ah = sc->sc_ah; |
| boolean_t stopped; |
| |
| ath9k_hw_stoppcurecv(ah); |
| ath9k_hw_setrxfilter(ah, 0); |
| stopped = ath9k_hw_stopdmarecv(ah); |
| |
| /* 3ms is long enough for 1 frame ??? */ |
| drv_usecwait(3000); |
| |
| sc->sc_rxlink = NULL; |
| |
| return (stopped); |
| } |
| |
| /* |
| * Intercept management frames to collect beacon rssi data |
| * and to do ibss merges. |
| */ |
| |
| void |
| arn_recv_mgmt(struct ieee80211com *ic, mblk_t *mp, struct ieee80211_node *in, |
| int subtype, int rssi, uint32_t rstamp) |
| { |
| struct arn_softc *sc = (struct arn_softc *)ic; |
| |
| /* |
| * Call up first so subsequent work can use information |
| * potentially stored in the node (e.g. for ibss merge). |
| */ |
| sc->sc_recv_mgmt(ic, mp, in, subtype, rssi, rstamp); |
| |
| ARN_LOCK(sc); |
| switch (subtype) { |
| case IEEE80211_FC0_SUBTYPE_BEACON: |
| /* update rssi statistics */ |
| if (sc->sc_bsync && in == ic->ic_bss && |
| ic->ic_state == IEEE80211_S_RUN) { |
| /* |
| * Resync beacon timers using the tsf of the beacon |
| * frame we just received. |
| */ |
| arn_beacon_config(sc); |
| } |
| /* FALLTHRU */ |
| case IEEE80211_FC0_SUBTYPE_PROBE_RESP: |
| if (ic->ic_opmode == IEEE80211_M_IBSS && |
| ic->ic_state == IEEE80211_S_RUN && |
| (in->in_capinfo & IEEE80211_CAPINFO_IBSS)) { |
| uint64_t tsf = arn_extend_tsf(sc, rstamp); |
| /* |
| * Handle ibss merge as needed; check the tsf on the |
| * frame before attempting the merge. The 802.11 spec |
| * says the station should change it's bssid to match |
| * the oldest station with the same ssid, where oldest |
| * is determined by the tsf. Note that hardware |
| * reconfiguration happens through callback to |
| * ath_newstate as the state machine will go from |
| * RUN -> RUN when this happens. |
| */ |
| if (LE_64(in->in_tstamp.tsf) >= tsf) { |
| ARN_DBG((ARN_DBG_BEACON, "arn: arn_recv_mgmt:" |
| "ibss merge, rstamp %u tsf %lu " |
| "tstamp %lu\n", rstamp, tsf, |
| in->in_tstamp.tsf)); |
| ARN_UNLOCK(sc); |
| ARN_DBG((ARN_DBG_BEACON, "arn_recv_mgmt():" |
| "ibss_merge: rstamp=%d in_tstamp=%02x %02x" |
| " %02x %02x %02x %02x %02x %02x\n", |
| rstamp, in->in_tstamp.data[0], |
| in->in_tstamp.data[1], |
| in->in_tstamp.data[2], |
| in->in_tstamp.data[3], |
| in->in_tstamp.data[4], |
| in->in_tstamp.data[5], |
| in->in_tstamp.data[6], |
| in->in_tstamp.data[7])); |
| (void) ieee80211_ibss_merge(in); |
| return; |
| } |
| } |
| break; |
| } |
| ARN_UNLOCK(sc); |
| } |
| |
| static void |
| arn_printrxbuf(struct ath_buf *bf, int32_t done) |
| { |
| struct ath_desc *ds = bf->bf_desc; |
| const struct ath_rx_status *rs = &ds->ds_rxstat; |
| |
| ARN_DBG((ARN_DBG_RECV, "arn: R (%p %p) %08x %08x %08x " |
| "%08x %08x %08x %c\n", |
| ds, bf->bf_daddr, |
| ds->ds_link, ds->ds_data, |
| ds->ds_ctl0, ds->ds_ctl1, |
| ds->ds_hw[0], ds->ds_hw[1], |
| !done ? ' ' : (rs->rs_status == 0) ? '*' : '!')); |
| } |
| |
| static void |
| arn_rx_handler(struct arn_softc *sc) |
| { |
| #define PA2DESC(_sc, _pa) \ |
| ((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \ |
| ((_pa) - (_sc)->sc_desc_dma.cookie.dmac_address))) |
| |
| ieee80211com_t *ic = (ieee80211com_t *)sc; |
| struct ath_buf *bf; |
| struct ath_hal *ah = sc->sc_ah; |
| struct ath_desc *ds; |
| struct ath_rx_status *rs; |
| mblk_t *rx_mp; |
| struct ieee80211_frame *wh; |
| int32_t len, ngood = 0, loop = 1; |
| uint32_t subtype; |
| int status; |
| int last_rssi = ATH_RSSI_DUMMY_MARKER; |
| struct ath_node *an; |
| struct ieee80211_node *in; |
| uint32_t cur_signal; |
| #ifdef ARN_DBG_AMSDU |
| uint8_t qos; |
| #endif |
| |
| do { |
| mutex_enter(&sc->sc_rxbuflock); |
| bf = list_head(&sc->sc_rxbuf_list); |
| if (bf == NULL) { |
| ARN_DBG((ARN_DBG_RECV, "arn: arn_rx_handler(): " |
| "no buffer\n")); |
| sc->sc_rxlink = NULL; |
| mutex_exit(&sc->sc_rxbuflock); |
| break; |
| } |
| ASSERT(bf->bf_dma.cookie.dmac_address != NULL); |
| ds = bf->bf_desc; |
| |
| /* |
| * Must provide the virtual address of the current |
| * descriptor, the physical address, and the virtual |
| * address of the next descriptor in the h/w chain. |
| * This allows the HAL to look ahead to see if the |
| * hardware is done with a descriptor by checking the |
| * done bit in the following descriptor and the address |
| * of the current descriptor the DMA engine is working |
| * on. All this is necessary because of our use of |
| * a self-linked list to avoid rx overruns. |
| */ |
| status = ath9k_hw_rxprocdesc(ah, ds, |
| bf->bf_daddr, |
| PA2DESC(sc, ds->ds_link), 0); |
| if (status == EINPROGRESS) { |
| struct ath_buf *tbf; |
| struct ath_desc *tds; |
| |
| if (list_is_last(&bf->bf_node, &sc->sc_rxbuf_list)) { |
| ARN_DBG((ARN_DBG_RECV, "arn: arn_rx_handler(): " |
| "List is in last! \n")); |
| sc->sc_rxlink = NULL; |
| break; |
| } |
| |
| tbf = list_object(&sc->sc_rxbuf_list, |
| bf->bf_node.list_next); |
| |
| /* |
| * On some hardware the descriptor status words could |
| * get corrupted, including the done bit. Because of |
| * this, check if the next descriptor's done bit is |
| * set or not. |
| * |
| * If the next descriptor's done bit is set, the current |
| * descriptor has been corrupted. Force s/w to discard |
| * this descriptor and continue... |
| */ |
| |
| tds = tbf->bf_desc; |
| status = ath9k_hw_rxprocdesc(ah, tds, tbf->bf_daddr, |
| PA2DESC(sc, tds->ds_link), 0); |
| if (status == EINPROGRESS) { |
| mutex_exit(&sc->sc_rxbuflock); |
| break; |
| } |
| } |
| list_remove(&sc->sc_rxbuf_list, bf); |
| mutex_exit(&sc->sc_rxbuflock); |
| |
| rs = &ds->ds_rxstat; |
| len = rs->rs_datalen; |
| |
| /* less than sizeof(struct ieee80211_frame) */ |
| if (len < 20) { |
| sc->sc_stats.ast_rx_tooshort++; |
| goto requeue; |
| } |
| |
| /* The status portion of the descriptor could get corrupted. */ |
| if (sc->rx_dmabuf_size < rs->rs_datalen) { |
| arn_problem("Requeued because of wrong rs_datalen\n"); |
| goto requeue; |
| } |
| |
| if (!arn_rx_prepare(ds, sc)) |
| goto requeue; |
| |
| if ((rx_mp = allocb(sc->rx_dmabuf_size, BPRI_MED)) == NULL) { |
| arn_problem("arn: arn_rx_handler(): " |
| "allocing mblk buffer failed.\n"); |
| return; |
| } |
| |
| ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORCPU); |
| bcopy(bf->bf_dma.mem_va, rx_mp->b_rptr, len); |
| |
| rx_mp->b_wptr += len; |
| wh = (struct ieee80211_frame *)rx_mp->b_rptr; |
| |
| if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == |
| IEEE80211_FC0_TYPE_CTL) { |
| /* |
| * Ignore control frame received in promisc mode. |
| */ |
| freemsg(rx_mp); |
| goto requeue; |
| } |
| /* Remove the CRC at the end of IEEE80211 frame */ |
| rx_mp->b_wptr -= IEEE80211_CRC_LEN; |
| |
| #ifdef DEBUG |
| arn_printrxbuf(bf, status == 0); |
| #endif |
| |
| #ifdef ARN_DBG_AMSDU |
| if (IEEE80211_IS_DATA_QOS(wh)) { |
| if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == |
| IEEE80211_FC1_DIR_DSTODS) |
| qos = ((struct ieee80211_qosframe_addr4 *) |
| wh)->i_qos[0]; |
| else |
| qos = |
| ((struct ieee80211_qosframe *)wh)->i_qos[0]; |
| |
| if (qos & IEEE80211_QOS_AMSDU) |
| arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va, |
| len, 1, 1); |
| } |
| #endif /* ARN_DBG_AMSDU */ |
| |
| /* |
| * Locate the node for sender, track state, and then |
| * pass the (referenced) node up to the 802.11 layer |
| * for its use. |
| */ |
| in = ieee80211_find_rxnode(ic, wh); |
| an = ATH_NODE(in); |
| |
| /* |
| * Theory for reporting quality: |
| * |
| * At a hardware RSSI of 45 you will be able to use |
| * MCS 7 reliably. |
| * At a hardware RSSI of 45 you will be able to use |
| * MCS 15 reliably. |
| * At a hardware RSSI of 35 you should be able use |
| * 54 Mbps reliably. |
| * |
| * MCS 7 is the highets MCS index usable by a 1-stream device. |
| * MCS 15 is the highest MCS index usable by a 2-stream device. |
| * |
| * All ath9k devices are either 1-stream or 2-stream. |
| * |
| * How many bars you see is derived from the qual reporting. |
| * |
| * A more elaborate scheme can be used here but it requires |
| * tables of SNR/throughput for each possible mode used. For |
| * the MCS table you can refer to the wireless wiki: |
| * |
| * http://wireless.kernel.org/en/developers/Documentation/ |
| * ieee80211/802.11n |
| */ |
| if (ds->ds_rxstat.rs_rssi != ATH9K_RSSI_BAD && |
| !ds->ds_rxstat.rs_moreaggr) { |
| /* LINTED: E_CONSTANT_CONDITION */ |
| ATH_RSSI_LPF(an->last_rssi, ds->ds_rxstat.rs_rssi); |
| } |
| last_rssi = an->last_rssi; |
| |
| if (last_rssi != ATH_RSSI_DUMMY_MARKER) |
| ds->ds_rxstat.rs_rssi = ATH_EP_RND(last_rssi, |
| ATH_RSSI_EP_MULTIPLIER); |
| |
| if (ds->ds_rxstat.rs_rssi < 0) |
| ds->ds_rxstat.rs_rssi = 0; |
| |
| if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == |
| IEEE80211_FC0_TYPE_MGT) { |
| subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
| if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) |
| sc->sc_halstats.ns_avgbrssi = |
| ds->ds_rxstat.rs_rssi; |
| } |
| |
| /* |
| * signal (13-15) DLADM_WLAN_STRENGTH_EXCELLENT |
| * signal (10-12) DLADM_WLAN_STRENGTH_VERY_GOOD |
| * signal (6-9) DLADM_WLAN_STRENGTH_GOOD |
| * signal (3-5) DLADM_WLAN_STRENGTH_WEAK |
| * signal (0-2) DLADM_WLAN_STRENGTH_VERY_WEAK |
| */ |
| if (rs->rs_rssi == 0) |
| cur_signal = 0; |
| else if (rs->rs_rssi >= 45) |
| cur_signal = MAX_RSSI; |
| else |
| cur_signal = rs->rs_rssi * MAX_RSSI / 45 + 1; |
| |
| /* |
| * Send the frame to net80211 for processing |
| */ |
| if (cur_signal <= 2 && ic->ic_state == IEEE80211_S_RUN) { |
| (void) ieee80211_input(ic, rx_mp, in, |
| (rs->rs_rssi + 10), rs->rs_tstamp); |
| } |
| else |
| (void) ieee80211_input(ic, rx_mp, in, rs->rs_rssi, |
| rs->rs_tstamp); |
| |
| /* release node */ |
| ieee80211_free_node(in); |
| |
| /* |
| * Arrange to update the last rx timestamp only for |
| * frames from our ap when operating in station mode. |
| * This assumes the rx key is always setup when associated. |
| */ |
| if (ic->ic_opmode == IEEE80211_M_STA && |
| rs->rs_keyix != ATH9K_RXKEYIX_INVALID) { |
| ngood++; |
| } |
| |
| /* |
| * change the default rx antenna if rx diversity chooses the |
| * other antenna 3 times in a row. |
| */ |
| if (sc->sc_defant != ds->ds_rxstat.rs_antenna) { |
| if (++sc->sc_rxotherant >= 3) { |
| ath9k_hw_setantenna(sc->sc_ah, |
| ds->ds_rxstat.rs_antenna); |
| sc->sc_defant = ds->ds_rxstat.rs_antenna; |
| sc->sc_rxotherant = 0; |
| } |
| } else { |
| sc->sc_rxotherant = 0; |
| } |
| |
| requeue: |
| mutex_enter(&sc->sc_rxbuflock); |
| list_insert_tail(&sc->sc_rxbuf_list, bf); |
| mutex_exit(&sc->sc_rxbuflock); |
| arn_rx_buf_link(sc, bf); |
| } while (loop); |
| |
| if (ngood) |
| sc->sc_lastrx = ath9k_hw_gettsf64(ah); |
| |
| #undef PA2DESC |
| } |
| |
| uint_t |
| arn_softint_handler(caddr_t data) |
| { |
| struct arn_softc *sc = (struct arn_softc *)data; |
| |
| ARN_LOCK(sc); |
| |
| if (sc->sc_rx_pend) { |
| /* Soft interrupt for this driver */ |
| sc->sc_rx_pend = 0; |
| ARN_UNLOCK(sc); |
| arn_rx_handler(sc); |
| return (DDI_INTR_CLAIMED); |
| } |
| |
| ARN_UNLOCK(sc); |
| |
| return (DDI_INTR_UNCLAIMED); |
| } |