rsi: Adding support for host based bgscan.

Added support for host based bgscan. The h/w queues are blocked
while bgscan is being performed and after coming to the connected
channel, the queues are unblocked.

Signed-off-by: Jahnavi Meher <jahnavi.meher@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index 4700714..1cb3164 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -341,6 +341,59 @@
 }
 
 /**
+ * rsi_channel_change() - This function is a performs the checks
+ *			  required for changing a channel and sets
+ *			  the channel accordingly.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_channel_change(struct ieee80211_hw *hw)
+{
+	struct rsi_hw *adapter = hw->priv;
+	struct rsi_common *common = adapter->priv;
+	int status = -EOPNOTSUPP;
+	struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+	u16 channel = curchan->hw_value;
+	struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+	rsi_dbg(INFO_ZONE,
+		"%s: Set channel: %d MHz type: %d channel_no %d\n",
+		__func__, curchan->center_freq,
+		curchan->flags, channel);
+
+	if (bss->assoc) {
+		if (!common->hw_data_qs_blocked &&
+		    (rsi_get_connected_channel(adapter) != channel)) {
+			rsi_dbg(INFO_ZONE, "blk data q %d\n", channel);
+			if (!rsi_send_block_unblock_frame(common, true))
+				common->hw_data_qs_blocked = true;
+		}
+	}
+
+	status = rsi_band_check(common);
+	if (!status)
+		status = rsi_set_channel(adapter->priv, channel);
+
+	if (bss->assoc) {
+		if (common->hw_data_qs_blocked &&
+		    (rsi_get_connected_channel(adapter) == channel)) {
+			rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+			if (!rsi_send_block_unblock_frame(common, false))
+				common->hw_data_qs_blocked = false;
+		}
+	} else {
+		if (common->hw_data_qs_blocked) {
+			rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+			if (!rsi_send_block_unblock_frame(common, false))
+				common->hw_data_qs_blocked = false;
+		}
+	}
+
+	return status;
+}
+
+/**
  * rsi_mac80211_config() - This function is a handler for configuration
  *			   requests. The stack calls this function to
  *			   change hardware configuration, e.g., channel.
@@ -357,17 +410,10 @@
 	int status = -EOPNOTSUPP;
 
 	mutex_lock(&common->mutex);
-	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-		struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-		u16 channel = curchan->hw_value;
 
-		rsi_dbg(INFO_ZONE,
-			"%s: Set channel: %d MHz type: %d channel_no %d\n",
-			__func__, curchan->center_freq,
-			curchan->flags, channel);
-		common->band = curchan->band;
-		status = rsi_set_channel(adapter->priv, channel);
-	}
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+		status = rsi_channel_change(hw);
+
 	mutex_unlock(&common->mutex);
 
 	return status;
@@ -421,6 +467,15 @@
 				      bss_conf->qos,
 				      bss_conf->aid);
 	}
+
+	if (changed & BSS_CHANGED_CQM) {
+		common->cqm_info.last_cqm_event_rssi = 0;
+		common->cqm_info.rssi_thold = bss_conf->cqm_rssi_thold;
+		common->cqm_info.rssi_hyst = bss_conf->cqm_rssi_hyst;
+		rsi_dbg(INFO_ZONE, "RSSI throld & hysteresis are: %d %d\n",
+			common->cqm_info.rssi_thold,
+			common->cqm_info.rssi_hyst);
+	}
 	mutex_unlock(&common->mutex);
 }
 
@@ -741,6 +796,37 @@
 }
 
 /**
+ * rsi_perform_cqm() - This function performs cqm.
+ * @common: Pointer to the driver private structure.
+ * @bssid: pointer to the bssid.
+ * @rssi: RSSI value.
+ */
+static void rsi_perform_cqm(struct rsi_common *common,
+			    u8 *bssid,
+			    s8 rssi)
+{
+	struct rsi_hw *adapter = common->priv;
+	s8 last_event = common->cqm_info.last_cqm_event_rssi;
+	int thold = common->cqm_info.rssi_thold;
+	u32 hyst = common->cqm_info.rssi_hyst;
+	enum nl80211_cqm_rssi_threshold_event event;
+
+	if (rssi < thold && (last_event == 0 || rssi < (last_event - hyst)))
+		event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+	else if (rssi > thold &&
+		 (last_event == 0 || rssi > (last_event + hyst)))
+		event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+	else
+		return;
+
+	common->cqm_info.last_cqm_event_rssi = rssi;
+	rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event);
+	ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL);
+
+	return;
+}
+
+/**
  * rsi_fill_rx_status() - This function fills rx status in
  *			  ieee80211_rx_status structure.
  * @hw: Pointer to the ieee80211_hw structure.
@@ -755,6 +841,7 @@
 			       struct rsi_common *common,
 			       struct ieee80211_rx_status *rxs)
 {
+	struct ieee80211_bss_conf *bss = &common->priv->vifs[0]->bss_conf;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct skb_info *rx_params = (struct skb_info *)info->driver_data;
 	struct ieee80211_hdr *hdr;
@@ -789,6 +876,14 @@
 		rxs->flag |= RX_FLAG_DECRYPTED;
 		rxs->flag |= RX_FLAG_IV_STRIPPED;
 	}
+
+	/* CQM only for connected AP beacons, the RSSI is a weighted avg */
+	if (bss->assoc && !(memcmp(bss->bssid, hdr->addr2, ETH_ALEN))) {
+		if (ieee80211_is_beacon(hdr->frame_control))
+			rsi_perform_cqm(common, hdr->addr2, rxs->signal);
+	}
+
+	return;
 }
 
 /**