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