Commit cc4bf501 authored by Luciano Coelho's avatar Luciano Coelho

Merge branch 'wl12xx-next' into for-linville

parents 41b58f18 55df5afb
...@@ -459,23 +459,39 @@ int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id) ...@@ -459,23 +459,39 @@ int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id)
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{ {
unsigned long flags;
u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS); u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
if (link >= WL12XX_MAX_LINKS) if (link >= WL12XX_MAX_LINKS)
return -EBUSY; return -EBUSY;
/* these bits are used by op_tx */
spin_lock_irqsave(&wl->wl_lock, flags);
__set_bit(link, wl->links_map); __set_bit(link, wl->links_map);
__set_bit(link, wlvif->links_map); __set_bit(link, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
*hlid = link; *hlid = link;
return 0; return 0;
} }
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{ {
unsigned long flags;
if (*hlid == WL12XX_INVALID_LINK_ID) if (*hlid == WL12XX_INVALID_LINK_ID)
return; return;
/* these bits are used by op_tx */
spin_lock_irqsave(&wl->wl_lock, flags);
__clear_bit(*hlid, wl->links_map); __clear_bit(*hlid, wl->links_map);
__clear_bit(*hlid, wlvif->links_map); __clear_bit(*hlid, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
/*
* At this point op_tx() will not add more packets to the queues. We
* can purge them.
*/
wl1271_tx_reset_link_queues(wl, *hlid);
*hlid = WL12XX_INVALID_LINK_ID; *hlid = WL12XX_INVALID_LINK_ID;
} }
...@@ -515,7 +531,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, ...@@ -515,7 +531,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
goto out_free; goto out_free;
} }
cmd->device.hlid = wlvif->dev_hlid; cmd->device.hlid = wlvif->dev_hlid;
cmd->device.session = wlvif->session_counter; cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
cmd->role_id, cmd->device.hlid, cmd->device.session); cmd->role_id, cmd->device.hlid, cmd->device.session);
...@@ -1802,6 +1818,14 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id) ...@@ -1802,6 +1818,14 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
goto out; goto out;
__clear_bit(role_id, wl->roc_map); __clear_bit(role_id, wl->roc_map);
/*
* Rearm the tx watchdog when removing the last ROC. This prevents
* recoveries due to just finished ROCs - when Tx hasn't yet had
* a chance to get out.
*/
if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES)
wl12xx_rearm_tx_watchdog_locked(wl);
out: out:
return ret; return ret;
} }
......
...@@ -690,6 +690,9 @@ struct conf_tx_settings { ...@@ -690,6 +690,9 @@ struct conf_tx_settings {
*/ */
u8 tmpl_short_retry_limit; u8 tmpl_short_retry_limit;
u8 tmpl_long_retry_limit; u8 tmpl_long_retry_limit;
/* Time in ms for Tx watchdog timer to expire */
u32 tx_watchdog_timeout;
}; };
enum { enum {
......
...@@ -217,6 +217,7 @@ static struct conf_drv_settings default_conf = { ...@@ -217,6 +217,7 @@ static struct conf_drv_settings default_conf = {
.basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
.tmpl_short_retry_limit = 10, .tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10, .tmpl_long_retry_limit = 10,
.tx_watchdog_timeout = 5000,
}, },
.conn = { .conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
...@@ -246,7 +247,7 @@ static struct conf_drv_settings default_conf = { ...@@ -246,7 +247,7 @@ static struct conf_drv_settings default_conf = {
.psm_entry_retries = 8, .psm_entry_retries = 8,
.psm_exit_retries = 16, .psm_exit_retries = 16,
.psm_entry_nullfunc_retries = 3, .psm_entry_nullfunc_retries = 3,
.dynamic_ps_timeout = 100, .dynamic_ps_timeout = 200,
.forced_ps = false, .forced_ps = false,
.keep_alive_interval = 55000, .keep_alive_interval = 55000,
.max_listen_interval = 20, .max_listen_interval = 20,
...@@ -392,15 +393,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, ...@@ -392,15 +393,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
static void wl1271_op_stop(struct ieee80211_hw *hw); static void wl1271_op_stop(struct ieee80211_hw *hw);
static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
static DEFINE_MUTEX(wl_list_mutex); static int wl12xx_set_authorized(struct wl1271 *wl,
static LIST_HEAD(wl_list); struct wl12xx_vif *wlvif)
static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
unsigned char operstate)
{ {
int ret; int ret;
if (operstate != IF_OPER_UP) if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
return -EINVAL;
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
return 0; return 0;
if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags)) if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
...@@ -415,76 +416,6 @@ static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -415,76 +416,6 @@ static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_info("Association completed."); wl1271_info("Association completed.");
return 0; return 0;
} }
static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
void *arg)
{
struct net_device *dev = arg;
struct wireless_dev *wdev;
struct wiphy *wiphy;
struct ieee80211_hw *hw;
struct wl1271 *wl;
struct wl1271 *wl_temp;
struct wl12xx_vif *wlvif;
int ret = 0;
/* Check that this notification is for us. */
if (what != NETDEV_CHANGE)
return NOTIFY_DONE;
wdev = dev->ieee80211_ptr;
if (wdev == NULL)
return NOTIFY_DONE;
wiphy = wdev->wiphy;
if (wiphy == NULL)
return NOTIFY_DONE;
hw = wiphy_priv(wiphy);
if (hw == NULL)
return NOTIFY_DONE;
wl_temp = hw->priv;
mutex_lock(&wl_list_mutex);
list_for_each_entry(wl, &wl_list, list) {
if (wl == wl_temp)
break;
}
mutex_unlock(&wl_list_mutex);
if (wl != wl_temp)
return NOTIFY_DONE;
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF)
goto out;
if (dev->operstate != IF_OPER_UP)
goto out;
/*
* The correct behavior should be just getting the appropriate wlvif
* from the given dev, but currently we don't have a mac80211
* interface for it.
*/
wl12xx_for_each_wlvif_sta(wl, wlvif) {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
continue;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl1271_check_operstate(wl, wlvif,
ieee80211_get_operstate(vif));
wl1271_ps_elp_sleep(wl);
}
out:
mutex_unlock(&wl->mutex);
return NOTIFY_OK;
}
static int wl1271_reg_notify(struct wiphy *wiphy, static int wl1271_reg_notify(struct wiphy *wiphy,
struct regulatory_request *request) struct regulatory_request *request)
...@@ -623,6 +554,80 @@ static void wl1271_rx_streaming_timer(unsigned long data) ...@@ -623,6 +554,80 @@ static void wl1271_rx_streaming_timer(unsigned long data)
ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work); ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
} }
/* wl->mutex must be taken */
void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
{
/* if the watchdog is not armed, don't do anything */
if (wl->tx_allocated_blocks == 0)
return;
cancel_delayed_work(&wl->tx_watchdog_work);
ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
}
static void wl12xx_tx_watchdog_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, tx_watchdog_work);
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
/* Tx went out in the meantime - everything is ok */
if (unlikely(wl->tx_allocated_blocks == 0))
goto out;
/*
* if a ROC is in progress, we might not have any Tx for a long
* time (e.g. pending Tx on the non-ROC channels)
*/
if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
wl->conf.tx.tx_watchdog_timeout);
wl12xx_rearm_tx_watchdog_locked(wl);
goto out;
}
/*
* if a scan is in progress, we might not have any Tx for a long
* time
*/
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
wl->conf.tx.tx_watchdog_timeout);
wl12xx_rearm_tx_watchdog_locked(wl);
goto out;
}
/*
* AP might cache a frame for a long time for a sleeping station,
* so rearm the timer if there's an AP interface with stations. If
* Tx is genuinely stuck we will most hopefully discover it when all
* stations are removed due to inactivity.
*/
if (wl->active_sta_count) {
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
" %d stations",
wl->conf.tx.tx_watchdog_timeout,
wl->active_sta_count);
wl12xx_rearm_tx_watchdog_locked(wl);
goto out;
}
wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
wl->conf.tx.tx_watchdog_timeout);
wl12xx_queue_recovery_work(wl);
out:
mutex_unlock(&wl->mutex);
}
static void wl1271_conf_init(struct wl1271 *wl) static void wl1271_conf_init(struct wl1271 *wl)
{ {
...@@ -815,6 +820,18 @@ static void wl12xx_fw_status(struct wl1271 *wl, ...@@ -815,6 +820,18 @@ static void wl12xx_fw_status(struct wl1271 *wl,
wl->tx_allocated_blocks -= freed_blocks; wl->tx_allocated_blocks -= freed_blocks;
/*
* If the FW freed some blocks:
* If we still have allocated blocks - re-arm the timer, Tx is
* not stuck. Otherwise, cancel the timer (no Tx currently).
*/
if (freed_blocks) {
if (wl->tx_allocated_blocks)
wl12xx_rearm_tx_watchdog_locked(wl);
else
cancel_delayed_work(&wl->tx_watchdog_work);
}
avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks; avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
/* /*
...@@ -1224,7 +1241,8 @@ static void wl1271_recovery_work(struct work_struct *work) ...@@ -1224,7 +1241,8 @@ static void wl1271_recovery_work(struct work_struct *work)
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
BUG_ON(bug_on_recovery); BUG_ON(bug_on_recovery &&
!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
/* /*
* Advance security sequence number to overcome potential progress * Advance security sequence number to overcome potential progress
...@@ -1487,6 +1505,7 @@ int wl1271_plt_stop(struct wl1271 *wl) ...@@ -1487,6 +1505,7 @@ int wl1271_plt_stop(struct wl1271 *wl)
cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->netstack_work);
cancel_work_sync(&wl->recovery_work); cancel_work_sync(&wl->recovery_work);
cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->elp_work);
cancel_delayed_work_sync(&wl->tx_watchdog_work);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
wl1271_power_off(wl); wl1271_power_off(wl);
...@@ -1528,7 +1547,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -1528,7 +1547,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
goto out; goto out;
} }
wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d",
hlid, q, skb->len);
skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
wl->tx_queue_count[q]++; wl->tx_queue_count[q]++;
...@@ -1626,10 +1646,6 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) ...@@ -1626,10 +1646,6 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
} }
static struct notifier_block wl1271_dev_notifier = {
.notifier_call = wl1271_dev_notify,
};
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int wl1271_configure_suspend_sta(struct wl1271 *wl, static int wl1271_configure_suspend_sta(struct wl1271 *wl,
struct wl12xx_vif *wlvif) struct wl12xx_vif *wlvif)
...@@ -1737,6 +1753,8 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, ...@@ -1737,6 +1753,8 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
WARN_ON(!wow || !wow->any); WARN_ON(!wow || !wow->any);
wl1271_tx_flush(wl);
wl->wow_enabled = true; wl->wow_enabled = true;
wl12xx_for_each_wlvif(wl, wlvif) { wl12xx_for_each_wlvif(wl, wlvif) {
ret = wl1271_configure_suspend(wl, wlvif); ret = wl1271_configure_suspend(wl, wlvif);
...@@ -1854,15 +1872,12 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) ...@@ -1854,15 +1872,12 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->state = WL1271_STATE_OFF; wl->state = WL1271_STATE_OFF;
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
mutex_lock(&wl_list_mutex);
list_del(&wl->list);
mutex_unlock(&wl_list_mutex);
wl1271_flush_deferred_work(wl); wl1271_flush_deferred_work(wl);
cancel_delayed_work_sync(&wl->scan_complete_work); cancel_delayed_work_sync(&wl->scan_complete_work);
cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->netstack_work);
cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->tx_work);
cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->elp_work);
cancel_delayed_work_sync(&wl->tx_watchdog_work);
/* let's notify MAC80211 about the remaining pending TX frames */ /* let's notify MAC80211 about the remaining pending TX frames */
wl12xx_tx_reset(wl, true); wl12xx_tx_reset(wl, true);
...@@ -2209,6 +2224,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -2209,6 +2224,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
if (wl12xx_need_fw_change(wl, vif_count, true)) { if (wl12xx_need_fw_change(wl, vif_count, true)) {
wl12xx_force_active_psm(wl); wl12xx_force_active_psm(wl);
set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
wl1271_recovery_work(&wl->recovery_work); wl1271_recovery_work(&wl->recovery_work);
return 0; return 0;
...@@ -2268,11 +2284,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -2268,11 +2284,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
out_unlock: out_unlock:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
mutex_lock(&wl_list_mutex);
if (!ret)
list_add(&wl->list, &wl_list);
mutex_unlock(&wl_list_mutex);
return ret; return ret;
} }
...@@ -2296,6 +2307,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, ...@@ -2296,6 +2307,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
if (wl->scan.state != WL1271_SCAN_STATE_IDLE && if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
wl->scan_vif == vif) { wl->scan_vif == vif) {
/*
* Rearm the tx watchdog just before idling scan. This
* prevents just-finished scans from triggering the watchdog
*/
wl12xx_rearm_tx_watchdog_locked(wl);
wl->scan.state = WL1271_SCAN_STATE_IDLE; wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL; wl->scan_vif = NULL;
...@@ -2398,6 +2415,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, ...@@ -2398,6 +2415,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
WARN_ON(iter != wlvif); WARN_ON(iter != wlvif);
if (wl12xx_need_fw_change(wl, vif_count, false)) { if (wl12xx_need_fw_change(wl, vif_count, false)) {
wl12xx_force_active_psm(wl); wl12xx_force_active_psm(wl);
set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
wl12xx_queue_recovery_work(wl); wl12xx_queue_recovery_work(wl);
cancel_recovery = false; cancel_recovery = false;
} }
...@@ -2417,7 +2435,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw, ...@@ -2417,7 +2435,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
wl1271_op_remove_interface(hw, vif); wl1271_op_remove_interface(hw, vif);
vif->type = ieee80211_iftype_p2p(new_type, p2p); vif->type = new_type;
vif->p2p = p2p; vif->p2p = p2p;
ret = wl1271_op_add_interface(hw, vif); ret = wl1271_op_add_interface(hw, vif);
...@@ -2596,35 +2614,22 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -2596,35 +2614,22 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_warning("rate policy for channel " wl1271_warning("rate policy for channel "
"failed %d", ret); "failed %d", ret);
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, /*
&wlvif->flags)) { * change the ROC channel. do it only if we are
if (wl12xx_dev_role_started(wlvif)) { * not idle. otherwise, CROC will be called
/* roaming */ * anyway.
ret = wl12xx_croc(wl, */
wlvif->dev_role_id); if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
if (ret < 0) &wlvif->flags) &&
return ret; wl12xx_dev_role_started(wlvif) &&
} !(conf->flags & IEEE80211_CONF_IDLE)) {
ret = wl1271_join(wl, wlvif, false); ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0) if (ret < 0)
wl1271_warning("cmd join on channel " return ret;
"failed %d", ret);
} else {
/*
* change the ROC channel. do it only if we are
* not idle. otherwise, CROC will be called
* anyway.
*/
if (wl12xx_dev_role_started(wlvif) &&
!(conf->flags & IEEE80211_CONF_IDLE)) {
ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0)
return ret;
ret = wl12xx_start_dev(wl, wlvif); ret = wl12xx_start_dev(wl, wlvif);
if (ret < 0) if (ret < 0)
return ret; return ret;
}
} }
} }
} }
...@@ -3151,8 +3156,6 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, ...@@ -3151,8 +3156,6 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct cfg80211_scan_request *req) struct cfg80211_scan_request *req)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret; int ret;
u8 *ssid = NULL; u8 *ssid = NULL;
size_t len = 0; size_t len = 0;
...@@ -3180,8 +3183,8 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, ...@@ -3180,8 +3183,8 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out; goto out;
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && /* fail if there is any role in ROC */
test_bit(wlvif->role_id, wl->roc_map)) { if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
/* don't allow scanning right now */ /* don't allow scanning right now */
ret = -EBUSY; ret = -EBUSY;
goto out_sleep; goto out_sleep;
...@@ -3221,6 +3224,13 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, ...@@ -3221,6 +3224,13 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
} }
/*
* Rearm the tx watchdog just before idling scan. This
* prevents just-finished scans from triggering the watchdog
*/
wl12xx_rearm_tx_watchdog_locked(wl);
wl->scan.state = WL1271_SCAN_STATE_IDLE; wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL; wl->scan_vif = NULL;
...@@ -3744,10 +3754,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3744,10 +3754,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
ibss_joined = true; ibss_joined = true;
} else { } else {
if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
&wlvif->flags)) { &wlvif->flags))
wl1271_unjoin(wl, wlvif); wl1271_unjoin(wl, wlvif);
wl12xx_start_dev(wl, wlvif);
}
} }
} }
...@@ -3765,7 +3773,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3765,7 +3773,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
do_join = true; do_join = true;
} }
if (changed & BSS_CHANGED_IDLE) { if (changed & BSS_CHANGED_IDLE && !is_ibss) {
ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
if (ret < 0) if (ret < 0)
wl1271_warning("idle mode change failed %d", ret); wl1271_warning("idle mode change failed %d", ret);
...@@ -3821,6 +3829,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3821,6 +3829,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
u32 rates; u32 rates;
int ieoffset; int ieoffset;
wlvif->aid = bss_conf->aid; wlvif->aid = bss_conf->aid;
wlvif->beacon_int = bss_conf->beacon_int;
set_assoc = true; set_assoc = true;
/* /*
...@@ -3901,7 +3910,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3901,7 +3910,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
/* restore the bssid filter and go to dummy bssid */ /* restore the bssid filter and go to dummy bssid */
if (was_assoc) { if (was_assoc) {
u32 conf_flags = wl->hw->conf.flags;
/* /*
* we might have to disable roc, if there was * we might have to disable roc, if there was
* no IF_OPER_UP notification. * no IF_OPER_UP notification.
...@@ -3924,7 +3932,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3924,7 +3932,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
} }
wl1271_unjoin(wl, wlvif); wl1271_unjoin(wl, wlvif);
if (!(conf_flags & IEEE80211_CONF_IDLE)) if (!bss_conf->idle)
wl12xx_start_dev(wl, wlvif); wl12xx_start_dev(wl, wlvif);
} }
} }
...@@ -3968,8 +3976,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3968,8 +3976,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
if (ret < 0) if (ret < 0)
goto out; goto out;
wl1271_check_operstate(wl, wlvif, if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
ieee80211_get_operstate(vif)); wl12xx_set_authorized(wl, wlvif);
} }
/* /*
* stop device role if started (we might already be in * stop device role if started (we might already be in
...@@ -4228,107 +4236,155 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) ...@@ -4228,107 +4236,155 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
clear_bit(hlid, wlvif->ap.sta_hlid_map); clear_bit(hlid, wlvif->ap.sta_hlid_map);
memset(wl->links[hlid].addr, 0, ETH_ALEN); memset(wl->links[hlid].addr, 0, ETH_ALEN);
wl->links[hlid].ba_bitmap = 0; wl->links[hlid].ba_bitmap = 0;
wl1271_tx_reset_link_queues(wl, hlid);
__clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, &wl->ap_ps_map);
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
wl12xx_free_link(wl, wlvif, &hlid); wl12xx_free_link(wl, wlvif, &hlid);
wl->active_sta_count--; wl->active_sta_count--;
/*
* rearm the tx watchdog when the last STA is freed - give the FW a
* chance to return STA-buffered packets before complaining.
*/
if (wl->active_sta_count == 0)
wl12xx_rearm_tx_watchdog_locked(wl);
} }
static int wl1271_op_sta_add(struct ieee80211_hw *hw, static int wl12xx_sta_add(struct wl1271 *wl,
struct ieee80211_vif *vif, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta) struct ieee80211_sta *sta)
{ {
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271_station *wl_sta; struct wl1271_station *wl_sta;
int ret = 0; int ret = 0;
u8 hlid; u8 hlid;
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
if (wlvif->bss_type != BSS_TYPE_AP_BSS)
goto out;
wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
ret = wl1271_allocate_sta(wl, wlvif, sta); ret = wl1271_allocate_sta(wl, wlvif, sta);
if (ret < 0) if (ret < 0)
goto out; return ret;
wl_sta = (struct wl1271_station *)sta->drv_priv; wl_sta = (struct wl1271_station *)sta->drv_priv;
hlid = wl_sta->hlid; hlid = wl_sta->hlid;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out_free_sta;
ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid); ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
if (ret < 0) if (ret < 0)
goto out_sleep; wl1271_free_sta(wl, wlvif, hlid);
ret = wl12xx_cmd_set_peer_state(wl, hlid); return ret;
if (ret < 0) }
goto out_sleep;
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid); static int wl12xx_sta_remove(struct wl1271 *wl,
if (ret < 0) struct wl12xx_vif *wlvif,
goto out_sleep; struct ieee80211_sta *sta)
{
struct wl1271_station *wl_sta;
int ret = 0, id;
out_sleep: wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
wl1271_ps_elp_sleep(wl);
out_free_sta: wl_sta = (struct wl1271_station *)sta->drv_priv;
id = wl_sta->hlid;
if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
return -EINVAL;
ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
if (ret < 0) if (ret < 0)
wl1271_free_sta(wl, wlvif, hlid); return ret;
out: wl1271_free_sta(wl, wlvif, wl_sta->hlid);
mutex_unlock(&wl->mutex);
return ret; return ret;
} }
static int wl1271_op_sta_remove(struct ieee80211_hw *hw, static int wl12xx_update_sta_state(struct wl1271 *wl,
struct ieee80211_vif *vif, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta) struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{ {
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271_station *wl_sta; struct wl1271_station *wl_sta;
int ret = 0, id; u8 hlid;
bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
int ret;
mutex_lock(&wl->mutex); wl_sta = (struct wl1271_station *)sta->drv_priv;
hlid = wl_sta->hlid;
if (unlikely(wl->state == WL1271_STATE_OFF)) /* Add station (AP mode) */
goto out; if (is_ap &&
old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE)
return wl12xx_sta_add(wl, wlvif, sta);
/* Remove station (AP mode) */
if (is_ap &&
old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
/* must not fail */
wl12xx_sta_remove(wl, wlvif, sta);
return 0;
}
if (wlvif->bss_type != BSS_TYPE_AP_BSS) /* Authorize station (AP mode) */
goto out; if (is_ap &&
new_state == IEEE80211_STA_AUTHORIZED) {
ret = wl12xx_cmd_set_peer_state(wl, hlid);
if (ret < 0)
return ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid); ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
hlid);
return ret;
}
wl_sta = (struct wl1271_station *)sta->drv_priv; /* Authorize station */
id = wl_sta->hlid; if (is_sta &&
if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map))) new_state == IEEE80211_STA_AUTHORIZED) {
set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
return wl12xx_set_authorized(wl, wlvif);
}
if (is_sta &&
old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
return 0;
}
return 0;
}
static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
sta->aid, old_state, new_state);
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) {
ret = -EBUSY;
goto out; goto out;
}
ret = wl1271_ps_elp_wakeup(wl); ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid); ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
if (ret < 0)
goto out_sleep;
wl1271_free_sta(wl, wlvif, wl_sta->hlid);
out_sleep:
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
if (new_state < old_state)
return 0;
return ret; return ret;
} }
...@@ -4497,6 +4553,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, ...@@ -4497,6 +4553,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch"); wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
wl1271_tx_flush(wl);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) { if (unlikely(wl->state == WL1271_STATE_OFF)) {
...@@ -4795,8 +4853,7 @@ static const struct ieee80211_ops wl1271_ops = { ...@@ -4795,8 +4853,7 @@ static const struct ieee80211_ops wl1271_ops = {
.conf_tx = wl1271_op_conf_tx, .conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf, .get_tsf = wl1271_op_get_tsf,
.get_survey = wl1271_op_get_survey, .get_survey = wl1271_op_get_survey,
.sta_add = wl1271_op_sta_add, .sta_state = wl12xx_op_sta_state,
.sta_remove = wl1271_op_sta_remove,
.ampdu_action = wl1271_op_ampdu_action, .ampdu_action = wl1271_op_ampdu_action,
.tx_frames_pending = wl1271_tx_frames_pending, .tx_frames_pending = wl1271_tx_frames_pending,
.set_bitrate_mask = wl12xx_set_bitrate_mask, .set_bitrate_mask = wl12xx_set_bitrate_mask,
...@@ -5117,8 +5174,6 @@ static int wl1271_register_hw(struct wl1271 *wl) ...@@ -5117,8 +5174,6 @@ static int wl1271_register_hw(struct wl1271 *wl)
wl1271_debugfs_init(wl); wl1271_debugfs_init(wl);
register_netdevice_notifier(&wl1271_dev_notifier);
wl1271_notice("loaded"); wl1271_notice("loaded");
out: out:
...@@ -5130,7 +5185,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl) ...@@ -5130,7 +5185,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
if (wl->plt) if (wl->plt)
wl1271_plt_stop(wl); wl1271_plt_stop(wl);
unregister_netdevice_notifier(&wl1271_dev_notifier);
ieee80211_unregister_hw(wl->hw); ieee80211_unregister_hw(wl->hw);
wl->mac80211_registered = false; wl->mac80211_registered = false;
...@@ -5251,7 +5305,6 @@ static struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -5251,7 +5305,6 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
wl = hw->priv; wl = hw->priv;
memset(wl, 0, sizeof(*wl)); memset(wl, 0, sizeof(*wl));
INIT_LIST_HEAD(&wl->list);
INIT_LIST_HEAD(&wl->wlvif_list); INIT_LIST_HEAD(&wl->wlvif_list);
wl->hw = hw; wl->hw = hw;
...@@ -5268,6 +5321,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -5268,6 +5321,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->tx_work, wl1271_tx_work);
INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
if (!wl->freezable_wq) { if (!wl->freezable_wq) {
......
...@@ -69,8 +69,6 @@ void wl1271_elp_work(struct work_struct *work) ...@@ -69,8 +69,6 @@ void wl1271_elp_work(struct work_struct *work)
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
#define ELP_ENTRY_DELAY 5
/* Routines to toggle sleep mode while in ELP */ /* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl) void wl1271_ps_elp_sleep(struct wl1271 *wl)
{ {
...@@ -90,7 +88,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) ...@@ -90,7 +88,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
} }
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
msecs_to_jiffies(ELP_ENTRY_DELAY)); msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
} }
int wl1271_ps_elp_wakeup(struct wl1271 *wl) int wl1271_ps_elp_wakeup(struct wl1271 *wl)
......
...@@ -55,6 +55,12 @@ void wl1271_scan_complete_work(struct work_struct *work) ...@@ -55,6 +55,12 @@ void wl1271_scan_complete_work(struct work_struct *work)
vif = wl->scan_vif; vif = wl->scan_vif;
wlvif = wl12xx_vif_to_data(vif); wlvif = wl12xx_vif_to_data(vif);
/*
* Rearm the tx watchdog just before idling scan. This
* prevents just-finished scans from triggering the watchdog
*/
wl12xx_rearm_tx_watchdog_locked(wl);
wl->scan.state = WL1271_SCAN_STATE_IDLE; wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.req = NULL; wl->scan.req = NULL;
......
...@@ -226,6 +226,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -226,6 +226,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl->tx_blocks_available -= total_blocks; wl->tx_blocks_available -= total_blocks;
wl->tx_allocated_blocks += total_blocks; wl->tx_allocated_blocks += total_blocks;
/* If the FW was empty before, arm the Tx watchdog */
if (wl->tx_allocated_blocks == total_blocks)
wl12xx_rearm_tx_watchdog_locked(wl);
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++; wl->tx_allocated_pkts[ac]++;
...@@ -527,6 +531,7 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, ...@@ -527,6 +531,7 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
if (skb) { if (skb) {
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags); spin_lock_irqsave(&wl->wl_lock, flags);
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--; wl->tx_queue_count[q]--;
spin_unlock_irqrestore(&wl->wl_lock, flags); spin_unlock_irqrestore(&wl->wl_lock, flags);
} }
...@@ -571,6 +576,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) ...@@ -571,6 +576,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
struct wl12xx_vif *wlvif = wl->last_wlvif; struct wl12xx_vif *wlvif = wl->last_wlvif;
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
/* continue from last wlvif (round robin) */
if (wlvif) { if (wlvif) {
wl12xx_for_each_wlvif_continue(wl, wlvif) { wl12xx_for_each_wlvif_continue(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif); skb = wl12xx_vif_skb_dequeue(wl, wlvif);
...@@ -581,7 +587,11 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) ...@@ -581,7 +587,11 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
} }
} }
/* do another pass */ /* dequeue from the system HLID before the restarting wlvif list */
if (!skb)
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
/* do a new pass over the wlvif list */
if (!skb) { if (!skb) {
wl12xx_for_each_wlvif(wl, wlvif) { wl12xx_for_each_wlvif(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif); skb = wl12xx_vif_skb_dequeue(wl, wlvif);
...@@ -589,12 +599,16 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) ...@@ -589,12 +599,16 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
wl->last_wlvif = wlvif; wl->last_wlvif = wlvif;
break; break;
} }
/*
* No need to continue after last_wlvif. The previous
* pass should have found it.
*/
if (wlvif == wl->last_wlvif)
break;
} }
} }
if (!skb)
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
if (!skb && if (!skb &&
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
int q; int q;
...@@ -602,6 +616,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) ...@@ -602,6 +616,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
skb = wl->dummy_packet; skb = wl->dummy_packet;
q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags); spin_lock_irqsave(&wl->wl_lock, flags);
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--; wl->tx_queue_count[q]--;
spin_unlock_irqrestore(&wl->wl_lock, flags); spin_unlock_irqrestore(&wl->wl_lock, flags);
} }
...@@ -959,7 +974,6 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -959,7 +974,6 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
else else
wlvif->sta.ba_rx_bitmap = 0; wlvif->sta.ba_rx_bitmap = 0;
wl1271_tx_reset_link_queues(wl, i);
wl->links[i].allocated_pkts = 0; wl->links[i].allocated_pkts = 0;
wl->links[i].prev_freed_pkts = 0; wl->links[i].prev_freed_pkts = 0;
} }
...@@ -973,8 +987,14 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) ...@@ -973,8 +987,14 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
for (i = 0; i < NUM_TX_QUEUES; i++) /* only reset the queues if something bad happened */
wl->tx_queue_count[i] = 0; if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);
for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_queue_count[i] = 0;
}
wl->stopped_queues_map = 0; wl->stopped_queues_map = 0;
...@@ -1024,6 +1044,7 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) ...@@ -1024,6 +1044,7 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
void wl1271_tx_flush(struct wl1271 *wl) void wl1271_tx_flush(struct wl1271 *wl)
{ {
unsigned long timeout; unsigned long timeout;
int i;
timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT); timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
while (!time_after(jiffies, timeout)) { while (!time_after(jiffies, timeout)) {
...@@ -1041,6 +1062,12 @@ void wl1271_tx_flush(struct wl1271 *wl) ...@@ -1041,6 +1062,12 @@ void wl1271_tx_flush(struct wl1271 *wl)
} }
wl1271_warning("Unable to flush all TX buffers, timed out."); wl1271_warning("Unable to flush all TX buffers, timed out.");
/* forcibly flush all Tx buffers on our queues */
mutex_lock(&wl->mutex);
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);
mutex_unlock(&wl->mutex);
} }
u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
......
...@@ -227,5 +227,6 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); ...@@ -227,5 +227,6 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
/* from main.c */ /* from main.c */
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl);
#endif #endif
...@@ -260,11 +260,13 @@ enum wl12xx_flags { ...@@ -260,11 +260,13 @@ enum wl12xx_flags {
WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RECOVERY_IN_PROGRESS, WL1271_FLAG_RECOVERY_IN_PROGRESS,
WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
WL1271_FLAG_INTENDED_FW_RECOVERY,
}; };
enum wl12xx_vif_flags { enum wl12xx_vif_flags {
WLVIF_FLAG_INITIALIZED, WLVIF_FLAG_INITIALIZED,
WLVIF_FLAG_STA_ASSOCIATED, WLVIF_FLAG_STA_ASSOCIATED,
WLVIF_FLAG_STA_AUTHORIZED,
WLVIF_FLAG_IBSS_JOINED, WLVIF_FLAG_IBSS_JOINED,
WLVIF_FLAG_AP_STARTED, WLVIF_FLAG_AP_STARTED,
WLVIF_FLAG_IN_PS, WLVIF_FLAG_IN_PS,
...@@ -452,8 +454,6 @@ struct wl1271 { ...@@ -452,8 +454,6 @@ struct wl1271 {
bool enable_11a; bool enable_11a;
struct list_head list;
/* Most recently reported noise in dBm */ /* Most recently reported noise in dBm */
s8 noise; s8 noise;
...@@ -495,6 +495,9 @@ struct wl1271 { ...@@ -495,6 +495,9 @@ struct wl1271 {
/* last wlvif we transmitted from */ /* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif; struct wl12xx_vif *last_wlvif;
/* work to fire when Tx is stuck */
struct delayed_work tx_watchdog_work;
}; };
struct wl1271_station { struct wl1271_station {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment