iwlagn: dynamically allocate & reflect calibration data

This makes handling the calibration data more generic
and no longer requires updating IWL_CALIB_MAX when a
new uCode comes with more calibration packets. Since
we just copy the data back, there's also no need for
understanding which calibration we received -- we can
just reflect it back to the runtime uCode.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
index c7bcafa..4d02105 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c
@@ -88,19 +88,18 @@
 		.id = REPLY_PHY_CALIBRATION_CMD,
 		.flags = CMD_SYNC,
 	};
-	int i = 0;
+	struct iwl_calib_result *res;
 
-	for (i = 0; i < IWL_CALIB_MAX; i++) {
+	list_for_each_entry(res, &priv->calib_results, list) {
 		int ret;
 
-		if (!priv->calib_results[i].buf)
-			continue;
-		hcmd.len[0] = priv->calib_results[i].buf_len;
-		hcmd.data[0] = priv->calib_results[i].buf;
+		hcmd.len[0] = res->cmd_len;
+		hcmd.data[0] = &res->hdr;
 		hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
 		ret = iwl_trans_send_cmd(trans(priv), &hcmd);
 		if (ret) {
-			IWL_ERR(priv, "Error %d iteration %d\n", ret, i);
+			IWL_ERR(priv, "Error %d on calib cmd %d\n",
+				ret, res->hdr.op_code);
 			return ret;
 		}
 	}
@@ -108,28 +107,39 @@
 	return 0;
 }
 
-int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len)
+int iwl_calib_set(struct iwl_priv *priv,
+		  const struct iwl_calib_hdr *cmd, int len)
 {
-	if (res->buf_len != len) {
-		kfree(res->buf);
-		res->buf = kzalloc(len, GFP_ATOMIC);
-	}
-	if (unlikely(res->buf == NULL))
-		return -ENOMEM;
+	struct iwl_calib_result *res, *tmp;
 
-	res->buf_len = len;
-	memcpy(res->buf, buf, len);
+	res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
+		      GFP_ATOMIC);
+	if (!res)
+		return -ENOMEM;
+	memcpy(&res->hdr, cmd, len);
+	res->cmd_len = len;
+
+	list_for_each_entry(tmp, &priv->calib_results, list) {
+		if (tmp->hdr.op_code == res->hdr.op_code) {
+			list_replace(&tmp->list, &res->list);
+			kfree(tmp);
+			return 0;
+		}
+	}
+
+	/* wasn't in list already */
+	list_add_tail(&res->list, &priv->calib_results);
+
 	return 0;
 }
 
 void iwl_calib_free_results(struct iwl_priv *priv)
 {
-	int i;
+	struct iwl_calib_result *res, *tmp;
 
-	for (i = 0; i < IWL_CALIB_MAX; i++) {
-		kfree(priv->calib_results[i].buf);
-		priv->calib_results[i].buf = NULL;
-		priv->calib_results[i].buf_len = 0;
+	list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
+		list_del(&res->list);
+		kfree(res);
 	}
 }