Index: chromeos/drivers/ath6kl/miscdrv/credit_dist.c |
diff --git a/chromeos/drivers/ath6kl/miscdrv/credit_dist.c b/chromeos/drivers/ath6kl/miscdrv/credit_dist.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc53ee5b3771c1d9cdda43b3b7610d0959da56fb |
--- /dev/null |
+++ b/chromeos/drivers/ath6kl/miscdrv/credit_dist.c |
@@ -0,0 +1,370 @@ |
+//------------------------------------------------------------------------------ |
+// <copyright file="credit_dist.c" company="Atheros"> |
+// Copyright (c) 2004-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 "a_config.h" |
+#include "athdefs.h" |
+#include "a_types.h" |
+#include "a_osapi.h" |
+#define ATH_MODULE_NAME misc |
+#include "a_debug.h" |
+#include "htc_api.h" |
+#include "common_drv.h" |
+ |
+/********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/ |
+ |
+#define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */ |
+ |
+#ifdef NO_VO_SERVICE |
+#define DATA_SVCS_USED 3 |
+#else |
+#define DATA_SVCS_USED 4 |
+#endif |
+ |
+static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPDistList); |
+ |
+static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPDistList); |
+ |
+/* reduce an ep's credits back to a set limit */ |
+static INLINE void ReduceCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
+ HTC_ENDPOINT_CREDIT_DIST *pEpDist, |
+ int Limit) |
+{ |
+ int credits; |
+ |
+ /* set the new limit */ |
+ pEpDist->TxCreditsAssigned = Limit; |
+ |
+ if (pEpDist->TxCredits <= Limit) { |
+ return; |
+ } |
+ |
+ /* figure out how much to take away */ |
+ credits = pEpDist->TxCredits - Limit; |
+ /* take them away */ |
+ pEpDist->TxCredits -= credits; |
+ pCredInfo->CurrentFreeCredits += credits; |
+} |
+ |
+/* give an endpoint some credits from the free credit pool */ |
+#define GiveCredits(pCredInfo,pEpDist,credits) \ |
+{ \ |
+ (pEpDist)->TxCredits += (credits); \ |
+ (pEpDist)->TxCreditsAssigned += (credits); \ |
+ (pCredInfo)->CurrentFreeCredits -= (credits); \ |
+} |
+ |
+ |
+/* default credit init callback. |
+ * This function is called in the context of HTCStart() to setup initial (application-specific) |
+ * credit distributions */ |
+static void ar6000_credit_init(void *Context, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPList, |
+ int TotalCredits) |
+{ |
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
+ int count; |
+ COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context; |
+ |
+ pCredInfo->CurrentFreeCredits = TotalCredits; |
+ pCredInfo->TotalAvailableCredits = TotalCredits; |
+ |
+ pCurEpDist = pEPList; |
+ |
+ /* run through the list and initialize */ |
+ while (pCurEpDist != NULL) { |
+ |
+ /* set minimums for each endpoint */ |
+ pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg; |
+ |
+ if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) { |
+ /* give control service some credits */ |
+ GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin); |
+ /* control service is always marked active, it never goes inactive EVER */ |
+ SET_EP_ACTIVE(pCurEpDist); |
+ } else if (pCurEpDist->ServiceID == WMI_DATA_BK_SVC) { |
+ /* this is the lowest priority data endpoint, save this off for easy access */ |
+ pCredInfo->pLowestPriEpDist = pCurEpDist; |
+ } |
+ |
+ /* Streams have to be created (explicit | implicit)for all kinds |
+ * of traffic. BE endpoints are also inactive in the beginning. |
+ * When BE traffic starts it creates implicit streams that |
+ * redistributes credits. |
+ */ |
+ |
+ /* note, all other endpoints have minimums set but are initially given NO credits. |
+ * Credits will be distributed as traffic activity demands */ |
+ pCurEpDist = pCurEpDist->pNext; |
+ } |
+ |
+ if (pCredInfo->CurrentFreeCredits <= 0) { |
+ AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits)); |
+ A_ASSERT(FALSE); |
+ return; |
+ } |
+ |
+ /* reset list */ |
+ pCurEpDist = pEPList; |
+ /* now run through the list and set max operating credit limits for everyone */ |
+ while (pCurEpDist != NULL) { |
+ if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) { |
+ /* control service max is just 1 max message */ |
+ pCurEpDist->TxCreditsNorm = pCurEpDist->TxCreditsPerMaxMsg; |
+ } else { |
+ /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are |
+ * the same. |
+ * We use a simple calculation here, we take the remaining credits and |
+ * determine how many max messages this can cover and then set each endpoint's |
+ * normal value equal to 3/4 this amount. |
+ * */ |
+ count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg; |
+ count = (count * 3) >> 2; |
+ count = max(count,pCurEpDist->TxCreditsPerMaxMsg); |
+ /* set normal */ |
+ pCurEpDist->TxCreditsNorm = count; |
+ |
+ } |
+ pCurEpDist = pCurEpDist->pNext; |
+ } |
+ |
+} |
+ |
+ |
+/* default credit distribution callback |
+ * This callback is invoked whenever endpoints require credit distributions. |
+ * A lock is held while this function is invoked, this function shall NOT block. |
+ * The pEPDistList is a list of distribution structures in prioritized order as |
+ * defined by the call to the HTCSetCreditDistribution() api. |
+ * |
+ */ |
+static void ar6000_credit_distribute(void *Context, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPDistList, |
+ HTC_CREDIT_DIST_REASON Reason) |
+{ |
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
+ COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context; |
+ |
+ switch (Reason) { |
+ case HTC_CREDIT_DIST_SEND_COMPLETE : |
+ pCurEpDist = pEPDistList; |
+ /* we are given the start of the endpoint distribution list. |
+ * There may be one or more endpoints to service. |
+ * Run through the list and distribute credits */ |
+ while (pCurEpDist != NULL) { |
+ |
+ if (pCurEpDist->TxCreditsToDist > 0) { |
+ /* return the credits back to the endpoint */ |
+ pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist; |
+ /* always zero out when we are done */ |
+ pCurEpDist->TxCreditsToDist = 0; |
+ |
+ if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) { |
+ /* reduce to the assigned limit, previous credit reductions |
+ * could have caused the limit to change */ |
+ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned); |
+ } |
+ |
+ if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) { |
+ /* oversubscribed endpoints need to reduce back to normal */ |
+ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm); |
+ } |
+ |
+ if (!IS_EP_ACTIVE(pCurEpDist)) { |
+ /* endpoint is inactive, now check for messages waiting for credits */ |
+ if (pCurEpDist->TxQueueDepth == 0) { |
+ /* EP is inactive and there are no pending messages, |
+ * reduce credits back to zero to recover credits */ |
+ ReduceCredits(pCredInfo, pCurEpDist, 0); |
+ } |
+ } |
+ } |
+ |
+ pCurEpDist = pCurEpDist->pNext; |
+ } |
+ |
+ break; |
+ |
+ case HTC_CREDIT_DIST_ACTIVITY_CHANGE : |
+ RedistributeCredits(pCredInfo,pEPDistList); |
+ break; |
+ case HTC_CREDIT_DIST_SEEK_CREDITS : |
+ SeekCredits(pCredInfo,pEPDistList); |
+ break; |
+ case HTC_DUMP_CREDIT_STATE : |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, free : %d\n", |
+ pCredInfo->TotalAvailableCredits, pCredInfo->CurrentFreeCredits)); |
+ break; |
+ default: |
+ break; |
+ |
+ } |
+ |
+ /* sanity checks done after each distribution action */ |
+ A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits); |
+ A_ASSERT(pCredInfo->CurrentFreeCredits >= 0); |
+ |
+} |
+ |
+/* redistribute credits based on activity change */ |
+static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPDistList) |
+{ |
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist = pEPDistList; |
+ |
+ /* walk through the list and remove credits from inactive endpoints */ |
+ while (pCurEpDist != NULL) { |
+ |
+ if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) { |
+ if (!IS_EP_ACTIVE(pCurEpDist)) { |
+ if (pCurEpDist->TxQueueDepth == 0) { |
+ /* EP is inactive and there are no pending messages, reduce credits back to zero */ |
+ ReduceCredits(pCredInfo, pCurEpDist, 0); |
+ } else { |
+ /* we cannot zero the credits assigned to this EP, but to keep |
+ * the credits available for these leftover packets, reduce to |
+ * a minimum */ |
+ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin); |
+ } |
+ } |
+ } |
+ |
+ /* NOTE in the active case, we do not need to do anything further, |
+ * when an EP goes active and needs credits, HTC will call into |
+ * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */ |
+ |
+ pCurEpDist = pCurEpDist->pNext; |
+ } |
+ |
+} |
+ |
+/* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */ |
+static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
+ HTC_ENDPOINT_CREDIT_DIST *pEPDist) |
+{ |
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
+ int credits = 0; |
+ int need; |
+ |
+ do { |
+ |
+ if (pEPDist->ServiceID == WMI_CONTROL_SVC) { |
+ /* we never oversubscribe on the control service, this is not |
+ * a high performance path and the target never holds onto control |
+ * credits for too long */ |
+ break; |
+ } |
+ |
+ if (pEPDist->ServiceID == WMI_DATA_VI_SVC) { |
+ if (pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) { |
+ /* limit VI service from oversubscribing */ |
+ break; |
+ } |
+ } |
+ |
+ if (pEPDist->ServiceID == WMI_DATA_VO_SVC) { |
+ if (pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) { |
+ /* limit VO service from oversubscribing */ |
+ break; |
+ } |
+ } |
+ |
+ /* for all other services, we follow a simple algorithm of |
+ * 1. checking the free pool for credits |
+ * 2. checking lower priority endpoints for credits to take */ |
+ |
+ /* give what we can */ |
+ credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); |
+ |
+ if (credits >= pEPDist->TxCreditsSeek) { |
+ /* we found some to fullfill the seek request */ |
+ break; |
+ } |
+ |
+ /* we don't have enough in the free pool, try taking away from lower priority services |
+ * |
+ * The rule for taking away credits: |
+ * 1. Only take from lower priority endpoints |
+ * 2. Only take what is allocated above the minimum (never starve an endpoint completely) |
+ * 3. Only take what you need. |
+ * |
+ * */ |
+ |
+ /* starting at the lowest priority */ |
+ pCurEpDist = pCredInfo->pLowestPriEpDist; |
+ |
+ /* work backwards until we hit the endpoint again */ |
+ while (pCurEpDist != pEPDist) { |
+ /* calculate how many we need so far */ |
+ need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits; |
+ |
+ if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) { |
+ /* the current one has been allocated more than it's minimum and it |
+ * has enough credits assigned above it's minimum to fullfill our need |
+ * try to take away just enough to fullfill our need */ |
+ ReduceCredits(pCredInfo, |
+ pCurEpDist, |
+ pCurEpDist->TxCreditsAssigned - need); |
+ |
+ if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) { |
+ /* we have enough */ |
+ break; |
+ } |
+ } |
+ |
+ pCurEpDist = pCurEpDist->pPrev; |
+ } |
+ |
+ /* return what we can get */ |
+ credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); |
+ |
+ } while (FALSE); |
+ |
+ /* did we find some credits? */ |
+ if (credits) { |
+ /* give what we can */ |
+ GiveCredits(pCredInfo, pEPDist, credits); |
+ } |
+ |
+} |
+ |
+/* initialize and setup credit distribution */ |
+A_STATUS ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, COMMON_CREDIT_STATE_INFO *pCredInfo) |
+{ |
+ HTC_SERVICE_ID servicepriority[5]; |
+ |
+ A_MEMZERO(pCredInfo,sizeof(COMMON_CREDIT_STATE_INFO)); |
+ |
+ servicepriority[0] = WMI_CONTROL_SVC; /* highest */ |
+ servicepriority[1] = WMI_DATA_VO_SVC; |
+ servicepriority[2] = WMI_DATA_VI_SVC; |
+ servicepriority[3] = WMI_DATA_BE_SVC; |
+ servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ |
+ |
+ /* set callbacks and priority list */ |
+ HTCSetCreditDistribution(HTCHandle, |
+ pCredInfo, |
+ ar6000_credit_distribute, |
+ ar6000_credit_init, |
+ servicepriority, |
+ 5); |
+ |
+ return A_OK; |
+} |
+ |