]>
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 | ||
46bc8d4b SG |
39 | /* il->sta_lock must be held */ |
40 | static void il_sta_ucode_activate(struct il_priv *il, u8 sta_id) | |
be663ab6 WYG |
41 | { |
42 | ||
46bc8d4b | 43 | if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) |
9406f797 | 44 | IL_ERR( |
be663ab6 | 45 | "ACTIVATE a non DRIVER active station id %u addr %pM\n", |
46bc8d4b | 46 | sta_id, il->stations[sta_id].sta.sta.addr); |
be663ab6 | 47 | |
46bc8d4b | 48 | if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { |
58de00a4 | 49 | D_ASSOC( |
be663ab6 WYG |
50 | "STA id %u addr %pM already present" |
51 | " in uCode (according to driver)\n", | |
46bc8d4b | 52 | sta_id, il->stations[sta_id].sta.sta.addr); |
be663ab6 | 53 | } else { |
46bc8d4b | 54 | il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; |
58de00a4 | 55 | D_ASSOC("Added STA id %u addr %pM to uCode\n", |
46bc8d4b | 56 | sta_id, il->stations[sta_id].sta.sta.addr); |
be663ab6 WYG |
57 | } |
58 | } | |
59 | ||
46bc8d4b | 60 | static int il_process_add_sta_resp(struct il_priv *il, |
e2ebc833 | 61 | struct il_addsta_cmd *addsta, |
dcae1c64 | 62 | struct il_rx_pkt *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 | 69 | if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { |
9406f797 | 70 | IL_ERR("Bad return from REPLY_ADD_STA (0x%08X)\n", |
be663ab6 WYG |
71 | pkt->hdr.flags); |
72 | return ret; | |
73 | } | |
74 | ||
58de00a4 | 75 | D_INFO("Processing response for adding station %u\n", |
be663ab6 WYG |
76 | sta_id); |
77 | ||
46bc8d4b | 78 | spin_lock_irqsave(&il->sta_lock, flags); |
be663ab6 WYG |
79 | |
80 | switch (pkt->u.add_sta.status) { | |
81 | case ADD_STA_SUCCESS_MSK: | |
58de00a4 | 82 | D_INFO("REPLY_ADD_STA PASSED\n"); |
46bc8d4b | 83 | il_sta_ucode_activate(il, sta_id); |
be663ab6 WYG |
84 | ret = 0; |
85 | break; | |
3b98c7f4 | 86 | case ADD_STA_NO_ROOM_IN_TBL: |
9406f797 | 87 | IL_ERR("Adding station %d failed, no room in table.\n", |
be663ab6 WYG |
88 | sta_id); |
89 | break; | |
90 | case ADD_STA_NO_BLOCK_ACK_RESOURCE: | |
9406f797 | 91 | IL_ERR( |
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: | |
9406f797 | 96 | IL_ERR("Attempting to modify non-existing station %d\n", |
be663ab6 WYG |
97 | sta_id); |
98 | break; | |
99 | default: | |
58de00a4 | 100 | D_ASSOC("Received REPLY_ADD_STA:(0x%08X)\n", |
be663ab6 WYG |
101 | pkt->u.add_sta.status); |
102 | break; | |
103 | } | |
104 | ||
58de00a4 | 105 | D_INFO("%s station id %u addr %pM\n", |
46bc8d4b | 106 | il->stations[sta_id].sta.mode == |
be663ab6 | 107 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", |
46bc8d4b | 108 | sta_id, il->stations[sta_id].sta.sta.addr); |
be663ab6 WYG |
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 | */ | |
58de00a4 | 118 | D_INFO("%s station according to cmd buffer %pM\n", |
46bc8d4b | 119 | il->stations[sta_id].sta.mode == |
be663ab6 WYG |
120 | STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", |
121 | addsta->sta.addr); | |
46bc8d4b | 122 | spin_unlock_irqrestore(&il->sta_lock, flags); |
be663ab6 WYG |
123 | |
124 | return ret; | |
125 | } | |
126 | ||
46bc8d4b | 127 | static void il_add_sta_callback(struct il_priv *il, |
e2ebc833 | 128 | struct il_device_cmd *cmd, |
dcae1c64 | 129 | struct il_rx_pkt *pkt) |
be663ab6 | 130 | { |
e2ebc833 SG |
131 | struct il_addsta_cmd *addsta = |
132 | (struct il_addsta_cmd *)cmd->cmd.payload; | |
be663ab6 | 133 | |
46bc8d4b | 134 | il_process_add_sta_resp(il, addsta, pkt, false); |
be663ab6 WYG |
135 | |
136 | } | |
137 | ||
46bc8d4b | 138 | int il_send_add_sta(struct il_priv *il, |
e2ebc833 | 139 | struct il_addsta_cmd *sta, u8 flags) |
be663ab6 | 140 | { |
dcae1c64 | 141 | struct il_rx_pkt *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 | ||
58de00a4 | 151 | D_INFO("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 | ||
46bc8d4b SG |
161 | cmd.len = il->cfg->ops->utils->build_addsta_hcmd(sta, data); |
162 | ret = il_send_cmd(il, &cmd); | |
be663ab6 WYG |
163 | |
164 | if (ret || (flags & CMD_ASYNC)) | |
165 | return ret; | |
166 | ||
167 | if (ret == 0) { | |
dcae1c64 | 168 | pkt = (struct il_rx_pkt *)cmd.reply_page; |
46bc8d4b | 169 | ret = il_process_add_sta_resp(il, sta, pkt, true); |
be663ab6 | 170 | } |
46bc8d4b | 171 | il_free_pages(il, cmd.reply_page); |
be663ab6 WYG |
172 | |
173 | return ret; | |
174 | } | |
e2ebc833 | 175 | EXPORT_SYMBOL(il_send_add_sta); |
be663ab6 | 176 | |
0c2c8852 | 177 | static void il_set_ht_add_station(struct il_priv *il, u8 idx, |
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; | |
58de00a4 | 189 | D_ASSOC("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 | ||
0c2c8852 | 195 | sta_flags = il->stations[idx].sta.station_flags; |
be663ab6 WYG |
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: | |
9406f797 | 209 | IL_WARN("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 | ||
46bc8d4b | 219 | if (il_is_ht40_tx_allowed(il, 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 | ||
0c2c8852 | 224 | il->stations[idx].sta.station_flags = sta_flags; |
be663ab6 WYG |
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 | */ | |
46bc8d4b | 234 | u8 il_prep_station(struct il_priv *il, 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 | |
46bc8d4b SG |
247 | for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { |
248 | if (!compare_ether_addr(il->stations[i].sta.sta.addr, | |
be663ab6 WYG |
249 | addr)) { |
250 | sta_id = i; | |
251 | break; | |
252 | } | |
253 | ||
46bc8d4b | 254 | if (!il->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 | */ | |
46bc8d4b | 271 | if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { |
58de00a4 | 272 | D_INFO( |
be663ab6 WYG |
273 | "STA %d already in process of being added.\n", |
274 | sta_id); | |
275 | return sta_id; | |
276 | } | |
277 | ||
46bc8d4b SG |
278 | if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && |
279 | (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && | |
280 | !compare_ether_addr(il->stations[sta_id].sta.sta.addr, addr)) { | |
58de00a4 | 281 | D_ASSOC( |
be663ab6 WYG |
282 | "STA %d (%pM) already added, not adding again.\n", |
283 | sta_id, addr); | |
284 | return sta_id; | |
285 | } | |
286 | ||
46bc8d4b | 287 | station = &il->stations[sta_id]; |
e2ebc833 | 288 | station->used = IL_STA_DRIVER_ACTIVE; |
58de00a4 | 289 | D_ASSOC("Add STA to driver ID %d: %pM\n", |
be663ab6 | 290 | sta_id, addr); |
46bc8d4b | 291 | il->num_stations++; |
be663ab6 WYG |
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 | */ | |
46bc8d4b | 313 | il_set_ht_add_station(il, sta_id, sta, ctx); |
be663ab6 WYG |
314 | |
315 | /* 3945 only */ | |
46bc8d4b | 316 | rate = (il->band == IEEE80211_BAND_5GHZ) ? |
2eb05816 | 317 | RATE_6M_PLCP : 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 | |
46bc8d4b | 332 | il_add_station_common(struct il_priv *il, |
e2ebc833 | 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; | |
46bc8d4b SG |
343 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
344 | sta_id = il_prep_station(il, ctx, addr, is_ap, sta); | |
e2ebc833 | 345 | if (sta_id == IL_INVALID_STATION) { |
9406f797 | 346 | IL_ERR("Unable to prepare station %pM for addition\n", |
be663ab6 | 347 | addr); |
46bc8d4b | 348 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 WYG |
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 | */ | |
46bc8d4b | 357 | if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { |
58de00a4 | 358 | D_INFO( |
be663ab6 WYG |
359 | "STA %d already in process of being added.\n", |
360 | sta_id); | |
46bc8d4b | 361 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 WYG |
362 | return -EEXIST; |
363 | } | |
364 | ||
46bc8d4b SG |
365 | if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && |
366 | (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { | |
58de00a4 | 367 | D_ASSOC( |
be663ab6 WYG |
368 | "STA %d (%pM) already added, not adding again.\n", |
369 | sta_id, addr); | |
46bc8d4b | 370 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 WYG |
371 | return -EEXIST; |
372 | } | |
373 | ||
46bc8d4b SG |
374 | il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; |
375 | memcpy(&sta_cmd, &il->stations[sta_id].sta, | |
e2ebc833 | 376 | sizeof(struct il_addsta_cmd)); |
46bc8d4b | 377 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 WYG |
378 | |
379 | /* Add station to device's station table */ | |
46bc8d4b | 380 | ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); |
be663ab6 | 381 | if (ret) { |
46bc8d4b | 382 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
9406f797 | 383 | IL_ERR("Adding station %pM failed.\n", |
46bc8d4b SG |
384 | il->stations[sta_id].sta.sta.addr); |
385 | il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; | |
386 | il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; | |
387 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); | |
be663ab6 WYG |
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 | 396 | * |
46bc8d4b | 397 | * il->sta_lock must be held |
be663ab6 | 398 | */ |
46bc8d4b | 399 | static void il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) |
be663ab6 WYG |
400 | { |
401 | /* Ucode must be active and driver must be non active */ | |
46bc8d4b | 402 | if ((il->stations[sta_id].used & |
e2ebc833 SG |
403 | (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != |
404 | IL_STA_UCODE_ACTIVE) | |
9406f797 | 405 | IL_ERR("removed non active STA %u\n", sta_id); |
be663ab6 | 406 | |
46bc8d4b | 407 | il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; |
be663ab6 | 408 | |
46bc8d4b | 409 | memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); |
58de00a4 | 410 | D_ASSOC("Removed STA %u\n", sta_id); |
be663ab6 WYG |
411 | } |
412 | ||
46bc8d4b | 413 | static int il_send_remove_station(struct il_priv *il, |
be663ab6 WYG |
414 | const u8 *addr, int sta_id, |
415 | bool temporary) | |
416 | { | |
dcae1c64 | 417 | struct il_rx_pkt *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 | ||
46bc8d4b | 436 | ret = il_send_cmd(il, &cmd); |
be663ab6 WYG |
437 | |
438 | if (ret) | |
439 | return ret; | |
440 | ||
dcae1c64 | 441 | pkt = (struct il_rx_pkt *)cmd.reply_page; |
e2ebc833 | 442 | if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { |
9406f797 | 443 | IL_ERR("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) { | |
46bc8d4b SG |
452 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
453 | il_sta_ucode_deactivate(il, sta_id); | |
454 | spin_unlock_irqrestore(&il->sta_lock, | |
be663ab6 WYG |
455 | flags_spin); |
456 | } | |
58de00a4 | 457 | D_ASSOC("REPLY_REMOVE_STA PASSED\n"); |
be663ab6 WYG |
458 | break; |
459 | default: | |
460 | ret = -EIO; | |
9406f797 | 461 | IL_ERR("REPLY_REMOVE_STA failed\n"); |
be663ab6 WYG |
462 | break; |
463 | } | |
464 | } | |
46bc8d4b | 465 | il_free_pages(il, 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 | */ |
46bc8d4b | 473 | int il_remove_station(struct il_priv *il, const u8 sta_id, |
be663ab6 WYG |
474 | const u8 *addr) |
475 | { | |
476 | unsigned long flags; | |
477 | ||
46bc8d4b | 478 | if (!il_is_ready(il)) { |
58de00a4 | 479 | D_INFO( |
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 | ||
58de00a4 | 490 | D_ASSOC("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 | ||
46bc8d4b | 496 | spin_lock_irqsave(&il->sta_lock, flags); |
be663ab6 | 497 | |
46bc8d4b | 498 | if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { |
58de00a4 | 499 | D_INFO("Removing %pM but non DRIVER active\n", |
be663ab6 WYG |
500 | addr); |
501 | goto out_err; | |
502 | } | |
503 | ||
46bc8d4b | 504 | if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { |
58de00a4 | 505 | D_INFO("Removing %pM but non UCODE active\n", |
be663ab6 WYG |
506 | addr); |
507 | goto out_err; | |
508 | } | |
509 | ||
46bc8d4b SG |
510 | if (il->stations[sta_id].used & IL_STA_LOCAL) { |
511 | kfree(il->stations[sta_id].lq); | |
512 | il->stations[sta_id].lq = NULL; | |
be663ab6 WYG |
513 | } |
514 | ||
46bc8d4b | 515 | il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; |
be663ab6 | 516 | |
46bc8d4b | 517 | il->num_stations--; |
be663ab6 | 518 | |
46bc8d4b | 519 | BUG_ON(il->num_stations < 0); |
be663ab6 | 520 | |
46bc8d4b | 521 | spin_unlock_irqrestore(&il->sta_lock, flags); |
be663ab6 | 522 | |
46bc8d4b | 523 | return il_send_remove_station(il, addr, sta_id, false); |
be663ab6 | 524 | out_err: |
46bc8d4b | 525 | spin_unlock_irqrestore(&il->sta_lock, flags); |
be663ab6 WYG |
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 | */ | |
46bc8d4b | 538 | void il_clear_ucode_stations(struct il_priv *il, |
e2ebc833 | 539 | struct il_rxon_context *ctx) |
be663ab6 WYG |
540 | { |
541 | int i; | |
542 | unsigned long flags_spin; | |
543 | bool cleared = false; | |
544 | ||
58de00a4 | 545 | D_INFO("Clearing ucode stations in driver\n"); |
be663ab6 | 546 | |
46bc8d4b SG |
547 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
548 | for (i = 0; i < il->hw_params.max_stations; i++) { | |
549 | if (ctx && ctx->ctxid != il->stations[i].ctxid) | |
be663ab6 WYG |
550 | continue; |
551 | ||
46bc8d4b | 552 | if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { |
58de00a4 | 553 | D_INFO( |
be663ab6 | 554 | "Clearing ucode active for station %d\n", i); |
46bc8d4b | 555 | il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; |
be663ab6 WYG |
556 | cleared = true; |
557 | } | |
558 | } | |
46bc8d4b | 559 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 WYG |
560 | |
561 | if (!cleared) | |
58de00a4 | 562 | D_INFO( |
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 | |
46bc8d4b | 576 | il_restore_stations(struct il_priv *il, 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 | ||
46bc8d4b | 586 | if (!il_is_ready(il)) { |
58de00a4 | 587 | D_INFO( |
be663ab6 WYG |
588 | "Not ready yet, not restoring any stations.\n"); |
589 | return; | |
590 | } | |
591 | ||
58de00a4 | 592 | D_ASSOC("Restoring all known stations ... start.\n"); |
46bc8d4b SG |
593 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
594 | for (i = 0; i < il->hw_params.max_stations; i++) { | |
595 | if (ctx->ctxid != il->stations[i].ctxid) | |
be663ab6 | 596 | continue; |
46bc8d4b | 597 | if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && |
232913b5 | 598 | !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { |
58de00a4 | 599 | D_ASSOC("Restoring sta %pM\n", |
46bc8d4b SG |
600 | il->stations[i].sta.sta.addr); |
601 | il->stations[i].sta.mode = 0; | |
602 | il->stations[i].used |= IL_STA_UCODE_INPROGRESS; | |
be663ab6 WYG |
603 | found = true; |
604 | } | |
605 | } | |
606 | ||
46bc8d4b SG |
607 | for (i = 0; i < il->hw_params.max_stations; i++) { |
608 | if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { | |
609 | memcpy(&sta_cmd, &il->stations[i].sta, | |
e2ebc833 | 610 | sizeof(struct il_addsta_cmd)); |
be663ab6 | 611 | send_lq = false; |
46bc8d4b SG |
612 | if (il->stations[i].lq) { |
613 | memcpy(&lq, il->stations[i].lq, | |
e2ebc833 | 614 | sizeof(struct il_link_quality_cmd)); |
be663ab6 WYG |
615 | send_lq = true; |
616 | } | |
46bc8d4b SG |
617 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
618 | ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); | |
be663ab6 | 619 | if (ret) { |
46bc8d4b | 620 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
9406f797 | 621 | IL_ERR("Adding station %pM failed.\n", |
46bc8d4b SG |
622 | il->stations[i].sta.sta.addr); |
623 | il->stations[i].used &= | |
e2ebc833 | 624 | ~IL_STA_DRIVER_ACTIVE; |
46bc8d4b | 625 | il->stations[i].used &= |
e2ebc833 | 626 | ~IL_STA_UCODE_INPROGRESS; |
46bc8d4b | 627 | spin_unlock_irqrestore(&il->sta_lock, |
be663ab6 WYG |
628 | flags_spin); |
629 | } | |
630 | /* | |
631 | * Rate scaling has already been initialized, send | |
632 | * current LQ command | |
633 | */ | |
634 | if (send_lq) | |
46bc8d4b | 635 | il_send_lq_cmd(il, ctx, &lq, |
be663ab6 | 636 | CMD_SYNC, true); |
46bc8d4b SG |
637 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
638 | il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; | |
be663ab6 WYG |
639 | } |
640 | } | |
641 | ||
46bc8d4b | 642 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 | 643 | if (!found) |
58de00a4 | 644 | D_INFO("Restoring all known stations" |
be663ab6 WYG |
645 | " .... no stations to be restored.\n"); |
646 | else | |
58de00a4 | 647 | D_INFO("Restoring all known stations" |
be663ab6 WYG |
648 | " .... complete.\n"); |
649 | } | |
e2ebc833 | 650 | EXPORT_SYMBOL(il_restore_stations); |
be663ab6 | 651 | |
0c2c8852 | 652 | int il_get_free_ucode_key_idx(struct il_priv *il) |
be663ab6 WYG |
653 | { |
654 | int i; | |
655 | ||
46bc8d4b SG |
656 | for (i = 0; i < il->sta_key_max_num; i++) |
657 | if (!test_and_set_bit(i, &il->ucode_key_table)) | |
be663ab6 WYG |
658 | return i; |
659 | ||
660 | return WEP_INVALID_OFFSET; | |
661 | } | |
0c2c8852 | 662 | EXPORT_SYMBOL(il_get_free_ucode_key_idx); |
be663ab6 | 663 | |
46bc8d4b | 664 | void il_dealloc_bcast_stations(struct il_priv *il) |
be663ab6 WYG |
665 | { |
666 | unsigned long flags; | |
667 | int i; | |
668 | ||
46bc8d4b SG |
669 | spin_lock_irqsave(&il->sta_lock, flags); |
670 | for (i = 0; i < il->hw_params.max_stations; i++) { | |
671 | if (!(il->stations[i].used & IL_STA_BCAST)) | |
be663ab6 WYG |
672 | continue; |
673 | ||
46bc8d4b SG |
674 | il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; |
675 | il->num_stations--; | |
676 | BUG_ON(il->num_stations < 0); | |
677 | kfree(il->stations[i].lq); | |
678 | il->stations[i].lq = NULL; | |
be663ab6 | 679 | } |
46bc8d4b | 680 | spin_unlock_irqrestore(&il->sta_lock, flags); |
be663ab6 | 681 | } |
e2ebc833 | 682 | EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); |
be663ab6 | 683 | |
d3175167 | 684 | #ifdef CONFIG_IWLEGACY_DEBUG |
46bc8d4b | 685 | static void il_dump_lq_cmd(struct il_priv *il, |
e2ebc833 | 686 | struct il_link_quality_cmd *lq) |
be663ab6 WYG |
687 | { |
688 | int i; | |
58de00a4 SG |
689 | D_RATE("lq station id 0x%x\n", lq->sta_id); |
690 | D_RATE("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++) | |
0c2c8852 | 695 | D_RATE("lq idx %d 0x%X\n", |
be663ab6 WYG |
696 | i, lq->rs_table[i].rate_n_flags); |
697 | } | |
698 | #else | |
46bc8d4b | 699 | static inline void il_dump_lq_cmd(struct il_priv *il, |
e2ebc833 | 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 | */ | |
46bc8d4b | 716 | static bool il_is_lq_table_valid(struct il_priv *il, |
e2ebc833 SG |
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 | ||
58de00a4 | 725 | D_INFO("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) { | |
58de00a4 | 730 | D_INFO( |
0c2c8852 | 731 | "idx %d of LQ expects HT channel\n", |
be663ab6 WYG |
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 | */ | |
46bc8d4b | 749 | int il_send_lq_cmd(struct il_priv *il, struct il_rxon_context *ctx, |
e2ebc833 | 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 | ||
46bc8d4b SG |
766 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
767 | if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { | |
768 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); | |
be663ab6 WYG |
769 | return -EINVAL; |
770 | } | |
46bc8d4b | 771 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); |
be663ab6 | 772 | |
46bc8d4b | 773 | il_dump_lq_cmd(il, lq); |
be663ab6 WYG |
774 | BUG_ON(init && (cmd.flags & CMD_ASYNC)); |
775 | ||
46bc8d4b SG |
776 | if (il_is_lq_table_valid(il, ctx, lq)) |
777 | ret = il_send_cmd(il, &cmd); | |
be663ab6 WYG |
778 | else |
779 | ret = -EINVAL; | |
780 | ||
781 | if (cmd.flags & CMD_ASYNC) | |
782 | return ret; | |
783 | ||
784 | if (init) { | |
58de00a4 | 785 | D_INFO("init LQ command complete," |
be663ab6 WYG |
786 | " clearing sta addition status for sta %d\n", |
787 | lq->sta_id); | |
46bc8d4b SG |
788 | spin_lock_irqsave(&il->sta_lock, flags_spin); |
789 | il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; | |
790 | spin_unlock_irqrestore(&il->sta_lock, flags_spin); | |
be663ab6 WYG |
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 | { | |
46bc8d4b | 800 | struct il_priv *il = hw->priv; |
e2ebc833 | 801 | struct il_station_priv_common *sta_common = (void *)sta->drv_priv; |
be663ab6 WYG |
802 | int ret; |
803 | ||
58de00a4 | 804 | D_INFO("received request to remove station %pM\n", |
be663ab6 | 805 | sta->addr); |
46bc8d4b | 806 | mutex_lock(&il->mutex); |
58de00a4 | 807 | D_INFO("proceeding to remove station %pM\n", |
be663ab6 | 808 | sta->addr); |
46bc8d4b | 809 | ret = il_remove_station(il, sta_common->sta_id, sta->addr); |
be663ab6 | 810 | if (ret) |
9406f797 | 811 | IL_ERR("Error removing station %pM\n", |
be663ab6 | 812 | sta->addr); |
46bc8d4b | 813 | mutex_unlock(&il->mutex); |
be663ab6 WYG |
814 | return ret; |
815 | } | |
e2ebc833 | 816 | EXPORT_SYMBOL(il_mac_sta_remove); |