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