diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-sta.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-sta.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c new file mode 100644 index 000000000000..e4fdfaa2b9b2 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c | |||
@@ -0,0 +1,355 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. | ||
4 | * | ||
5 | * Portions of this file are derived from the ipw3945 project, as well | ||
6 | * as portions of the ieee80211 subsystem header files. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of version 2 of the GNU General Public License as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along with | ||
18 | * this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | ||
20 | * | ||
21 | * The full GNU General Public License is included in this distribution in the | ||
22 | * file called LICENSE. | ||
23 | * | ||
24 | * Contact Information: | ||
25 | * James P. Ketrenos <ipw2100-admin@linux.intel.com> | ||
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
27 | * | ||
28 | *****************************************************************************/ | ||
29 | |||
30 | #include <net/mac80211.h> | ||
31 | |||
32 | #include "iwl-eeprom.h" | ||
33 | #include "iwl-4965.h" | ||
34 | #include "iwl-core.h" | ||
35 | #include "iwl-sta.h" | ||
36 | #include "iwl-io.h" | ||
37 | #include "iwl-helpers.h" | ||
38 | #include "iwl-4965.h" | ||
39 | #include "iwl-sta.h" | ||
40 | |||
41 | int iwl_get_free_ucode_key_index(struct iwl_priv *priv) | ||
42 | { | ||
43 | int i; | ||
44 | |||
45 | for (i = 0; i < STA_KEY_MAX_NUM; i++) | ||
46 | if (!test_and_set_bit(i, &priv->ucode_key_table)) | ||
47 | return i; | ||
48 | |||
49 | return -1; | ||
50 | } | ||
51 | |||
52 | int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) | ||
53 | { | ||
54 | int i, not_empty = 0; | ||
55 | u8 buff[sizeof(struct iwl_wep_cmd) + | ||
56 | sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; | ||
57 | struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; | ||
58 | size_t cmd_size = sizeof(struct iwl_wep_cmd); | ||
59 | struct iwl_host_cmd cmd = { | ||
60 | .id = REPLY_WEPKEY, | ||
61 | .data = wep_cmd, | ||
62 | .meta.flags = CMD_ASYNC, | ||
63 | }; | ||
64 | |||
65 | memset(wep_cmd, 0, cmd_size + | ||
66 | (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); | ||
67 | |||
68 | for (i = 0; i < WEP_KEYS_MAX ; i++) { | ||
69 | wep_cmd->key[i].key_index = i; | ||
70 | if (priv->wep_keys[i].key_size) { | ||
71 | wep_cmd->key[i].key_offset = i; | ||
72 | not_empty = 1; | ||
73 | } else { | ||
74 | wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; | ||
75 | } | ||
76 | |||
77 | wep_cmd->key[i].key_size = priv->wep_keys[i].key_size; | ||
78 | memcpy(&wep_cmd->key[i].key[3], priv->wep_keys[i].key, | ||
79 | priv->wep_keys[i].key_size); | ||
80 | } | ||
81 | |||
82 | wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; | ||
83 | wep_cmd->num_keys = WEP_KEYS_MAX; | ||
84 | |||
85 | cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; | ||
86 | |||
87 | cmd.len = cmd_size; | ||
88 | |||
89 | if (not_empty || send_if_empty) | ||
90 | return iwl_send_cmd(priv, &cmd); | ||
91 | else | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | int iwl_remove_default_wep_key(struct iwl_priv *priv, | ||
96 | struct ieee80211_key_conf *keyconf) | ||
97 | { | ||
98 | int ret; | ||
99 | unsigned long flags; | ||
100 | |||
101 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
102 | |||
103 | if (!test_and_clear_bit(keyconf->keyidx, &priv->ucode_key_table)) | ||
104 | IWL_ERROR("index %d not used in uCode key table.\n", | ||
105 | keyconf->keyidx); | ||
106 | |||
107 | priv->default_wep_key--; | ||
108 | memset(&priv->wep_keys[keyconf->keyidx], 0, sizeof(priv->wep_keys[0])); | ||
109 | ret = iwl_send_static_wepkey_cmd(priv, 1); | ||
110 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
111 | |||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | int iwl_set_default_wep_key(struct iwl_priv *priv, | ||
116 | struct ieee80211_key_conf *keyconf) | ||
117 | { | ||
118 | int ret; | ||
119 | unsigned long flags; | ||
120 | |||
121 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; | ||
122 | keyconf->hw_key_idx = keyconf->keyidx; | ||
123 | priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP; | ||
124 | |||
125 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
126 | priv->default_wep_key++; | ||
127 | |||
128 | if (test_and_set_bit(keyconf->keyidx, &priv->ucode_key_table)) | ||
129 | IWL_ERROR("index %d already used in uCode key table.\n", | ||
130 | keyconf->keyidx); | ||
131 | |||
132 | priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; | ||
133 | memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key, | ||
134 | keyconf->keylen); | ||
135 | |||
136 | ret = iwl_send_static_wepkey_cmd(priv, 0); | ||
137 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv, | ||
143 | struct ieee80211_key_conf *keyconf, | ||
144 | u8 sta_id) | ||
145 | { | ||
146 | unsigned long flags; | ||
147 | __le16 key_flags = 0; | ||
148 | int ret; | ||
149 | |||
150 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; | ||
151 | keyconf->hw_key_idx = keyconf->keyidx; | ||
152 | |||
153 | key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); | ||
154 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | ||
155 | key_flags &= ~STA_KEY_FLG_INVALID; | ||
156 | |||
157 | if (keyconf->keylen == WEP_KEY_LEN_128) | ||
158 | key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; | ||
159 | |||
160 | if (sta_id == priv->hw_params.bcast_sta_id) | ||
161 | key_flags |= STA_KEY_MULTICAST_MSK; | ||
162 | |||
163 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
164 | |||
165 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | ||
166 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | ||
167 | priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; | ||
168 | |||
169 | memcpy(priv->stations[sta_id].keyinfo.key, | ||
170 | keyconf->key, keyconf->keylen); | ||
171 | |||
172 | memcpy(&priv->stations[sta_id].sta.key.key[3], | ||
173 | keyconf->key, keyconf->keylen); | ||
174 | |||
175 | priv->stations[sta_id].sta.key.key_offset = | ||
176 | iwl_get_free_ucode_key_index(priv); | ||
177 | priv->stations[sta_id].sta.key.key_flags = key_flags; | ||
178 | |||
179 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | ||
180 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | ||
181 | |||
182 | ret = iwl4965_send_add_station(priv, | ||
183 | &priv->stations[sta_id].sta, CMD_ASYNC); | ||
184 | |||
185 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
186 | |||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv, | ||
191 | struct ieee80211_key_conf *keyconf, | ||
192 | u8 sta_id) | ||
193 | { | ||
194 | unsigned long flags; | ||
195 | __le16 key_flags = 0; | ||
196 | |||
197 | key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); | ||
198 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | ||
199 | key_flags &= ~STA_KEY_FLG_INVALID; | ||
200 | |||
201 | if (sta_id == priv->hw_params.bcast_sta_id) | ||
202 | key_flags |= STA_KEY_MULTICAST_MSK; | ||
203 | |||
204 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
205 | keyconf->hw_key_idx = keyconf->keyidx; | ||
206 | |||
207 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
208 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | ||
209 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | ||
210 | |||
211 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, | ||
212 | keyconf->keylen); | ||
213 | |||
214 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, | ||
215 | keyconf->keylen); | ||
216 | |||
217 | priv->stations[sta_id].sta.key.key_offset = | ||
218 | iwl_get_free_ucode_key_index(priv); | ||
219 | priv->stations[sta_id].sta.key.key_flags = key_flags; | ||
220 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | ||
221 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | ||
222 | |||
223 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
224 | |||
225 | IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n"); | ||
226 | return iwl4965_send_add_station(priv, | ||
227 | &priv->stations[sta_id].sta, CMD_ASYNC); | ||
228 | } | ||
229 | |||
230 | static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv, | ||
231 | struct ieee80211_key_conf *keyconf, | ||
232 | u8 sta_id) | ||
233 | { | ||
234 | unsigned long flags; | ||
235 | int ret = 0; | ||
236 | |||
237 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
238 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; | ||
239 | keyconf->hw_key_idx = keyconf->keyidx; | ||
240 | |||
241 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
242 | |||
243 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | ||
244 | priv->stations[sta_id].keyinfo.conf = keyconf; | ||
245 | priv->stations[sta_id].keyinfo.keylen = 16; | ||
246 | priv->stations[sta_id].sta.key.key_offset = | ||
247 | iwl_get_free_ucode_key_index(priv); | ||
248 | |||
249 | /* This copy is acutally not needed: we get the key with each TX */ | ||
250 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16); | ||
251 | |||
252 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16); | ||
253 | |||
254 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
255 | |||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | int iwl_remove_dynamic_key(struct iwl_priv *priv, u8 sta_id) | ||
260 | { | ||
261 | unsigned long flags; | ||
262 | |||
263 | priv->key_mapping_key = 0; | ||
264 | |||
265 | spin_lock_irqsave(&priv->sta_lock, flags); | ||
266 | if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset, | ||
267 | &priv->ucode_key_table)) | ||
268 | IWL_ERROR("index %d not used in uCode key table.\n", | ||
269 | priv->stations[sta_id].sta.key.key_offset); | ||
270 | memset(&priv->stations[sta_id].keyinfo, 0, | ||
271 | sizeof(struct iwl4965_hw_key)); | ||
272 | memset(&priv->stations[sta_id].sta.key, 0, | ||
273 | sizeof(struct iwl4965_keyinfo)); | ||
274 | priv->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; | ||
275 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | ||
276 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | ||
277 | spin_unlock_irqrestore(&priv->sta_lock, flags); | ||
278 | |||
279 | IWL_DEBUG_INFO("hwcrypto: clear ucode station key info\n"); | ||
280 | return iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, 0); | ||
281 | } | ||
282 | |||
283 | int iwl_set_dynamic_key(struct iwl_priv *priv, | ||
284 | struct ieee80211_key_conf *key, u8 sta_id) | ||
285 | { | ||
286 | int ret; | ||
287 | |||
288 | priv->key_mapping_key = 1; | ||
289 | |||
290 | switch (key->alg) { | ||
291 | case ALG_CCMP: | ||
292 | ret = iwl_set_ccmp_dynamic_key_info(priv, key, sta_id); | ||
293 | break; | ||
294 | case ALG_TKIP: | ||
295 | ret = iwl_set_tkip_dynamic_key_info(priv, key, sta_id); | ||
296 | break; | ||
297 | case ALG_WEP: | ||
298 | ret = iwl_set_wep_dynamic_key_info(priv, key, sta_id); | ||
299 | break; | ||
300 | default: | ||
301 | IWL_ERROR("Unknown alg: %s alg = %d\n", __func__, key->alg); | ||
302 | ret = -EINVAL; | ||
303 | } | ||
304 | |||
305 | return ret; | ||
306 | } | ||
307 | |||
308 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
309 | static void iwl_dump_lq_cmd(struct iwl_priv *priv, | ||
310 | struct iwl_link_quality_cmd *lq) | ||
311 | { | ||
312 | int i; | ||
313 | IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id); | ||
314 | IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n", | ||
315 | lq->general_params.single_stream_ant_msk, | ||
316 | lq->general_params.dual_stream_ant_msk); | ||
317 | |||
318 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) | ||
319 | IWL_DEBUG_RATE("lq index %d 0x%X\n", | ||
320 | i, lq->rs_table[i].rate_n_flags); | ||
321 | } | ||
322 | #else | ||
323 | static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, | ||
324 | struct iwl_link_quality_cmd *lq) | ||
325 | { | ||
326 | } | ||
327 | #endif | ||
328 | |||
329 | int iwl_send_lq_cmd(struct iwl_priv *priv, | ||
330 | struct iwl_link_quality_cmd *lq, u8 flags) | ||
331 | { | ||
332 | struct iwl_host_cmd cmd = { | ||
333 | .id = REPLY_TX_LINK_QUALITY_CMD, | ||
334 | .len = sizeof(struct iwl_link_quality_cmd), | ||
335 | .meta.flags = flags, | ||
336 | .data = lq, | ||
337 | }; | ||
338 | |||
339 | if ((lq->sta_id == 0xFF) && | ||
340 | (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) | ||
341 | return -EINVAL; | ||
342 | |||
343 | if (lq->sta_id == 0xFF) | ||
344 | lq->sta_id = IWL_AP_ID; | ||
345 | |||
346 | iwl_dump_lq_cmd(priv,lq); | ||
347 | |||
348 | if (iwl_is_associated(priv) && priv->assoc_station_added && | ||
349 | priv->lq_mngr.lq_ready) | ||
350 | return iwl_send_cmd(priv, &cmd); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | EXPORT_SYMBOL(iwl_send_lq_cmd); | ||
355 | |||