blob: 3c354a8952ec1f05416c441255a47f16a0e49c15 [file] [log] [blame]
Wey-Yi Guybe663ab2011-02-21 11:27:26 -08001/******************************************************************************
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
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020039/* il->sta_lock must be held */
40static void il_sta_ucode_activate(struct il_priv *il, u8 sta_id)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080041{
42
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020043 if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
Stanislaw Gruszka9406f792011-08-18 22:07:57 +020044 IL_ERR(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080045 "ACTIVATE a non DRIVER active station id %u addr %pM\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020046 sta_id, il->stations[sta_id].sta.sta.addr);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080047
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020048 if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +010049 D_ASSOC(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080050 "STA id %u addr %pM already present"
51 " in uCode (according to driver)\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020052 sta_id, il->stations[sta_id].sta.sta.addr);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080053 } else {
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020054 il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +010055 D_ASSOC("Added STA id %u addr %pM to uCode\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020056 sta_id, il->stations[sta_id].sta.sta.addr);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080057 }
58}
59
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020060static int il_process_add_sta_resp(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +020061 struct il_addsta_cmd *addsta,
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +020062 struct il_rx_pkt *pkt,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080063 bool sync)
64{
65 u8 sta_id = addsta->sta.sta_id;
66 unsigned long flags;
67 int ret = -EIO;
68
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +020069 if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
Stanislaw Gruszka9406f792011-08-18 22:07:57 +020070 IL_ERR("Bad return from REPLY_ADD_STA (0x%08X)\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080071 pkt->hdr.flags);
72 return ret;
73 }
74
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +010075 D_INFO("Processing response for adding station %u\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080076 sta_id);
77
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020078 spin_lock_irqsave(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080079
80 switch (pkt->u.add_sta.status) {
81 case ADD_STA_SUCCESS_MSK:
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +010082 D_INFO("REPLY_ADD_STA PASSED\n");
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +020083 il_sta_ucode_activate(il, sta_id);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080084 ret = 0;
85 break;
86 case ADD_STA_NO_ROOM_IN_TABLE:
Stanislaw Gruszka9406f792011-08-18 22:07:57 +020087 IL_ERR("Adding station %d failed, no room in table.\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080088 sta_id);
89 break;
90 case ADD_STA_NO_BLOCK_ACK_RESOURCE:
Stanislaw Gruszka9406f792011-08-18 22:07:57 +020091 IL_ERR(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080092 "Adding station %d failed, no block ack resource.\n",
93 sta_id);
94 break;
95 case ADD_STA_MODIFY_NON_EXIST_STA:
Stanislaw Gruszka9406f792011-08-18 22:07:57 +020096 IL_ERR("Attempting to modify non-existing station %d\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -080097 sta_id);
98 break;
99 default:
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100100 D_ASSOC("Received REPLY_ADD_STA:(0x%08X)\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800101 pkt->u.add_sta.status);
102 break;
103 }
104
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100105 D_INFO("%s station id %u addr %pM\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200106 il->stations[sta_id].sta.mode ==
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800107 STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200108 sta_id, il->stations[sta_id].sta.sta.addr);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800109
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
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300113 * written to the command buffer often is not the same MAC address
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800114 * 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 */
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100118 D_INFO("%s station according to cmd buffer %pM\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200119 il->stations[sta_id].sta.mode ==
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800120 STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
121 addsta->sta.addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200122 spin_unlock_irqrestore(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800123
124 return ret;
125}
126
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200127static void il_add_sta_callback(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200128 struct il_device_cmd *cmd,
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +0200129 struct il_rx_pkt *pkt)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800130{
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200131 struct il_addsta_cmd *addsta =
132 (struct il_addsta_cmd *)cmd->cmd.payload;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800133
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200134 il_process_add_sta_resp(il, addsta, pkt, false);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800135
136}
137
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200138int il_send_add_sta(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200139 struct il_addsta_cmd *sta, u8 flags)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800140{
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +0200141 struct il_rx_pkt *pkt = NULL;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800142 int ret = 0;
143 u8 data[sizeof(*sta)];
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200144 struct il_host_cmd cmd = {
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800145 .id = REPLY_ADD_STA,
146 .flags = flags,
147 .data = data,
148 };
149 u8 sta_id __maybe_unused = sta->sta.sta_id;
150
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100151 D_INFO("Adding sta %u (%pM) %ssynchronously\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800152 sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
153
154 if (flags & CMD_ASYNC)
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200155 cmd.callback = il_add_sta_callback;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800156 else {
157 cmd.flags |= CMD_WANT_SKB;
158 might_sleep();
159 }
160
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200161 cmd.len = il->cfg->ops->utils->build_addsta_hcmd(sta, data);
162 ret = il_send_cmd(il, &cmd);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800163
164 if (ret || (flags & CMD_ASYNC))
165 return ret;
166
167 if (ret == 0) {
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +0200168 pkt = (struct il_rx_pkt *)cmd.reply_page;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200169 ret = il_process_add_sta_resp(il, sta, pkt, true);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800170 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200171 il_free_pages(il, cmd.reply_page);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800172
173 return ret;
174}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200175EXPORT_SYMBOL(il_send_add_sta);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800176
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200177static void il_set_ht_add_station(struct il_priv *il, u8 index,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800178 struct ieee80211_sta *sta,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200179 struct il_rxon_context *ctx)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800180{
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;
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100189 D_ASSOC("spatial multiplexing power save mode: %s\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800190 (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
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200195 sta_flags = il->stations[index].sta.station_flags;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800196
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:
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200209 IL_WARN("Invalid MIMO PS mode %d\n", mimo_ps_mode);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800210 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
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200219 if (il_is_ht40_tx_allowed(il, ctx, &sta->ht_cap))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800220 sta_flags |= STA_FLG_HT40_EN_MSK;
221 else
222 sta_flags &= ~STA_FLG_HT40_EN_MSK;
223
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200224 il->stations[index].sta.station_flags = sta_flags;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800225 done:
226 return;
227}
228
229/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200230 * il_prep_station - Prepare station information for addition
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800231 *
232 * should be called with sta_lock held
233 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200234u8 il_prep_station(struct il_priv *il, struct il_rxon_context *ctx,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800235 const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
236{
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200237 struct il_station_entry *station;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800238 int i;
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200239 u8 sta_id = IL_INVALID_STATION;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800240 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
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200247 for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) {
248 if (!compare_ether_addr(il->stations[i].sta.sta.addr,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800249 addr)) {
250 sta_id = i;
251 break;
252 }
253
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200254 if (!il->stations[i].used &&
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200255 sta_id == IL_INVALID_STATION)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800256 sta_id = i;
257 }
258
259 /*
260 * These two conditions have the same outcome, but keep them
261 * separate
262 */
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200263 if (unlikely(sta_id == IL_INVALID_STATION))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800264 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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200271 if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100272 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800273 "STA %d already in process of being added.\n",
274 sta_id);
275 return sta_id;
276 }
277
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200278 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)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100281 D_ASSOC(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800282 "STA %d (%pM) already added, not adding again.\n",
283 sta_id, addr);
284 return sta_id;
285 }
286
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200287 station = &il->stations[sta_id];
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200288 station->used = IL_STA_DRIVER_ACTIVE;
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100289 D_ASSOC("Add STA to driver ID %d: %pM\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800290 sta_id, addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200291 il->num_stations++;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800292
293 /* Set up the REPLY_ADD_STA command to send to device */
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200294 memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800295 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) {
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200302 struct il_station_priv_common *sta_priv;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800303
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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200313 il_set_ht_add_station(il, sta_id, sta, ctx);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800314
315 /* 3945 only */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200316 rate = (il->band == IEEE80211_BAND_5GHZ) ?
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200317 IL_RATE_6M_PLCP : IL_RATE_1M_PLCP;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800318 /* 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}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200324EXPORT_SYMBOL_GPL(il_prep_station);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800325
326#define STA_WAIT_TIMEOUT (HZ/2)
327
328/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200329 * il_add_station_common -
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800330 */
331int
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200332il_add_station_common(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200333 struct il_rxon_context *ctx,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800334 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;
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200340 struct il_addsta_cmd sta_cmd;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800341
342 *sta_id_r = 0;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200343 spin_lock_irqsave(&il->sta_lock, flags_spin);
344 sta_id = il_prep_station(il, ctx, addr, is_ap, sta);
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200345 if (sta_id == IL_INVALID_STATION) {
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200346 IL_ERR("Unable to prepare station %pM for addition\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800347 addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200348 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800349 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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200357 if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100358 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800359 "STA %d already in process of being added.\n",
360 sta_id);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200361 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800362 return -EEXIST;
363 }
364
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200365 if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
366 (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100367 D_ASSOC(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800368 "STA %d (%pM) already added, not adding again.\n",
369 sta_id, addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200370 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800371 return -EEXIST;
372 }
373
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200374 il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS;
375 memcpy(&sta_cmd, &il->stations[sta_id].sta,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200376 sizeof(struct il_addsta_cmd));
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200377 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800378
379 /* Add station to device's station table */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200380 ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800381 if (ret) {
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200382 spin_lock_irqsave(&il->sta_lock, flags_spin);
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200383 IL_ERR("Adding station %pM failed.\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200384 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);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800388 }
389 *sta_id_r = sta_id;
390 return ret;
391}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200392EXPORT_SYMBOL(il_add_station_common);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800393
394/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200395 * il_sta_ucode_deactivate - deactivate ucode status for a station
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800396 *
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200397 * il->sta_lock must be held
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800398 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200399static void il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800400{
401 /* Ucode must be active and driver must be non active */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200402 if ((il->stations[sta_id].used &
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200403 (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
404 IL_STA_UCODE_ACTIVE)
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200405 IL_ERR("removed non active STA %u\n", sta_id);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800406
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200407 il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800408
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200409 memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry));
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100410 D_ASSOC("Removed STA %u\n", sta_id);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800411}
412
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200413static int il_send_remove_station(struct il_priv *il,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800414 const u8 *addr, int sta_id,
415 bool temporary)
416{
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +0200417 struct il_rx_pkt *pkt;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800418 int ret;
419
420 unsigned long flags_spin;
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200421 struct il_rem_sta_cmd rm_sta_cmd;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800422
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200423 struct il_host_cmd cmd = {
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800424 .id = REPLY_REMOVE_STA,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200425 .len = sizeof(struct il_rem_sta_cmd),
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800426 .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
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200436 ret = il_send_cmd(il, &cmd);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800437
438 if (ret)
439 return ret;
440
Stanislaw Gruszkadcae1c62011-08-26 14:36:21 +0200441 pkt = (struct il_rx_pkt *)cmd.reply_page;
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200442 if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200443 IL_ERR("Bad return from REPLY_REMOVE_STA (0x%08X)\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800444 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) {
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200452 spin_lock_irqsave(&il->sta_lock, flags_spin);
453 il_sta_ucode_deactivate(il, sta_id);
454 spin_unlock_irqrestore(&il->sta_lock,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800455 flags_spin);
456 }
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100457 D_ASSOC("REPLY_REMOVE_STA PASSED\n");
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800458 break;
459 default:
460 ret = -EIO;
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200461 IL_ERR("REPLY_REMOVE_STA failed\n");
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800462 break;
463 }
464 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200465 il_free_pages(il, cmd.reply_page);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800466
467 return ret;
468}
469
470/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200471 * il_remove_station - Remove driver's knowledge of station.
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800472 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200473int il_remove_station(struct il_priv *il, const u8 sta_id,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800474 const u8 *addr)
475{
476 unsigned long flags;
477
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200478 if (!il_is_ready(il)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100479 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800480 "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
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100490 D_ASSOC("Removing STA from driver:%d %pM\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800491 sta_id, addr);
492
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200493 if (WARN_ON(sta_id == IL_INVALID_STATION))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800494 return -EINVAL;
495
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200496 spin_lock_irqsave(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800497
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200498 if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100499 D_INFO("Removing %pM but non DRIVER active\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800500 addr);
501 goto out_err;
502 }
503
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200504 if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100505 D_INFO("Removing %pM but non UCODE active\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800506 addr);
507 goto out_err;
508 }
509
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200510 if (il->stations[sta_id].used & IL_STA_LOCAL) {
511 kfree(il->stations[sta_id].lq);
512 il->stations[sta_id].lq = NULL;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800513 }
514
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200515 il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800516
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200517 il->num_stations--;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800518
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200519 BUG_ON(il->num_stations < 0);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800520
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200521 spin_unlock_irqrestore(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800522
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200523 return il_send_remove_station(il, addr, sta_id, false);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800524out_err:
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200525 spin_unlock_irqrestore(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800526 return -EINVAL;
527}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200528EXPORT_SYMBOL_GPL(il_remove_station);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800529
530/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200531 * il_clear_ucode_stations - clear ucode station table bits
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800532 *
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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200538void il_clear_ucode_stations(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200539 struct il_rxon_context *ctx)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800540{
541 int i;
542 unsigned long flags_spin;
543 bool cleared = false;
544
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100545 D_INFO("Clearing ucode stations in driver\n");
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800546
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200547 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)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800550 continue;
551
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200552 if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100553 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800554 "Clearing ucode active for station %d\n", i);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200555 il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800556 cleared = true;
557 }
558 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200559 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800560
561 if (!cleared)
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100562 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800563 "No active stations found to be cleared\n");
564}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200565EXPORT_SYMBOL(il_clear_ucode_stations);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800566
567/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200568 * il_restore_stations() - Restore driver known stations to device
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800569 *
570 * All stations considered active by driver, but not present in ucode, is
571 * restored.
572 *
573 * Function sleeps.
574 */
575void
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200576il_restore_stations(struct il_priv *il, struct il_rxon_context *ctx)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800577{
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200578 struct il_addsta_cmd sta_cmd;
579 struct il_link_quality_cmd lq;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800580 unsigned long flags_spin;
581 int i;
582 bool found = false;
583 int ret;
584 bool send_lq;
585
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200586 if (!il_is_ready(il)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100587 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800588 "Not ready yet, not restoring any stations.\n");
589 return;
590 }
591
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100592 D_ASSOC("Restoring all known stations ... start.\n");
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200593 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)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800596 continue;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200597 if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
Stanislaw Gruszka232913b2011-08-26 10:45:16 +0200598 !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100599 D_ASSOC("Restoring sta %pM\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200600 il->stations[i].sta.sta.addr);
601 il->stations[i].sta.mode = 0;
602 il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800603 found = true;
604 }
605 }
606
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200607 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,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200610 sizeof(struct il_addsta_cmd));
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800611 send_lq = false;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200612 if (il->stations[i].lq) {
613 memcpy(&lq, il->stations[i].lq,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200614 sizeof(struct il_link_quality_cmd));
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800615 send_lq = true;
616 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200617 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
618 ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800619 if (ret) {
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200620 spin_lock_irqsave(&il->sta_lock, flags_spin);
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200621 IL_ERR("Adding station %pM failed.\n",
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200622 il->stations[i].sta.sta.addr);
623 il->stations[i].used &=
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200624 ~IL_STA_DRIVER_ACTIVE;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200625 il->stations[i].used &=
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200626 ~IL_STA_UCODE_INPROGRESS;
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200627 spin_unlock_irqrestore(&il->sta_lock,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800628 flags_spin);
629 }
630 /*
631 * Rate scaling has already been initialized, send
632 * current LQ command
633 */
634 if (send_lq)
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200635 il_send_lq_cmd(il, ctx, &lq,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800636 CMD_SYNC, true);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200637 spin_lock_irqsave(&il->sta_lock, flags_spin);
638 il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800639 }
640 }
641
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200642 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800643 if (!found)
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100644 D_INFO("Restoring all known stations"
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800645 " .... no stations to be restored.\n");
646 else
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100647 D_INFO("Restoring all known stations"
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800648 " .... complete.\n");
649}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200650EXPORT_SYMBOL(il_restore_stations);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800651
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200652int il_get_free_ucode_key_index(struct il_priv *il)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800653{
654 int i;
655
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200656 for (i = 0; i < il->sta_key_max_num; i++)
657 if (!test_and_set_bit(i, &il->ucode_key_table))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800658 return i;
659
660 return WEP_INVALID_OFFSET;
661}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200662EXPORT_SYMBOL(il_get_free_ucode_key_index);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800663
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200664void il_dealloc_bcast_stations(struct il_priv *il)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800665{
666 unsigned long flags;
667 int i;
668
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200669 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))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800672 continue;
673
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200674 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;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800679 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200680 spin_unlock_irqrestore(&il->sta_lock, flags);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800681}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200682EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800683
Stanislaw Gruszkad3175162011-11-15 11:25:42 +0100684#ifdef CONFIG_IWLEGACY_DEBUG
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200685static void il_dump_lq_cmd(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200686 struct il_link_quality_cmd *lq)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800687{
688 int i;
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100689 D_RATE("lq station id 0x%x\n", lq->sta_id);
690 D_RATE("lq ant 0x%X 0x%X\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800691 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++)
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100695 D_RATE("lq index %d 0x%X\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800696 i, lq->rs_table[i].rate_n_flags);
697}
698#else
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200699static inline void il_dump_lq_cmd(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200700 struct il_link_quality_cmd *lq)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800701{
702}
703#endif
704
705/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200706 * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800707 *
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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200716static bool il_is_lq_table_valid(struct il_priv *il,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200717 struct il_rxon_context *ctx,
718 struct il_link_quality_cmd *lq)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800719{
720 int i;
721
722 if (ctx->ht.enabled)
723 return true;
724
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100725 D_INFO("Channel %u is not an HT channel\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800726 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) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100730 D_INFO(
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800731 "index %d of LQ expects HT channel\n",
732 i);
733 return false;
734 }
735 }
736 return true;
737}
738
739/**
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200740 * il_send_lq_cmd() - Send link quality command
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800741 * @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 */
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200749int il_send_lq_cmd(struct il_priv *il, struct il_rxon_context *ctx,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200750 struct il_link_quality_cmd *lq, u8 flags, bool init)
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800751{
752 int ret = 0;
753 unsigned long flags_spin;
754
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200755 struct il_host_cmd cmd = {
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800756 .id = REPLY_TX_LINK_QUALITY_CMD,
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200757 .len = sizeof(struct il_link_quality_cmd),
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800758 .flags = flags,
759 .data = lq,
760 };
761
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200762 if (WARN_ON(lq->sta_id == IL_INVALID_STATION))
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800763 return -EINVAL;
764
765
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200766 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);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800769 return -EINVAL;
770 }
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200771 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800772
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200773 il_dump_lq_cmd(il, lq);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800774 BUG_ON(init && (cmd.flags & CMD_ASYNC));
775
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200776 if (il_is_lq_table_valid(il, ctx, lq))
777 ret = il_send_cmd(il, &cmd);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800778 else
779 ret = -EINVAL;
780
781 if (cmd.flags & CMD_ASYNC)
782 return ret;
783
784 if (init) {
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100785 D_INFO("init LQ command complete,"
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800786 " clearing sta addition status for sta %d\n",
787 lq->sta_id);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200788 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);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800791 }
792 return ret;
793}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200794EXPORT_SYMBOL(il_send_lq_cmd);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800795
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200796int il_mac_sta_remove(struct ieee80211_hw *hw,
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800797 struct ieee80211_vif *vif,
798 struct ieee80211_sta *sta)
799{
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200800 struct il_priv *il = hw->priv;
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200801 struct il_station_priv_common *sta_common = (void *)sta->drv_priv;
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800802 int ret;
803
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100804 D_INFO("received request to remove station %pM\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800805 sta->addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200806 mutex_lock(&il->mutex);
Stanislaw Gruszka58de00a2011-11-15 11:21:01 +0100807 D_INFO("proceeding to remove station %pM\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800808 sta->addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200809 ret = il_remove_station(il, sta_common->sta_id, sta->addr);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800810 if (ret)
Stanislaw Gruszka9406f792011-08-18 22:07:57 +0200811 IL_ERR("Error removing station %pM\n",
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800812 sta->addr);
Stanislaw Gruszka46bc8d42011-10-24 16:49:25 +0200813 mutex_unlock(&il->mutex);
Wey-Yi Guybe663ab2011-02-21 11:27:26 -0800814 return ret;
815}
Stanislaw Gruszkae2ebc832011-10-24 15:41:30 +0200816EXPORT_SYMBOL(il_mac_sta_remove);