Commit | Line | Data |
---|---|---|
be663ab6 WYG |
1 | /****************************************************************************** |
2 | * | |
3 | * Copyright(c) 2003 - 2011 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 | * Intel Linux Wireless <ilw@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 | #include <linux/etherdevice.h> | |
32 | #include <linux/sched.h> | |
33 | #include <linux/lockdep.h> | |
34 | ||
35 | #include "iwl-dev.h" | |
36 | #include "iwl-core.h" | |
37 | #include "iwl-sta.h" | |
38 | ||
39 | /* priv->sta_lock must be held */ | |
e2ebc833 | 40 | static void il_sta_ucode_activate(struct il_priv *priv, u8 sta_id) |
be663ab6 WYG |
41 | { |
42 | ||
e2ebc833 SG |
43 | if (!(priv->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) |
44 | IL_ERR(priv, | |
be663ab6 WYG |
45 | "ACTIVATE a non DRIVER active station id %u addr %pM\n", |
46 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
47 | ||
e2ebc833 SG |
48 | if (priv->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { |
49 | IL_DEBUG_ASSOC(priv, | |
be663ab6 WYG |
50 | "STA id %u addr %pM already present" |
51 | " in uCode (according to driver)\n", | |
52 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
53 | } else { | |
e2ebc833 SG |
54 | priv->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; |
55 | IL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", | |
be663ab6 WYG |
56 | sta_id, priv->stations[sta_id].sta.sta.addr); |
57 | } | |
58 | } | |
59 | ||
e2ebc833 SG |
60 | static int il_process_add_sta_resp(struct il_priv *priv, |
61 | struct il_addsta_cmd *addsta, | |
62 | struct il_rx_packet *pkt, | |
be663ab6 WYG |
63 | bool sync) |
64 | { | |
65 | u8 sta_id = addsta->sta.sta_id; | |
66 | unsigned long flags; | |
67 | int ret = -EIO; | |
68 | ||
e2ebc833 SG |
69 | if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { |
70 | IL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", | |
be663ab6 WYG |
71 | pkt->hdr.flags); |
72 | return ret; | |
73 | } | |
74 | ||
e2ebc833 | 75 | IL_DEBUG_INFO(priv, "Processing response for adding station %u\n", |
be663ab6 WYG |
76 | sta_id); |
77 | ||
78 | spin_lock_irqsave(&priv->sta_lock, flags); | |
79 | ||
80 | switch (pkt->u.add_sta.status) { | |
81 | case ADD_STA_SUCCESS_MSK: | |
e2ebc833 SG |
82 | IL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); |
83 | il_sta_ucode_activate(priv, sta_id); | |
be663ab6 WYG |
84 | ret = 0; |
85 | break; | |
86 | case ADD_STA_NO_ROOM_IN_TABLE: | |
e2ebc833 | 87 | IL_ERR(priv, "Adding station %d failed, no room in table.\n", |
be663ab6 WYG |
88 | sta_id); |
89 | break; | |
90 | case ADD_STA_NO_BLOCK_ACK_RESOURCE: | |
e2ebc833 | 91 | IL_ERR(priv, |
be663ab6 WYG |
92 | "Adding station %d failed, no block ack resource.\n", |
93 | sta_id); | |
94 | break; | |
95 | case ADD_STA_MODIFY_NON_EXIST_STA: | |
e2ebc833 | 96 | IL_ERR(priv, "Attempting to modify non-existing station %d\n", |
be663ab6 WYG |
97 | sta_id); |
98 | break; | |
99 | default: | |
e2ebc833 | 100 | IL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", |
be663ab6 WYG |
101 | pkt->u.add_sta.status); |
102 | break; | |
103 | } | |
104 | ||
e2ebc833 | 105 | IL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", |
be663ab6 WYG |
106 | priv->stations[sta_id].sta.mode == |
107 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", | |
108 | sta_id, priv->stations[sta_id].sta.sta.addr); | |
109 | ||
110 | /* | |
111 | * XXX: The MAC address in the command buffer is often changed from | |
112 | * the original sent to the device. That is, the MAC address | |
25985edc | 113 | * written to the command buffer often is not the same MAC address |
be663ab6 WYG |
114 | * read from the command buffer when the command returns. This |
115 | * issue has not yet been resolved and this debugging is left to | |
116 | * observe the problem. | |
117 | */ | |
e2ebc833 | 118 | IL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", |
be663ab6 WYG |
119 | priv->stations[sta_id].sta.mode == |
120 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", | |
121 | addsta->sta.addr); | |
122 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
123 | ||
124 | return ret; | |
125 | } | |
126 | ||
e2ebc833 SG |
127 | static void il_add_sta_callback(struct il_priv *priv, |
128 | struct il_device_cmd *cmd, | |
129 | struct il_rx_packet *pkt) | |
be663ab6 | 130 | { |
e2ebc833 SG |
131 | struct il_addsta_cmd *addsta = |
132 | (struct il_addsta_cmd *)cmd->cmd.payload; | |
be663ab6 | 133 | |
e2ebc833 | 134 | il_process_add_sta_resp(priv, addsta, pkt, false); |
be663ab6 WYG |
135 | |
136 | } | |
137 | ||
e2ebc833 SG |
138 | int il_send_add_sta(struct il_priv *priv, |
139 | struct il_addsta_cmd *sta, u8 flags) | |
be663ab6 | 140 | { |
e2ebc833 | 141 | struct il_rx_packet *pkt = NULL; |
be663ab6 WYG |
142 | int ret = 0; |
143 | u8 data[sizeof(*sta)]; | |
e2ebc833 | 144 | struct il_host_cmd cmd = { |
be663ab6 WYG |
145 | .id = REPLY_ADD_STA, |
146 | .flags = flags, | |
147 | .data = data, | |
148 | }; | |
149 | u8 sta_id __maybe_unused = sta->sta.sta_id; | |
150 | ||
e2ebc833 | 151 | IL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", |
be663ab6 WYG |
152 | sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); |
153 | ||
154 | if (flags & CMD_ASYNC) | |
e2ebc833 | 155 | cmd.callback = il_add_sta_callback; |
be663ab6 WYG |
156 | else { |
157 | cmd.flags |= CMD_WANT_SKB; | |
158 | might_sleep(); | |
159 | } | |
160 | ||
161 | cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); | |
e2ebc833 | 162 | ret = il_send_cmd(priv, &cmd); |
be663ab6 WYG |
163 | |
164 | if (ret || (flags & CMD_ASYNC)) | |
165 | return ret; | |
166 | ||
167 | if (ret == 0) { | |
e2ebc833 SG |
168 | pkt = (struct il_rx_packet *)cmd.reply_page; |
169 | ret = il_process_add_sta_resp(priv, sta, pkt, true); | |
be663ab6 | 170 | } |
e2ebc833 | 171 | il_free_pages(priv, cmd.reply_page); |
be663ab6 WYG |
172 | |
173 | return ret; | |
174 | } | |
e2ebc833 | 175 | EXPORT_SYMBOL(il_send_add_sta); |
be663ab6 | 176 | |
e2ebc833 | 177 | static void il_set_ht_add_station(struct il_priv *priv, u8 index, |
be663ab6 | 178 | struct ieee80211_sta *sta, |
e2ebc833 | 179 | struct il_rxon_context *ctx) |
be663ab6 WYG |
180 | { |
181 | struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; | |
182 | __le32 sta_flags; | |
183 | u8 mimo_ps_mode; | |
184 | ||
185 | if (!sta || !sta_ht_inf->ht_supported) | |
186 | goto done; | |
187 | ||
188 | mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; | |
e2ebc833 | 189 | IL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n", |
be663ab6 WYG |
190 | (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? |
191 | "static" : | |
192 | (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? | |
193 | "dynamic" : "disabled"); | |
194 | ||
195 | sta_flags = priv->stations[index].sta.station_flags; | |
196 | ||
197 | sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); | |
198 | ||
199 | switch (mimo_ps_mode) { | |
200 | case WLAN_HT_CAP_SM_PS_STATIC: | |
201 | sta_flags |= STA_FLG_MIMO_DIS_MSK; | |
202 | break; | |
203 | case WLAN_HT_CAP_SM_PS_DYNAMIC: | |
204 | sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; | |
205 | break; | |
206 | case WLAN_HT_CAP_SM_PS_DISABLED: | |
207 | break; | |
208 | default: | |
e2ebc833 | 209 | IL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); |
be663ab6 WYG |
210 | break; |
211 | } | |
212 | ||
213 | sta_flags |= cpu_to_le32( | |
214 | (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); | |
215 | ||
216 | sta_flags |= cpu_to_le32( | |
217 | (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); | |
218 | ||
e2ebc833 | 219 | if (il_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) |
be663ab6 WYG |
220 | sta_flags |= STA_FLG_HT40_EN_MSK; |
221 | else | |
222 | sta_flags &= ~STA_FLG_HT40_EN_MSK; | |
223 | ||
224 | priv->stations[index].sta.station_flags = sta_flags; | |
225 | done: | |
226 | return; | |
227 | } | |
228 | ||
229 | /** | |
e2ebc833 | 230 | * il_prep_station - Prepare station information for addition |
be663ab6 WYG |
231 | * |
232 | * should be called with sta_lock held | |
233 | */ | |
e2ebc833 | 234 | u8 il_prep_station(struct il_priv *priv, struct il_rxon_context *ctx, |
be663ab6 WYG |
235 | const u8 *addr, bool is_ap, struct ieee80211_sta *sta) |
236 | { | |
e2ebc833 | 237 | struct il_station_entry *station; |
be663ab6 | 238 | int i; |
e2ebc833 | 239 | u8 sta_id = IL_INVALID_STATION; |
be663ab6 WYG |
240 | u16 rate; |
241 | ||
242 | if (is_ap) | |
243 | sta_id = ctx->ap_sta_id; | |
244 | else if (is_broadcast_ether_addr(addr)) | |
245 | sta_id = ctx->bcast_sta_id; | |
246 | else | |
e2ebc833 | 247 | for (i = IL_STA_ID; i < priv->hw_params.max_stations; i++) { |
be663ab6 WYG |
248 | if (!compare_ether_addr(priv->stations[i].sta.sta.addr, |
249 | addr)) { | |
250 | sta_id = i; | |
251 | break; | |
252 | } | |
253 | ||
254 | if (!priv->stations[i].used && | |
e2ebc833 | 255 | sta_id == IL_INVALID_STATION) |
be663ab6 WYG |
256 | sta_id = i; |
257 | } | |
258 | ||
259 | /* | |
260 | * These two conditions have the same outcome, but keep them | |
261 | * separate | |
262 | */ | |
e2ebc833 | 263 | if (unlikely(sta_id == IL_INVALID_STATION)) |
be663ab6 WYG |
264 | return sta_id; |
265 | ||
266 | /* | |
267 | * uCode is not able to deal with multiple requests to add a | |
268 | * station. Keep track if one is in progress so that we do not send | |
269 | * another. | |
270 | */ | |
e2ebc833 SG |
271 | if (priv->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { |
272 | IL_DEBUG_INFO(priv, | |
be663ab6 WYG |
273 | "STA %d already in process of being added.\n", |
274 | sta_id); | |
275 | return sta_id; | |
276 | } | |
277 | ||
e2ebc833 SG |
278 | if ((priv->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && |
279 | (priv->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && | |
be663ab6 | 280 | !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) { |
e2ebc833 | 281 | IL_DEBUG_ASSOC(priv, |
be663ab6 WYG |
282 | "STA %d (%pM) already added, not adding again.\n", |
283 | sta_id, addr); | |
284 | return sta_id; | |
285 | } | |
286 | ||
287 | station = &priv->stations[sta_id]; | |
e2ebc833 SG |
288 | station->used = IL_STA_DRIVER_ACTIVE; |
289 | IL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", | |
be663ab6 WYG |
290 | sta_id, addr); |
291 | priv->num_stations++; | |
292 | ||
293 | /* Set up the REPLY_ADD_STA command to send to device */ | |
e2ebc833 | 294 | memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); |
be663ab6 WYG |
295 | memcpy(station->sta.sta.addr, addr, ETH_ALEN); |
296 | station->sta.mode = 0; | |
297 | station->sta.sta.sta_id = sta_id; | |
298 | station->sta.station_flags = ctx->station_flags; | |
299 | station->ctxid = ctx->ctxid; | |
300 | ||
301 | if (sta) { | |
e2ebc833 | 302 | struct il_station_priv_common *sta_priv; |
be663ab6 WYG |
303 | |
304 | sta_priv = (void *)sta->drv_priv; | |
305 | sta_priv->ctx = ctx; | |
306 | } | |
307 | ||
308 | /* | |
309 | * OK to call unconditionally, since local stations (IBSS BSSID | |
310 | * STA and broadcast STA) pass in a NULL sta, and mac80211 | |
311 | * doesn't allow HT IBSS. | |
312 | */ | |
e2ebc833 | 313 | il_set_ht_add_station(priv, sta_id, sta, ctx); |
be663ab6 WYG |
314 | |
315 | /* 3945 only */ | |
316 | rate = (priv->band == IEEE80211_BAND_5GHZ) ? | |
e2ebc833 | 317 | IL_RATE_6M_PLCP : IL_RATE_1M_PLCP; |
be663ab6 WYG |
318 | /* Turn on both antennas for the station... */ |
319 | station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); | |
320 | ||
321 | return sta_id; | |
322 | ||
323 | } | |
e2ebc833 | 324 | EXPORT_SYMBOL_GPL(il_prep_station); |
be663ab6 WYG |
325 | |
326 | #define STA_WAIT_TIMEOUT (HZ/2) | |
327 | ||
328 | /** | |
e2ebc833 | 329 | * il_add_station_common - |
be663ab6 WYG |
330 | */ |
331 | int | |
e2ebc833 SG |
332 | il_add_station_common(struct il_priv *priv, |
333 | struct il_rxon_context *ctx, | |
be663ab6 WYG |
334 | const u8 *addr, bool is_ap, |
335 | struct ieee80211_sta *sta, u8 *sta_id_r) | |
336 | { | |
337 | unsigned long flags_spin; | |
338 | int ret = 0; | |
339 | u8 sta_id; | |
e2ebc833 | 340 | struct il_addsta_cmd sta_cmd; |
be663ab6 WYG |
341 | |
342 | *sta_id_r = 0; | |
343 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 SG |
344 | sta_id = il_prep_station(priv, ctx, addr, is_ap, sta); |
345 | if (sta_id == IL_INVALID_STATION) { | |
346 | IL_ERR(priv, "Unable to prepare station %pM for addition\n", | |
be663ab6 WYG |
347 | addr); |
348 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
349 | return -EINVAL; | |
350 | } | |
351 | ||
352 | /* | |
353 | * uCode is not able to deal with multiple requests to add a | |
354 | * station. Keep track if one is in progress so that we do not send | |
355 | * another. | |
356 | */ | |
e2ebc833 SG |
357 | if (priv->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { |
358 | IL_DEBUG_INFO(priv, | |
be663ab6 WYG |
359 | "STA %d already in process of being added.\n", |
360 | sta_id); | |
361 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
362 | return -EEXIST; | |
363 | } | |
364 | ||
e2ebc833 SG |
365 | if ((priv->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && |
366 | (priv->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { | |
367 | IL_DEBUG_ASSOC(priv, | |
be663ab6 WYG |
368 | "STA %d (%pM) already added, not adding again.\n", |
369 | sta_id, addr); | |
370 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
371 | return -EEXIST; | |
372 | } | |
373 | ||
e2ebc833 | 374 | priv->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; |
be663ab6 | 375 | memcpy(&sta_cmd, &priv->stations[sta_id].sta, |
e2ebc833 | 376 | sizeof(struct il_addsta_cmd)); |
be663ab6 WYG |
377 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
378 | ||
379 | /* Add station to device's station table */ | |
e2ebc833 | 380 | ret = il_send_add_sta(priv, &sta_cmd, CMD_SYNC); |
be663ab6 WYG |
381 | if (ret) { |
382 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 383 | IL_ERR(priv, "Adding station %pM failed.\n", |
be663ab6 | 384 | priv->stations[sta_id].sta.sta.addr); |
e2ebc833 SG |
385 | priv->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; |
386 | priv->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; | |
be663ab6 WYG |
387 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
388 | } | |
389 | *sta_id_r = sta_id; | |
390 | return ret; | |
391 | } | |
e2ebc833 | 392 | EXPORT_SYMBOL(il_add_station_common); |
be663ab6 WYG |
393 | |
394 | /** | |
e2ebc833 | 395 | * il_sta_ucode_deactivate - deactivate ucode status for a station |
be663ab6 WYG |
396 | * |
397 | * priv->sta_lock must be held | |
398 | */ | |
e2ebc833 | 399 | static void il_sta_ucode_deactivate(struct il_priv *priv, u8 sta_id) |
be663ab6 WYG |
400 | { |
401 | /* Ucode must be active and driver must be non active */ | |
402 | if ((priv->stations[sta_id].used & | |
e2ebc833 SG |
403 | (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != |
404 | IL_STA_UCODE_ACTIVE) | |
405 | IL_ERR(priv, "removed non active STA %u\n", sta_id); | |
be663ab6 | 406 | |
e2ebc833 | 407 | priv->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; |
be663ab6 | 408 | |
e2ebc833 SG |
409 | memset(&priv->stations[sta_id], 0, sizeof(struct il_station_entry)); |
410 | IL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); | |
be663ab6 WYG |
411 | } |
412 | ||
e2ebc833 | 413 | static int il_send_remove_station(struct il_priv *priv, |
be663ab6 WYG |
414 | const u8 *addr, int sta_id, |
415 | bool temporary) | |
416 | { | |
e2ebc833 | 417 | struct il_rx_packet *pkt; |
be663ab6 WYG |
418 | int ret; |
419 | ||
420 | unsigned long flags_spin; | |
e2ebc833 | 421 | struct il_rem_sta_cmd rm_sta_cmd; |
be663ab6 | 422 | |
e2ebc833 | 423 | struct il_host_cmd cmd = { |
be663ab6 | 424 | .id = REPLY_REMOVE_STA, |
e2ebc833 | 425 | .len = sizeof(struct il_rem_sta_cmd), |
be663ab6 WYG |
426 | .flags = CMD_SYNC, |
427 | .data = &rm_sta_cmd, | |
428 | }; | |
429 | ||
430 | memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); | |
431 | rm_sta_cmd.num_sta = 1; | |
432 | memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); | |
433 | ||
434 | cmd.flags |= CMD_WANT_SKB; | |
435 | ||
e2ebc833 | 436 | ret = il_send_cmd(priv, &cmd); |
be663ab6 WYG |
437 | |
438 | if (ret) | |
439 | return ret; | |
440 | ||
e2ebc833 SG |
441 | pkt = (struct il_rx_packet *)cmd.reply_page; |
442 | if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { | |
443 | IL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", | |
be663ab6 WYG |
444 | pkt->hdr.flags); |
445 | ret = -EIO; | |
446 | } | |
447 | ||
448 | if (!ret) { | |
449 | switch (pkt->u.rem_sta.status) { | |
450 | case REM_STA_SUCCESS_MSK: | |
451 | if (!temporary) { | |
452 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 453 | il_sta_ucode_deactivate(priv, sta_id); |
be663ab6 WYG |
454 | spin_unlock_irqrestore(&priv->sta_lock, |
455 | flags_spin); | |
456 | } | |
e2ebc833 | 457 | IL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); |
be663ab6 WYG |
458 | break; |
459 | default: | |
460 | ret = -EIO; | |
e2ebc833 | 461 | IL_ERR(priv, "REPLY_REMOVE_STA failed\n"); |
be663ab6 WYG |
462 | break; |
463 | } | |
464 | } | |
e2ebc833 | 465 | il_free_pages(priv, cmd.reply_page); |
be663ab6 WYG |
466 | |
467 | return ret; | |
468 | } | |
469 | ||
470 | /** | |
e2ebc833 | 471 | * il_remove_station - Remove driver's knowledge of station. |
be663ab6 | 472 | */ |
e2ebc833 | 473 | int il_remove_station(struct il_priv *priv, const u8 sta_id, |
be663ab6 WYG |
474 | const u8 *addr) |
475 | { | |
476 | unsigned long flags; | |
477 | ||
e2ebc833 SG |
478 | if (!il_is_ready(priv)) { |
479 | IL_DEBUG_INFO(priv, | |
be663ab6 WYG |
480 | "Unable to remove station %pM, device not ready.\n", |
481 | addr); | |
482 | /* | |
483 | * It is typical for stations to be removed when we are | |
484 | * going down. Return success since device will be down | |
485 | * soon anyway | |
486 | */ | |
487 | return 0; | |
488 | } | |
489 | ||
e2ebc833 | 490 | IL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", |
be663ab6 WYG |
491 | sta_id, addr); |
492 | ||
e2ebc833 | 493 | if (WARN_ON(sta_id == IL_INVALID_STATION)) |
be663ab6 WYG |
494 | return -EINVAL; |
495 | ||
496 | spin_lock_irqsave(&priv->sta_lock, flags); | |
497 | ||
e2ebc833 SG |
498 | if (!(priv->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { |
499 | IL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", | |
be663ab6 WYG |
500 | addr); |
501 | goto out_err; | |
502 | } | |
503 | ||
e2ebc833 SG |
504 | if (!(priv->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { |
505 | IL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", | |
be663ab6 WYG |
506 | addr); |
507 | goto out_err; | |
508 | } | |
509 | ||
e2ebc833 | 510 | if (priv->stations[sta_id].used & IL_STA_LOCAL) { |
be663ab6 WYG |
511 | kfree(priv->stations[sta_id].lq); |
512 | priv->stations[sta_id].lq = NULL; | |
513 | } | |
514 | ||
e2ebc833 | 515 | priv->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; |
be663ab6 WYG |
516 | |
517 | priv->num_stations--; | |
518 | ||
519 | BUG_ON(priv->num_stations < 0); | |
520 | ||
521 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
522 | ||
e2ebc833 | 523 | return il_send_remove_station(priv, addr, sta_id, false); |
be663ab6 WYG |
524 | out_err: |
525 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
526 | return -EINVAL; | |
527 | } | |
e2ebc833 | 528 | EXPORT_SYMBOL_GPL(il_remove_station); |
be663ab6 WYG |
529 | |
530 | /** | |
e2ebc833 | 531 | * il_clear_ucode_stations - clear ucode station table bits |
be663ab6 WYG |
532 | * |
533 | * This function clears all the bits in the driver indicating | |
534 | * which stations are active in the ucode. Call when something | |
535 | * other than explicit station management would cause this in | |
536 | * the ucode, e.g. unassociated RXON. | |
537 | */ | |
e2ebc833 SG |
538 | void il_clear_ucode_stations(struct il_priv *priv, |
539 | struct il_rxon_context *ctx) | |
be663ab6 WYG |
540 | { |
541 | int i; | |
542 | unsigned long flags_spin; | |
543 | bool cleared = false; | |
544 | ||
e2ebc833 | 545 | IL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); |
be663ab6 WYG |
546 | |
547 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
548 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
549 | if (ctx && ctx->ctxid != priv->stations[i].ctxid) | |
550 | continue; | |
551 | ||
e2ebc833 SG |
552 | if (priv->stations[i].used & IL_STA_UCODE_ACTIVE) { |
553 | IL_DEBUG_INFO(priv, | |
be663ab6 | 554 | "Clearing ucode active for station %d\n", i); |
e2ebc833 | 555 | priv->stations[i].used &= ~IL_STA_UCODE_ACTIVE; |
be663ab6 WYG |
556 | cleared = true; |
557 | } | |
558 | } | |
559 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
560 | ||
561 | if (!cleared) | |
e2ebc833 | 562 | IL_DEBUG_INFO(priv, |
be663ab6 WYG |
563 | "No active stations found to be cleared\n"); |
564 | } | |
e2ebc833 | 565 | EXPORT_SYMBOL(il_clear_ucode_stations); |
be663ab6 WYG |
566 | |
567 | /** | |
e2ebc833 | 568 | * il_restore_stations() - Restore driver known stations to device |
be663ab6 WYG |
569 | * |
570 | * All stations considered active by driver, but not present in ucode, is | |
571 | * restored. | |
572 | * | |
573 | * Function sleeps. | |
574 | */ | |
575 | void | |
e2ebc833 | 576 | il_restore_stations(struct il_priv *priv, struct il_rxon_context *ctx) |
be663ab6 | 577 | { |
e2ebc833 SG |
578 | struct il_addsta_cmd sta_cmd; |
579 | struct il_link_quality_cmd lq; | |
be663ab6 WYG |
580 | unsigned long flags_spin; |
581 | int i; | |
582 | bool found = false; | |
583 | int ret; | |
584 | bool send_lq; | |
585 | ||
e2ebc833 SG |
586 | if (!il_is_ready(priv)) { |
587 | IL_DEBUG_INFO(priv, | |
be663ab6 WYG |
588 | "Not ready yet, not restoring any stations.\n"); |
589 | return; | |
590 | } | |
591 | ||
e2ebc833 | 592 | IL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); |
be663ab6 WYG |
593 | spin_lock_irqsave(&priv->sta_lock, flags_spin); |
594 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
595 | if (ctx->ctxid != priv->stations[i].ctxid) | |
596 | continue; | |
e2ebc833 SG |
597 | if ((priv->stations[i].used & IL_STA_DRIVER_ACTIVE) && |
598 | !(priv->stations[i].used & IL_STA_UCODE_ACTIVE)) { | |
599 | IL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", | |
be663ab6 WYG |
600 | priv->stations[i].sta.sta.addr); |
601 | priv->stations[i].sta.mode = 0; | |
e2ebc833 | 602 | priv->stations[i].used |= IL_STA_UCODE_INPROGRESS; |
be663ab6 WYG |
603 | found = true; |
604 | } | |
605 | } | |
606 | ||
607 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
e2ebc833 | 608 | if ((priv->stations[i].used & IL_STA_UCODE_INPROGRESS)) { |
be663ab6 | 609 | memcpy(&sta_cmd, &priv->stations[i].sta, |
e2ebc833 | 610 | sizeof(struct il_addsta_cmd)); |
be663ab6 WYG |
611 | send_lq = false; |
612 | if (priv->stations[i].lq) { | |
613 | memcpy(&lq, priv->stations[i].lq, | |
e2ebc833 | 614 | sizeof(struct il_link_quality_cmd)); |
be663ab6 WYG |
615 | send_lq = true; |
616 | } | |
617 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
e2ebc833 | 618 | ret = il_send_add_sta(priv, &sta_cmd, CMD_SYNC); |
be663ab6 WYG |
619 | if (ret) { |
620 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 621 | IL_ERR(priv, "Adding station %pM failed.\n", |
be663ab6 WYG |
622 | priv->stations[i].sta.sta.addr); |
623 | priv->stations[i].used &= | |
e2ebc833 | 624 | ~IL_STA_DRIVER_ACTIVE; |
be663ab6 | 625 | priv->stations[i].used &= |
e2ebc833 | 626 | ~IL_STA_UCODE_INPROGRESS; |
be663ab6 WYG |
627 | spin_unlock_irqrestore(&priv->sta_lock, |
628 | flags_spin); | |
629 | } | |
630 | /* | |
631 | * Rate scaling has already been initialized, send | |
632 | * current LQ command | |
633 | */ | |
634 | if (send_lq) | |
e2ebc833 | 635 | il_send_lq_cmd(priv, ctx, &lq, |
be663ab6 WYG |
636 | CMD_SYNC, true); |
637 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 638 | priv->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; |
be663ab6 WYG |
639 | } |
640 | } | |
641 | ||
642 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
643 | if (!found) | |
e2ebc833 | 644 | IL_DEBUG_INFO(priv, "Restoring all known stations" |
be663ab6 WYG |
645 | " .... no stations to be restored.\n"); |
646 | else | |
e2ebc833 | 647 | IL_DEBUG_INFO(priv, "Restoring all known stations" |
be663ab6 WYG |
648 | " .... complete.\n"); |
649 | } | |
e2ebc833 | 650 | EXPORT_SYMBOL(il_restore_stations); |
be663ab6 | 651 | |
e2ebc833 | 652 | int il_get_free_ucode_key_index(struct il_priv *priv) |
be663ab6 WYG |
653 | { |
654 | int i; | |
655 | ||
656 | for (i = 0; i < priv->sta_key_max_num; i++) | |
657 | if (!test_and_set_bit(i, &priv->ucode_key_table)) | |
658 | return i; | |
659 | ||
660 | return WEP_INVALID_OFFSET; | |
661 | } | |
e2ebc833 | 662 | EXPORT_SYMBOL(il_get_free_ucode_key_index); |
be663ab6 | 663 | |
e2ebc833 | 664 | void il_dealloc_bcast_stations(struct il_priv *priv) |
be663ab6 WYG |
665 | { |
666 | unsigned long flags; | |
667 | int i; | |
668 | ||
669 | spin_lock_irqsave(&priv->sta_lock, flags); | |
670 | for (i = 0; i < priv->hw_params.max_stations; i++) { | |
e2ebc833 | 671 | if (!(priv->stations[i].used & IL_STA_BCAST)) |
be663ab6 WYG |
672 | continue; |
673 | ||
e2ebc833 | 674 | priv->stations[i].used &= ~IL_STA_UCODE_ACTIVE; |
be663ab6 WYG |
675 | priv->num_stations--; |
676 | BUG_ON(priv->num_stations < 0); | |
677 | kfree(priv->stations[i].lq); | |
678 | priv->stations[i].lq = NULL; | |
679 | } | |
680 | spin_unlock_irqrestore(&priv->sta_lock, flags); | |
681 | } | |
e2ebc833 | 682 | EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); |
be663ab6 WYG |
683 | |
684 | #ifdef CONFIG_IWLWIFI_LEGACY_DEBUG | |
e2ebc833 SG |
685 | static void il_dump_lq_cmd(struct il_priv *priv, |
686 | struct il_link_quality_cmd *lq) | |
be663ab6 WYG |
687 | { |
688 | int i; | |
e2ebc833 SG |
689 | IL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); |
690 | IL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", | |
be663ab6 WYG |
691 | lq->general_params.single_stream_ant_msk, |
692 | lq->general_params.dual_stream_ant_msk); | |
693 | ||
694 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) | |
e2ebc833 | 695 | IL_DEBUG_RATE(priv, "lq index %d 0x%X\n", |
be663ab6 WYG |
696 | i, lq->rs_table[i].rate_n_flags); |
697 | } | |
698 | #else | |
e2ebc833 SG |
699 | static inline void il_dump_lq_cmd(struct il_priv *priv, |
700 | struct il_link_quality_cmd *lq) | |
be663ab6 WYG |
701 | { |
702 | } | |
703 | #endif | |
704 | ||
705 | /** | |
e2ebc833 | 706 | * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity |
be663ab6 WYG |
707 | * |
708 | * It sometimes happens when a HT rate has been in use and we | |
709 | * loose connectivity with AP then mac80211 will first tell us that the | |
710 | * current channel is not HT anymore before removing the station. In such a | |
711 | * scenario the RXON flags will be updated to indicate we are not | |
712 | * communicating HT anymore, but the LQ command may still contain HT rates. | |
713 | * Test for this to prevent driver from sending LQ command between the time | |
714 | * RXON flags are updated and when LQ command is updated. | |
715 | */ | |
e2ebc833 SG |
716 | static bool il_is_lq_table_valid(struct il_priv *priv, |
717 | struct il_rxon_context *ctx, | |
718 | struct il_link_quality_cmd *lq) | |
be663ab6 WYG |
719 | { |
720 | int i; | |
721 | ||
722 | if (ctx->ht.enabled) | |
723 | return true; | |
724 | ||
e2ebc833 | 725 | IL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", |
be663ab6 WYG |
726 | ctx->active.channel); |
727 | for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { | |
728 | if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & | |
729 | RATE_MCS_HT_MSK) { | |
e2ebc833 | 730 | IL_DEBUG_INFO(priv, |
be663ab6 WYG |
731 | "index %d of LQ expects HT channel\n", |
732 | i); | |
733 | return false; | |
734 | } | |
735 | } | |
736 | return true; | |
737 | } | |
738 | ||
739 | /** | |
e2ebc833 | 740 | * il_send_lq_cmd() - Send link quality command |
be663ab6 WYG |
741 | * @init: This command is sent as part of station initialization right |
742 | * after station has been added. | |
743 | * | |
744 | * The link quality command is sent as the last step of station creation. | |
745 | * This is the special case in which init is set and we call a callback in | |
746 | * this case to clear the state indicating that station creation is in | |
747 | * progress. | |
748 | */ | |
e2ebc833 SG |
749 | int il_send_lq_cmd(struct il_priv *priv, struct il_rxon_context *ctx, |
750 | struct il_link_quality_cmd *lq, u8 flags, bool init) | |
be663ab6 WYG |
751 | { |
752 | int ret = 0; | |
753 | unsigned long flags_spin; | |
754 | ||
e2ebc833 | 755 | struct il_host_cmd cmd = { |
be663ab6 | 756 | .id = REPLY_TX_LINK_QUALITY_CMD, |
e2ebc833 | 757 | .len = sizeof(struct il_link_quality_cmd), |
be663ab6 WYG |
758 | .flags = flags, |
759 | .data = lq, | |
760 | }; | |
761 | ||
e2ebc833 | 762 | if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) |
be663ab6 WYG |
763 | return -EINVAL; |
764 | ||
765 | ||
766 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 767 | if (!(priv->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { |
be663ab6 WYG |
768 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
769 | return -EINVAL; | |
770 | } | |
771 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); | |
772 | ||
e2ebc833 | 773 | il_dump_lq_cmd(priv, lq); |
be663ab6 WYG |
774 | BUG_ON(init && (cmd.flags & CMD_ASYNC)); |
775 | ||
e2ebc833 SG |
776 | if (il_is_lq_table_valid(priv, ctx, lq)) |
777 | ret = il_send_cmd(priv, &cmd); | |
be663ab6 WYG |
778 | else |
779 | ret = -EINVAL; | |
780 | ||
781 | if (cmd.flags & CMD_ASYNC) | |
782 | return ret; | |
783 | ||
784 | if (init) { | |
e2ebc833 | 785 | IL_DEBUG_INFO(priv, "init LQ command complete," |
be663ab6 WYG |
786 | " clearing sta addition status for sta %d\n", |
787 | lq->sta_id); | |
788 | spin_lock_irqsave(&priv->sta_lock, flags_spin); | |
e2ebc833 | 789 | priv->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; |
be663ab6 WYG |
790 | spin_unlock_irqrestore(&priv->sta_lock, flags_spin); |
791 | } | |
792 | return ret; | |
793 | } | |
e2ebc833 | 794 | EXPORT_SYMBOL(il_send_lq_cmd); |
be663ab6 | 795 | |
e2ebc833 | 796 | int il_mac_sta_remove(struct ieee80211_hw *hw, |
be663ab6 WYG |
797 | struct ieee80211_vif *vif, |
798 | struct ieee80211_sta *sta) | |
799 | { | |
e2ebc833 SG |
800 | struct il_priv *priv = hw->priv; |
801 | struct il_station_priv_common *sta_common = (void *)sta->drv_priv; | |
be663ab6 WYG |
802 | int ret; |
803 | ||
e2ebc833 | 804 | IL_DEBUG_INFO(priv, "received request to remove station %pM\n", |
be663ab6 WYG |
805 | sta->addr); |
806 | mutex_lock(&priv->mutex); | |
e2ebc833 | 807 | IL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", |
be663ab6 | 808 | sta->addr); |
e2ebc833 | 809 | ret = il_remove_station(priv, sta_common->sta_id, sta->addr); |
be663ab6 | 810 | if (ret) |
e2ebc833 | 811 | IL_ERR(priv, "Error removing station %pM\n", |
be663ab6 WYG |
812 | sta->addr); |
813 | mutex_unlock(&priv->mutex); | |
814 | return ret; | |
815 | } | |
e2ebc833 | 816 | EXPORT_SYMBOL(il_mac_sta_remove); |