1 /*
2  * Copyright (c) 2011-2018,The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* OS abstraction libraries */
21 #include <qdf_nbuf.h>           /* qdf_nbuf_t, etc. */
22 #include <qdf_atomic.h>         /* qdf_atomic_read, etc. */
23 #include <qdf_util.h>           /* qdf_unlikely */
24 
25 /* APIs for other modules */
26 #include <htt.h>                /* HTT_TX_EXT_TID_MGMT */
27 #include <ol_htt_tx_api.h>      /* htt_tx_desc_tid */
28 
29 /* internal header files relevant for all systems */
30 #include <ol_txrx_internal.h>   /* TXRX_ASSERT1 */
31 #include <ol_tx_desc.h>         /* ol_tx_desc */
32 #include <ol_tx_send.h>         /* ol_tx_send */
33 #include <ol_txrx.h>
34 
35 /* internal header files relevant only for HL systems */
36 #include <ol_tx_classify.h>   /* ol_tx_classify, ol_tx_classify_mgmt */
37 #include <ol_tx_queue.h>        /* ol_tx_enqueue */
38 #include <ol_tx_sched.h>      /* ol_tx_sched */
39 
40 /* internal header files relevant only for specific systems (Pronto) */
41 #include <ol_txrx_encap.h>      /* OL_TX_ENCAP, etc */
42 #include <ol_tx.h>
43 #include <cdp_txrx_ipa.h>
44 
45 /**
46  * ol_tx_ll_wrapper() wrapper to ol_tx_ll
47  *
48  */
49 qdf_nbuf_t
ol_tx_ll_wrapper(ol_txrx_vdev_handle vdev,qdf_nbuf_t msdu_list)50 ol_tx_ll_wrapper(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
51 {
52 	return ol_tx_ll(vdev, msdu_list);
53 }
54 
55 #if defined(FEATURE_TSO)
ol_tx_ll(ol_txrx_vdev_handle vdev,qdf_nbuf_t msdu_list)56 qdf_nbuf_t ol_tx_ll(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
57 {
58 	qdf_nbuf_t msdu = msdu_list;
59 	struct ol_txrx_msdu_info_t msdu_info;
60 	uint32_t tso_msdu_stats_idx = 0;
61 
62 	msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type;
63 	msdu_info.htt.action.tx_comp_req = 0;
64 	/*
65 	 * The msdu_list variable could be used instead of the msdu var,
66 	 * but just to clarify which operations are done on a single MSDU
67 	 * vs. a list of MSDUs, use a distinct variable for single MSDUs
68 	 * within the list.
69 	 */
70 	while (msdu) {
71 		qdf_nbuf_t next;
72 		struct ol_tx_desc_t *tx_desc = NULL;
73 		int segments = 1;
74 
75 		msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu);
76 		msdu_info.peer = NULL;
77 
78 		if (qdf_unlikely(ol_tx_prepare_tso(vdev, msdu, &msdu_info))) {
79 			qdf_print("ol_tx_prepare_tso failed\n");
80 			TXRX_STATS_MSDU_LIST_INCR(vdev->pdev,
81 						  tx.dropped.host_reject,
82 						   msdu);
83 			return msdu;
84 		}
85 
86 		segments = msdu_info.tso_info.num_segs;
87 
88 		if (msdu_info.tso_info.is_tso) {
89 			tso_msdu_stats_idx =
90 					ol_tx_tso_get_stats_idx(vdev->pdev);
91 			msdu_info.tso_info.msdu_stats_idx = tso_msdu_stats_idx;
92 			ol_tx_tso_update_stats(vdev->pdev,
93 					       &(msdu_info.tso_info),
94 					       msdu, tso_msdu_stats_idx);
95 		}
96 
97 		/*
98 		 * The netbuf may get linked into a different list inside the
99 		 * ol_tx_send function, so store the next pointer before the
100 		 * tx_send call.
101 		 */
102 		next = qdf_nbuf_next(msdu);
103 		/* init the current segment to the 1st segment in the list */
104 		while (segments) {
105 			if (msdu_info.tso_info.curr_seg)
106 				QDF_NBUF_CB_PADDR(msdu) =
107 					msdu_info.tso_info.curr_seg->
108 					seg.tso_frags[0].paddr;
109 
110 			segments--;
111 
112 			tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info);
113 			if (!tx_desc)
114 				return msdu;
115 
116 			ol_tx_trace_pkt(msdu, tx_desc->id, vdev->vdev_id,
117 					vdev->qdf_opmode);
118 
119 			/*
120 			 * If this is a jumbo nbuf, then increment the number
121 			 * of nbuf users for each additional segment of the msdu
122 			 * This will ensure that the skb is freed only after
123 			 * receiving tx completion for all segments of an nbuf.
124 			 */
125 			if (segments)
126 				qdf_nbuf_inc_users(msdu);
127 
128 			TXRX_STATS_MSDU_INCR(vdev->pdev, tx.from_stack, msdu);
129 
130 			/*
131 			 * If debug display is enabled, show the meta-data being
132 			 * downloaded to the target via the HTT tx descriptor.
133 			 */
134 			htt_tx_desc_display(tx_desc->htt_tx_desc);
135 
136 			ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
137 
138 			if (msdu_info.tso_info.curr_seg) {
139 				msdu_info.tso_info.curr_seg =
140 					 msdu_info.tso_info.curr_seg->next;
141 			}
142 
143 			if (msdu_info.tso_info.is_tso) {
144 				TXRX_STATS_TSO_INC_SEG(vdev->pdev,
145 						       tso_msdu_stats_idx);
146 				TXRX_STATS_TSO_INC_SEG_IDX(vdev->pdev,
147 							   tso_msdu_stats_idx);
148 			}
149 		} /* while segments */
150 
151 		msdu = next;
152 	} /* while msdus */
153 	return NULL;            /* all MSDUs were accepted */
154 }
155 #else /* TSO */
156 
ol_tx_ll(ol_txrx_vdev_handle vdev,qdf_nbuf_t msdu_list)157 qdf_nbuf_t ol_tx_ll(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
158 {
159 	qdf_nbuf_t msdu = msdu_list;
160 	struct ol_txrx_msdu_info_t msdu_info;
161 
162 	msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type;
163 	msdu_info.htt.action.tx_comp_req = 0;
164 	msdu_info.tso_info.is_tso = 0;
165 	/*
166 	 * The msdu_list variable could be used instead of the msdu var,
167 	 * but just to clarify which operations are done on a single MSDU
168 	 * vs. a list of MSDUs, use a distinct variable for single MSDUs
169 	 * within the list.
170 	 */
171 	while (msdu) {
172 		qdf_nbuf_t next;
173 		struct ol_tx_desc_t *tx_desc = NULL;
174 
175 		msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu);
176 		msdu_info.peer = NULL;
177 		tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info);
178 		if (!tx_desc)
179 			return msdu;
180 
181 		ol_tx_trace_pkt(msdu, tx_desc->id, vdev->vdev_id,
182 				vdev->qdf_opmode);
183 
184 		TXRX_STATS_MSDU_INCR(vdev->pdev, tx.from_stack, msdu);
185 
186 		/*
187 		 * If debug display is enabled, show the meta-data being
188 		 * downloaded to the target via the HTT tx descriptor.
189 		 */
190 		htt_tx_desc_display(tx_desc->htt_tx_desc);
191 		/*
192 		 * The netbuf may get linked into a different list inside the
193 		 * ol_tx_send function, so store the next pointer before the
194 		 * tx_send call.
195 		 */
196 		next = qdf_nbuf_next(msdu);
197 		ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
198 		msdu = next;
199 	}
200 	return NULL;            /* all MSDUs were accepted */
201 }
202 #endif /* TSO */
203