]>
Commit | Line | Data |
---|---|---|
6974e363 EG |
1 | /****************************************************************************** |
2 | * | |
1f447808 | 3 | * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. |
6974e363 EG |
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: | |
759ef89f | 25 | * Intel Linux Wireless <ilw@linux.intel.com> |
6974e363 EG |
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> |
fe6b23dd | 32 | #include <linux/sched.h> |
6974e363 | 33 | |
3e0d4cb1 | 34 | #include "iwl-dev.h" |
6974e363 EG |
35 | #include "iwl-core.h" |
36 | #include "iwl-sta.h" | |
7a999bf0 | 37 | |
947b13a7 TW |
38 | u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) |
39 | { | |
40 | int i; | |
41 | int start = 0; | |
42 | int ret = IWL_INVALID_STATION; | |
43 | unsigned long flags; | |
947b13a7 | 44 | |
05c914fe JB |
45 | if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) || |
46 | (priv->iw_mode == NL80211_IFTYPE_AP)) | |
947b13a7 TW |
47 | start = IWL_STA_ID; |
48 | ||
49 | if (is_broadcast_ether_addr(addr)) | |
50 | return priv->hw_params.bcast_sta_id; | |
51 | ||
52 | spin_lock_irqsave(&priv->sta_lock, flags); | |
53 | for (i = start; i < priv->hw_params.max_stations; i++) | |
54 | if (priv->stations[i].used && | |
55 | (!compare_ether_addr(priv->stations[i].sta.sta.addr, | |
56 | addr))) { | |
57 | ret = i; | |
58 | goto out; | |
59 | } | |
60 | ||
e1623446 | 61 | IWL_DEBUG_ASSOC_LIMIT(priv, "can not find STA %pM total %d\n", |
e174961c | 62 | addr, priv->num_stations); |
947b13a7 TW |
63 | |
64 | out: | |
fe6b23dd RC |
65 | /* |
66 | * It may be possible that more commands interacting with stations | |
67 | * arrive before we completed processing the adding of | |
68 | * station | |
69 | */ | |
70 | if (ret != IWL_INVALID_STATION && | |
71 | (!(priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) || | |
72 | ((priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) && | |
73 | (priv->stations[ret].used & IWL_STA_UCODE_INPROGRESS)))) { | |
91dd6c27 | 74 | IWL_ERR(priv, "Requested station info for sta %d before ready.\n", |
fe6b23dd RC |
75 | ret); |
76 | ret = IWL_INVALID_STATION; | |
77 | } | |
947b13a7 TW |
78 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
79 | return ret; | |
80 | } | |
81 | EXPORT_SYMBOL(iwl_find_station); | |
82 | ||
be1f3ab6 EG |
83 | int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) |
84 | { | |
05c914fe | 85 | if (priv->iw_mode == NL80211_IFTYPE_STATION) { |
be1f3ab6 EG |
86 | return IWL_AP_ID; |
87 | } else { | |
88 | u8 *da = ieee80211_get_DA(hdr); | |
c587de0b | 89 | return iwl_find_station(priv, da); |
be1f3ab6 EG |
90 | } |
91 | } | |
92 | EXPORT_SYMBOL(iwl_get_ra_sta_id); | |
93 | ||
1fa97aae | 94 | /* priv->sta_lock must be held */ |
24e5c401 EG |
95 | static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) |
96 | { | |
24e5c401 | 97 | |
c587de0b | 98 | if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) |
1fa97aae RC |
99 | IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u addr %pM\n", |
100 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
24e5c401 | 101 | |
1fa97aae RC |
102 | if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { |
103 | IWL_DEBUG_ASSOC(priv, | |
104 | "STA id %u addr %pM already present in uCode (according to driver)\n", | |
105 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
106 | } else { | |
107 | priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; | |
108 | IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", | |
109 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
110 | } | |
24e5c401 EG |
111 | } |
112 | ||
1fa97aae RC |
113 | static void iwl_process_add_sta_resp(struct iwl_priv *priv, |
114 | struct iwl_addsta_cmd *addsta, | |
115 | struct iwl_rx_packet *pkt, | |
116 | bool sync) | |
42132bce | 117 | { |
3257e5d4 | 118 | u8 sta_id = addsta->sta.sta_id; |
1fa97aae | 119 | unsigned long flags; |
42132bce | 120 | |
2f301227 | 121 | if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { |
15b1687c | 122 | IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", |
1fa97aae | 123 | pkt->hdr.flags); |
5696aea6 | 124 | return; |
42132bce TW |
125 | } |
126 | ||
1fa97aae RC |
127 | IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", |
128 | sta_id); | |
129 | ||
130 | spin_lock_irqsave(&priv->sta_lock, flags); | |
131 | ||
2f301227 | 132 | switch (pkt->u.add_sta.status) { |
42132bce | 133 | case ADD_STA_SUCCESS_MSK: |
1fa97aae | 134 | IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); |
24e5c401 | 135 | iwl_sta_ucode_activate(priv, sta_id); |
1fa97aae RC |
136 | break; |
137 | case ADD_STA_NO_ROOM_IN_TABLE: | |
138 | IWL_ERR(priv, "Adding station %d failed, no room in table.\n", | |
139 | sta_id); | |
140 | break; | |
141 | case ADD_STA_NO_BLOCK_ACK_RESOURCE: | |
142 | IWL_ERR(priv, "Adding station %d failed, no block ack resource.\n", | |
143 | sta_id); | |
144 | break; | |
145 | case ADD_STA_MODIFY_NON_EXIST_STA: | |
91dd6c27 | 146 | IWL_ERR(priv, "Attempting to modify non-existing station %d\n", |
1fa97aae RC |
147 | sta_id); |
148 | break; | |
42132bce | 149 | default: |
1fa97aae RC |
150 | IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", |
151 | pkt->u.add_sta.status); | |
42132bce TW |
152 | break; |
153 | } | |
1fa97aae RC |
154 | |
155 | IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", | |
156 | priv->stations[sta_id].sta.mode == | |
157 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", | |
158 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
159 | ||
160 | /* | |
161 | * XXX: The MAC address in the command buffer is often changed from | |
162 | * the original sent to the device. That is, the MAC address | |
163 | * written to the command buffer often is not the same MAC adress | |
164 | * read from the command buffer when the command returns. This | |
165 | * issue has not yet been resolved and this debugging is left to | |
166 | * observe the problem. | |
167 | */ | |
168 | IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", | |
169 | priv->stations[sta_id].sta.mode == | |
170 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", | |
171 | addsta->sta.addr); | |
1fa97aae RC |
172 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
173 | } | |
174 | ||
175 | static void iwl_add_sta_callback(struct iwl_priv *priv, | |
176 | struct iwl_device_cmd *cmd, | |
177 | struct iwl_rx_packet *pkt) | |
178 | { | |
179 | struct iwl_addsta_cmd *addsta = | |
180 | (struct iwl_addsta_cmd *)cmd->cmd.payload; | |
181 | ||
182 | iwl_process_add_sta_resp(priv, addsta, pkt, false); | |
183 | ||
42132bce TW |
184 | } |
185 | ||
17f841cd | 186 | int iwl_send_add_sta(struct iwl_priv *priv, |
133636de TW |
187 | struct iwl_addsta_cmd *sta, u8 flags) |
188 | { | |
2f301227 | 189 | struct iwl_rx_packet *pkt = NULL; |
133636de TW |
190 | int ret = 0; |
191 | u8 data[sizeof(*sta)]; | |
192 | struct iwl_host_cmd cmd = { | |
193 | .id = REPLY_ADD_STA, | |
c2acea8e | 194 | .flags = flags, |
133636de TW |
195 | .data = data, |
196 | }; | |
f875f518 | 197 | u8 sta_id __maybe_unused = sta->sta.sta_id; |
fe6b23dd RC |
198 | |
199 | IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", | |
200 | sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); | |
133636de | 201 | |
42132bce | 202 | if (flags & CMD_ASYNC) |
c2acea8e | 203 | cmd.callback = iwl_add_sta_callback; |
42132bce | 204 | else |
c2acea8e | 205 | cmd.flags |= CMD_WANT_SKB; |
133636de TW |
206 | |
207 | cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); | |
208 | ret = iwl_send_cmd(priv, &cmd); | |
209 | ||
210 | if (ret || (flags & CMD_ASYNC)) | |
211 | return ret; | |
212 | ||
133636de | 213 | if (ret == 0) { |
1fa97aae RC |
214 | pkt = (struct iwl_rx_packet *)cmd.reply_page; |
215 | iwl_process_add_sta_resp(priv, sta, pkt, true); | |
133636de | 216 | } |
64a76b50 | 217 | iwl_free_pages(priv, cmd.reply_page); |
133636de TW |
218 | |
219 | return ret; | |
220 | } | |
17f841cd | 221 | EXPORT_SYMBOL(iwl_send_add_sta); |
947b13a7 | 222 | |
4f40e4d9 | 223 | static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, |
d9fe60de | 224 | struct ieee80211_sta_ht_cap *sta_ht_inf) |
4f40e4d9 TW |
225 | { |
226 | __le32 sta_flags; | |
227 | u8 mimo_ps_mode; | |
228 | ||
229 | if (!sta_ht_inf || !sta_ht_inf->ht_supported) | |
230 | goto done; | |
231 | ||
00c5ae2f | 232 | mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; |
3f3e0376 WYG |
233 | IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n", |
234 | (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? | |
235 | "static" : | |
236 | (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? | |
237 | "dynamic" : "disabled"); | |
4f40e4d9 TW |
238 | |
239 | sta_flags = priv->stations[index].sta.station_flags; | |
240 | ||
241 | sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); | |
242 | ||
243 | switch (mimo_ps_mode) { | |
00c5ae2f | 244 | case WLAN_HT_CAP_SM_PS_STATIC: |
4f40e4d9 TW |
245 | sta_flags |= STA_FLG_MIMO_DIS_MSK; |
246 | break; | |
00c5ae2f | 247 | case WLAN_HT_CAP_SM_PS_DYNAMIC: |
4f40e4d9 TW |
248 | sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; |
249 | break; | |
00c5ae2f | 250 | case WLAN_HT_CAP_SM_PS_DISABLED: |
4f40e4d9 TW |
251 | break; |
252 | default: | |
39aadf8c | 253 | IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); |
4f40e4d9 TW |
254 | break; |
255 | } | |
256 | ||
257 | sta_flags |= cpu_to_le32( | |
258 | (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); | |
259 | ||
260 | sta_flags |= cpu_to_le32( | |
261 | (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); | |
262 | ||
7aafef1c WYG |
263 | if (iwl_is_ht40_tx_allowed(priv, sta_ht_inf)) |
264 | sta_flags |= STA_FLG_HT40_EN_MSK; | |
4f40e4d9 | 265 | else |
7aafef1c | 266 | sta_flags &= ~STA_FLG_HT40_EN_MSK; |
4f40e4d9 TW |
267 | |
268 | priv->stations[index].sta.station_flags = sta_flags; | |
269 | done: | |
270 | return; | |
271 | } | |
4f40e4d9 TW |
272 | |
273 | /** | |
fe6b23dd RC |
274 | * iwl_prep_station - Prepare station information for addition |
275 | * | |
276 | * should be called with sta_lock held | |
4f40e4d9 | 277 | */ |
fe6b23dd RC |
278 | static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr, |
279 | bool is_ap, | |
280 | struct ieee80211_sta_ht_cap *ht_info) | |
4f40e4d9 | 281 | { |
4f40e4d9 | 282 | struct iwl_station_entry *station; |
c587de0b | 283 | int i; |
fe6b23dd | 284 | u8 sta_id = IWL_INVALID_STATION; |
c587de0b | 285 | u16 rate; |
4f40e4d9 | 286 | |
4f40e4d9 | 287 | if (is_ap) |
24e5c401 | 288 | sta_id = IWL_AP_ID; |
4f40e4d9 | 289 | else if (is_broadcast_ether_addr(addr)) |
24e5c401 | 290 | sta_id = priv->hw_params.bcast_sta_id; |
4f40e4d9 TW |
291 | else |
292 | for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) { | |
293 | if (!compare_ether_addr(priv->stations[i].sta.sta.addr, | |
294 | addr)) { | |
24e5c401 | 295 | sta_id = i; |
4f40e4d9 TW |
296 | break; |
297 | } | |
298 | ||
299 | if (!priv->stations[i].used && | |
24e5c401 EG |
300 | sta_id == IWL_INVALID_STATION) |
301 | sta_id = i; | |
4f40e4d9 TW |
302 | } |
303 | ||
fe6b23dd RC |
304 | /* |
305 | * These two conditions have the same outcome, but keep them | |
306 | * separate | |
307 | */ | |
308 | if (unlikely(sta_id == IWL_INVALID_STATION)) | |
309 | return sta_id; | |
310 | ||
311 | /* | |
312 | * uCode is not able to deal with multiple requests to add a | |
313 | * station. Keep track if one is in progress so that we do not send | |
314 | * another. | |
315 | */ | |
316 | if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { | |
317 | IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", | |
318 | sta_id); | |
24e5c401 | 319 | return sta_id; |
4f40e4d9 TW |
320 | } |
321 | ||
fe6b23dd RC |
322 | if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && |
323 | (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && | |
24e5c401 | 324 | !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) { |
fe6b23dd RC |
325 | IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", |
326 | sta_id, addr); | |
24e5c401 | 327 | return sta_id; |
4f40e4d9 TW |
328 | } |
329 | ||
24e5c401 EG |
330 | station = &priv->stations[sta_id]; |
331 | station->used = IWL_STA_DRIVER_ACTIVE; | |
e1623446 | 332 | IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", |
e174961c | 333 | sta_id, addr); |
4f40e4d9 TW |
334 | priv->num_stations++; |
335 | ||
336 | /* Set up the REPLY_ADD_STA command to send to device */ | |
337 | memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); | |
338 | memcpy(station->sta.sta.addr, addr, ETH_ALEN); | |
339 | station->sta.mode = 0; | |
24e5c401 | 340 | station->sta.sta.sta_id = sta_id; |
4f40e4d9 TW |
341 | station->sta.station_flags = 0; |
342 | ||
343 | /* BCAST station and IBSS stations do not work in HT mode */ | |
24e5c401 | 344 | if (sta_id != priv->hw_params.bcast_sta_id && |
05c914fe | 345 | priv->iw_mode != NL80211_IFTYPE_ADHOC) |
24e5c401 | 346 | iwl_set_ht_add_station(priv, sta_id, ht_info); |
4f40e4d9 | 347 | |
c587de0b TW |
348 | /* 3945 only */ |
349 | rate = (priv->band == IEEE80211_BAND_5GHZ) ? | |
350 | IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP; | |
351 | /* Turn on both antennas for the station... */ | |
352 | station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); | |
353 | ||
fe6b23dd RC |
354 | return sta_id; |
355 | ||
356 | } | |
357 | ||
358 | #define STA_WAIT_TIMEOUT (HZ/2) | |
359 | ||
360 | /** | |
361 | * iwl_add_station_common - | |
362 | */ | |
363 | int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr, | |
364 | bool is_ap, | |
365 | struct ieee80211_sta_ht_cap *ht_info, | |
366 | u8 *sta_id_r) | |
367 | { | |
368 | struct iwl_station_entry *station; | |
369 | unsigned long flags_spin; | |
370 | int ret = 0; | |
371 | u8 sta_id; | |
372 | ||
373 | *sta_id_r = 0; | |
374 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
375 | sta_id = iwl_prep_station(priv, addr, is_ap, ht_info); | |
376 | if (sta_id == IWL_INVALID_STATION) { | |
377 | IWL_ERR(priv, "Unable to prepare station %pM for addition\n", | |
378 | addr); | |
379 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
380 | return -EINVAL; | |
381 | } | |
382 | ||
383 | /* | |
384 | * uCode is not able to deal with multiple requests to add a | |
385 | * station. Keep track if one is in progress so that we do not send | |
386 | * another. | |
387 | */ | |
388 | if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { | |
389 | IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", | |
390 | sta_id); | |
391 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
392 | return -EEXIST; | |
393 | } | |
394 | ||
395 | if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && | |
396 | (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { | |
397 | IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", | |
398 | sta_id, addr); | |
399 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
400 | return -EEXIST; | |
401 | } | |
402 | ||
403 | priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; | |
404 | station = &priv->stations[sta_id]; | |
4f40e4d9 TW |
405 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
406 | ||
407 | /* Add station to device's station table */ | |
fe6b23dd RC |
408 | ret = iwl_send_add_sta(priv, &station->sta, CMD_SYNC); |
409 | if (ret) { | |
410 | IWL_ERR(priv, "Adding station %pM failed.\n", station->sta.sta.addr); | |
411 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
412 | priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; | |
413 | priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; | |
414 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
415 | } | |
416 | *sta_id_r = sta_id; | |
417 | return ret; | |
4f40e4d9 | 418 | } |
fe6b23dd | 419 | EXPORT_SYMBOL(iwl_add_station_common); |
4f40e4d9 | 420 | |
fe6b23dd | 421 | static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap) |
7a999bf0 | 422 | { |
fe6b23dd RC |
423 | int i, r; |
424 | struct iwl_link_quality_cmd link_cmd = { | |
425 | .reserved1 = 0, | |
426 | }; | |
427 | u32 rate_flags; | |
57e40d36 | 428 | int ret = 0; |
fe6b23dd RC |
429 | |
430 | /* Set up the rate scaling to start at selected rate, fall back | |
431 | * all the way down to 1M in IEEE order, and then spin on 1M */ | |
432 | if (is_ap) | |
433 | r = IWL_RATE_54M_INDEX; | |
434 | else if (priv->band == IEEE80211_BAND_5GHZ) | |
435 | r = IWL_RATE_6M_INDEX; | |
436 | else | |
437 | r = IWL_RATE_1M_INDEX; | |
24e5c401 | 438 | |
fe6b23dd RC |
439 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { |
440 | rate_flags = 0; | |
441 | if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) | |
442 | rate_flags |= RATE_MCS_CCK_MSK; | |
24e5c401 | 443 | |
fe6b23dd RC |
444 | rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << |
445 | RATE_MCS_ANT_POS; | |
24e5c401 | 446 | |
fe6b23dd RC |
447 | link_cmd.rs_table[i].rate_n_flags = |
448 | iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); | |
449 | r = iwl_get_prev_ieee_rate(r); | |
450 | } | |
24e5c401 | 451 | |
fe6b23dd RC |
452 | link_cmd.general_params.single_stream_ant_msk = |
453 | first_antenna(priv->hw_params.valid_tx_ant); | |
3a23d695 WYG |
454 | |
455 | link_cmd.general_params.dual_stream_ant_msk = | |
456 | priv->hw_params.valid_tx_ant & | |
457 | ~first_antenna(priv->hw_params.valid_tx_ant); | |
458 | if (!link_cmd.general_params.dual_stream_ant_msk) { | |
459 | link_cmd.general_params.dual_stream_ant_msk = ANT_AB; | |
460 | } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { | |
461 | link_cmd.general_params.dual_stream_ant_msk = | |
462 | priv->hw_params.valid_tx_ant; | |
463 | } | |
464 | ||
fe6b23dd RC |
465 | link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; |
466 | link_cmd.agg_params.agg_time_limit = | |
467 | cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); | |
24e5c401 | 468 | |
fe6b23dd RC |
469 | /* Update the rate scaling for control frame Tx to AP */ |
470 | link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id; | |
24e5c401 | 471 | |
57e40d36 | 472 | ret = iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, |
fe6b23dd | 473 | sizeof(link_cmd), &link_cmd); |
57e40d36 WYG |
474 | if (ret) |
475 | IWL_ERR(priv, "REPLY_TX_LINK_QUALITY_CMD failed (%d)\n", ret); | |
7a999bf0 TW |
476 | } |
477 | ||
fe6b23dd RC |
478 | /* |
479 | * iwl_add_local_stations - Add stations not requested by mac80211 | |
480 | * | |
481 | * This will be either the broadcast station or the bssid station needed by | |
482 | * ad-hoc. | |
483 | * | |
484 | * Function sleeps. | |
485 | */ | |
486 | int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs) | |
7a999bf0 | 487 | { |
fe6b23dd RC |
488 | int ret; |
489 | u8 sta_id; | |
7a999bf0 | 490 | |
fe6b23dd RC |
491 | ret = iwl_add_station_common(priv, addr, 0, NULL, &sta_id); |
492 | if (ret) { | |
493 | IWL_ERR(priv, "Unable to add station %pM\n", addr); | |
494 | return ret; | |
7a999bf0 TW |
495 | } |
496 | ||
fe6b23dd RC |
497 | if (init_rs) |
498 | /* Set up default rate scaling table in device's station table */ | |
499 | iwl_sta_init_lq(priv, addr, false); | |
500 | return 0; | |
501 | } | |
502 | EXPORT_SYMBOL(iwl_add_local_station); | |
503 | ||
504 | /** | |
505 | * iwl_sta_ucode_deactivate - deactivate ucode status for a station | |
506 | * | |
507 | * priv->sta_lock must be held | |
508 | */ | |
509 | static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) | |
510 | { | |
511 | /* Ucode must be active and driver must be non active */ | |
512 | if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE) | |
513 | IWL_ERR(priv, "removed non active STA %u\n", sta_id); | |
514 | ||
515 | priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; | |
516 | ||
517 | memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); | |
518 | IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); | |
7a999bf0 TW |
519 | } |
520 | ||
fe6b23dd RC |
521 | static int iwl_send_remove_station(struct iwl_priv *priv, |
522 | struct iwl_station_entry *station) | |
7a999bf0 | 523 | { |
2f301227 | 524 | struct iwl_rx_packet *pkt; |
7a999bf0 TW |
525 | int ret; |
526 | ||
fe6b23dd | 527 | unsigned long flags_spin; |
7a999bf0 TW |
528 | struct iwl_rem_sta_cmd rm_sta_cmd; |
529 | ||
530 | struct iwl_host_cmd cmd = { | |
531 | .id = REPLY_REMOVE_STA, | |
532 | .len = sizeof(struct iwl_rem_sta_cmd), | |
fe6b23dd | 533 | .flags = CMD_SYNC, |
7a999bf0 TW |
534 | .data = &rm_sta_cmd, |
535 | }; | |
536 | ||
537 | memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); | |
538 | rm_sta_cmd.num_sta = 1; | |
fe6b23dd RC |
539 | memcpy(&rm_sta_cmd.addr, &station->sta.sta.addr , ETH_ALEN); |
540 | ||
541 | cmd.flags |= CMD_WANT_SKB; | |
7a999bf0 | 542 | |
7a999bf0 TW |
543 | ret = iwl_send_cmd(priv, &cmd); |
544 | ||
fe6b23dd | 545 | if (ret) |
7a999bf0 TW |
546 | return ret; |
547 | ||
2f301227 ZY |
548 | pkt = (struct iwl_rx_packet *)cmd.reply_page; |
549 | if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { | |
15b1687c | 550 | IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", |
2f301227 | 551 | pkt->hdr.flags); |
7a999bf0 TW |
552 | ret = -EIO; |
553 | } | |
554 | ||
555 | if (!ret) { | |
2f301227 | 556 | switch (pkt->u.rem_sta.status) { |
7a999bf0 | 557 | case REM_STA_SUCCESS_MSK: |
fe6b23dd RC |
558 | spin_lock_irqsave(&priv->sta_lock, flags_spin); |
559 | iwl_sta_ucode_deactivate(priv, station->sta.sta.sta_id); | |
560 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
e1623446 | 561 | IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); |
7a999bf0 TW |
562 | break; |
563 | default: | |
564 | ret = -EIO; | |
15b1687c | 565 | IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); |
7a999bf0 TW |
566 | break; |
567 | } | |
568 | } | |
64a76b50 | 569 | iwl_free_pages(priv, cmd.reply_page); |
7a999bf0 TW |
570 | |
571 | return ret; | |
572 | } | |
be1f3ab6 | 573 | |
7a999bf0 TW |
574 | /** |
575 | * iwl_remove_station - Remove driver's knowledge of station. | |
7a999bf0 | 576 | */ |
fe6b23dd | 577 | static int iwl_remove_station(struct iwl_priv *priv, struct ieee80211_sta *sta) |
7a999bf0 | 578 | { |
24e5c401 EG |
579 | int sta_id = IWL_INVALID_STATION; |
580 | int i, ret = -EINVAL; | |
7a999bf0 | 581 | unsigned long flags; |
fe6b23dd RC |
582 | bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; |
583 | struct iwl_station_entry *station; | |
584 | ||
585 | if (!iwl_is_ready(priv)) { | |
586 | IWL_DEBUG_INFO(priv, | |
91dd6c27 | 587 | "Unable to remove station %pM, device not ready.\n", |
fe6b23dd RC |
588 | sta->addr); |
589 | /* | |
590 | * It is typical for stations to be removed when we are | |
591 | * going down. Return success since device will be down | |
592 | * soon anyway | |
593 | */ | |
594 | return 0; | |
595 | } | |
7a999bf0 TW |
596 | |
597 | spin_lock_irqsave(&priv->sta_lock, flags); | |
598 | ||
599 | if (is_ap) | |
24e5c401 | 600 | sta_id = IWL_AP_ID; |
7a999bf0 TW |
601 | else |
602 | for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) | |
603 | if (priv->stations[i].used && | |
604 | !compare_ether_addr(priv->stations[i].sta.sta.addr, | |
fe6b23dd | 605 | sta->addr)) { |
24e5c401 | 606 | sta_id = i; |
7a999bf0 TW |
607 | break; |
608 | } | |
609 | ||
24e5c401 | 610 | if (unlikely(sta_id == IWL_INVALID_STATION)) |
7a999bf0 TW |
611 | goto out; |
612 | ||
e1623446 | 613 | IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", |
fe6b23dd | 614 | sta_id, sta->addr); |
24e5c401 EG |
615 | |
616 | if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { | |
fe6b23dd RC |
617 | IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", |
618 | sta->addr); | |
24e5c401 EG |
619 | goto out; |
620 | } | |
621 | ||
622 | if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { | |
fe6b23dd RC |
623 | IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", |
624 | sta->addr); | |
24e5c401 | 625 | goto out; |
7a999bf0 TW |
626 | } |
627 | ||
24e5c401 EG |
628 | |
629 | priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; | |
630 | ||
631 | priv->num_stations--; | |
632 | ||
7a999bf0 | 633 | BUG_ON(priv->num_stations < 0); |
24e5c401 | 634 | |
fe6b23dd | 635 | station = &priv->stations[sta_id]; |
7a999bf0 | 636 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
24e5c401 | 637 | |
fe6b23dd | 638 | ret = iwl_send_remove_station(priv, station); |
24e5c401 | 639 | return ret; |
7a999bf0 TW |
640 | out: |
641 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
24e5c401 | 642 | return ret; |
7a999bf0 | 643 | } |
24e5c401 | 644 | |
83dde8c9 | 645 | /** |
7e246191 RC |
646 | * iwl_clear_ucode_stations() - clear entire station table driver and/or ucode |
647 | * @priv: | |
648 | * @force: If set then the uCode station table needs to be cleared here. If | |
649 | * not set then the uCode station table has already been cleared, | |
650 | * for example after sending it a RXON command without ASSOC bit | |
651 | * set, and we just need to change driver state here. | |
83dde8c9 | 652 | */ |
7e246191 | 653 | void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force) |
83dde8c9 | 654 | { |
48676eb3 | 655 | int i; |
7e246191 RC |
656 | unsigned long flags_spin; |
657 | bool cleared = false; | |
658 | ||
659 | IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver%s\n", | |
660 | force ? " and ucode" : ""); | |
661 | ||
662 | if (force) { | |
663 | if (!iwl_is_ready(priv)) { | |
664 | /* | |
665 | * If device is not ready at this point the station | |
666 | * table is likely already empty (uCode not ready | |
667 | * to receive station requests) or will soon be | |
668 | * due to interface going down. | |
669 | */ | |
670 | IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n"); | |
671 | } else { | |
672 | iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL); | |
673 | } | |
674 | } | |
83dde8c9 | 675 | |
7e246191 RC |
676 | spin_lock_irqsave(&priv->sta_lock, flags_spin); |
677 | if (force) { | |
678 | IWL_DEBUG_INFO(priv, "Clearing all station information in driver\n"); | |
679 | priv->num_stations = 0; | |
680 | memset(priv->stations, 0, sizeof(priv->stations)); | |
681 | } else { | |
682 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
683 | if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { | |
91dd6c27 | 684 | IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d\n", i); |
7e246191 RC |
685 | priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; |
686 | cleared = true; | |
687 | } | |
688 | } | |
689 | } | |
690 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
691 | ||
692 | if (!cleared) | |
693 | IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n"); | |
694 | } | |
695 | EXPORT_SYMBOL(iwl_clear_ucode_stations); | |
83dde8c9 | 696 | |
7e246191 RC |
697 | /** |
698 | * iwl_restore_stations() - Restore driver known stations to device | |
699 | * | |
700 | * All stations considered active by driver, but not present in ucode, is | |
701 | * restored. | |
fe6b23dd RC |
702 | * |
703 | * Function sleeps. | |
7e246191 RC |
704 | */ |
705 | void iwl_restore_stations(struct iwl_priv *priv) | |
706 | { | |
fe6b23dd | 707 | struct iwl_station_entry *station; |
7e246191 RC |
708 | unsigned long flags_spin; |
709 | int i; | |
710 | bool found = false; | |
fe6b23dd | 711 | int ret; |
83dde8c9 | 712 | |
7e246191 RC |
713 | if (!iwl_is_ready(priv)) { |
714 | IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); | |
715 | return; | |
716 | } | |
83dde8c9 | 717 | |
7e246191 RC |
718 | IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); |
719 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
720 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
721 | if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && | |
722 | !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { | |
723 | IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", | |
724 | priv->stations[i].sta.sta.addr); | |
725 | priv->stations[i].sta.mode = 0; | |
726 | priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; | |
727 | found = true; | |
728 | } | |
729 | } | |
5e46882e | 730 | |
7e246191 RC |
731 | for (i = 0; i < priv->hw_params.max_stations; i++) { |
732 | if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { | |
fe6b23dd RC |
733 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
734 | station = &priv->stations[i]; | |
735 | ret = iwl_send_add_sta(priv, &priv->stations[i].sta, CMD_SYNC); | |
736 | if (ret) { | |
737 | IWL_ERR(priv, "Adding station %pM failed.\n", | |
738 | station->sta.sta.addr); | |
739 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
740 | priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE; | |
741 | priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; | |
742 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
743 | } | |
744 | /* | |
745 | * Rate scaling has already been initialized, send | |
746 | * current LQ command | |
747 | */ | |
748 | if (station->lq) | |
749 | iwl_send_lq_cmd(priv, station->lq, CMD_SYNC, true); | |
750 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
7e246191 RC |
751 | priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; |
752 | } | |
48676eb3 MA |
753 | } |
754 | ||
7e246191 RC |
755 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
756 | if (!found) | |
757 | IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n"); | |
758 | else | |
fe6b23dd | 759 | IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n"); |
83dde8c9 | 760 | } |
7e246191 | 761 | EXPORT_SYMBOL(iwl_restore_stations); |
83dde8c9 | 762 | |
6e21f15c | 763 | int iwl_get_free_ucode_key_index(struct iwl_priv *priv) |
80fb47a1 EG |
764 | { |
765 | int i; | |
766 | ||
767 | for (i = 0; i < STA_KEY_MAX_NUM; i++) | |
77bab602 | 768 | if (!test_and_set_bit(i, &priv->ucode_key_table)) |
80fb47a1 EG |
769 | return i; |
770 | ||
40a9a829 | 771 | return WEP_INVALID_OFFSET; |
80fb47a1 | 772 | } |
6e21f15c | 773 | EXPORT_SYMBOL(iwl_get_free_ucode_key_index); |
6974e363 | 774 | |
335348b1 | 775 | static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) |
6974e363 EG |
776 | { |
777 | int i, not_empty = 0; | |
778 | u8 buff[sizeof(struct iwl_wep_cmd) + | |
779 | sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; | |
780 | struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; | |
781 | size_t cmd_size = sizeof(struct iwl_wep_cmd); | |
782 | struct iwl_host_cmd cmd = { | |
783 | .id = REPLY_WEPKEY, | |
784 | .data = wep_cmd, | |
72e15d71 | 785 | .flags = CMD_SYNC, |
6974e363 EG |
786 | }; |
787 | ||
72e15d71 JB |
788 | might_sleep(); |
789 | ||
6974e363 EG |
790 | memset(wep_cmd, 0, cmd_size + |
791 | (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); | |
792 | ||
793 | for (i = 0; i < WEP_KEYS_MAX ; i++) { | |
794 | wep_cmd->key[i].key_index = i; | |
795 | if (priv->wep_keys[i].key_size) { | |
796 | wep_cmd->key[i].key_offset = i; | |
797 | not_empty = 1; | |
798 | } else { | |
799 | wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; | |
800 | } | |
801 | ||
802 | wep_cmd->key[i].key_size = priv->wep_keys[i].key_size; | |
803 | memcpy(&wep_cmd->key[i].key[3], priv->wep_keys[i].key, | |
804 | priv->wep_keys[i].key_size); | |
805 | } | |
806 | ||
807 | wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; | |
808 | wep_cmd->num_keys = WEP_KEYS_MAX; | |
809 | ||
810 | cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; | |
811 | ||
812 | cmd.len = cmd_size; | |
813 | ||
814 | if (not_empty || send_if_empty) | |
815 | return iwl_send_cmd(priv, &cmd); | |
816 | else | |
817 | return 0; | |
818 | } | |
335348b1 JB |
819 | |
820 | int iwl_restore_default_wep_keys(struct iwl_priv *priv) | |
821 | { | |
822 | WARN_ON(!mutex_is_locked(&priv->mutex)); | |
823 | ||
824 | return iwl_send_static_wepkey_cmd(priv, 0); | |
825 | } | |
826 | EXPORT_SYMBOL(iwl_restore_default_wep_keys); | |
6974e363 EG |
827 | |
828 | int iwl_remove_default_wep_key(struct iwl_priv *priv, | |
80fb47a1 | 829 | struct ieee80211_key_conf *keyconf) |
6974e363 EG |
830 | { |
831 | int ret; | |
6974e363 | 832 | |
72e15d71 JB |
833 | WARN_ON(!mutex_is_locked(&priv->mutex)); |
834 | ||
2d1bb9e5 RC |
835 | IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", |
836 | keyconf->keyidx); | |
80fb47a1 | 837 | |
80fb47a1 | 838 | memset(&priv->wep_keys[keyconf->keyidx], 0, sizeof(priv->wep_keys[0])); |
2d1bb9e5 RC |
839 | if (iwl_is_rfkill(priv)) { |
840 | IWL_DEBUG_WEP(priv, "Not sending REPLY_WEPKEY command due to RFKILL.\n"); | |
72e15d71 | 841 | /* but keys in device are clear anyway so return success */ |
2d1bb9e5 RC |
842 | return 0; |
843 | } | |
6974e363 | 844 | ret = iwl_send_static_wepkey_cmd(priv, 1); |
e1623446 | 845 | IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", |
4564ce8b | 846 | keyconf->keyidx, ret); |
6974e363 EG |
847 | |
848 | return ret; | |
849 | } | |
27aaba0c | 850 | EXPORT_SYMBOL(iwl_remove_default_wep_key); |
6974e363 EG |
851 | |
852 | int iwl_set_default_wep_key(struct iwl_priv *priv, | |
853 | struct ieee80211_key_conf *keyconf) | |
854 | { | |
855 | int ret; | |
72e15d71 JB |
856 | |
857 | WARN_ON(!mutex_is_locked(&priv->mutex)); | |
6974e363 | 858 | |
4564ce8b EG |
859 | if (keyconf->keylen != WEP_KEY_LEN_128 && |
860 | keyconf->keylen != WEP_KEY_LEN_64) { | |
e1623446 | 861 | IWL_DEBUG_WEP(priv, "Bad WEP key length %d\n", keyconf->keylen); |
4564ce8b EG |
862 | return -EINVAL; |
863 | } | |
864 | ||
6974e363 | 865 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; |
ccc038ab | 866 | keyconf->hw_key_idx = HW_KEY_DEFAULT; |
6974e363 EG |
867 | priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP; |
868 | ||
6974e363 EG |
869 | priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; |
870 | memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key, | |
871 | keyconf->keylen); | |
872 | ||
873 | ret = iwl_send_static_wepkey_cmd(priv, 0); | |
e1623446 | 874 | IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", |
4564ce8b | 875 | keyconf->keylen, keyconf->keyidx, ret); |
6974e363 EG |
876 | |
877 | return ret; | |
878 | } | |
27aaba0c | 879 | EXPORT_SYMBOL(iwl_set_default_wep_key); |
6974e363 | 880 | |
7480513f | 881 | static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv, |
0211ddda EG |
882 | struct ieee80211_key_conf *keyconf, |
883 | u8 sta_id) | |
884 | { | |
885 | unsigned long flags; | |
886 | __le16 key_flags = 0; | |
887 | int ret; | |
888 | ||
889 | keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; | |
0211ddda EG |
890 | |
891 | key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); | |
892 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | |
893 | key_flags &= ~STA_KEY_FLG_INVALID; | |
894 | ||
895 | if (keyconf->keylen == WEP_KEY_LEN_128) | |
896 | key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; | |
897 | ||
5425e490 | 898 | if (sta_id == priv->hw_params.bcast_sta_id) |
0211ddda EG |
899 | key_flags |= STA_KEY_MULTICAST_MSK; |
900 | ||
901 | spin_lock_irqsave(&priv->sta_lock, flags); | |
902 | ||
903 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
904 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | |
905 | priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; | |
906 | ||
907 | memcpy(priv->stations[sta_id].keyinfo.key, | |
908 | keyconf->key, keyconf->keylen); | |
909 | ||
910 | memcpy(&priv->stations[sta_id].sta.key.key[3], | |
911 | keyconf->key, keyconf->keylen); | |
912 | ||
3ec47732 EG |
913 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) |
914 | == STA_KEY_FLG_NO_ENC) | |
915 | priv->stations[sta_id].sta.key.key_offset = | |
80fb47a1 | 916 | iwl_get_free_ucode_key_index(priv); |
3ec47732 EG |
917 | /* else, we are overriding an existing key => no need to allocated room |
918 | * in uCode. */ | |
0211ddda | 919 | |
40a9a829 | 920 | WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, |
e724b8fe | 921 | "no space for a new key"); |
40a9a829 | 922 | |
3ec47732 | 923 | priv->stations[sta_id].sta.key.key_flags = key_flags; |
0211ddda EG |
924 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; |
925 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
926 | ||
133636de | 927 | ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
0211ddda EG |
928 | |
929 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
930 | ||
931 | return ret; | |
932 | } | |
7480513f EG |
933 | |
934 | static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv, | |
935 | struct ieee80211_key_conf *keyconf, | |
936 | u8 sta_id) | |
937 | { | |
938 | unsigned long flags; | |
939 | __le16 key_flags = 0; | |
40a9a829 | 940 | int ret; |
7480513f EG |
941 | |
942 | key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); | |
943 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | |
944 | key_flags &= ~STA_KEY_FLG_INVALID; | |
945 | ||
5425e490 | 946 | if (sta_id == priv->hw_params.bcast_sta_id) |
7480513f EG |
947 | key_flags |= STA_KEY_MULTICAST_MSK; |
948 | ||
949 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
7480513f EG |
950 | |
951 | spin_lock_irqsave(&priv->sta_lock, flags); | |
952 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
953 | priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; | |
954 | ||
955 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, | |
956 | keyconf->keylen); | |
957 | ||
958 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, | |
959 | keyconf->keylen); | |
960 | ||
3ec47732 EG |
961 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) |
962 | == STA_KEY_FLG_NO_ENC) | |
963 | priv->stations[sta_id].sta.key.key_offset = | |
964 | iwl_get_free_ucode_key_index(priv); | |
965 | /* else, we are overriding an existing key => no need to allocated room | |
966 | * in uCode. */ | |
967 | ||
40a9a829 | 968 | WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, |
e724b8fe | 969 | "no space for a new key"); |
40a9a829 | 970 | |
7480513f EG |
971 | priv->stations[sta_id].sta.key.key_flags = key_flags; |
972 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | |
973 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
974 | ||
40a9a829 TW |
975 | ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
976 | ||
7480513f EG |
977 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
978 | ||
40a9a829 | 979 | return ret; |
7480513f EG |
980 | } |
981 | ||
982 | static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv, | |
983 | struct ieee80211_key_conf *keyconf, | |
984 | u8 sta_id) | |
985 | { | |
986 | unsigned long flags; | |
987 | int ret = 0; | |
299f5462 RC |
988 | __le16 key_flags = 0; |
989 | ||
990 | key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); | |
991 | key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); | |
992 | key_flags &= ~STA_KEY_FLG_INVALID; | |
993 | ||
994 | if (sta_id == priv->hw_params.bcast_sta_id) | |
995 | key_flags |= STA_KEY_MULTICAST_MSK; | |
7480513f EG |
996 | |
997 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
998 | keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; | |
7480513f EG |
999 | |
1000 | spin_lock_irqsave(&priv->sta_lock, flags); | |
1001 | ||
1002 | priv->stations[sta_id].keyinfo.alg = keyconf->alg; | |
7480513f | 1003 | priv->stations[sta_id].keyinfo.keylen = 16; |
3ec47732 EG |
1004 | |
1005 | if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) | |
1006 | == STA_KEY_FLG_NO_ENC) | |
1007 | priv->stations[sta_id].sta.key.key_offset = | |
77bab602 | 1008 | iwl_get_free_ucode_key_index(priv); |
3ec47732 EG |
1009 | /* else, we are overriding an existing key => no need to allocated room |
1010 | * in uCode. */ | |
7480513f | 1011 | |
40a9a829 | 1012 | WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, |
e724b8fe | 1013 | "no space for a new key"); |
40a9a829 | 1014 | |
299f5462 RC |
1015 | priv->stations[sta_id].sta.key.key_flags = key_flags; |
1016 | ||
1017 | ||
7480513f EG |
1018 | /* This copy is acutally not needed: we get the key with each TX */ |
1019 | memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16); | |
1020 | ||
1021 | memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16); | |
1022 | ||
1023 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1024 | ||
1025 | return ret; | |
1026 | } | |
1027 | ||
9f58671e TW |
1028 | void iwl_update_tkip_key(struct iwl_priv *priv, |
1029 | struct ieee80211_key_conf *keyconf, | |
1030 | const u8 *addr, u32 iv32, u16 *phase1key) | |
1031 | { | |
1032 | u8 sta_id = IWL_INVALID_STATION; | |
1033 | unsigned long flags; | |
9f58671e | 1034 | int i; |
9f58671e | 1035 | |
c587de0b | 1036 | sta_id = iwl_find_station(priv, addr); |
9f58671e | 1037 | if (sta_id == IWL_INVALID_STATION) { |
e1623446 | 1038 | IWL_DEBUG_MAC80211(priv, "leave - %pM not in station map.\n", |
9f58671e TW |
1039 | addr); |
1040 | return; | |
1041 | } | |
1042 | ||
1043 | if (iwl_scan_cancel(priv)) { | |
1044 | /* cancel scan failed, just live w/ bad key and rely | |
1045 | briefly on SW decryption */ | |
1046 | return; | |
1047 | } | |
1048 | ||
9f58671e TW |
1049 | spin_lock_irqsave(&priv->sta_lock, flags); |
1050 | ||
9f58671e TW |
1051 | priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; |
1052 | ||
1053 | for (i = 0; i < 5; i++) | |
1054 | priv->stations[sta_id].sta.key.tkip_rx_ttak[i] = | |
1055 | cpu_to_le16(phase1key[i]); | |
1056 | ||
1057 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; | |
1058 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
1059 | ||
1060 | iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); | |
1061 | ||
1062 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1063 | ||
1064 | } | |
1065 | EXPORT_SYMBOL(iwl_update_tkip_key); | |
1066 | ||
3ec47732 EG |
1067 | int iwl_remove_dynamic_key(struct iwl_priv *priv, |
1068 | struct ieee80211_key_conf *keyconf, | |
1069 | u8 sta_id) | |
7480513f EG |
1070 | { |
1071 | unsigned long flags; | |
3ec47732 EG |
1072 | int ret = 0; |
1073 | u16 key_flags; | |
1074 | u8 keyidx; | |
7480513f | 1075 | |
ccc038ab | 1076 | priv->key_mapping_key--; |
7480513f EG |
1077 | |
1078 | spin_lock_irqsave(&priv->sta_lock, flags); | |
3ec47732 EG |
1079 | key_flags = le16_to_cpu(priv->stations[sta_id].sta.key.key_flags); |
1080 | keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; | |
1081 | ||
e1623446 | 1082 | IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", |
4564ce8b EG |
1083 | keyconf->keyidx, sta_id); |
1084 | ||
3ec47732 EG |
1085 | if (keyconf->keyidx != keyidx) { |
1086 | /* We need to remove a key with index different that the one | |
1087 | * in the uCode. This means that the key we need to remove has | |
1088 | * been replaced by another one with different index. | |
1089 | * Don't do anything and return ok | |
1090 | */ | |
1091 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1092 | return 0; | |
1093 | } | |
1094 | ||
40a9a829 | 1095 | if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET) { |
39aadf8c | 1096 | IWL_WARN(priv, "Removing wrong key %d 0x%x\n", |
40a9a829 TW |
1097 | keyconf->keyidx, key_flags); |
1098 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1099 | return 0; | |
1100 | } | |
1101 | ||
7480513f EG |
1102 | if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset, |
1103 | &priv->ucode_key_table)) | |
15b1687c | 1104 | IWL_ERR(priv, "index %d not used in uCode key table.\n", |
7480513f EG |
1105 | priv->stations[sta_id].sta.key.key_offset); |
1106 | memset(&priv->stations[sta_id].keyinfo, 0, | |
6def9761 | 1107 | sizeof(struct iwl_hw_key)); |
7480513f EG |
1108 | memset(&priv->stations[sta_id].sta.key, 0, |
1109 | sizeof(struct iwl4965_keyinfo)); | |
3ec47732 EG |
1110 | priv->stations[sta_id].sta.key.key_flags = |
1111 | STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; | |
1112 | priv->stations[sta_id].sta.key.key_offset = WEP_INVALID_OFFSET; | |
7480513f EG |
1113 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; |
1114 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
7480513f | 1115 | |
2d1bb9e5 | 1116 | if (iwl_is_rfkill(priv)) { |
91dd6c27 | 1117 | IWL_DEBUG_WEP(priv, "Not sending REPLY_ADD_STA command because RFKILL enabled.\n"); |
2d1bb9e5 RC |
1118 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
1119 | return 0; | |
1120 | } | |
ccc038ab | 1121 | ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
3ec47732 EG |
1122 | spin_unlock_irqrestore(&priv->sta_lock, flags); |
1123 | return ret; | |
7480513f | 1124 | } |
27aaba0c | 1125 | EXPORT_SYMBOL(iwl_remove_dynamic_key); |
7480513f EG |
1126 | |
1127 | int iwl_set_dynamic_key(struct iwl_priv *priv, | |
ccc038ab | 1128 | struct ieee80211_key_conf *keyconf, u8 sta_id) |
7480513f EG |
1129 | { |
1130 | int ret; | |
1131 | ||
ccc038ab EG |
1132 | priv->key_mapping_key++; |
1133 | keyconf->hw_key_idx = HW_KEY_DYNAMIC; | |
7480513f | 1134 | |
ccc038ab | 1135 | switch (keyconf->alg) { |
7480513f | 1136 | case ALG_CCMP: |
ccc038ab | 1137 | ret = iwl_set_ccmp_dynamic_key_info(priv, keyconf, sta_id); |
7480513f EG |
1138 | break; |
1139 | case ALG_TKIP: | |
ccc038ab | 1140 | ret = iwl_set_tkip_dynamic_key_info(priv, keyconf, sta_id); |
7480513f EG |
1141 | break; |
1142 | case ALG_WEP: | |
ccc038ab | 1143 | ret = iwl_set_wep_dynamic_key_info(priv, keyconf, sta_id); |
7480513f EG |
1144 | break; |
1145 | default: | |
15b1687c WT |
1146 | IWL_ERR(priv, |
1147 | "Unknown alg: %s alg = %d\n", __func__, keyconf->alg); | |
7480513f EG |
1148 | ret = -EINVAL; |
1149 | } | |
1150 | ||
e1623446 | 1151 | IWL_DEBUG_WEP(priv, "Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d\n", |
4564ce8b EG |
1152 | keyconf->alg, keyconf->keylen, keyconf->keyidx, |
1153 | sta_id, ret); | |
1154 | ||
7480513f EG |
1155 | return ret; |
1156 | } | |
27aaba0c | 1157 | EXPORT_SYMBOL(iwl_set_dynamic_key); |
7480513f | 1158 | |
66c73db7 TW |
1159 | #ifdef CONFIG_IWLWIFI_DEBUG |
1160 | static void iwl_dump_lq_cmd(struct iwl_priv *priv, | |
1161 | struct iwl_link_quality_cmd *lq) | |
1162 | { | |
1163 | int i; | |
e1623446 TW |
1164 | IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); |
1165 | IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", | |
66c73db7 TW |
1166 | lq->general_params.single_stream_ant_msk, |
1167 | lq->general_params.dual_stream_ant_msk); | |
1168 | ||
1169 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) | |
e1623446 | 1170 | IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", |
66c73db7 TW |
1171 | i, lq->rs_table[i].rate_n_flags); |
1172 | } | |
1173 | #else | |
1174 | static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, | |
1175 | struct iwl_link_quality_cmd *lq) | |
1176 | { | |
1177 | } | |
1178 | #endif | |
1179 | ||
fe6b23dd RC |
1180 | /** |
1181 | * iwl_send_lq_cmd() - Send link quality command | |
1182 | * @init: This command is sent as part of station initialization right | |
1183 | * after station has been added. | |
1184 | * | |
1185 | * The link quality command is sent as the last step of station creation. | |
1186 | * This is the special case in which init is set and we call a callback in | |
1187 | * this case to clear the state indicating that station creation is in | |
1188 | * progress. | |
1189 | */ | |
66c73db7 | 1190 | int iwl_send_lq_cmd(struct iwl_priv *priv, |
fe6b23dd | 1191 | struct iwl_link_quality_cmd *lq, u8 flags, bool init) |
66c73db7 | 1192 | { |
fe6b23dd RC |
1193 | int ret = 0; |
1194 | unsigned long flags_spin; | |
1195 | ||
66c73db7 TW |
1196 | struct iwl_host_cmd cmd = { |
1197 | .id = REPLY_TX_LINK_QUALITY_CMD, | |
1198 | .len = sizeof(struct iwl_link_quality_cmd), | |
c2acea8e | 1199 | .flags = flags, |
66c73db7 TW |
1200 | .data = lq, |
1201 | }; | |
1202 | ||
76c3c698 | 1203 | if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) |
66c73db7 TW |
1204 | return -EINVAL; |
1205 | ||
3ac7f146 | 1206 | iwl_dump_lq_cmd(priv, lq); |
fe6b23dd | 1207 | BUG_ON(init && (cmd.flags & CMD_ASYNC)); |
66c73db7 | 1208 | |
fe6b23dd RC |
1209 | ret = iwl_send_cmd(priv, &cmd); |
1210 | if (ret || (cmd.flags & CMD_ASYNC)) | |
1211 | return ret; | |
66c73db7 | 1212 | |
fe6b23dd | 1213 | if (init) { |
91dd6c27 | 1214 | IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d\n", |
fe6b23dd RC |
1215 | lq->sta_id); |
1216 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
1217 | priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; | |
1218 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
1219 | } | |
66c73db7 TW |
1220 | return 0; |
1221 | } | |
1222 | EXPORT_SYMBOL(iwl_send_lq_cmd); | |
1223 | ||
9a9ca65f RC |
1224 | /** |
1225 | * iwl_add_bcast_station - add broadcast station into station table. | |
1226 | */ | |
1227 | void iwl_add_bcast_station(struct iwl_priv *priv) | |
1228 | { | |
3459ab5a | 1229 | IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); |
fe6b23dd | 1230 | iwl_add_local_station(priv, iwl_bcast_addr, true); |
9a9ca65f RC |
1231 | } |
1232 | EXPORT_SYMBOL(iwl_add_bcast_station); | |
1233 | ||
3459ab5a RC |
1234 | /** |
1235 | * iwl3945_add_bcast_station - add broadcast station into station table. | |
1236 | */ | |
1237 | void iwl3945_add_bcast_station(struct iwl_priv *priv) | |
1238 | { | |
1239 | IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); | |
fe6b23dd RC |
1240 | iwl_add_local_station(priv, iwl_bcast_addr, false); |
1241 | /* | |
1242 | * It is assumed that when station is added more initialization | |
1243 | * needs to be done, but for 3945 it is not the case and we can | |
1244 | * just release station table access right here. | |
1245 | */ | |
1246 | priv->stations[priv->hw_params.bcast_sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; | |
1247 | ||
3459ab5a RC |
1248 | } |
1249 | EXPORT_SYMBOL(iwl3945_add_bcast_station); | |
1250 | ||
4f40e4d9 TW |
1251 | /** |
1252 | * iwl_get_sta_id - Find station's index within station table | |
1253 | * | |
1254 | * If new IBSS station, create new entry in station table | |
1255 | */ | |
1256 | int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) | |
1257 | { | |
1258 | int sta_id; | |
943ab70f | 1259 | __le16 fc = hdr->frame_control; |
4f40e4d9 TW |
1260 | |
1261 | /* If this frame is broadcast or management, use broadcast station id */ | |
943ab70f | 1262 | if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) |
4f40e4d9 TW |
1263 | return priv->hw_params.bcast_sta_id; |
1264 | ||
1265 | switch (priv->iw_mode) { | |
1266 | ||
1267 | /* If we are a client station in a BSS network, use the special | |
1268 | * AP station entry (that's the only station we communicate with) */ | |
05c914fe | 1269 | case NL80211_IFTYPE_STATION: |
fe6b23dd RC |
1270 | /* |
1271 | * If addition of station not complete yet, which means | |
1272 | * that rate scaling has not been initialized, then return | |
1273 | * the broadcast station. | |
1274 | */ | |
1275 | if (!(priv->stations[IWL_AP_ID].used & IWL_STA_UCODE_ACTIVE)) | |
1276 | return priv->hw_params.bcast_sta_id; | |
4f40e4d9 TW |
1277 | return IWL_AP_ID; |
1278 | ||
1279 | /* If we are an AP, then find the station, or use BCAST */ | |
05c914fe | 1280 | case NL80211_IFTYPE_AP: |
c587de0b | 1281 | sta_id = iwl_find_station(priv, hdr->addr1); |
4f40e4d9 TW |
1282 | if (sta_id != IWL_INVALID_STATION) |
1283 | return sta_id; | |
1284 | return priv->hw_params.bcast_sta_id; | |
1285 | ||
1286 | /* If this frame is going out to an IBSS network, find the station, | |
1287 | * or create a new station table entry */ | |
05c914fe | 1288 | case NL80211_IFTYPE_ADHOC: |
c587de0b | 1289 | sta_id = iwl_find_station(priv, hdr->addr1); |
4f40e4d9 TW |
1290 | if (sta_id != IWL_INVALID_STATION) |
1291 | return sta_id; | |
1292 | ||
e1623446 | 1293 | IWL_DEBUG_DROP(priv, "Station %pM not in station map. " |
4f40e4d9 | 1294 | "Defaulting to broadcast...\n", |
e174961c | 1295 | hdr->addr1); |
3d816c77 | 1296 | iwl_print_hex_dump(priv, IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr)); |
4f40e4d9 TW |
1297 | return priv->hw_params.bcast_sta_id; |
1298 | ||
1299 | default: | |
39aadf8c WT |
1300 | IWL_WARN(priv, "Unknown mode of operation: %d\n", |
1301 | priv->iw_mode); | |
4f40e4d9 TW |
1302 | return priv->hw_params.bcast_sta_id; |
1303 | } | |
1304 | } | |
1305 | EXPORT_SYMBOL(iwl_get_sta_id); | |
1306 | ||
5083e563 | 1307 | /** |
9f58671e | 1308 | * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table |
5083e563 | 1309 | */ |
9f58671e | 1310 | void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) |
5083e563 TW |
1311 | { |
1312 | unsigned long flags; | |
1313 | ||
1314 | /* Remove "disable" flag, to enable Tx for this TID */ | |
1315 | spin_lock_irqsave(&priv->sta_lock, flags); | |
1316 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; | |
1317 | priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); | |
1318 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
1319 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1320 | ||
1321 | iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); | |
1322 | } | |
9f58671e TW |
1323 | EXPORT_SYMBOL(iwl_sta_tx_modify_enable_tid); |
1324 | ||
1325 | int iwl_sta_rx_agg_start(struct iwl_priv *priv, | |
1326 | const u8 *addr, int tid, u16 ssn) | |
1327 | { | |
1328 | unsigned long flags; | |
1329 | int sta_id; | |
1330 | ||
c587de0b | 1331 | sta_id = iwl_find_station(priv, addr); |
9f58671e TW |
1332 | if (sta_id == IWL_INVALID_STATION) |
1333 | return -ENXIO; | |
1334 | ||
1335 | spin_lock_irqsave(&priv->sta_lock, flags); | |
1336 | priv->stations[sta_id].sta.station_flags_msk = 0; | |
1337 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; | |
1338 | priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; | |
1339 | priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); | |
1340 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
1341 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1342 | ||
1343 | return iwl_send_add_sta(priv, &priv->stations[sta_id].sta, | |
1344 | CMD_ASYNC); | |
1345 | } | |
1346 | EXPORT_SYMBOL(iwl_sta_rx_agg_start); | |
1347 | ||
1348 | int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid) | |
1349 | { | |
1350 | unsigned long flags; | |
1351 | int sta_id; | |
1352 | ||
c587de0b | 1353 | sta_id = iwl_find_station(priv, addr); |
a2f1cbeb WYG |
1354 | if (sta_id == IWL_INVALID_STATION) { |
1355 | IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); | |
9f58671e | 1356 | return -ENXIO; |
a2f1cbeb | 1357 | } |
9f58671e TW |
1358 | |
1359 | spin_lock_irqsave(&priv->sta_lock, flags); | |
1360 | priv->stations[sta_id].sta.station_flags_msk = 0; | |
1361 | priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; | |
1362 | priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; | |
1363 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
1364 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1365 | ||
1366 | return iwl_send_add_sta(priv, &priv->stations[sta_id].sta, | |
1367 | CMD_ASYNC); | |
1368 | } | |
1369 | EXPORT_SYMBOL(iwl_sta_rx_agg_stop); | |
1370 | ||
6ab10ff8 | 1371 | void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) |
9f58671e TW |
1372 | { |
1373 | unsigned long flags; | |
1374 | ||
1375 | spin_lock_irqsave(&priv->sta_lock, flags); | |
1376 | priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; | |
1377 | priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; | |
1378 | priv->stations[sta_id].sta.sta.modify_mask = 0; | |
6ab10ff8 | 1379 | priv->stations[sta_id].sta.sleep_tx_count = 0; |
9f58671e TW |
1380 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; |
1381 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
1382 | ||
1383 | iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); | |
1384 | } | |
6ab10ff8 | 1385 | EXPORT_SYMBOL(iwl_sta_modify_ps_wake); |
9f58671e | 1386 | |
6ab10ff8 | 1387 | void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) |
9f58671e | 1388 | { |
6ab10ff8 | 1389 | unsigned long flags; |
9f58671e | 1390 | |
6ab10ff8 JB |
1391 | spin_lock_irqsave(&priv->sta_lock, flags); |
1392 | priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; | |
1393 | priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; | |
1394 | priv->stations[sta_id].sta.sta.modify_mask = | |
1395 | STA_MODIFY_SLEEP_TX_COUNT_MSK; | |
1396 | priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); | |
1397 | priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | |
1398 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
9f58671e | 1399 | |
6ab10ff8 | 1400 | iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); |
9f58671e | 1401 | } |
74bcdb33 | 1402 | EXPORT_SYMBOL(iwl_sta_modify_sleep_tx_count); |
fe6b23dd RC |
1403 | |
1404 | int iwl_mac_sta_remove(struct ieee80211_hw *hw, | |
1405 | struct ieee80211_vif *vif, | |
1406 | struct ieee80211_sta *sta) | |
1407 | { | |
1408 | int ret; | |
1409 | struct iwl_priv *priv = hw->priv; | |
1410 | IWL_DEBUG_INFO(priv, "received request to remove station %pM\n", | |
1411 | sta->addr); | |
1412 | ret = iwl_remove_station(priv, sta); | |
1413 | if (ret) | |
1414 | IWL_ERR(priv, "Error removing station %pM\n", | |
1415 | sta->addr); | |
1416 | return ret; | |
1417 | } | |
1418 | EXPORT_SYMBOL(iwl_mac_sta_remove); |