Kaynağa Gözat

qcacmn: Add APIs to create elem/sublem fragment sequence

Add an API to create a sequence of 802.11 element fragments, and
similarly, add an API to create a sequence of 802.11 subelement
fragments. Add an inline mode for both these APIs to carry out the
fragmentation within the source buffer in order to reduce buffer
requirements and to eliminate memory copies/moves for the lead
element/subelement. Conversely, add a non-inline mode to create the
fragment sequence in a separate buffer.

Add common helper functionality shared between these APIs.

CRs-Fixed: 3124099
Change-Id: I48ef6e529360ea40d58320327a50884bc1638689
Krishna Rao 3 yıl önce
ebeveyn
işleme
b76c403d0e

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

@@ -194,6 +194,66 @@ wlan_get_elem_fragseq_requirements(uint8_t elemid,
 				   bool *is_frag_required,
 				   qdf_size_t *required_fragbuff_size);
 
+/**
+ * wlan_create_elem_fragseq() - Create sequence of element fragments
+ *
+ * @inline_frag: Whether to use inline fragmentation, wherein the fragmentation
+ * is carried out inline within the source buffer and no memmoves/memcopy would
+ * be required for the lead element.
+ * @elemid: Element ID
+ * @elemidext: Element ID extension. This is applicable only if elemid is
+ * WLAN_ELEMID_EXTN_ELEM, otherwise it is ignored.
+ * @payloadbuff: Buffer containing the element payload to be fragmented. If
+ * inline fragmentation is selected, the corresponding element fragment sequence
+ * will be generated inline into this buffer, and prior to the payload the
+ * buffer should have two bytes reserved in the beginning for the element ID and
+ * element length fields to be written, and a third byte reserved after them for
+ * the element ID extension to be written (if the element ID is
+ * WLAN_ELEMID_EXTN_ELEM).
+ * @payloadbuff_maxsize: Maximum size of payloadbuff
+ * @payloadlen: Length of element payload to be fragmented. Irrespective of
+ * whether inline fragmentation is to be used or not, this should not include
+ * the length of the element ID and element length, and if the element ID is
+ * WLAN_ELEMID_EXTN_ELEM, it should not include the length of the element ID
+ * extension.
+ * @fragbuff: The buffer into which the element fragment sequence should be
+ * generated. This is inapplicable and ignored if inline fragmentation is used.
+ * @fragbuff_maxsize: The maximum size of fragbuff. This is inapplicable and
+ * ignored if inline fragmentation is used.
+ * @fragseqlen: Pointer to location where the length of the fragment sequence
+ * created should be written. This is 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 the element ID is WLAN_ELEMID_EXTN_ELEM, this also includes the length of
+ * the element ID extension. The caller should ignore this if the function
+ * returns failure.
+ *
+ * Create a sequence of element fragments. In case fragmentation is not required
+ * for the given element ID and payload length, the function returns an error.
+ * This function is intended to be used by callers which do not have the ability
+ * (or for maintainability purposes do not desire the complexity) to inject new
+ * fragments on the fly where required, when populating the fields in the
+ * element (which would completely eliminate memory moves/copies). An inline
+ * mode is available to carry out the fragmentation within the source buffer in
+ * order to reduce buffer requirements and to eliminate memory copies/moves for
+ * the lead element. In the inline mode, the source buffer should have bytes
+ * reserved in the beginning for the element ID, element length, and if
+ * applicable, the element ID extension. In the inline mode the buffer content
+ * (if any) after the fragments is moved as well.
+ *
+ * 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_create_elem_fragseq(bool inline_frag,
+				    uint8_t elemid,
+				    uint8_t elemidext,
+				    uint8_t *payloadbuff,
+				    qdf_size_t payloadbuff_maxsize,
+				    qdf_size_t payloadlen,
+				    uint8_t *fragbuff,
+				    qdf_size_t fragbuff_maxsize,
+				    qdf_size_t *fragseqlen);
+
 /**
  * wlan_get_subelem_fragseq_requirements() - Get requirements related to
  * generation of subelement fragment sequence.
@@ -235,6 +295,61 @@ wlan_get_subelem_fragseq_requirements(uint8_t subelemid,
 				      bool *is_frag_required,
 				      qdf_size_t *required_fragbuff_size);
 
+/**
+ * wlan_create_subelem_fragseq() - Create sequence of subelement fragments
+ *
+ * @inline_frag: Whether to use inline fragmentation, wherein the fragmentation
+ * is carried out inline within the source buffer and no memmoves/memcopy would
+ * be required for the lead subelement.
+ * @subelemid: Subelement ID
+ * @subelemid: Fragment ID to be used for the subelement (this can potentially
+ * vary across protocol areas)
+ * @payloadbuff: Buffer containing the subelement payload to be fragmented. If
+ * inline fragmentation is selected, the corresponding subelement fragment
+ * sequence will be generated inline into this buffer, and prior to the payload
+ * the buffer should have two bytes reserved in the beginning for the subelement
+ * ID and subelement length fields to be written.
+ * @payloadbuff_maxsize: Maximum size of payloadbuff
+ * @payloadlen: Length of subelement payload to be fragmented. Irrespective of
+ * whether inline fragmentation is to be used or not, this should not include
+ * the length of the subelement ID and subelement length.
+ * @fragbuff: The buffer into which the subelement fragment sequence should be
+ * generated. This is inapplicable and ignored if inline fragmentation is used.
+ * @fragbuff_maxsize: The maximum size of fragbuff. This is inapplicable and
+ * ignored if inline fragmentation is used.
+ * @fragseqlen: Pointer to location where the length of the fragment sequence
+ * created should be written. This is 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. The caller should ignore this if the function
+ * returns failure.
+ *
+ * Create a sequence of subelement fragments. In case fragmentation is not
+ * required for the given payload length, the function returns an error. This
+ * function is intended to be used by callers which do not have the ability (or
+ * for maintainability purposes do not desire the complexity) to inject new
+ * fragments on the fly where required, when populating the fields in the
+ * subelement (which would completely eliminate memory moves/copies). An inline
+ * mode is available to carry out the fragmentation within the source buffer in
+ * order to reduce buffer requirements and to eliminate memory copies/moves for
+ * the lead subelement. In the inline mode, the source buffer should have bytes
+ * reserved in the beginning for the subelement ID and the subelement length. In
+ * the inline mode the buffer content (if any) after the fragments is moved as
+ * well.
+ *
+ * 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_create_subelem_fragseq(bool inline_frag,
+				       uint8_t subelemid,
+				       uint8_t subelemfragid,
+				       uint8_t *payloadbuff,
+				       qdf_size_t payloadbuff_maxsize,
+				       qdf_size_t payloadlen,
+				       uint8_t *fragbuff,
+				       qdf_size_t fragbuff_maxsize,
+				       qdf_size_t *fragseqlen);
+
 /**
  * wlan_is_emulation_platform() - check if platform is emulation based
  * @phy_version - psoc nif phy_version

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

@@ -499,6 +499,312 @@ wlan_get_elemsubelem_fragseq_creationparams(bool is_subelem,
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS
+wlan_create_elemsubelem_fragseq(bool inline_frag,
+				bool is_subelem,
+				uint8_t id,
+				uint8_t idext,
+				uint8_t subelemfragid,
+				uint8_t *payloadbuff,
+				qdf_size_t payloadbuff_maxsize,
+				qdf_size_t payloadlen,
+				uint8_t *fragbuff,
+				qdf_size_t fragbuff_maxsize,
+				qdf_size_t *fragseqlen)
+{
+	/* 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;
+	int elemunit_idext_pos;
+	uint8_t *curr_elemunit_ptr;
+
+	/* Whether fragmentation is required */
+	bool is_frag_required;
+
+	 /*Fragment sequence length (inclusive of payload and all headers) */
+	qdf_size_t expected_fragseqlen;
+
+	/* Number of fragments with the maximum size */
+	uint32_t num_maxsizefrags;
+	/* Size of the last fragment which is smaller than the maximum (if
+	 * present). If such a fragment is not present, this size will be zero.
+	 */
+	qdf_size_t smallerfrag_size;
+
+	 /* The number of extra header bytes that would be introduced (not
+	  * inclusive of the header of the lead fragment).
+	  */
+	qdf_size_t extrahdrbytes;
+	/* The number of extra header bytes remaining to be introduced */
+	qdf_size_t extrahdrbytes_remaining;
+
+	 /* The lead bytes that occur before the payload */
+	qdf_size_t prepayload_leadbytes;
+
+	/* Miscellaneous variables */
+	uint8_t *src;
+	uint8_t *dst;
+	uint16_t i;
+	qdf_size_t bytes_to_transfer;
+
+	QDF_STATUS ret;
+
+	/* Helper function to create an element or subelement fragment sequence.
+	 * Refer to the documentation of the public APIs which call this helper,
+	 * for more information. These APIs are mainly wrappers over this
+	 * helper.
+	 */
+
+	ret = wlan_get_elemunit_info(is_subelem,
+				     subelemfragid,
+				     &elemunit_fragid,
+				     &elemunit_hdrlen,
+				     &elemunit_maxpayloadlen,
+				     &elemunit_id_pos,
+				     &elemunit_len_pos,
+				     &elemunit_idext_pos);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		qdf_rl_nofl_err("Get elem unit info: Error %d",
+				ret);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	ret = wlan_get_elemsubelem_fragseq_creationparams(is_subelem,
+							  id,
+							  payloadlen,
+							  &is_frag_required,
+							  &expected_fragseqlen,
+							  &prepayload_leadbytes,
+							  &num_maxsizefrags,
+							  &smallerfrag_size,
+							  &extrahdrbytes);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return ret;
+
+	if (!is_frag_required) {
+		/* We treat this as an error since the caller is expected to
+		 * have first determined requirements related to fragmentation,
+		 * including whether fragmentation is required or not.
+		 */
+		if (!is_subelem && (id == WLAN_ELEMID_EXTN_ELEM))
+			qdf_nofl_err("Fragmentation inapplicable for elem with elem ID ext and post elem ID ext payload len %zu",
+				     payloadlen);
+		else
+			qdf_nofl_err("Fragmentation inapplicable for subelem/elem without elem ID ext and with payload len %zu",
+				     payloadlen);
+
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!payloadbuff) {
+		qdf_nofl_err("Payload buff is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (payloadbuff_maxsize == 0) {
+		qdf_nofl_err("Payload buff max size is 0");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (payloadbuff_maxsize < payloadlen) {
+		qdf_nofl_err("Payload buff max size %zu < payload len %zu",
+			     payloadbuff_maxsize,
+			     payloadlen);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (inline_frag) {
+		if (payloadbuff_maxsize < expected_fragseqlen) {
+			qdf_nofl_err("Inline frag buff max size %zu < frag sequence expected len %zu",
+				     payloadbuff_maxsize,
+				     expected_fragseqlen);
+			return QDF_STATUS_E_INVAL;
+		}
+	} else {
+		if (!fragbuff) {
+			qdf_nofl_err("Frag sequence buff is NULL");
+			return QDF_STATUS_E_NULL_VALUE;
+		}
+
+		if (fragbuff_maxsize == 0) {
+			qdf_nofl_err("Frag sequence buff max size is 0");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		if (fragbuff_maxsize < expected_fragseqlen) {
+			qdf_nofl_err("Frag sequence buff max size %zu < frag sequence expected len %zu",
+				     fragbuff_maxsize,
+				     expected_fragseqlen);
+			return QDF_STATUS_E_INVAL;
+		}
+	}
+
+	if (!fragseqlen) {
+		qdf_nofl_err("Pointer to location of frag sequence len is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	/* Preferably, ensure that error checks (if any) for future changes are
+	 * executed before this point. We wouldn't want to touch the destination
+	 * buffer unless we are sure we can successfully execute (especially for
+	 * the inline mode).
+	 */
+
+	/* We rely on wlan_get_elemsubelem_fragseq_creationparams() to give us
+	 * sane values for extrahdrbytes and other parameters.
+	 */
+
+	extrahdrbytes_remaining = extrahdrbytes;
+
+	/* We need to accommodate elemunit_hdrlen bytes for each non-leading
+	 * fragment by moving the non-leading fragment to a higher location.
+	 * Shift bytes and form fragment elements/subelements starting with the
+	 * last fragment and going backwards from there.
+	 */
+
+	/* First move/copy the smaller sized fragment if present */
+	if (smallerfrag_size) {
+		/* The source for the copy/move is just after the end of all the
+		 * max sized fragments (including the lead fragment). The
+		 * element unit header is present for the lead fragment alone.
+		 */
+		src = payloadbuff + elemunit_hdrlen +
+				(num_maxsizefrags * elemunit_maxpayloadlen);
+
+		/* The destination for the copy/move is computed to reflect a
+		 * shift by extrahdrbytes_remaining to accommodate the headers
+		 * for the smaller fragment and all the non-lead max sized
+		 * fragments.
+		 */
+		if (inline_frag)
+			dst = src + extrahdrbytes_remaining;
+		else
+			dst = fragbuff + elemunit_hdrlen +
+				(num_maxsizefrags * elemunit_maxpayloadlen) +
+				extrahdrbytes_remaining;
+
+		bytes_to_transfer = smallerfrag_size;
+
+		/* In the case of inline fragmentation, if the payload buffer
+		 * has additional contents beyond the payload, include those
+		 * contents in the move/copy.
+		 */
+		if (inline_frag &&
+		    (payloadbuff_maxsize > (prepayload_leadbytes + payloadlen)))
+			bytes_to_transfer += (payloadbuff_maxsize -
+					      prepayload_leadbytes -
+					      payloadlen);
+
+		if (inline_frag)
+			qdf_mem_move(dst, src, bytes_to_transfer);
+		else
+			qdf_mem_copy(dst, src, bytes_to_transfer);
+
+		/* Copy/move of payload done. Set fragment ID and length in
+		 * element unit header.
+		 */
+		curr_elemunit_ptr = dst - elemunit_hdrlen;
+		curr_elemunit_ptr[elemunit_id_pos] = elemunit_fragid;
+		curr_elemunit_ptr[elemunit_len_pos] = smallerfrag_size;
+
+		extrahdrbytes_remaining -= elemunit_hdrlen;
+	}
+
+	/* Next, move/copy the non-lead max-sized fragments, if present.
+	 * Fragments at higher memory locations are processed first.
+	 */
+	for (i = num_maxsizefrags; i > 1; i--) {
+		/* Process the 'i'th max-sized fragment. The lead max-sized
+		 * fragment has i=1 and is not processed in this 'for' loop.
+		 * Also note that 'previous .* fragments' in comments for this
+		 * 'for' loop refers to fragments in lower memory locations as
+		 * compared to the current, i.e. 'i'th max-sized fragment.
+		 */
+
+		/* The source for the copy/move is just after the end of all the
+		 * previous max-sized fragments (including the lead fragment).
+		 * The element unit header is present for the lead fragment
+		 * alone.
+		 */
+		src = payloadbuff + elemunit_hdrlen +
+			((i - 1) * elemunit_maxpayloadlen);
+
+		/* The destination for the copy/move is computed to reflect a
+		 * shift by extrahdrbytes_remaining to accommodate the headers
+		 * for the current non-lead max-sized fragment and all the
+		 * previous max-sized non-lead fragments.
+		 */
+		if (inline_frag)
+			dst = src + extrahdrbytes_remaining;
+		else
+			dst = fragbuff + elemunit_hdrlen +
+				((i - 1) * elemunit_maxpayloadlen) +
+				extrahdrbytes_remaining;
+
+		bytes_to_transfer = elemunit_maxpayloadlen;
+
+		/* In the case of inline fragmentation, if this is the last
+		 * non-lead max-sized fragment (i.e. at the highest memory
+		 * location), if the payload buffer has additional contents
+		 * beyond the payload, and these additional contents have not
+		 * already been taken care of by the presence (and processing)
+		 * of a smaller fragment, include the additional contents in the
+		 * move/copy.
+		 */
+		if (inline_frag &&
+		    (i == num_maxsizefrags) &&
+		    (payloadbuff_maxsize > (prepayload_leadbytes +
+					    payloadlen)) &&
+			!smallerfrag_size)
+			bytes_to_transfer += (payloadbuff_maxsize -
+					      prepayload_leadbytes -
+					      payloadlen);
+
+		if (inline_frag)
+			qdf_mem_move(dst, src, bytes_to_transfer);
+		else
+			qdf_mem_copy(dst, src, bytes_to_transfer);
+
+		/* Copy/move of payload done. Set fragment ID and length in
+		 * element unit header.
+		 */
+		curr_elemunit_ptr = dst - elemunit_hdrlen;
+		curr_elemunit_ptr[elemunit_id_pos] = elemunit_fragid;
+		curr_elemunit_ptr[elemunit_len_pos] = elemunit_maxpayloadlen;
+
+		extrahdrbytes_remaining -= elemunit_hdrlen;
+	}
+
+	/* Update the element unit pointer for the lead max-sized fragment.
+	 *
+	 * Copy the payload of the lead max-sized fragment if inline
+	 * fragmentation is not being used.
+	 */
+	if (inline_frag) {
+		curr_elemunit_ptr = payloadbuff;
+	} else {
+		qdf_mem_copy(fragbuff + elemunit_hdrlen,
+			     payloadbuff + elemunit_hdrlen,
+			     elemunit_maxpayloadlen);
+		curr_elemunit_ptr = fragbuff;
+	}
+
+	/* Set IDs and length in the header for the leading fragment */
+	curr_elemunit_ptr[elemunit_id_pos] = id;
+	curr_elemunit_ptr[elemunit_len_pos] = elemunit_maxpayloadlen;
+	if (!is_subelem && (id == WLAN_ELEMID_EXTN_ELEM))
+		curr_elemunit_ptr[elemunit_idext_pos] = idext;
+
+	*fragseqlen = expected_fragseqlen;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 QDF_STATUS
 wlan_get_elem_fragseq_requirements(uint8_t elemid,
 				   qdf_size_t payloadlen,
@@ -516,6 +822,29 @@ wlan_get_elem_fragseq_requirements(uint8_t elemid,
 							   NULL);
 }
 
+QDF_STATUS wlan_create_elem_fragseq(bool inline_frag,
+				    uint8_t elemid,
+				    uint8_t elemidext,
+				    uint8_t *payloadbuff,
+				    qdf_size_t payloadbuff_maxsize,
+				    qdf_size_t payloadlen,
+				    uint8_t *fragbuff,
+				    qdf_size_t fragbuff_maxsize,
+				    qdf_size_t *fragseqlen)
+{
+	return  wlan_create_elemsubelem_fragseq(inline_frag,
+						false,
+						elemid,
+						elemidext,
+						0,
+						payloadbuff,
+						payloadbuff_maxsize,
+						payloadlen,
+						fragbuff,
+						fragbuff_maxsize,
+						fragseqlen);
+}
+
 QDF_STATUS
 wlan_get_subelem_fragseq_requirements(uint8_t subelemid,
 				      qdf_size_t payloadlen,
@@ -533,6 +862,29 @@ wlan_get_subelem_fragseq_requirements(uint8_t subelemid,
 							   NULL);
 }
 
+QDF_STATUS wlan_create_subelem_fragseq(bool inline_frag,
+				       uint8_t subelemid,
+				       uint8_t subelemfragid,
+				       uint8_t *payloadbuff,
+				       qdf_size_t payloadbuff_maxsize,
+				       qdf_size_t payloadlen,
+				       uint8_t *fragbuff,
+				       qdf_size_t fragbuff_maxsize,
+				       qdf_size_t *fragseqlen)
+{
+	return  wlan_create_elemsubelem_fragseq(inline_frag,
+						true,
+						subelemid,
+						0,
+						subelemfragid,
+						payloadbuff,
+						payloadbuff_maxsize,
+						payloadlen,
+						fragbuff,
+						fragbuff_maxsize,
+						fragseqlen);
+}
+
 bool wlan_is_emulation_platform(uint32_t phy_version)
 {
 	if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||