diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/base.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 167 |
1 files changed, 125 insertions, 42 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 3abbe7513ab5..7ac3a720e52c 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -199,7 +199,7 @@ static void __devexit ath5k_pci_remove(struct pci_dev *pdev); static int ath5k_pci_suspend(struct device *dev); static int ath5k_pci_resume(struct device *dev); -SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); +static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); #define ATH5K_PM_OPS (&ath5k_pm_ops) #else #define ATH5K_PM_OPS NULL @@ -231,7 +231,7 @@ static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mc_list); + struct netdev_hw_addr_list *mc_list); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, @@ -308,7 +308,7 @@ static int ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf); static int ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, - struct ath5k_txq *txq); + struct ath5k_txq *txq, int padsize); static inline void ath5k_txbuf_free(struct ath5k_softc *sc, struct ath5k_buf *bf) { @@ -1138,8 +1138,6 @@ ath5k_mode_setup(struct ath5k_softc *sc) struct ath5k_hw *ah = sc->ah; u32 rfilt; - ah->ah_op_mode = sc->opmode; - /* configure rx filter */ rfilt = sc->filter_flags; ath5k_hw_set_rx_filter(ah, rfilt); @@ -1148,8 +1146,9 @@ ath5k_mode_setup(struct ath5k_softc *sc) ath5k_hw_set_bssid_mask(ah, sc->bssidmask); /* configure operational mode */ - ath5k_hw_set_opmode(ah); + ath5k_hw_set_opmode(ah, sc->opmode); + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode); ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); } @@ -1272,7 +1271,7 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) static int ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, - struct ath5k_txq *txq) + struct ath5k_txq *txq, int padsize) { struct ath5k_hw *ah = sc->ah; struct ath5k_desc *ds = bf->desc; @@ -1324,7 +1323,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, sc->vif, pktlen, info)); } ret = ah->ah_setup_tx_desc(ah, ds, pktlen, - ieee80211_get_hdrlen_from_skb(skb), + ieee80211_get_hdrlen_from_skb(skb), padsize, get_hw_packet_type(skb), (sc->power_level * 2), hw_rate, @@ -1806,6 +1805,67 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, } } +/* + * Compute padding position. skb must contains an IEEE 802.11 frame + */ +static int ath5k_common_padpos(struct sk_buff *skb) +{ + struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; + __le16 frame_control = hdr->frame_control; + int padpos = 24; + + if (ieee80211_has_a4(frame_control)) { + padpos += ETH_ALEN; + } + if (ieee80211_is_data_qos(frame_control)) { + padpos += IEEE80211_QOS_CTL_LEN; + } + + return padpos; +} + +/* + * This function expects a 802.11 frame and returns the number of + * bytes added, or -1 if we don't have enought header room. + */ + +static int ath5k_add_padding(struct sk_buff *skb) +{ + int padpos = ath5k_common_padpos(skb); + int padsize = padpos & 3; + + if (padsize && skb->len>padpos) { + + if (skb_headroom(skb) < padsize) + return -1; + + skb_push(skb, padsize); + memmove(skb->data, skb->data+padsize, padpos); + return padsize; + } + + return 0; +} + +/* + * This function expects a 802.11 frame and returns the number of + * bytes removed + */ + +static int ath5k_remove_padding(struct sk_buff *skb) +{ + int padpos = ath5k_common_padpos(skb); + int padsize = padpos & 3; + + if (padsize && skb->len>=padpos+padsize) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + return padsize; + } + + return 0; +} + static void ath5k_tasklet_rx(unsigned long data) { @@ -1819,8 +1879,6 @@ ath5k_tasklet_rx(unsigned long data) struct ath5k_buf *bf; struct ath5k_desc *ds; int ret; - int hdrlen; - int padsize; int rx_flag; spin_lock(&sc->rxbuflock); @@ -1845,18 +1903,28 @@ ath5k_tasklet_rx(unsigned long data) break; else if (unlikely(ret)) { ATH5K_ERR(sc, "error in processing rx descriptor\n"); + sc->stats.rxerr_proc++; spin_unlock(&sc->rxbuflock); return; } + sc->stats.rx_all_count++; + if (unlikely(rs.rs_more)) { ATH5K_WARN(sc, "unsupported jumbo\n"); + sc->stats.rxerr_jumbo++; goto next; } if (unlikely(rs.rs_status)) { - if (rs.rs_status & AR5K_RXERR_PHY) + if (rs.rs_status & AR5K_RXERR_CRC) + sc->stats.rxerr_crc++; + if (rs.rs_status & AR5K_RXERR_FIFO) + sc->stats.rxerr_fifo++; + if (rs.rs_status & AR5K_RXERR_PHY) { + sc->stats.rxerr_phy++; goto next; + } if (rs.rs_status & AR5K_RXERR_DECRYPT) { /* * Decrypt error. If the error occurred @@ -1868,12 +1936,14 @@ ath5k_tasklet_rx(unsigned long data) * * XXX do key cache faulting */ + sc->stats.rxerr_decrypt++; if (rs.rs_keyix == AR5K_RXKEYIX_INVALID && !(rs.rs_status & AR5K_RXERR_CRC)) goto accept; } if (rs.rs_status & AR5K_RXERR_MIC) { rx_flag |= RX_FLAG_MMIC_ERROR; + sc->stats.rxerr_mic++; goto accept; } @@ -1905,12 +1975,8 @@ accept: * bytes and we can optimize this a bit. In addition, we must * not try to remove padding from short control frames that do * not have payload. */ - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - padsize = ath5k_pad_size(hdrlen); - if (padsize) { - memmove(skb->data + padsize, skb->data, hdrlen); - skb_pull(skb, padsize); - } + ath5k_remove_padding(skb); + rxs = IEEE80211_SKB_RXCB(skb); /* @@ -1943,6 +2009,12 @@ accept: rxs->signal = rxs->noise + rs.rs_rssi; rxs->antenna = rs.rs_antenna; + + if (rs.rs_antenna > 0 && rs.rs_antenna < 5) + sc->stats.antenna_rx[rs.rs_antenna]++; + else + sc->stats.antenna_rx[0]++; /* invalid */ + rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); rxs->flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); @@ -1997,6 +2069,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) break; } + sc->stats.tx_all_count++; skb = bf->skb; info = IEEE80211_SKB_CB(skb); bf->skb = NULL; @@ -2023,13 +2096,30 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) if (unlikely(ts.ts_status)) { sc->ll_stats.dot11ACKFailureCount++; - if (ts.ts_status & AR5K_TXERR_FILT) + if (ts.ts_status & AR5K_TXERR_FILT) { info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + sc->stats.txerr_filt++; + } + if (ts.ts_status & AR5K_TXERR_XRETRY) + sc->stats.txerr_retry++; + if (ts.ts_status & AR5K_TXERR_FIFO) + sc->stats.txerr_fifo++; } else { info->flags |= IEEE80211_TX_STAT_ACK; info->status.ack_signal = ts.ts_rssi; } + /* + * Remove MAC header padding before giving the frame + * back to mac80211. + */ + ath5k_remove_padding(skb); + + if (ts.ts_antenna > 0 && ts.ts_antenna < 5) + sc->stats.antenna_tx[ts.ts_antenna]++; + else + sc->stats.antenna_tx[0]++; /* invalid */ + ieee80211_tx_status(sc->hw, skb); spin_lock(&sc->txbuflock); @@ -2073,6 +2163,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) int ret = 0; u8 antenna; u32 flags; + const int padsize = 0; bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); @@ -2120,7 +2211,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) * from tx power (value is in dB units already) */ ds->ds_data = bf->skbaddr; ret = ah->ah_setup_tx_desc(ah, ds, skb->len, - ieee80211_get_hdrlen_from_skb(skb), + ieee80211_get_hdrlen_from_skb(skb), padsize, AR5K_PKT_TYPE_BEACON, (sc->power_level * 2), ieee80211_get_tx_rate(sc->hw, info)->hw_value, 1, AR5K_TXKEYIX_INVALID, @@ -2680,7 +2771,6 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_softc *sc = hw->priv; struct ath5k_buf *bf; unsigned long flags; - int hdrlen; int padsize; ath5k_debug_dump_skb(sc, skb, "TX ", 1); @@ -2692,17 +2782,11 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, * the hardware expects the header padded to 4 byte boundaries * if this is not the case we add the padding after the header */ - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - padsize = ath5k_pad_size(hdrlen); - if (padsize) { - - if (skb_headroom(skb) < padsize) { - ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" - " headroom to pad %d\n", hdrlen, padsize); - goto drop_packet; - } - skb_push(skb, padsize); - memmove(skb->data, skb->data+padsize, hdrlen); + padsize = ath5k_add_padding(skb); + if (padsize < 0) { + ATH5K_ERR(sc, "tx hdrlen not %%4: not enough" + " headroom to pad"); + goto drop_packet; } spin_lock_irqsave(&sc->txbuflock, flags); @@ -2721,7 +2805,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, bf->skb = skb; - if (ath5k_txbuf_setup(sc, bf, txq)) { + if (ath5k_txbuf_setup(sc, bf, txq, padsize)) { bf->skb = NULL; spin_lock_irqsave(&sc->txbuflock, flags); list_add_tail(&bf->list, &sc->txbuf); @@ -2836,6 +2920,8 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, goto end; } + ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode); + ath5k_hw_set_lladdr(sc->ah, vif->addr); ath5k_mode_setup(sc); @@ -2906,7 +2992,7 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) * then we must allow the user to set how many tx antennas we * have available */ - ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); + ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); unlock: mutex_unlock(&sc->lock); @@ -2914,22 +3000,20 @@ unlock: } static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, - int mc_count, struct dev_addr_list *mclist) + struct netdev_hw_addr_list *mc_list) { u32 mfilt[2], val; - int i; u8 pos; + struct netdev_hw_addr *ha; mfilt[0] = 0; mfilt[1] = 1; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; + netdev_hw_addr_list_for_each(ha, mc_list) { /* calculate XOR of eight 6-bit values */ - val = get_unaligned_le32(mclist->dmi_addr + 0); + val = get_unaligned_le32(ha->addr + 0); pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = get_unaligned_le32(mclist->dmi_addr + 3); + val = get_unaligned_le32(ha->addr + 3); pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); @@ -2937,8 +3021,7 @@ static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, * but not sure, needs testing, if we do use this we'd * neet to inform below to not reset the mcast */ /* ath5k_hw_set_mcast_filterindex(ah, - * mclist->dmi_addr[5]); */ - mclist = mclist->next; + * ha->addr[5]); */ } return ((u64)(mfilt[1]) << 32) | mfilt[0]; |