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