X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=carlfw%2Fsrc%2Fwlanrx.c;fp=carlfw%2Fsrc%2Fwlanrx.c;h=02cbce5363a28741a9a86833cef8c6d89cd8d029;hb=11c1064a307a661df20e237e3623e1b5d88641fa;hp=0000000000000000000000000000000000000000;hpb=5d61afe9cc7019ce23856396488846950e656245;p=carl9170fw.git diff --git a/carlfw/src/wlanrx.c b/carlfw/src/wlanrx.c new file mode 100644 index 0000000..02cbce5 --- /dev/null +++ b/carlfw/src/wlanrx.c @@ -0,0 +1,174 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WLAN receive routines + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg + * Copyright 2009-2012 Christian Lamparter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "shared/phy.h" +#include "hostif.h" +#include "timer.h" +#include "wl.h" +#include "printf.h" +#include "rf.h" +#include "linux/ieee80211.h" +#include "wol.h" + +static struct carl9170_bar_ctx *wlan_get_bar_cache_buffer(void) +{ + struct carl9170_bar_ctx *tmp; + + tmp = &fw.wlan.ba_cache[fw.wlan.ba_tail_idx]; + fw.wlan.ba_tail_idx++; + fw.wlan.ba_tail_idx %= CONFIG_CARL9170FW_BACK_REQS_NUM; + if (fw.wlan.queued_ba < CONFIG_CARL9170FW_BACK_REQS_NUM) + fw.wlan.queued_ba++; + + return tmp; +} + +static void handle_bar(struct dma_desc *desc __unused, struct ieee80211_hdr *hdr, + unsigned int len, unsigned int mac_err) +{ + struct ieee80211_bar *bar; + struct carl9170_bar_ctx *ctx; + + if (unlikely(mac_err)) { + /* + * This check does a number of things: + * 1. checks if the frame is in good nick + * 2. checks if the RA (MAC) matches + */ + return ; + } + + if (unlikely(len < (sizeof(struct ieee80211_bar) + FCS_LEN))) { + /* + * Sneaky, corrupted BARs... but not with us! + */ + + return ; + } + + bar = (void *) hdr; + + if ((bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_MULTI_TID)) || + !(bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA))) { + /* not implemented yet */ + + return ; + } + + ctx = wlan_get_bar_cache_buffer(); + + memcpy(ctx->ra, bar->ra, 6); + memcpy(ctx->ta, bar->ta, 6); + ctx->control = bar->control; + ctx->start_seq_num = bar->start_seq_num; +} + +static unsigned int wlan_rx_filter(struct dma_desc *desc) +{ + struct ieee80211_hdr *hdr; + unsigned int data_len; + unsigned int rx_filter; + unsigned int mac_err; + + data_len = ar9170_get_rx_mpdu_len(desc); + mac_err = ar9170_get_rx_macstatus_error(desc); + +#define AR9170_RX_ERROR_BAD (AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_PLCP) + + if (unlikely(data_len < (4 + 6 + FCS_LEN) || + desc->totalLen > CONFIG_CARL9170FW_RX_FRAME_LEN) || + mac_err & AR9170_RX_ERROR_BAD) { + /* + * This frame is too damaged to do anything + * useful with it. + */ + + return CARL9170_RX_FILTER_BAD; + } + + rx_filter = 0; + if (mac_err & AR9170_RX_ERROR_WRONG_RA) + rx_filter |= CARL9170_RX_FILTER_OTHER_RA; + + if (mac_err & AR9170_RX_ERROR_DECRYPT) + rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL; + + hdr = ar9170_get_rx_i3e(desc); + if (likely(ieee80211_is_data(hdr->frame_control))) { + rx_filter |= CARL9170_RX_FILTER_DATA; + } else if (ieee80211_is_ctl(hdr->frame_control)) { + switch (le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BACK_REQ: + handle_bar(desc, hdr, data_len, mac_err); + rx_filter |= CARL9170_RX_FILTER_CTL_BACKR; + break; + case IEEE80211_STYPE_PSPOLL: + rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL; + break; + case IEEE80211_STYPE_BACK: + if (fw.wlan.queued_bar) { + /* + * Don't filter block acks when the application + * has queued BARs. This is because the firmware + * can't do the accouting and the application + * has to sort out if the BA belongs to any BARs. + */ + break; + } + /* otherwise fall through */ + default: + rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; + break; + } + } else { + /* ieee80211_is_mgmt */ + rx_filter |= CARL9170_RX_FILTER_MGMT; + } + + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { + wol_rx(rx_filter, hdr, min(data_len, + (unsigned int)AR9170_BLOCK_SIZE)); + } + +#undef AR9170_RX_ERROR_BAD + + return rx_filter; +} + +void handle_wlan_rx(void) +{ + struct dma_desc *desc; + + for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) { + if (!(wlan_rx_filter(desc) & fw.wlan.rx_filter)) { + dma_put(&fw.pta.up_queue, desc); + up_trigger(); + } else { + dma_reclaim(&fw.wlan.rx_queue, desc); + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } + } +}