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