*     regular image.
  * @IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG: support getting more shared
  *     memory addresses from the firmware.
+ * @IWL_UCODE_TLV_CAPA_LQM_SUPPORT: supports Link Quality Measurement
  *
  * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
  */
        IWL_UCODE_TLV_CAPA_CTDP_SUPPORT                 = (__force iwl_ucode_tlv_capa_t)76,
        IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED             = (__force iwl_ucode_tlv_capa_t)77,
        IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG        = (__force iwl_ucode_tlv_capa_t)80,
+       IWL_UCODE_TLV_CAPA_LQM_SUPPORT                  = (__force iwl_ucode_tlv_capa_t)81,
 
        NUM_IWL_UCODE_TLV_CAPA
 #ifdef __CHECKER__
 
 /* Please keep this enum *SORTED* by hex value.
  * Needed for binary search, otherwise a warning will be triggered.
  */
+enum iwl_mac_conf_subcmd_ids {
+       LINK_QUALITY_MEASUREMENT_CMD = 0x1,
+       LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE,
+};
+
 enum iwl_phy_ops_subcmd_ids {
        CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
        CTDP_CONFIG_CMD = 0x03,
        LEGACY_GROUP = 0x0,
        LONG_GROUP = 0x1,
        SYSTEM_GROUP = 0x2,
+       MAC_CONF_GROUP = 0x3,
        PHY_OPS_GROUP = 0x4,
        DATA_PATH_GROUP = 0x5,
        PROT_OFFLOAD_GROUP = 0xb,
        u8 data[MAX_STORED_BEACON_SIZE];
 } __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_1 */
 
+#define LQM_NUMBER_OF_STATIONS_IN_REPORT 16
+
+enum iwl_lqm_cmd_operatrions {
+       LQM_CMD_OPERATION_START_MEASUREMENT = 0x01,
+       LQM_CMD_OPERATION_STOP_MEASUREMENT = 0x02,
+};
+
+enum iwl_lqm_status {
+       LQM_STATUS_SUCCESS = 0,
+       LQM_STATUS_TIMEOUT = 1,
+       LQM_STATUS_ABORT = 2,
+};
+
+/**
+ * Link Quality Measurement command
+ * @cmd_operatrion: command operation to be performed (start or stop)
+ *     as defined above.
+ * @mac_id: MAC ID the measurement applies to.
+ * @measurement_time: time of the total measurement to be performed, in uSec.
+ * @timeout: maximum time allowed until a response is sent, in uSec.
+ */
+struct iwl_link_qual_msrmnt_cmd {
+       __le32 cmd_operation;
+       __le32 mac_id;
+       __le32 measurement_time;
+       __le32 timeout;
+} __packed /* LQM_CMD_API_S_VER_1 */;
+
+/**
+ * Link Quality Measurement notification
+ *
+ * @frequent_stations_air_time: an array containing the total air time
+ *     (in uSec) used by the most frequently transmitting stations.
+ * @number_of_stations: the number of uniqe stations included in the array
+ *     (a number between 0 to 16)
+ * @total_air_time_other_stations: the total air time (uSec) used by all the
+ *     stations which are not included in the above report.
+ * @time_in_measurement_window: the total time in uSec in which a measurement
+ *     took place.
+ * @tx_frame_dropped: the number of TX frames dropped due to retry limit during
+ *     measurement
+ * @mac_id: MAC ID the measurement applies to.
+ * @status: return status. may be one of the LQM_STATUS_* defined above.
+ * @reserved: reserved.
+ */
+struct iwl_link_qual_msrmnt_notif {
+       __le32 frequent_stations_air_time[LQM_NUMBER_OF_STATIONS_IN_REPORT];
+       __le32 number_of_stations;
+       __le32 total_air_time_other_stations;
+       __le32 time_in_measurement_window;
+       __le32 tx_frame_dropped;
+       __le32 mac_id;
+       __le32 status;
+       __le32 reserved[3];
+} __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */
+
 #endif /* __fw_api_h__ */
 
        if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
 
+       if (changes & BSS_CHANGED_ASSOC && !bss_conf->assoc &&
+           mvmvif->lqm_active)
+               iwl_mvm_send_lqm_cmd(vif, LQM_CMD_OPERATION_STOP_MEASUREMENT,
+                                    0, 0);
+
        /*
         * If we're not associated yet, take the (new) BSSID before associating
         * so the firmware knows. If we're already associated, then use the old
 
                break;
        case NL80211_IFTYPE_STATION:
+               if (mvmvif->lqm_active)
+                       iwl_mvm_send_lqm_cmd(vif,
+                                            LQM_CMD_OPERATION_STOP_MEASUREMENT,
+                                            0, 0);
+
                /* Schedule the time event to a bit before beacon 1,
                 * to make sure we're in the new channel when the
                 * GO/AP arrives.
 
 
        /* TCP Checksum Offload */
        netdev_features_t features;
+
+       /*
+        * link quality measurement - used to check whether this interface
+        * is in the middle of a link quality measurement
+        */
+       bool lqm_active;
 };
 
 static inline struct iwl_mvm_vif *
 void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                             const char *errmsg);
 
+/* Link Quality Measurement */
+int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif,
+                        enum iwl_lqm_cmd_operatrions operation,
+                        u32 duration, u32 timeout);
+bool iwl_mvm_lqm_active(struct iwl_mvm *mvm);
+
 #endif /* __IWL_MVM_H__ */
 
        HCMD_NAME(SHARED_MEM_CFG_CMD),
 };
 
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = {
+       HCMD_NAME(LINK_QUALITY_MEASUREMENT_CMD),
+       HCMD_NAME(LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF),
+};
+
 /* Please keep this array *SORTED* by hex value.
  * Access is done through binary search
  */
        [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
        [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
        [SYSTEM_GROUP] = HCMD_ARR(iwl_mvm_system_names),
+       [MAC_CONF_GROUP] = HCMD_ARR(iwl_mvm_mac_conf_names),
        [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
        [DATA_PATH_GROUP] = HCMD_ARR(iwl_mvm_data_path_names),
        [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names),
 
 out:
        ieee80211_connection_loss(vif);
 }
+
+int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif,
+                        enum iwl_lqm_cmd_operatrions operation,
+                        u32 duration, u32 timeout)
+{
+       struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_link_qual_msrmnt_cmd cmd = {
+               .cmd_operation = cpu_to_le32(operation),
+               .mac_id = cpu_to_le32(mvm_vif->id),
+               .measurement_time = cpu_to_le32(duration),
+               .timeout = cpu_to_le32(timeout),
+       };
+       u32 cmdid =
+               iwl_cmd_id(LINK_QUALITY_MEASUREMENT_CMD, MAC_CONF_GROUP, 0);
+       int ret;
+
+       if (!fw_has_capa(&mvm_vif->mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_LQM_SUPPORT))
+               return -EOPNOTSUPP;
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return -EINVAL;
+
+       switch (operation) {
+       case LQM_CMD_OPERATION_START_MEASUREMENT:
+               if (iwl_mvm_lqm_active(mvm_vif->mvm))
+                       return -EBUSY;
+               if (!vif->bss_conf.assoc)
+                       return -EINVAL;
+               mvm_vif->lqm_active = true;
+               break;
+       case LQM_CMD_OPERATION_STOP_MEASUREMENT:
+               if (!iwl_mvm_lqm_active(mvm_vif->mvm))
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm_vif->mvm, cmdid, 0, sizeof(cmd),
+                                  &cmd);
+
+       /* command failed - roll back lqm_active state */
+       if (ret) {
+               mvm_vif->lqm_active =
+                       operation == LQM_CMD_OPERATION_STOP_MEASUREMENT;
+       }
+
+       return ret;
+}
+
+static void iwl_mvm_lqm_active_iterator(void *_data, u8 *mac,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
+       bool *lqm_active = _data;
+
+       *lqm_active = *lqm_active || mvm_vif->lqm_active;
+}
+
+bool iwl_mvm_lqm_active(struct iwl_mvm *mvm)
+{
+       bool ret = false;
+
+       lockdep_assert_held(&mvm->mutex);
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_lqm_active_iterator, &ret);
+
+       return ret;
+}