rsi: Adding support for host based bgscan.
authorJahnavi Meher <jahnavi.meher@gmail.com>
Mon, 16 Jun 2014 14:16:48 +0000 (19:46 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 19 Jun 2014 19:49:23 +0000 (15:49 -0400)
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>
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_main.h
drivers/net/wireless/rsi/rsi_mgmt.h

index 4700714e600f5a109effe9df3844bc87fa2e7770..1cb316417887d01bc85348d3d904a5dfd35c9b5e 100644 (file)
@@ -340,6 +340,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&common->mutex);
 }
 
+/**
+ * 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
@@ -357,17 +410,10 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
        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 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
                                      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);
 }
 
@@ -740,6 +795,37 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
        return 0;
 }
 
+/**
+ * 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.
@@ -755,6 +841,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                               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 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                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;
 }
 
 /**
index cbd5a7e7c73c6b2eb1b046ae175c1d2001e9d403..3ef343469c4ef0aa3e69b3f485026a7d30a542d9 100644 (file)
@@ -1200,6 +1200,49 @@ static int rsi_eeprom_read(struct rsi_common *common)
        return rsi_send_internal_mgmt_frame(common, skb);
 }
 
+/**
+ * This function sends a frame to block/unblock
+ * data queues in the firmware
+ *
+ * @param common Pointer to the driver private structure.
+ * @param block event - block if true, unblock if false
+ * @return 0 on success, -1 on failure.
+ */
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
+{
+       struct rsi_mac_frame *mgmt_frame;
+       struct sk_buff *skb;
+
+       rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__);
+
+       skb = dev_alloc_skb(FRAME_DESC_SZ);
+       if (!skb) {
+               rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       memset(skb->data, 0, FRAME_DESC_SZ);
+       mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+       mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+       mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
+
+       if (block_event == true) {
+               rsi_dbg(INFO_ZONE, "blocking the data qs\n");
+               mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
+       } else {
+               rsi_dbg(INFO_ZONE, "unblocking the data qs\n");
+               mgmt_frame->desc_word[5] = cpu_to_le16(0xf);
+       }
+
+       skb_put(skb, FRAME_DESC_SZ);
+
+       return rsi_send_internal_mgmt_frame(common, skb);
+
+}
+
+
 /**
  * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
  * @common: Pointer to the driver private structure.
index 86291310e293858e107f4d069afd683fc63b2660..5baed945f60e2cb276eeeaa7a520156132af4a55 100644 (file)
@@ -142,6 +142,12 @@ struct rsi_thread {
        atomic_t thread_done;
 };
 
+struct cqm_info {
+       s8 last_cqm_event_rssi;
+       int rssi_thold;
+       u32 rssi_hyst;
+};
+
 struct rsi_hw;
 
 struct rsi_common {
@@ -194,6 +200,9 @@ struct rsi_common {
        u32 pkt_cnt;
        u8 min_weight;
 
+       /* bgscan related */
+       struct cqm_info cqm_info;
+
        bool hw_data_qs_blocked;
 };
 
index f10d6c67e82f16534127091f21f65f0f493d1154..3741173fd3acea1132224caaeab02aba47d84b36 100644 (file)
@@ -168,7 +168,7 @@ enum cmd_frame_type {
        SCAN_REQUEST,
        TSF_UPDATE,
        PEER_NOTIFY,
-       BLOCK_UNBLOCK,
+       BLOCK_HW_QUEUE,
        SET_KEY_REQ,
        AUTO_RATE_IND,
        BOOTUP_PARAMS_REQUEST,
@@ -293,6 +293,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
 int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
                     u8 key_type, u8 key_id, u32 cipher);
 int rsi_set_channel(struct rsi_common *common, u16 chno);
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
 void rsi_inform_bss_status(struct rsi_common *common, u8 status,
                           const u8 *bssid, u8 qos_enable, u16 aid);
 void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);