diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/sta.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sta.c | 88 | 
1 files changed, 47 insertions, 41 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 300a249486e4..354acbde088e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1201,7 +1201,8 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)  	return max_offs;  } -static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, +static u8 iwl_mvm_get_key_sta_id(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif,  				 struct ieee80211_sta *sta)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1218,8 +1219,21 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,  	 * station ID, then use AP's station ID.  	 */  	if (vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) -		return mvmvif->ap_sta_id; +	    mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { +		u8 sta_id = mvmvif->ap_sta_id; + +		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], +						lockdep_is_held(&mvm->mutex)); +		/* +		 * It is possible that the 'sta' parameter is NULL, +		 * for example when a GTK is removed - the sta_id will then +		 * be the AP ID, and no station was passed by mac80211. +		 */ +		if (IS_ERR_OR_NULL(sta)) +			return IWL_MVM_STATION_COUNT; + +		return sta_id; +	}  	return IWL_MVM_STATION_COUNT;  } @@ -1227,7 +1241,8 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,  static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,  				struct iwl_mvm_sta *mvm_sta,  				struct ieee80211_key_conf *keyconf, bool mcast, -				u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) +				u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, +				u8 key_offset)  {  	struct iwl_mvm_add_sta_key_cmd cmd = {};  	__le16 key_flags; @@ -1269,7 +1284,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,  	if (mcast)  		key_flags |= cpu_to_le16(STA_KEY_MULTICAST); -	cmd.key_offset = keyconf->hw_key_idx; +	cmd.key_offset = key_offset;  	cmd.key_flags = key_flags;  	cmd.sta_id = sta_id; @@ -1360,6 +1375,7 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  				 struct ieee80211_vif *vif,  				 struct ieee80211_sta *sta,  				 struct ieee80211_key_conf *keyconf, +				 u8 key_offset,  				 bool mcast)  {  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); @@ -1375,17 +1391,17 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  		ieee80211_get_key_rx_seq(keyconf, 0, &seq);  		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);  		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, -					   seq.tkip.iv32, p1k, 0); +					   seq.tkip.iv32, p1k, 0, key_offset);  		break;  	case WLAN_CIPHER_SUITE_CCMP:  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_WEP104:  		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, -					   0, NULL, 0); +					   0, NULL, 0, key_offset);  		break;  	default:  		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, -					   0, NULL, 0); +					   0, NULL, 0, key_offset);  	}  	return ret; @@ -1433,7 +1449,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  			struct ieee80211_vif *vif,  			struct ieee80211_sta *sta,  			struct ieee80211_key_conf *keyconf, -			bool have_key_offset) +			u8 key_offset)  {  	bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);  	u8 sta_id; @@ -1443,7 +1459,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex);  	/* Get the station id from the mvm local station table */ -	sta_id = iwl_mvm_get_key_sta_id(vif, sta); +	sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);  	if (sta_id == IWL_MVM_STATION_COUNT) {  		IWL_ERR(mvm, "Failed to find station id\n");  		return -EINVAL; @@ -1470,18 +1486,25 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))  		return -EINVAL; -	if (!have_key_offset) { -		/* -		 * The D3 firmware hardcodes the PTK offset to 0, so we have to -		 * configure it there. As a result, this workaround exists to -		 * let the caller set the key offset (hw_key_idx), see d3.c. -		 */ -		keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm); -		if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) +	/* If the key_offset is not pre-assigned, we need to find a +	 * new offset to use.  In normal cases, the offset is not +	 * pre-assigned, but during HW_RESTART we want to reuse the +	 * same indices, so we pass them when this function is called. +	 * +	 * In D3 entry, we need to hardcoded the indices (because the +	 * firmware hardcodes the PTK offset to 0).  In this case, we +	 * need to make sure we don't overwrite the hw_key_idx in the +	 * keyconf structure, because otherwise we cannot configure +	 * the original ones back when resuming. +	 */ +	if (key_offset == STA_KEY_IDX_INVALID) { +		key_offset  = iwl_mvm_set_fw_key_idx(mvm); +		if (key_offset == STA_KEY_IDX_INVALID)  			return -ENOSPC; +		keyconf->hw_key_idx = key_offset;  	} -	ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast); +	ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast);  	if (ret) {  		__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);  		goto end; @@ -1495,7 +1518,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	 */  	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||  	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { -		ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast); +		ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, +					    key_offset, !mcast);  		if (ret) {  			__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);  			__iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); @@ -1521,7 +1545,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex);  	/* Get the station id from the mvm local station table */ -	sta_id = iwl_mvm_get_key_sta_id(vif, sta); +	sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);  	IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",  		      keyconf->keyidx, sta_id); @@ -1547,24 +1571,6 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,  		return 0;  	} -	/* -	 * It is possible that the 'sta' parameter is NULL, and thus -	 * there is a need to retrieve the sta from the local station table, -	 * for example when a GTK is removed (where the sta_id will then be -	 * the AP ID, and no station was passed by mac80211.) -	 */ -	if (!sta) { -		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], -						lockdep_is_held(&mvm->mutex)); -		if (!sta) { -			IWL_ERR(mvm, "Invalid station id\n"); -			return -EINVAL; -		} -	} - -	if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) -		return -EINVAL; -  	ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);  	if (ret)  		return ret; @@ -1584,7 +1590,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,  			     u16 *phase1key)  {  	struct iwl_mvm_sta *mvm_sta; -	u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta); +	u8 sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta);  	bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);  	if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) @@ -1602,7 +1608,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,  	mvm_sta = iwl_mvm_sta_from_mac80211(sta);  	iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, -			     iv32, phase1key, CMD_ASYNC); +			     iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);  	rcu_read_unlock();  }  | 
