#include "debugfs.h"
#include "boot.h"
+static bool use_fw_ps = true;
+module_param(use_fw_ps, bool, 0644);
+MODULE_PARM_DESC(use_fw_ps, "Enable powersave once and leave it for chip's "
+ "firmware to manage. When disabled, mac80211 "
+ "will toggle powersave on tx activity instead. "
+ "Default: y/Y/1");
+
void wl1251_enable_interrupts(struct wl1251 *wl)
{
wl->if_ops->enable_irq(wl);
goto out_sleep;
}
+ if (intr & WL1251_ACX_INTR_TX_RESULT) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
+ wl1251_tx_complete(wl);
+ }
+
if (intr & WL1251_ACX_INTR_RX0_DATA) {
wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
wl1251_rx(wl);
+
+ if ((intr & WL1251_ACX_INTR_RX1_DATA)
+ && skb_queue_len(&wl->tx_queue) > 0)
+ wl1251_tx_work_unlocked(wl, false);
}
if (intr & WL1251_ACX_INTR_RX1_DATA) {
wl1251_rx(wl);
}
- if (intr & WL1251_ACX_INTR_TX_RESULT) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
- wl1251_tx_complete(wl);
- }
-
if (intr & WL1251_ACX_INTR_EVENT_A) {
wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A");
wl1251_event_handle(wl, 0);
wl1251_debug(DEBUG_IRQ,
"WL1251_ACX_INTR_INIT_COMPLETE");
- while (skb_queue_len(&wl->tx_queue) > 0
- && wl1251_tx_path_status(wl) == 0) {
-
- struct sk_buff *skb = skb_dequeue(&wl->tx_queue);
- if (skb == NULL)
- goto out_sleep;
-
- ret = wl1251_tx_frame(wl, skb);
- if (ret == -EBUSY) {
- skb_queue_head(&wl->tx_queue, skb);
- break;
- } else if (ret < 0) {
- dev_kfree_skb(skb);
- }
- }
+ if (skb_queue_len(&wl->tx_queue) > 0)
+ wl1251_tx_work_unlocked(wl, false);
if (--ctr == 0)
break;
if (ret < 0)
wl1251_warning("join timeout");
+ wl1251_no_ps_event(wl);
+
out:
return ret;
}
wl->rssi_thold = 0;
wl->channel = WL1251_DEFAULT_CHANNEL;
wl->monitor_present = false;
+ wl->joined = false;
+ wl->long_doze_mode_set = false;
wl1251_debugfs_reset(wl);
mutex_lock(&wl->mutex);
wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
wl->vif = NULL;
+ memset(wl->bssid, 0, ETH_ALEN);
mutex_unlock(&wl->mutex);
}
+static int wl1251_build_null_data(struct wl1251 *wl)
+{
+ struct sk_buff *skb = NULL;
+ int size;
+ void *ptr;
+ int ret = -ENOMEM;
+
+
+ if (wl->bss_type == BSS_TYPE_IBSS) {
+ size = sizeof(struct wl12xx_null_data_template);
+ ptr = NULL;
+ } else {
+ skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+ if (!skb)
+ goto out;
+ size = skb->len;
+ ptr = skb->data;
+ }
+
+ ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size);
+
+out:
+ dev_kfree_skb(skb);
+ if (ret)
+ wl1251_warning("cmd buld null data failed %d", ret);
+
+ return ret;
+
+}
+
static int wl1251_build_qos_null_data(struct wl1251 *wl)
{
struct ieee80211_qos_hdr template;
}
}
- need_ps = wl->psm_requested && !wl->bss_lost;
have_ps = wl->station_mode == STATION_POWER_SAVE_MODE;
+ need_ps = wl->psm_requested && !wl->bss_lost
+ && wl->rate < wl->ps_rate_threshold;
if (need_ps == have_ps) {
//wl1251_info("ps: already in mode %d", have_ps);
if (need_ps) {
wait = 0;
- diff = jiffies - wl->last_io_jiffies;
- if (diff < msecs_to_jiffies(150)) {
- //wl1251_info("ps: postponed psm, j %ld", diff);
- wait = msecs_to_jiffies(150) - diff + 1;
- }
+ diff = jiffies - wl->last_no_ps_jiffies[1];
+ if (diff < msecs_to_jiffies(1000))
+ wait = msecs_to_jiffies(1000) - diff + 1;
+
+ diff = jiffies - wl->last_no_ps_jiffies[0];
+ if (diff < msecs_to_jiffies(3000))
+ wait += msecs_to_jiffies(1000);
for (i = 0; i < ARRAY_SIZE(wl->tx_frames); i++) {
if (wl->tx_frames[i] != NULL) {
if (ret < 0)
goto out_sleep;
- //wl1251_info("psm %d, j %ld, d %ld", need_ps,
- // jiffies - wl->last_io_jiffies);
+ // wl1251_info("psm %d, r %u", need_ps, wl->rate);
out_sleep:
wl1251_ps_elp_sleep(wl);
mutex_unlock(&wl->mutex);
}
+static bool wl1251_can_do_pm(struct ieee80211_conf *conf, struct wl1251 *wl)
+{
+ return (conf->flags & IEEE80211_CONF_PS) && !wl->monitor_present;
+}
+
static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct wl1251 *wl = hw->priv;
if (channel != wl->channel) {
wl->channel = channel;
- ret = wl1251_join(wl, wl->bss_type, wl->channel,
- wl->beacon_int, wl->dtim_period);
+ /*
+ * Use ENABLE_RX command for channel switching when no
+ * interface is present (monitor mode only).
+ * This leaves the tx path disabled in firmware, whereas
+ * the usual JOIN command seems to transmit some frames
+ * at firmware level.
+ *
+ * Note that bss_type must be BSS_TYPE_STA_BSS, also at least
+ * one join has to be performed before CMD_ENABLE_RX can
+ * properly switch channels (join will be done by CONF_IDLE).
+ */
+ if (wl->vif == NULL) {
+ wl->bss_type = BSS_TYPE_STA_BSS;
+ wl->joined = false;
+ ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
+ } else {
+ ret = wl1251_join(wl, wl->bss_type, wl->channel,
+ wl->beacon_int, wl->dtim_period);
+ }
if (ret < 0)
goto out_sleep;
}
- if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
+ if (wl1251_can_do_pm(conf, wl) && !wl->psm_requested) {
wl1251_debug(DEBUG_PSM, "psm enabled");
wl->psm_requested = true;
wl->dtim_period = conf->ps_dtim_period;
ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
- } else if (!(conf->flags & IEEE80211_CONF_PS) &&
- wl->psm_requested) {
+ } else if (!wl1251_can_do_pm(conf, wl) && wl->psm_requested) {
wl1251_debug(DEBUG_PSM, "psm disabled");
wl->psm_requested = false;
ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels,
req->n_channels, WL1251_SCAN_NUM_PROBES);
if (ret < 0) {
+ wl1251_debug(DEBUG_SCAN, "scan failed %d", ret);
wl->scanning = false;
goto out_sleep;
}
{
struct wl1251 *wl = hw->priv;
struct sk_buff *beacon, *skb;
+ bool do_join = false;
bool enable;
int ret;
wl->rssi_thold = bss_conf->cqm_rssi_thold;
}
- if (changed & BSS_CHANGED_BSSID) {
+ if ((changed & BSS_CHANGED_BSSID) &&
+ memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
- skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
- if (!skb)
- goto out_sleep;
-
- ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA,
- skb->data, skb->len);
- dev_kfree_skb(skb);
- if (ret < 0)
- goto out_sleep;
-
- ret = wl1251_build_qos_null_data(wl);
- if (ret < 0)
- goto out;
+ if (!is_zero_ether_addr(wl->bssid)) {
+ ret = wl1251_build_null_data(wl);
+ if (ret < 0)
+ goto out_sleep;
- if (wl->bss_type != BSS_TYPE_IBSS) {
- ret = wl1251_join(wl, wl->bss_type, wl->channel,
- wl->beacon_int, wl->dtim_period);
+ ret = wl1251_build_qos_null_data(wl);
if (ret < 0)
goto out_sleep;
+
+ do_join = true;
}
}
if (ret < 0)
goto out_sleep;
- ret = wl1251_join(wl, wl->bss_type, wl->beacon_int,
- wl->channel, wl->dtim_period);
+ do_join = true;
+ }
+ if (do_join) {
+ ret = wl1251_join(wl, wl->bss_type, wl->channel,
+ wl->beacon_int, wl->dtim_period);
if (ret < 0)
goto out_sleep;
}
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_SUPPORTS_CQM_RSSI;
+ if (use_fw_ps)
+ wl->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
wl->hw->wiphy->max_scan_ssids = 1;
INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
wl->channel = WL1251_DEFAULT_CHANNEL;
wl->monitor_present = false;
+ wl->joined = false;
wl->scanning = false;
+ wl->bss_type = MAX_BSS_TYPE;
wl->default_key = 0;
wl->listen_int = 1;
wl->rx_counter = 0;
wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
wl->vif = NULL;
+ wl->ps_rate_threshold = 100000;
for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
wl->tx_frames[i] = NULL;