//------------------------------------------------------------------------------ // // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved. // // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // //------------------------------------------------------------------------------ //============================================================================== // Author(s): ="Atheros" //============================================================================== #include "htc_internal.h" void HTCControlTxComplete(void *Context, HTC_PACKET *pPacket) { /* not implemented * we do not send control TX frames during normal runtime, only during setup */ AR_DEBUG_ASSERT(FALSE); } /* callback when a control message arrives on this endpoint */ void HTCControlRecv(void *Context, HTC_PACKET *pPacket) { AR_DEBUG_ASSERT(pPacket->Endpoint == ENDPOINT_0); if (pPacket->Status == A_ECANCELED) { /* this is a flush operation, return the control packet back to the pool */ HTC_FREE_CONTROL_RX((HTC_TARGET*)Context,pPacket); return; } /* the only control messages we are expecting are NULL messages (credit resports) */ if (pPacket->ActualLength > 0) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCControlRecv, got message with length:%d \n", pPacket->ActualLength + (A_UINT32)HTC_HDR_LENGTH)); #ifdef ATH_DEBUG_MODULE /* dump header and message */ DebugDumpBytes(pPacket->pBuffer - HTC_HDR_LENGTH, pPacket->ActualLength + HTC_HDR_LENGTH, "Unexpected ENDPOINT 0 Message"); #endif } HTC_RECYCLE_RX_PKT((HTC_TARGET*)Context,pPacket,&((HTC_TARGET*)Context)->EndPoint[0]); } A_STATUS HTCSendSetupComplete(HTC_TARGET *target) { HTC_PACKET *pSendPacket = NULL; A_STATUS status; do { /* allocate a packet to send to the target */ pSendPacket = HTC_ALLOC_CONTROL_TX(target); if (NULL == pSendPacket) { status = A_NO_MEMORY; break; } if (target->HTCTargetVersion >= HTC_VERSION_2P1) { HTC_SETUP_COMPLETE_EX_MSG *pSetupCompleteEx; A_UINT32 setupFlags = 0; pSetupCompleteEx = (HTC_SETUP_COMPLETE_EX_MSG *)pSendPacket->pBuffer; A_MEMZERO(pSetupCompleteEx, sizeof(HTC_SETUP_COMPLETE_EX_MSG)); pSetupCompleteEx->MessageID = HTC_MSG_SETUP_COMPLETE_EX_ID; if (target->MaxMsgPerBundle > 0) { /* host can do HTC bundling, indicate this to the target */ setupFlags |= HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV; pSetupCompleteEx->MaxMsgsPerBundledRecv = target->MaxMsgPerBundle; } A_MEMCPY(&pSetupCompleteEx->SetupFlags, &setupFlags, sizeof(pSetupCompleteEx->SetupFlags)); SET_HTC_PACKET_INFO_TX(pSendPacket, NULL, (A_UINT8 *)pSetupCompleteEx, sizeof(HTC_SETUP_COMPLETE_EX_MSG), ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } else { HTC_SETUP_COMPLETE_MSG *pSetupComplete; /* assemble setup complete message */ pSetupComplete = (HTC_SETUP_COMPLETE_MSG *)pSendPacket->pBuffer; A_MEMZERO(pSetupComplete, sizeof(HTC_SETUP_COMPLETE_MSG)); pSetupComplete->MessageID = HTC_MSG_SETUP_COMPLETE_ID; SET_HTC_PACKET_INFO_TX(pSendPacket, NULL, (A_UINT8 *)pSetupComplete, sizeof(HTC_SETUP_COMPLETE_MSG), ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } /* we want synchronous operation */ pSendPacket->Completion = NULL; HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0); /* send the message */ status = HTCIssueSend(target,pSendPacket); } while (FALSE); if (pSendPacket != NULL) { HTC_FREE_CONTROL_TX(target,pSendPacket); } return status; } A_STATUS HTCConnectService(HTC_HANDLE HTCHandle, HTC_SERVICE_CONNECT_REQ *pConnectReq, HTC_SERVICE_CONNECT_RESP *pConnectResp) { HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); A_STATUS status = A_OK; HTC_PACKET *pRecvPacket = NULL; HTC_PACKET *pSendPacket = NULL; HTC_CONNECT_SERVICE_RESPONSE_MSG *pResponseMsg; HTC_CONNECT_SERVICE_MSG *pConnectMsg; HTC_ENDPOINT_ID assignedEndpoint = ENDPOINT_MAX; HTC_ENDPOINT *pEndpoint; unsigned int maxMsgSize = 0; AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:0x%lX SvcID:0x%X \n", (unsigned long)target, pConnectReq->ServiceID)); do { AR_DEBUG_ASSERT(pConnectReq->ServiceID != 0); if (HTC_CTRL_RSVD_SVC == pConnectReq->ServiceID) { /* special case for pseudo control service */ assignedEndpoint = ENDPOINT_0; maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH; } else { /* allocate a packet to send to the target */ pSendPacket = HTC_ALLOC_CONTROL_TX(target); if (NULL == pSendPacket) { AR_DEBUG_ASSERT(FALSE); status = A_NO_MEMORY; break; } /* assemble connect service message */ pConnectMsg = (HTC_CONNECT_SERVICE_MSG *)pSendPacket->pBuffer; AR_DEBUG_ASSERT(pConnectMsg != NULL); A_MEMZERO(pConnectMsg,sizeof(HTC_CONNECT_SERVICE_MSG)); pConnectMsg->MessageID = HTC_MSG_CONNECT_SERVICE_ID; pConnectMsg->ServiceID = pConnectReq->ServiceID; pConnectMsg->ConnectionFlags = pConnectReq->ConnectionFlags; /* check caller if it wants to transfer meta data */ if ((pConnectReq->pMetaData != NULL) && (pConnectReq->MetaDataLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) { /* copy meta data into message buffer (after header ) */ A_MEMCPY((A_UINT8 *)pConnectMsg + sizeof(HTC_CONNECT_SERVICE_MSG), pConnectReq->pMetaData, pConnectReq->MetaDataLength); pConnectMsg->ServiceMetaLength = pConnectReq->MetaDataLength; } SET_HTC_PACKET_INFO_TX(pSendPacket, NULL, (A_UINT8 *)pConnectMsg, sizeof(HTC_CONNECT_SERVICE_MSG) + pConnectMsg->ServiceMetaLength, ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); /* we want synchronous operation */ pSendPacket->Completion = NULL; HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0); status = HTCIssueSend(target,pSendPacket); if (A_FAILED(status)) { break; } /* wait for response */ status = HTCWaitforControlMessage(target, &pRecvPacket); if (A_FAILED(status)) { break; } /* we controlled the buffer creation so it has to be properly aligned */ pResponseMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)pRecvPacket->pBuffer; if ((pResponseMsg->MessageID != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) || (pRecvPacket->ActualLength < sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) { /* this message is not valid */ AR_DEBUG_ASSERT(FALSE); status = A_EPROTO; break; } pConnectResp->ConnectRespCode = pResponseMsg->Status; /* check response status */ if (pResponseMsg->Status != HTC_SERVICE_SUCCESS) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" Target failed service 0x%X connect request (status:%d)\n", pResponseMsg->ServiceID, pResponseMsg->Status)); status = A_EPROTO; break; } assignedEndpoint = (HTC_ENDPOINT_ID) pResponseMsg->EndpointID; maxMsgSize = pResponseMsg->MaxMsgSize; if ((pConnectResp->pMetaData != NULL) && (pResponseMsg->ServiceMetaLength > 0) && (pResponseMsg->ServiceMetaLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) { /* caller supplied a buffer and the target responded with data */ int copyLength = min((int)pConnectResp->BufferLength, (int)pResponseMsg->ServiceMetaLength); /* copy the meta data */ A_MEMCPY(pConnectResp->pMetaData, ((A_UINT8 *)pResponseMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG), copyLength); pConnectResp->ActualLength = copyLength; } } /* the rest of these are parameter checks so set the error status */ status = A_EPROTO; if (assignedEndpoint >= ENDPOINT_MAX) { AR_DEBUG_ASSERT(FALSE); break; } if (0 == maxMsgSize) { AR_DEBUG_ASSERT(FALSE); break; } pEndpoint = &target->EndPoint[assignedEndpoint]; pEndpoint->Id = assignedEndpoint; if (pEndpoint->ServiceID != 0) { /* endpoint already in use! */ AR_DEBUG_ASSERT(FALSE); break; } /* return assigned endpoint to caller */ pConnectResp->Endpoint = assignedEndpoint; pConnectResp->MaxMsgLength = maxMsgSize; /* setup the endpoint */ pEndpoint->ServiceID = pConnectReq->ServiceID; /* this marks the endpoint in use */ pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth; pEndpoint->MaxMsgLength = maxMsgSize; /* copy all the callbacks */ pEndpoint->EpCallBacks = pConnectReq->EpCallbacks; /* set the credit distribution info for this endpoint, this information is * passed back to the credit distribution callback function */ pEndpoint->CreditDist.ServiceID = pConnectReq->ServiceID; pEndpoint->CreditDist.pHTCReserved = pEndpoint; pEndpoint->CreditDist.Endpoint = assignedEndpoint; pEndpoint->CreditDist.TxCreditSize = target->TargetCreditSize; if (pConnectReq->MaxSendMsgSize != 0) { /* override TxCreditsPerMaxMsg calculation, this optimizes the credit-low indications * since the host will actually issue smaller messages in the Send path */ if (pConnectReq->MaxSendMsgSize > maxMsgSize) { /* can't be larger than the maximum the target can support */ AR_DEBUG_ASSERT(FALSE); break; } pEndpoint->CreditDist.TxCreditsPerMaxMsg = pConnectReq->MaxSendMsgSize / target->TargetCreditSize; } else { pEndpoint->CreditDist.TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize; } if (0 == pEndpoint->CreditDist.TxCreditsPerMaxMsg) { pEndpoint->CreditDist.TxCreditsPerMaxMsg = 1; } /* save local connection flags */ pEndpoint->LocalConnectionFlags = pConnectReq->LocalConnectionFlags; status = A_OK; } while (FALSE); if (pSendPacket != NULL) { HTC_FREE_CONTROL_TX(target,pSendPacket); } if (pRecvPacket != NULL) { HTC_FREE_CONTROL_RX(target,pRecvPacket); } AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCConnectService \n")); return status; } static void AddToEndpointDistList(HTC_TARGET *target, HTC_ENDPOINT_CREDIT_DIST *pEpDist) { HTC_ENDPOINT_CREDIT_DIST *pCurEntry,*pLastEntry; if (NULL == target->EpCreditDistributionListHead) { target->EpCreditDistributionListHead = pEpDist; pEpDist->pNext = NULL; pEpDist->pPrev = NULL; return; } /* queue to the end of the list, this does not have to be very * fast since this list is built at startup time */ pCurEntry = target->EpCreditDistributionListHead; while (pCurEntry) { pLastEntry = pCurEntry; pCurEntry = pCurEntry->pNext; } pLastEntry->pNext = pEpDist; pEpDist->pPrev = pLastEntry; pEpDist->pNext = NULL; } /* default credit init callback */ static void HTCDefaultCreditInit(void *Context, HTC_ENDPOINT_CREDIT_DIST *pEPList, int TotalCredits) { HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; int totalEps = 0; int creditsPerEndpoint; pCurEpDist = pEPList; /* first run through the list and figure out how many endpoints we are dealing with */ while (pCurEpDist != NULL) { pCurEpDist = pCurEpDist->pNext; totalEps++; } /* even distribution */ creditsPerEndpoint = TotalCredits/totalEps; pCurEpDist = pEPList; /* run through the list and set minimum and normal credits and * provide the endpoint with some credits to start */ while (pCurEpDist != NULL) { if (creditsPerEndpoint < pCurEpDist->TxCreditsPerMaxMsg) { /* too many endpoints and not enough credits */ AR_DEBUG_ASSERT(FALSE); break; } /* our minimum is set for at least 1 max message */ pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg; /* this value is ignored by our credit alg, since we do * not dynamically adjust credits, this is the policy of * the "default" credit distribution, something simple and easy */ pCurEpDist->TxCreditsNorm = 0xFFFF; /* give the endpoint minimum credits */ pCurEpDist->TxCredits = creditsPerEndpoint; pCurEpDist->TxCreditsAssigned = creditsPerEndpoint; pCurEpDist = pCurEpDist->pNext; } } /* default credit distribution callback, NOTE, this callback holds the TX lock */ void HTCDefaultCreditDist(void *Context, HTC_ENDPOINT_CREDIT_DIST *pEPDistList, HTC_CREDIT_DIST_REASON Reason) { HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; if (Reason == HTC_CREDIT_DIST_SEND_COMPLETE) { pCurEpDist = pEPDistList; /* simple distribution */ while (pCurEpDist != NULL) { if (pCurEpDist->TxCreditsToDist > 0) { /* just give the endpoint back the credits */ pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist; pCurEpDist->TxCreditsToDist = 0; } pCurEpDist = pCurEpDist->pNext; } } /* note we do not need to handle the other reason codes as this is a very * simple distribution scheme, no need to seek for more credits or handle inactivity */ } void HTCSetCreditDistribution(HTC_HANDLE HTCHandle, void *pCreditDistContext, HTC_CREDIT_DIST_CALLBACK CreditDistFunc, HTC_CREDIT_INIT_CALLBACK CreditInitFunc, HTC_SERVICE_ID ServicePriorityOrder[], int ListLength) { HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); int i; int ep; if (CreditInitFunc != NULL) { /* caller has supplied their own distribution functions */ target->InitCredits = CreditInitFunc; AR_DEBUG_ASSERT(CreditDistFunc != NULL); target->DistributeCredits = CreditDistFunc; target->pCredDistContext = pCreditDistContext; } else { /* caller wants HTC to do distribution */ /* if caller wants service to handle distributions then * it must set both of these to NULL! */ AR_DEBUG_ASSERT(CreditDistFunc == NULL); target->InitCredits = HTCDefaultCreditInit; target->DistributeCredits = HTCDefaultCreditDist; target->pCredDistContext = target; } /* always add HTC control endpoint first, we only expose the list after the * first one, this is added for TX queue checking */ AddToEndpointDistList(target, &target->EndPoint[ENDPOINT_0].CreditDist); /* build the list of credit distribution structures in priority order * supplied by the caller, these will follow endpoint 0 */ for (i = 0; i < ListLength; i++) { /* match services with endpoints and add the endpoints to the distribution list * in FIFO order */ for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) { if (target->EndPoint[ep].ServiceID == ServicePriorityOrder[i]) { /* queue this one to the list */ AddToEndpointDistList(target, &target->EndPoint[ep].CreditDist); break; } } AR_DEBUG_ASSERT(ep < ENDPOINT_MAX); } }