diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/net/wireless/ath/carl9170/rx.c')
-rw-r--r-- | ANDROID_3.4.5/drivers/net/wireless/ath/carl9170/rx.c | 938 |
1 files changed, 0 insertions, 938 deletions
diff --git a/ANDROID_3.4.5/drivers/net/wireless/ath/carl9170/rx.c b/ANDROID_3.4.5/drivers/net/wireless/ath/carl9170/rx.c deleted file mode 100644 index dc99030e..00000000 --- a/ANDROID_3.4.5/drivers/net/wireless/ath/carl9170/rx.c +++ /dev/null @@ -1,938 +0,0 @@ -/* - * Atheros CARL9170 driver - * - * 802.11 & command trap routines - * - * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> - * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com> - * - * 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; see the file COPYING. If not, see - * http://www.gnu.org/licenses/. - * - * This file incorporates work covered by the following copyright and - * permission notice: - * Copyright (c) 2007-2008 Atheros Communications, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/etherdevice.h> -#include <linux/crc32.h> -#include <net/mac80211.h> -#include "carl9170.h" -#include "hw.h" -#include "cmd.h" - -static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len) -{ - bool restart = false; - enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON; - - if (len > 3) { - if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) { - ar->fw.err_counter++; - if (ar->fw.err_counter > 3) { - restart = true; - reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS; - } - } - - if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) { - ar->fw.bug_counter++; - restart = true; - reason = CARL9170_RR_FATAL_FIRMWARE_ERROR; - } - } - - wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf); - - if (restart) - carl9170_restart(ar, reason); -} - -static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp) -{ - u32 ps; - bool new_ps; - - ps = le32_to_cpu(rsp->psm.state); - - new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE; - if (ar->ps.state != new_ps) { - if (!new_ps) { - ar->ps.sleep_ms = jiffies_to_msecs(jiffies - - ar->ps.last_action); - } - - ar->ps.last_action = jiffies; - - ar->ps.state = new_ps; - } -} - -static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq) -{ - if (ar->cmd_seq < -1) - return 0; - - /* - * Initialize Counter - */ - if (ar->cmd_seq < 0) - ar->cmd_seq = seq; - - /* - * The sequence is strictly monotonic increasing and it never skips! - * - * Therefore we can safely assume that whenever we received an - * unexpected sequence we have lost some valuable data. - */ - if (seq != ar->cmd_seq) { - int count; - - count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs; - - wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! " - "w:%d g:%d\n", count, ar->cmd_seq, seq); - - carl9170_restart(ar, CARL9170_RR_LOST_RSP); - return -EIO; - } - - ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs; - return 0; -} - -static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) -{ - /* - * Some commands may have a variable response length - * and we cannot predict the correct length in advance. - * So we only check if we provided enough space for the data. - */ - if (unlikely(ar->readlen != (len - 4))) { - dev_warn(&ar->udev->dev, "received invalid command response:" - "got %d, instead of %d\n", len - 4, ar->readlen); - print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET, - ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f); - print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET, - buffer, len); - /* - * Do not complete. The command times out, - * and we get a stack trace from there. - */ - carl9170_restart(ar, CARL9170_RR_INVALID_RSP); - } - - spin_lock(&ar->cmd_lock); - if (ar->readbuf) { - if (len >= 4) - memcpy(ar->readbuf, buffer + 4, len - 4); - - ar->readbuf = NULL; - } - complete(&ar->cmd_wait); - spin_unlock(&ar->cmd_lock); -} - -void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) -{ - struct carl9170_rsp *cmd = (void *) buf; - struct ieee80211_vif *vif; - - if (carl9170_check_sequence(ar, cmd->hdr.seq)) - return; - - if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { - if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) - carl9170_cmd_callback(ar, len, buf); - - return; - } - - if (unlikely(cmd->hdr.len != (len - 4))) { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "FW: received over-/under" - "sized event %x (%d, but should be %d).\n", - cmd->hdr.cmd, cmd->hdr.len, len - 4); - - print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, - buf, len); - } - - return; - } - - /* hardware event handlers */ - switch (cmd->hdr.cmd) { - case CARL9170_RSP_PRETBTT: - /* pre-TBTT event */ - rcu_read_lock(); - vif = carl9170_get_main_vif(ar); - - if (!vif) { - rcu_read_unlock(); - break; - } - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - carl9170_handle_ps(ar, cmd); - break; - - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - carl9170_update_beacon(ar, true); - break; - - default: - break; - } - rcu_read_unlock(); - - break; - - - case CARL9170_RSP_TXCOMP: - /* TX status notification */ - carl9170_tx_process_status(ar, cmd); - break; - - case CARL9170_RSP_BEACON_CONFIG: - /* - * (IBSS) beacon send notification - * bytes: 04 c2 XX YY B4 B3 B2 B1 - * - * XX always 80 - * YY always 00 - * B1-B4 "should" be the number of send out beacons. - */ - break; - - case CARL9170_RSP_ATIM: - /* End of Atim Window */ - break; - - case CARL9170_RSP_WATCHDOG: - /* Watchdog Interrupt */ - carl9170_restart(ar, CARL9170_RR_WATCHDOG); - break; - - case CARL9170_RSP_TEXT: - /* firmware debug */ - carl9170_dbg_message(ar, (char *)buf + 4, len - 4); - break; - - case CARL9170_RSP_HEXDUMP: - wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4); - print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE, - (char *)buf + 4, len - 4); - break; - - case CARL9170_RSP_RADAR: - if (!net_ratelimit()) - break; - - wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this " - "incident to linux-wireless@vger.kernel.org !\n"); - break; - - case CARL9170_RSP_GPIO: -#ifdef CONFIG_CARL9170_WPC - if (ar->wps.pbc) { - bool state = !!(cmd->gpio.gpio & cpu_to_le32( - AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)); - - if (state != ar->wps.pbc_state) { - ar->wps.pbc_state = state; - input_report_key(ar->wps.pbc, KEY_WPS_BUTTON, - state); - input_sync(ar->wps.pbc); - } - } -#endif /* CONFIG_CARL9170_WPC */ - break; - - case CARL9170_RSP_BOOT: - complete(&ar->fw_boot_wait); - break; - - default: - wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n", - cmd->hdr.cmd); - print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); - break; - } -} - -static int carl9170_rx_mac_status(struct ar9170 *ar, - struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac, - struct ieee80211_rx_status *status) -{ - struct ieee80211_channel *chan; - u8 error, decrypt; - - BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); - BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4); - - error = mac->error; - - if (error & AR9170_RX_ERROR_WRONG_RA) { - if (!ar->sniffer_enabled) - return -EINVAL; - } - - if (error & AR9170_RX_ERROR_PLCP) { - if (!(ar->filter_state & FIF_PLCPFAIL)) - return -EINVAL; - - status->flag |= RX_FLAG_FAILED_PLCP_CRC; - } - - if (error & AR9170_RX_ERROR_FCS) { - ar->tx_fcs_errors++; - - if (!(ar->filter_state & FIF_FCSFAIL)) - return -EINVAL; - - status->flag |= RX_FLAG_FAILED_FCS_CRC; - } - - decrypt = ar9170_get_decrypt_type(mac); - if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && - decrypt != AR9170_ENC_ALG_NONE) { - if ((decrypt == AR9170_ENC_ALG_TKIP) && - (error & AR9170_RX_ERROR_MMIC)) - status->flag |= RX_FLAG_MMIC_ERROR; - - status->flag |= RX_FLAG_DECRYPTED; - } - - if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled) - return -ENODATA; - - error &= ~(AR9170_RX_ERROR_MMIC | - AR9170_RX_ERROR_FCS | - AR9170_RX_ERROR_WRONG_RA | - AR9170_RX_ERROR_DECRYPT | - AR9170_RX_ERROR_PLCP); - - /* drop any other error frames */ - if (unlikely(error)) { - /* TODO: update netdevice's RX dropped/errors statistics */ - - if (net_ratelimit()) - wiphy_dbg(ar->hw->wiphy, "received frame with " - "suspicious error code (%#x).\n", error); - - return -EINVAL; - } - - chan = ar->channel; - if (chan) { - status->band = chan->band; - status->freq = chan->center_freq; - } - - switch (mac->status & AR9170_RX_STATUS_MODULATION) { - case AR9170_RX_STATUS_MODULATION_CCK: - if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) - status->flag |= RX_FLAG_SHORTPRE; - switch (head->plcp[0]) { - case AR9170_RX_PHY_RATE_CCK_1M: - status->rate_idx = 0; - break; - case AR9170_RX_PHY_RATE_CCK_2M: - status->rate_idx = 1; - break; - case AR9170_RX_PHY_RATE_CCK_5M: - status->rate_idx = 2; - break; - case AR9170_RX_PHY_RATE_CCK_11M: - status->rate_idx = 3; - break; - default: - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "invalid plcp cck " - "rate (%x).\n", head->plcp[0]); - } - - return -EINVAL; - } - break; - - case AR9170_RX_STATUS_MODULATION_DUPOFDM: - case AR9170_RX_STATUS_MODULATION_OFDM: - switch (head->plcp[0] & 0xf) { - case AR9170_TXRX_PHY_RATE_OFDM_6M: - status->rate_idx = 0; - break; - case AR9170_TXRX_PHY_RATE_OFDM_9M: - status->rate_idx = 1; - break; - case AR9170_TXRX_PHY_RATE_OFDM_12M: - status->rate_idx = 2; - break; - case AR9170_TXRX_PHY_RATE_OFDM_18M: - status->rate_idx = 3; - break; - case AR9170_TXRX_PHY_RATE_OFDM_24M: - status->rate_idx = 4; - break; - case AR9170_TXRX_PHY_RATE_OFDM_36M: - status->rate_idx = 5; - break; - case AR9170_TXRX_PHY_RATE_OFDM_48M: - status->rate_idx = 6; - break; - case AR9170_TXRX_PHY_RATE_OFDM_54M: - status->rate_idx = 7; - break; - default: - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "invalid plcp ofdm " - "rate (%x).\n", head->plcp[0]); - } - - return -EINVAL; - } - if (status->band == IEEE80211_BAND_2GHZ) - status->rate_idx += 4; - break; - - case AR9170_RX_STATUS_MODULATION_HT: - if (head->plcp[3] & 0x80) - status->flag |= RX_FLAG_40MHZ; - if (head->plcp[6] & 0x80) - status->flag |= RX_FLAG_SHORT_GI; - - status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); - status->flag |= RX_FLAG_HT; - break; - - default: - BUG(); - return -ENOSYS; - } - - return 0; -} - -static void carl9170_rx_phy_status(struct ar9170 *ar, - struct ar9170_rx_phystatus *phy, struct ieee80211_rx_status *status) -{ - int i; - - BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20); - - for (i = 0; i < 3; i++) - if (phy->rssi[i] != 0x80) - status->antenna |= BIT(i); - - /* post-process RSSI */ - for (i = 0; i < 7; i++) - if (phy->rssi[i] & 0x80) - phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; - - /* TODO: we could do something with phy_errors */ - status->signal = ar->noise[0] + phy->rssi_combined; -} - -static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len) -{ - struct sk_buff *skb; - int reserved = 0; - struct ieee80211_hdr *hdr = (void *) buf; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - reserved += NET_IP_ALIGN; - - if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) - reserved += NET_IP_ALIGN; - } - - if (ieee80211_has_a4(hdr->frame_control)) - reserved += NET_IP_ALIGN; - - reserved = 32 + (reserved & NET_IP_ALIGN); - - skb = dev_alloc_skb(len + reserved); - if (likely(skb)) { - skb_reserve(skb, reserved); - memcpy(skb_put(skb, len), buf, len); - } - - return skb; -} - -static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie) -{ - struct ieee80211_mgmt *mgmt = (void *)data; - u8 *pos, *end; - - pos = (u8 *)mgmt->u.beacon.variable; - end = data + len; - while (pos < end) { - if (pos + 2 + pos[1] > end) - return NULL; - - if (pos[0] == ie) - return pos; - - pos += 2 + pos[1]; - } - return NULL; -} - -/* - * NOTE: - * - * The firmware is in charge of waking up the device just before - * the AP is expected to transmit the next beacon. - * - * This leaves the driver with the important task of deciding when - * to set the PHY back to bed again. - */ -static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) -{ - struct ieee80211_hdr *hdr = (void *) data; - struct ieee80211_tim_ie *tim_ie; - u8 *tim; - u8 tim_len; - bool cam; - - if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS))) - return; - - /* check if this really is a beacon */ - if (!ieee80211_is_beacon(hdr->frame_control)) - return; - - /* min. beacon length + FCS_LEN */ - if (len <= 40 + FCS_LEN) - return; - - /* and only beacons from the associated BSSID, please */ - if (compare_ether_addr(hdr->addr3, ar->common.curbssid) || - !ar->common.curaid) - return; - - ar->ps.last_beacon = jiffies; - - tim = carl9170_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); - if (!tim) - return; - - if (tim[1] < sizeof(*tim_ie)) - return; - - tim_len = tim[1]; - tim_ie = (struct ieee80211_tim_ie *) &tim[2]; - - if (!WARN_ON_ONCE(!ar->hw->conf.ps_dtim_period)) - ar->ps.dtim_counter = (tim_ie->dtim_count - 1) % - ar->hw->conf.ps_dtim_period; - - /* Check whenever the PHY can be turned off again. */ - - /* 1. What about buffered unicast traffic for our AID? */ - cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid); - - /* 2. Maybe the AP wants to send multicast/broadcast data? */ - cam |= !!(tim_ie->bitmap_ctrl & 0x01); - - if (!cam) { - /* back to low-power land. */ - ar->ps.off_override &= ~PS_OFF_BCN; - carl9170_ps_check(ar); - } else { - /* force CAM */ - ar->ps.off_override |= PS_OFF_BCN; - } -} - -static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms) -{ - __le16 fc; - - if ((ms & AR9170_RX_STATUS_MPDU) == AR9170_RX_STATUS_MPDU_SINGLE) { - /* - * This frame is not part of an aMPDU. - * Therefore it is not subjected to any - * of the following content restrictions. - */ - return true; - } - - /* - * "802.11n - 7.4a.3 A-MPDU contents" describes in which contexts - * certain frame types can be part of an aMPDU. - * - * In order to keep the processing cost down, I opted for a - * stateless filter solely based on the frame control field. - */ - - fc = ((struct ieee80211_hdr *)buf)->frame_control; - if (ieee80211_is_data_qos(fc) && ieee80211_is_data_present(fc)) - return true; - - if (ieee80211_is_ack(fc) || ieee80211_is_back(fc) || - ieee80211_is_back_req(fc)) - return true; - - if (ieee80211_is_action(fc)) - return true; - - return false; -} - -/* - * If the frame alignment is right (or the kernel has - * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there - * is only a single MPDU in the USB frame, then we could - * submit to mac80211 the SKB directly. However, since - * there may be multiple packets in one SKB in stream - * mode, and we need to observe the proper ordering, - * this is non-trivial. - */ - -static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) -{ - struct ar9170_rx_head *head; - struct ar9170_rx_macstatus *mac; - struct ar9170_rx_phystatus *phy = NULL; - struct ieee80211_rx_status status; - struct sk_buff *skb; - int mpdu_len; - u8 mac_status; - - if (!IS_STARTED(ar)) - return; - - if (unlikely(len < sizeof(*mac))) - goto drop; - - mpdu_len = len - sizeof(*mac); - - mac = (void *)(buf + mpdu_len); - mac_status = mac->status; - switch (mac_status & AR9170_RX_STATUS_MPDU) { - case AR9170_RX_STATUS_MPDU_FIRST: - /* Aggregated MPDUs start with an PLCP header */ - if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) { - head = (void *) buf; - - /* - * The PLCP header needs to be cached for the - * following MIDDLE + LAST A-MPDU packets. - * - * So, if you are wondering why all frames seem - * to share a common RX status information, - * then you have the answer right here... - */ - memcpy(&ar->rx_plcp, (void *) buf, - sizeof(struct ar9170_rx_head)); - - mpdu_len -= sizeof(struct ar9170_rx_head); - buf += sizeof(struct ar9170_rx_head); - - ar->rx_has_plcp = true; - } else { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "plcp info " - "is clipped.\n"); - } - - goto drop; - } - break; - - case AR9170_RX_STATUS_MPDU_LAST: - /* - * The last frame of an A-MPDU has an extra tail - * which does contain the phy status of the whole - * aggregate. - */ - - if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) { - mpdu_len -= sizeof(struct ar9170_rx_phystatus); - phy = (void *)(buf + mpdu_len); - } else { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "frame tail " - "is clipped.\n"); - } - - goto drop; - } - - case AR9170_RX_STATUS_MPDU_MIDDLE: - /* These are just data + mac status */ - if (unlikely(!ar->rx_has_plcp)) { - if (!net_ratelimit()) - return; - - wiphy_err(ar->hw->wiphy, "rx stream does not start " - "with a first_mpdu frame tag.\n"); - - goto drop; - } - - head = &ar->rx_plcp; - break; - - case AR9170_RX_STATUS_MPDU_SINGLE: - /* single mpdu has both: plcp (head) and phy status (tail) */ - head = (void *) buf; - - mpdu_len -= sizeof(struct ar9170_rx_head); - mpdu_len -= sizeof(struct ar9170_rx_phystatus); - - buf += sizeof(struct ar9170_rx_head); - phy = (void *)(buf + mpdu_len); - break; - - default: - BUG_ON(1); - break; - } - - /* FC + DU + RA + FCS */ - if (unlikely(mpdu_len < (2 + 2 + ETH_ALEN + FCS_LEN))) - goto drop; - - memset(&status, 0, sizeof(status)); - if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) - goto drop; - - if (!carl9170_ampdu_check(ar, buf, mac_status)) - goto drop; - - if (phy) - carl9170_rx_phy_status(ar, phy, &status); - - carl9170_ps_beacon(ar, buf, mpdu_len); - - skb = carl9170_rx_copy_data(buf, mpdu_len); - if (!skb) - goto drop; - - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(ar->hw, skb); - return; - -drop: - ar->rx_dropped++; -} - -static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf, - const unsigned int resplen) -{ - struct carl9170_rsp *cmd; - int i = 0; - - while (i < resplen) { - cmd = (void *) &respbuf[i]; - - i += cmd->hdr.len + 4; - if (unlikely(i > resplen)) - break; - - carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4); - } - - if (unlikely(i != resplen)) { - if (!net_ratelimit()) - return; - - wiphy_err(ar->hw->wiphy, "malformed firmware trap:\n"); - print_hex_dump_bytes("rxcmd:", DUMP_PREFIX_OFFSET, - respbuf, resplen); - } -} - -static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len) -{ - unsigned int i = 0; - - /* weird thing, but this is the same in the original driver */ - while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) { - i += 2; - len -= 2; - buf += 2; - } - - if (unlikely(len < 4)) - return; - - /* found the 6 * 0xffff marker? */ - if (i == 12) - carl9170_rx_untie_cmds(ar, buf, len); - else - carl9170_handle_mpdu(ar, buf, len); -} - -static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) -{ - unsigned int tlen, wlen = 0, clen = 0; - struct ar9170_stream *rx_stream; - u8 *tbuf; - - tbuf = buf; - tlen = len; - - while (tlen >= 4) { - rx_stream = (void *) tbuf; - clen = le16_to_cpu(rx_stream->length); - wlen = ALIGN(clen, 4); - - /* check if this is stream has a valid tag.*/ - if (rx_stream->tag != cpu_to_le16(AR9170_RX_STREAM_TAG)) { - /* - * TODO: handle the highly unlikely event that the - * corrupted stream has the TAG at the right position. - */ - - /* check if the frame can be repaired. */ - if (!ar->rx_failover_missing) { - - /* this is not "short read". */ - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, - "missing tag!\n"); - } - - __carl9170_rx(ar, tbuf, tlen); - return; - } - - if (ar->rx_failover_missing > tlen) { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, - "possible multi " - "stream corruption!\n"); - goto err_telluser; - } else { - goto err_silent; - } - } - - memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); - ar->rx_failover_missing -= tlen; - - if (ar->rx_failover_missing <= 0) { - /* - * nested carl9170_rx_stream call! - * - * termination is guaranteed, even when the - * combined frame also have an element with - * a bad tag. - */ - - ar->rx_failover_missing = 0; - carl9170_rx_stream(ar, ar->rx_failover->data, - ar->rx_failover->len); - - skb_reset_tail_pointer(ar->rx_failover); - skb_trim(ar->rx_failover, 0); - } - - return; - } - - /* check if stream is clipped */ - if (wlen > tlen - 4) { - if (ar->rx_failover_missing) { - /* TODO: handle double stream corruption. */ - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "double rx " - "stream corruption!\n"); - goto err_telluser; - } else { - goto err_silent; - } - } - - /* - * save incomplete data set. - * the firmware will resend the missing bits when - * the rx - descriptor comes round again. - */ - - memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); - ar->rx_failover_missing = clen - tlen; - return; - } - __carl9170_rx(ar, rx_stream->payload, clen); - - tbuf += wlen + 4; - tlen -= wlen + 4; - } - - if (tlen) { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "%d bytes of unprocessed " - "data left in rx stream!\n", tlen); - } - - goto err_telluser; - } - - return; - -err_telluser: - wiphy_err(ar->hw->wiphy, "damaged RX stream data [want:%d, " - "data:%d, rx:%d, pending:%d ]\n", clen, wlen, tlen, - ar->rx_failover_missing); - - if (ar->rx_failover_missing) - print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET, - ar->rx_failover->data, - ar->rx_failover->len); - - print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET, - buf, len); - - wiphy_err(ar->hw->wiphy, "please check your hardware and cables, if " - "you see this message frequently.\n"); - -err_silent: - if (ar->rx_failover_missing) { - skb_reset_tail_pointer(ar->rx_failover); - skb_trim(ar->rx_failover, 0); - ar->rx_failover_missing = 0; - } -} - -void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len) -{ - if (ar->fw.rx_stream) - carl9170_rx_stream(ar, buf, len); - else - __carl9170_rx(ar, buf, len); -} |