1 /*
2  * Copyright (c) 2013-2020 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 #include "htc_debug.h"
21 #include "htc_internal.h"
22 #include <hif.h>
23 #include <qdf_nbuf.h>           /* qdf_nbuf_t */
24 #include "qdf_module.h"
25 
26 /* use credit flow control over HTC */
27 unsigned int htc_credit_flow = 1;
28 #ifndef DEBUG_CREDIT
29 #define DEBUG_CREDIT 0
30 #endif
31 
32 /* HTC credit flow global disable */
htc_global_credit_flow_disable(void)33 void htc_global_credit_flow_disable(void)
34 {
35 	htc_credit_flow = 0;
36 }
37 
38 /* HTC credit flow global enable */
htc_global_credit_flow_enable(void)39 void htc_global_credit_flow_enable(void)
40 {
41 	htc_credit_flow = 1;
42 }
43 
44 #ifdef HIF_SDIO
45 
46 /**
47  * htc_alt_data_credit_size_update() - update tx credit size info
48  *				on max bundle size
49  * @target: hif context
50  * @ul_pipe: endpoint ul pipe id
51  * @dl_pipe: endpoint dl pipe id
52  * @txCreditSize: endpoint tx credit size
53  *
54  *
55  * When AltDataCreditSize is non zero, it indicates the credit size for
56  * HTT and all other services on Mbox0. Mbox1 has WMI_CONTROL_SVC which
57  * uses the default credit size. Use AltDataCreditSize only when
58  * mailbox is swapped. Mailbox swap bit is set by bmi_target_ready at
59  * the end of BMI phase.
60  *
61  * The Credit Size is a parameter associated with the mbox rather than
62  * a service. Multiple services can run on this mbox.
63  *
64  * If AltDataCreditSize is 0, that means the firmware doesn't support
65  * this feature. Default to the TargetCreditSize
66  *
67  * Return: None
68  */
69 static inline void
htc_alt_data_credit_size_update(HTC_TARGET * target,uint8_t * ul_pipe,uint8_t * dl_pipe,int * txCreditSize)70 htc_alt_data_credit_size_update(HTC_TARGET *target,
71 				uint8_t *ul_pipe,
72 				uint8_t *dl_pipe,
73 				int *txCreditSize)
74 {
75 	if ((target->AltDataCreditSize) &&
76 	    (*ul_pipe == 1) && (*dl_pipe == 0))
77 		*txCreditSize = target->AltDataCreditSize;
78 
79 }
80 #else
81 
82 static inline void
htc_alt_data_credit_size_update(HTC_TARGET * target,uint8_t * ul_pipe,uint8_t * dl_pipe,int * txCreditSize)83 htc_alt_data_credit_size_update(HTC_TARGET *target,
84 				uint8_t *ul_pipe,
85 				uint8_t *dl_pipe,
86 				int *txCreditSize)
87 {
88 }
89 #endif
90 
htc_connect_service(HTC_HANDLE HTCHandle,struct htc_service_connect_req * pConnectReq,struct htc_service_connect_resp * pConnectResp)91 QDF_STATUS htc_connect_service(HTC_HANDLE HTCHandle,
92 			     struct htc_service_connect_req *pConnectReq,
93 			     struct htc_service_connect_resp *pConnectResp)
94 {
95 	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
96 	QDF_STATUS status = QDF_STATUS_SUCCESS;
97 	HTC_PACKET *pSendPacket = NULL;
98 	HTC_CONNECT_SERVICE_RESPONSE_MSG *pResponseMsg;
99 	HTC_CONNECT_SERVICE_MSG *pConnectMsg;
100 	HTC_ENDPOINT_ID assignedEndpoint = ENDPOINT_MAX;
101 	HTC_ENDPOINT *pEndpoint;
102 	unsigned int maxMsgSize = 0;
103 	qdf_nbuf_t netbuf;
104 	uint8_t txAlloc;
105 	int length;
106 	bool disableCreditFlowCtrl = false;
107 	uint16_t conn_flags;
108 	uint16_t rsp_msg_id, rsp_msg_serv_id, rsp_msg_max_msg_size;
109 	uint8_t rsp_msg_status, rsp_msg_end_id, rsp_msg_serv_meta_len;
110 	int ret;
111 
112 	AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
113 			("+htc_connect_service, target:%pK SvcID:0x%X\n", target,
114 			 pConnectReq->service_id));
115 
116 	do {
117 
118 		AR_DEBUG_ASSERT(pConnectReq->service_id != 0);
119 
120 		if (HTC_CTRL_RSVD_SVC == pConnectReq->service_id) {
121 			/* special case for pseudo control service */
122 			assignedEndpoint = ENDPOINT_0;
123 			maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH;
124 			txAlloc = 0;
125 
126 		} else {
127 
128 			txAlloc = htc_get_credit_allocation(target,
129 					pConnectReq->service_id);
130 
131 			if (!txAlloc) {
132 				AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
133 						("Service %d does not allocate target credits!\n",
134 						 pConnectReq->service_id));
135 			}
136 
137 			/* allocate a packet to send to the target */
138 			pSendPacket = htc_alloc_control_tx_packet(target);
139 
140 			if (!pSendPacket) {
141 				AR_DEBUG_ASSERT(false);
142 				status = QDF_STATUS_E_NOMEM;
143 				break;
144 			}
145 
146 			netbuf =
147 				(qdf_nbuf_t)
148 				GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket);
149 			length =
150 				sizeof(HTC_CONNECT_SERVICE_MSG) +
151 				pConnectReq->MetaDataLength;
152 
153 			/* assemble connect service message */
154 			qdf_nbuf_put_tail(netbuf, length);
155 			pConnectMsg =
156 			    (HTC_CONNECT_SERVICE_MSG *) qdf_nbuf_data(netbuf);
157 
158 			if (!pConnectMsg) {
159 				AR_DEBUG_ASSERT(0);
160 				status = QDF_STATUS_E_FAULT;
161 				break;
162 			}
163 
164 			qdf_mem_zero(pConnectMsg,
165 				     sizeof(HTC_CONNECT_SERVICE_MSG));
166 
167 			conn_flags =
168 				(pConnectReq->
169 				 ConnectionFlags & ~HTC_SET_RECV_ALLOC_MASK) |
170 				HTC_CONNECT_FLAGS_SET_RECV_ALLOCATION(txAlloc);
171 			HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG,
172 				      MESSAGEID, HTC_MSG_CONNECT_SERVICE_ID);
173 			HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG,
174 				      SERVICE_ID, pConnectReq->service_id);
175 			HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG,
176 				      CONNECTIONFLAGS, conn_flags);
177 
178 			if (pConnectReq->
179 			    ConnectionFlags &
180 			    HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL) {
181 				disableCreditFlowCtrl = true;
182 			}
183 
184 			if (!htc_credit_flow)
185 				disableCreditFlowCtrl = true;
186 
187 			/* check caller if it wants to transfer meta data */
188 			if ((pConnectReq->pMetaData) &&
189 			    (pConnectReq->MetaDataLength <=
190 			     HTC_SERVICE_META_DATA_MAX_LENGTH)) {
191 				/* copy meta data into msg buffer (after hdr) */
192 				qdf_mem_copy((uint8_t *) pConnectMsg +
193 					 sizeof(HTC_CONNECT_SERVICE_MSG),
194 					 pConnectReq->pMetaData,
195 					 pConnectReq->MetaDataLength);
196 
197 				HTC_SET_FIELD(pConnectMsg,
198 					      HTC_CONNECT_SERVICE_MSG,
199 					      SERVICEMETALENGTH,
200 					      pConnectReq->MetaDataLength);
201 			}
202 
203 			SET_HTC_PACKET_INFO_TX(pSendPacket,
204 					       NULL,
205 					       (uint8_t *) pConnectMsg,
206 					       length,
207 					       ENDPOINT_0,
208 					       HTC_SERVICE_TX_PACKET_TAG);
209 
210 			status = htc_send_pkt((HTC_HANDLE) target, pSendPacket);
211 			/* we don't own it anymore */
212 			pSendPacket = NULL;
213 			if (QDF_IS_STATUS_ERROR(status))
214 				break;
215 
216 			/* wait for response */
217 			status = htc_wait_recv_ctrl_message(target);
218 			if (QDF_IS_STATUS_ERROR(status))
219 				break;
220 			/* we controlled the buffer creation so it has to be
221 			 * properly aligned
222 			 */
223 			pResponseMsg =
224 				(HTC_CONNECT_SERVICE_RESPONSE_MSG *) target->
225 				CtrlResponseBuffer;
226 
227 			rsp_msg_id = HTC_GET_FIELD(pResponseMsg,
228 					   HTC_CONNECT_SERVICE_RESPONSE_MSG,
229 					   MESSAGEID);
230 			rsp_msg_serv_id =
231 				HTC_GET_FIELD(pResponseMsg,
232 					      HTC_CONNECT_SERVICE_RESPONSE_MSG,
233 					      SERVICEID);
234 			rsp_msg_status =
235 				HTC_GET_FIELD(pResponseMsg,
236 					      HTC_CONNECT_SERVICE_RESPONSE_MSG,
237 					      STATUS);
238 			rsp_msg_end_id =
239 				HTC_GET_FIELD(pResponseMsg,
240 					      HTC_CONNECT_SERVICE_RESPONSE_MSG,
241 					      ENDPOINTID);
242 			rsp_msg_max_msg_size =
243 				HTC_GET_FIELD(pResponseMsg,
244 					      HTC_CONNECT_SERVICE_RESPONSE_MSG,
245 					      MAXMSGSIZE);
246 			rsp_msg_serv_meta_len =
247 				HTC_GET_FIELD(pResponseMsg,
248 					      HTC_CONNECT_SERVICE_RESPONSE_MSG,
249 					      SERVICEMETALENGTH);
250 
251 			if ((rsp_msg_id != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID)
252 			    || (target->CtrlResponseLength <
253 				sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) {
254 				/* this message is not valid */
255 				AR_DEBUG_ASSERT(false);
256 				status = QDF_STATUS_E_PROTO;
257 				break;
258 			}
259 
260 			AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
261 					("htc_connect_service, service 0x%X connect response from target status:%d, assigned ep: %d\n",
262 					 rsp_msg_serv_id, rsp_msg_status,
263 					 rsp_msg_end_id));
264 
265 			pConnectResp->ConnectRespCode = rsp_msg_status;
266 
267 			/* check response status */
268 			if (rsp_msg_status != HTC_SERVICE_SUCCESS) {
269 				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
270 						(" Target failed service 0x%X connect request (status:%d)\n",
271 						 rsp_msg_serv_id,
272 						 rsp_msg_status));
273 				status = QDF_STATUS_E_PROTO;
274 /* TODO: restore the ifdef when FW supports services 301 and 302
275  * (HTT_MSG_DATA[23]_MSG_SVC)
276  */
277 /* #ifdef QCA_TX_HTT2_SUPPORT */
278 				/* Keep work and not to block the control msg */
279 				target->CtrlResponseProcessing = false;
280 /* #endif */ /* QCA_TX_HTT2_SUPPORT */
281 				break;
282 			}
283 
284 			assignedEndpoint = (HTC_ENDPOINT_ID) rsp_msg_end_id;
285 			maxMsgSize = rsp_msg_max_msg_size;
286 
287 			if ((pConnectResp->pMetaData) &&
288 			    (rsp_msg_serv_meta_len > 0) &&
289 			    (rsp_msg_serv_meta_len <=
290 			     HTC_SERVICE_META_DATA_MAX_LENGTH)) {
291 				/* caller supplied a buffer and the target
292 				 * responded with data
293 				 */
294 				int copyLength =
295 					min((int)pConnectResp->BufferLength,
296 					    (int)rsp_msg_serv_meta_len);
297 				/* copy the meta data */
298 				qdf_mem_copy(pConnectResp->pMetaData,
299 					 ((uint8_t *) pResponseMsg) +
300 					 sizeof
301 					 (HTC_CONNECT_SERVICE_RESPONSE_MSG),
302 					 copyLength);
303 				pConnectResp->ActualLength = copyLength;
304 			}
305 			/* done processing response buffer */
306 			target->CtrlResponseProcessing = false;
307 		}
308 
309 		/* rest of these are parameter checks so set the error status */
310 		status = QDF_STATUS_E_PROTO;
311 
312 		if (assignedEndpoint >= ENDPOINT_MAX) {
313 			AR_DEBUG_ASSERT(false);
314 			break;
315 		}
316 
317 		if (0 == maxMsgSize) {
318 			AR_DEBUG_ASSERT(false);
319 			break;
320 		}
321 
322 		pEndpoint = &target->endpoint[assignedEndpoint];
323 		pEndpoint->Id = assignedEndpoint;
324 		if (pEndpoint->service_id != 0) {
325 			/* endpoint already in use! */
326 			AR_DEBUG_ASSERT(false);
327 			break;
328 		}
329 
330 		/* return assigned endpoint to caller */
331 		pConnectResp->Endpoint = assignedEndpoint;
332 		pConnectResp->MaxMsgLength = maxMsgSize;
333 
334 		/* setup the endpoint */
335 		/* service_id marks the endpoint in use */
336 		pEndpoint->service_id = pConnectReq->service_id;
337 		pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth;
338 		pEndpoint->MaxMsgLength = maxMsgSize;
339 		pEndpoint->TxCredits = txAlloc;
340 		pEndpoint->TxCreditSize = target->TargetCreditSize;
341 		pEndpoint->TxCreditsPerMaxMsg =
342 			maxMsgSize / target->TargetCreditSize;
343 		if (maxMsgSize % target->TargetCreditSize)
344 			pEndpoint->TxCreditsPerMaxMsg++;
345 #if DEBUG_CREDIT
346 		qdf_print(" Endpoint%d initial credit:%d, size:%d.",
347 			  pEndpoint->Id, pEndpoint->TxCredits,
348 			  pEndpoint->TxCreditSize);
349 #endif
350 
351 		/* copy all the callbacks */
352 		pEndpoint->EpCallBacks = pConnectReq->EpCallbacks;
353 		pEndpoint->async_update = 0;
354 
355 		ret = hif_map_service_to_pipe(target->hif_dev,
356 					      pEndpoint->service_id,
357 					      &pEndpoint->UL_PipeID,
358 					      &pEndpoint->DL_PipeID,
359 					      &pEndpoint->ul_is_polled,
360 					      &pEndpoint->dl_is_polled);
361 		status = qdf_status_from_os_return(ret);
362 		if (QDF_IS_STATUS_ERROR(status))
363 			break;
364 
365 		htc_alt_data_credit_size_update(target,
366 						&pEndpoint->UL_PipeID,
367 						&pEndpoint->DL_PipeID,
368 						&pEndpoint->TxCreditSize);
369 
370 		/* not currently supported */
371 		qdf_assert(!pEndpoint->dl_is_polled);
372 
373 		if (pEndpoint->ul_is_polled) {
374 			qdf_timer_init(target->osdev,
375 				&pEndpoint->ul_poll_timer,
376 				htc_send_complete_check_cleanup,
377 				pEndpoint,
378 				QDF_TIMER_TYPE_SW);
379 		}
380 
381 		HTC_TRACE("SVC:0x%4.4X, ULpipe:%d DLpipe:%d id:%d Ready",
382 			  pEndpoint->service_id, pEndpoint->UL_PipeID,
383 			  pEndpoint->DL_PipeID, pEndpoint->Id);
384 
385 		if (disableCreditFlowCtrl && pEndpoint->TxCreditFlowEnabled) {
386 			pEndpoint->TxCreditFlowEnabled = false;
387 			HTC_TRACE("SVC:0x%4.4X ep:%d TX flow control disabled",
388 				  pEndpoint->service_id, assignedEndpoint);
389 		}
390 
391 	} while (false);
392 
393 	AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_connect_service\n"));
394 
395 	return status;
396 }
397 qdf_export_symbol(htc_connect_service);
398 
htc_set_credit_distribution(HTC_HANDLE HTCHandle,void * pCreditDistContext,HTC_CREDIT_DIST_CALLBACK CreditDistFunc,HTC_CREDIT_INIT_CALLBACK CreditInitFunc,HTC_SERVICE_ID ServicePriorityOrder[],int ListLength)399 void htc_set_credit_distribution(HTC_HANDLE HTCHandle,
400 				 void *pCreditDistContext,
401 				 HTC_CREDIT_DIST_CALLBACK CreditDistFunc,
402 				 HTC_CREDIT_INIT_CALLBACK CreditInitFunc,
403 				 HTC_SERVICE_ID ServicePriorityOrder[],
404 				 int ListLength)
405 {
406 	/* NOT Supported, this transport does not use a credit based flow
407 	 * control mechanism
408 	 */
409 
410 }
411 
htc_fw_event_handler(void * context,QDF_STATUS status)412 void htc_fw_event_handler(void *context, QDF_STATUS status)
413 {
414 	HTC_TARGET *target = (HTC_TARGET *) context;
415 	struct htc_init_info *initInfo = &target->HTCInitInfo;
416 
417 	/* check if target failure handler exists and pass error code to it. */
418 	if (target->HTCInitInfo.TargetFailure)
419 		initInfo->TargetFailure(initInfo->pContext, status);
420 }
421 
422 
htc_set_async_ep(HTC_HANDLE HTCHandle,HTC_ENDPOINT_ID htc_ep_id,bool value)423 void htc_set_async_ep(HTC_HANDLE HTCHandle,
424 			HTC_ENDPOINT_ID htc_ep_id, bool value)
425 {
426 	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
427 	HTC_ENDPOINT *pEndpoint = &target->endpoint[htc_ep_id];
428 
429 	pEndpoint->async_update = value;
430 	HTC_INFO("%s: htc_handle %pK, ep %d, value %d", __func__,
431 		HTCHandle, htc_ep_id, value);
432 }
433 
434