diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/time-event.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/time-event.c | 232 |
1 files changed, 87 insertions, 145 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; - - 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)); - - WARN_ONCE(!le32_to_cpu(notif->status), - "Failed to schedule protected session TE\n"); + 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; +} - te_data->running = true; - te_data->end_jiffies = jiffies + - TU_TO_JIFFIES(te_data->duration); - return true; +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; + + lockdep_assert_held(&mvm->mutex); + + 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); + + /* + * 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); - default: - WARN_ON(1); - return false; - }; + 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) |