OpenCloudOS-Kernel/drivers/net/wireless/ath/ath9k/recv.c

1176 lines
30 KiB
C
Raw Normal View History

/*
* Copyright (c) 2008-2011 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 <linux/dma-mapping.h>
#include "ath9k.h"
#include "ar9003_mac.h"
#define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb))
static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
{
return sc->ps_enabled &&
(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP);
}
/*
* 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.
*/
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf,
bool flush)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds;
struct sk_buff *skb;
ds = bf->bf_desc;
ds->ds_link = 0; /* link to null */
ds->ds_data = bf->bf_buf_addr;
/* virtual addr of the beginning of the buffer. */
skb = bf->bf_mpdu;
BUG_ON(skb == NULL);
ds->ds_vdata = skb->data;
/*
* setup rx descriptors. The rx_bufsize here tells the hardware
* how much data it can DMA to us and that we are prepared
* to process
*/
ath9k_hw_setuprxdesc(ah, ds,
common->rx_bufsize,
0);
if (sc->rx.rxlink)
*sc->rx.rxlink = bf->bf_daddr;
else if (!flush)
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
sc->rx.rxlink = &ds->ds_link;
}
static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf,
bool flush)
{
if (sc->rx.buf_hold)
ath_rx_buf_link(sc, sc->rx.buf_hold, flush);
sc->rx.buf_hold = bf;
}
static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
{
/* XXX block beacon interrupts */
ath9k_hw_setantenna(sc->sc_ah, antenna);
sc->rx.defant = antenna;
sc->rx.rxotherant = 0;
}
static void ath_opmode_init(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
u32 rfilt, mfilt[2];
/* configure rx filter */
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(ah, rfilt);
/* configure bssid mask */
ath_hw_setbssidmask(common);
/* configure operational mode */
ath9k_hw_setopmode(ah);
/* calculate and install multicast filter */
mfilt[0] = mfilt[1] = ~0;
ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
}
static bool ath_rx_edma_buf_link(struct ath_softc *sc,
enum ath9k_rx_qtype qtype)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_rx_edma *rx_edma;
struct sk_buff *skb;
struct ath_rxbuf *bf;
rx_edma = &sc->rx.rx_edma[qtype];
if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
return false;
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
list_del_init(&bf->list);
skb = bf->bf_mpdu;
memset(skb->data, 0, ah->caps.rx_status_len);
dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
ah->caps.rx_status_len, DMA_TO_DEVICE);
SKB_CB_ATHBUF(skb) = bf;
ath9k_hw_addrxbuf_edma(ah, bf->bf_buf_addr, qtype);
__skb_queue_tail(&rx_edma->rx_fifo, skb);
return true;
}
static void ath_rx_addbuffer_edma(struct ath_softc *sc,
enum ath9k_rx_qtype qtype)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_rxbuf *bf, *tbf;
if (list_empty(&sc->rx.rxbuf)) {
ath_dbg(common, QUEUE, "No free rx buf available\n");
return;
}
list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list)
if (!ath_rx_edma_buf_link(sc, qtype))
break;
}
static void ath_rx_remove_buffer(struct ath_softc *sc,
enum ath9k_rx_qtype qtype)
{
struct ath_rxbuf *bf;
struct ath_rx_edma *rx_edma;
struct sk_buff *skb;
rx_edma = &sc->rx.rx_edma[qtype];
while ((skb = __skb_dequeue(&rx_edma->rx_fifo)) != NULL) {
bf = SKB_CB_ATHBUF(skb);
BUG_ON(!bf);
list_add_tail(&bf->list, &sc->rx.rxbuf);
}
}
static void ath_rx_edma_cleanup(struct ath_softc *sc)
{
ath9k: Fix a dma warning/memory leak proper dma_unmapping and freeing of skb's has to be done in the rx cleanup for EDMA chipsets when the device is unloaded and this also seems to address the following warning which shows up occasionally when the device is unloaded Call Trace: [<c0148cd2>] warn_slowpath_common+0x72/0xa0 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c0148da3>] warn_slowpath_fmt+0x33/0x40 [<c03b669c>] dma_debug_device_change+0x19c/0x200 [<c0657f12>] notifier_call_chain+0x82/0xb0 [<c0171370>] __blocking_notifier_call_chain+0x60/0x90 [<c01713bf>] blocking_notifier_call_chain+0x1f/0x30 [<c044f594>] __device_release_driver+0xa4/0xc0 [<c044f647>] driver_detach+0x97/0xa0 [<c044e65c>] bus_remove_driver+0x6c/0xe0 [<c029af0b>] ? sysfs_addrm_finish+0x4b/0x60 [<c0450109>] driver_unregister+0x49/0x80 [<c0299f54>] ? sysfs_remove_file+0x14/0x20 [<c03c3ab2>] pci_unregister_driver+0x32/0x80 [<f92c2162>] ath_pci_exit+0x12/0x20 [ath9k] [<f92c8467>] ath9k_exit+0x17/0x36 [ath9k] [<c06523cd>] ? mutex_unlock+0xd/0x10 [<c018e27f>] sys_delete_module+0x13f/0x200 [<c02139bb>] ? sys_munmap+0x4b/0x60 [<c06547c5>] ? restore_all+0xf/0xf [<c0657a20>] ? spurious_fault+0xe0/0xe0 [<c01832f4>] ? trace_hardirqs_on_caller+0xf4/0x180 [<c065b863>] sysenter_do_call+0x12/0x38 ---[ end trace 16e1c1521c06bcf9 ]--- Mapped at: [<c03b7938>] debug_dma_map_page+0x48/0x120 [<f92ba3e8>] ath_rx_init+0x3f8/0x4b0 [ath9k] [<f92b5ae4>] ath9k_init_device+0x4c4/0x7b0 [ath9k] [<f92c2813>] ath_pci_probe+0x263/0x330 [ath9k] Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-09-23 17:03:14 +08:00
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_rxbuf *bf;
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
ath9k: Fix a dma warning/memory leak proper dma_unmapping and freeing of skb's has to be done in the rx cleanup for EDMA chipsets when the device is unloaded and this also seems to address the following warning which shows up occasionally when the device is unloaded Call Trace: [<c0148cd2>] warn_slowpath_common+0x72/0xa0 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c0148da3>] warn_slowpath_fmt+0x33/0x40 [<c03b669c>] dma_debug_device_change+0x19c/0x200 [<c0657f12>] notifier_call_chain+0x82/0xb0 [<c0171370>] __blocking_notifier_call_chain+0x60/0x90 [<c01713bf>] blocking_notifier_call_chain+0x1f/0x30 [<c044f594>] __device_release_driver+0xa4/0xc0 [<c044f647>] driver_detach+0x97/0xa0 [<c044e65c>] bus_remove_driver+0x6c/0xe0 [<c029af0b>] ? sysfs_addrm_finish+0x4b/0x60 [<c0450109>] driver_unregister+0x49/0x80 [<c0299f54>] ? sysfs_remove_file+0x14/0x20 [<c03c3ab2>] pci_unregister_driver+0x32/0x80 [<f92c2162>] ath_pci_exit+0x12/0x20 [ath9k] [<f92c8467>] ath9k_exit+0x17/0x36 [ath9k] [<c06523cd>] ? mutex_unlock+0xd/0x10 [<c018e27f>] sys_delete_module+0x13f/0x200 [<c02139bb>] ? sys_munmap+0x4b/0x60 [<c06547c5>] ? restore_all+0xf/0xf [<c0657a20>] ? spurious_fault+0xe0/0xe0 [<c01832f4>] ? trace_hardirqs_on_caller+0xf4/0x180 [<c065b863>] sysenter_do_call+0x12/0x38 ---[ end trace 16e1c1521c06bcf9 ]--- Mapped at: [<c03b7938>] debug_dma_map_page+0x48/0x120 [<f92ba3e8>] ath_rx_init+0x3f8/0x4b0 [ath9k] [<f92b5ae4>] ath9k_init_device+0x4c4/0x7b0 [ath9k] [<f92c2813>] ath_pci_probe+0x263/0x330 [ath9k] Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-09-23 17:03:14 +08:00
if (bf->bf_mpdu) {
dma_unmap_single(sc->dev, bf->bf_buf_addr,
common->rx_bufsize,
DMA_BIDIRECTIONAL);
dev_kfree_skb_any(bf->bf_mpdu);
ath9k: Fix a dma warning/memory leak proper dma_unmapping and freeing of skb's has to be done in the rx cleanup for EDMA chipsets when the device is unloaded and this also seems to address the following warning which shows up occasionally when the device is unloaded Call Trace: [<c0148cd2>] warn_slowpath_common+0x72/0xa0 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c03b669c>] ? dma_debug_device_change+0x19c/0x200 [<c0148da3>] warn_slowpath_fmt+0x33/0x40 [<c03b669c>] dma_debug_device_change+0x19c/0x200 [<c0657f12>] notifier_call_chain+0x82/0xb0 [<c0171370>] __blocking_notifier_call_chain+0x60/0x90 [<c01713bf>] blocking_notifier_call_chain+0x1f/0x30 [<c044f594>] __device_release_driver+0xa4/0xc0 [<c044f647>] driver_detach+0x97/0xa0 [<c044e65c>] bus_remove_driver+0x6c/0xe0 [<c029af0b>] ? sysfs_addrm_finish+0x4b/0x60 [<c0450109>] driver_unregister+0x49/0x80 [<c0299f54>] ? sysfs_remove_file+0x14/0x20 [<c03c3ab2>] pci_unregister_driver+0x32/0x80 [<f92c2162>] ath_pci_exit+0x12/0x20 [ath9k] [<f92c8467>] ath9k_exit+0x17/0x36 [ath9k] [<c06523cd>] ? mutex_unlock+0xd/0x10 [<c018e27f>] sys_delete_module+0x13f/0x200 [<c02139bb>] ? sys_munmap+0x4b/0x60 [<c06547c5>] ? restore_all+0xf/0xf [<c0657a20>] ? spurious_fault+0xe0/0xe0 [<c01832f4>] ? trace_hardirqs_on_caller+0xf4/0x180 [<c065b863>] sysenter_do_call+0x12/0x38 ---[ end trace 16e1c1521c06bcf9 ]--- Mapped at: [<c03b7938>] debug_dma_map_page+0x48/0x120 [<f92ba3e8>] ath_rx_init+0x3f8/0x4b0 [ath9k] [<f92b5ae4>] ath9k_init_device+0x4c4/0x7b0 [ath9k] [<f92c2813>] ath_pci_probe+0x263/0x330 [ath9k] Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-09-23 17:03:14 +08:00
bf->bf_buf_addr = 0;
bf->bf_mpdu = NULL;
}
}
}
static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size)
{
__skb_queue_head_init(&rx_edma->rx_fifo);
rx_edma->rx_fifo_hwsize = size;
}
static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
struct sk_buff *skb;
struct ath_rxbuf *bf;
int error = 0, i;
u32 size;
ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
ah->caps.rx_status_len);
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_LP],
ah->caps.rx_lp_qdepth);
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
ah->caps.rx_hp_qdepth);
size = sizeof(struct ath_rxbuf) * nbufs;
bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
if (!bf)
return -ENOMEM;
INIT_LIST_HEAD(&sc->rx.rxbuf);
for (i = 0; i < nbufs; i++, bf++) {
skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL);
if (!skb) {
error = -ENOMEM;
goto rx_init_fail;
}
memset(skb->data, 0, common->rx_bufsize);
bf->bf_mpdu = skb;
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
common->rx_bufsize,
DMA_BIDIRECTIONAL);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
bf->bf_buf_addr = 0;
ath_err(common,
"dma_mapping_error() on RX init\n");
error = -ENOMEM;
goto rx_init_fail;
}
list_add_tail(&bf->list, &sc->rx.rxbuf);
}
return 0;
rx_init_fail:
ath_rx_edma_cleanup(sc);
return error;
}
static void ath_edma_start_recv(struct ath_softc *sc)
{
ath9k_hw_rxena(sc->sc_ah);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
ath_opmode_init(sc);
ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
}
static void ath_edma_stop_recv(struct ath_softc *sc)
{
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
}
int ath_rx_init(struct ath_softc *sc, int nbufs)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct sk_buff *skb;
struct ath_rxbuf *bf;
int error = 0;
spin_lock_init(&sc->sc_pcu_lock);
common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
sc->sc_ah->caps.rx_status_len;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
return ath_rx_edma_init(sc, nbufs);
ath_dbg(common, CONFIG, "cachelsz %u rxbufsize %u\n",
common->cachelsz, common->rx_bufsize);
/* Initialize rx descriptors */
error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf,
"rx", nbufs, 1, 0);
if (error != 0) {
ath_err(common,
"failed to allocate rx descriptors: %d\n",
error);
goto err;
}
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = ath_rxbuf_alloc(common, common->rx_bufsize,
GFP_KERNEL);
if (skb == NULL) {
error = -ENOMEM;
goto err;
}
bf->bf_mpdu = skb;
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
common->rx_bufsize,
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
bf->bf_buf_addr = 0;
ath_err(common,
"dma_mapping_error() on RX init\n");
error = -ENOMEM;
goto err;
}
}
sc->rx.rxlink = NULL;
err:
if (error)
ath_rx_cleanup(sc);
return error;
}
void ath_rx_cleanup(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ath_rxbuf *bf;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_rx_edma_cleanup(sc);
return;
}
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = bf->bf_mpdu;
if (skb) {
dma_unmap_single(sc->dev, bf->bf_buf_addr,
common->rx_bufsize,
DMA_FROM_DEVICE);
dev_kfree_skb(skb);
bf->bf_buf_addr = 0;
bf->bf_mpdu = NULL;
}
}
}
/*
* 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
*/
u32 ath_calcrxfilter(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u32 rfilt;
if (config_enabled(CONFIG_ATH9K_TX99))
return 0;
rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
| ATH9K_RX_FILTER_MCAST;
/* if operating on a DFS channel, enable radar pulse detection */
if (sc->hw->conf.radar_enabled)
rfilt |= ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR;
spin_lock_bh(&sc->chan_lock);
if (sc->cur_chan->rxfilter & FIF_PROBE_REQ)
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
/*
* Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station
* mode interface or when in monitor mode. AP mode does not need this
* since it receives all in-BSS frames anyway.
*/
if (sc->sc_ah->is_monitoring)
rfilt |= ATH9K_RX_FILTER_PROM;
if ((sc->cur_chan->rxfilter & FIF_CONTROL) ||
sc->sc_ah->dynack.enabled)
rfilt |= ATH9K_RX_FILTER_CONTROL;
if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
(sc->cur_chan->nvifs <= 1) &&
!(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC))
rfilt |= ATH9K_RX_FILTER_MYBEACON;
else
rfilt |= ATH9K_RX_FILTER_BEACON;
if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
(sc->cur_chan->rxfilter & FIF_PSPOLL))
rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (sc->cur_chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
rfilt |= ATH9K_RX_FILTER_COMP_BAR;
if (sc->cur_chan->nvifs > 1 || (sc->cur_chan->rxfilter & FIF_OTHER_BSS)) {
/* This is needed for older chips */
if (sc->sc_ah->hw_version.macVersion <= AR_SREV_VERSION_9160)
rfilt |= ATH9K_RX_FILTER_PROM;
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
}
if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
if (ath9k_is_chanctx_enabled() &&
test_bit(ATH_OP_SCANNING, &common->op_flags))
rfilt |= ATH9K_RX_FILTER_BEACON;
spin_unlock_bh(&sc->chan_lock);
return rfilt;
}
void ath_startrecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_rxbuf *bf, *tbf;
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_edma_start_recv(sc);
return;
}
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
sc->rx.buf_hold = NULL;
sc->rx.rxlink = NULL;
list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
ath_rx_buf_link(sc, bf, false);
}
/* We could have deleted elements so the list may be empty now */
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
ath9k_hw_rxena(ah);
start_recv:
ath_opmode_init(sc);
ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
}
static void ath_flushrecv(struct ath_softc *sc)
{
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_rx_tasklet(sc, 1, true);
ath_rx_tasklet(sc, 1, false);
}
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
bool stopped, reset = false;
ath9k_hw_abortpcurecv(ah);
ath9k_hw_setrxfilter(ah, 0);
stopped = ath9k_hw_stopdmarecv(ah, &reset);
ath_flushrecv(sc);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_edma_stop_recv(sc);
else
sc->rx.rxlink = NULL;
if (!(ah->ah_flags & AH_UNPLUGGED) &&
unlikely(!stopped)) {
ath_err(ath9k_hw_common(sc->sc_ah),
"Could not stop RX, we could be "
"confusing the DMA engine when we start RX up\n");
ATH_DBG_WARN_ON_ONCE(!stopped);
}
return stopped && !reset;
}
static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
{
/* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
struct ieee80211_mgmt *mgmt;
u8 *pos, *end, id, elen;
struct ieee80211_tim_ie *tim;
mgmt = (struct ieee80211_mgmt *)skb->data;
pos = mgmt->u.beacon.variable;
end = skb->data + skb->len;
while (pos + 2 < end) {
id = *pos++;
elen = *pos++;
if (pos + elen > end)
break;
if (id == WLAN_EID_TIM) {
if (elen < sizeof(*tim))
break;
tim = (struct ieee80211_tim_ie *) pos;
if (tim->dtim_count != 0)
break;
return tim->bitmap_ctrl & 0x01;
}
pos += elen;
}
return false;
}
static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
ath9k: Fix beacon processing in offchannel When interfaces of different types are present in a context and an offchannel request is received on a STA interface, we end up trying to process beacons received when we are offchannel. This hits the below warning since offchannel will not have proper beacon parameters. Fix this by not processing beacons received when we are offchannel. ------------[ cut here ]------------ WARNING: CPU: 1 PID: 0 at ../drivers/net/wireless/ath/ath9k/recv.c:552 ath_rx_tasklet+0xf8f/0x1020 [ath9k]() CPU: 1 PID: 0 Comm: swapper/1 Tainted: G O 3.17.0-rc4-wl-debug #16 Hardware name: LENOVO 28427ZQ/INVALID, BIOS 6JET58WW (1.16 ) 09/17/2009 0000000000000000 15916787056abba3 ffff88013b603d08 ffffffff8156af94 ffff88013b603d50 ffff88013b603d40 ffffffff81070dbd ffff8800a84bb300 ffff8800b05db358 ffff8800a84cc578 ffff8800a84bb300 ffff8800b05daa40 Call Trace: <IRQ> [<ffffffff8156af94>] dump_stack+0x4d/0x6f [<ffffffff81070dbd>] warn_slowpath_common+0x7d/0xa0 [<ffffffff81070e3c>] warn_slowpath_fmt+0x5c/0x80 [<ffffffff81572143>] ? _raw_spin_lock_irqsave+0x83/0xa0 [<ffffffffa09d04a6>] ? ath_rx_tasklet+0x9f6/0x1020 [ath9k] [<ffffffffa09d0a3f>] ath_rx_tasklet+0xf8f/0x1020 [ath9k] [<ffffffffa09cd4f4>] ath9k_tasklet+0xf4/0x310 [ath9k] [<ffffffff81075a97>] tasklet_action+0xe7/0xf0 [<ffffffff8107508a>] __do_softirq+0x12a/0x340 [<ffffffff8107544e>] irq_exit+0x9e/0xc0 [<ffffffff81575e56>] do_IRQ+0x56/0xe0 [<ffffffff81573c72>] common_interrupt+0x72/0x72 <EOI> [<ffffffff81421037>] ? cpuidle_enter_state+0x67/0x1a0 [<ffffffff81421257>] cpuidle_enter+0x17/0x20 [<ffffffff810b3ebe>] cpu_startup_entry+0x3ce/0x420 [<ffffffff81048563>] start_secondary+0x233/0x2c0 ---[ end trace f15c3e33ba78d840 ]--- ------------[ cut here ]------------ Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-12 14:40:48 +08:00
bool skip_beacon = false;
if (skb->len < 24 + 8 + 2 + 2)
return;
sc->ps_flags &= ~PS_WAIT_FOR_BEACON;
if (sc->ps_flags & PS_BEACON_SYNC) {
sc->ps_flags &= ~PS_BEACON_SYNC;
ath_dbg(common, PS,
"Reconfigure beacon timers based on synchronized timestamp\n");
ath9k: Fix beacon processing in offchannel When interfaces of different types are present in a context and an offchannel request is received on a STA interface, we end up trying to process beacons received when we are offchannel. This hits the below warning since offchannel will not have proper beacon parameters. Fix this by not processing beacons received when we are offchannel. ------------[ cut here ]------------ WARNING: CPU: 1 PID: 0 at ../drivers/net/wireless/ath/ath9k/recv.c:552 ath_rx_tasklet+0xf8f/0x1020 [ath9k]() CPU: 1 PID: 0 Comm: swapper/1 Tainted: G O 3.17.0-rc4-wl-debug #16 Hardware name: LENOVO 28427ZQ/INVALID, BIOS 6JET58WW (1.16 ) 09/17/2009 0000000000000000 15916787056abba3 ffff88013b603d08 ffffffff8156af94 ffff88013b603d50 ffff88013b603d40 ffffffff81070dbd ffff8800a84bb300 ffff8800b05db358 ffff8800a84cc578 ffff8800a84bb300 ffff8800b05daa40 Call Trace: <IRQ> [<ffffffff8156af94>] dump_stack+0x4d/0x6f [<ffffffff81070dbd>] warn_slowpath_common+0x7d/0xa0 [<ffffffff81070e3c>] warn_slowpath_fmt+0x5c/0x80 [<ffffffff81572143>] ? _raw_spin_lock_irqsave+0x83/0xa0 [<ffffffffa09d04a6>] ? ath_rx_tasklet+0x9f6/0x1020 [ath9k] [<ffffffffa09d0a3f>] ath_rx_tasklet+0xf8f/0x1020 [ath9k] [<ffffffffa09cd4f4>] ath9k_tasklet+0xf4/0x310 [ath9k] [<ffffffff81075a97>] tasklet_action+0xe7/0xf0 [<ffffffff8107508a>] __do_softirq+0x12a/0x340 [<ffffffff8107544e>] irq_exit+0x9e/0xc0 [<ffffffff81575e56>] do_IRQ+0x56/0xe0 [<ffffffff81573c72>] common_interrupt+0x72/0x72 <EOI> [<ffffffff81421037>] ? cpuidle_enter_state+0x67/0x1a0 [<ffffffff81421257>] cpuidle_enter+0x17/0x20 [<ffffffff810b3ebe>] cpu_startup_entry+0x3ce/0x420 [<ffffffff81048563>] start_secondary+0x233/0x2c0 ---[ end trace f15c3e33ba78d840 ]--- ------------[ cut here ]------------ Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-12 14:40:48 +08:00
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
ath9k: Fix beacon processing in offchannel When interfaces of different types are present in a context and an offchannel request is received on a STA interface, we end up trying to process beacons received when we are offchannel. This hits the below warning since offchannel will not have proper beacon parameters. Fix this by not processing beacons received when we are offchannel. ------------[ cut here ]------------ WARNING: CPU: 1 PID: 0 at ../drivers/net/wireless/ath/ath9k/recv.c:552 ath_rx_tasklet+0xf8f/0x1020 [ath9k]() CPU: 1 PID: 0 Comm: swapper/1 Tainted: G O 3.17.0-rc4-wl-debug #16 Hardware name: LENOVO 28427ZQ/INVALID, BIOS 6JET58WW (1.16 ) 09/17/2009 0000000000000000 15916787056abba3 ffff88013b603d08 ffffffff8156af94 ffff88013b603d50 ffff88013b603d40 ffffffff81070dbd ffff8800a84bb300 ffff8800b05db358 ffff8800a84cc578 ffff8800a84bb300 ffff8800b05daa40 Call Trace: <IRQ> [<ffffffff8156af94>] dump_stack+0x4d/0x6f [<ffffffff81070dbd>] warn_slowpath_common+0x7d/0xa0 [<ffffffff81070e3c>] warn_slowpath_fmt+0x5c/0x80 [<ffffffff81572143>] ? _raw_spin_lock_irqsave+0x83/0xa0 [<ffffffffa09d04a6>] ? ath_rx_tasklet+0x9f6/0x1020 [ath9k] [<ffffffffa09d0a3f>] ath_rx_tasklet+0xf8f/0x1020 [ath9k] [<ffffffffa09cd4f4>] ath9k_tasklet+0xf4/0x310 [ath9k] [<ffffffff81075a97>] tasklet_action+0xe7/0xf0 [<ffffffff8107508a>] __do_softirq+0x12a/0x340 [<ffffffff8107544e>] irq_exit+0x9e/0xc0 [<ffffffff81575e56>] do_IRQ+0x56/0xe0 [<ffffffff81573c72>] common_interrupt+0x72/0x72 <EOI> [<ffffffff81421037>] ? cpuidle_enter_state+0x67/0x1a0 [<ffffffff81421257>] cpuidle_enter+0x17/0x20 [<ffffffff810b3ebe>] cpu_startup_entry+0x3ce/0x420 [<ffffffff81048563>] start_secondary+0x233/0x2c0 ---[ end trace f15c3e33ba78d840 ]--- ------------[ cut here ]------------ Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-12 14:40:48 +08:00
if (ath9k_is_chanctx_enabled()) {
if (sc->cur_chan == &sc->offchannel.chan)
skip_beacon = true;
}
#endif
ath9k: Fix beacon processing in offchannel When interfaces of different types are present in a context and an offchannel request is received on a STA interface, we end up trying to process beacons received when we are offchannel. This hits the below warning since offchannel will not have proper beacon parameters. Fix this by not processing beacons received when we are offchannel. ------------[ cut here ]------------ WARNING: CPU: 1 PID: 0 at ../drivers/net/wireless/ath/ath9k/recv.c:552 ath_rx_tasklet+0xf8f/0x1020 [ath9k]() CPU: 1 PID: 0 Comm: swapper/1 Tainted: G O 3.17.0-rc4-wl-debug #16 Hardware name: LENOVO 28427ZQ/INVALID, BIOS 6JET58WW (1.16 ) 09/17/2009 0000000000000000 15916787056abba3 ffff88013b603d08 ffffffff8156af94 ffff88013b603d50 ffff88013b603d40 ffffffff81070dbd ffff8800a84bb300 ffff8800b05db358 ffff8800a84cc578 ffff8800a84bb300 ffff8800b05daa40 Call Trace: <IRQ> [<ffffffff8156af94>] dump_stack+0x4d/0x6f [<ffffffff81070dbd>] warn_slowpath_common+0x7d/0xa0 [<ffffffff81070e3c>] warn_slowpath_fmt+0x5c/0x80 [<ffffffff81572143>] ? _raw_spin_lock_irqsave+0x83/0xa0 [<ffffffffa09d04a6>] ? ath_rx_tasklet+0x9f6/0x1020 [ath9k] [<ffffffffa09d0a3f>] ath_rx_tasklet+0xf8f/0x1020 [ath9k] [<ffffffffa09cd4f4>] ath9k_tasklet+0xf4/0x310 [ath9k] [<ffffffff81075a97>] tasklet_action+0xe7/0xf0 [<ffffffff8107508a>] __do_softirq+0x12a/0x340 [<ffffffff8107544e>] irq_exit+0x9e/0xc0 [<ffffffff81575e56>] do_IRQ+0x56/0xe0 [<ffffffff81573c72>] common_interrupt+0x72/0x72 <EOI> [<ffffffff81421037>] ? cpuidle_enter_state+0x67/0x1a0 [<ffffffff81421257>] cpuidle_enter+0x17/0x20 [<ffffffff810b3ebe>] cpu_startup_entry+0x3ce/0x420 [<ffffffff81048563>] start_secondary+0x233/0x2c0 ---[ end trace f15c3e33ba78d840 ]--- ------------[ cut here ]------------ Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-12 14:40:48 +08:00
if (!skip_beacon &&
!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
ath9k_set_beacon(sc);
ath9k_p2p_beacon_sync(sc);
}
if (ath_beacon_dtim_pending_cab(skb)) {
/*
* Remain awake waiting for buffered broadcast/multicast
* frames. If the last broadcast/multicast frame is not
* received properly, the next beacon frame will work as
* a backup trigger for returning into NETWORK SLEEP state,
* so we are waiting for it as well.
*/
ath_dbg(common, PS,
"Received DTIM beacon indicating buffered broadcast/multicast frame(s)\n");
sc->ps_flags |= PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON;
return;
}
if (sc->ps_flags & PS_WAIT_FOR_CAB) {
/*
* This can happen if a broadcast frame is dropped or the AP
* fails to send a frame indicating that all CAB frames have
* been delivered.
*/
sc->ps_flags &= ~PS_WAIT_FOR_CAB;
ath_dbg(common, PS, "PS wait for CAB frames timed out\n");
}
}
static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
{
struct ieee80211_hdr *hdr;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
hdr = (struct ieee80211_hdr *)skb->data;
/* Process Beacon and CAB receive in PS state */
if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc))
&& mybeacon) {
ath_rx_ps_beacon(sc, skb);
} else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
(ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_action(hdr->frame_control)) &&
is_multicast_ether_addr(hdr->addr1) &&
!ieee80211_has_moredata(hdr->frame_control)) {
/*
* No more broadcast/multicast frames to be received at this
* point.
*/
sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON);
ath_dbg(common, PS,
"All PS CAB frames received, back to sleep\n");
} else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) &&
!is_multicast_ether_addr(hdr->addr1) &&
!ieee80211_has_morefrags(hdr->frame_control)) {
sc->ps_flags &= ~PS_WAIT_FOR_PSPOLL_DATA;
ath_dbg(common, PS,
"Going back to sleep after having received PS-Poll data (0x%lx)\n",
sc->ps_flags & (PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA |
PS_WAIT_FOR_TX_ACK));
}
}
static bool ath_edma_get_buffers(struct ath_softc *sc,
enum ath9k_rx_qtype qtype,
struct ath_rx_status *rs,
struct ath_rxbuf **dest)
{
struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ath_rxbuf *bf;
int ret;
skb = skb_peek(&rx_edma->rx_fifo);
if (!skb)
return false;
bf = SKB_CB_ATHBUF(skb);
BUG_ON(!bf);
dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
common->rx_bufsize, DMA_FROM_DEVICE);
ret = ath9k_hw_process_rxdesc_edma(ah, rs, skb->data);
if (ret == -EINPROGRESS) {
/*let device gain the buffer again*/
dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
common->rx_bufsize, DMA_FROM_DEVICE);
return false;
}
__skb_unlink(skb, &rx_edma->rx_fifo);
if (ret == -EINVAL) {
/* corrupt descriptor, skip this one and the following one */
list_add_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_edma_buf_link(sc, qtype);
skb = skb_peek(&rx_edma->rx_fifo);
if (skb) {
bf = SKB_CB_ATHBUF(skb);
BUG_ON(!bf);
__skb_unlink(skb, &rx_edma->rx_fifo);
list_add_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_edma_buf_link(sc, qtype);
}
ath9k: fix panic caused by returning a descriptor we have queued for reuse Commit 3a2923e83c introduced a bug when a corrupt descriptor is encountered - although the following descriptor is discarded and returned to the queue for reuse the associated frame is also returned for processing. This leads to a panic: BUG: unable to handle kernel NULL pointer dereference at 000000000000003a IP: [<ffffffffa02599a5>] ath_rx_tasklet+0x165/0x1b00 [ath9k] Call Trace: <IRQ> [<ffffffff812d7fa0>] ? map_single+0x60/0x60 [<ffffffffa028f044>] ? ath9k_ioread32+0x34/0x90 [ath9k] [<ffffffffa0292eec>] athk9k_tasklet+0xdc/0x160 [ath9k] [<ffffffff8105e133>] tasklet_action+0x63/0xd0 [<ffffffff8105dbc0>] __do_softirq+0xc0/0x1e0 [<ffffffff8101a873>] ? native_sched_clock+0x13/0x80 [<ffffffff815f9d5c>] call_softirq+0x1c/0x30 [<ffffffff810151f5>] do_softirq+0x75/0xb0 [<ffffffff8105df95>] irq_exit+0xb5/0xc0 [<ffffffff815fa5b3>] do_IRQ+0x63/0xe0 [<ffffffff815f0cea>] common_interrupt+0x6a/0x6a <EOI> [<ffffffff8131840a>] ? intel_idle+0xea/0x150 [<ffffffff813183eb>] ? intel_idle+0xcb/0x150 [<ffffffff814a1db9>] cpuidle_enter+0x19/0x20 [<ffffffff814a23d9>] cpuidle_idle_call+0xa9/0x240 [<ffffffff8101c4bf>] cpu_idle+0xaf/0x120 [<ffffffff815cda8e>] rest_init+0x72/0x74 [<ffffffff81cf4c1a>] start_kernel+0x3b7/0x3c4 [<ffffffff81cf4662>] ? repair_env_string+0x5e/0x5e [<ffffffff81cf4346>] x86_64_start_reservations+0x131/0x135 [<ffffffff81cf444a>] x86_64_start_kernel+0x100/0x10f Making sure bf is cleared to NULL in this case restores the old behaviour. Signed-off-by: Tom Hughes <tom@compton.nu> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-28 01:21:15 +08:00
bf = NULL;
}
*dest = bf;
return true;
}
static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
struct ath_rx_status *rs,
enum ath9k_rx_qtype qtype)
{
struct ath_rxbuf *bf = NULL;
while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
if (!bf)
continue;
return bf;
}
return NULL;
}
static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
struct ath_rx_status *rs)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds;
struct ath_rxbuf *bf;
int ret;
if (list_empty(&sc->rx.rxbuf)) {
sc->rx.rxlink = NULL;
return NULL;
}
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
if (bf == sc->rx.buf_hold)
return 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.
*/
ret = ath9k_hw_rxprocdesc(ah, ds, rs);
if (ret == -EINPROGRESS) {
struct ath_rx_status trs;
struct ath_rxbuf *tbf;
struct ath_desc *tds;
memset(&trs, 0, sizeof(trs));
if (list_is_last(&bf->list, &sc->rx.rxbuf)) {
sc->rx.rxlink = NULL;
return NULL;
}
tbf = list_entry(bf->list.next, struct ath_rxbuf, list);
/*
* 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;
ret = ath9k_hw_rxprocdesc(ah, tds, &trs);
if (ret == -EINPROGRESS)
return NULL;
/*
* Re-check previous descriptor, in case it has been filled
* in the mean time.
*/
ret = ath9k_hw_rxprocdesc(ah, ds, rs);
if (ret == -EINPROGRESS) {
/*
* mark descriptor as zero-length and set the 'more'
* flag to ensure that both buffers get discarded
*/
rs->rs_datalen = 0;
rs->rs_more = true;
}
}
list_del(&bf->list);
if (!bf->bf_mpdu)
return bf;
/*
* Synchronize the DMA transfer with CPU before
* 1. accessing the frame
* 2. requeueing the same buffer to h/w
*/
dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
common->rx_bufsize,
DMA_FROM_DEVICE);
return bf;
}
static void ath9k_process_tsf(struct ath_rx_status *rs,
struct ieee80211_rx_status *rxs,
u64 tsf)
{
u32 tsf_lower = tsf & 0xffffffff;
rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp;
if (rs->rs_tstamp > tsf_lower &&
unlikely(rs->rs_tstamp - tsf_lower > 0x10000000))
rxs->mactime -= 0x100000000ULL;
if (rs->rs_tstamp < tsf_lower &&
unlikely(tsf_lower - rs->rs_tstamp > 0x10000000))
rxs->mactime += 0x100000000ULL;
}
/*
* For Decrypt or Demic errors, we only mark packet status here and always push
* up the frame up to let mac80211 handle the actual error case, be it no
* decryption key or real decryption error. This let us keep statistics there.
*/
static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
struct sk_buff *skb,
struct ath_rx_status *rx_stats,
struct ieee80211_rx_status *rx_status,
bool *decrypt_error, u64 tsf)
{
struct ieee80211_hw *hw = sc->hw;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hdr *hdr;
bool discard_current = sc->rx.discard_next;
/*
* Discard corrupt descriptors which are marked in
* ath_get_next_rx_buf().
*/
if (discard_current)
goto corrupt;
sc->rx.discard_next = false;
/*
* Discard zero-length packets.
*/
if (!rx_stats->rs_datalen) {
RX_STAT_INC(rx_len_err);
goto corrupt;
}
/*
* rs_status follows rs_datalen so if rs_datalen is too large
* we can take a hint that hardware corrupted it, so ignore
* those frames.
*/
if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) {
RX_STAT_INC(rx_len_err);
goto corrupt;
}
/* Only use status info from the last fragment */
if (rx_stats->rs_more)
return 0;
/*
* Return immediately if the RX descriptor has been marked
* as corrupt based on the various error bits.
*
* This is different from the other corrupt descriptor
* condition handled above.
*/
if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC)
goto corrupt;
hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len);
ath9k_process_tsf(rx_stats, rx_status, tsf);
ath_debug_stat_rx(sc, rx_stats);
/*
* Process PHY errors and return so that the packet
* can be dropped.
*/
if (rx_stats->rs_status & ATH9K_RXERR_PHY) {
ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime);
if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
RX_STAT_INC(rx_spectral);
return -EINVAL;
}
/*
* everything but the rate is checked here, the rate check is done
* separately to avoid doing two lookups for a rate for each frame.
*/
spin_lock_bh(&sc->chan_lock);
if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error,
sc->cur_chan->rxfilter)) {
spin_unlock_bh(&sc->chan_lock);
return -EINVAL;
}
spin_unlock_bh(&sc->chan_lock);
if (ath_is_mybeacon(common, hdr)) {
RX_STAT_INC(rx_beacons);
rx_stats->is_mybeacon = true;
}
/*
* This shouldn't happen, but have a safety check anyway.
*/
if (WARN_ON(!ah->curchan))
return -EINVAL;
if (ath9k_cmn_process_rate(common, hw, rx_stats, rx_status)) {
/*
* No valid hardware bitrate found -- we should not get here
* because hardware has already validated this frame as OK.
*/
ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
rx_stats->rs_rate);
RX_STAT_INC(rx_rate_err);
return -EINVAL;
}
if (ath9k_is_chanctx_enabled()) {
if (rx_stats->is_mybeacon)
ath_chanctx_beacon_recv_ev(sc,
ATH_CHANCTX_EVENT_BEACON_RECEIVED);
}
ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
rx_status->band = ah->curchan->chan->band;
rx_status->freq = ah->curchan->chan->center_freq;
rx_status->antenna = rx_stats->rs_antenna;
rx_status->flag |= RX_FLAG_MACTIME_END;
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
if (ieee80211_is_data_present(hdr->frame_control) &&
!ieee80211_is_qos_nullfunc(hdr->frame_control))
sc->rx.num_pkts++;
#endif
return 0;
corrupt:
sc->rx.discard_next = rx_stats->rs_more;
return -EINVAL;
}
/*
* Run the LNA combining algorithm only in these cases:
*
* Standalone WLAN cards with both LNA/Antenna diversity
* enabled in the EEPROM.
*
* WLAN+BT cards which are in the supported card list
* in ath_pci_id_table and the user has loaded the
* driver with "bt_ant_diversity" set to true.
*/
static void ath9k_antenna_check(struct ath_softc *sc,
struct ath_rx_status *rs)
{
struct ath_hw *ah = sc->sc_ah;
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB))
return;
/*
* Change the default rx antenna if rx diversity
* chooses the other antenna 3 times in a row.
*/
if (sc->rx.defant != rs->rs_antenna) {
if (++sc->rx.rxotherant >= 3)
ath_setdefantenna(sc, rs->rs_antenna);
} else {
sc->rx.rxotherant = 0;
}
if (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV) {
if (common->bt_ant_diversity)
ath_ant_comb_scan(sc, rs);
} else {
ath_ant_comb_scan(sc, rs);
}
}
static void ath9k_apply_ampdu_details(struct ath_softc *sc,
struct ath_rx_status *rs, struct ieee80211_rx_status *rxs)
{
if (rs->rs_isaggr) {
rxs->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN;
rxs->ampdu_reference = sc->rx.ampdu_ref;
if (!rs->rs_moreaggr) {
rxs->flag |= RX_FLAG_AMPDU_IS_LAST;
sc->rx.ampdu_ref++;
}
if (rs->rs_flags & ATH9K_RX_DELIM_CRC_PRE)
rxs->flag |= RX_FLAG_AMPDU_DELIM_CRC_ERROR;
}
}
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
{
struct ath_rxbuf *bf;
struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hw *hw = sc->hw;
int retval;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
int dma_type;
u64 tsf = 0;
ath9k: fix power save race conditions ath9k has a race on putting the chip into network sleep and having registers read from hardware. The race occurs because although ath9k_ps_restore() locks its own callers it makes use of some variables which get altered in the driver at different code paths. The variables are the ps_enabled and ps_flags. This is easily reprodicible in large network environments when roaming with the wpa_supplicant simple bgscan. You'd get some 0xdeadbeef read out on certain registers such as: ath: timeout (100000 us) on reg 0x806c: 0xdeadbeef & 0x01f00000 != 0x00000000 ath: RX failed to go idle in 10 ms RXSM=0xdeadbeef ath: timeout (100000 us) on reg 0x7000: 0xdeadbeef & 0x00000003 != 0x00000000 ath: Chip reset failed The fix is to protect the ath9k_config(hw, IEEE80211_CONF_CHANGE_PS) calls with a spin_lock_irqsave() which will disable contendors for these variables from interrupt context, timers, re-entry from mac80211 on the same callback, and most importantly from ath9k_ps_restore() which is the only call which will put the device into network sleep. There are quite a few threads and bug reports on these a few of them are: https://bugs.launchpad.net/ubuntu/karmic/+source/linux/+bug/407040 http://code.google.com/p/chromium-os/issues/detail?id=5709 http://code.google.com/p/chromium-os/issues/detail?id=5943 Stable fixes apply to [2.6.32+] Cc: stable@kernel.org Cc: Paul Stewart <pstew@google.com> Cc: Amod Bodas <amod.bodas@atheros.com> Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-09-17 03:12:26 +08:00
unsigned long flags;
dma_addr_t new_buf_addr;
unsigned int budget = 512;
struct ieee80211_hdr *hdr;
if (edma)
dma_type = DMA_BIDIRECTIONAL;
else
dma_type = DMA_FROM_DEVICE;
qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
tsf = ath9k_hw_gettsf64(ah);
do {
bool decrypt_error = false;
memset(&rs, 0, sizeof(rs));
if (edma)
bf = ath_edma_get_next_rx_buf(sc, &rs, qtype);
else
bf = ath_get_next_rx_buf(sc, &rs);
if (!bf)
break;
skb = bf->bf_mpdu;
if (!skb)
continue;
/*
* Take frame header from the first fragment and RX status from
* the last one.
*/
if (sc->rx.frag)
hdr_skb = sc->rx.frag;
else
hdr_skb = skb;
rxs = IEEE80211_SKB_RXCB(hdr_skb);
memset(rxs, 0, sizeof(struct ieee80211_rx_status));
retval = ath9k_rx_skb_preprocess(sc, hdr_skb, &rs, rxs,
&decrypt_error, tsf);
if (retval)
goto requeue_drop_frag;
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
/* If there is no memory we ignore the current RX'd frame,
* tell hardware it can give us a new frame using the old
* skb and put it at the tail of the sc->rx.rxbuf list for
* processing. */
if (!requeue_skb) {
RX_STAT_INC(rx_oom_err);
goto requeue_drop_frag;
}
/* We will now give hardware our shiny new allocated skb */
new_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
common->rx_bufsize, dma_type);
if (unlikely(dma_mapping_error(sc->dev, new_buf_addr))) {
dev_kfree_skb_any(requeue_skb);
goto requeue_drop_frag;
}
/* Unmap the frame */
dma_unmap_single(sc->dev, bf->bf_buf_addr,
common->rx_bufsize, dma_type);
ath9k: Fix RX DMA mapping After the commit "ath9k: improve dma map failure handling", the wrong buffer was DMA-unmapped, introducing warnings like the one below. This patch fixes the issue. WARNING: at /home/sujith/dev/wireless-testing/lib/dma-debug.c:986 check_sync+0x4bc/0x580() Hardware name: LIFEBOOK AH531 ath9k 0000:02:00.0: DMA-API: device driver tries to sync DMA memory it has not allocated [device address=0x00000000d9012800] [size=48 bytes] Pid: 86, comm: kworker/u:5 Tainted: G W O 3.9.0-rc8-wl-debug #106 Call Trace: [<ffffffff810410c0>] warn_slowpath_common+0x70/0xa0 [<ffffffff8104113c>] warn_slowpath_fmt+0x4c/0x50 [<ffffffff8125432c>] check_sync+0x4bc/0x580 [<ffffffff8109e5f7>] ? trace_hardirqs_on_caller+0xa7/0x190 [<ffffffff8109e6ed>] ? trace_hardirqs_on+0xd/0x10 [<ffffffff81254488>] debug_dma_sync_single_for_device+0x48/0x50 [<ffffffffa0a53825>] ? ath9k_iowrite32+0x35/0x90 [ath9k] [<ffffffff812512f0>] ? swiotlb_tbl_sync_single+0x50/0x90 [<ffffffff81251350>] ? swiotlb_sync_single+0x20/0x30 [<ffffffff8125137f>] ? swiotlb_sync_single_for_device+0xf/0x20 [<ffffffffa0a58baf>] ath_rx_edma_buf_link+0xef/0x140 [ath9k] [<ffffffffa0a58c4e>] ath_rx_addbuffer_edma+0x4e/0x90 [ath9k] [<ffffffffa0a59c51>] ath_startrecv+0xf1/0x120 [ath9k] [<ffffffffa0a550e0>] ath_complete_reset+0x20/0x130 [ath9k] [<ffffffffa0a5790d>] ath_reset_internal+0x10d/0x210 [ath9k] [<ffffffffa0a5878c>] ath9k_config+0x47c/0x7b0 [ath9k] [<ffffffffa06d4978>] ieee80211_hw_config+0x88/0x3f0 [mac80211] [<ffffffffa06d4a3f>] ? ieee80211_hw_config+0x14f/0x3f0 [mac80211] [<ffffffffa06dbed1>] __ieee80211_scan_completed+0xc1/0x440 [mac80211] [<ffffffffa06dd002>] ieee80211_scan_work+0x82/0x440 [mac80211] [<ffffffff810606a3>] process_one_work+0x1e3/0x530 [<ffffffff81060641>] ? process_one_work+0x181/0x530 [<ffffffff8106163f>] worker_thread+0x10f/0x3c0 [<ffffffff81061530>] ? manage_workers+0x330/0x330 [<ffffffff810665da>] kthread+0xea/0xf0 [<ffffffff810664f0>] ? kthread_create_on_node+0x140/0x140 [<ffffffff8146085c>] ret_from_fork+0x7c/0xb0 [<ffffffff810664f0>] ? kthread_create_on_node+0x140/0x140 Cc: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Acked-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-04-23 14:52:19 +08:00
bf->bf_mpdu = requeue_skb;
bf->bf_buf_addr = new_buf_addr;
skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len);
if (ah->caps.rx_status_len)
skb_pull(skb, ah->caps.rx_status_len);
if (!rs.rs_more)
ath9k_cmn_rx_skb_postprocess(common, hdr_skb, &rs,
rxs, decrypt_error);
if (rs.rs_more) {
RX_STAT_INC(rx_frags);
/*
* rs_more indicates chained descriptors which can be
* used to link buffers together for a sort of
* scatter-gather operation.
*/
if (sc->rx.frag) {
/* too many fragments - cannot handle frame */
dev_kfree_skb_any(sc->rx.frag);
dev_kfree_skb_any(skb);
RX_STAT_INC(rx_too_many_frags_err);
skb = NULL;
}
sc->rx.frag = skb;
goto requeue;
}
if (sc->rx.frag) {
int space = skb->len - skb_tailroom(hdr_skb);
if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) {
dev_kfree_skb(skb);
RX_STAT_INC(rx_oom_err);
goto requeue_drop_frag;
}
sc->rx.frag = NULL;
skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len),
skb->len);
dev_kfree_skb_any(skb);
skb = hdr_skb;
}
if (rxs->flag & RX_FLAG_MMIC_STRIPPED)
skb_trim(skb, skb->len - 8);
spin_lock_irqsave(&sc->sc_pm_lock, flags);
if ((sc->ps_flags & (PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA)) ||
ath9k_check_auto_sleep(sc))
ath_rx_ps(sc, skb, rs.is_mybeacon);
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
ath9k_antenna_check(sc, &rs);
ath9k_apply_ampdu_details(sc, &rs, rxs);
ath_debug_rate_stats(sc, &rs, skb);
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_ack(hdr->frame_control))
ath_dynack_sample_ack_ts(sc->sc_ah, skb, rs.rs_tstamp);
ieee80211_rx(hw, skb);
requeue_drop_frag:
if (sc->rx.frag) {
dev_kfree_skb_any(sc->rx.frag);
sc->rx.frag = NULL;
}
requeue:
list_add_tail(&bf->list, &sc->rx.rxbuf);
if (!edma) {
ath_rx_buf_relink(sc, bf, flush);
if (!flush)
ath9k_hw_rxena(ah);
} else if (!flush) {
ath_rx_edma_buf_link(sc, qtype);
}
if (!budget--)
break;
} while (1);
if (!(ah->imask & ATH9K_INT_RXEOL)) {
ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
ath9k_hw_set_interrupts(ah);
}
return 0;
}