1 /* 2 * Copyright (c) 2013-2019 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 110 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 111 ("+htc_connect_service, target:%pK SvcID:0x%X\n", target, 112 pConnectReq->service_id)); 113 114 do { 115 116 AR_DEBUG_ASSERT(pConnectReq->service_id != 0); 117 118 if (HTC_CTRL_RSVD_SVC == pConnectReq->service_id) { 119 /* special case for pseudo control service */ 120 assignedEndpoint = ENDPOINT_0; 121 maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH; 122 txAlloc = 0; 123 124 } else { 125 126 txAlloc = htc_get_credit_allocation(target, 127 pConnectReq->service_id); 128 129 if (!txAlloc) { 130 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 131 ("Service %d does not allocate target credits!\n", 132 pConnectReq->service_id)); 133 } 134 135 /* allocate a packet to send to the target */ 136 pSendPacket = htc_alloc_control_tx_packet(target); 137 138 if (!pSendPacket) { 139 AR_DEBUG_ASSERT(false); 140 status = QDF_STATUS_E_NOMEM; 141 break; 142 } 143 144 netbuf = 145 (qdf_nbuf_t) 146 GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket); 147 length = 148 sizeof(HTC_CONNECT_SERVICE_MSG) + 149 pConnectReq->MetaDataLength; 150 151 /* assemble connect service message */ 152 qdf_nbuf_put_tail(netbuf, length); 153 pConnectMsg = 154 (HTC_CONNECT_SERVICE_MSG *) qdf_nbuf_data(netbuf); 155 156 if (!pConnectMsg) { 157 AR_DEBUG_ASSERT(0); 158 status = QDF_STATUS_E_FAULT; 159 break; 160 } 161 162 qdf_mem_zero(pConnectMsg, 163 sizeof(HTC_CONNECT_SERVICE_MSG)); 164 165 conn_flags = 166 (pConnectReq-> 167 ConnectionFlags & ~HTC_SET_RECV_ALLOC_MASK) | 168 HTC_CONNECT_FLAGS_SET_RECV_ALLOCATION(txAlloc); 169 HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, 170 MESSAGEID, HTC_MSG_CONNECT_SERVICE_ID); 171 HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, 172 SERVICE_ID, pConnectReq->service_id); 173 HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, 174 CONNECTIONFLAGS, conn_flags); 175 176 if (pConnectReq-> 177 ConnectionFlags & 178 HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL) { 179 disableCreditFlowCtrl = true; 180 } 181 182 if (!htc_credit_flow) 183 disableCreditFlowCtrl = true; 184 185 /* check caller if it wants to transfer meta data */ 186 if ((pConnectReq->pMetaData) && 187 (pConnectReq->MetaDataLength <= 188 HTC_SERVICE_META_DATA_MAX_LENGTH)) { 189 /* copy meta data into msg buffer (after hdr) */ 190 qdf_mem_copy((uint8_t *) pConnectMsg + 191 sizeof(HTC_CONNECT_SERVICE_MSG), 192 pConnectReq->pMetaData, 193 pConnectReq->MetaDataLength); 194 195 HTC_SET_FIELD(pConnectMsg, 196 HTC_CONNECT_SERVICE_MSG, 197 SERVICEMETALENGTH, 198 pConnectReq->MetaDataLength); 199 } 200 201 SET_HTC_PACKET_INFO_TX(pSendPacket, 202 NULL, 203 (uint8_t *) pConnectMsg, 204 length, 205 ENDPOINT_0, 206 HTC_SERVICE_TX_PACKET_TAG); 207 208 status = htc_send_pkt((HTC_HANDLE) target, pSendPacket); 209 /* we don't own it anymore */ 210 pSendPacket = NULL; 211 if (QDF_IS_STATUS_ERROR(status)) 212 break; 213 214 /* wait for response */ 215 status = htc_wait_recv_ctrl_message(target); 216 if (QDF_IS_STATUS_ERROR(status)) 217 break; 218 /* we controlled the buffer creation so it has to be 219 * properly aligned 220 */ 221 pResponseMsg = 222 (HTC_CONNECT_SERVICE_RESPONSE_MSG *) target-> 223 CtrlResponseBuffer; 224 225 rsp_msg_id = HTC_GET_FIELD(pResponseMsg, 226 HTC_CONNECT_SERVICE_RESPONSE_MSG, 227 MESSAGEID); 228 rsp_msg_serv_id = 229 HTC_GET_FIELD(pResponseMsg, 230 HTC_CONNECT_SERVICE_RESPONSE_MSG, 231 SERVICEID); 232 rsp_msg_status = 233 HTC_GET_FIELD(pResponseMsg, 234 HTC_CONNECT_SERVICE_RESPONSE_MSG, 235 STATUS); 236 rsp_msg_end_id = 237 HTC_GET_FIELD(pResponseMsg, 238 HTC_CONNECT_SERVICE_RESPONSE_MSG, 239 ENDPOINTID); 240 rsp_msg_max_msg_size = 241 HTC_GET_FIELD(pResponseMsg, 242 HTC_CONNECT_SERVICE_RESPONSE_MSG, 243 MAXMSGSIZE); 244 rsp_msg_serv_meta_len = 245 HTC_GET_FIELD(pResponseMsg, 246 HTC_CONNECT_SERVICE_RESPONSE_MSG, 247 SERVICEMETALENGTH); 248 249 if ((rsp_msg_id != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) 250 || (target->CtrlResponseLength < 251 sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) { 252 /* this message is not valid */ 253 AR_DEBUG_ASSERT(false); 254 status = QDF_STATUS_E_PROTO; 255 break; 256 } 257 258 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 259 ("htc_connect_service, service 0x%X connect response from target status:%d, assigned ep: %d\n", 260 rsp_msg_serv_id, rsp_msg_status, 261 rsp_msg_end_id)); 262 263 pConnectResp->ConnectRespCode = rsp_msg_status; 264 265 /* check response status */ 266 if (rsp_msg_status != HTC_SERVICE_SUCCESS) { 267 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, 268 (" Target failed service 0x%X connect request (status:%d)\n", 269 rsp_msg_serv_id, 270 rsp_msg_status)); 271 status = QDF_STATUS_E_PROTO; 272 /* TODO: restore the ifdef when FW supports services 301 and 302 273 * (HTT_MSG_DATA[23]_MSG_SVC) 274 */ 275 /* #ifdef QCA_TX_HTT2_SUPPORT */ 276 /* Keep work and not to block the control msg */ 277 target->CtrlResponseProcessing = false; 278 /* #endif */ /* QCA_TX_HTT2_SUPPORT */ 279 break; 280 } 281 282 assignedEndpoint = (HTC_ENDPOINT_ID) rsp_msg_end_id; 283 maxMsgSize = rsp_msg_max_msg_size; 284 285 if ((pConnectResp->pMetaData) && 286 (rsp_msg_serv_meta_len > 0) && 287 (rsp_msg_serv_meta_len <= 288 HTC_SERVICE_META_DATA_MAX_LENGTH)) { 289 /* caller supplied a buffer and the target 290 * responded with data 291 */ 292 int copyLength = 293 min((int)pConnectResp->BufferLength, 294 (int)rsp_msg_serv_meta_len); 295 /* copy the meta data */ 296 qdf_mem_copy(pConnectResp->pMetaData, 297 ((uint8_t *) pResponseMsg) + 298 sizeof 299 (HTC_CONNECT_SERVICE_RESPONSE_MSG), 300 copyLength); 301 pConnectResp->ActualLength = copyLength; 302 } 303 /* done processing response buffer */ 304 target->CtrlResponseProcessing = false; 305 } 306 307 /* rest of these are parameter checks so set the error status */ 308 status = QDF_STATUS_E_PROTO; 309 310 if (assignedEndpoint >= ENDPOINT_MAX) { 311 AR_DEBUG_ASSERT(false); 312 break; 313 } 314 315 if (0 == maxMsgSize) { 316 AR_DEBUG_ASSERT(false); 317 break; 318 } 319 320 pEndpoint = &target->endpoint[assignedEndpoint]; 321 pEndpoint->Id = assignedEndpoint; 322 if (pEndpoint->service_id != 0) { 323 /* endpoint already in use! */ 324 AR_DEBUG_ASSERT(false); 325 break; 326 } 327 328 /* return assigned endpoint to caller */ 329 pConnectResp->Endpoint = assignedEndpoint; 330 pConnectResp->MaxMsgLength = maxMsgSize; 331 332 /* setup the endpoint */ 333 /* service_id marks the endpoint in use */ 334 pEndpoint->service_id = pConnectReq->service_id; 335 pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth; 336 pEndpoint->MaxMsgLength = maxMsgSize; 337 pEndpoint->TxCredits = txAlloc; 338 pEndpoint->TxCreditSize = target->TargetCreditSize; 339 pEndpoint->TxCreditsPerMaxMsg = 340 maxMsgSize / target->TargetCreditSize; 341 if (maxMsgSize % target->TargetCreditSize) 342 pEndpoint->TxCreditsPerMaxMsg++; 343 #if DEBUG_CREDIT 344 qdf_print(" Endpoint%d initial credit:%d, size:%d.", 345 pEndpoint->Id, pEndpoint->TxCredits, 346 pEndpoint->TxCreditSize); 347 #endif 348 349 /* copy all the callbacks */ 350 pEndpoint->EpCallBacks = pConnectReq->EpCallbacks; 351 pEndpoint->async_update = 0; 352 353 status = hif_map_service_to_pipe(target->hif_dev, 354 pEndpoint->service_id, 355 &pEndpoint->UL_PipeID, 356 &pEndpoint->DL_PipeID, 357 &pEndpoint->ul_is_polled, 358 &pEndpoint->dl_is_polled); 359 if (QDF_IS_STATUS_ERROR(status)) 360 break; 361 362 htc_alt_data_credit_size_update(target, 363 &pEndpoint->UL_PipeID, 364 &pEndpoint->DL_PipeID, 365 &pEndpoint->TxCreditSize); 366 367 /* not currently supported */ 368 qdf_assert(!pEndpoint->dl_is_polled); 369 370 if (pEndpoint->ul_is_polled) { 371 qdf_timer_init(target->osdev, 372 &pEndpoint->ul_poll_timer, 373 htc_send_complete_check_cleanup, 374 pEndpoint, 375 QDF_TIMER_TYPE_SW); 376 } 377 378 HTC_TRACE("SVC:0x%4.4X, ULpipe:%d DLpipe:%d id:%d Ready", 379 pEndpoint->service_id, pEndpoint->UL_PipeID, 380 pEndpoint->DL_PipeID, pEndpoint->Id); 381 382 if (disableCreditFlowCtrl && pEndpoint->TxCreditFlowEnabled) { 383 pEndpoint->TxCreditFlowEnabled = false; 384 HTC_TRACE("SVC:0x%4.4X ep:%d TX flow control disabled", 385 pEndpoint->service_id, assignedEndpoint); 386 } 387 388 } while (false); 389 390 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_connect_service\n")); 391 392 return status; 393 } 394 qdf_export_symbol(htc_connect_service); 395 396 void htc_set_credit_distribution(HTC_HANDLE HTCHandle, 397 void *pCreditDistContext, 398 HTC_CREDIT_DIST_CALLBACK CreditDistFunc, 399 HTC_CREDIT_INIT_CALLBACK CreditInitFunc, 400 HTC_SERVICE_ID ServicePriorityOrder[], 401 int ListLength) 402 { 403 /* NOT Supported, this transport does not use a credit based flow 404 * control mechanism 405 */ 406 407 } 408 409 void htc_fw_event_handler(void *context, QDF_STATUS status) 410 { 411 HTC_TARGET *target = (HTC_TARGET *) context; 412 struct htc_init_info *initInfo = &target->HTCInitInfo; 413 414 /* check if target failure handler exists and pass error code to it. */ 415 if (target->HTCInitInfo.TargetFailure) 416 initInfo->TargetFailure(initInfo->pContext, status); 417 } 418 419 420 void htc_set_async_ep(HTC_HANDLE HTCHandle, 421 HTC_ENDPOINT_ID htc_ep_id, bool value) 422 { 423 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 424 HTC_ENDPOINT *pEndpoint = &target->endpoint[htc_ep_id]; 425 426 pEndpoint->async_update = value; 427 qdf_print("%s: htc_handle %pK, ep %d, value %d", __func__, 428 HTCHandle, htc_ep_id, value); 429 } 430 431