iwlwifi: mvm: Implement CQM offloading
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / mvm / rx.c
index 5057fd3bcb50da6bea1629fce9b38d0c33d96e72..2a8cb5a60535d161f410116c32967cbd9a983987 100644 (file)
@@ -167,6 +167,9 @@ static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
 
 /*
  * iwl_mvm_get_signal_strength - use new rx PHY INFO API
+ * values are reported by the fw as positive values - need to negate
+ * to obtain their dBM.  Account for missing antennas by replacing 0
+ * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
  */
 static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
                                        struct iwl_rx_phy_info *phy_info,
@@ -177,12 +180,15 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
 
        val =
            le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
-       energy_a = -((val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
-                                               IWL_RX_INFO_ENERGY_ANT_A_POS);
-       energy_b = -((val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
-                                               IWL_RX_INFO_ENERGY_ANT_B_POS);
-       energy_c = -((val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
-                                               IWL_RX_INFO_ENERGY_ANT_C_POS);
+       energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_A_POS;
+       energy_a = energy_a ? -energy_a : -256;
+       energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_B_POS;
+       energy_b = energy_b ? -energy_b : -256;
+       energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_C_POS;
+       energy_c = energy_c ? -energy_c : -256;
        max_energy = max(energy_a, energy_b);
        max_energy = max(max_energy, energy_c);
 
@@ -390,11 +396,62 @@ static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
        memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx));
 }
 
+struct iwl_mvm_stat_data {
+       struct iwl_notif_statistics *stats;
+       struct iwl_mvm *mvm;
+};
+
+static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
+                                 struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_stat_data *data = _data;
+       struct iwl_notif_statistics *stats = data->stats;
+       struct iwl_mvm *mvm = data->mvm;
+       int sig = -stats->general.beacon_filter_average_energy;
+       int last_event;
+       int thold = vif->bss_conf.cqm_rssi_thold;
+       int hyst = vif->bss_conf.cqm_rssi_hyst;
+       u16 id = le32_to_cpu(stats->rx.general.mac_id);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (mvmvif->id != id)
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       mvmvif->bf_data.ave_beacon_signal = sig;
+
+       if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+               return;
+
+       /* CQM Notification */
+       last_event = mvmvif->bf_data.last_cqm_event;
+       if (thold && sig < thold && (last_event == 0 ||
+                                    sig < last_event - hyst)) {
+               mvmvif->bf_data.last_cqm_event = sig;
+               IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
+                            sig);
+               ieee80211_cqm_rssi_notify(
+                       vif,
+                       NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                       GFP_KERNEL);
+       } else if (sig > thold &&
+                  (last_event == 0 || sig > last_event + hyst)) {
+               mvmvif->bf_data.last_cqm_event = sig;
+               IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
+                            sig);
+               ieee80211_cqm_rssi_notify(
+                       vif,
+                       NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                       GFP_KERNEL);
+       }
+}
+
 /*
  * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
  *
  * TODO: This handler is implemented partially.
- * It only gets the NIC's temperature.
  */
 int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
                          struct iwl_rx_cmd_buffer *rxb,
@@ -403,6 +460,10 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_notif_statistics *stats = (void *)&pkt->data;
        struct mvm_statistics_general_common *common = &stats->general.common;
+       struct iwl_mvm_stat_data data = {
+               .stats = stats,
+               .mvm = mvm,
+       };
 
        if (mvm->temperature != le32_to_cpu(common->temperature)) {
                mvm->temperature = le32_to_cpu(common->temperature);
@@ -410,5 +471,9 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
        }
        iwl_mvm_update_rx_statistics(mvm, stats);
 
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_stat_iterator,
+                                           &data);
        return 0;
 }
This page took 0.025696 seconds and 5 git commands to generate.