diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/time-event.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/time-event.c | 228 | 
1 files changed, 85 insertions, 143 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index b9f076f4f17..c09b71f2375 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -76,6 +76,15 @@  #define TU_TO_JIFFIES(_tu)	(usecs_to_jiffies((_tu) * 1024))  #define MSEC_TO_TU(_msec)	(_msec*1000/1024) +/* For ROC use a TE type which has priority high enough to be scheduled when + * there is a concurrent BSS or GO/AP. Currently, use a TE type that has + * priority similar to the TE priority used for action scans by the FW. + * TODO: This needs to be changed, based on the reason for the ROC, i.e., use + * TE_P2P_DEVICE_DISCOVERABLE for remain on channel without mgmt skb, and use + * TE_P2P_DEVICE_ACTION_SCAN + */ +#define IWL_MVM_ROC_TE_TYPE TE_P2P_DEVICE_ACTION_SCAN +  void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,  			   struct iwl_mvm_time_event_data *te_data)  { @@ -175,9 +184,11 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,  		 */  		if (te_data->vif->type == NL80211_IFTYPE_STATION &&  		    (!te_data->vif->bss_conf.assoc || -		     !te_data->vif->bss_conf.dtim_period)) +		     !te_data->vif->bss_conf.dtim_period)) {  			IWL_ERR(mvm,  				"No assocation and the time event is over already...\n"); +			ieee80211_connection_loss(te_data->vif); +		}  		iwl_mvm_te_clear_data(mvm, te_data);  	} else if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_START) { @@ -219,57 +230,86 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,  	return 0;  } -static bool iwl_mvm_time_event_notif(struct iwl_notif_wait_data *notif_wait, -				     struct iwl_rx_packet *pkt, void *data) +static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, +					struct iwl_rx_packet *pkt, void *data)  {  	struct iwl_mvm *mvm =  		container_of(notif_wait, struct iwl_mvm, notif_wait);  	struct iwl_mvm_time_event_data *te_data = data; -	struct ieee80211_vif *vif = te_data->vif; -	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct iwl_time_event_notif *notif;  	struct iwl_time_event_resp *resp; +	int resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; -	u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color); +	if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) +		return true; -	/* until we do something else */ -	WARN_ON(te_data->id != TE_BSS_STA_AGGRESSIVE_ASSOC); +	if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { +		IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); +		return true; +	} -	switch (pkt->hdr.cmd) { -	case TIME_EVENT_CMD: -		resp = (void *)pkt->data; -		/* TODO: I can't check that since the fw is buggy - it doesn't -		 * put the right values when we remove a TE. We can be here -		 * when we remove a TE because the remove TE command is sent in -		 * ASYNC... -		 * WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color)); -		 */ -		te_data->uid = le32_to_cpu(resp->unique_id); -		IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid); -		return false; +	resp = (void *)pkt->data; +	te_data->uid = le32_to_cpu(resp->unique_id); +	IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", +		     te_data->uid); +	return true; +} -	case TIME_EVENT_NOTIFICATION: -		notif = (void *)pkt->data; -		WARN_ON(le32_to_cpu(notif->status) != 1); -		WARN_ON(mac_id_n_color != le32_to_cpu(notif->id_and_color)); -		/* check if this is our Time Event that is starting */ -		if (le32_to_cpu(notif->unique_id) != te_data->uid) -			return false; -		IWL_DEBUG_TE(mvm, "Event %d is starting - time is %d\n", -			     te_data->uid, le32_to_cpu(notif->timestamp)); +static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, +				       struct ieee80211_vif *vif, +				       struct iwl_mvm_time_event_data *te_data, +				       struct iwl_time_event_cmd *te_cmd) +{ +	static const u8 time_event_response[] = { TIME_EVENT_CMD }; +	struct iwl_notification_wait wait_time_event; +	int ret; -		WARN_ONCE(!le32_to_cpu(notif->status), -			  "Failed to schedule protected session TE\n"); +	lockdep_assert_held(&mvm->mutex); -		te_data->running = true; -		te_data->end_jiffies = jiffies + -				       TU_TO_JIFFIES(te_data->duration); -		return true; +	spin_lock_bh(&mvm->time_event_lock); +	if (WARN_ON(te_data->id != TE_MAX)) { +		spin_unlock_bh(&mvm->time_event_lock); +		return -EIO; +	} +	te_data->vif = vif; +	te_data->duration = le32_to_cpu(te_cmd->duration); +	te_data->id = le32_to_cpu(te_cmd->id); +	list_add_tail(&te_data->list, &mvm->time_event_list); +	spin_unlock_bh(&mvm->time_event_lock); -	default: -		WARN_ON(1); -		return false; -	}; +	/* +	 * Use a notification wait, which really just processes the +	 * command response and doesn't wait for anything, in order +	 * to be able to process the response and get the UID inside +	 * the RX path. Using CMD_WANT_SKB doesn't work because it +	 * stores the buffer and then wakes up this thread, by which +	 * time another notification (that the time event started) +	 * might already be processed unsuccessfully. +	 */ +	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, +				   time_event_response, +				   ARRAY_SIZE(time_event_response), +				   iwl_mvm_time_event_response, te_data); + +	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, +				   sizeof(*te_cmd), te_cmd); +	if (ret) { +		IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); +		iwl_remove_notification(&mvm->notif_wait, &wait_time_event); +		goto out_clear_te; +	} + +	/* No need to wait for anything, so just pass 1 (0 isn't valid) */ +	ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); +	/* should never fail */ +	WARN_ON_ONCE(ret); + +	if (ret) { + out_clear_te: +		spin_lock_bh(&mvm->time_event_lock); +		iwl_mvm_te_clear_data(mvm, te_data); +		spin_unlock_bh(&mvm->time_event_lock); +	} +	return ret;  }  void iwl_mvm_protect_session(struct iwl_mvm *mvm, @@ -278,11 +318,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; -	static const u8 time_event_notif[] = { TIME_EVENT_CMD, -					       TIME_EVENT_NOTIFICATION }; -	struct iwl_notification_wait wait_time_event;  	struct iwl_time_event_cmd time_cmd = {}; -	int ret;  	lockdep_assert_held(&mvm->mutex); @@ -309,12 +345,6 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,  		iwl_mvm_stop_session_protection(mvm, vif);  	} -	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, -				   time_event_notif, -				   ARRAY_SIZE(time_event_notif), -				   iwl_mvm_time_event_notif, -				   &mvmvif->time_event_data); -  	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);  	time_cmd.id_and_color =  		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); @@ -322,6 +352,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,  	time_cmd.apply_time =  		cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG)); +  	time_cmd.dep_policy = TE_INDEPENDENT;  	time_cmd.is_present = cpu_to_le32(1);  	time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE); @@ -333,33 +364,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,  	time_cmd.repeat = cpu_to_le32(1);  	time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END); -	te_data->vif = vif; -	te_data->duration = duration; - -	spin_lock_bh(&mvm->time_event_lock); -	te_data->id = le32_to_cpu(time_cmd.id); -	list_add_tail(&te_data->list, &mvm->time_event_list); -	spin_unlock_bh(&mvm->time_event_lock); - -	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, -				   sizeof(time_cmd), &time_cmd); -	if (ret) { -		IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); -		goto out_remove_notif; -	} - -	ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ); -	if (ret) { -		IWL_ERR(mvm, "%s - failed on timeout\n", __func__); -		spin_lock_bh(&mvm->time_event_lock); -		iwl_mvm_te_clear_data(mvm, te_data); -		spin_unlock_bh(&mvm->time_event_lock); -	} - -	return; - -out_remove_notif: -	iwl_remove_notification(&mvm->notif_wait, &wait_time_event); +	iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);  }  /* @@ -424,43 +429,12 @@ void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,  	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);  } -static bool iwl_mvm_roc_te_notif(struct iwl_notif_wait_data *notif_wait, -				 struct iwl_rx_packet *pkt, void *data) -{ -	struct iwl_mvm *mvm = -		container_of(notif_wait, struct iwl_mvm, notif_wait); -	struct iwl_mvm_time_event_data *te_data = data; -	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); -	struct iwl_time_event_resp *resp; - -	u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color); - -	/* until we do something else */ -	WARN_ON(te_data->id != TE_P2P_DEVICE_DISCOVERABLE); - -	switch (pkt->hdr.cmd) { -	case TIME_EVENT_CMD: -		resp = (void *)pkt->data; -		WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color)); -		te_data->uid = le32_to_cpu(resp->unique_id); -		IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid); -		return true; - -	default: -		WARN_ON(1); -		return false; -	}; -} -  int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			  int duration)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; -	static const u8 roc_te_notif[] = { TIME_EVENT_CMD }; -	struct iwl_notification_wait wait_time_event;  	struct iwl_time_event_cmd time_cmd = {}; -	int ret;  	lockdep_assert_held(&mvm->mutex);  	if (te_data->running) { @@ -474,16 +448,10 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	 */  	flush_work(&mvm->roc_done_wk); -	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, -				   roc_te_notif, -				   ARRAY_SIZE(roc_te_notif), -				   iwl_mvm_roc_te_notif, -				   &mvmvif->time_event_data); -  	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);  	time_cmd.id_and_color =  		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); -	time_cmd.id = cpu_to_le32(TE_P2P_DEVICE_DISCOVERABLE); +	time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE);  	time_cmd.apply_time = cpu_to_le32(0);  	time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT); @@ -492,7 +460,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	time_cmd.interval = cpu_to_le32(1);  	/* -	 * TE_P2P_DEVICE_DISCOVERABLE can have lower priority than other events +	 * IWL_MVM_ROC_TE_TYPE can have lower priority than other events  	 * that are being scheduled by the driver/fw, and thus it might not be  	 * scheduled. To improve the chances of it being scheduled, allow it to  	 * be fragmented. @@ -505,33 +473,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	time_cmd.repeat = cpu_to_le32(1);  	time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END); -	/* Push the te data to the tracked te list */ -	te_data->vif = vif; -	te_data->duration = MSEC_TO_TU(duration); - -	spin_lock_bh(&mvm->time_event_lock); -	te_data->id = le32_to_cpu(time_cmd.id); -	list_add_tail(&te_data->list, &mvm->time_event_list); -	spin_unlock_bh(&mvm->time_event_lock); - -	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, -				   sizeof(time_cmd), &time_cmd); -	if (ret) { -		IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); -		goto out_remove_notif; -	} - -	ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ); -	if (ret) { -		IWL_ERR(mvm, "%s - failed on timeout\n", __func__); -		iwl_mvm_te_clear_data(mvm, te_data); -	} - -	return ret; - -out_remove_notif: -	iwl_remove_notification(&mvm->notif_wait, &wait_time_event); -	return ret; +	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);  }  void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)  |