iwlagn: implement loading a new firmware file type

The old firmware file type does not allow indicating
any firmware capabilities, which we frequently want
to make things easier.

This implements a new firmware type that is based on
a TLV structure, and adds a TLV for the maximum length
of probe requests in scans.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 65ae636..97184e1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1505,9 +1505,13 @@
 	iwl_write32(priv, CSR_RESET, 0);
 }
 
+struct iwlagn_ucode_capabilities {
+	u32 max_probe_length;
+};
 
 static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context);
-static int iwl_mac_setup_register(struct iwl_priv *priv);
+static int iwl_mac_setup_register(struct iwl_priv *priv,
+				  struct iwlagn_ucode_capabilities *capa);
 
 static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
 {
@@ -1619,6 +1623,114 @@
 	return 0;
 }
 
+static int iwlagn_wanted_ucode_alternative = 1;
+
+static int iwlagn_load_firmware(struct iwl_priv *priv,
+				const struct firmware *ucode_raw,
+				struct iwlagn_firmware_pieces *pieces,
+				struct iwlagn_ucode_capabilities *capa)
+{
+	struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data;
+	struct iwl_ucode_tlv *tlv;
+	size_t len = ucode_raw->size;
+	const u8 *data;
+	int wanted_alternative = iwlagn_wanted_ucode_alternative, tmp;
+	u64 alternatives;
+
+	if (len < sizeof(*ucode))
+		return -EINVAL;
+
+	if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC))
+		return -EINVAL;
+
+	/*
+	 * Check which alternatives are present, and "downgrade"
+	 * when the chosen alternative is not present, warning
+	 * the user when that happens. Some files may not have
+	 * any alternatives, so don't warn in that case.
+	 */
+	alternatives = le64_to_cpu(ucode->alternatives);
+	tmp = wanted_alternative;
+	if (wanted_alternative > 63)
+		wanted_alternative = 63;
+	while (wanted_alternative && !(alternatives & BIT(wanted_alternative)))
+		wanted_alternative--;
+	if (wanted_alternative && wanted_alternative != tmp)
+		IWL_WARN(priv,
+			 "uCode alternative %d not available, choosing %d\n",
+			 tmp, wanted_alternative);
+
+	priv->ucode_ver = le32_to_cpu(ucode->ver);
+	pieces->build = le32_to_cpu(ucode->build);
+	data = ucode->data;
+
+	len -= sizeof(*ucode);
+
+	while (len >= sizeof(*tlv)) {
+		u32 tlv_len;
+		enum iwl_ucode_tlv_type tlv_type;
+		u16 tlv_alt;
+		const u8 *tlv_data;
+
+		len -= sizeof(*tlv);
+		tlv = (void *)data;
+
+		tlv_len = le32_to_cpu(tlv->length);
+		tlv_type = le16_to_cpu(tlv->type);
+		tlv_alt = le16_to_cpu(tlv->alternative);
+		tlv_data = tlv->data;
+
+		if (len < tlv_len)
+			return -EINVAL;
+		len -= ALIGN(tlv_len, 4);
+		data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+		/*
+		 * Alternative 0 is always valid.
+		 *
+		 * Skip alternative TLVs that are not selected.
+		 */
+		if (tlv_alt != 0 && tlv_alt != wanted_alternative)
+			continue;
+
+		switch (tlv_type) {
+		case IWL_UCODE_TLV_INST:
+			pieces->inst = tlv_data;
+			pieces->inst_size = tlv_len;
+			break;
+		case IWL_UCODE_TLV_DATA:
+			pieces->data = tlv_data;
+			pieces->data_size = tlv_len;
+			break;
+		case IWL_UCODE_TLV_INIT:
+			pieces->init = tlv_data;
+			pieces->init_size = tlv_len;
+			break;
+		case IWL_UCODE_TLV_INIT_DATA:
+			pieces->init_data = tlv_data;
+			pieces->init_data_size = tlv_len;
+			break;
+		case IWL_UCODE_TLV_BOOT:
+			pieces->boot = tlv_data;
+			pieces->boot_size = tlv_len;
+			break;
+		case IWL_UCODE_TLV_PROBE_MAX_LEN:
+			if (tlv_len != 4)
+				return -EINVAL;
+			capa->max_probe_length =
+				le32_to_cpup((__le32 *)tlv_data);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (len)
+		return -EINVAL;
+
+	return 0;
+}
+
 /**
  * iwl_ucode_callback - callback when firmware was loaded
  *
@@ -1636,6 +1748,9 @@
 	u32 api_ver;
 	char buildstr[25];
 	u32 build;
+	struct iwlagn_ucode_capabilities ucode_capa = {
+		.max_probe_length = 200,
+	};
 
 	memset(&pieces, 0, sizeof(pieces));
 
@@ -1660,7 +1775,8 @@
 	if (ucode->ver)
 		err = iwlagn_load_legacy_firmware(priv, ucode_raw, &pieces);
 	else
-		err = -EINVAL;
+		err = iwlagn_load_firmware(priv, ucode_raw, &pieces,
+					   &ucode_capa);
 
 	if (err)
 		goto try_again;
@@ -1757,7 +1873,6 @@
 		goto try_again;
 	}
 
-
 	/* Allocate ucode buffers for card's bus-master loading ... */
 
 	/* Runtime instructions and 2 copies of data:
@@ -1841,7 +1956,7 @@
 	 *
 	 * 9. Setup and register with mac80211 and debugfs
 	 **************************************************/
-	err = iwl_mac_setup_register(priv);
+	err = iwl_mac_setup_register(priv, &ucode_capa);
 	if (err)
 		goto out_unbind;
 
@@ -2716,7 +2831,8 @@
  * Not a mac80211 entry point function, but it fits in with all the
  * other mac80211 functions grouped here.
  */
-static int iwl_mac_setup_register(struct iwl_priv *priv)
+static int iwl_mac_setup_register(struct iwl_priv *priv,
+				  struct iwlagn_ucode_capabilities *capa)
 {
 	int ret;
 	struct ieee80211_hw *hw = priv->hw;
@@ -2751,7 +2867,7 @@
 
 	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
 	/* we create the 802.11 header and a zero-length SSID element */
-	hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
+	hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 2;
 
 	/* Default value; 4 EDCA QOS priorities */
 	hw->queues = 4;
@@ -3974,3 +4090,8 @@
 module_param_named(
 	disable_hw_scan, iwlagn_mod_params.disable_hw_scan, int, S_IRUGO);
 MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
+
+module_param_named(ucode_alternative, iwlagn_wanted_ucode_alternative, int,
+		   S_IRUGO);
+MODULE_PARM_DESC(ucode_alternative,
+		 "specify ucode alternative to use from ucode file");