lib: qmi: Add api to verify maximum length of a QMI message

The new API is added to verify the maximum length of a QMI message
embedded inside the message descriptor matches the expected maximum
length of the concerned QMI message. The expected maximum length of
the QMI message is calculated using the message descriptor which
describes all the elements in the structure.

Change-Id: I09602df410d9891d60f1502245ad055653f8f0f7
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
index 4c5f6d3..b1fd217 100644
--- a/include/linux/qmi_encdec.h
+++ b/include/linux/qmi_encdec.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -150,6 +150,15 @@
 int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
 		      void *in_buf, uint32_t in_buf_len);
 
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ *          descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc);
+
 #else
 static inline int qmi_kernel_encode(struct msg_desc *desc,
 				    void *out_buf, uint32_t out_buf_len,
@@ -164,6 +173,11 @@
 {
 	return -EOPNOTSUPP;
 }
+
+static inline bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+	return false;
+}
 #endif
 
 #endif
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
index 5c489cf..3f618cb 100644
--- a/lib/qmi_encdec.c
+++ b/lib/qmi_encdec.c
@@ -90,6 +90,71 @@
 			      int dec_level);
 
 /**
+ * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
+ * @ei_array: Struct info array describing the structure.
+ * @level: Level to identify the depth of the nested structures.
+ *
+ * @return: expected maximum length of the QMI message or 0 on failure.
+ */
+static int qmi_calc_max_msg_len(struct elem_info *ei_array,
+				int level)
+{
+	int max_msg_len = 0;
+	struct elem_info *temp_ei;
+
+	if (!ei_array)
+		return max_msg_len;
+
+	for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
+		/* Flag to identify the optional element is not encoded */
+		if (temp_ei->data_type == QMI_OPT_FLAG)
+			continue;
+
+		if (temp_ei->data_type == QMI_DATA_LEN) {
+			max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
+					sizeof(uint8_t) : sizeof(uint16_t));
+			continue;
+		} else if (temp_ei->data_type == QMI_STRUCT) {
+			max_msg_len += qmi_calc_max_msg_len(temp_ei->ei_array,
+							    (level + 1));
+		} else {
+			max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
+		}
+
+		/*
+		 * Type & Length info. not prepended for elements in the
+		 * nested structure.
+		 */
+		if (level == 1)
+			max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+	}
+	return max_msg_len;
+}
+
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ *          descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+	int calc_max_msg_len;
+
+	if (!desc)
+		return false;
+
+	calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+	if (calc_max_msg_len != desc->max_msg_len) {
+		pr_err("%s: Calc. len %d != Passed len %d\n",
+			__func__, calc_max_msg_len, desc->max_msg_len);
+		return false;
+	}
+	return true;
+}
+
+/**
  * qmi_kernel_encode() - Encode to QMI message wire format
  * @desc: Pointer to structure descriptor.
  * @out_buf: Buffer to hold the encoded QMI message.
@@ -103,6 +168,7 @@
 		      void *in_c_struct)
 {
 	int enc_level = 1;
+	int ret, calc_max_msg_len;
 
 	if (!desc || !desc->ei_array)
 		return -EINVAL;
@@ -113,8 +179,14 @@
 	if (desc->max_msg_len < out_buf_len)
 		return -ETOOSMALL;
 
-	return _qmi_kernel_encode(desc->ei_array, out_buf,
-				  in_c_struct, out_buf_len, enc_level);
+	ret = _qmi_kernel_encode(desc->ei_array, out_buf,
+				 in_c_struct, out_buf_len, enc_level);
+	if (ret == -ETOOSMALL) {
+		calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+		pr_err("%s: Calc. len %d != Out buf len %d\n",
+			__func__, calc_max_msg_len, out_buf_len);
+	}
+	return ret;
 }
 EXPORT_SYMBOL(qmi_kernel_encode);