diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index f80e8c4..40040ad 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -552,7 +552,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 		sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
 	if (sdata->vif.bss_conf.use_short_slot)
 		sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-	sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
+	sinfo->bss_param.dtim_period = sdata->vif.ps_dtim_period;
 	sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
 
 	sinfo->sta_flags.set = 0;
@@ -1596,7 +1596,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
 	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
-		ieee80211_recalc_ps(local, -1);
+		ieee80211_recalc_ps(sdata);
 		ieee80211_recalc_ps_vif(sdata);
 	}
 
@@ -2532,10 +2532,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
 	sdata_unlock(sdata);
 
-	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-
-	ieee80211_recalc_ps(local, -1);
+	ieee80211_recalc_ps(sdata);
 	ieee80211_recalc_ps_vif(sdata);
 
 	return 0;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a489676..4b0750f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -394,6 +394,7 @@ struct ieee80211_if_managed {
 	struct work_struct csa_connection_drop_work;
 	struct work_struct dynamic_ps_enable_work;
 	struct work_struct dynamic_ps_disable_work;
+	enum ieee80211_vif_ps_mode offchannel_ps_mode;
 
 	unsigned long beacon_timeout;
 	unsigned long probe_timeout;
@@ -1186,12 +1187,6 @@ struct ieee80211_local {
 				*/
 
 	bool pspolling;
-	bool offchannel_ps_enabled;
-	/*
-	 * PS can only be enabled when we have exactly one managed
-	 * interface (and monitors) in PS, this then points there.
-	 */
-	struct ieee80211_sub_if_data *ps_sdata;
 	struct notifier_block network_latency_notifier;
 	struct notifier_block ifa_notifier;
 	struct notifier_block ifa6_notifier;
@@ -1362,7 +1357,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_disassoc_request *req);
 void ieee80211_send_pspoll(struct ieee80211_local *local,
 			   struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
+void ieee80211_recalc_ps(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mgd_recalc_ps(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mgd_notify_rx(struct ieee80211_rx_data *rx);
 int ieee80211_max_network_latency(struct notifier_block *nb,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 784b651..6444a50 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -682,7 +682,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 	if (hw_reconf_flags)
 		ieee80211_hw_config(local, hw_reconf_flags);
 
-	ieee80211_recalc_ps(local, -1);
+	ieee80211_ps_vif_open(sdata);
 
 	if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
@@ -943,6 +943,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 		return;
 	}
 
+	ieee80211_ps_vif_close(sdata);
+
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
 		break;
@@ -963,8 +965,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 			drv_remove_interface(local, sdata);
 	}
 
-	ieee80211_recalc_ps(local, -1);
-
 	if (local->open_count == 0) {
 		ieee80211_stop_device(local);
 
@@ -1609,6 +1609,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 		strlcpy(sdata->name, name, IFNAMSIZ);
 		ieee80211_assign_perm_addr(local, wdev->address, type);
 		memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
+		ieee80211_ps_init_vif(sdata);
 	} else {
 		if (local->hw.queues >= IEEE80211_NUM_ACS)
 			txqs = IEEE80211_NUM_ACS;
@@ -1644,6 +1645,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 		ndev->ieee80211_ptr = &sdata->wdev;
 		memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
 		memcpy(sdata->name, ndev->name, IFNAMSIZ);
+		ieee80211_ps_init_vif(sdata);
 
 		sdata->dev = ndev;
 	}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 4ec8c0a..6deb080 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1138,7 +1138,7 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
 static void ieee80211_enable_ps(struct ieee80211_local *local,
 				struct ieee80211_sub_if_data *sdata)
 {
-	struct ieee80211_conf *conf = &local->hw.conf;
+	struct ieee80211_vif *vif = &sdata->vif;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
 	/*
@@ -1148,36 +1148,38 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
 	if (local->scanning)
 		return;
 
-	if (conf->dynamic_ps_timeout > 0 &&
+	vif->dynamic_ps_active = vif->dynamic_ps_timeout > 0;
+	if (vif->dynamic_ps_active &&
 	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
 		mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
-			  msecs_to_jiffies(conf->dynamic_ps_timeout));
+			  msecs_to_jiffies(vif->dynamic_ps_timeout));
 	} else {
-		if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+		if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
+			ieee80211_vif_set_ps_mode(sdata,
+						  IEEE80211_VIF_PS_AWAKE_PM);
 			ieee80211_send_nullfunc(local, sdata, 1);
+		}
 
 		if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
 		    (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
 			return;
 
-		conf->flags |= IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_DOZE);
 	}
 }
 
 static void ieee80211_change_ps(struct ieee80211_sub_if_data *sdata, bool ps_enable)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_conf *conf = &local->hw.conf;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
 	if (ps_enable) {
 		ieee80211_enable_ps(local, sdata);
-	} else if (conf->flags & IEEE80211_CONF_PS) {
-		conf->flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	} else if (sdata->vif.ps_mode < IEEE80211_VIF_PS_AWAKE) {
 		del_timer_sync(&ifmgd->dynamic_ps_timer);
 		cancel_work_sync(&ifmgd->dynamic_ps_enable_work);
+		sdata->vif.dynamic_ps_active = false;
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE);
 	}
 }
 
@@ -1212,44 +1214,20 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
 }
 
 /* need to hold RTNL or interface lock */
-void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
+void ieee80211_mgd_recalc_ps(struct ieee80211_sub_if_data *sdata)
 {
-	struct ieee80211_sub_if_data *sdata, *old_ps_sdata, *found = NULL;
-	int count = 0;
-	int timeout;
-
-	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
-		local->ps_sdata = NULL;
-		return;
-	}
-
-	old_ps_sdata = local->ps_sdata;
-
-	list_for_each_entry(sdata, &local->interfaces, list) {
-		if (!ieee80211_sdata_running(sdata))
-			continue;
-		if (sdata->vif.type == NL80211_IFTYPE_AP) {
-			/* If an AP vif is found, then disable PS
-			 * by setting the count to zero thereby setting
-			 * ps_sdata to NULL.
-			 */
-			count = 0;
-			break;
-		}
-		if (sdata->vif.type != NL80211_IFTYPE_STATION)
-			continue;
-		found = sdata;
-		count++;
-	}
+	struct ieee80211_local *local = sdata->local;
+	bool ps_enable = false;
 
-	if (count == 1 && ieee80211_powersave_allowed(found)) {
+	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS &&
+	    ieee80211_powersave_allowed(sdata)) {
+		struct ieee80211_vif *vif = &sdata->vif;
+		int latency, timeout;
 		s32 beaconint_us;
 
-		if (latency < 0)
-			latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
-
+		latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
 		beaconint_us = ieee80211_tu_to_usec(
-					found->vif.bss_conf.beacon_int);
+					sdata->vif.bss_conf.beacon_int);
 
 		timeout = local->dynamic_ps_forced_timeout;
 		if (timeout < 0) {
@@ -1266,13 +1244,11 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 			else
 				timeout = 100;
 		}
-		local->hw.conf.dynamic_ps_timeout = timeout;
+		vif->dynamic_ps_timeout = timeout;
 
-		if (beaconint_us > latency) {
-			local->ps_sdata = NULL;
-		} else {
+		if (latency >= beaconint_us) {
 			int maxslp = 1;
-			u8 dtimper = found->u.mgd.dtim_period;
+			u8 dtimper = sdata->u.mgd.dtim_period;
 
 			/* If the TIM IE is invalid, pretend the value is 1 */
 			if (!dtimper)
@@ -1281,18 +1257,13 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 				maxslp = min_t(int, dtimper,
 						    latency / beaconint_us);
 
-			local->hw.conf.max_sleep_period = maxslp;
-			local->hw.conf.ps_dtim_period = dtimper;
-			local->ps_sdata = found;
+			vif->max_sleep_period = maxslp;
+			vif->ps_dtim_period = dtimper;
+			ps_enable = true;
 		}
-	} else {
-		local->ps_sdata = NULL;
 	}
 
-	if (local->ps_sdata)
-		ieee80211_change_ps(local->ps_sdata, true);
-	else if (old_ps_sdata)
-		ieee80211_change_ps(old_ps_sdata, false);
+	ieee80211_change_ps(sdata, ps_enable);
 }
 
 void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata)
@@ -1310,14 +1281,11 @@ static void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
 	struct ieee80211_sub_if_data *sdata =
 		container_of(work, struct ieee80211_sub_if_data,
 			     u.mgd.dynamic_ps_disable_work);
-	struct ieee80211_local *local = sdata->local;
 
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	}
+	if (sdata->vif.ps_mode < IEEE80211_VIF_PS_AWAKE)
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE);
 
-	ieee80211_wake_queues_by_reason(&local->hw,
+	ieee80211_wake_queues_by_reason(&sdata->local->hw,
 					IEEE80211_MAX_QUEUE_MAP,
 					IEEE80211_QUEUE_STOP_REASON_PS);
 }
@@ -1330,21 +1298,23 @@ static void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	unsigned long flags;
-	int q;
+	int q, n_queues;
 
-	/* can only happen when PS was just disabled anyway */
-	if (!local->ps_sdata)
+	if (!sdata->vif.dynamic_ps_active ||
+	    sdata->vif.ps_mode == IEEE80211_VIF_PS_DOZE)
 		return;
 
-	if (local->hw.conf.flags & IEEE80211_CONF_PS)
-		return;
-
-	if (local->hw.conf.dynamic_ps_timeout > 0) {
-		/* don't enter PS if TX frames are pending */
+	if (sdata->vif.dynamic_ps_timeout > 0) {
+		/*
+		 * don't enter PS if TX frames are pending
+		 *
+		 * XXX: Ideally we should be checking only for frames on
+		 * this interface.
+		 */
 		if (drv_tx_frames_pending(local)) {
 			mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
 				  msecs_to_jiffies(
-				  local->hw.conf.dynamic_ps_timeout));
+				  sdata->vif.dynamic_ps_timeout));
 			return;
 		}
 
@@ -1353,14 +1323,18 @@ static void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 		 * dynamic_ps_timer expiry. Postpone the ps timer if it
 		 * is not the actual idle state.
 		 */
+		n_queues = IEEE80211_NUM_ACS;
+		if (local->hw.queues < n_queues)
+			n_queues = 1;
+
 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-		for (q = 0; q < local->hw.queues; q++) {
+		for (q = 0; q < n_queues; q++) {
 			if (local->queue_stop_reasons[q]) {
 				spin_unlock_irqrestore(&local->queue_stop_reason_lock,
 						       flags);
 				mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
 					  msecs_to_jiffies(
-					  local->hw.conf.dynamic_ps_timeout));
+					  sdata->vif.dynamic_ps_timeout));
 				return;
 			}
 		}
@@ -1372,8 +1346,10 @@ static void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 		if (drv_tx_frames_pending(local)) {
 			mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
 				  msecs_to_jiffies(
-				  local->hw.conf.dynamic_ps_timeout));
+				  sdata->vif.dynamic_ps_timeout));
 		} else {
+			ieee80211_vif_set_ps_mode(sdata,
+						  IEEE80211_VIF_PS_AWAKE_PM);
 			ieee80211_send_nullfunc(local, sdata, 1);
 			/* Flush to get the tx status of nullfunc frame */
 			ieee80211_flush_queues(local, sdata);
@@ -1384,8 +1360,7 @@ static void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 	      (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) ||
 	    (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
 		ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
-		local->hw.conf.flags |= IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_DOZE);
 	}
 }
 
@@ -1403,15 +1378,16 @@ static void ieee80211_dynamic_ps_timer(unsigned long data)
 void ieee80211_mgd_notify_rx(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
+	struct ieee80211_vif *vif = &sdata->vif;
 	struct ieee80211_local *local = rx->local;
 
-	if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
+	if (vif->dynamic_ps_active &&
 	    !is_multicast_ether_addr(
 		    ((struct ethhdr *)rx->skb->data)->h_dest) &&
 	    (!local->scanning &&
 	     !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) {
 			mod_timer(&sdata->u.mgd.dynamic_ps_timer, jiffies +
-			 msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+				  msecs_to_jiffies(vif->dynamic_ps_timeout));
 	}
 }
 
@@ -1660,7 +1636,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
 	mutex_lock(&local->iflist_mtx);
-	ieee80211_recalc_ps(local, -1);
+	ieee80211_mgd_recalc_ps(sdata);
 	mutex_unlock(&local->iflist_mtx);
 
 	ieee80211_recalc_smps(sdata);
@@ -1695,11 +1671,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 	 * to do it before sending disassoc, as otherwise the null-packet
 	 * won't be valid.
 	 */
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	}
-	local->ps_sdata = NULL;
+	sdata->vif.dynamic_ps_active = false;
+	ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE);
 
 	/* disable per-vif ps */
 	ieee80211_recalc_ps_vif(sdata);
@@ -1804,7 +1777,7 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
 	__ieee80211_stop_poll(sdata);
 
 	mutex_lock(&local->iflist_mtx);
-	ieee80211_recalc_ps(local, -1);
+	ieee80211_mgd_recalc_ps(sdata);
 	mutex_unlock(&local->iflist_mtx);
 
 	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
@@ -1946,7 +1919,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
 		goto out;
 
 	mutex_lock(&sdata->local->iflist_mtx);
-	ieee80211_recalc_ps(sdata->local, -1);
+	ieee80211_mgd_recalc_ps(sdata);
 	mutex_unlock(&sdata->local->iflist_mtx);
 
 	ifmgd->probe_send_count = 0;
@@ -2921,12 +2894,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 							elems.tim_len,
 							ifmgd->aid);
 		if (directed_tim) {
-			if (local->hw.conf.dynamic_ps_timeout > 0) {
-				if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-					local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-					ieee80211_hw_config(local,
-							    IEEE80211_CONF_CHANGE_PS);
-				}
+			if (sdata->vif.dynamic_ps_active) {
+				ieee80211_vif_set_ps_mode(sdata,
+							  IEEE80211_VIF_PS_AWAKE);
 				ieee80211_send_nullfunc(local, sdata, 0);
 			} else if (!local->pspolling && sdata->u.mgd.powersave) {
 				local->pspolling = true;
@@ -3015,7 +2985,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 		ifmgd->have_beacon = true;
 
 		mutex_lock(&local->iflist_mtx);
-		ieee80211_recalc_ps(local, -1);
+		ieee80211_mgd_recalc_ps(sdata);
 		mutex_unlock(&local->iflist_mtx);
 
 		ieee80211_recalc_ps_vif(sdata);
@@ -3570,21 +3540,6 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
 	rcu_read_unlock();
 }
 
-int ieee80211_max_network_latency(struct notifier_block *nb,
-				  unsigned long data, void *dummy)
-{
-	s32 latency_usec = (s32) data;
-	struct ieee80211_local *local =
-		container_of(nb, struct ieee80211_local,
-			     network_latency_notifier);
-
-	mutex_lock(&local->iflist_mtx);
-	ieee80211_recalc_ps(local, latency_usec);
-	mutex_unlock(&local->iflist_mtx);
-
-	return 0;
-}
-
 static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
 				     struct cfg80211_bss *cbss)
 {
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 2049a0a..5802c00 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -29,8 +29,6 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-	local->offchannel_ps_enabled = false;
-
 	/* FIXME: what to do when local->pspolling is true? */
 
 	del_timer_sync(&ifmgd->dynamic_ps_timer);
@@ -39,13 +37,9 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 
 	cancel_work_sync(&ifmgd->dynamic_ps_enable_work);
 
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		local->offchannel_ps_enabled = true;
-		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	}
-
-	if (!local->offchannel_ps_enabled ||
+	ifmgd->offchannel_ps_mode = sdata->vif.ps_mode;
+	ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE_PM);
+	if (ifmgd->offchannel_ps_mode != IEEE80211_VIF_PS_DOZE ||
 	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
 		/*
 		 * If power save was enabled, no need to send a nullfunc
@@ -64,38 +58,23 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	enum ieee80211_vif_ps_mode mode = ifmgd->offchannel_ps_mode;
 
-	if (!local->ps_sdata)
-		ieee80211_send_nullfunc(local, sdata, 0);
-	else if (local->offchannel_ps_enabled) {
-		/*
-		 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
-		 * will send a nullfunc frame with the powersave bit set
-		 * even though the AP already knows that we are sleeping.
-		 * This could be avoided by sending a null frame with power
-		 * save bit disabled before enabling the power save, but
-		 * this doesn't gain anything.
-		 *
-		 * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
-		 * to send a nullfunc frame because AP already knows that
-		 * we are sleeping, let's just enable power save mode in
-		 * hardware.
-		 */
-		/* TODO:  Only set hardware if CONF_PS changed?
-		 * TODO:  Should we set offchannel_ps_enabled to false?
-		 */
-		local->hw.conf.flags |= IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
-		/*
-		 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
-		 * had been running before leaving the operating channel,
-		 * restart the timer now and send a nullfunc frame to inform
-		 * the AP that we are awake.
-		 */
+	/*
+	 * If mode is AWAKE_PM we may have started off-channel during a
+	 * transition to powersave. The AP should already think we're in
+	 * powersave, so go straight to DOZE.
+	 */
+	if (mode == IEEE80211_VIF_PS_AWAKE_PM)
+		mode = IEEE80211_VIF_PS_DOZE;
+
+	ieee80211_vif_set_ps_mode(sdata, mode);
+	if (mode == IEEE80211_VIF_PS_AWAKE) {
 		ieee80211_send_nullfunc(local, sdata, 0);
-		mod_timer(&sdata->u.mgd.dynamic_ps_timer, jiffies +
-			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+		if (sdata->vif.dynamic_ps_active)
+			mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
+				  msecs_to_jiffies(sdata->vif.dynamic_ps_timeout));
 	}
 
 	ieee80211_sta_reset_beacon_monitor(sdata);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4d73c46..b0e538e 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -345,6 +345,8 @@ EXPORT_SYMBOL(ieee80211_scan_completed);
 
 static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 {
+	struct ieee80211_sub_if_data *sdata;
+
 	/* Software scan is not supported in multi-channel cases */
 	if (local->use_chanctx)
 		return -EOPNOTSUPP;
@@ -378,6 +380,11 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 	/* We need to set power level at maximum rate for scanning. */
 	ieee80211_hw_config(local, 0);
 
+	sdata = rcu_dereference_protected(local->scan_sdata,
+					  lockdep_is_held(&local->mtx));
+	if (sdata && sdata->vif.ps_mode != IEEE80211_VIF_PS_AWAKE)
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE);
+
 	ieee80211_queue_delayed_work(&local->hw,
 				     &local->scan_work, 0);
 
@@ -726,6 +733,8 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
 static void ieee80211_scan_state_resume(struct ieee80211_local *local,
 					unsigned long *next_delay)
 {
+	struct ieee80211_sub_if_data *sdata;
+
 	ieee80211_offchannel_stop_vifs(local);
 
 	if (local->ops->flush) {
@@ -737,6 +746,11 @@ static void ieee80211_scan_state_resume(struct ieee80211_local *local,
 	/* remember when we left the operating channel */
 	local->leave_oper_channel_time = jiffies;
 
+	sdata = rcu_dereference_protected(local->scan_sdata,
+					  lockdep_is_held(&local->mtx));
+	if (sdata && sdata->vif.ps_mode != IEEE80211_VIF_PS_AWAKE)
+		ieee80211_vif_set_ps_mode(sdata, IEEE80211_VIF_PS_AWAKE);
+
 	/* advance to the next channel to be scanned */
 	local->next_scan_state = SCAN_SET_CHANNEL;
 }
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 3298fe9..5e001e0 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -732,15 +732,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 			local->dot11FailedCount++;
 	}
 
+	sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
 	if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
 	    (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
 	    !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
-	    local->ps_sdata && !(local->scanning)) {
-		if (info->flags & IEEE80211_TX_STAT_ACK) {
-			local->ps_sdata->u.mgd.flags |=
-					IEEE80211_STA_NULLFUNC_ACKED;
-		} else
-			mod_timer(&local->ps_sdata->u.mgd.dynamic_ps_timer,
+	    sdata->vif.ps_mode < IEEE80211_VIF_PS_AWAKE &&
+	    !(local->scanning)) {
+		if (info->flags & IEEE80211_TX_STAT_ACK)
+			sdata->u.mgd.flags |= IEEE80211_STA_NULLFUNC_ACKED;
+		else
+			mod_timer(&sdata->u.mgd.dynamic_ps_timer,
 				  jiffies + msecs_to_jiffies(10));
 	}
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 0ffc2066..995fae4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -198,6 +198,7 @@ static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_local *local = tx->local;
+	struct ieee80211_sub_if_data *sdata = tx->sdata;
 	struct ieee80211_if_managed *ifmgd;
 
 	/* driver doesn't support power save */
@@ -209,14 +210,15 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 		return TX_CONTINUE;
 
 	/* dynamic power save disabled */
-	if (local->hw.conf.dynamic_ps_timeout <= 0)
+	if (sdata->vif.dynamic_ps_timeout <= 0)
 		return TX_CONTINUE;
 
 	/* we are scanning, don't enable power save */
 	if (local->scanning)
 		return TX_CONTINUE;
 
-	if (!local->ps_sdata)
+	if (!sdata->vif.dynamic_ps_active &&
+	    sdata->vif.ps_mode == IEEE80211_VIF_PS_AWAKE)
 		return TX_CONTINUE;
 
 	/* No point if we're going to suspend */
@@ -227,7 +229,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 	if (tx->sdata->vif.type != NL80211_IFTYPE_STATION)
 		return TX_CONTINUE;
 
-	ifmgd = &tx->sdata->u.mgd;
+	ifmgd = &sdata->u.mgd;
 
 	/*
 	 * Don't wakeup from power save if u-apsd is enabled, voip ac has
@@ -247,7 +249,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 	    skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO)
 		return TX_CONTINUE;
 
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+	if (sdata->vif.ps_mode == IEEE80211_VIF_PS_DOZE) {
 		ieee80211_stop_queues_by_reason(&local->hw,
 						IEEE80211_MAX_QUEUE_MAP,
 						IEEE80211_QUEUE_STOP_REASON_PS);
@@ -261,7 +263,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 		return TX_CONTINUE;
 
 	mod_timer(&ifmgd->dynamic_ps_timer, jiffies +
-		  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+		  msecs_to_jiffies(sdata->vif.dynamic_ps_timeout));
 
 	return TX_CONTINUE;
 }
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 010cd2c..9542295 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1664,7 +1664,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 		}
 	}
 
-	ieee80211_recalc_ps(local, -1);
+	ieee80211_recalc_ps(sdata);
 
 	/*
 	 * The sta might be in psm against the ap (e.g. because
@@ -1672,15 +1672,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 	 * explicitly send a null packet in order to make sure
 	 * it'll sync against the ap (and get out of psm).
 	 */
-	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
-		list_for_each_entry(sdata, &local->interfaces, list) {
-			if (sdata->vif.type != NL80211_IFTYPE_STATION)
-				continue;
-			if (!sdata->u.mgd.associated)
-				continue;
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (sdata->vif.type != NL80211_IFTYPE_STATION)
+			continue;
+		if (!sdata->u.mgd.associated)
+			continue;
+		if (sdata->vif.ps_mode != IEEE80211_VIF_PS_AWAKE)
+			continue;
 
-			ieee80211_send_nullfunc(local, sdata, 0);
-		}
+		ieee80211_send_nullfunc(local, sdata, 0);
 	}
 
 	/* APs are now beaconing, add back stations */
@@ -1795,6 +1795,17 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
 
+void ieee80211_recalc_ps(struct ieee80211_sub_if_data *sdata)
+{
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_STATION:
+		ieee80211_mgd_recalc_ps(sdata);
+		break;
+	default:
+		break;
+	}
+}
+
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
@@ -1835,6 +1846,24 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
 	mutex_unlock(&local->chanctx_mtx);
 }
 
+int ieee80211_max_network_latency(struct notifier_block *nb,
+				  unsigned long data, void *dummy)
+{
+	struct ieee80211_local *local =
+		container_of(nb, struct ieee80211_local,
+			     network_latency_notifier);
+	struct ieee80211_sub_if_data *sdata;
+
+	mutex_lock(&local->iflist_mtx);
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (ieee80211_sdata_running(sdata))
+			ieee80211_recalc_ps(sdata);
+	}
+	mutex_unlock(&local->iflist_mtx);
+
+	return 0;
+}
+
 static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
 {
 	int i;
