Kaynağa Gözat

qcacmn: Add APIs to get info about elem/subelem fragment sequence

Add an API to get the following information for a first 802.11 element
present in the beginning of a given buffer, and a series of elements
after it in the given buffer: a) Whether a contiguous subset of these
elements starting with the first element form an element fragment
sequence. b) If they form an element fragment sequence, then the total
length of this sequence inclusive of headers and payloads of all the
elements in the sequence. c) If they form an element fragment
sequence, then the total size of the payloads of all the elements in
the sequence (not including the element ID extension of the lead
element, if applicable).

Similarly, add an API to get the following information for a first
802.11 subelement present in the beginning of a given buffer, and a
series of subelements after it in the given buffer: a) Whether a
contiguous subset of these subelements starting with the first
subelement form a subelement fragment sequence. b) If they form a
subelement fragment sequence, then the total length of this sequence
inclusive of headers and payloads of all the subelements in the
sequence. c) If they form a subelement fragment sequence, then the
total size of the payloads of all the subelements in the sequence.

Add common helper functionality shared between these APIs.

CRs-Fixed: 3124120
Change-Id: I26d2d75670dbb99c927517181d58f4d816ae754b
Krishna Rao 3 yıl önce
ebeveyn
işleme
8b712ebbaf

+ 131 - 0
umac/cmn_services/utils/inc/wlan_utility.h

@@ -350,6 +350,137 @@ QDF_STATUS wlan_create_subelem_fragseq(bool inline_frag,
 				       qdf_size_t fragbuff_maxsize,
 				       qdf_size_t *fragseqlen);
 
+/**
+ * wlan_get_elem_fragseq_info() - Get information about element fragment
+ * sequence
+ *
+ * @elembuff: Buffer containing a series of elements to be checked for whether a
+ * contiguous subset of these elements (starting with the first element in the
+ * buffer) form an element fragment sequence. The buffer should start with the
+ * Element ID of the first element. The buffer should not contain any material
+ * other than elements.
+ * @elembuff_maxsize: Maximum size of elembuff
+ * @is_fragseq: Pointer to location of a flag indicating whether this is an
+ * element fragment sequence or not. The flag will be set to true if elembuff
+ * contains an element fragment sequence starting with the element present in
+ * the beginning of the buffer, or the flag will be set to false if the buffer
+ * contains a single non-fragmented element in the beginning. Please note
+ * standards related limitation given in function description below.
+ * @fragseq_totallen: Pointer to location of total length of element fragment
+ * sequence. If is_fragseq is true, then this is set to the total length of the
+ * element fragment sequence, inclusive of the header and payload of the leading
+ * element and the headers and payloads of all subsequent fragments applicable
+ * to that element. If is_fragseq is false, the caller should ignore this.
+ * Please note standards related limitation given in function description below.
+ * @fragseq_payloadlen: Pointer to location of length of payload of element
+ * fragment sequence. If is_fragseq is true, then this length is set to the
+ * total size of the element fragment sequence payload, which does not include
+ * the sizes of the headers of the lead element and subsequent fragments, and
+ * which (if the lead element's element ID is WLAN_ELEMID_EXTN_ELEM) does not
+ * include the size of the lead element's element ID extension. If is_fragseq is
+ * false, the caller should ignore this. Please note standards related
+ * limitation given in function description below.
+ *
+ * Get the following information for a first element present in the beginning of
+ * a given buffer, and a series of elements after it in the given buffer: a)
+ * Whether a contiguous subset of these elements starting with the first element
+ * form an element fragment sequence. b) If they form an element fragment
+ * sequence, then the total length of this sequence inclusive of headers and
+ * payloads of all the elements in the sequence. c) If they form an element
+ * fragment sequence, then the total size of the payloads of all the elements in
+ * the sequence (not including the element ID extension of the lead element, if
+ * applicable). While determining this information, the function may return
+ * errors, including for protocol parsing issues. These protocol parsing issues
+ * include one in which the first element has a length lesser than 255, but the
+ * very next element after it is a fragment element (which is not allowed by the
+ * standard).  Separately, please note a limitation arising from the standard
+ * wherein if the caller passes a truncated maximum buffer size such that the
+ * buffer ends prematurely just at the end of a potential lead element with
+ * length 255 or just at the end of a non-lead fragment element with length 255,
+ * the function will have to conclude that the last successfully parsed element
+ * is the final one in the non-fragment or fragment sequence, and return results
+ * accordingly. If another fragment actually exists beyond the given buffer,
+ * this function cannot detect the condition since there is no provision in the
+ * standard to indicate a total fragment sequence size in one place in the
+ * beginning or anywhere else. Hence the caller should take care to provide the
+ * complete buffer with the max size set accordingly.
+ *
+ * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
+ * the reason for error in the case of failure
+ */
+QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
+				      qdf_size_t elembuff_maxsize,
+				      bool *is_fragseq,
+				      qdf_size_t *fragseq_totallen,
+				      qdf_size_t *fragseq_payloadlen);
+
+/**
+ * wlan_get_subelem_fragseq_info() - Get information about subelement fragment
+ * sequence
+ *
+ * @subelemid: Fragment ID applicable for the subelement (this can potentially
+ * vary across protocol areas)
+ * @subelembuff: Buffer containing a series of subelements to be checked for
+ * whether a contiguous subset of these subelements (starting with the first
+ * subelement in the buffer) form a subelement fragment sequence. The containing
+ * element is required to have already been defragmented (if applicable). The
+ * buffer should start with the subelement ID of the first subelement. The
+ * buffer should not contain any material apart from subelements.
+ * @subelembuff_maxsize: Maximum size of subelembuff
+ * @is_fragseq: Pointer to location of a flag indicating whether this is a
+ * subelement fragment sequence or not. The flag will be set to true if the
+ * buffer contains a subelement fragment sequence starting with the subelement
+ * present in the beginning of the buffer, or the flag will be set to false if
+ * the buffer contains a single non-fragmented subelement in the beginning.
+ * Please note standards related limitation given in function description below.
+ * @fragseq_totallen: Pointer to location of total length of subelement fragment
+ * sequence. If is_fragseq is true, then this is set to the total length of the
+ * subelement fragment sequence, inclusive of the header and payload of the
+ * leading subelement and the headers and payloads of all subsequent fragments
+ * applicable to that subelement. If is_fragseq is false, the caller should
+ * ignore this. Please note standards related limitation given in function
+ * description below.
+ * @fragseq_payloadlen: Pointer to location of length of payload of subelement
+ * fragment sequence. If is_fragseq is true, then this length is set to the
+ * total size of the subelement fragment sequence payload, which does not
+ * include the sizes of the headers of the lead subelement and subsequent
+ * fragments. If is_fragseq is false, the caller should ignore this. Please note
+ * standards related limitation given in function description below.
+ *
+ * Get the following information for a first subelement present in the beginning
+ * of a given buffer, and a series of subelements after it in the given buffer:
+ * a) Whether a contiguous subset of these subelements starting with the first
+ * subelement form a subelement fragment sequence. b) If they form a subelement
+ * fragment sequence, then the total length of this sequence inclusive of
+ * headers and payloads of all the subelements in the sequence. c) If they form
+ * a subelement fragment sequence, then the total size of the payloads of all
+ * the subelements in the sequence.  While determining this information, the
+ * function may return errors, including for protocol parsing issues. These
+ * protocol parsing issues include one in which the first subelement has a
+ * length lesser than 255, but the very next subelement after it is a fragment
+ * subelement (which is not allowed by the standard so far). Separately, please
+ * note a limitation arising from the standard wherein if the caller passes a
+ * truncated maximum buffer size such that the buffer ends prematurely just at
+ * the end of a potential lead subelement with length 255 or just at the end of
+ * a non-lead fragment subelement with length 255, the function will have to
+ * conclude that the last successfully parsed subelement is the final one in the
+ * non-fragment or fragment sequence, and return results accordingly. If another
+ * fragment actually exists beyond the given buffer, this function cannot detect
+ * the condition since there is no provision in the standard to indicate a total
+ * fragment sequence size in one place in the beginning or anywhere else. Hence
+ * the caller should take care to provide the complete buffer with the max size
+ * set accordingly.
+ *
+ * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
+ * the reason for error in the case of failure
+ */
+QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
+					 uint8_t *subelembuff,
+					 qdf_size_t subelembuff_maxsize,
+					 bool *is_fragseq,
+					 qdf_size_t *fragseq_totallen,
+					 qdf_size_t *fragseq_payloadlen);
+
 /**
  * wlan_is_emulation_platform() - check if platform is emulation based
  * @phy_version - psoc nif phy_version

+ 349 - 0
umac/cmn_services/utils/src/wlan_utility.c

@@ -805,6 +805,324 @@ wlan_create_elemsubelem_fragseq(bool inline_frag,
 	return QDF_STATUS_SUCCESS;
 }
 
+static inline QDF_STATUS
+wlan_get_elemsubelem_successorfrag(bool is_subelem,
+				   uint8_t subelemfragid,
+				   uint8_t *curr_elemunit_ptr,
+				   uint8_t *buff,
+				   qdf_size_t buff_maxsize,
+				   uint8_t **successorfrag,
+				   qdf_size_t *successorfrag_totallen,
+				   qdf_size_t *successorfrag_payloadlen)
+{
+	/* elemunit, i.e. 'element unit' here refers to either an 802.11 element
+	 * or a 802.11 subelement.
+	 */
+	uint8_t elemunit_fragid;
+	qdf_size_t elemunit_hdrlen;
+	qdf_size_t elemunit_maxpayloadlen;
+	int elemunit_id_pos;
+	int elemunit_len_pos;
+
+	qdf_size_t curr_elemunit_totallen;
+
+	uint8_t *next_elemunit_ptr;
+	qdf_size_t next_elemunit_totallen;
+
+	QDF_STATUS ret;
+
+	/* This helper checks if the very next element unit after the current
+	 * one is a valid fragment element unit and that there is sufficient
+	 * space in the buffer for this next element, and if so, it returns a
+	 * pointer to this fragment element unit as well as the total length of
+	 * this fragment element unit and the length of the payload of this
+	 * fragment element unit.
+	 */
+
+	ret = wlan_get_elemunit_info(is_subelem,
+				     subelemfragid,
+				     &elemunit_fragid,
+				     &elemunit_hdrlen,
+				     &elemunit_maxpayloadlen,
+				     &elemunit_id_pos,
+				     &elemunit_len_pos,
+				     NULL);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		qdf_rl_nofl_err("Investigate error %d when trying to get element unit info",
+				ret);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!curr_elemunit_ptr) {
+		qdf_nofl_err("Pointer to current element unit is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!buff) {
+		qdf_nofl_err("Buffer for element units is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (buff_maxsize == 0) {
+		qdf_nofl_err("Max size of buffer for element units is 0");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!successorfrag) {
+		qdf_nofl_err("Pointer to pointer to successor fragment is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!successorfrag_totallen) {
+		qdf_nofl_err("Pointer to successor fragment's total length is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!successorfrag_payloadlen) {
+		qdf_nofl_err("Pointer to successor fragment's payload length is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if ((buff + buff_maxsize) < (curr_elemunit_ptr + elemunit_hdrlen)) {
+		qdf_rl_nofl_err("Space %zu octets after current element unit offset %zu in buffer for element units is lesser than header size of element unit %zu octets",
+				buff_maxsize - (curr_elemunit_ptr - buff),
+				curr_elemunit_ptr - buff,
+				elemunit_hdrlen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	curr_elemunit_totallen =
+		elemunit_hdrlen + curr_elemunit_ptr[elemunit_len_pos];
+
+	if ((buff + buff_maxsize) <
+		(curr_elemunit_ptr + curr_elemunit_totallen)) {
+		qdf_rl_nofl_err("Space %zu octets after current element unit offset %zu in buffer for element units is lesser than indicated total length of element unit %zu octets",
+				buff_maxsize - (curr_elemunit_ptr - buff),
+				curr_elemunit_ptr - buff,
+				curr_elemunit_totallen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	if ((buff + buff_maxsize) ==
+		(curr_elemunit_ptr + curr_elemunit_totallen)) {
+		/* We have reached the end of the buffer. There is no successor
+		 * fragment.
+		 */
+		*successorfrag = NULL;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	next_elemunit_ptr = curr_elemunit_ptr + curr_elemunit_totallen;
+
+	if ((buff + buff_maxsize) < (next_elemunit_ptr + elemunit_hdrlen)) {
+		qdf_rl_nofl_err("Space %zu octets after next element unit offset %zu in buffer for element units is lesser than header size of element unit %zu octets",
+				buff_maxsize - (next_elemunit_ptr - buff),
+				next_elemunit_ptr - buff,
+				elemunit_hdrlen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	next_elemunit_totallen =
+		elemunit_hdrlen + next_elemunit_ptr[elemunit_len_pos];
+
+	if ((buff + buff_maxsize) <
+		(next_elemunit_ptr + next_elemunit_totallen)) {
+		qdf_rl_nofl_err("Space %zu octets after next element unit offset %zu in buffer for element units is lesser than indicated total length of element unit %zu octets",
+				buff_maxsize - (next_elemunit_ptr - buff),
+				next_elemunit_ptr - buff,
+				next_elemunit_totallen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	if (next_elemunit_ptr[elemunit_id_pos] != elemunit_fragid) {
+		*successorfrag = NULL;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	/* We should not be seeing a successor fragment if the length of the
+	 * current element unit is lesser than the max.
+	 */
+	if (curr_elemunit_ptr[elemunit_len_pos] != elemunit_maxpayloadlen) {
+		qdf_rl_nofl_err("Potential successor fragment found though length %u octets of payload of current element unit is not equal to max payload length %zu",
+				curr_elemunit_ptr[elemunit_len_pos],
+				elemunit_maxpayloadlen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	if (next_elemunit_ptr[elemunit_len_pos] == 0) {
+		qdf_rl_nofl_err("Potential successor fragment's length is 0");
+		return QDF_STATUS_E_PROTO;
+	}
+
+	*successorfrag = next_elemunit_ptr;
+	*successorfrag_totallen = next_elemunit_totallen;
+	*successorfrag_payloadlen = next_elemunit_ptr[elemunit_len_pos];
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+wlan_get_elemsubelem_fragseq_info(bool is_subelem,
+				  uint8_t subelemfragid,
+				  uint8_t *buff,
+				  qdf_size_t buff_maxsize,
+				  bool *is_fragseq,
+				  qdf_size_t *fragseq_totallen,
+				  qdf_size_t *fragseq_payloadlen)
+{
+	/* elemunit, i.e. 'element unit' here refers to either an 802.11 element
+	 * or a 802.11 subelement.
+	 */
+	qdf_size_t elemunit_hdrlen;
+	int elemunit_id_pos;
+	int elemunit_len_pos;
+
+	uint8_t *curr_elemunit_ptr;
+	qdf_size_t curr_elemunit_totallen;
+
+	qdf_size_t fragseq_currtotallen;
+	qdf_size_t fragseq_currpayloadlen;
+
+	uint8_t *successorfrag;
+	qdf_size_t successorfrag_totallen;
+	qdf_size_t successorfrag_payloadlen;
+
+	QDF_STATUS ret;
+
+	/* Helper function to get element or subelement fragment sequence
+	 * information. Refer to the documentation of the public APIs which
+	 * call this helper, for more information. These APIs are mainly
+	 * wrappers over this helper.
+	 *
+	 * If this helper reports that an element fragment sequence is present,
+	 * it also serves to check for the sanity of various lengths and
+	 * protocol requirements related to the fragment sequence (either by
+	 * itself or though other helpers).
+	 */
+
+	ret = wlan_get_elemunit_info(is_subelem,
+				     0,
+				     NULL,
+				     &elemunit_hdrlen,
+				     NULL,
+				     &elemunit_id_pos,
+				     &elemunit_len_pos,
+				     NULL);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		qdf_rl_nofl_err("Investigate error %d when trying to get element unit info",
+				ret);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!buff) {
+		qdf_nofl_err("Buffer for element units is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (buff_maxsize == 0) {
+		qdf_nofl_err("Max size of buffer for element units is 0");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!is_fragseq) {
+		qdf_nofl_err("Pointer to location for status of fragment sequence presence is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!fragseq_totallen) {
+		qdf_nofl_err("Pointer to location for total length of fragment sequence is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!fragseq_payloadlen) {
+		qdf_nofl_err("Pointer to location of payload length of fragment sequence is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	curr_elemunit_ptr = buff;
+	fragseq_currtotallen = 0;
+	fragseq_currpayloadlen = 0;
+
+	if ((buff + buff_maxsize) < (curr_elemunit_ptr + elemunit_hdrlen)) {
+		qdf_rl_nofl_err("Space %zu octets after current element unit offset %zu in buffer for element units is lesser than header size of element unit %zu octets",
+				buff_maxsize - (curr_elemunit_ptr - buff),
+				curr_elemunit_ptr - buff,
+				elemunit_hdrlen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	curr_elemunit_totallen =
+		elemunit_hdrlen + curr_elemunit_ptr[elemunit_len_pos];
+
+	if ((buff + buff_maxsize) <
+		(curr_elemunit_ptr + curr_elemunit_totallen)) {
+		qdf_rl_nofl_err("Space %zu octets after current element unit offset %zu in buffer for element units is lesser than indicated total length of element unit %zu octets",
+				buff_maxsize - (curr_elemunit_ptr - buff),
+				curr_elemunit_ptr - buff,
+				curr_elemunit_totallen);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	successorfrag = NULL;
+
+	ret = wlan_get_elemsubelem_successorfrag(is_subelem,
+						 subelemfragid,
+						 curr_elemunit_ptr,
+						 buff,
+						 buff_maxsize,
+						 &successorfrag,
+						 &successorfrag_totallen,
+						 &successorfrag_payloadlen);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return ret;
+
+	if (!successorfrag) {
+		*is_fragseq = false;
+		*fragseq_totallen = 0;
+		*fragseq_payloadlen = 0;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	fragseq_currtotallen += curr_elemunit_totallen;
+
+	if (!is_subelem &&
+	    (curr_elemunit_ptr[elemunit_id_pos] == WLAN_ELEMID_EXTN_ELEM)) {
+		fragseq_currpayloadlen +=
+			(curr_elemunit_ptr[elemunit_len_pos] - 1);
+	} else {
+		fragseq_currpayloadlen +=
+			curr_elemunit_ptr[elemunit_len_pos];
+	}
+
+	while (successorfrag) {
+		/* wlan_get_elemsubelem_successorfrag() has already validated
+		 * the length values for the successor fragment.
+		 */
+		fragseq_currtotallen += successorfrag_totallen;
+		fragseq_currpayloadlen += successorfrag_payloadlen;
+
+		curr_elemunit_ptr = successorfrag;
+		successorfrag = NULL;
+
+		ret = wlan_get_elemsubelem_successorfrag(is_subelem,
+							 subelemfragid,
+							 curr_elemunit_ptr,
+							 buff,
+							 buff_maxsize,
+							 &successorfrag,
+							 &successorfrag_totallen,
+							 &successorfrag_payloadlen);
+		if (QDF_IS_STATUS_ERROR(ret))
+			return ret;
+	}
+
+	*is_fragseq = true;
+	*fragseq_totallen = fragseq_currtotallen;
+	*fragseq_payloadlen = fragseq_currpayloadlen;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 QDF_STATUS
 wlan_get_elem_fragseq_requirements(uint8_t elemid,
 				   qdf_size_t payloadlen,
@@ -885,6 +1203,37 @@ QDF_STATUS wlan_create_subelem_fragseq(bool inline_frag,
 						fragseqlen);
 }
 
+QDF_STATUS wlan_get_elem_fragseq_info(uint8_t *elembuff,
+				      qdf_size_t elembuff_maxsize,
+				      bool *is_fragseq,
+				      qdf_size_t *fragseq_totallen,
+				      qdf_size_t *fragseq_payloadlen)
+{
+	return wlan_get_elemsubelem_fragseq_info(false,
+						 0,
+						 elembuff,
+						 elembuff_maxsize,
+						 is_fragseq,
+						 fragseq_totallen,
+						 fragseq_payloadlen);
+}
+
+QDF_STATUS wlan_get_subelem_fragseq_info(uint8_t subelemfragid,
+					 uint8_t *subelembuff,
+					 qdf_size_t subelembuff_maxsize,
+					 bool *is_fragseq,
+					 qdf_size_t *fragseq_totallen,
+					 qdf_size_t *fragseq_payloadlen)
+{
+	return wlan_get_elemsubelem_fragseq_info(true,
+						 subelemfragid,
+						 subelembuff,
+						 subelembuff_maxsize,
+						 is_fragseq,
+						 fragseq_totallen,
+						 fragseq_payloadlen);
+}
+
 bool wlan_is_emulation_platform(uint32_t phy_version)
 {
 	if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||