static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; wl1271_event_mbox_dump(mbox); vector = mbox->events_vector & ~(mbox->events_mask); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { ret = wl1271_event_scan_complete(wl, mbox); if (ret < 0) return ret; } if (vector & BSS_LOSE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); if (wl->psm_requested && wl->psm) { ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; } } return 0; }
static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl) { int delay = wl->conf.conn.ps_poll_recovery_period; int ret; wl->ps_poll_failures++; if (wl->ps_poll_failures == 1) wl1271_info("AP with dysfunctional ps-poll, " "trying to work around it."); /* force active mode receive data from the AP */ if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, wl->basic_rate, true); if (ret < 0) return; set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags); ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work, msecs_to_jiffies(delay)); } /* * If already in active mode, lets we should be getting data from * the AP right away. If we enter PSM too fast after this, and data * remains on the AP, we will get another event like this, and we'll * go into active once more. */ }
void wl1271_pspoll_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, pspoll_work); wl1271_debug(DEBUG_EVENT, "pspoll work"); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags)) goto out; if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) goto out; /* * if we end up here, then we were in powersave when the pspoll * delivery failure occurred, and no-one changed state since, so * we should go back to powersave. */ wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); out: mutex_unlock(&wl->mutex); };
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; u32 total_retries = wl->conf.conn.psm_entry_retries; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM entry failed"); if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < total_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); } else { wl1271_info("No ack to nullfunc from AP."); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; /* enable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, true); if (ret < 0) break; /* * BET has only a minor effect in 5GHz and masks * channel switch IEs, so we only enable BET on 2.4GHz */ if (wl->band == IEEE80211_BAND_2GHZ) /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); if (wl->ps_compl) { complete(wl->ps_compl); wl->ps_compl = NULL; } break; default: break; } return ret; }
static ssize_t forced_ps_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret, ps_mode; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in forced_ps"); return -EINVAL; } if (value != 1 && value != 0) { wl1271_warning("forced_ps should be either 0 or 1"); return -ERANGE; } mutex_lock(&wl->mutex); if (wl->conf.conn.forced_ps == value) goto out; wl->conf.conn.forced_ps = value; if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* In case we're already in PSM, trigger it again to switch mode * immediately without waiting for re-association */ ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE; wl12xx_for_each_wlvif_sta(wl, wlvif) { if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, ps_mode); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; }
static ssize_t dynamic_ps_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in dynamic_ps"); return -EINVAL; } if (value < 1 || value > 65535) { wl1271_warning("dyanmic_ps_timeout is not in valid range"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.dynamic_ps_timeout = value; if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* In case we're already in PSM, trigger it again to set new timeout * immediately without waiting for re-association */ wl12xx_for_each_wlvif_sta(wl, wlvif) { if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; }
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: if (!wl->psm) { wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else { wl1271_error("PSM entry failed, giving up.\n"); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; break; case EVENT_EXIT_POWER_SAVE_FAIL: wl1271_info("PSM exit failed"); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } return ret; }
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { enum wl1271_cmd_ps_mode mode; struct wl1271 *wl = hw->priv; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->aid = bss_conf->aid; ret = wl1271_cmd_build_ps_poll(wl, wl->aid); if (ret < 0) goto out_sleep; ret = wl1271_acx_aid(wl, wl->aid); if (ret < 0) goto out_sleep; if (wl->psm_requested && !wl->psm) { mode = STATION_POWER_SAVE_MODE; ret = wl1271_ps_set_mode(wl, mode); if (ret < 0) goto out_sleep; } } } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); else ret = wl1271_acx_slot(wl, SLOT_TIME_LONG); if (ret < 0) { wl1271_warning("Set slot time failed %d", ret); goto out_sleep; } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); else wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { if (bss_conf->use_cts_prot) ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); else ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1271_warning("Set ctsprotect failed %d", ret); goto out_sleep; } } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); }
static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { u8 old_channel = wl->channel; wl->channel = channel; ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); if (ret < 0) { wl->channel = old_channel; goto out_sleep; } } ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); wl->psm_requested = true; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1271_info("psm disabled"); wl->psm_requested = false; if (wl->psm) ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); } if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out; wl->power_level = conf->power_level; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; u32 total_retries = wl->conf.conn.psm_entry_retries; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM entry failed"); if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < total_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); } else { wl1271_info("No ack to nullfunc from AP."); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; /* enable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, true); if (ret < 0) break; /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); if (ret < 0) break; /* go to extremely low power mode */ wl1271_ps_elp_sleep(wl); break; case EVENT_EXIT_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM exit failed"); if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { wl->psm_entry_retry = 0; break; } /* make sure the firmware goes to active mode - the frame to be sent next will indicate to the AP, that we are active. */ ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, wl->basic_rate, false); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } return ret; }
static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { u8 old_channel = wl->channel; wl->channel = channel; /* FIXME: use beacon interval provided by mac80211 */ ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); if (ret < 0) { wl->channel = old_channel; goto out_sleep; } } ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); wl->psm_requested = true; /* * We enter PSM only if we're already associated. * If we're not, we'll enter it when joining an SSID, * through the bss_info_changed() hook. */ ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1271_info("psm disabled"); wl->psm_requested = false; if (wl->psm) ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); } if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out; wl->power_level = conf->power_level; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }