commit c95f835dea4e38b428cca7b61bcd8f28f932d4dc Author: Jiri Slaby Date: Sun Jul 1 11:28:19 2007 +0200 start, stop, config and empty tx diff --git a/ath/if_ath.c b/ath/if_ath.c index 44628a9..0ba0492 100644 --- a/ath/if_ath.c +++ b/ath/if_ath.c @@ -1,9 +1,3 @@ -/* - * #define AR_DEBUG here if you need to debug the ath_pci module (athdebug) - * disable this if not needed because it adds an amount of load - */ -#define AR_DEBUG - /* unaligned little endian access */ #define LE_READ_2(_p) (le16_to_cpu(get_unaligned((__le16 *)(_p)))) #define LE_READ_4(_p) (le32_to_cpu(get_unaligned((__le32 *)(_p)))) @@ -198,230 +192,6 @@ ath_bmiss_tasklet(unsigned long data) } #endif } -#ifdef BLE -static u_int -ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan) -{ -#define N(a) (sizeof(a) / sizeof(a[0])) - static const u_int modeflags[] = { - 0, /* IEEE80211_MODE_AUTO */ - CHANNEL_A, /* IEEE80211_MODE_11A */ - CHANNEL_B, /* IEEE80211_MODE_11B */ - CHANNEL_G, /* IEEE80211_MODE_11G */ - 0, /* IEEE80211_MODE_FH */ - CHANNEL_T, /* IEEE80211_MODE_TURBO_A */ - CHANNEL_108G /* IEEE80211_MODE_TURBO_G */ - }; - enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan); - - KASSERT(mode < N(modeflags), "unexpected phy mode %u", mode); - KASSERT(modeflags[mode] != 0, "mode %u undefined", mode); - return modeflags[mode]; -#undef N -} -#endif -static int -ath_init(struct ieee80211_hw *hw) -{ - struct ath_softc *sc = hw->priv; - struct ath_hal *ah = sc->sc_ah; - enum ath5k_status status; - int error = 0; - - ATH_LOCK(sc); - -// DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, ic->ic_opmode); - - /* - * Stop anything previously setup. This is safe - * whether this is the first time through or not. - */ - ath_stop_locked(hw); -#ifdef BLE - /* - * Change our interface type if we are in monitor mode. - */ - dev->type = (ic->ic_opmode == IEEE80211_M_MONITOR) ? - ARPHRD_IEEE80211_RADIOTAP : ARPHRD_ETHER; - - /* - * The basic interface to setting the hardware in a good - * state is ``reset''. On return the hardware is known to - * be powered up and with interrupts disabled. This must - * be followed by initialization of the appropriate bits - * and then setup of the interrupt mask. - */ - sc->sc_curchan.freq = ic->ic_ibss_chan->ic_freq; - sc->sc_curchan.channel_flags = ath_chan2flags(ic, ic->ic_ibss_chan); -#endif - if (!ath5k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, false, - &status)) { - printk(KERN_ERR "unable to reset hardware; hal status %u\n", - status); - error = -EIO; - goto done; - } - /* - * This is needed only to setup initial state - * but it's best done after a reset. - */ - ath_update_txpow(sc); - - /* - * Likewise this is set during reset so update - * state cached in the driver. - */ - sc->sc_diversity = ath_hal_getdiversity(ah); - - /* - * Setup the hardware after reset: the key cache - * is filled as needed and the receive engine is - * set going. Frame transmit is handled entirely - * in the frame output path; there's nothing to do - * here except setup the interrupt mask. - */ - if (ath_startrecv(sc) != 0) { - printk(KERN_ERR "unable to start recv logic\n"); - error = -EIO; - goto done; - } - - /* - * Enable interrupts. - */ - sc->sc_imask = AR5K_INT_RX | AR5K_INT_TX - | AR5K_INT_RXEOL | AR5K_INT_RXORN - | AR5K_INT_FATAL | AR5K_INT_GLOBAL; // TODO: compiler warning integer overflow in expression -#ifdef BLE - /* - * Enable MIB interrupts when there are hardware phy counters. - * Note we only do this (at the moment) for station mode. - */ - if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) - sc->sc_imask |= AR5K_INT_MIB; - ath5k_hw_set_intr(ah, sc->sc_imask); - - dev->flags |= IFF_RUNNING; - ic->ic_state = IEEE80211_S_INIT; - - /* - * The hardware should be ready to go now so it's safe - * to kick the 802.11 state machine as it's likely to - * immediately call back to us to send mgmt frames. - */ - ni = ic->ic_bss; - ni->ni_chan = ic->ic_ibss_chan; - ath_chan_change(sc, ni->ni_chan); - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - } else - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); -#endif -done: - ATH_UNLOCK(sc); - return error; -} - -static int -ath_stop_locked(struct ieee80211_hw *hw) -{ -#ifdef BLE - struct ath_softc *sc = hw->priv; - struct ath_hal *ah = sc->sc_ah; - - DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u\n", - __func__, sc->sc_invalid); - - ATH_LOCK_ASSERT(sc); - if (dev->flags & IFF_RUNNING) { - /* - * Shutdown the hardware and driver: - * reset 802.11 state machine - * stop output from above - * disable interrupts - * turn off timers - * turn off the radio - * clear transmit machinery - * clear receive machinery - * drain and release tx queues - * reclaim beacon resources - * power down hardware - * - * Note that some of this work is not possible if the - * hardware is gone (invalid). - */ -// ieee80211_new_state(ic, IEEE80211_S_INIT, -1); -// netif_stop_queue(dev); -// if (sc->sc_rawdev_enabled) -// netif_stop_queue(&sc->sc_rawdev); - - dev->flags &= ~IFF_RUNNING; - if (!sc->sc_invalid) { - if (sc->sc_softled) { - del_timer(&sc->sc_ledtimer); - ath5k_hw_set_gpio(ah, sc->sc_ledpin, - !sc->sc_ledon); - sc->sc_blinking = 0; - } - ath5k_hw_set_intr(ah, 0); - } - ath_draintxq(sc); - if (!sc->sc_invalid) { - ath_stoprecv(sc); - ath5k_hw_phy_disable(ah); - } else - sc->sc_rxlink = NULL; - ath_beacon_free(sc); - } -#endif - return 0; -} - -/* - * Stop the device, grabbing the top-level lock to protect - * against concurrent entry through ath_init (which can happen - * if another thread does a system call and the thread doing the - * stop is preempted). - */ -static int -ath_stop(struct ieee80211_hw *hw) -{ - struct ath_softc *sc = hw->priv; - int error; - - ATH_LOCK(sc); - error = ath_stop_locked(hw); - if (error == 0 && !sc->sc_invalid) { - /* - * Set the chip in full sleep mode. Note that we are - * careful to do this only when bringing the interface - * completely to a stop. When the chip is in this state - * it must be carefully woken up or references to - * registers in the PCI clock domain may freeze the bus - * (and system). This varies by chip and is mostly an - * issue with newer parts that go to sleep more quickly. - */ - if (sc->sc_ah->ah_mac_version >= 7 && sc->sc_ah->ah_mac_revision >= 8) { - /* - * XXX - * don't put newer MAC revisions > 7.8 to sleep because - * of the above mentioned problems - */ - DPRINTF(sc, ATH_DEBUG_RESET, - "%s: mac version > 7.8, not putting device to sleep\n", - __func__); - } - else { - DPRINTF(sc, ATH_DEBUG_RESET, - "%s: putting device to full sleep\n", __func__); - ath5k_hw_set_power(sc->sc_ah, AR5K_PM_FULL_SLEEP, - true, 0); - } - } - ATH_UNLOCK(sc); - return error; -} /* * Reset the hardware w/o losing operational state. This is @@ -1467,103 +1237,7 @@ ath_key_update_end(struct ieee80211com *ic) #endif } -/* - * 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 scanning - * o accept any additional packets specified by sc_rxfilter - */ -static u_int32_t -ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ath_hal *ah = sc->sc_ah; - struct net_device *dev = ic->ic_dev; - u_int32_t rfilt; - - rfilt = (ath5k_hw_get_rx_filter(ah) & AR5K_RX_FILTER_PHYERROR) - | AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | AR5K_RX_FILTER_MCAST | AR5K_RX_FILTER_PHYRADAR; - if (ic->ic_opmode != IEEE80211_M_STA && - ic->ic_opmode != IEEE80211_M_AHDEMO) - rfilt |= AR5K_RX_FILTER_PROBEREQ; - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - (dev->flags & IFF_PROMISC)) - rfilt |= AR5K_RX_FILTER_PROM; - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_IBSS || - state == IEEE80211_S_SCAN) - rfilt |= AR5K_RX_FILTER_BEACON; - - if (sc->sc_rawdev_enabled && (sc->sc_rawdev.flags & IFF_UP)) { - /* if the rawdev is up, accept all normal packets */ - rfilt |= (AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROM | - AR5K_RX_FILTER_PROBEREQ); - } - - rfilt |= sc->sc_rxfilter; - - return rfilt; -} - -static void -ath_mode_init(struct net_device *dev) -{ - struct ath_softc *sc = dev->priv; -// struct ieee80211com *ic = &sc->sc_ic; - struct ath_hal *ah = sc->sc_ah; - u_int32_t rfilt, mfilt[2], val; - u_int8_t pos; - struct dev_mc_list *mc; - /* configure rx filter */ -// rfilt = ath_calcrxfilter(sc, ic->ic_state); -// ath5k_hw_set_rx_filter(ah, rfilt); - - /* configure operational mode */ - ath5k_hw_set_opmode(ah); - - /* - * Handle any link-level address change. Note that we only - * need to force ic_myaddr; any other addresses are handled - * as a byproduct of the ifnet code marking the interface - * down then up. - * - * XXX should get from lladdr instead of arpcom but that's more work - */ -// IEEE80211_ADDR_COPY(ic->ic_myaddr, dev->dev_addr); - ath5k_hw_set_lladdr(ah, dev->dev_addr); - - /* calculate and install multicast filter */ - if ((dev->flags & IFF_ALLMULTI) == 0) { - mfilt[0] = mfilt[1] = 0; - for (mc = dev->mc_list; mc; mc = mc->next) { - /* calculate XOR of eight 6bit values */ - val = LE_READ_4(mc->dmi_addr + 0); - pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = LE_READ_4(mc->dmi_addr + 3); - pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - pos &= 0x3f; - mfilt[pos / 32] |= (1 << (pos % 32)); - } - } else { - mfilt[0] = mfilt[1] = ~0; - } - ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); - DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n", - __func__, rfilt, mfilt[0], mfilt[1]); -} #endif /* * Set the slot time based on the current setting. @@ -2222,113 +1896,6 @@ ath_node_getrssi(const struct ieee80211_node *ni) } #endif -static int -ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) -{ - struct ath_hal *ah = sc->sc_ah; - struct sk_buff *skb; - struct ath_desc *ds; - int headroom_needed = 0; - - if (sc->sc_opmode == IEEE80211_IF_TYPE_MNTR) { - headroom_needed = sizeof(struct wlan_ng_prism2_header); - } -#ifdef BLE - else if (sc->sc_rawdev.type == ARPHRD_IEEE80211_PRISM) { - headroom_needed = sizeof(struct wlan_ng_prism2_header); - } else if (sc->sc_rawdev.type == ARPHRD_IEEE80211_RADIOTAP) { - headroom_needed = sizeof(struct ath_rx_radiotap_header); - } -#endif - - /* - * Check if we have enough headroom. If not, just free the skb - * and we'll alloc another one below. - */ - if (bf->bf_skb && skb_headroom(bf->bf_skb) < headroom_needed) { - bus_unmap_single(sc->sc_bdev, - bf->bf_skbaddr, sc->sc_rxbufsize, - BUS_DMA_FROMDEVICE); - dev_kfree_skb(bf->bf_skb); - bf->bf_skb = NULL; - } - - skb = bf->bf_skb; - - if (skb == NULL) { - u_int off; - - /* - * Allocate buffer with headroom_needed space for the - * fake physical layer header at the start. - */ - skb = dev_alloc_skb(sc->sc_rxbufsize + - headroom_needed + - sc->sc_cachelsz - 1); - if (skb == NULL) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: skbuff alloc of size %d failed\n", - __func__, - (int)(sc->sc_rxbufsize - + headroom_needed - + sc->sc_cachelsz - 1)); - sc->sc_stats.ast_rx_nobuf++; - return ENOMEM; - } - /* - * Reserve space for the fake physical layer header. - */ - skb_reserve(skb, headroom_needed); - /* - * Cache-line-align. This is important (for the - * 5210 at least) as not doing so causes bogus data - * in rx'd frames. - */ - off = ((unsigned long) skb->data) % sc->sc_cachelsz; - if (off != 0) - skb_reserve(skb, sc->sc_cachelsz - off); - -// skb->dev = sc->sc_dev; - bf->bf_skb = skb; - bf->bf_skbaddr = bus_map_single(sc->sc_bdev, - skb->data, sc->sc_rxbufsize, BUS_DMA_FROMDEVICE); - if (BUS_DMA_MAP_ERROR(bf->bf_skbaddr)) { - printk(KERN_ERR "%s: DMA mapping failed\n", __func__); - dev_kfree_skb(skb); - bf->bf_skb = NULL; - sc->sc_stats.ast_rx_busdma++; - return ENOMEM; - } - } - - /* - * Setup descriptors. For receive we always terminate - * the descriptor list with a self-linked entry so we'll - * not get overrun under high load (as can happen with a - * 5212 when ANI processing enables PHY error frames). - * - * To insure the last descriptor is self-linked we create - * each descriptor as self-linked and add it to the end. As - * each additional descriptor is added the previous self-linked - * entry is ``fixed'' naturally. This should be safe even - * if DMA is happening. When processing RX interrupts we - * never remove/process the last, self-linked, entry on the - * descriptor list. This insures the hardware always has - * someplace to write a new frame. - */ - ds = bf->bf_desc; - ds->ds_link = bf->bf_daddr; /* link to self */ - ds->ds_data = bf->bf_skbaddr; - ath5k_hw_setup_rx_desc(ah, ds, - skb_tailroom(skb), /* buffer size */ - 0); - - if (sc->sc_rxlink != NULL) - *sc->sc_rxlink = bf->bf_daddr; - sc->sc_rxlink = &ds->ds_link; - return 0; -} - #ifdef BLE /* * Add additional headers to a transmitted frame and netif_rx it on @@ -3840,297 +3407,8 @@ ath_tx_timeout(struct net_device *dev) ath_init(dev); } -static void -ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) -{ -#ifdef AR_DEBUG - struct ath_hal *ah = sc->sc_ah; -#endif - struct ath_buf *bf; - - /* - * NB: this assumes output has been stopped and - * we do not need to block ath_tx_tasklet - */ - for (;;) { - ATH_TXQ_LOCK_BH(txq); -// bf = STAILQ_FIRST(&txq->axq_q); - bf = NULL; - if (bf == NULL) { - txq->axq_link = NULL; - ATH_TXQ_UNLOCK_BH(txq); - break; - } -// ATH_TXQ_REMOVE_HEAD(txq, bf_list); - ATH_TXQ_UNLOCK_BH(txq); -#ifdef AR_DEBUG - if (sc->sc_debug & ATH_DEBUG_RESET) - ath_printtxbuf(bf, ah->ah_proc_tx_desc(ah, - bf->bf_desc) == AR5K_OK); -#endif /* AR_DEBUG */ - bus_unmap_single(sc->sc_bdev, - bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); - dev_kfree_skb(bf->bf_skb); - bf->bf_skb = NULL; -#ifdef BLE - ni = bf->bf_node; - bf->bf_node = NULL; - if (ni != NULL) { - /* - * Reclaim node reference. - */ - ieee80211_free_node(ni); - } -#endif - ATH_TXBUF_LOCK_BH(sc); -// STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); - ATH_TXBUF_UNLOCK_BH(sc); - } -} - -static void -ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) -{ - struct ath_hal *ah = sc->sc_ah; - - ath5k_hw_stop_tx_dma(ah, txq->axq_qnum); - DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %x, link %p\n", - __func__, txq->axq_qnum, - ath5k_hw_get_tx_buf(ah, txq->axq_qnum), txq->axq_link); -} - -/* - * Drain the transmit queues and reclaim resources. - */ -static void -ath_draintxq(struct ath_softc *sc) -{ - struct ath_hal *ah = sc->sc_ah; - int i; - - /* XXX return value */ - if (!sc->sc_invalid) { - /* don't touch the hardware if marked invalid */ - (void) ath5k_hw_stop_tx_dma(ah, sc->sc_bhalq); - DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__, - ath5k_hw_get_tx_buf(ah, sc->sc_bhalq)); - for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) { - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_stopdma(sc, &sc->sc_txq[i]); - } - } -// sc->sc_dev->trans_start = jiffies; - -// netif_start_queue(sc->sc_dev); // TODO: needed here? - for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) { - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_draintxq(sc, &sc->sc_txq[i]); - } - sc->sc_tx_timer = 0; -} - -/* - * Disable the receive h/w in preparation for a reset. - */ -static void -ath_stoprecv(struct ath_softc *sc) -{ -#define PA2DESC(_sc, _pa) \ - ((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \ - ((_pa) - (_sc)->sc_desc_daddr))) - struct ath_hal *ah = sc->sc_ah; - - ath5k_hw_stop_pcu_recv(ah); /* disable PCU */ - ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ - ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ - mdelay(3); /* 3ms is long enough for 1 frame */ -#ifdef AR_DEBUG - if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { // TODO: compiler warns integer overflow -// struct ath_buf *bf; - - printk("%s: rx queue %x, link %p\n", __func__, - ath5k_hw_get_rx_buf(ah), sc->sc_rxlink); -#ifdef BLE - STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - struct ath_desc *ds = bf->bf_desc; - enum ath5k_status status = ah->ah_proc_rx_desc(ah, ds, - bf->bf_daddr, PA2DESC(sc, ds->ds_link)); - if (status == AR5K_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) - ath_printrxbuf(bf, status == AR5K_OK); - } -#endif - } -#endif - sc->sc_rxlink = NULL; /* just in case */ -#undef PA2DESC -} -#endif -/* - * Enable the receive h/w following a reset. - */ -static int -ath_startrecv(struct ath_softc *sc) -{ - struct ath_hal *ah = sc->sc_ah; -// struct net_device *dev = ic->ic_dev; - struct ath_buf *bf; - - /* - * Cisco's VPN software requires that drivers be able to - * receive encapsulated frames that are larger than the MTU. - * Since we can't be sure how large a frame we'll get, setup - * to handle the larges on possible. If you're not using the - * VPN driver and want to save memory, define ATH_ENFORCE_MTU - * and you'll allocate only what you really need. - */ -#ifdef ATH_ENFORCE_MTU - if (sc->sc_ic.ic_opmode == IEEE80211_M_MONITOR) { - sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); - } else { - sc->sc_rxbufsize = roundup(sizeof(struct ieee80211_frame) + - dev->mtu + IEEE80211_CRC_LEN + - (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + - IEEE80211_WEP_CRCLEN), sc->sc_cachelsz); - } -#else - sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); #endif #ifdef BLE - DPRINTF(sc, ATH_DEBUG_RESET, "%s: mtu %u cachelsz %u rxbufsize %u\n", - __func__, dev->mtu, sc->sc_cachelsz, sc->sc_rxbufsize); - - sc->sc_rxlink = NULL; - STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - int error = ath_rxbuf_init(sc, bf); - if (error != 0) { - DPRINTF(sc, ATH_DEBUG_RECV, - "%s: ath_rxbuf_init failed %d\n", - __func__, error); - return error; - } - } - - bf = STAILQ_FIRST(&sc->sc_rxbuf); -#endif - bf = NULL; - ath5k_hw_put_rx_buf(ah, bf->bf_daddr); - ath5k_hw_start_rx(ah); /* enable recv descriptors */ -// ath_mode_init(dev); /* set filters, etc. */ - ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ - return 0; -} - -#ifdef BLE -/* - * Update internal state after a channel change. - */ -static void -ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) -{ - unsigned int mode; -// u_int16_t flags; - - /* - * Change channels and update the h/w rate map - * if we're switching; e.g. 11a to 11b/g. - */ -// mode = ieee80211_chan2mode(ic, chan); - if (mode != sc->sc_curmode) - ath_setcurmode(sc, mode); - /* - * Update BPF state. NB: ethereal et. al. don't handle - * merged flags well so pick a unique mode for their use. - */ - if (IEEE80211_IS_CHAN_A(chan)) - flags = IEEE80211_CHAN_A; - /* XXX 11g schizophrenia */ - else if (IEEE80211_IS_CHAN_G(chan) || - IEEE80211_IS_CHAN_PUREG(chan)) - flags = IEEE80211_CHAN_G; - else - flags = IEEE80211_CHAN_B; - if (IEEE80211_IS_CHAN_T(chan)) - flags |= CHANNEL_TURBO; -} - -/* - * Set/change channels. If the channel is really being changed, - * it's done by reseting the chip. To accomplish this we must - * first cleanup any pending DMA, then restart stuff after a la - * ath_init. - */ -static int -ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath5k_channel hchan; - - /* - * Convert to a HAL channel description with - * the flags constrained to reflect the current - * operating mode. - */ -#ifdef BLE - if (chan == IEEE80211_CHAN_ANYC) { - return 0; - } - hchan.freq = chan->freq; - hchan.channel_flags = ath_chan2flags(ic, chan); -#endif - - DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n", - __func__, - ath_hal_mhz2ieee(sc->sc_curchan.freq, - sc->sc_curchan.channel_flags), - sc->sc_curchan.freq, - ath_hal_mhz2ieee(hchan.freq, hchan.channel_flags), hchan.freq); - if (hchan.freq != sc->sc_curchan.freq || - hchan.channel_flags != sc->sc_curchan.channel_flags) { - enum ath5k_status status; - - /* - * To switch channels clear any pending DMA operations; - * wait long enough for the RX fifo to drain, reset the - * hardware at the new frequency, and then re-enable - * the relevant bits of the h/w. - */ - ath5k_hw_set_intr(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* clear pending tx frames */ - ath_stoprecv(sc); /* turn off frame recv */ - if (!ath5k_hw_reset(ah, sc->sc_opmode, &hchan, true, &status)) { - printk(KERN_ERR "ath_chan_set: unable to reset " - "channel %u (%u Mhz)\n", 0, -// ieee80211_chan2ieee(ic, chan), - chan->freq); - return EIO; - } - sc->sc_curchan = hchan; - ath_update_txpow(sc); /* update tx power state */ - sc->sc_diversity = ath_hal_getdiversity(ah); - - /* - * Re-enable rx framework. - */ - if (ath_startrecv(sc) != 0) { - printk(KERN_ERR "ath_chan_set: unable to restart recv " - "logic\n"); - return EIO; - } - - /* - * Change channels and update the h/w rate map - * if we're switching; e.g. 11a to 11b/g. - */ -// ic->ic_ibss_chan = chan; - ath_chan_change(sc, chan); - - /* - * Re-enable interrupts. - */ - ath5k_hw_set_intr(ah, sc->sc_imask); - } - return 0; -} #endif static void ath_next_scan(unsigned long arg) @@ -4437,26 +3715,6 @@ ath_led_event(struct ath_softc *sc, int event) } } #endif -static void -ath_update_txpow(struct ath_softc *sc) -{ -#ifdef BLE - struct ath_hal *ah = sc->sc_ah; - u_int32_t txpow; - - if (sc->sc_curtxpow != ic->ic_txpowlimit) { - ath5k_hw_set_txpower_limit(ah, ic->ic_txpowlimit); - /* read back in case value is clamped */ - (void)ath_hal_gettxpowlimit(ah, &txpow); - ic->ic_txpowlimit = sc->sc_curtxpow = txpow; - } - /* - * Fetch max tx power level for status requests. - */ - (void)ath_hal_getmaxtxpow(sc->sc_ah, &txpow); - ic->ic_bss->ni_txpower = txpow; -#endif -} #ifdef BLE static int @@ -4527,34 +3785,6 @@ ath_vlan_kill_vid(struct net_device *dev, unsigned short vid) } #endif /* IEEE80211_VLAN_TAG_USED */ #endif -#ifdef AR_DEBUG -static void -ath_printrxbuf(struct ath_buf *bf, int done) -{ - struct ath_desc *ds = bf->bf_desc; - - printk("R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n", - ds, (unsigned long long)bf->bf_daddr, - ds->ds_link, ds->ds_data, - ds->ds_ctl0, ds->ds_ctl1, - ds->ds_hw[0], ds->ds_hw[1], - !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'); -} -#ifdef BLE -static void -ath_printtxbuf(struct ath_buf *bf, int done) -{ - struct ath_desc *ds = bf->bf_desc; - - printk("T (%p %llx) %08x %08x %08x %08x %08x %08x %08x %08x %c\n", - ds, (unsigned long long)bf->bf_daddr, - ds->ds_link, ds->ds_data, - ds->ds_ctl0, ds->ds_ctl1, - ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], - !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'); -} -#endif -#endif /* AR_DEBUG */ #ifdef BLE /* * Return netdevice statistics. diff --git a/ath/if_ath_pci.c b/ath/if_ath_pci.c index b2fcefe..0fc213e 100644 --- a/ath/if_ath_pci.c +++ b/ath/if_ath_pci.c @@ -37,6 +37,7 @@ #include "if_athvar.h" #define ATH_DEBUG_MODES 0 /* Show found modes in the log? */ +#define AR_DEBUG 1 #define KASSERT(exp, msg...) do { \ if (unlikely(!(exp))) { \ @@ -82,6 +83,11 @@ MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use"); module_param(xchanmode, int, 0); MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode"); +#ifdef AR_DEBUG +static unsigned int ath_debug; +module_param_named(debug, ath_debug, uint, 0); +#endif + /* * User a static table of PCI id's for now. While this is the * "new way" to do things, we may want to switch back to having @@ -109,30 +115,544 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = { }; MODULE_DEVICE_TABLE(pci, ath_pci_id_table); -static int ath_open(struct ieee80211_hw *hw) +#ifdef AR_DEBUG +static void ath_printrxbuf(struct ath_buf *bf, int done) { - printk("%s\n", __FUNCTION__); + struct ath_desc *ds = bf->desc; + + printk("R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n", + ds, (unsigned long long)bf->daddr, + ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], + !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'); +} + +static void ath_printtxbuf(struct ath_buf *bf, int done) +{ + struct ath_desc *ds = bf->desc; + + printk("T (%p %llx) %08x %08x %08x %08x %08x %08x %08x %08x %c\n", + ds, (unsigned long long)bf->daddr, + ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], + !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'); +} +#endif + +static int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hw *ah = sc->ah; + struct sk_buff *skb = bf->skb; + struct ath_desc *ds; + + if (skb == NULL) { + unsigned int off; + + /* + * Allocate buffer with headroom_needed space for the + * fake physical layer header at the start. + */ + skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1); + if (skb == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, "%s: skbuff alloc of " + "size %u failed\n", __func__, + sc->rxbufsize + sc->cachelsz - 1); + sc->stats.ast_rx_nobuf++; + return -ENOMEM; + } + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + off = ((unsigned long)skb->data) % sc->cachelsz; + if (off != 0) + skb_reserve(skb, sc->cachelsz - off); + + bf->skb = skb; + bf->skbaddr = pci_map_single(sc->pdev, + skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(bf->skbaddr)) { + printk(KERN_ERR "%s: DMA mapping failed\n", __func__); + dev_kfree_skb(skb); + bf->skb = NULL; + sc->stats.ast_rx_busdma++; + return -ENOMEM; + } + } + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + ds = bf->desc; + ds->ds_link = bf->daddr; /* link to self */ + ds->ds_data = bf->skbaddr; + ath5k_hw_setup_rx_desc(ah, ds, + skb_tailroom(skb), /* buffer size */ + 0); + + if (sc->rxlink != NULL) + *sc->rxlink = bf->daddr; + sc->rxlink = &ds->ds_link; return 0; } -static int ath_stop(struct ieee80211_hw *hw) +/* + * 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 scanning + * o accept any additional packets specified by sc_rxfilter + */ +static u32 ath_calcrxfilter(struct ath_softc *sc) { - printk("%s\n", __FUNCTION__); + struct ath_hw *ah = sc->ah; + u32 rfilt; + + rfilt = (ath5k_hw_get_rx_filter(ah) & AR5K_RX_FILTER_PHYERROR) | + AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | + AR5K_RX_FILTER_MCAST | AR5K_RX_FILTER_PHYRADAR; +#ifdef BLE + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_AHDEMO) + rfilt |= AR5K_RX_FILTER_PROBEREQ; + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + (dev->flags & IFF_PROMISC)) + rfilt |= AR5K_RX_FILTER_PROM; + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_IBSS || + state == IEEE80211_S_SCAN) +#endif + rfilt |= AR5K_RX_FILTER_BEACON; + + return rfilt; +} + +static void ath_mode_init(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + u32 rfilt; + + /* configure rx filter */ + rfilt = ath_calcrxfilter(sc); + ath5k_hw_set_rx_filter(ah, rfilt); + + if (ath_hal_hasbssidmask(ah)) + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + + /* configure operational mode */ + ath5k_hw_set_opmode(ah); + + ath5k_hw_set_mcast_filter(ah, 0, 0); + DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); +} + +/* + * Enable the receive h/w following a reset. + */ +static int ath_startrecv(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + struct list_head *pos; + struct ath_buf *bf; + int ret; + + sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: cachelsz %u rxbufsize %u\n", + __func__, sc->cachelsz, sc->rxbufsize); + + sc->rxlink = NULL; + list_for_each(pos, &sc->rxbuf) { + bf = list_entry(pos, struct ath_buf, list); + ret = ath_rxbuf_init(sc, bf); + if (ret != 0) + goto err; + } + + bf = list_first_entry(&sc->rxbuf, struct ath_buf, list); + ath5k_hw_put_rx_buf(ah, bf->daddr); + ath5k_hw_start_rx(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ + return 0; +err: + return ret; } -static int ath_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +static inline void ath_update_txpow(struct ath_softc *sc) { - printk("%s\n", __FUNCTION__); + ath5k_hw_set_txpower_limit(sc->ah, 0); +} + +static int ath_stop_locked(struct ath_softc *); + +static int ath_init(struct ath_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + +// DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, sc->opmode); + + /* + * Stop anything previously setup. This is safe + * no matter this is the first time through or not. + */ + ath_stop_locked(sc); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->curchan = sc->hw->conf.chan; + ret = ath5k_hw_reset(sc->ah, IEEE80211_IF_TYPE_STA, sc->curchan, false); + if (ret) { + printk(KERN_ERR "unable to reset hardware: %d\n", ret); + goto done; + } + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ + ath_update_txpow(sc); + + /* + * Setup the hardware after reset: the key cache + * is filled as needed and the receive engine is + * set going. Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + ret = ath_startrecv(sc); + if (ret) + goto done; + + /* + * Enable interrupts. + */ + sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL | AR5K_INT_RXORN + | AR5K_INT_FATAL | AR5K_INT_GLOBAL; + + ath5k_hw_set_intr(sc->ah, sc->imask); + + ret = 0; +done: + mutex_unlock(&sc->lock); + return ret; +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void ath_stoprecv(struct ath_softc *sc) +{ +#define PA2DESC(_sc, _pa) ((struct ath_desc *)((caddr_t)(_sc)->desc + \ + ((_pa) - (_sc)->desc_daddr))) + struct ath_hw *ah = sc->ah; + + ath5k_hw_stop_pcu_recv(ah); /* disable PCU */ + ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ + ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ + mdelay(3); /* 3ms is long enough for 1 frame */ +#ifdef AR_DEBUG + if (sc->debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { // TODO: compiler warns integer overflow + struct list_head *pos; + struct ath_desc *ds; + struct ath_buf *bf; + int status; + + printk(KERN_DEBUG "%s: rx queue %x, link %p\n", __func__, + ath5k_hw_get_rx_buf(ah), sc->rxlink); + + list_for_each(pos, &sc->rxbuf) { + bf = list_entry(pos, struct ath_buf, list); + ds = bf->desc; + status = ah->ah_proc_rx_desc(ah, ds, bf->daddr, + PA2DESC(sc, ds->ds_link)); + if (!status || (sc->debug & ATH_DEBUG_FATAL)) + ath_printrxbuf(bf, status == 0); + } + } +#endif + sc->rxlink = NULL; /* just in case */ +#undef PA2DESC +} + +static void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) +{ +#ifdef AR_DEBUG + struct ath_hw *ah = sc->ah; +#endif + struct ath_buf *bf; + + /* + * NB: this assumes output has been stopped and + * we do not need to block ath_tx_tasklet + */ + for (;;) { + spin_lock_bh(&txq->axq_lock); + if (list_empty(&txq->axq_q)) { + txq->axq_link = NULL; + spin_unlock_bh(&txq->axq_lock); + break; + } + bf = list_first_entry(&txq->axq_q, struct ath_buf, list); + list_del(&bf->list); + spin_unlock_bh(&txq->axq_lock); +#ifdef AR_DEBUG + if (sc->debug & ATH_DEBUG_RESET) + ath_printtxbuf(bf, !ah->ah_proc_tx_desc(ah, bf->desc)); +#endif + pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(bf->skb); + bf->skb = NULL; + + spin_lock_bh(&sc->txbuflock); + list_add_tail(&bf->list, &sc->txbuf); + spin_unlock_bh(&sc->txbuflock); + } +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void ath_draintxq(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + int i; + + /* XXX return value */ + if (!sc->invalid) { + /* don't touch the hardware if marked invalid */ + (void)ath5k_hw_stop_tx_dma(ah, sc->bhalq); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__, + ath5k_hw_get_tx_buf(ah, sc->bhalq)); + for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) + if (test_bit(i, sc->txqsetup)) { + ath5k_hw_stop_tx_dma(ah, sc->txq[i].axq_qnum); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: txq [%u] %x, " + "link %p\n", __func__, + sc->txq[i].axq_qnum, + ath5k_hw_get_tx_buf(ah, + sc->txq[i].axq_qnum), + sc->txq[i].axq_link); + } + } + ieee80211_start_queues(sc->hw); /* XXX move to callers */ + + for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) + if (test_bit(i, sc->txqsetup)) + ath_tx_draintxq(sc, &sc->txq[i]); + +// sc->tx_timer = 0; +} + +static int ath_stop_locked(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u\n", __func__, sc->invalid); + + /* + * Shutdown the hardware and driver: + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + ieee80211_stop_queues(sc->hw); + + if (!sc->invalid) { + if (sc->softled) { +#ifdef BLE + del_timer(&sc->ledtimer); + ath5k_hw_set_gpio(ah, sc->ledpin, !sc->ledon); + sc->blinking = 0; +#endif + } + ath5k_hw_set_intr(ah, 0); + } + ath_draintxq(sc); + if (!sc->invalid) { + ath_stoprecv(sc); + ath5k_hw_phy_disable(ah); + } else + sc->rxlink = NULL; +// ath_beacon_free(sc); + return 0; } -static int ath_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int ath_stop_hw(struct ath_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + ret = ath_stop_locked(sc); + if (ret == 0 && !sc->invalid) { + /* + * Set the chip in full sleep mode. Note that we are + * careful to do this only when bringing the interface + * completely to a stop. When the chip is in this state + * it must be carefully woken up or references to + * registers in the PCI clock domain may freeze the bus + * (and system). This varies by chip and is mostly an + * issue with newer parts that go to sleep more quickly. + */ + if (sc->ah->ah_mac_version >= 7 && sc->ah->ah_mac_revision >= 8) { + /* + * XXX + * don't put newer MAC revisions > 7.8 to sleep because + * of the above mentioned problems + */ + DPRINTF(sc, ATH_DEBUG_RESET, "%s: mac version > 7.8, " + "not putting device to sleep\n", __func__); + } + else { + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: putting device to full sleep\n", __func__); + ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); + } + } + mutex_unlock(&sc->lock); + return ret; +} + +/* + * Set/change channels. If the channel is really being changed, + * it's done by reseting the chip. To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * ath_init. + */ +static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) +{ + struct ath_hw *ah = sc->ah; + int ret; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n", + __func__, sc->curchan->chan, sc->curchan->freq, + chan->chan, chan->freq); + + if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) { + /* + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath5k_hw_set_intr(ah, 0); /* disable interrupts */ + ath_draintxq(sc); /* clear pending tx frames */ + ath_stoprecv(sc); /* turn off frame recv */ + ret = ath5k_hw_reset(ah, IEEE80211_IF_TYPE_STA, chan, true); + if (ret) { + printk(KERN_ERR "%s: unable to reset channel %u " + "(%u Mhz)\n", __func__, chan->chan, chan->freq); + return ret; + } + sc->curchan = chan; + ath_update_txpow(sc); + + /* + * Re-enable rx framework. + */ + ret = ath_startrecv(sc); + if (ret) { + printk(KERN_ERR "%s: unable to restart recv logic\n", + __func__); + return ret; + } + + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + */ +// ath_chan_change(sc, chan); + + /* + * Re-enable interrupts. + */ + ath5k_hw_set_intr(ah, sc->imask); + } + + return 0; +} + +static int ath_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) { printk("%s\n", __FUNCTION__); + + dev_kfree_skb_any(skb); + return 0; } +static int ath_open(struct ieee80211_hw *hw) +{ + printk("%s\n", __FUNCTION__); + + return ath_init(hw->priv); +} + +static int ath_stop(struct ieee80211_hw *hw) +{ + printk("%s\n", __FUNCTION__); + + return ath_stop_hw(hw->priv); +} + +static int ath_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct ath_softc *sc = hw->priv; + + sc->curmode = conf->phymode; + + return ath_chan_set(sc, conf->chan); +} + static int ath_conf_tx(struct ieee80211_hw *hw, int queue, const struct ieee80211_tx_queue_params *params) { @@ -141,7 +661,7 @@ static int ath_conf_tx(struct ieee80211_hw *hw, int queue, } static struct ieee80211_ops ath_hw_ops = { - .tx = NULL, + .tx = ath_tx, .reset = NULL, .open = ath_open, .stop = ath_stop, @@ -150,7 +670,6 @@ static struct ieee80211_ops ath_hw_ops = { .config = ath_config, .config_interface = NULL, .set_key = NULL, - .hw_scan = ath_hw_scan, .get_stats = NULL, .conf_tx = ath_conf_tx, /* .get_tx_stats = , @@ -168,7 +687,6 @@ static irqreturn_t ath_intr(int irq, void *dev_id) if (sc->invalid || !ath5k_hw_is_intr_pending(ah)) return IRQ_NONE; -return IRQ_HANDLED; do { /* @@ -177,8 +695,9 @@ return IRQ_HANDLED; * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ - ath5k_hw_get_isr(ah, &status); /* NB: clears ISR too */ - DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); + ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ + DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x/0x%x\n", __func__, + status, sc->imask); status &= sc->imask; /* discard unasked for bits */ if (status & AR5K_INT_FATAL) { /* @@ -189,11 +708,11 @@ return IRQ_HANDLED; */ sc->stats.ast_hardware++; ath5k_hw_set_intr(ah, 0); /* disable intrs until rst */ - tasklet_schedule(&sc->fataltq); +// tasklet_schedule(&sc->fataltq); } else if (status & AR5K_INT_RXORN) { sc->stats.ast_rxorn++; ath5k_hw_set_intr(ah, 0); /* disable intrs until rst */ - tasklet_schedule(&sc->rxorntq); +// tasklet_schedule(&sc->rxorntq); } else { if (status & AR5K_INT_SWBA) { /* @@ -218,36 +737,23 @@ return IRQ_HANDLED; /* bump tx trigger level */ ath5k_hw_update_tx_triglevel(ah, true); } - if (status & AR5K_INT_RX) +/* if (status & AR5K_INT_RX) tasklet_schedule(&sc->rxtq); if (status & AR5K_INT_TX) tasklet_schedule(&sc->txtq); if (status & AR5K_INT_BMISS) { sc->stats.ast_bmiss++; tasklet_schedule(&sc->bmisstq); - } + }*/ if (status & AR5K_INT_MIB) { sc->stats.ast_mib++; - /* - * Disable interrupts until we service the MIB - * interrupt; otherwise it will continue to fire. - */ - ath5k_hw_set_intr(ah, 0); - /* - * Let the hal handle the event. We assume it will - * clear whatever condition caused the interrupt. - */ -#ifdef BLE - ath5k_hw_proc_mib_event(ah, - &ATH_NODE(sc->sc_ic.ic_bss)->an_halstats); -#endif - ath5k_hw_set_intr(ah, sc->imask); + /* TODO */ } } } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); - if (!counter) - printk(KERN_WARNING "ath: too much interrupts, giving up for " + if (!counter && printk_ratelimit()) + printk(KERN_WARNING "ath: too many interrupts, giving up for " "now\n"); return IRQ_HANDLED; @@ -571,14 +1077,14 @@ err: sc->desc = NULL; return ret; } -#if 0 + static void ath_descdma_cleanup(struct ath_softc *sc, struct pci_dev *pdev, struct list_head *head) { struct list_head *pos; struct ath_buf *bf; - list_for_each(pos, head, bf_list){ + list_for_each(pos, head) { bf = list_entry(pos, struct ath_buf, list); if (bf->skb) { pci_unmap_single(pdev, bf->skbaddr, sc->rxbufsize, @@ -590,14 +1096,13 @@ static void ath_descdma_cleanup(struct ath_softc *sc, struct pci_dev *pdev, INIT_LIST_HEAD(head); } -#endif + static void ath_desc_free(struct ath_softc *sc, struct pci_dev *pdev) { -#ifdef BLE - ath_descdma_cleanup(sc, pdev, sc->bbuf); - ath_descdma_cleanup(sc, pdev, sc->txbuf); - ath_descdma_cleanup(sc, pdev, sc->rxbuf); -#endif + ath_descdma_cleanup(sc, pdev, &sc->bbuf); + ath_descdma_cleanup(sc, pdev, &sc->txbuf); + ath_descdma_cleanup(sc, pdev, &sc->rxbuf); + /* Free memory associated with all descriptors */ pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); @@ -875,6 +1380,10 @@ static int ath_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) ath5k_hw_get_lladdr(ah, mac); SET_IEEE80211_PERM_ADDR(hw, mac); + if (ath_hal_hasbssidmask(ah)) { + memset(sc->bssidmask, 0xff, ETH_ALEN); + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + } ret = ieee80211_register_hw(hw); if (ret) { @@ -898,7 +1407,7 @@ static void ath_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; -// ath_stop(hw); + ath_stop_hw(sc); /* XXX needed? */ /* * NB: the order of these is important: * o call the 802.11 layer before detaching the hal to @@ -1017,6 +1526,7 @@ static int __devinit ath_pci_probe(struct pci_dev *pdev, * Mark the device as detached to avoid processing * interrupts until setup is complete. */ + sc->debug = ath_debug; sc->invalid = 1; sc->iobase = mem; sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ @@ -1096,7 +1606,7 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) if (sc->softled) ath5k_hw_set_gpio(sc->ah, sc->ledpin, 1); -// ath_stop(hw); + ath_stop_hw(sc); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -1126,7 +1636,7 @@ static int ath_pci_resume(struct pci_dev *pdev) */ pci_write_config_byte(pdev, 0x41, 0); -// ath_init(hw); + ath_init(sc); if (sc->softled) { ath5k_hw_set_gpio_output(sc->ah, sc->ledpin); ath5k_hw_set_gpio(sc->ah, sc->ledpin, 0); diff --git a/ath/if_athvar.h b/ath/if_athvar.h index c58286c..1b7d2c5 100644 --- a/ath/if_athvar.h +++ b/ath/if_athvar.h @@ -45,6 +45,7 @@ #include #include #include +#include #include "ath5k.h" #include "if_athioctl.h" @@ -132,7 +133,7 @@ struct ath_txq { u_int axq_qnum; /* hardware q number */ u_int axq_depth; /* queue depth (stat only) */ u_int axq_intrcnt; /* interrupt count */ - u32 *axq_link; /* link ptr in last TX desc */ + u32 *axq_link; /* link ptr in last TX desc */ struct list_head axq_q; /* transmit queue */ spinlock_t axq_lock; /* lock on q and link */ /* @@ -150,15 +151,6 @@ struct ath_txq { */ }; -#define ATH_TXQ_LOCK_INIT(_sc, _tq) spin_lock_init(&(_tq)->axq_lock) -#define ATH_TXQ_LOCK_DESTROY(_tq) -#define ATH_TXQ_LOCK(_tq) spin_lock(&(_tq)->axq_lock) -#define ATH_TXQ_UNLOCK(_tq) spin_unlock(&(_tq)->axq_lock) -#define ATH_TXQ_LOCK_BH(_tq) spin_lock_bh(&(_tq)->axq_lock) -#define ATH_TXQ_UNLOCK_BH(_tq) spin_unlock_bh(&(_tq)->axq_lock) -#define ATH_TXQ_LOCK_ASSERT(_tq) \ - KASSERT(spin_is_locked(&(_tq)->axq_lock), ("txq not locked!")) - #define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ (_tq)->axq_depth++; \ @@ -175,6 +167,7 @@ struct ath_txq { #endif struct ath_softc { + struct pci_dev *pdev; /* for dma mapping */ void __iomem *iobase; /* address of the device */ struct mutex lock; /* dev-level lock */ // struct net_device_stats sc_devstats; /* device statistics */ @@ -228,10 +221,10 @@ struct ath_softc { const struct ath5k_rate_table *sc_rates[NUM_IEEE80211_MODES]; const struct ath5k_rate_table *sc_currates; /* current rate table */ #endif - unsigned int curmode; /* current phy mode */ + unsigned int curmode; /* current phy mode */ + struct ieee80211_channel *curchan; /* current h/w channel */ #ifdef BLE u16 sc_curtxpow; /* current tx power limit */ - struct ieee80211_channel sc_curchan; /* current h/w channel */ u8 sc_rixmap[256]; /* IEEE to h/w rate table ix */ #endif struct { @@ -251,6 +244,7 @@ struct ath_softc { u8 sc_keymap[ATH_KEYBYTES];/* key use bit map */ struct ieee80211_node *sc_keyixmap[ATH_KEYMAX];/* key ix->node map */ #endif + u8 bssidmask[ETH_ALEN]; u_int ledpin; /* GPIO pin for driving LED */ #ifdef BLE u_int sc_ledon; /* pin setting for LED on */ @@ -260,7 +254,6 @@ struct ath_softc { u8 sc_txrate; /* current tx rate for LED */ u16 sc_ledoff; /* off time for current blink */ struct timer_list sc_ledtimer; /* led off timer */ - u32 sc_rxfilter; union { struct ath_tx_radiotap_header th; @@ -276,9 +269,8 @@ struct ath_softc { struct tasklet_struct fataltq; /* fatal int tasklet */ #ifdef BLE struct tasklet_struct sc_radartq; /* Radar detection */ - - int sc_rxbufsize; /* rx size based on mtu */ #endif + unsigned int rxbufsize; /* rx size based on mtu */ struct list_head rxbuf; /* receive buffer */ struct tasklet_struct rxtq; /* rx intr tasklet */ struct tasklet_struct rxorntq; /* rxorn intr tasklet */ @@ -375,6 +367,8 @@ void ath_sysctl_unregister(void); ath5k_hw_set_capability(_ah, AR5K_CAP_TPC, 1, _v, NULL) #define ath_hal_hasbursting(_ah) \ (ath5k_hw_get_capability(_ah, AR5K_CAP_BURST, 0, NULL) == 0) +#define ath_hal_hasbssidmask(_ah) \ + (ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0) #ifdef notyet #define ath_hal_hasmcastkeysearch(_ah) \ (ath5k_hw_get_capability(_ah, AR5K_CAP_MCAST_KEYSRCH, 0, NULL) == 0) diff --git a/openhal/ath5k.h b/openhal/ath5k.h index 8339bfc..6f8f180 100644 --- a/openhal/ath5k.h +++ b/openhal/ath5k.h @@ -1004,13 +1004,12 @@ const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath_hw *hal, unsig void ath5k_hw_detach(struct ath_hw *hal); /* Reset Functions */ int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, struct ieee80211_channel *channel, bool change_channel); -//bool ath5k_hw_nic_reset(struct ath_hw *hal, u32 val); /* Power management functions */ int ath5k_hw_set_power(struct ath_hw *hal, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); enum ath5k_power_mode ath5k_hw_get_power_mode(struct ath_hw *hal); /* DMA Related Functions */ void ath5k_hw_start_rx(struct ath_hw *hal); -bool ath5k_hw_stop_rx_dma(struct ath_hw *hal); +int ath5k_hw_stop_rx_dma(struct ath_hw *hal); u32 ath5k_hw_get_rx_buf(struct ath_hw *hal); void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr); bool ath5k_hw_tx_start(struct ath_hw *hal, unsigned int queue); @@ -1020,7 +1019,7 @@ bool ath5k_hw_put_tx_buf(struct ath_hw *hal, unsigned int queue, u32 phys_addr); bool ath5k_hw_update_tx_triglevel(struct ath_hw *hal, bool increase); /* Interrupt handling */ bool ath5k_hw_is_intr_pending(struct ath_hw *hal); -bool ath5k_hw_get_isr(struct ath_hw *hal, u32 *interrupt_mask); +int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask); enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask); void ath5k_hw_radar_alert(struct ath_hw *hal, bool enable); /* EEPROM access functions */ @@ -1053,7 +1052,6 @@ void ath5k_hw_set_beacon_timers(struct ath_hw *hal, const struct ath5k_beacon_st void ath5k_hw_reset_beacon(struct ath_hw *hal); bool ath5k_hw_wait_for_beacon(struct ath_hw *hal, unsigned long phys_addr); void ath5k_hw_update_mib_counters(struct ath_hw *hal, struct ath5k_mib_stats *statistics); -void ath5k_hw_proc_mib_event(struct ath_hw *hal, const struct ath5k_node_stats *stats) ; /* ACK/CTS Timeouts */ bool ath5k_hw_set_ack_timeout(struct ath_hw *hal, unsigned int timeout); unsigned int ath5k_hw_get_ack_timeout(struct ath_hw *hal); @@ -1114,6 +1112,7 @@ void ath5k_hw_ar5211_rfregs(struct ath_hw *hal, struct ieee80211_channel *channe bool ath5k_hw_rfgain(struct ath_hw *hal, unsigned int phy, u_int freq); enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath_hw *hal); /* Misc functions */ +int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power); void ath5k_hw_dump_state(struct ath_hw *hal); int ath5k_hw_get_capability(struct ath_hw *hal, enum ath5k_capability_type cap_type,u32 capability, u32 *result); int ath5k_hw_set_capability(struct ath_hw *hal, enum ath5k_capability_type cap_type, u32 capability, u32 setting); diff --git a/openhal/ath5k_hw.c b/openhal/ath5k_hw.c index 34133a1..0fef518 100644 --- a/openhal/ath5k_hw.c +++ b/openhal/ath5k_hw.c @@ -41,8 +41,7 @@ static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR; static int ath5k_hw_nic_reset(struct ath_hw *, u32); static int ath5k_hw_nic_wakeup(struct ath_hw *, int, bool); static u16 ath5k_hw_radio_revision(struct ath_hw *, unsigned int); -static bool ath5k_hw_txpower(struct ath_hw *, struct ieee80211_channel *, - unsigned int); +static int ath5k_hw_txpower(struct ath_hw *, struct ieee80211_channel *, unsigned int); static bool ath5k_hw_setup_4word_tx_desc(struct ath_hw *, struct ath_desc *, unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, @@ -685,11 +684,12 @@ void ath5k_hw_detach(struct ath_hw *hal) int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, struct ieee80211_channel *channel, bool change_channel) { + const struct ath5k_rate_table *rt; struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; - u8 mac[ETH_ALEN]; u32 data, noise_floor, s_seq, s_ant, s_led[3]; + u8 mac[ETH_ALEN]; unsigned int i, phy, mode, freq, off, ee_mode, ant[2]; - const struct ath5k_rate_table *rt; + int ret; AR5K_TRACE; @@ -723,8 +723,9 @@ int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, /*Wakeup the device*/ - if (ath5k_hw_nic_wakeup(hal, channel->val, false) == false) - return -EIO; + ret = ath5k_hw_nic_wakeup(hal, channel->val, false); + if (ret) + return ret; /* * Initialize operating mode @@ -966,10 +967,9 @@ int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, /* * Set TX power (XXX use txpower from net80211) */ - if (ath5k_hw_txpower(hal, channel, - AR5K_TUNE_DEFAULT_TXPOWER) == false) { - return -EIO; - } + ret = ath5k_hw_txpower(hal, channel, AR5K_TUNE_DEFAULT_TXPOWER); + if (ret) + return ret; /* * Write RF registers @@ -1403,8 +1403,7 @@ ath5k_hw_get_power_mode(struct ath_hw *hal) /* * Start DMA receive */ -void -ath5k_hw_start_rx(struct ath_hw *hal) +void ath5k_hw_start_rx(struct ath_hw *hal) { AR5K_TRACE; ath5k_hw_reg_write(hal, AR5K_CR_RXE, AR5K_CR); @@ -1413,10 +1412,9 @@ ath5k_hw_start_rx(struct ath_hw *hal) /* * Stop DMA receive */ -bool -ath5k_hw_stop_rx_dma(struct ath_hw *hal) +int ath5k_hw_stop_rx_dma(struct ath_hw *hal) { - int i; + unsigned int i; AR5K_TRACE; ath5k_hw_reg_write(hal, AR5K_CR_RXD, AR5K_CR); @@ -1424,19 +1422,18 @@ ath5k_hw_stop_rx_dma(struct ath_hw *hal) /* * It may take some time to disable the DMA receive unit */ - for (i = 2000; - i > 0 && (ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_CR_RXE) != 0; - i--) + for (i = 2000; i > 0 && + (ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_CR_RXE) != 0; + i--) udelay(10); - return i > 0 ? true : false; + return i ? 0 : -EBUSY; } /* * Get the address of the RX Descriptor */ -u32 -ath5k_hw_get_rx_buf(struct ath_hw *hal) +u32 ath5k_hw_get_rx_buf(struct ath_hw *hal) { return ath5k_hw_reg_read(hal, AR5K_RXDP); } @@ -1444,8 +1441,7 @@ ath5k_hw_get_rx_buf(struct ath_hw *hal) /* * Set the address of the RX Descriptor */ -void -ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr) +void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr) { AR5K_TRACE; @@ -1713,8 +1709,7 @@ bool ath5k_hw_is_intr_pending(struct ath_hw *hal) /* * Get interrupt mask (ISR) */ -bool -ath5k_hw_get_isr(struct ath_hw *hal, u32 *interrupt_mask) +int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask) { u32 data; @@ -1725,9 +1720,10 @@ ath5k_hw_get_isr(struct ath_hw *hal, u32 *interrupt_mask) * on 5210 */ if (hal->ah_version == AR5K_AR5210) { - if ((data = ath5k_hw_reg_read(hal, AR5K_ISR)) == AR5K_INT_NOCARD) { + data = ath5k_hw_reg_read(hal, AR5K_ISR); + if (data == AR5K_INT_NOCARD) { *interrupt_mask = data; - return false; + return -ENODEV; } } @@ -1742,7 +1738,7 @@ ath5k_hw_get_isr(struct ath_hw *hal, u32 *interrupt_mask) *interrupt_mask = (data & AR5K_INT_COMMON) & hal->ah_imr; if (data == AR5K_INT_NOCARD) - return false; + return -ENODEV; if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR)) *interrupt_mask |= AR5K_INT_RX; @@ -1775,14 +1771,13 @@ ath5k_hw_get_isr(struct ath_hw *hal, u32 *interrupt_mask) if (*interrupt_mask == 0 && printk_ratelimit()) AR5K_PRINTF("0x%08x\n", data); - return true; + return 0; } /* * Set interrupt mask */ -enum ath5k_int -ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask) +enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask) { enum ath5k_int old_mask, int_mask; @@ -1801,26 +1796,18 @@ ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask) int_mask = new_mask & AR5K_INT_COMMON; if (new_mask & AR5K_INT_RX) - int_mask |= - AR5K_IMR_RXOK | - AR5K_IMR_RXERR | - AR5K_IMR_RXORN | + int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN | AR5K_IMR_RXDESC; if (new_mask & AR5K_INT_TX) - int_mask |= - AR5K_IMR_TXOK | - AR5K_IMR_TXERR | - AR5K_IMR_TXDESC | + int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC | AR5K_IMR_TXURN; if (hal->ah_version != AR5K_AR5210) { if (new_mask & AR5K_INT_FATAL) { int_mask |= AR5K_IMR_HIUERR; - AR5K_REG_ENABLE_BITS(hal, AR5K_SIMR2, - AR5K_SIMR2_MCABT | - AR5K_SIMR2_SSERR | - AR5K_SIMR2_DPERR); + AR5K_REG_ENABLE_BITS(hal, AR5K_SIMR2, AR5K_SIMR2_MCABT | + AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); } } @@ -3111,12 +3098,6 @@ ath5k_hw_update_mib_counters(struct ath_hw *hal, struct ath5k_mib_stats *statist } } -void /*Unimplemented*/ -ath5k_hw_proc_mib_event(struct ath_hw *hal, const struct ath5k_node_stats *stats) -{ - AR5K_TRACE; -} - /* * ACK/CTS Timeouts */ @@ -5612,11 +5593,10 @@ ath5k_hw_get_rf_gain(struct ath_hw *hal) /* * Initialize the tx power table (not fully implemented) */ -static void -ath5k_txpower_table(struct ath_hw *hal, struct ieee80211_channel *channel, s16 max_power) +static void ath5k_txpower_table(struct ath_hw *hal, struct ieee80211_channel *channel, s16 max_power) { u16 txpower, *rates; - int i, min, max, n; + unsigned int i, min, max, n; rates = hal->ah_txpower.txp_rates; @@ -5650,16 +5630,16 @@ ath5k_txpower_table(struct ath_hw *hal, struct ieee80211_channel *channel, s16 m /* * Set transmition power */ -static bool /*O.K. - txpower_table is unimplemented so this doesn't work*/ +static int /*O.K. - txpower_table is unimplemented so this doesn't work*/ ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned int txpower) { bool tpc = hal->ah_txpower.txp_tpc; - int i; + unsigned int i; AR5K_TRACE; if (txpower > AR5K_TUNE_MAX_TXPOWER) { AR5K_PRINTF("invalid tx power: %u\n", txpower); - return false; + return -EINVAL; } /* Reset TX power values */ @@ -5703,11 +5683,10 @@ ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); } - return true; + return 0; } -bool -ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power) +int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power) { /*Just a try M.F.*/ struct ieee80211_channel *channel = &hal->ah_current_channel;