diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 28 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 238 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 143 |
5 files changed, 281 insertions, 137 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 440c3ff03aec..c090913d10d1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -665,8 +665,7 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: ar->state = ATH10K_STATE_RESTARTING; - del_timer_sync(&ar->scan.timeout); - ath10k_reset_scan((unsigned long)ar); + ath10k_scan_finish(ar); ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: @@ -1086,7 +1085,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); - setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); ar->workqueue = create_singlethread_workqueue("ath10k_wq"); if (!ar->workqueue) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 94b1ff2082cf..95c611db43b8 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -348,6 +348,29 @@ enum ath10k_dev_flags { ATH10K_FLAG_CORE_REGISTERED, }; +enum ath10k_scan_state { + ATH10K_SCAN_IDLE, + ATH10K_SCAN_STARTING, + ATH10K_SCAN_RUNNING, + ATH10K_SCAN_ABORTING, +}; + +static inline const char *ath10k_scan_state_str(enum ath10k_scan_state state) +{ + switch (state) { + case ATH10K_SCAN_IDLE: + return "idle"; + case ATH10K_SCAN_STARTING: + return "starting"; + case ATH10K_SCAN_RUNNING: + return "running"; + case ATH10K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -417,10 +440,9 @@ struct ath10k { struct completion started; struct completion completed; struct completion on_channel; - struct timer_list timeout; + struct delayed_work timeout; + enum ath10k_scan_state state; bool is_roc; - bool in_progress; - bool aborting; int vdev_id; int roc_freq; } scan; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3c7942030361..3bf7ddeb3271 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2159,34 +2159,40 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) /* Scanning */ /************/ -/* - * This gets called if we dont get a heart-beat during scan. - * This may indicate the FW has hung and we need to abort the - * scan manually to prevent cancel_hw_scan() from deadlocking - */ -void ath10k_reset_scan(unsigned long ptr) +void __ath10k_scan_finish(struct ath10k *ar) { - struct ath10k *ar = (struct ath10k *)ptr; + lockdep_assert_held(&ar->data_lock); - spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); - return; + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + if (ar->scan.is_roc) + ieee80211_remain_on_channel_expired(ar->hw); + else + ieee80211_scan_completed(ar->hw, + (ar->scan.state == + ATH10K_SCAN_ABORTING)); + /* fall through */ + case ATH10K_SCAN_STARTING: + ar->scan.state = ATH10K_SCAN_IDLE; + ar->scan_channel = NULL; + ath10k_offchan_tx_purge(ar); + cancel_delayed_work(&ar->scan.timeout); + complete_all(&ar->scan.completed); + break; } +} - ath10k_warn("scan timed out, firmware problem?\n"); - - if (ar->scan.is_roc) - ieee80211_remain_on_channel_expired(ar->hw); - else - ieee80211_scan_completed(ar->hw, 1 /* aborted */); - - ar->scan.in_progress = false; - complete_all(&ar->scan.completed); +void ath10k_scan_finish(struct ath10k *ar) +{ + spin_lock_bh(&ar->data_lock); + __ath10k_scan_finish(ar); spin_unlock_bh(&ar->data_lock); } -static int ath10k_abort_scan(struct ath10k *ar) +static int ath10k_scan_stop(struct ath10k *ar) { struct wmi_stop_scan_arg arg = { .req_id = 1, /* FIXME */ @@ -2197,47 +2203,79 @@ static int ath10k_abort_scan(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - del_timer_sync(&ar->scan.timeout); - - spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); - return 0; - } - - ar->scan.aborting = true; - spin_unlock_bh(&ar->data_lock); - ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) { ath10k_warn("failed to stop wmi scan: %d\n", ret); - spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; - ath10k_offchan_tx_purge(ar); - spin_unlock_bh(&ar->data_lock); - return -EIO; + goto out; } ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); - if (ret == 0) - ath10k_warn("timed out while waiting for scan to stop\n"); + if (ret == 0) { + ath10k_warn("failed to receive scan abortion completion: timed out\n"); + ret = -ETIMEDOUT; + } else if (ret > 0) { + ret = 0; + } - /* scan completion may be done right after we timeout here, so let's - * check the in_progress and tell mac80211 scan is completed. if we - * don't do that and FW fails to send us scan completion indication - * then userspace won't be able to scan anymore */ - ret = 0; +out: + /* Scan state should be updated upon scan completion but in case + * firmware fails to deliver the event (for whatever reason) it is + * desired to clean up scan state anyway. Firmware may have just + * dropped the scan completion event delivery due to transport pipe + * being overflown with data and/or it can recover on its own before + * next scan request is submitted. + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state != ATH10K_SCAN_IDLE) + __ath10k_scan_finish(ar); + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static void ath10k_scan_abort(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - ath10k_warn("failed to stop scan, it's still in progress\n"); - ar->scan.in_progress = false; - ath10k_offchan_tx_purge(ar); - ret = -ETIMEDOUT; + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + /* This can happen if timeout worker kicked in and called + * abortion while scan completion was being processed. + */ + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_ABORTING: + ath10k_warn("refusing scan abortion due to invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + ar->scan.state = ATH10K_SCAN_ABORTING; + spin_unlock_bh(&ar->data_lock); + + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn("failed to abort scan: %d\n", ret); + + spin_lock_bh(&ar->data_lock); + break; } + spin_unlock_bh(&ar->data_lock); +} - return ret; +void ath10k_scan_timeout_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + scan.timeout.work); + + mutex_lock(&ar->conf_mutex); + ath10k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); } static int ath10k_start_scan(struct ath10k *ar, @@ -2253,17 +2291,16 @@ static int ath10k_start_scan(struct ath10k *ar, ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); if (ret == 0) { - ath10k_abort_scan(ar); - return ret; + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn("failed to stop scan: %d\n", ret); + + return -ETIMEDOUT; } - /* the scan can complete earlier, before we even - * start the timer. in that case the timer handler - * checks ar->scan.in_progress and bails out if its - * false. Add a 200ms margin to account event/command - * processing. */ - mod_timer(&ar->scan.timeout, jiffies + - msecs_to_jiffies(arg->max_scan_time+200)); + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg->max_scan_time+200)); return 0; } @@ -2339,8 +2376,7 @@ void ath10k_halt(struct ath10k *ar) ath10k_monitor_stop(ar); } - del_timer_sync(&ar->scan.timeout); - ath10k_reset_scan((unsigned long)ar); + ath10k_scan_finish(ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); @@ -2531,6 +2567,7 @@ static void ath10k_stop(struct ieee80211_hw *hw) } mutex_unlock(&ar->conf_mutex); + cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->restart_work); } @@ -3176,20 +3213,26 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + ar->scan.state = ATH10K_SCAN_STARTING; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + ret = 0; + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: ret = -EBUSY; - goto exit; + break; } - - reinit_completion(&ar->scan.started); - reinit_completion(&ar->scan.completed); - ar->scan.in_progress = true; - ar->scan.aborting = false; - ar->scan.is_roc = false; - ar->scan.vdev_id = arvif->vdev_id; spin_unlock_bh(&ar->data_lock); + if (ret) + goto exit; + memset(&arg, 0, sizeof(arg)); ath10k_wmi_start_scan_init(ar, &arg); arg.vdev_id = arvif->vdev_id; @@ -3223,7 +3266,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, if (ret) { ath10k_warn("failed to start hw scan: %d\n", ret); spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; + ar->scan.state = ATH10K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); } @@ -3236,14 +3279,10 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath10k *ar = hw->priv; - int ret; mutex_lock(&ar->conf_mutex); - ret = ath10k_abort_scan(ar); - if (ret) { - ath10k_warn("failed to abort scan: %d\n", ret); - ieee80211_scan_completed(hw, 1 /* aborted */); - } + cancel_delayed_work_sync(&ar->scan.timeout); + ath10k_scan_abort(ar); mutex_unlock(&ar->conf_mutex); } @@ -3666,27 +3705,33 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct wmi_start_scan_arg arg; - int ret; + int ret = 0; mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + reinit_completion(&ar->scan.on_channel); + ar->scan.state = ATH10K_SCAN_STARTING; + ar->scan.is_roc = true; + ar->scan.vdev_id = arvif->vdev_id; + ar->scan.roc_freq = chan->center_freq; + ret = 0; + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: ret = -EBUSY; - goto exit; + break; } - - reinit_completion(&ar->scan.started); - reinit_completion(&ar->scan.completed); - reinit_completion(&ar->scan.on_channel); - ar->scan.in_progress = true; - ar->scan.aborting = false; - ar->scan.is_roc = true; - ar->scan.vdev_id = arvif->vdev_id; - ar->scan.roc_freq = chan->center_freq; spin_unlock_bh(&ar->data_lock); + if (ret) + goto exit; + memset(&arg, 0, sizeof(arg)); ath10k_wmi_start_scan_init(ar, &arg); arg.vdev_id = arvif->vdev_id; @@ -3703,7 +3748,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, if (ret) { ath10k_warn("failed to start roc scan: %d\n", ret); spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; + ar->scan.state = ATH10K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); goto exit; } @@ -3711,7 +3756,11 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ); if (ret == 0) { ath10k_warn("failed to switch to channel for roc scan\n"); - ath10k_abort_scan(ar); + + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn("failed to stop scan: %d\n", ret); + ret = -ETIMEDOUT; goto exit; } @@ -3727,7 +3776,8 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); - ath10k_abort_scan(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + ath10k_scan_abort(ar); mutex_unlock(&ar->conf_mutex); return 0; diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index ef4f84376d7c..e64fce70e9eb 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -31,7 +31,9 @@ void ath10k_mac_destroy(struct ath10k *ar); int ath10k_mac_register(struct ath10k *ar); void ath10k_mac_unregister(struct ath10k *ar); struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); -void ath10k_reset_scan(unsigned long ptr); +void __ath10k_scan_finish(struct ath10k *ar); +void ath10k_scan_finish(struct ath10k *ar); +void ath10k_scan_timeout_work(struct work_struct *work); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index f48f29563a4d..23acbadeb8fa 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -811,6 +811,94 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) return ret; } +static void ath10k_wmi_event_scan_started(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + ath10k_warn("received scan started event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_STARTING: + ar->scan.state = ATH10K_SCAN_RUNNING; + + if (ar->scan.is_roc) + ieee80211_ready_on_channel(ar->hw); + + complete(&ar->scan.started); + break; + } +} + +static void ath10k_wmi_event_scan_completed(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + /* One suspected reason scan can be completed while starting is + * if firmware fails to deliver all scan events to the host, + * e.g. when transport pipe is full. This has been observed + * with spectral scan phyerr events starving wmi transport + * pipe. In such case the "scan completed" event should be (and + * is) ignored by the host as it may be just firmware's scan + * state machine recovering. + */ + ath10k_warn("received scan completed event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + __ath10k_scan_finish(ar); + break; + } +} + +static void ath10k_wmi_event_scan_bss_chan(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn("received scan bss chan event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + ar->scan_channel = NULL; + break; + } +} + +static void ath10k_wmi_event_scan_foreign_chan(struct ath10k *ar, u32 freq) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn("received scan foreign chan event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + + if (ar->scan.is_roc && ar->scan.roc_freq == freq) + complete(&ar->scan.on_channel); + break; + } +} + static const char * ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type, enum wmi_scan_completion_reason reason) @@ -864,54 +952,32 @@ static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) scan_id = __le32_to_cpu(event->scan_id); vdev_id = __le32_to_cpu(event->vdev_id); + spin_lock_bh(&ar->data_lock); + ath10k_dbg(ATH10K_DBG_WMI, - "scan event %s type %d reason %d freq %d req_id %d " - "scan_id %d vdev_id %d\n", + "scan event %s type %d reason %d freq %d req_id %d scan_id %d vdev_id %d state %s (%d)\n", ath10k_wmi_event_scan_type_str(event_type, reason), - event_type, reason, freq, req_id, scan_id, vdev_id); - - spin_lock_bh(&ar->data_lock); + event_type, reason, freq, req_id, scan_id, vdev_id, + ath10k_scan_state_str(ar->scan.state), ar->scan.state); switch (event_type) { case WMI_SCAN_EVENT_STARTED: - if (ar->scan.in_progress && ar->scan.is_roc) - ieee80211_ready_on_channel(ar->hw); - - complete(&ar->scan.started); + ath10k_wmi_event_scan_started(ar); break; case WMI_SCAN_EVENT_COMPLETED: - ar->scan_channel = NULL; - if (!ar->scan.in_progress) { - ath10k_warn("no scan requested, ignoring\n"); - break; - } - - if (ar->scan.is_roc) { - ath10k_offchan_tx_purge(ar); - - if (!ar->scan.aborting) - ieee80211_remain_on_channel_expired(ar->hw); - } else { - ieee80211_scan_completed(ar->hw, ar->scan.aborting); - } - - del_timer(&ar->scan.timeout); - complete_all(&ar->scan.completed); - ar->scan.in_progress = false; + ath10k_wmi_event_scan_completed(ar); break; case WMI_SCAN_EVENT_BSS_CHANNEL: - ar->scan_channel = NULL; + ath10k_wmi_event_scan_bss_chan(ar); break; case WMI_SCAN_EVENT_FOREIGN_CHANNEL: - ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); - if (ar->scan.in_progress && ar->scan.is_roc && - ar->scan.roc_freq == freq) { - complete(&ar->scan.on_channel); - } + ath10k_wmi_event_scan_foreign_chan(ar, freq); + break; + case WMI_SCAN_EVENT_START_FAILED: + ath10k_warn("received scan start failure event\n"); break; case WMI_SCAN_EVENT_DEQUEUED: case WMI_SCAN_EVENT_PREEMPTED: - case WMI_SCAN_EVENT_START_FAILED: default: break; } @@ -1171,9 +1237,14 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - ath10k_warn("chan info event without a scan request?\n"); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn("received chan info event without a scan request, ignoring\n"); goto exit; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + break; } idx = freq_to_idx(ar, freq); |