📄 iwl-4965-rs.c
字号:
/****************************************************************************** * * Copyright(c) 2005 - 2007 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. * * Contact Information: * James P. Ketrenos <ipw2100-admin@linux.intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/#include <linux/kernel.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/wireless.h>#include <net/mac80211.h>#include <net/ieee80211.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/workqueue.h>#define IWL 4965#include "../net/mac80211/ieee80211_rate.h"#include "iwlwifi.h"#include "iwl-helpers.h"#define RS_NAME "iwl-4965-rs"#define NUM_TRY_BEFORE_ANTENNA_TOGGLE 1#define IWL_NUMBER_TRY 1#define IWL_HT_NUMBER_TRY 3#define IWL_RATE_MAX_WINDOW 62#define IWL_RATE_HIGH_TH 10880#define IWL_RATE_MIN_FAILURE_TH 6#define IWL_RATE_MIN_SUCCESS_TH 8#define IWL_RATE_DECREASE_TH 1920#define IWL_RATE_INCREASE_TH 8960#define IWL_RATE_SCALE_FLUSH_INTVL (2*HZ) /*2 seconds */static u8 rs_ht_to_legacy[] = { IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX};struct iwl_rate { u32 rate_n_flags;} __attribute__ ((packed));struct iwl_rate_scale_data { u64 data; s32 success_counter; s32 success_ratio; s32 counter; s32 average_tpt; unsigned long stamp;};struct iwl_scale_tbl_info { enum iwl_table_type lq_type; enum iwl_antenna_type antenna_type; u8 is_SGI; u8 is_fat; u8 is_dup; u8 action; s32 *expected_tpt; struct iwl_rate current_rate; struct iwl_rate_scale_data win[IWL_RATE_COUNT];};struct iwl_rate_scale_priv { u8 active_tbl; u8 enable_counter; u8 stay_in_tbl; u8 search_better_tbl; s32 last_tpt; u32 table_count_limit; u32 max_failure_limit; u32 max_success_limit; u32 table_count; u32 total_failed; u32 total_success; u32 flush_timer; u8 action_counter; u8 antenna; u8 valid_antenna; u8 is_green; u8 is_dup; u8 phymode; u8 ibss_sta_added; u32 supp_rates; u16 active_rate; u16 active_siso_rate; u16 active_mimo_rate; u16 active_rate_basic; struct iwl_link_quality_cmd lq; struct iwl_scale_tbl_info lq_info[LQ_SIZE];#ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; struct dentry *rs_sta_dbgfs_stats_table_file; struct iwl_rate dbg_fixed; struct iwl_priv *drv;#endif};static void rs_rate_scale_perform(struct iwl_priv *priv, struct net_device *dev, struct ieee80211_hdr *hdr, struct sta_info *sta);static void rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data, struct iwl_rate *tx_mcs, struct iwl_link_quality_cmd *tbl);#ifdef CONFIG_MAC80211_DEBUGFSstatic void rs_dbgfs_set_mcs(struct iwl_rate_scale_priv *rs_priv, struct iwl_rate *mcs, int index);#elsestatic void rs_dbgfs_set_mcs(struct iwl_rate_scale_priv *rs_priv, struct iwl_rate *mcs, int index){}#endifstatic s32 expected_tpt_A[IWL_RATE_COUNT] = { 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186};static s32 expected_tpt_G[IWL_RATE_COUNT] = { 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186};static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = { 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202};static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = { 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211};static s32 expected_tpt_mimo20MHz[IWL_RATE_COUNT] = { 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251};static s32 expected_tpt_mimo20MHzSGI[IWL_RATE_COUNT] = { 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257};static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = { 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257};static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = { 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264};static s32 expected_tpt_mimo40MHz[IWL_RATE_COUNT] = { 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289};static s32 expected_tpt_mimo40MHzSGI[IWL_RATE_COUNT] = { 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293};static int iwl_lq_sync_callback(struct iwl_priv *priv, struct iwl_cmd *cmd, struct sk_buff *skb){ /*We didn't cache the SKB; let the caller free it */ return 1;}static inline u8 iwl_rate_get_rate(u32 rate_n_flags){ return (u8)(rate_n_flags & 0xFF);}static int rs_send_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq, u8 flags){#ifdef CONFIG_IWLWIFI_DEBUG int i;#endif int rc = -1; struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = sizeof(struct iwl_link_quality_cmd), .meta.flags = flags, .data = lq, }; if ((lq->sta_id == 0xFF) && (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) return rc; if (lq->sta_id == 0xFF) lq->sta_id = IWL_AP_ID; IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id); IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, lq->general_params.dual_stream_ant_msk);#ifdef CONFIG_IWLWIFI_DEBUG for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) IWL_DEBUG_RATE("lq index %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);#endif if (flags & CMD_ASYNC) cmd.meta.u.callback = iwl_lq_sync_callback; if (iwl_is_associated(priv) && priv->assoc_station_added && priv->lq_mngr.lq_ready) rc = iwl_send_cmd(priv, &cmd); return rc;}static int rs_rate_scale_clear_window(struct iwl_rate_scale_data *window){ window->data = 0; window->success_counter = 0; window->success_ratio = IWL_INVALID_VALUE; window->counter = 0; window->average_tpt = IWL_INVALID_VALUE; window->stamp = 0; return 0;}static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, int scale_index, s32 tpt, u32 status){ int rc = 0; struct iwl_rate_scale_data *window = NULL; u64 mask; u8 win_size = IWL_RATE_MAX_WINDOW; s32 fail_count; if (scale_index < 0) return -1; if (scale_index >= IWL_RATE_COUNT) return -1; window = &(windows[scale_index]); if (window->counter >= win_size) { window->counter = win_size - 1; mask = 1; mask = (mask << (win_size - 1)); if ((window->data & mask)) { window->data &= ~mask; window->success_counter = window->success_counter - 1; } } window->counter = window->counter + 1; mask = window->data; window->data = (mask << 1); if (status != 0) { window->success_counter = window->success_counter + 1; window->data |= 0x1; } if (window->counter > 0) window->success_ratio = 128 * (100 * window->success_counter) / window->counter; else window->success_ratio = IWL_INVALID_VALUE; fail_count = window->counter - window->success_counter; if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) window->average_tpt = (window->success_ratio * tpt + 64) / 128; else window->average_tpt = IWL_INVALID_VALUE; window->stamp = jiffies; return rc;}int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate, struct iwl_scale_tbl_info *tbl, int index, u8 use_green){ int rc = 0; if (is_legacy(tbl->lq_type)) { mcs_rate->rate_n_flags = iwl_rates[index].plcp; if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) mcs_rate->rate_n_flags |= RATE_MCS_CCK_MSK; } else if (is_siso(tbl->lq_type)) { if (index > IWL_LAST_OFDM_RATE) index = IWL_LAST_OFDM_RATE; mcs_rate->rate_n_flags = iwl_rates[index].plcp_siso | RATE_MCS_HT_MSK; } else { if (index > IWL_LAST_OFDM_RATE) index = IWL_LAST_OFDM_RATE; mcs_rate->rate_n_flags = iwl_rates[index].plcp_mimo | RATE_MCS_HT_MSK; } switch (tbl->antenna_type) { case ANT_BOTH: mcs_rate->rate_n_flags |= RATE_MCS_ANT_AB_MSK; break; case ANT_MAIN: mcs_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK; break; case ANT_AUX: mcs_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK; break; case ANT_NONE: break; } if (is_legacy(tbl->lq_type)) return rc; if (tbl->is_fat) { if (tbl->is_dup) mcs_rate->rate_n_flags |= RATE_MCS_DUP_MSK; else mcs_rate->rate_n_flags |= RATE_MCS_FAT_MSK; } if (tbl->is_SGI) mcs_rate->rate_n_flags |= RATE_MCS_SGI_MSK; if (use_green) { mcs_rate->rate_n_flags |= RATE_MCS_GF_MSK; if (is_siso(tbl->lq_type)) mcs_rate->rate_n_flags &= ~RATE_MCS_SGI_MSK; } return rc;}static int rs_get_tbl_info_from_mcs(const struct iwl_rate *mcs_rate, int phymode, struct iwl_scale_tbl_info *tbl, int *rate_idx){ int index; u32 ant_msk; index = iwl_rate_index_from_plcp(mcs_rate->rate_n_flags); if (index == IWL_RATE_INVALID) { *rate_idx = -1; return -1; } tbl->is_SGI = 0; tbl->is_fat = 0; tbl->is_dup = 0; tbl->antenna_type = ANT_BOTH; if (!(mcs_rate->rate_n_flags & RATE_MCS_HT_MSK)) { ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK); if (ant_msk == RATE_MCS_ANT_AB_MSK) tbl->lq_type = LQ_NONE; else { if (phymode == MODE_IEEE80211A) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK) tbl->antenna_type = ANT_MAIN; else tbl->antenna_type = ANT_AUX; } *rate_idx = index; } else if (iwl_rate_get_rate(mcs_rate->rate_n_flags) <= IWL_RATE_SISO_60M_PLCP) { tbl->lq_type = LQ_SISO; ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK); if (ant_msk == RATE_MCS_ANT_AB_MSK) tbl->lq_type = LQ_NONE; else { if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK) tbl->antenna_type = ANT_MAIN; else tbl->antenna_type = ANT_AUX; } if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) || (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)) tbl->is_fat = 1; if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; *rate_idx = index; } else { tbl->lq_type = LQ_MIMO; if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) || (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)) tbl->is_fat = 1; if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; *rate_idx = index; } return 0;}static inline void rs_toggle_antenna(struct iwl_rate *new_rate, struct iwl_scale_tbl_info *tbl){ if (tbl->antenna_type == ANT_AUX) { tbl->antenna_type = ANT_MAIN; new_rate->rate_n_flags &= ~RATE_MCS_ANT_B_MSK; new_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK; } else { tbl->antenna_type = ANT_AUX; new_rate->rate_n_flags &= ~RATE_MCS_ANT_A_MSK; new_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK; }}static inline s8 rs_use_green(struct iwl_priv *priv){ s8 rc = 0;#ifdef CONFIG_IWLWIFI_HT if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht) return 0; if ((priv->current_assoc_ht.is_green_field) && !(priv->current_assoc_ht.operating_mode & 0x4)) rc = 1;#endif /*CONFIG_IWLWIFI_HT */ return rc;}/** * rs_get_supported_rates - get the available rates * * if management frame or broadcast frame only return * basic available rates. * */static void rs_get_supported_rates(struct iwl_rate_scale_priv *lq_data, struct ieee80211_hdr *hdr, enum iwl_table_type rate_type, u16 *data_rate){ if (is_legacy(rate_type)) *data_rate = lq_data->active_rate; else { if (is_siso(rate_type)) *data_rate = lq_data->active_siso_rate; else *data_rate = lq_data->active_mimo_rate; } if (hdr && is_multicast_ether_addr(hdr->addr1) && lq_data->active_rate_basic) *data_rate = lq_data->active_rate_basic;}static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type){ u8 high = IWL_RATE_INVALID; u8 low = IWL_RATE_INVALID; /* 802.11A or ht walks to the next literal adjascent rate in * the rate table */ if (is_a_band(rate_type) || !is_legacy(rate_type)) { int i; u32 mask; /* Find the previous rate that is in the rate mask */ i = index - 1; for (mask = (1 << i); i >= 0; i--, mask >>= 1) { if (rate_mask & mask) { low = i; break; } } /* Find the next rate that is in the rate mask */ i = index + 1; for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { if (rate_mask & mask) { high = i; break; } } return (high << 8) | low; } low = index; while (low != IWL_RATE_INVALID) { low = iwl_rates[low].prev_rs; if (low == IWL_RATE_INVALID) break; if (rate_mask & (1 << low)) break; IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low); } high = index; while (high != IWL_RATE_INVALID) { high = iwl_rates[high].next_rs; if (high == IWL_RATE_INVALID) break; if (rate_mask & (1 << high)) break; IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high); } return (high << 8) | low;}static int rs_get_lower_rate(struct iwl_rate_scale_priv *lq_data, struct iwl_scale_tbl_info *tbl, u8 scale_index, u8 ht_possible, struct iwl_rate *mcs_rate){ s32 low; u16 rate_mask; u16 high_low; u8 switch_to_legacy = 0; u8 is_green = lq_data->is_green; /* check if we need to switch from HT to legacy rates. * assumption is that mandatory rates (1Mbps or 6Mbps) * are always supported (spec demand) */ if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { switch_to_legacy = 1; scale_index = rs_ht_to_legacy[scale_index]; if (lq_data->phymode == MODE_IEEE80211A) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; if ((tbl->antenna_type == ANT_BOTH) || (tbl->antenna_type == ANT_NONE)) tbl->antenna_type = ANT_MAIN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -