diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d66fbfe..6893cb9 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -873,6 +873,13 @@ enum mac80211_rate_control_flags {
 };
 
 
+/*
+ * How many frames need to have been used in average station's
+ * signal strength before checking against the threshold
+ */
+#define IEEE80211_STA_SIGNAL_AVE_MIN_COUNT	4
+
+
 /* there are 40 bytes if you don't need the rateset to be kept */
 #define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40
 
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ba6e408..b460051 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3938,6 +3938,113 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
 	return drv_abort_pmsr(local, sdata, request);
 }
 
+void sta_mon_rssi_config_free(struct sta_info *sta)
+{
+	struct sta_mon_rssi_config *old_rssi_config;
+
+	if (sta->rssi_config) {
+		old_rssi_config = sta->rssi_config;
+		RCU_INIT_POINTER(sta->rssi_config, NULL);
+		kfree_rcu(old_rssi_config, rcu_head);
+	}
+}
+
+void ieee80211_update_rssi_config(struct sta_info *sta)
+{
+	s32 last;
+	u32 hyst;
+	int i, n;
+
+	if (!sta->rssi_config || sta->rssi_config->fixed_thold)
+		return;
+
+	if (!sta->rssi_config->last_value)
+		sta->rssi_config->last_value =
+			-ewma_signal_read(&sta->rx_stats_avg.signal);
+
+	last = sta->rssi_config->last_value;
+	hyst = sta->rssi_config->hyst;
+	n = sta->rssi_config->n_thresholds;
+
+	for (i = 0; i < n; i++)
+		if (last < sta->rssi_config->thresholds[i])
+			break;
+
+	sta->rssi_config->low =
+		i > 0 ? (sta->rssi_config->thresholds[i - 1] - hyst) : S32_MIN;
+	sta->rssi_config->high =
+		i < n ? (sta->rssi_config->thresholds[i] + hyst - 1) : S32_MAX;
+}
+
+static int
+ieee80211_set_sta_mon_rssi_config(struct wiphy *wiphy,
+				  struct net_device *dev,
+				  const u8 *mac_addr,
+				  const struct cfg80211_sta_mon *sta_mon_cfg)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct sta_mon_rssi_config *rssi_config;
+	struct sta_info *sta;
+	int ret = 0;
+
+	mutex_lock(&local->sta_mtx);
+	rcu_read_lock();
+
+	if (mac_addr) {
+		sta = sta_info_get_bss(sdata, mac_addr);
+		if (!sta) {
+			ret = -ENOENT;
+			goto out;
+		}
+
+		if (sta_mon_cfg->fixed_thold &&
+		    sta_mon_cfg->n_tholds > 2) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		rssi_config = kzalloc(sizeof(*rssi_config) +
+				      sta_mon_cfg->n_tholds * sizeof(s32),
+				      GFP_KERNEL);
+		if (!rssi_config) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+		if (sta_mon_cfg->fixed_thold) {
+			sta_mon_rssi_config_free(sta);
+			if (sta_mon_cfg->n_tholds == 1) {
+				rssi_config->low = sta_mon_cfg->rssi_tholds[0];
+				rssi_config->high = sta_mon_cfg->rssi_tholds[0];
+			} else {
+				rssi_config->low = sta_mon_cfg->rssi_tholds[0];
+				rssi_config->high = sta_mon_cfg->rssi_tholds[1];
+			}
+			rssi_config->fixed_thold = sta_mon_cfg->fixed_thold;
+			rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+			rssi_config->n_thresholds = sta_mon_cfg->n_tholds;
+			sta->rssi_config = rssi_config;
+		} else {
+			sta_mon_rssi_config_free(sta);
+			rssi_config->n_thresholds = sta_mon_cfg->n_tholds;
+			memcpy(rssi_config->thresholds,
+			       sta_mon_cfg->rssi_tholds,
+			       rssi_config->n_thresholds * sizeof(s32));
+			rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+			sta->rssi_config = rssi_config;
+			/* Calculate low and high RSSI thresholds */
+			ieee80211_update_rssi_config(sta);
+		}
+	}
+
+out:
+	rcu_read_unlock();
+	mutex_unlock(&local->sta_mtx);
+	return ret;
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.del_virtual_intf = ieee80211_del_iface,
@@ -4035,4 +4142,5 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
 	.get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
 	.start_pmsr = ieee80211_start_pmsr,
 	.abort_pmsr = ieee80211_abort_pmsr,
+	.set_sta_mon_rssi_config = ieee80211_set_sta_mon_rssi_config,
 };
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4a03c18..de27a6f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1732,6 +1732,57 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid)
 	return RX_CONTINUE;
 }
 
+static void ieee80211_sta_rx_signal_thold_check(struct ieee80211_rx_data *rx)
+{
+	struct sta_info *sta = rx->sta;
+	struct ieee80211_bss_conf *bss_conf = &rx->sdata->vif.bss_conf;
+	bool rssi_cross =  false;
+
+	if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
+				NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG))
+		return;
+
+	if (!sta->rssi_config)
+		return;
+
+	rcu_read_lock();
+	sta->rssi_config->count_rx_signal++;
+	if (sta->rssi_config->count_rx_signal <
+	    IEEE80211_STA_SIGNAL_AVE_MIN_COUNT)
+		return;
+
+	if (sta->rssi_config->low && bss_conf->enable_beacon) {
+		int last_event = sta->rssi_config->last_value;
+		int sig = -ewma_signal_read(&sta->rx_stats_avg.signal);
+		int low = sta->rssi_config->low;
+		int high = sta->rssi_config->high;
+
+		if (sig < low &&
+		    (last_event == 0 || last_event >= low)) {
+			sta->rssi_config->last_value = sig;
+			cfg80211_sta_mon_rssi_notify(
+				rx->sdata->dev, sta->addr,
+				NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+				sig, GFP_ATOMIC);
+			rssi_cross = true;
+		} else if (sig > high &&
+			   (last_event == 0 || last_event <= high)) {
+			sta->rssi_config->last_value = sig;
+			cfg80211_sta_mon_rssi_notify(
+				rx->sdata->dev, sta->addr,
+				NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+				sig, GFP_ATOMIC);
+			rssi_cross = true;
+		}
+	}
+
+	if (rssi_cross) {
+		ieee80211_update_rssi_config(sta);
+		rssi_cross = false;
+	}
+	rcu_read_unlock();
+}
+
 static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 {
@@ -1787,6 +1838,7 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid)
 	if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
 		sta->rx_stats.last_signal = status->signal;
 		ewma_signal_add(&sta->rx_stats_avg.signal, -status->signal);
+		ieee80211_sta_rx_signal_thold_check(rx);
 	}
 
 	if (status->chains) {
@@ -4219,9 +4271,11 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
 	/* statistics part of ieee80211_rx_h_sta_process() */
 	if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
 		stats->last_signal = status->signal;
-		if (!fast_rx->uses_rss)
+		if (!fast_rx->uses_rss) {
 			ewma_signal_add(&sta->rx_stats_avg.signal,
 					-status->signal);
+			ieee80211_sta_rx_signal_thold_check(rx);
+		}
 	}
 
 	if (status->chains) {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a4932ee..ea22c2d 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1029,6 +1029,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
 
 	rate_control_remove_sta_debugfs(sta);
 	ieee80211_sta_debugfs_remove(sta);
+	sta_mon_rssi_config_free(sta);
 
 	cleanup_single_sta(sta);
 }
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 71f7e49..17b3726 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -427,6 +427,33 @@ struct ieee80211_sta_rx_stats {
 	u64 msdu[IEEE80211_NUM_TIDS + 1];
 };
 
+/**
+ * struct sta_mon_rssi_config - Monitor station's signal strength
+ * @rcu_head: rcu head for freeing structure
+ * @n_rssi_tholds: Number of thresholds passed by user
+ * @low: RSSI lower threshold for this station, a zero value implies
+ *	disabled
+ * @high: RSSI upper threshold for this station
+ * @hyst: RSSI hysteresis for this station
+ * @last_value: Last RSSI value for this station triggered the
+ *	RSSI cross event
+ * @fixed_thold - Indicate whether to use fixed thresholds limit or not
+ * @threholds: RSSI threshold limit passed by the user
+ * @count_rx_signal: Number of data frames used in averaging station signal.
+ *	This can be used to avoid generating less reliable station rssi cross
+ *	events that would be based only on couple of received frames
+ */
+struct sta_mon_rssi_config {
+	struct rcu_head rcu_head;
+	int n_thresholds;
+	s32 low, high;
+	u32 hyst;
+	s32 last_value;
+	u8 fixed_thold;
+	unsigned int count_rx_signal;
+	s32 thresholds[0];
+};
+
 /*
  * The bandwidth threshold below which the per-station CoDel parameters will be
  * scaled to be more lenient (to prevent starvation of slow stations). This
@@ -496,6 +523,7 @@ struct ieee80211_sta_rx_stats {
  * @fast_rx: RX fastpath information
  * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
  *	the BSS one.
+ * @rssi_config: RSSI threshold config parameters for this station
  * @tx_stats: TX statistics
  * @tx_stats.packets: # of packets transmitted
  * @tx_stats.bytes: # of bytes in all packets transmitted
@@ -623,6 +651,7 @@ struct sta_info {
 
 	struct cfg80211_chan_def tdls_chandef;
 
+	struct sta_mon_rssi_config __rcu *rssi_config;
 	/* keep last! */
 	struct ieee80211_sta sta;
 };
@@ -760,6 +789,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
 			  const u8 *addr);
 int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
 			      const u8 *addr);
+void sta_mon_rssi_config_free(struct sta_info *sta);
 
 void sta_info_recalc_tim(struct sta_info *sta);
 
@@ -792,6 +822,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 			  unsigned long exp_time);
 u8 sta_info_tx_streams(struct sta_info *sta);
+void ieee80211_update_rssi_config(struct sta_info *sta);
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
