diff options
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 197 |
1 files changed, 191 insertions, 6 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 4987c3f942ce..3c0a0a86ba12 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -81,6 +81,9 @@ MODULE_PARM_DESC(ap_mode_default, | |||
81 | */ | 81 | */ |
82 | 82 | ||
83 | #define MWL8K_HW_TIMER_REGISTER 0x0000a600 | 83 | #define MWL8K_HW_TIMER_REGISTER 0x0000a600 |
84 | #define BBU_RXRDY_CNT_REG 0x0000a860 | ||
85 | #define NOK_CCA_CNT_REG 0x0000a6a0 | ||
86 | #define BBU_AVG_NOISE_VAL 0x67 | ||
84 | 87 | ||
85 | #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ | 88 | #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ |
86 | MWL8K_A2H_INT_CHNL_SWITCHED | \ | 89 | MWL8K_A2H_INT_CHNL_SWITCHED | \ |
@@ -112,6 +115,8 @@ MODULE_PARM_DESC(ap_mode_default, | |||
112 | */ | 115 | */ |
113 | #define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) | 116 | #define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) |
114 | 117 | ||
118 | #define MWL8K_NUM_CHANS 18 | ||
119 | |||
115 | struct rxd_ops { | 120 | struct rxd_ops { |
116 | int rxd_size; | 121 | int rxd_size; |
117 | void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); | 122 | void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); |
@@ -289,6 +294,12 @@ struct mwl8k_priv { | |||
289 | 294 | ||
290 | /* bitmap of running BSSes */ | 295 | /* bitmap of running BSSes */ |
291 | u32 running_bsses; | 296 | u32 running_bsses; |
297 | |||
298 | /* ACS related */ | ||
299 | bool sw_scan_start; | ||
300 | struct ieee80211_channel *acs_chan; | ||
301 | unsigned long channel_time; | ||
302 | struct survey_info survey[MWL8K_NUM_CHANS]; | ||
292 | }; | 303 | }; |
293 | 304 | ||
294 | #define MAX_WEP_KEY_LEN 13 | 305 | #define MAX_WEP_KEY_LEN 13 |
@@ -396,6 +407,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = { | |||
396 | #define MWL8K_CMD_SET_HW_SPEC 0x0004 | 407 | #define MWL8K_CMD_SET_HW_SPEC 0x0004 |
397 | #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 | 408 | #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 |
398 | #define MWL8K_CMD_GET_STAT 0x0014 | 409 | #define MWL8K_CMD_GET_STAT 0x0014 |
410 | #define MWL8K_CMD_BBP_REG_ACCESS 0x001a | ||
399 | #define MWL8K_CMD_RADIO_CONTROL 0x001c | 411 | #define MWL8K_CMD_RADIO_CONTROL 0x001c |
400 | #define MWL8K_CMD_RF_TX_POWER 0x001e | 412 | #define MWL8K_CMD_RF_TX_POWER 0x001e |
401 | #define MWL8K_CMD_TX_POWER 0x001f | 413 | #define MWL8K_CMD_TX_POWER 0x001f |
@@ -2987,6 +2999,47 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) | |||
2987 | } | 2999 | } |
2988 | 3000 | ||
2989 | /* | 3001 | /* |
3002 | * CMD_BBP_REG_ACCESS. | ||
3003 | */ | ||
3004 | struct mwl8k_cmd_bbp_reg_access { | ||
3005 | struct mwl8k_cmd_pkt header; | ||
3006 | __le16 action; | ||
3007 | __le16 offset; | ||
3008 | u8 value; | ||
3009 | u8 rsrv[3]; | ||
3010 | } __packed; | ||
3011 | |||
3012 | static int | ||
3013 | mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, | ||
3014 | u16 action, | ||
3015 | u16 offset, | ||
3016 | u8 *value) | ||
3017 | { | ||
3018 | struct mwl8k_cmd_bbp_reg_access *cmd; | ||
3019 | int rc; | ||
3020 | |||
3021 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
3022 | if (cmd == NULL) | ||
3023 | return -ENOMEM; | ||
3024 | |||
3025 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); | ||
3026 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); | ||
3027 | cmd->action = cpu_to_le16(action); | ||
3028 | cmd->offset = cpu_to_le16(offset); | ||
3029 | |||
3030 | rc = mwl8k_post_cmd(hw, &cmd->header); | ||
3031 | |||
3032 | if (!rc) | ||
3033 | *value = cmd->value; | ||
3034 | else | ||
3035 | *value = 0; | ||
3036 | |||
3037 | kfree(cmd); | ||
3038 | |||
3039 | return rc; | ||
3040 | } | ||
3041 | |||
3042 | /* | ||
2990 | * CMD_SET_POST_SCAN. | 3043 | * CMD_SET_POST_SCAN. |
2991 | */ | 3044 | */ |
2992 | struct mwl8k_cmd_set_post_scan { | 3045 | struct mwl8k_cmd_set_post_scan { |
@@ -3016,6 +3069,64 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) | |||
3016 | return rc; | 3069 | return rc; |
3017 | } | 3070 | } |
3018 | 3071 | ||
3072 | static int freq_to_idx(struct mwl8k_priv *priv, int freq) | ||
3073 | { | ||
3074 | struct ieee80211_supported_band *sband; | ||
3075 | int band, ch, idx = 0; | ||
3076 | |||
3077 | for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { | ||
3078 | sband = priv->hw->wiphy->bands[band]; | ||
3079 | if (!sband) | ||
3080 | continue; | ||
3081 | |||
3082 | for (ch = 0; ch < sband->n_channels; ch++, idx++) | ||
3083 | if (sband->channels[ch].center_freq == freq) | ||
3084 | goto exit; | ||
3085 | } | ||
3086 | |||
3087 | exit: | ||
3088 | return idx; | ||
3089 | } | ||
3090 | |||
3091 | static void mwl8k_update_survey(struct mwl8k_priv *priv, | ||
3092 | struct ieee80211_channel *channel) | ||
3093 | { | ||
3094 | u32 cca_cnt, rx_rdy; | ||
3095 | s8 nf = 0, idx; | ||
3096 | struct survey_info *survey; | ||
3097 | |||
3098 | idx = freq_to_idx(priv, priv->acs_chan->center_freq); | ||
3099 | if (idx >= MWL8K_NUM_CHANS) { | ||
3100 | wiphy_err(priv->hw->wiphy, "Failed to update survey\n"); | ||
3101 | return; | ||
3102 | } | ||
3103 | |||
3104 | survey = &priv->survey[idx]; | ||
3105 | |||
3106 | cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); | ||
3107 | cca_cnt /= 1000; /* uSecs to mSecs */ | ||
3108 | survey->channel_time_busy = (u64) cca_cnt; | ||
3109 | |||
3110 | rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); | ||
3111 | rx_rdy /= 1000; /* uSecs to mSecs */ | ||
3112 | survey->channel_time_rx = (u64) rx_rdy; | ||
3113 | |||
3114 | priv->channel_time = jiffies - priv->channel_time; | ||
3115 | survey->channel_time = jiffies_to_msecs(priv->channel_time); | ||
3116 | |||
3117 | survey->channel = channel; | ||
3118 | |||
3119 | mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf); | ||
3120 | |||
3121 | /* Make sure sign is negative else ACS at hostapd fails */ | ||
3122 | survey->noise = nf * -1; | ||
3123 | |||
3124 | survey->filled = SURVEY_INFO_NOISE_DBM | | ||
3125 | SURVEY_INFO_CHANNEL_TIME | | ||
3126 | SURVEY_INFO_CHANNEL_TIME_BUSY | | ||
3127 | SURVEY_INFO_CHANNEL_TIME_RX; | ||
3128 | } | ||
3129 | |||
3019 | /* | 3130 | /* |
3020 | * CMD_SET_RF_CHANNEL. | 3131 | * CMD_SET_RF_CHANNEL. |
3021 | */ | 3132 | */ |
@@ -3033,6 +3144,7 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, | |||
3033 | enum nl80211_channel_type channel_type = | 3144 | enum nl80211_channel_type channel_type = |
3034 | cfg80211_get_chandef_type(&conf->chandef); | 3145 | cfg80211_get_chandef_type(&conf->chandef); |
3035 | struct mwl8k_cmd_set_rf_channel *cmd; | 3146 | struct mwl8k_cmd_set_rf_channel *cmd; |
3147 | struct mwl8k_priv *priv = hw->priv; | ||
3036 | int rc; | 3148 | int rc; |
3037 | 3149 | ||
3038 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | 3150 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); |
@@ -3049,13 +3161,29 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, | |||
3049 | else if (channel->band == IEEE80211_BAND_5GHZ) | 3161 | else if (channel->band == IEEE80211_BAND_5GHZ) |
3050 | cmd->channel_flags |= cpu_to_le32(0x00000004); | 3162 | cmd->channel_flags |= cpu_to_le32(0x00000004); |
3051 | 3163 | ||
3052 | if (channel_type == NL80211_CHAN_NO_HT || | 3164 | if (!priv->sw_scan_start) { |
3053 | channel_type == NL80211_CHAN_HT20) | 3165 | if (channel_type == NL80211_CHAN_NO_HT || |
3166 | channel_type == NL80211_CHAN_HT20) | ||
3167 | cmd->channel_flags |= cpu_to_le32(0x00000080); | ||
3168 | else if (channel_type == NL80211_CHAN_HT40MINUS) | ||
3169 | cmd->channel_flags |= cpu_to_le32(0x000001900); | ||
3170 | else if (channel_type == NL80211_CHAN_HT40PLUS) | ||
3171 | cmd->channel_flags |= cpu_to_le32(0x000000900); | ||
3172 | } else { | ||
3054 | cmd->channel_flags |= cpu_to_le32(0x00000080); | 3173 | cmd->channel_flags |= cpu_to_le32(0x00000080); |
3055 | else if (channel_type == NL80211_CHAN_HT40MINUS) | 3174 | } |
3056 | cmd->channel_flags |= cpu_to_le32(0x000001900); | 3175 | |
3057 | else if (channel_type == NL80211_CHAN_HT40PLUS) | 3176 | if (priv->sw_scan_start) { |
3058 | cmd->channel_flags |= cpu_to_le32(0x000000900); | 3177 | /* Store current channel stats |
3178 | * before switching to newer one. | ||
3179 | * This will be processed only for AP fw. | ||
3180 | */ | ||
3181 | if (priv->channel_time != 0) | ||
3182 | mwl8k_update_survey(priv, priv->acs_chan); | ||
3183 | |||
3184 | priv->channel_time = jiffies; | ||
3185 | priv->acs_chan = channel; | ||
3186 | } | ||
3059 | 3187 | ||
3060 | rc = mwl8k_post_cmd(hw, &cmd->header); | 3188 | rc = mwl8k_post_cmd(hw, &cmd->header); |
3061 | kfree(cmd); | 3189 | kfree(cmd); |
@@ -5263,6 +5391,27 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, | |||
5263 | { | 5391 | { |
5264 | struct mwl8k_priv *priv = hw->priv; | 5392 | struct mwl8k_priv *priv = hw->priv; |
5265 | struct ieee80211_conf *conf = &hw->conf; | 5393 | struct ieee80211_conf *conf = &hw->conf; |
5394 | struct ieee80211_supported_band *sband; | ||
5395 | |||
5396 | if (priv->ap_fw) { | ||
5397 | sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
5398 | |||
5399 | if (sband && idx >= sband->n_channels) { | ||
5400 | idx -= sband->n_channels; | ||
5401 | sband = NULL; | ||
5402 | } | ||
5403 | |||
5404 | if (!sband) | ||
5405 | sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
5406 | |||
5407 | if (!sband || idx >= sband->n_channels) | ||
5408 | return -ENOENT; | ||
5409 | |||
5410 | memcpy(survey, &priv->survey[idx], sizeof(*survey)); | ||
5411 | survey->channel = &sband->channels[idx]; | ||
5412 | |||
5413 | return 0; | ||
5414 | } | ||
5266 | 5415 | ||
5267 | if (idx != 0) | 5416 | if (idx != 0) |
5268 | return -ENOENT; | 5417 | return -ENOENT; |
@@ -5406,6 +5555,40 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |||
5406 | return rc; | 5555 | return rc; |
5407 | } | 5556 | } |
5408 | 5557 | ||
5558 | static void mwl8k_sw_scan_start(struct ieee80211_hw *hw) | ||
5559 | { | ||
5560 | struct mwl8k_priv *priv = hw->priv; | ||
5561 | u8 tmp; | ||
5562 | |||
5563 | if (!priv->ap_fw) | ||
5564 | return; | ||
5565 | |||
5566 | /* clear all stats */ | ||
5567 | priv->channel_time = 0; | ||
5568 | ioread32(priv->regs + BBU_RXRDY_CNT_REG); | ||
5569 | ioread32(priv->regs + NOK_CCA_CNT_REG); | ||
5570 | mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); | ||
5571 | |||
5572 | priv->sw_scan_start = true; | ||
5573 | } | ||
5574 | |||
5575 | static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw) | ||
5576 | { | ||
5577 | struct mwl8k_priv *priv = hw->priv; | ||
5578 | u8 tmp; | ||
5579 | |||
5580 | if (!priv->ap_fw) | ||
5581 | return; | ||
5582 | |||
5583 | priv->sw_scan_start = false; | ||
5584 | |||
5585 | /* clear all stats */ | ||
5586 | priv->channel_time = 0; | ||
5587 | ioread32(priv->regs + BBU_RXRDY_CNT_REG); | ||
5588 | ioread32(priv->regs + NOK_CCA_CNT_REG); | ||
5589 | mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); | ||
5590 | } | ||
5591 | |||
5409 | static const struct ieee80211_ops mwl8k_ops = { | 5592 | static const struct ieee80211_ops mwl8k_ops = { |
5410 | .tx = mwl8k_tx, | 5593 | .tx = mwl8k_tx, |
5411 | .start = mwl8k_start, | 5594 | .start = mwl8k_start, |
@@ -5424,6 +5607,8 @@ static const struct ieee80211_ops mwl8k_ops = { | |||
5424 | .get_stats = mwl8k_get_stats, | 5607 | .get_stats = mwl8k_get_stats, |
5425 | .get_survey = mwl8k_get_survey, | 5608 | .get_survey = mwl8k_get_survey, |
5426 | .ampdu_action = mwl8k_ampdu_action, | 5609 | .ampdu_action = mwl8k_ampdu_action, |
5610 | .sw_scan_start = mwl8k_sw_scan_start, | ||
5611 | .sw_scan_complete = mwl8k_sw_scan_complete, | ||
5427 | }; | 5612 | }; |
5428 | 5613 | ||
5429 | static void mwl8k_finalize_join_worker(struct work_struct *work) | 5614 | static void mwl8k_finalize_join_worker(struct work_struct *work) |