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