These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / wireless / iwlwifi / iwl-drv.c
index 7267152..463cadf 100644 (file)
@@ -6,7 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -372,6 +372,30 @@ static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
        return 0;
 }
 
+static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data,
+                               const u32 len)
+{
+       struct iwl_fw_gscan_capabilities *fw_capa = (void *)data;
+       struct iwl_gscan_capabilities *capa = &fw->gscan_capa;
+
+       if (len < sizeof(*fw_capa))
+               return -EINVAL;
+
+       capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size);
+       capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets);
+       capa->max_ap_cache_per_scan =
+               le32_to_cpu(fw_capa->max_ap_cache_per_scan);
+       capa->max_rssi_sample_size = le32_to_cpu(fw_capa->max_rssi_sample_size);
+       capa->max_scan_reporting_threshold =
+               le32_to_cpu(fw_capa->max_scan_reporting_threshold);
+       capa->max_hotlist_aps = le32_to_cpu(fw_capa->max_hotlist_aps);
+       capa->max_significant_change_aps =
+               le32_to_cpu(fw_capa->max_significant_change_aps);
+       capa->max_bssid_history_entries =
+               le32_to_cpu(fw_capa->max_bssid_history_entries);
+       return 0;
+}
+
 /*
  * Gets uCode section from tlv.
  */
@@ -423,13 +447,19 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
 {
        const struct iwl_ucode_api *ucode_api = (void *)data;
        u32 api_index = le32_to_cpu(ucode_api->api_index);
+       u32 api_flags = le32_to_cpu(ucode_api->api_flags);
+       int i;
 
-       if (api_index >= IWL_API_ARRAY_SIZE) {
+       if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
                IWL_ERR(drv, "api_index larger than supported by driver\n");
-               return -EINVAL;
+               /* don't return an error so we can load FW that has more bits */
+               return 0;
        }
 
-       capa->api[api_index] = le32_to_cpu(ucode_api->api_flags);
+       for (i = 0; i < 32; i++) {
+               if (api_flags & BIT(i))
+                       __set_bit(i + 32 * api_index, capa->_api);
+       }
 
        return 0;
 }
@@ -439,13 +469,19 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
 {
        const struct iwl_ucode_capa *ucode_capa = (void *)data;
        u32 api_index = le32_to_cpu(ucode_capa->api_index);
+       u32 api_flags = le32_to_cpu(ucode_capa->api_capa);
+       int i;
 
-       if (api_index >= IWL_CAPABILITIES_ARRAY_SIZE) {
+       if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
                IWL_ERR(drv, "api_index larger than supported by driver\n");
-               return -EINVAL;
+               /* don't return an error so we can load FW that has more bits */
+               return 0;
        }
 
-       capa->capa[api_index] = le32_to_cpu(ucode_capa->api_capa);
+       for (i = 0; i < 32; i++) {
+               if (api_flags & BIT(i))
+                       __set_bit(i + 32 * api_index, capa->_capa);
+       }
 
        return 0;
 }
@@ -561,13 +597,15 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        size_t len = ucode_raw->size;
        const u8 *data;
        u32 tlv_len;
+       u32 usniffer_img;
        enum iwl_ucode_tlv_type tlv_type;
        const u8 *tlv_data;
        char buildstr[25];
-       u32 build;
+       u32 build, paging_mem_size;
        int num_of_cpus;
        bool usniffer_images = false;
        bool usniffer_req = false;
+       bool gscan_capa = false;
 
        if (len < sizeof(*ucode)) {
                IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -943,12 +981,46 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                            IWL_UCODE_REGULAR_USNIFFER,
                                            tlv_len);
                        break;
+               case IWL_UCODE_TLV_PAGING:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       paging_mem_size = le32_to_cpup((__le32 *)tlv_data);
+
+                       IWL_DEBUG_FW(drv,
+                                    "Paging: paging enabled (size = %u bytes)\n",
+                                    paging_mem_size);
+
+                       if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) {
+                               IWL_ERR(drv,
+                                       "Paging: driver supports up to %lu bytes for paging image\n",
+                                       MAX_PAGING_IMAGE_SIZE);
+                               return -EINVAL;
+                       }
+
+                       if (paging_mem_size & (FW_PAGING_SIZE - 1)) {
+                               IWL_ERR(drv,
+                                       "Paging: image isn't multiple %lu\n",
+                                       FW_PAGING_SIZE);
+                               return -EINVAL;
+                       }
+
+                       drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size =
+                               paging_mem_size;
+                       usniffer_img = IWL_UCODE_REGULAR_USNIFFER;
+                       drv->fw.img[usniffer_img].paging_mem_size =
+                               paging_mem_size;
+                       break;
                case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
                        if (tlv_len != sizeof(u32))
                                goto invalid_tlv_len;
                        drv->fw.sdio_adma_addr =
                                le32_to_cpup((__le32 *)tlv_data);
                        break;
+               case IWL_UCODE_TLV_FW_GSCAN_CAPA:
+                       if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len))
+                               goto invalid_tlv_len;
+                       gscan_capa = true;
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@ -967,6 +1039,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                return -EINVAL;
        }
 
+       /*
+        * If ucode advertises that it supports GSCAN but GSCAN
+        * capabilities TLV is not present, warn and continue without GSCAN.
+        */
+       if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) &&
+           WARN(!gscan_capa,
+                "GSCAN is supported but capabilities TLV is unavailable\n"))
+               __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT,
+                           capa->_capa);
+
        return 0;
 
  invalid_tlv_len:
@@ -1148,7 +1230,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        if (err)
                goto try_again;
 
-       if (drv->fw.ucode_capa.api[0] & IWL_UCODE_TLV_API_NEW_VERSION)
+       if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION))
                api_ver = drv->fw.ucode_ver;
        else
                api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
@@ -1239,6 +1321,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
                sizeof(struct iwl_fw_dbg_trigger_txq_timer);
        trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
                sizeof(struct iwl_fw_dbg_trigger_time_event);
+       trigger_tlv_sz[FW_DBG_TRIGGER_BA] =
+               sizeof(struct iwl_fw_dbg_trigger_ba);
 
        for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
                if (pieces->dbg_trigger_tlv[i]) {