mwifiex: use cfg80211 dynamic scan table and cfg80211_get_bss API
[pandora-kernel.git] / drivers / staging / ath6kl / miscdrv / credit_dist.c
1 //------------------------------------------------------------------------------
2 // <copyright file="credit_dist.c" company="Atheros">
3 //    Copyright (c) 2004-2010 Atheros Corporation.  All rights reserved.
4 // 
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Author(s): ="Atheros"
22 //==============================================================================
23
24 #include "a_config.h"
25 #include "athdefs.h"
26 #include "a_osapi.h"
27 #define ATH_MODULE_NAME misc
28 #include "a_debug.h"
29 #include "htc_api.h"
30 #include "common_drv.h"
31
32 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
33
34 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
35 #define CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS 1
36
37 #ifdef NO_VO_SERVICE
38 #define DATA_SVCS_USED 3
39 #else
40 #define DATA_SVCS_USED 4
41 #endif
42
43 static void RedistributeCredits(struct common_credit_state_info *pCredInfo,
44                                 struct htc_endpoint_credit_dist *pEPDistList);
45
46 static void SeekCredits(struct common_credit_state_info *pCredInfo,
47                         struct htc_endpoint_credit_dist *pEPDistList);
48
49 /* reduce an ep's credits back to a set limit */
50 static INLINE void ReduceCredits(struct common_credit_state_info *pCredInfo,
51                                 struct htc_endpoint_credit_dist  *pEpDist,
52                                 int                       Limit)
53 {
54     int credits;
55
56         /* set the new limit */
57     pEpDist->TxCreditsAssigned = Limit;
58
59     if (pEpDist->TxCredits <= Limit) {
60         return;
61     }
62
63         /* figure out how much to take away */
64     credits = pEpDist->TxCredits - Limit;
65         /* take them away */
66     pEpDist->TxCredits -= credits;
67     pCredInfo->CurrentFreeCredits += credits;
68 }
69
70 /* give an endpoint some credits from the free credit pool */
71 #define GiveCredits(pCredInfo,pEpDist,credits)      \
72 {                                                   \
73     (pEpDist)->TxCredits += (credits);              \
74     (pEpDist)->TxCreditsAssigned += (credits);      \
75     (pCredInfo)->CurrentFreeCredits -= (credits);   \
76 }
77
78
79 /* default credit init callback.
80  * This function is called in the context of HTCStart() to setup initial (application-specific)
81  * credit distributions */
82 static void ar6000_credit_init(void                     *Context,
83                                struct htc_endpoint_credit_dist *pEPList,
84                                int                      TotalCredits)
85 {
86     struct htc_endpoint_credit_dist *pCurEpDist;
87     int                      count;
88     struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context;
89
90     pCredInfo->CurrentFreeCredits = TotalCredits;
91     pCredInfo->TotalAvailableCredits = TotalCredits;
92
93     pCurEpDist = pEPList;
94
95         /* run through the list and initialize */
96     while (pCurEpDist != NULL) {
97
98             /* set minimums for each endpoint */
99         pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
100
101 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
102  
103       if (TotalCredits > 4)
104       {
105           if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC)  || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)){
106                     /* assign at least min credits to lower than VO priority services */
107                 GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
108                     /* force active */
109                 SET_EP_ACTIVE(pCurEpDist);
110           }
111       }
112  
113 #endif
114
115         if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
116                 /* give control service some credits */
117             GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
118                 /* control service is always marked active, it never goes inactive EVER */
119             SET_EP_ACTIVE(pCurEpDist);
120         } else if (pCurEpDist->ServiceID == WMI_DATA_BK_SVC) {
121                 /* this is the lowest priority data endpoint, save this off for easy access */
122             pCredInfo->pLowestPriEpDist = pCurEpDist;
123         }
124
125         /* Streams have to be created (explicit | implicit)for all kinds
126          * of traffic. BE endpoints are also inactive in the beginning.
127          * When BE traffic starts it creates implicit streams that
128          * redistributes credits.
129          */
130
131         /* note, all other endpoints have minimums set but are initially given NO credits.
132          * Credits will be distributed as traffic activity demands */
133         pCurEpDist = pCurEpDist->pNext;
134     }
135
136     if (pCredInfo->CurrentFreeCredits <= 0) {
137         AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits));
138         A_ASSERT(false);
139         return;
140     }
141
142         /* reset list */
143     pCurEpDist = pEPList;
144         /* now run through the list and set max operating credit limits for everyone */
145     while (pCurEpDist != NULL) {
146         if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
147                 /* control service max is just 1 max message */
148             pCurEpDist->TxCreditsNorm = pCurEpDist->TxCreditsPerMaxMsg;
149         } else {
150                 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
151                  * the same.
152                  * We use a simple calculation here, we take the remaining credits and
153                  * determine how many max messages this can cover and then set each endpoint's
154                  * normal value equal to 3/4 this amount.
155                  * */
156             count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg;
157             count = (count * 3) >> 2;
158             count = max(count,pCurEpDist->TxCreditsPerMaxMsg);
159                 /* set normal */
160             pCurEpDist->TxCreditsNorm = count;
161
162         }
163         pCurEpDist = pCurEpDist->pNext;
164     }
165
166 }
167
168
169 /* default credit distribution callback
170  * This callback is invoked whenever endpoints require credit distributions.
171  * A lock is held while this function is invoked, this function shall NOT block.
172  * The pEPDistList is a list of distribution structures in prioritized order as
173  * defined by the call to the HTCSetCreditDistribution() api.
174  *
175  */
176 static void ar6000_credit_distribute(void                     *Context,
177                                      struct htc_endpoint_credit_dist *pEPDistList,
178                                      HTC_CREDIT_DIST_REASON   Reason)
179 {
180     struct htc_endpoint_credit_dist *pCurEpDist;
181     struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context;
182
183     switch (Reason) {
184         case HTC_CREDIT_DIST_SEND_COMPLETE :
185             pCurEpDist = pEPDistList;
186                 /* we are given the start of the endpoint distribution list.
187                  * There may be one or more endpoints to service.
188                  * Run through the list and distribute credits */
189             while (pCurEpDist != NULL) {
190
191                 if (pCurEpDist->TxCreditsToDist > 0) {
192                         /* return the credits back to the endpoint */
193                     pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
194                         /* always zero out when we are done */
195                     pCurEpDist->TxCreditsToDist = 0;
196
197                     if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) {
198                             /* reduce to the assigned limit, previous credit reductions
199                              * could have caused the limit to change */
200                         ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned);
201                     }
202
203                     if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) {
204                             /* oversubscribed endpoints need to reduce back to normal */
205                         ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm);
206                     }
207                 
208                     if (!IS_EP_ACTIVE(pCurEpDist)) {
209                             /* endpoint is inactive, now check for messages waiting for credits */
210                         if (pCurEpDist->TxQueueDepth == 0) {
211                                 /* EP is inactive and there are no pending messages, 
212                                  * reduce credits back to zero to recover credits */
213                             ReduceCredits(pCredInfo, pCurEpDist, 0);
214                         }
215                     }
216                 }
217
218                 pCurEpDist = pCurEpDist->pNext;
219             }
220
221             break;
222
223         case HTC_CREDIT_DIST_ACTIVITY_CHANGE :
224             RedistributeCredits(pCredInfo,pEPDistList);
225             break;
226         case HTC_CREDIT_DIST_SEEK_CREDITS :
227             SeekCredits(pCredInfo,pEPDistList);
228             break;
229         case HTC_DUMP_CREDIT_STATE :
230             AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, free : %d\n",
231                                                                         pCredInfo->TotalAvailableCredits, pCredInfo->CurrentFreeCredits));
232             break;
233         default:
234             break;
235
236     }
237
238         /* sanity checks done after each distribution action */
239     A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits);
240     A_ASSERT(pCredInfo->CurrentFreeCredits >= 0);
241
242 }
243
244 /* redistribute credits based on activity change */
245 static void RedistributeCredits(struct common_credit_state_info *pCredInfo,
246                                 struct htc_endpoint_credit_dist *pEPDistList)
247 {
248     struct htc_endpoint_credit_dist *pCurEpDist = pEPDistList;
249
250         /* walk through the list and remove credits from inactive endpoints */
251     while (pCurEpDist != NULL) {
252
253 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
254
255         if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC)  || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)) {
256               /* force low priority streams to always be active to retain their minimum credit distribution */
257              SET_EP_ACTIVE(pCurEpDist);
258         }
259 #endif
260
261         if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) {
262             if (!IS_EP_ACTIVE(pCurEpDist)) {
263                 if (pCurEpDist->TxQueueDepth == 0) {
264                         /* EP is inactive and there are no pending messages, reduce credits back to zero */
265                     ReduceCredits(pCredInfo, pCurEpDist, 0);
266                 } else {
267                         /* we cannot zero the credits assigned to this EP, but to keep
268                          * the credits available for these leftover packets, reduce to
269                          * a minimum */
270                     ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin);
271                 }
272             }
273         }
274
275         /* NOTE in the active case, we do not need to do anything further,
276          * when an EP goes active and needs credits, HTC will call into
277          * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS  */
278
279         pCurEpDist = pCurEpDist->pNext;
280     }
281
282 }
283
284 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */
285 static void SeekCredits(struct common_credit_state_info *pCredInfo,
286                         struct htc_endpoint_credit_dist *pEPDist)
287 {
288     struct htc_endpoint_credit_dist *pCurEpDist;
289     int                      credits = 0;
290     int                      need;
291
292     do {
293
294         if (pEPDist->ServiceID == WMI_CONTROL_SVC) {
295                 /* we never oversubscribe on the control service, this is not
296                  * a high performance path and the target never holds onto control
297                  * credits for too long */
298             break;
299         }
300
301 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
302         if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
303             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
304                  /* limit VI service from oversubscribing */
305                  break;
306             }
307         }
308  
309         if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
310             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
311                  /* limit VO service from oversubscribing */
312                 break;
313             }
314         }
315 #else
316         if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
317             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
318                 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
319                  /* limit VI service from oversubscribing */
320                  /* at least one free credit will not be used by VI */
321                  break;
322             }
323         }
324  
325         if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
326             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
327                 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
328                  /* limit VO service from oversubscribing */
329                  /* at least one free credit will not be used by VO */
330                 break;
331             }
332         }
333 #endif
334
335         /* for all other services, we follow a simple algorithm of
336          * 1. checking the free pool for credits
337          * 2. checking lower priority endpoints for credits to take */
338
339             /* give what we can */
340         credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
341
342         if (credits >= pEPDist->TxCreditsSeek) {
343                 /* we found some to fulfill the seek request */
344             break;
345         }
346
347         /* we don't have enough in the free pool, try taking away from lower priority services
348          *
349          * The rule for taking away credits:
350          *   1. Only take from lower priority endpoints
351          *   2. Only take what is allocated above the minimum (never starve an endpoint completely)
352          *   3. Only take what you need.
353          *
354          * */
355
356             /* starting at the lowest priority */
357         pCurEpDist = pCredInfo->pLowestPriEpDist;
358
359             /* work backwards until we hit the endpoint again */
360         while (pCurEpDist != pEPDist) {
361                 /* calculate how many we need so far */
362             need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits;
363
364             if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) {
365                     /* the current one has been allocated more than it's minimum and it
366                      * has enough credits assigned above it's minimum to fulfill our need
367                      * try to take away just enough to fulfill our need */
368                 ReduceCredits(pCredInfo,
369                               pCurEpDist,
370                               pCurEpDist->TxCreditsAssigned - need);
371
372                 if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) {
373                         /* we have enough */
374                     break;
375                 }
376             }
377
378             pCurEpDist = pCurEpDist->pPrev;
379         }
380
381             /* return what we can get */
382         credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
383
384     } while (false);
385
386         /* did we find some credits? */
387     if (credits) {
388             /* give what we can */
389         GiveCredits(pCredInfo, pEPDist, credits);
390     }
391
392 }
393
394 /* initialize and setup credit distribution */
395 int ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, struct common_credit_state_info *pCredInfo)
396 {
397     HTC_SERVICE_ID servicepriority[5];
398
399     A_MEMZERO(pCredInfo,sizeof(struct common_credit_state_info));
400
401     servicepriority[0] = WMI_CONTROL_SVC;  /* highest */
402     servicepriority[1] = WMI_DATA_VO_SVC;
403     servicepriority[2] = WMI_DATA_VI_SVC;
404     servicepriority[3] = WMI_DATA_BE_SVC;
405     servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */
406
407         /* set callbacks and priority list */
408     HTCSetCreditDistribution(HTCHandle,
409                              pCredInfo,
410                              ar6000_credit_distribute,
411                              ar6000_credit_init,
412                              servicepriority,
413                              5);
414
415     return 0;
416 }
417