]>
Commit | Line | Data |
---|---|---|
6974e363 EG |
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 <[email protected]> | |
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
27 | * | |
28 | *****************************************************************************/ | |
29 | ||
30 | #include <net/mac80211.h> | |
947b13a7 | 31 | #include <linux/etherdevice.h> |
6974e363 EG |
32 | |
33 | #include "iwl-eeprom.h" | |
3e0d4cb1 | 34 | #include "iwl-dev.h" |
6974e363 EG |
35 | #include "iwl-core.h" |
36 | #include "iwl-sta.h" | |
37 | #include "iwl-io.h" | |
38 | #include "iwl-helpers.h" | |
80fb47a1 | 39 | |
947b13a7 TW |
40 | u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) |
41 | { | |
42 | int i; | |
43 | int start = 0; | |
44 | int ret = IWL_INVALID_STATION; | |
45 | unsigned long flags; | |
46 | DECLARE_MAC_BUF(mac); | |
47 | ||
48 | if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) || | |
49 | (priv->iw_mode == IEEE80211_IF_TYPE_AP)) | |
50 | start = IWL_STA_ID; | |
51 | ||
52 | if (is_broadcast_ether_addr(addr)) | |
53 | return priv->hw_params.bcast_sta_id; | |
54 | ||
55 | spin_lock_irqsave(&priv->sta_lock, flags); | |
56 | for (i = start; i < priv->hw_params.max_stations; i++) | |
57 | if (priv->stations[i].used && | |
58 | (!compare_ether_addr(priv->stations[i].sta.sta.addr, | |
59 | addr))) { | |
60 | ret = i; | |
61 | goto out; | |
62 | } | |
63 | ||
64 | IWL_DEBUG_ASSOC_LIMIT("can not find STA %s total %d\n", | |
65 | print_mac(mac, addr), priv->num_stations); | |
66 | ||
67 | out: | |
68 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
69 | return ret; | |
70 | } | |
71 | EXPORT_SYMBOL(iwl_find_station); | |
72 | ||
133636de TW |
73 | int iwl_send_add_sta(struct iwl_priv *priv, |
74 | struct iwl_addsta_cmd *sta, u8 flags) | |
75 | { | |
76 | struct iwl_rx_packet *res = NULL; | |
77 | int ret = 0; | |
78 | u8 data[sizeof(*sta)]; | |
79 | struct iwl_host_cmd cmd = { | |
80 | .id = REPLY_ADD_STA, | |
81 | .meta.flags = flags, | |
82 | .data = data, | |
83 | }; | |
84 | ||
85 | if (!(flags & CMD_ASYNC)) | |
86 | cmd.meta.flags |= CMD_WANT_SKB; | |
87 | ||
88 | cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); | |
89 | ret = iwl_send_cmd(priv, &cmd); | |
90 | ||
91 | if (ret || (flags & CMD_ASYNC)) | |
92 | return ret; | |
93 | ||
94 | res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; | |
95 | if (res->hdr.flags & IWL_CMD_FAILED_MSK) { | |
96 | IWL_ERROR("Bad return from REPLY_ADD_STA (0x%08X)\n", | |
97 | res->hdr.flags); | |
98 | ret = -EIO; | |
99 | } | |
100 | ||
101 | if (ret == 0) { | |
102 | switch (res->u.add_sta.status) { | |
103 | case ADD_STA_SUCCESS_MSK: | |
104 | IWL_DEBUG_INFO("REPLY_ADD_STA PASSED\n"); | |
105 | break; | |
106 | default: | |
107 | ret = -EIO; | |
108 | IWL_WARNING("REPLY_ADD_STA failed\n"); | |
109 | break; | |
110 | } | |
111 | } | |
112 | ||
113 | priv->alloc_rxb_skb--; | |
114 | dev_kfree_skb_any(cmd.meta.u.skb); | |
115 | ||
116 | return ret; | |
117 | } | |
118 | EXPORT_SYMBOL(iwl_send_add_sta); | |
947b13a7 | 119 | |
80fb47a1 EG |
120 | int iwl_get_free_ucode_key_index(struct iwl_priv *priv) |
121 | { | |
122 | int i; | |
123 | ||
124 | for (i = 0; i < STA_KEY_MAX_NUM; i++) | |
77bab602 | 125 | if (!test_and_set_bit(i, &priv->ucode_key_table)) |
80fb47a1 EG |
126 | return i; |
127 | ||
128 | return -1; | |
129 | } | |
6974e363 EG |
130 | |
131 | int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) | |
132 | { | |
133 | int i, not_empty = 0; | |
134 | u8 buff[sizeof(struct iwl_wep_cmd) + | |
135 | sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; | |
136 | struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; | |
137 | size_t cmd_size = sizeof(struct iwl_wep_cmd); | |
138 | struct iwl_host_cmd cmd = { | |
139 | .id = REPLY_WEPKEY, | |
140 | .data = wep_cmd, | |
141 | .meta.flags = CMD_ASYNC, | |
142 | }; | |
143 | ||
144 | memset(wep_cmd, 0, cmd_size + | |
145 | (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); | |
146 | ||
147 | for (i = 0; i < WEP_KEYS_MAX ; i++) { | |
148 | wep_cmd->key[i].key_index = i; | |
149 | if (priv->wep_keys[i].key_size) { | |
150 | wep_cmd->key[i].key_offset = i; | |
151 | not_empty = 1; | |
152 | } else { | |
153 | wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; | |
154 | } | |
155 | ||
156 | wep_cmd->key[i].key_size = priv->wep_keys[i].key_size; | |
157 | memcpy(&wep_cmd->key[i].key[3], priv->wep_keys[i].key, | |
158 | priv->wep_keys[i].key_size); | |
159 | } | |
160 | ||
161 | wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; | |
162 | wep_cmd->num_keys = WEP_KEYS_MAX; | |
163 | ||
164 | cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; | |
165 | ||
166 | cmd.len = cmd_size; | |
167 | ||
168 | if (not_empty || send_if_empty) | |
169 | return iwl_send_cmd(priv, &cmd); | |
170 | else | |
171 | return 0; | |
172 | } | |
27aaba0c | 173 | EXPORT_SYMBOL(iwl_send_static_wepkey_cmd); |
6974e363 EG |
174 | |
175 | int iwl_remove_default_wep_key(struct iwl_priv *priv, | |
80fb47a1 | 176 | struct ieee80211_key_conf *keyconf) |
6974e363 EG |
177 | { |
178 | int ret; | |
179 | unsigned long flags; | |
180 | ||
181 | spin_lock_irqsave(&priv->sta_lock, flags); | |
80fb47a1 EG |
182 | |
183 | if (!test_and_clear_bit(keyconf->keyidx, &priv->ucode_key_table)) | |
184 | IWL_ERROR("index %d not used in uCode key table.\n", | |
185 | keyconf->keyidx); | |
186 | ||
6974e363 | 187 | priv->default_wep_key--; |
80fb47a1 | 188 | memset(&priv->wep_keys[keyconf->keyidx], 0, sizeof(priv->wep_keys[0])); |
6974e363 EG |
189 | ret = iwl_send_static_wepkey_cmd(priv, 1); |
190 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
191 | ||
192 | return ret; | |
193 | } | |
27aaba0c | 194 | EXPORT_SYMBOL(iwl_remove_default_wep_key); |
6974e363 EG |
195 | |
196 | int iwl_set_default_wep_key(struct iwl_priv *priv, | |
197 | struct ieee80211_key_conf *keyconf) | |
198 | { | |
199 | int ret; | |
200 | unsigned long flags; | |
201 | ||
202 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; | |
203 | keyconf->hw_key_idx = keyconf->keyidx; | |
204 | priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP; | |
205 | ||
206 | spin_lock_irqsave(&priv->sta_lock, flags); | |
207 | priv->default_wep_key++; | |
208 | ||
80fb47a1 EG |
209 | if (test_and_set_bit(keyconf->keyidx, &priv->ucode_key_table)) |
210 | IWL_ERROR("index %d already used in uCode key table.\n", | |
211 | keyconf->keyidx); | |
212 | ||
6974e363 EG |
213 | priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; |
214 | memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key, | |
215 | keyconf->keylen); | |
216 | ||
217 | ret = iwl_send_static_wepkey_cmd(priv, 0); | |
218 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
219 | ||
220 | return ret; | |
221 | } | |
27aaba0c | 222 | EXPORT_SYMBOL(iwl_set_default_wep_key); |
6974e363 | 223 | |
7480513f | 224 | static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv, |
0211ddda EG |
225 | struct ieee80211_key_conf *keyconf, |
226 | u8 sta_id) | |
227 | { | |
228 | unsigned long flags; | |
229 | __le16 key_flags = 0; | |
230 | int ret; | |
231 | ||
232 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; | |
233 | keyconf->hw_key_idx = keyconf->keyidx; | |
234 | ||
235 | key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); | |
236 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | |
237 | key_flags &= ~STA_KEY_FLG_INVALID; | |
238 | ||
239 | if (keyconf->keylen == WEP_KEY_LEN_128) | |
240 | key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; | |
241 | ||
5425e490 | 242 | if (sta_id == priv->hw_params.bcast_sta_id) |
0211ddda EG |
243 | key_flags |= STA_KEY_MULTICAST_MSK; |
244 | ||
245 | spin_lock_irqsave(&priv->sta_lock, flags); | |
246 | ||
247 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
248 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | |
249 | priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; | |
250 | ||
251 | memcpy(priv->stations[sta_id].keyinfo.key, | |
252 | keyconf->key, keyconf->keylen); | |
253 | ||
254 | memcpy(&priv->stations[sta_id].sta.key.key[3], | |
255 | keyconf->key, keyconf->keylen); | |
256 | ||
3ec47732 EG |
257 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) |
258 | == STA_KEY_FLG_NO_ENC) | |
259 | priv->stations[sta_id].sta.key.key_offset = | |
80fb47a1 | 260 | iwl_get_free_ucode_key_index(priv); |
3ec47732 EG |
261 | /* else, we are overriding an existing key => no need to allocated room |
262 | * in uCode. */ | |
0211ddda | 263 | |
3ec47732 | 264 | priv->stations[sta_id].sta.key.key_flags = key_flags; |
0211ddda EG |
265 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; |
266 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
267 | ||
133636de | 268 | ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
0211ddda EG |
269 | |
270 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
271 | ||
272 | return ret; | |
273 | } | |
7480513f EG |
274 | |
275 | static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv, | |
276 | struct ieee80211_key_conf *keyconf, | |
277 | u8 sta_id) | |
278 | { | |
279 | unsigned long flags; | |
280 | __le16 key_flags = 0; | |
281 | ||
282 | key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); | |
283 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | |
284 | key_flags &= ~STA_KEY_FLG_INVALID; | |
285 | ||
5425e490 | 286 | if (sta_id == priv->hw_params.bcast_sta_id) |
7480513f EG |
287 | key_flags |= STA_KEY_MULTICAST_MSK; |
288 | ||
289 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
290 | keyconf->hw_key_idx = keyconf->keyidx; | |
291 | ||
292 | spin_lock_irqsave(&priv->sta_lock, flags); | |
293 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
294 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | |
295 | ||
296 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, | |
297 | keyconf->keylen); | |
298 | ||
299 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, | |
300 | keyconf->keylen); | |
301 | ||
3ec47732 EG |
302 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) |
303 | == STA_KEY_FLG_NO_ENC) | |
304 | priv->stations[sta_id].sta.key.key_offset = | |
305 | iwl_get_free_ucode_key_index(priv); | |
306 | /* else, we are overriding an existing key => no need to allocated room | |
307 | * in uCode. */ | |
308 | ||
7480513f EG |
309 | priv->stations[sta_id].sta.key.key_flags = key_flags; |
310 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | |
311 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
312 | ||
313 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
314 | ||
315 | IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n"); | |
133636de | 316 | return iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
7480513f EG |
317 | } |
318 | ||
319 | static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv, | |
320 | struct ieee80211_key_conf *keyconf, | |
321 | u8 sta_id) | |
322 | { | |
323 | unsigned long flags; | |
324 | int ret = 0; | |
325 | ||
326 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
327 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; | |
328 | keyconf->hw_key_idx = keyconf->keyidx; | |
329 | ||
330 | spin_lock_irqsave(&priv->sta_lock, flags); | |
331 | ||
332 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
333 | priv->stations[sta_id].keyinfo.conf = keyconf; | |
334 | priv->stations[sta_id].keyinfo.keylen = 16; | |
3ec47732 EG |
335 | |
336 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) | |
337 | == STA_KEY_FLG_NO_ENC) | |
338 | priv->stations[sta_id].sta.key.key_offset = | |
77bab602 | 339 | iwl_get_free_ucode_key_index(priv); |
3ec47732 EG |
340 | /* else, we are overriding an existing key => no need to allocated room |
341 | * in uCode. */ | |
7480513f EG |
342 | |
343 | /* This copy is acutally not needed: we get the key with each TX */ | |
344 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16); | |
345 | ||
346 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16); | |
347 | ||
348 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
349 | ||
350 | return ret; | |
351 | } | |
352 | ||
3ec47732 EG |
353 | int iwl_remove_dynamic_key(struct iwl_priv *priv, |
354 | struct ieee80211_key_conf *keyconf, | |
355 | u8 sta_id) | |
7480513f EG |
356 | { |
357 | unsigned long flags; | |
3ec47732 EG |
358 | int ret = 0; |
359 | u16 key_flags; | |
360 | u8 keyidx; | |
7480513f EG |
361 | |
362 | priv->key_mapping_key = 0; | |
363 | ||
364 | spin_lock_irqsave(&priv->sta_lock, flags); | |
3ec47732 EG |
365 | key_flags = le16_to_cpu(priv->stations[sta_id].sta.key.key_flags); |
366 | keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; | |
367 | ||
368 | if (keyconf->keyidx != keyidx) { | |
369 | /* We need to remove a key with index different that the one | |
370 | * in the uCode. This means that the key we need to remove has | |
371 | * been replaced by another one with different index. | |
372 | * Don't do anything and return ok | |
373 | */ | |
374 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
375 | return 0; | |
376 | } | |
377 | ||
7480513f EG |
378 | if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset, |
379 | &priv->ucode_key_table)) | |
380 | IWL_ERROR("index %d not used in uCode key table.\n", | |
381 | priv->stations[sta_id].sta.key.key_offset); | |
382 | memset(&priv->stations[sta_id].keyinfo, 0, | |
6def9761 | 383 | sizeof(struct iwl_hw_key)); |
7480513f EG |
384 | memset(&priv->stations[sta_id].sta.key, 0, |
385 | sizeof(struct iwl4965_keyinfo)); | |
3ec47732 EG |
386 | priv->stations[sta_id].sta.key.key_flags = |
387 | STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; | |
388 | priv->stations[sta_id].sta.key.key_offset = WEP_INVALID_OFFSET; | |
7480513f EG |
389 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; |
390 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
7480513f EG |
391 | |
392 | IWL_DEBUG_INFO("hwcrypto: clear ucode station key info\n"); | |
133636de | 393 | ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, 0); |
3ec47732 EG |
394 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
395 | return ret; | |
7480513f | 396 | } |
27aaba0c | 397 | EXPORT_SYMBOL(iwl_remove_dynamic_key); |
7480513f EG |
398 | |
399 | int iwl_set_dynamic_key(struct iwl_priv *priv, | |
400 | struct ieee80211_key_conf *key, u8 sta_id) | |
401 | { | |
402 | int ret; | |
403 | ||
404 | priv->key_mapping_key = 1; | |
405 | ||
406 | switch (key->alg) { | |
407 | case ALG_CCMP: | |
408 | ret = iwl_set_ccmp_dynamic_key_info(priv, key, sta_id); | |
409 | break; | |
410 | case ALG_TKIP: | |
411 | ret = iwl_set_tkip_dynamic_key_info(priv, key, sta_id); | |
412 | break; | |
413 | case ALG_WEP: | |
414 | ret = iwl_set_wep_dynamic_key_info(priv, key, sta_id); | |
415 | break; | |
416 | default: | |
417 | IWL_ERROR("Unknown alg: %s alg = %d\n", __func__, key->alg); | |
418 | ret = -EINVAL; | |
419 | } | |
420 | ||
421 | return ret; | |
422 | } | |
27aaba0c | 423 | EXPORT_SYMBOL(iwl_set_dynamic_key); |
7480513f | 424 | |
66c73db7 TW |
425 | #ifdef CONFIG_IWLWIFI_DEBUG |
426 | static void iwl_dump_lq_cmd(struct iwl_priv *priv, | |
427 | struct iwl_link_quality_cmd *lq) | |
428 | { | |
429 | int i; | |
430 | IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id); | |
431 | IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n", | |
432 | lq->general_params.single_stream_ant_msk, | |
433 | lq->general_params.dual_stream_ant_msk); | |
434 | ||
435 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) | |
436 | IWL_DEBUG_RATE("lq index %d 0x%X\n", | |
437 | i, lq->rs_table[i].rate_n_flags); | |
438 | } | |
439 | #else | |
440 | static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, | |
441 | struct iwl_link_quality_cmd *lq) | |
442 | { | |
443 | } | |
444 | #endif | |
445 | ||
446 | int iwl_send_lq_cmd(struct iwl_priv *priv, | |
447 | struct iwl_link_quality_cmd *lq, u8 flags) | |
448 | { | |
449 | struct iwl_host_cmd cmd = { | |
450 | .id = REPLY_TX_LINK_QUALITY_CMD, | |
451 | .len = sizeof(struct iwl_link_quality_cmd), | |
452 | .meta.flags = flags, | |
453 | .data = lq, | |
454 | }; | |
455 | ||
456 | if ((lq->sta_id == 0xFF) && | |
457 | (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) | |
458 | return -EINVAL; | |
459 | ||
460 | if (lq->sta_id == 0xFF) | |
461 | lq->sta_id = IWL_AP_ID; | |
462 | ||
463 | iwl_dump_lq_cmd(priv,lq); | |
464 | ||
465 | if (iwl_is_associated(priv) && priv->assoc_station_added && | |
466 | priv->lq_mngr.lq_ready) | |
467 | return iwl_send_cmd(priv, &cmd); | |
468 | ||
469 | return 0; | |
470 | } | |
471 | EXPORT_SYMBOL(iwl_send_lq_cmd); | |
472 |