/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> #include <net/mac80211.h> #include "iwl-eeprom.h" #include "iwl-debug.h" #include "iwl-core.h" #include "iwl-io.h" #include "iwl-power.h" #include "iwl-shared.h" #include "iwl-agn.h" #include "iwl-trans.h" const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; #ifdef CONFIG_IWLWIFI_DEBUGFS #define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES) void iwl_reset_traffic_log(struct iwl_priv *priv) { priv->tx_traffic_idx = 0; priv->rx_traffic_idx = 0; if (priv->tx_traffic) memset(priv->tx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); if (priv->rx_traffic) memset(priv->rx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); } int iwl_alloc_traffic_mem(struct iwl_priv *priv) { u32 traffic_size = IWL_TRAFFIC_DUMP_SIZE; if (iwl_have_debug_level(IWL_DL_TX)) { if (!priv->tx_traffic) { priv->tx_traffic = kzalloc(traffic_size, GFP_KERNEL); if (!priv->tx_traffic) return -ENOMEM; } } if (iwl_have_debug_level(IWL_DL_RX)) { if (!priv->rx_traffic) { priv->rx_traffic = kzalloc(traffic_size, GFP_KERNEL); if (!priv->rx_traffic) return -ENOMEM; } } iwl_reset_traffic_log(priv); return 0; } void iwl_free_traffic_mem(struct iwl_priv *priv) { kfree(priv->tx_traffic); priv->tx_traffic = NULL; kfree(priv->rx_traffic); priv->rx_traffic = NULL; } void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, u16 length, struct ieee80211_hdr *header) { __le16 fc; u16 len; if (likely(!iwl_have_debug_level(IWL_DL_TX))) return; if (!priv->tx_traffic) return; fc = header->frame_control; if (ieee80211_is_data(fc)) { len = (length > IWL_TRAFFIC_ENTRY_SIZE) ? IWL_TRAFFIC_ENTRY_SIZE : length; memcpy((priv->tx_traffic + (priv->tx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), header, len); priv->tx_traffic_idx = (priv->tx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; } } void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, u16 length, struct ieee80211_hdr *header) { __le16 fc; u16 len; if (likely(!iwl_have_debug_level(IWL_DL_RX))) return; if (!priv->rx_traffic) return; fc = header->frame_control; if (ieee80211_is_data(fc)) { len = (length > IWL_TRAFFIC_ENTRY_SIZE) ? IWL_TRAFFIC_ENTRY_SIZE : length; memcpy((priv->rx_traffic + (priv->rx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), header, len); priv->rx_traffic_idx = (priv->rx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; } } const char *get_mgmt_string(int cmd) { switch (cmd) { IWL_CMD(MANAGEMENT_ASSOC_REQ); IWL_CMD(MANAGEMENT_ASSOC_RESP); IWL_CMD(MANAGEMENT_REASSOC_REQ); IWL_CMD(MANAGEMENT_REASSOC_RESP); IWL_CMD(MANAGEMENT_PROBE_REQ); IWL_CMD(MANAGEMENT_PROBE_RESP); IWL_CMD(MANAGEMENT_BEACON); IWL_CMD(MANAGEMENT_ATIM); IWL_CMD(MANAGEMENT_DISASSOC); IWL_CMD(MANAGEMENT_AUTH); IWL_CMD(MANAGEMENT_DEAUTH); IWL_CMD(MANAGEMENT_ACTION); default: return "UNKNOWN"; } } const char *get_ctrl_string(int cmd) { switch (cmd) { IWL_CMD(CONTROL_BACK_REQ); IWL_CMD(CONTROL_BACK); IWL_CMD(CONTROL_PSPOLL); IWL_CMD(CONTROL_RTS); IWL_CMD(CONTROL_CTS); IWL_CMD(CONTROL_ACK); IWL_CMD(CONTROL_CFEND); IWL_CMD(CONTROL_CFENDACK); default: return "UNKNOWN"; } } void iwl_clear_traffic_stats(struct iwl_priv *priv) { memset(&priv->tx_stats, 0, sizeof(struct traffic_stats)); memset(&priv->rx_stats, 0, sizeof(struct traffic_stats)); } /* * if CONFIG_IWLWIFI_DEBUGFS defined, iwl_update_stats function will * record all the MGMT, CTRL and DATA pkt for both TX and Rx pass. * Use debugFs to display the rx/rx_statistics * if CONFIG_IWLWIFI_DEBUGFS not being defined, then no MGMT and CTRL * information will be recorded, but DATA pkt still will be recorded * for the reason of iwl_led.c need to control the led blinking based on * number of tx and rx data. * */ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) { struct traffic_stats *stats; if (is_tx) stats = &priv->tx_stats; else stats = &priv->rx_stats; if (ieee80211_is_mgmt(fc)) { switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): stats->mgmt[MANAGEMENT_ASSOC_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): stats->mgmt[MANAGEMENT_ASSOC_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): stats->mgmt[MANAGEMENT_REASSOC_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): stats->mgmt[MANAGEMENT_REASSOC_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): stats->mgmt[MANAGEMENT_PROBE_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): stats->mgmt[MANAGEMENT_PROBE_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_BEACON): stats->mgmt[MANAGEMENT_BEACON]++; break; case cpu_to_le16(IEEE80211_STYPE_ATIM): stats->mgmt[MANAGEMENT_ATIM]++; break; case cpu_to_le16(IEEE80211_STYPE_DISASSOC): stats->mgmt[MANAGEMENT_DISASSOC]++; break; case cpu_to_le16(IEEE80211_STYPE_AUTH): stats->mgmt[MANAGEMENT_AUTH]++; break; case cpu_to_le16(IEEE80211_STYPE_DEAUTH): stats->mgmt[MANAGEMENT_DEAUTH]++; break; case cpu_to_le16(IEEE80211_STYPE_ACTION): stats->mgmt[MANAGEMENT_ACTION]++; break; } } else if (ieee80211_is_ctl(fc)) { switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): stats->ctrl[CONTROL_BACK_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_BACK): stats->ctrl[CONTROL_BACK]++; break; case cpu_to_le16(IEEE80211_STYPE_PSPOLL): stats->ctrl[CONTROL_PSPOLL]++; break; case cpu_to_le16(IEEE80211_STYPE_RTS): stats->ctrl[CONTROL_RTS]++; break; case cpu_to_le16(IEEE80211_STYPE_CTS): stats->ctrl[CONTROL_CTS]++; break; case cpu_to_le16(IEEE80211_STYPE_ACK): stats->ctrl[CONTROL_ACK]++; break; case cpu_to_le16(IEEE80211_STYPE_CFEND): stats->ctrl[CONTROL_CFEND]++; break; case cpu_to_le16(IEEE80211_STYPE_CFENDACK): stats->ctrl[CONTROL_CFENDACK]++; break; } } else { /* data */ stats->data_cnt++; stats->data_bytes += len; } } #endif int iwl_force_rf_reset(struct iwl_priv *priv, bool external) { struct iwl_rf_reset *rf_reset; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return -EAGAIN; if (!iwl_is_any_associated(priv)) { IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); return -ENOLINK; } rf_reset = &priv->rf_reset; rf_reset->reset_request_count++; if (!external && rf_reset->last_reset_jiffies && time_after(rf_reset->last_reset_jiffies + IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { IWL_DEBUG_INFO(priv, "RF reset rejected\n"); rf_reset->reset_reject_count++; return -EAGAIN; } rf_reset->reset_success_count++; rf_reset->last_reset_jiffies = jiffies; /* * There is no easy and better way to force reset the radio, * the only known method is switching channel which will force to * reset and tune the radio. * Use internal short scan (single channel) operation to should * achieve this objective. * Driver should reset the radio when number of consecutive missed * beacon, or any other uCode error condition detected. */ IWL_DEBUG_INFO(priv, "perform radio reset.\n"); iwl_internal_short_hw_scan(priv); return 0; } int iwl_cmd_echo_test(struct iwl_priv *priv) { int ret; struct iwl_host_cmd cmd = { .id = REPLY_ECHO, .len = { 0 }, .flags = CMD_SYNC, }; ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); else IWL_DEBUG_INFO(priv, "echo testing pass\n"); return ret; }