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