Index: chromeos/drivers/ath6kl/htc2/htc_services.c |
diff --git a/chromeos/drivers/ath6kl/htc2/htc_services.c b/chromeos/drivers/ath6kl/htc2/htc_services.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e0e3426b152e6cf4c78d95d7849bd08bd23b3f17 |
--- /dev/null |
+++ b/chromeos/drivers/ath6kl/htc2/htc_services.c |
@@ -0,0 +1,444 @@ |
+//------------------------------------------------------------------------------ |
+// <copyright file="htc_services.c" company="Atheros"> |
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved. |
+// |
+// This program is free software; you can redistribute it and/or modify |
+// it under the terms of the GNU General Public License version 2 as |
+// published by the Free Software Foundation; |
+// |
+// Software distributed under the License is distributed on an "AS |
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
+// implied. See the License for the specific language governing |
+// rights and limitations under the License. |
+// |
+// |
+//------------------------------------------------------------------------------ |
+//============================================================================== |
+// 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 + HTC_HDR_LENGTH)); |
+ |
+ /* dump header and message */ |
+ DebugDumpBytes(pPacket->pBuffer - HTC_HDR_LENGTH, |
+ pPacket->ActualLength + HTC_HDR_LENGTH, |
+ "Unexpected ENDPOINT 0 Message"); |
+ } |
+ |
+ 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; |
+ int maxMsgSize = 0; |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:0x%X SvcID:0x%X \n", |
+ (A_UINT32)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); |
+ } |
+ |
+} |