Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 3 | * GPL LICENSE SUMMARY |
| 4 | * |
| 5 | * Copyright(c) 2008 Intel Corporation. All rights reserved. |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of version 2 of the GNU General Public License as |
| 9 | * published by the Free Software Foundation. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, but |
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
| 19 | * USA |
| 20 | * |
| 21 | * The full GNU General Public License is included in this distribution |
| 22 | * in the file called LICENSE.GPL. |
| 23 | * |
| 24 | * Contact Information: |
| 25 | * Tomas Winkler <tomas.winkler@intel.com> |
| 26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| 27 | *****************************************************************************/ |
| 28 | |
| 29 | #include <linux/kernel.h> |
| 30 | #include <linux/module.h> |
| 31 | #include <linux/version.h> |
Assaf Krauss | 1d0a082 | 2008-03-14 10:38:48 -0700 | [diff] [blame] | 32 | #include <net/mac80211.h> |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 33 | |
Tomas Winkler | 712b6cf | 2008-03-12 16:58:52 -0700 | [diff] [blame] | 34 | struct iwl_priv; /* FIXME: remove */ |
Tomas Winkler | 0a6857e | 2008-03-12 16:58:49 -0700 | [diff] [blame] | 35 | #include "iwl-debug.h" |
Assaf Krauss | 6bc913b | 2008-03-11 16:17:18 -0700 | [diff] [blame] | 36 | #include "iwl-eeprom.h" |
Tomas Winkler | fee1247 | 2008-04-03 16:05:21 -0700 | [diff] [blame] | 37 | #include "iwl-4965.h" /* FIXME: remove */ |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 38 | #include "iwl-core.h" |
Mohamed Abbas | ad97edd | 2008-03-28 16:21:06 -0700 | [diff] [blame] | 39 | #include "iwl-rfkill.h" |
Mohamed Abbas | 5da4b55 | 2008-04-21 15:41:51 -0700 | [diff] [blame^] | 40 | #include "iwl-power.h" |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 41 | |
Assaf Krauss | 1d0a082 | 2008-03-14 10:38:48 -0700 | [diff] [blame] | 42 | |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 43 | MODULE_DESCRIPTION("iwl core"); |
| 44 | MODULE_VERSION(IWLWIFI_VERSION); |
| 45 | MODULE_AUTHOR(DRV_COPYRIGHT); |
Tomas Winkler | 712b6cf | 2008-03-12 16:58:52 -0700 | [diff] [blame] | 46 | MODULE_LICENSE("GPL"); |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 47 | |
Tomas Winkler | 0a6857e | 2008-03-12 16:58:49 -0700 | [diff] [blame] | 48 | #ifdef CONFIG_IWLWIFI_DEBUG |
| 49 | u32 iwl_debug_level; |
| 50 | EXPORT_SYMBOL(iwl_debug_level); |
Tomas Winkler | df48c32 | 2008-03-06 10:40:19 -0800 | [diff] [blame] | 51 | #endif |
Assaf Krauss | 1d0a082 | 2008-03-14 10:38:48 -0700 | [diff] [blame] | 52 | |
| 53 | /* This function both allocates and initializes hw and priv. */ |
| 54 | struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg, |
| 55 | struct ieee80211_ops *hw_ops) |
| 56 | { |
| 57 | struct iwl_priv *priv; |
| 58 | |
| 59 | /* mac80211 allocates memory for this device instance, including |
| 60 | * space for this driver's private structure */ |
| 61 | struct ieee80211_hw *hw = |
| 62 | ieee80211_alloc_hw(sizeof(struct iwl_priv), hw_ops); |
| 63 | if (hw == NULL) { |
| 64 | IWL_ERROR("Can not allocate network device\n"); |
| 65 | goto out; |
| 66 | } |
| 67 | |
| 68 | priv = hw->priv; |
| 69 | priv->hw = hw; |
| 70 | |
| 71 | out: |
| 72 | return hw; |
| 73 | } |
| 74 | EXPORT_SYMBOL(iwl_alloc_all); |
| 75 | |
Assaf Krauss | bf85ea4 | 2008-03-14 10:38:49 -0700 | [diff] [blame] | 76 | /** |
| 77 | * iwlcore_clear_stations_table - Clear the driver's station table |
| 78 | * |
| 79 | * NOTE: This does not clear or otherwise alter the device's station table. |
| 80 | */ |
| 81 | void iwlcore_clear_stations_table(struct iwl_priv *priv) |
| 82 | { |
| 83 | unsigned long flags; |
| 84 | |
| 85 | spin_lock_irqsave(&priv->sta_lock, flags); |
| 86 | |
| 87 | priv->num_stations = 0; |
| 88 | memset(priv->stations, 0, sizeof(priv->stations)); |
| 89 | |
| 90 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
| 91 | } |
| 92 | EXPORT_SYMBOL(iwlcore_clear_stations_table); |
| 93 | |
| 94 | void iwlcore_reset_qos(struct iwl_priv *priv) |
| 95 | { |
| 96 | u16 cw_min = 15; |
| 97 | u16 cw_max = 1023; |
| 98 | u8 aifs = 2; |
| 99 | u8 is_legacy = 0; |
| 100 | unsigned long flags; |
| 101 | int i; |
| 102 | |
| 103 | spin_lock_irqsave(&priv->lock, flags); |
| 104 | priv->qos_data.qos_active = 0; |
| 105 | |
| 106 | if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) { |
| 107 | if (priv->qos_data.qos_enable) |
| 108 | priv->qos_data.qos_active = 1; |
| 109 | if (!(priv->active_rate & 0xfff0)) { |
| 110 | cw_min = 31; |
| 111 | is_legacy = 1; |
| 112 | } |
| 113 | } else if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { |
| 114 | if (priv->qos_data.qos_enable) |
| 115 | priv->qos_data.qos_active = 1; |
| 116 | } else if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) { |
| 117 | cw_min = 31; |
| 118 | is_legacy = 1; |
| 119 | } |
| 120 | |
| 121 | if (priv->qos_data.qos_active) |
| 122 | aifs = 3; |
| 123 | |
| 124 | priv->qos_data.def_qos_parm.ac[0].cw_min = cpu_to_le16(cw_min); |
| 125 | priv->qos_data.def_qos_parm.ac[0].cw_max = cpu_to_le16(cw_max); |
| 126 | priv->qos_data.def_qos_parm.ac[0].aifsn = aifs; |
| 127 | priv->qos_data.def_qos_parm.ac[0].edca_txop = 0; |
| 128 | priv->qos_data.def_qos_parm.ac[0].reserved1 = 0; |
| 129 | |
| 130 | if (priv->qos_data.qos_active) { |
| 131 | i = 1; |
| 132 | priv->qos_data.def_qos_parm.ac[i].cw_min = cpu_to_le16(cw_min); |
| 133 | priv->qos_data.def_qos_parm.ac[i].cw_max = cpu_to_le16(cw_max); |
| 134 | priv->qos_data.def_qos_parm.ac[i].aifsn = 7; |
| 135 | priv->qos_data.def_qos_parm.ac[i].edca_txop = 0; |
| 136 | priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| 137 | |
| 138 | i = 2; |
| 139 | priv->qos_data.def_qos_parm.ac[i].cw_min = |
| 140 | cpu_to_le16((cw_min + 1) / 2 - 1); |
| 141 | priv->qos_data.def_qos_parm.ac[i].cw_max = |
| 142 | cpu_to_le16(cw_max); |
| 143 | priv->qos_data.def_qos_parm.ac[i].aifsn = 2; |
| 144 | if (is_legacy) |
| 145 | priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| 146 | cpu_to_le16(6016); |
| 147 | else |
| 148 | priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| 149 | cpu_to_le16(3008); |
| 150 | priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| 151 | |
| 152 | i = 3; |
| 153 | priv->qos_data.def_qos_parm.ac[i].cw_min = |
| 154 | cpu_to_le16((cw_min + 1) / 4 - 1); |
| 155 | priv->qos_data.def_qos_parm.ac[i].cw_max = |
| 156 | cpu_to_le16((cw_max + 1) / 2 - 1); |
| 157 | priv->qos_data.def_qos_parm.ac[i].aifsn = 2; |
| 158 | priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| 159 | if (is_legacy) |
| 160 | priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| 161 | cpu_to_le16(3264); |
| 162 | else |
| 163 | priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| 164 | cpu_to_le16(1504); |
| 165 | } else { |
| 166 | for (i = 1; i < 4; i++) { |
| 167 | priv->qos_data.def_qos_parm.ac[i].cw_min = |
| 168 | cpu_to_le16(cw_min); |
| 169 | priv->qos_data.def_qos_parm.ac[i].cw_max = |
| 170 | cpu_to_le16(cw_max); |
| 171 | priv->qos_data.def_qos_parm.ac[i].aifsn = aifs; |
| 172 | priv->qos_data.def_qos_parm.ac[i].edca_txop = 0; |
| 173 | priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| 174 | } |
| 175 | } |
| 176 | IWL_DEBUG_QOS("set QoS to default \n"); |
| 177 | |
| 178 | spin_unlock_irqrestore(&priv->lock, flags); |
| 179 | } |
| 180 | EXPORT_SYMBOL(iwlcore_reset_qos); |
| 181 | |
| 182 | /** |
| 183 | * iwlcore_set_rxon_channel - Set the phymode and channel values in staging RXON |
| 184 | * @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz |
| 185 | * @channel: Any channel valid for the requested phymode |
| 186 | |
| 187 | * In addition to setting the staging RXON, priv->phymode is also set. |
| 188 | * |
| 189 | * NOTE: Does not commit to the hardware; it sets appropriate bit fields |
| 190 | * in the staging RXON flag structure based on the phymode |
| 191 | */ |
| 192 | int iwlcore_set_rxon_channel(struct iwl_priv *priv, |
| 193 | enum ieee80211_band band, |
| 194 | u16 channel) |
| 195 | { |
Assaf Krauss | 8622e70 | 2008-03-21 13:53:43 -0700 | [diff] [blame] | 196 | if (!iwl_get_channel_info(priv, band, channel)) { |
Assaf Krauss | bf85ea4 | 2008-03-14 10:38:49 -0700 | [diff] [blame] | 197 | IWL_DEBUG_INFO("Could not set channel to %d [%d]\n", |
| 198 | channel, band); |
| 199 | return -EINVAL; |
| 200 | } |
| 201 | |
| 202 | if ((le16_to_cpu(priv->staging_rxon.channel) == channel) && |
| 203 | (priv->band == band)) |
| 204 | return 0; |
| 205 | |
| 206 | priv->staging_rxon.channel = cpu_to_le16(channel); |
| 207 | if (band == IEEE80211_BAND_5GHZ) |
| 208 | priv->staging_rxon.flags &= ~RXON_FLG_BAND_24G_MSK; |
| 209 | else |
| 210 | priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; |
| 211 | |
| 212 | priv->band = band; |
| 213 | |
| 214 | IWL_DEBUG_INFO("Staging channel set to %d [%d]\n", channel, band); |
| 215 | |
| 216 | return 0; |
| 217 | } |
| 218 | EXPORT_SYMBOL(iwlcore_set_rxon_channel); |
| 219 | |
| 220 | static void iwlcore_init_hw(struct iwl_priv *priv) |
| 221 | { |
| 222 | struct ieee80211_hw *hw = priv->hw; |
| 223 | hw->rate_control_algorithm = "iwl-4965-rs"; |
| 224 | |
| 225 | /* Tell mac80211 and its clients (e.g. Wireless Extensions) |
| 226 | * the range of signal quality values that we'll provide. |
| 227 | * Negative values for level/noise indicate that we'll provide dBm. |
| 228 | * For WE, at least, non-0 values here *enable* display of values |
| 229 | * in app (iwconfig). */ |
| 230 | hw->max_rssi = -20; /* signal level, negative indicates dBm */ |
| 231 | hw->max_noise = -20; /* noise level, negative indicates dBm */ |
| 232 | hw->max_signal = 100; /* link quality indication (%) */ |
| 233 | |
| 234 | /* Tell mac80211 our Tx characteristics */ |
| 235 | hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE; |
| 236 | |
| 237 | /* Default value; 4 EDCA QOS priorities */ |
| 238 | hw->queues = 4; |
| 239 | #ifdef CONFIG_IWL4965_HT |
| 240 | /* Enhanced value; more queues, to support 11n aggregation */ |
| 241 | hw->queues = 16; |
| 242 | #endif /* CONFIG_IWL4965_HT */ |
| 243 | } |
| 244 | |
| 245 | int iwl_setup(struct iwl_priv *priv) |
| 246 | { |
| 247 | int ret = 0; |
| 248 | iwlcore_init_hw(priv); |
| 249 | ret = priv->cfg->ops->lib->init_drv(priv); |
| 250 | return ret; |
| 251 | } |
| 252 | EXPORT_SYMBOL(iwl_setup); |
| 253 | |
Mohamed Abbas | c8381fd | 2008-03-28 16:21:05 -0700 | [diff] [blame] | 254 | /* Low level driver call this function to update iwlcore with |
| 255 | * driver status. |
| 256 | */ |
| 257 | int iwlcore_low_level_notify(struct iwl_priv *priv, |
| 258 | enum iwlcore_card_notify notify) |
| 259 | { |
Mohamed Abbas | 03d29c6 | 2008-04-03 16:05:24 -0700 | [diff] [blame] | 260 | int ret; |
Mohamed Abbas | c8381fd | 2008-03-28 16:21:05 -0700 | [diff] [blame] | 261 | switch (notify) { |
| 262 | case IWLCORE_INIT_EVT: |
Mohamed Abbas | 03d29c6 | 2008-04-03 16:05:24 -0700 | [diff] [blame] | 263 | ret = iwl_rfkill_init(priv); |
| 264 | if (ret) |
| 265 | IWL_ERROR("Unable to initialize RFKILL system. " |
| 266 | "Ignoring error: %d\n", ret); |
Mohamed Abbas | 5da4b55 | 2008-04-21 15:41:51 -0700 | [diff] [blame^] | 267 | iwl_power_initialize(priv); |
Mohamed Abbas | c8381fd | 2008-03-28 16:21:05 -0700 | [diff] [blame] | 268 | break; |
| 269 | case IWLCORE_START_EVT: |
Mohamed Abbas | 5da4b55 | 2008-04-21 15:41:51 -0700 | [diff] [blame^] | 270 | iwl_power_update_mode(priv, 1); |
Mohamed Abbas | c8381fd | 2008-03-28 16:21:05 -0700 | [diff] [blame] | 271 | break; |
| 272 | case IWLCORE_STOP_EVT: |
| 273 | break; |
| 274 | case IWLCORE_REMOVE_EVT: |
Mohamed Abbas | ad97edd | 2008-03-28 16:21:06 -0700 | [diff] [blame] | 275 | iwl_rfkill_unregister(priv); |
Mohamed Abbas | c8381fd | 2008-03-28 16:21:05 -0700 | [diff] [blame] | 276 | break; |
| 277 | } |
| 278 | |
| 279 | return 0; |
| 280 | } |
| 281 | EXPORT_SYMBOL(iwlcore_low_level_notify); |
| 282 | |
Emmanuel Grumbach | 49ea859 | 2008-04-15 16:01:37 -0700 | [diff] [blame] | 283 | int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags) |
| 284 | { |
| 285 | u32 stat_flags = 0; |
| 286 | struct iwl_host_cmd cmd = { |
| 287 | .id = REPLY_STATISTICS_CMD, |
| 288 | .meta.flags = flags, |
| 289 | .len = sizeof(stat_flags), |
| 290 | .data = (u8 *) &stat_flags, |
| 291 | }; |
| 292 | return iwl_send_cmd(priv, &cmd); |
| 293 | } |
| 294 | EXPORT_SYMBOL(iwl_send_statistics_request); |
Tomas Winkler | 7e8c519 | 2008-04-15 16:01:43 -0700 | [diff] [blame] | 295 | |