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