Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / drivers / staging / ath6kl / htc2 / htc_send.c
1 //------------------------------------------------------------------------------
2 // <copyright file="htc_send.c" company="Atheros">
3 //    Copyright (c) 2007-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 #include "htc_internal.h"
24
25 typedef enum _HTC_SEND_QUEUE_RESULT {
26     HTC_SEND_QUEUE_OK = 0,    /* packet was queued */
27     HTC_SEND_QUEUE_DROP = 1,  /* this packet should be dropped */
28 } HTC_SEND_QUEUE_RESULT;
29
30 #define DO_EP_TX_COMPLETION(ep,q)  DoSendCompletion(ep,q)
31
32 /* call the distribute credits callback with the distribution */
33 #define DO_DISTRIBUTION(t,reason,description,pList) \
34 {                                             \
35     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,           \
36         ("  calling distribute function (%s) (dfn:0x%lX, ctxt:0x%lX, dist:0x%lX) \n", \
37                 (description),                                           \
38                 (unsigned long)(t)->DistributeCredits,                   \
39                 (unsigned long)(t)->pCredDistContext,                    \
40                 (unsigned long)pList));                                  \
41     (t)->DistributeCredits((t)->pCredDistContext,                        \
42                            (pList),                                      \
43                            (reason));                                    \
44 }
45
46 static void DoSendCompletion(HTC_ENDPOINT       *pEndpoint,
47                              HTC_PACKET_QUEUE   *pQueueToIndicate)
48 {           
49     do {
50                 
51         if (HTC_QUEUE_EMPTY(pQueueToIndicate)) {
52                 /* nothing to indicate */
53             break;    
54         }
55  
56         if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) {    
57             AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d, send complete multiple callback (%d pkts) \n",
58                      pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate)));
59                 /* a multiple send complete handler is being used, pass the queue to the handler */                             
60             pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint->EpCallBacks.pContext,
61                                                         pQueueToIndicate);
62                 /* all packets are now owned by the callback, reset queue to be safe */
63             INIT_HTC_PACKET_QUEUE(pQueueToIndicate);                                                      
64         } else {
65             HTC_PACKET *pPacket;  
66             /* using legacy EpTxComplete */         
67             do {
68                 pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
69                 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d send complete callback on packet 0x%lX \n", \
70                         pEndpoint->Id, (unsigned long)(pPacket)));
71                 pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);                                              
72             } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));                                              
73         }
74         
75     } while (FALSE);
76
77 }
78
79 /* do final completion on sent packet */
80 static INLINE void CompleteSentPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket)
81 {
82     pPacket->Completion = NULL;  
83     
84     if (A_FAILED(pPacket->Status)) {
85         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
86             ("CompleteSentPacket: request failed (status:%d, ep:%d, length:%d creds:%d) \n",
87                 pPacket->Status, pPacket->Endpoint, pPacket->ActualLength, pPacket->PktInfo.AsTx.CreditsUsed));                
88             /* on failure to submit, reclaim credits for this packet */        
89         LOCK_HTC_TX(target);        
90         pEndpoint->CreditDist.TxCreditsToDist += pPacket->PktInfo.AsTx.CreditsUsed;
91         pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
92         DO_DISTRIBUTION(target,
93                         HTC_CREDIT_DIST_SEND_COMPLETE,
94                         "Send Complete",
95                         target->EpCreditDistributionListHead->pNext);
96         UNLOCK_HTC_TX(target);            
97     }
98         /* first, fixup the head room we allocated */
99     pPacket->pBuffer += HTC_HDR_LENGTH; 
100 }
101
102 /* our internal send packet completion handler when packets are submited to the AR6K device
103  * layer */
104 static void HTCSendPktCompletionHandler(void *Context, HTC_PACKET *pPacket)
105 {
106     HTC_TARGET      *target = (HTC_TARGET *)Context;
107     HTC_ENDPOINT    *pEndpoint = &target->EndPoint[pPacket->Endpoint];
108     HTC_PACKET_QUEUE container;
109     
110     CompleteSentPacket(target,pEndpoint,pPacket);
111     INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
112         /* do completion */
113     DO_EP_TX_COMPLETION(pEndpoint,&container);
114 }
115
116 A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket)
117 {
118     A_STATUS status;
119     A_BOOL   sync = FALSE;
120
121     if (pPacket->Completion == NULL) {
122             /* mark that this request was synchronously issued */
123         sync = TRUE;
124     }
125
126     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
127                     ("+-HTCIssueSend: transmit length : %d (%s) \n",
128                     pPacket->ActualLength + (A_UINT32)HTC_HDR_LENGTH,
129                     sync ? "SYNC" : "ASYNC" ));
130
131         /* send message to device */
132     status = DevSendPacket(&target->Device,
133                            pPacket,
134                            pPacket->ActualLength + HTC_HDR_LENGTH);
135
136     if (sync) {
137             /* use local sync variable.  If this was issued asynchronously, pPacket is no longer
138              * safe to access. */
139         pPacket->pBuffer += HTC_HDR_LENGTH;
140     }
141     
142     /* if this request was asynchronous, the packet completion routine will be invoked by
143      * the device layer when the HIF layer completes the request */
144
145     return status;
146 }
147
148     /* get HTC send packets from the TX queue on an endpoint */
149 static INLINE void GetHTCSendPackets(HTC_TARGET        *target, 
150                                      HTC_ENDPOINT      *pEndpoint, 
151                                      HTC_PACKET_QUEUE  *pQueue)
152 {
153     int          creditsRequired;
154     int          remainder;
155     A_UINT8      sendFlags;
156     HTC_PACKET   *pPacket;
157     unsigned int transferLength;
158
159     /****** NOTE : the TX lock is held when this function is called *****************/
160     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets \n"));
161      
162         /* loop until we can grab as many packets out of the queue as we can */       
163     while (TRUE) {    
164         
165         sendFlags = 0;   
166             /* get packet at head, but don't remove it */
167         pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue);       
168         if (pPacket == NULL) {
169             break;    
170         }
171         
172         AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%lX , Queue Depth: %d\n",
173                 (unsigned long)pPacket, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
174         
175         transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, pPacket->ActualLength + HTC_HDR_LENGTH);       
176        
177         if (transferLength <= target->TargetCreditSize) {
178             creditsRequired = 1;    
179         } else {
180                 /* figure out how many credits this message requires */
181             creditsRequired = transferLength / target->TargetCreditSize;
182             remainder = transferLength % target->TargetCreditSize;
183             
184             if (remainder) {
185                 creditsRequired++;
186             }
187         }
188
189         AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Creds Required:%d   Got:%d\n",
190                             creditsRequired, pEndpoint->CreditDist.TxCredits));
191
192         if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
193
194                 /* not enough credits */
195             if (pPacket->Endpoint == ENDPOINT_0) {
196                     /* leave it in the queue */
197                 break;
198             }
199                 /* invoke the registered distribution function only if this is not
200                  * endpoint 0, we let the driver layer provide more credits if it can.
201                  * We pass the credit distribution list starting at the endpoint in question
202                  * */
203
204                 /* set how many credits we need  */
205             pEndpoint->CreditDist.TxCreditsSeek =
206                                     creditsRequired - pEndpoint->CreditDist.TxCredits;
207             DO_DISTRIBUTION(target,
208                             HTC_CREDIT_DIST_SEEK_CREDITS,
209                             "Seek Credits",
210                             &pEndpoint->CreditDist);
211             pEndpoint->CreditDist.TxCreditsSeek = 0;
212
213             if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
214                     /* still not enough credits to send, leave packet in the queue */
215                 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
216                     (" Not enough credits for ep %d leaving packet in queue..\n",
217                     pPacket->Endpoint));
218                 break;
219             }
220
221         }
222
223         pEndpoint->CreditDist.TxCredits -= creditsRequired;
224         INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired);
225
226             /* check if we need credits back from the target */
227         if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
228                 /* we are getting low on credits, see if we can ask for more from the distribution function */
229             pEndpoint->CreditDist.TxCreditsSeek =
230                         pEndpoint->CreditDist.TxCreditsPerMaxMsg - pEndpoint->CreditDist.TxCredits;
231
232             DO_DISTRIBUTION(target,
233                             HTC_CREDIT_DIST_SEEK_CREDITS,
234                             "Seek Credits",
235                             &pEndpoint->CreditDist);
236
237             pEndpoint->CreditDist.TxCreditsSeek = 0;
238                 /* see if we were successful in getting more */
239             if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
240                     /* tell the target we need credits ASAP! */
241                 sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
242                 INC_HTC_EP_STAT(pEndpoint, TxCreditLowIndications, 1);
243                 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Host Needs Credits  \n"));
244             }
245         }
246                         
247             /* now we can fully dequeue */
248         pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue); 
249             /* save the number of credits this packet consumed */
250         pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired;
251             /* all TX packets are handled asynchronously */
252         pPacket->Completion = HTCSendPktCompletionHandler;
253         pPacket->pContext = target;
254         INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
255             /* save send flags */
256         pPacket->PktInfo.AsTx.SendFlags = sendFlags;
257         pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;         
258         pEndpoint->SeqNo++;
259             /* queue this packet into the caller's queue */
260         HTC_PACKET_ENQUEUE(pQueue,pPacket);
261     }
262     
263     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n"));
264      
265 }
266
267 static void HTCAsyncSendScatterCompletion(HIF_SCATTER_REQ *pScatterReq)
268 {
269     int                 i;    
270     HTC_PACKET          *pPacket;
271     HTC_ENDPOINT        *pEndpoint = (HTC_ENDPOINT *)pScatterReq->Context;
272     HTC_TARGET          *target = (HTC_TARGET *)pEndpoint->target;
273     A_STATUS            status = A_OK;
274     HTC_PACKET_QUEUE    sendCompletes;
275     
276     INIT_HTC_PACKET_QUEUE(&sendCompletes);
277           
278     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCAsyncSendScatterCompletion  TotLen: %d  Entries: %d\n",
279         pScatterReq->TotalLength, pScatterReq->ValidScatterEntries));
280     
281     DEV_FINISH_SCATTER_OPERATION(pScatterReq);
282            
283     if (A_FAILED(pScatterReq->CompletionStatus)) {
284         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Send Scatter Request Failed: %d \n",pScatterReq->CompletionStatus));            
285         status = A_ERROR;
286     }
287     
288         /* walk through the scatter list and process */
289     for (i = 0; i < pScatterReq->ValidScatterEntries; i++) {
290         pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
291         A_ASSERT(pPacket != NULL);
292         pPacket->Status = status;
293         CompleteSentPacket(target,pEndpoint,pPacket);
294             /* add it to the completion queue */
295         HTC_PACKET_ENQUEUE(&sendCompletes, pPacket);      
296     }
297     
298         /* free scatter request */
299     DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
300         /* complete all packets */
301     DO_EP_TX_COMPLETION(pEndpoint,&sendCompletes);
302                
303     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCAsyncSendScatterCompletion \n"));
304 }
305
306     /* drain a queue and send as bundles 
307      * this function may return without fully draining the queue under the following conditions :
308      *    - scatter resources are exhausted
309      *    - a message that will consume a partial credit will stop the bundling process early 
310      *    - we drop below the minimum number of messages for a bundle 
311      * */
312 static void HTCIssueSendBundle(HTC_ENDPOINT      *pEndpoint, 
313                                HTC_PACKET_QUEUE  *pQueue, 
314                                int               *pBundlesSent, 
315                                int               *pTotalBundlesPkts)
316 {
317     int                 pktsToScatter;
318     unsigned int        scatterSpaceRemaining;
319     HIF_SCATTER_REQ     *pScatterReq = NULL;
320     int                 i, packetsInScatterReq;
321     unsigned int        transferLength;
322     HTC_PACKET          *pPacket;
323     A_BOOL              done = FALSE;
324     int                 bundlesSent = 0;
325     int                 totalPktsInBundle = 0;
326     HTC_TARGET          *target = pEndpoint->target;
327     int                 creditRemainder = 0;
328     int                 creditPad;
329     
330     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCIssueSendBundle \n"));
331     
332     while (!done) {
333           
334         pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pQueue);
335         pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle);
336         
337         if (pktsToScatter < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
338                 /* not enough to bundle */
339             break;    
340         }
341         
342         pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device); 
343         
344         if (pScatterReq == NULL) {
345                 /* no scatter resources  */
346             AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("   No more scatter resources \n"));
347             break;    
348         }       
349         
350         AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("   pkts to scatter: %d \n", pktsToScatter));
351         
352         pScatterReq->TotalLength = 0;
353         pScatterReq->ValidScatterEntries = 0;  
354         
355         packetsInScatterReq = 0;
356         scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_SEND_LENGTH(&target->Device);
357         
358         for (i = 0; i < pktsToScatter; i++) {
359             
360             pScatterReq->ScatterList[i].pCallerContexts[0] = NULL;
361             
362             pPacket = HTC_GET_PKT_AT_HEAD(pQueue);        
363             if (pPacket == NULL) {
364                 A_ASSERT(FALSE);
365                 break;    
366             }
367             
368             creditPad = 0;
369             transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, 
370                                                       pPacket->ActualLength + HTC_HDR_LENGTH);               
371                 /* see if the padded transfer length falls on a credit boundary */         
372             creditRemainder = transferLength % target->TargetCreditSize;
373                                 
374             if (creditRemainder != 0) {
375                     /* the transfer consumes a "partial" credit, this packet cannot be bundled unless
376                      * we add additional "dummy" padding (max 255 bytes) to consume the entire credit 
377                      *** NOTE: only allow the send padding if the endpoint is allowed to */
378                 if (pEndpoint->LocalConnectionFlags & HTC_LOCAL_CONN_FLAGS_ENABLE_SEND_BUNDLE_PADDING) {
379                     if (transferLength < target->TargetCreditSize) {
380                             /* special case where the transfer is less than a credit */
381                         creditPad = target->TargetCreditSize - transferLength;                    
382                     } else {
383                         creditPad = creditRemainder;    
384                     }
385                                     
386                         /* now check to see if we can indicate padding in the HTC header */
387                     if ((creditPad > 0) && (creditPad <= 255)) {
388                             /* adjust the transferlength of this packet with the new credit padding */
389                         transferLength += creditPad;            
390                     } else {
391                             /* the amount to pad is too large, bail on this packet, we have to 
392                              * send it using the non-bundled method */
393                         pPacket = NULL;
394                     }
395                 } else {
396                         /* bail on this packet, user does not want padding applied */
397                     pPacket = NULL;    
398                 }
399             }                       
400                        
401             if (NULL == pPacket) {
402                     /* can't bundle */
403                 done = TRUE;
404                 break;    
405             }         
406                
407             if (scatterSpaceRemaining < transferLength) {
408                     /* exceeds what we can transfer */
409                 break;    
410             }
411             
412             scatterSpaceRemaining -= transferLength;
413                 /* now remove it from the queue */ 
414             pPacket = HTC_PACKET_DEQUEUE(pQueue);           
415                 /* save it in the scatter list */
416             pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket;            
417                 /* prepare packet and flag message as part of a send bundle */               
418             HTC_PREPARE_SEND_PKT(pPacket,
419                                  pPacket->PktInfo.AsTx.SendFlags | HTC_FLAGS_SEND_BUNDLE, 
420                                  creditPad,                                 
421                                  pPacket->PktInfo.AsTx.SeqNo); 
422             pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer;
423             pScatterReq->ScatterList[i].Length = transferLength;
424             A_ASSERT(transferLength);
425             pScatterReq->TotalLength += transferLength;
426             pScatterReq->ValidScatterEntries++;
427             packetsInScatterReq++;             
428             AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("  %d, Adding packet : 0x%lX, len:%d (remaining space:%d) \n", 
429                     i, (unsigned long)pPacket,transferLength,scatterSpaceRemaining));                                                      
430         }
431                     
432         if (packetsInScatterReq >= HTC_MIN_HTC_MSGS_TO_BUNDLE) {          
433                 /* send path is always asynchronous */
434             pScatterReq->CompletionRoutine = HTCAsyncSendScatterCompletion;
435             pScatterReq->Context = pEndpoint;
436             bundlesSent++;
437             totalPktsInBundle += packetsInScatterReq;
438             packetsInScatterReq = 0;
439             AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Send Scatter total bytes: %d , entries: %d\n",
440                                 pScatterReq->TotalLength,pScatterReq->ValidScatterEntries));
441             DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_WRITE, DEV_SCATTER_ASYNC);
442                 /* we don't own this anymore */
443             pScatterReq = NULL;
444                 /* try to send some more */
445             continue;               
446         } 
447         
448             /* not enough packets to use the scatter request, cleanup */
449         if (pScatterReq != NULL) {
450             if (packetsInScatterReq > 0) {
451                     /* work backwards to requeue requests */
452                 for (i = (packetsInScatterReq - 1); i >= 0; i--) {
453                     pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
454                     if (pPacket != NULL) {
455                             /* undo any prep */
456                         HTC_UNPREPARE_SEND_PKT(pPacket);
457                             /* queue back to the head */
458                         HTC_PACKET_ENQUEUE_TO_HEAD(pQueue,pPacket);   
459                     }  
460                 }  
461             }               
462             DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);    
463         }  
464         
465         /* if we get here, we sent all that we could, get out */
466         break;  
467         
468     }
469     
470     *pBundlesSent = bundlesSent;
471     *pTotalBundlesPkts = totalPktsInBundle;
472     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCIssueSendBundle (sent:%d) \n",bundlesSent));  
473      
474     return; 
475 }
476
477 /*
478  * if there are no credits, the packet(s) remains in the queue.
479  * this function returns the result of the attempt to send a queue of HTC packets */
480 static HTC_SEND_QUEUE_RESULT HTCTrySend(HTC_TARGET       *target,
481                                         HTC_ENDPOINT     *pEndpoint,
482                                         HTC_PACKET_QUEUE *pCallersSendQueue)
483 {
484     HTC_PACKET_QUEUE      sendQueue; /* temp queue to hold packets at various stages */
485     HTC_PACKET            *pPacket;
486     int                   bundlesSent;
487     int                   pktsInBundles;
488     int                   overflow;
489     HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;
490     
491     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (Queue:0x%lX Depth:%d)\n",
492             (unsigned long)pCallersSendQueue, 
493             (pCallersSendQueue == NULL) ? 0 : HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue)));
494
495         /* init the local send queue */
496     INIT_HTC_PACKET_QUEUE(&sendQueue);
497     
498     do {
499         
500         if (NULL == pCallersSendQueue) {
501                 /* caller didn't provide a queue, just wants us to check queues and send */
502             break;    
503         }
504         
505         if (HTC_QUEUE_EMPTY(pCallersSendQueue)) {
506                 /* empty queue */
507             result = HTC_SEND_QUEUE_DROP;
508             break;    
509         }
510   
511         if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >= pEndpoint->MaxTxQueueDepth) {
512                     /* we've already overflowed */
513             overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);    
514         } else {
515                 /* figure out how much we will overflow by */
516             overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
517             overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue); 
518                 /* figure out how much we will overflow the TX queue by */
519             overflow -= pEndpoint->MaxTxQueueDepth;     
520         }
521                      
522             /* if overflow is negative or zero, we are okay */    
523         if (overflow > 0) {
524             AR_DEBUG_PRINTF(ATH_DEBUG_SEND, 
525                 (" Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d \n",
526                 pEndpoint->Id, overflow, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue), pEndpoint->MaxTxQueueDepth));      
527         }   
528         if ((overflow <= 0) || (pEndpoint->EpCallBacks.EpSendFull == NULL)) {
529                 /* all packets will fit or caller did not provide send full indication handler
530                  * --  just move all of them to the local sendQueue object */
531             HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue, pCallersSendQueue);           
532         } else {
533             int               i;
534             int               goodPkts = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) - overflow;
535                         
536             A_ASSERT(goodPkts >= 0);
537                 /* we have overflowed, and a callback is provided */        
538                 /* dequeue all non-overflow packets into the sendqueue */
539             for (i = 0; i < goodPkts; i++) {
540                     /* pop off caller's queue*/
541                 pPacket = HTC_PACKET_DEQUEUE(pCallersSendQueue);
542                 A_ASSERT(pPacket != NULL);
543                     /* insert into local queue */
544                 HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
545             }
546             
547                 /* the caller's queue has all the packets that won't fit*/                
548                 /* walk through the caller's queue and indicate each one to the send full handler */            
549             ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue->QueueHead, pPacket, HTC_PACKET, ListLink) {            
550                 
551                 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Indicating overflowed TX packet: 0x%lX \n", 
552                                             (unsigned long)pPacket));    
553                 if (pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext,
554                                                       pPacket) == HTC_SEND_FULL_DROP) {
555                         /* callback wants the packet dropped */
556                     INC_HTC_EP_STAT(pEndpoint, TxDropped, 1);
557                         /* leave this one in the caller's queue for cleanup */
558                 } else {
559                         /* callback wants to keep this packet, remove from caller's queue */
560                     HTC_PACKET_REMOVE(pCallersSendQueue, pPacket);
561                         /* put it in the send queue */
562                     HTC_PACKET_ENQUEUE(&sendQueue,pPacket);                                      
563                 }
564                 
565             } ITERATE_END;
566             
567             if (HTC_QUEUE_EMPTY(&sendQueue)) {
568                     /* no packets made it in, caller will cleanup */
569                 result = HTC_SEND_QUEUE_DROP;
570                 break;   
571             } 
572         }
573         
574     } while (FALSE);
575     
576     if (result != HTC_SEND_QUEUE_OK) {
577         AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend:  \n"));
578         return result;
579     }
580
581     LOCK_HTC_TX(target);
582     
583     if (!HTC_QUEUE_EMPTY(&sendQueue)) {
584             /* transfer packets */
585         HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue,&sendQueue);
586         A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue));
587         INIT_HTC_PACKET_QUEUE(&sendQueue); 
588     }
589     
590         /* increment tx processing count on entry */    
591     pEndpoint->TxProcessCount++;
592     if (pEndpoint->TxProcessCount > 1) {
593             /* another thread or task is draining the TX queues on this endpoint
594              * that thread will reset the tx processing count when the queue is drained */
595         pEndpoint->TxProcessCount--;
596         UNLOCK_HTC_TX(target);
597         AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (busy) \n"));
598         return HTC_SEND_QUEUE_OK; 
599     }
600     
601     /***** beyond this point only 1 thread may enter ******/
602             
603         /* now drain the endpoint TX queue for transmission as long as we have enough
604          * credits */
605     while (TRUE) {
606           
607         if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) {
608             break;
609         }
610                 
611             /* get all the packets for this endpoint that we can for this pass */
612         GetHTCSendPackets(target, pEndpoint, &sendQueue);        
613      
614         if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) {
615                 /* didn't get any packets due to a lack of credits */
616             break;    
617         }
618         
619         UNLOCK_HTC_TX(target);
620         
621             /* any packets to send are now in our local send queue */    
622          
623         bundlesSent = 0;
624         pktsInBundles = 0;
625      
626         while (TRUE) {
627             
628                 /* try to send a bundle on each pass */            
629             if ((target->SendBundlingEnabled) &&
630                     (HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
631                  int temp1,temp2;       
632                     /* bundling is enabled and there is at least a minimum number of packets in the send queue
633                      * send what we can in this pass */                       
634                  HTCIssueSendBundle(pEndpoint, &sendQueue, &temp1, &temp2);
635                  bundlesSent += temp1;
636                  pktsInBundles += temp2;
637             }
638         
639                 /* if not bundling or there was a packet that could not be placed in a bundle, pull it out
640                  * and send it the normal way */
641             pPacket = HTC_PACKET_DEQUEUE(&sendQueue);
642             if (NULL == pPacket) {
643                     /* local queue is fully drained */
644                 break;    
645             }
646             HTC_PREPARE_SEND_PKT(pPacket,
647                                  pPacket->PktInfo.AsTx.SendFlags,
648                                  0,
649                                  pPacket->PktInfo.AsTx.SeqNo);  
650             HTCIssueSend(target, pPacket);
651             
652                 /* go back and see if we can bundle some more */
653         }
654         
655         LOCK_HTC_TX(target);
656         
657         INC_HTC_EP_STAT(pEndpoint, TxBundles, bundlesSent);
658         INC_HTC_EP_STAT(pEndpoint, TxPacketsBundled, pktsInBundles);
659         
660     }
661         
662         /* done with this endpoint, we can clear the count */
663     pEndpoint->TxProcessCount = 0;
664     UNLOCK_HTC_TX(target);
665     
666     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend:  \n"));
667
668     return HTC_SEND_QUEUE_OK;
669 }
670
671 A_STATUS  HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
672 {
673     HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
674     HTC_ENDPOINT    *pEndpoint;
675     HTC_PACKET      *pPacket;
676
677     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: 0x%lX, Pkts %d \n",
678                     (unsigned long)pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
679     
680         /* get packet at head to figure out which endpoint these packets will go into */
681     pPacket = HTC_GET_PKT_AT_HEAD(pPktQueue);
682     if (NULL == pPacket) {
683         AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
684         return A_EINVAL;   
685     }
686     
687     AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
688     pEndpoint = &target->EndPoint[pPacket->Endpoint];
689     
690     HTCTrySend(target, pEndpoint, pPktQueue);
691
692         /* do completion on any packets that couldn't get in */
693     if (!HTC_QUEUE_EMPTY(pPktQueue)) {        
694         
695         HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
696             if (HTC_STOPPING(target)) {
697                 pPacket->Status = A_ECANCELED;
698             } else {
699                 pPacket->Status = A_NO_RESOURCE;
700             } 
701         } HTC_PACKET_QUEUE_ITERATE_END;
702                    
703         DO_EP_TX_COMPLETION(pEndpoint,pPktQueue);
704     }
705
706     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
707
708     return A_OK;   
709 }
710
711 /* HTC API - HTCSendPkt */
712 A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
713 {
714     HTC_PACKET_QUEUE queue;
715     
716     AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
717                     ("+-HTCSendPkt: Enter endPointId: %d, buffer: 0x%lX, length: %d \n",
718                     pPacket->Endpoint, (unsigned long)pPacket->pBuffer, pPacket->ActualLength));                   
719     INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket); 
720     return HTCSendPktsMultiple(HTCHandle, &queue);
721 }
722
723 /* check TX queues to drain because of credit distribution update */
724 static INLINE void HTCCheckEndpointTxQueues(HTC_TARGET *target)
725 {
726     HTC_ENDPOINT                *pEndpoint;
727     HTC_ENDPOINT_CREDIT_DIST    *pDistItem;
728
729     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCCheckEndpointTxQueues \n"));
730     pDistItem = target->EpCreditDistributionListHead;
731
732         /* run through the credit distribution list to see
733          * if there are packets queued
734          * NOTE: no locks need to be taken since the distribution list
735          * is not dynamic (cannot be re-ordered) and we are not modifying any state */
736     while (pDistItem != NULL) {
737         pEndpoint = (HTC_ENDPOINT *)pDistItem->pHTCReserved;
738
739         if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) > 0) {
740             AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n",
741                     pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
742                 /* try to start the stalled queue, this list is ordered by priority.
743                  * Highest priority queue get's processed first, if there are credits available the
744                  * highest priority queue will get a chance to reclaim credits from lower priority
745                  * ones */
746             HTCTrySend(target, pEndpoint, NULL);
747         }
748
749         pDistItem = pDistItem->pNext;
750     }
751
752     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCCheckEndpointTxQueues \n"));
753 }
754
755 /* process credit reports and call distribution function */
756 void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
757 {
758     int             i;
759     HTC_ENDPOINT    *pEndpoint;
760     int             totalCredits = 0;
761     A_BOOL          doDist = FALSE;
762
763     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries));
764
765         /* lock out TX while we update credits */
766     LOCK_HTC_TX(target);
767
768     for (i = 0; i < NumEntries; i++, pRpt++) {
769         if (pRpt->EndpointID >= ENDPOINT_MAX) {
770             AR_DEBUG_ASSERT(FALSE);
771             break;
772         }
773
774         pEndpoint = &target->EndPoint[pRpt->EndpointID];
775
776         AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("  Endpoint %d got %d credits \n",
777                 pRpt->EndpointID, pRpt->Credits));
778
779
780 #ifdef HTC_EP_STAT_PROFILING
781
782         INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1);
783         INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, pRpt->Credits);
784
785         if (FromEndpoint == pRpt->EndpointID) {
786                 /* this credit report arrived on the same endpoint indicating it arrived in an RX
787                  * packet */
788             INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, pRpt->Credits);
789             INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1);
790         } else if (FromEndpoint == ENDPOINT_0) {
791                 /* this credit arrived on endpoint 0 as a NULL message */
792             INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, pRpt->Credits);
793             INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1);
794         } else {
795                 /* arrived on another endpoint */
796             INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, pRpt->Credits);
797             INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1);
798         }
799
800 #endif
801
802         if (ENDPOINT_0 == pRpt->EndpointID) {
803                 /* always give endpoint 0 credits back */
804             pEndpoint->CreditDist.TxCredits += pRpt->Credits;
805         } else {
806                 /* for all other endpoints, update credits to distribute, the distribution function
807                  * will handle giving out credits back to the endpoints */
808             pEndpoint->CreditDist.TxCreditsToDist += pRpt->Credits;
809                 /* flag that we have to do the distribution */
810             doDist = TRUE;
811         }
812         
813             /* refresh tx depth for distribution function that will recover these credits
814              * NOTE: this is only valid when there are credits to recover! */
815         pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
816         
817         totalCredits += pRpt->Credits;
818     }
819
820     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("  Report indicated %d credits to distribute \n", totalCredits));
821
822     if (doDist) {
823             /* this was a credit return based on a completed send operations
824              * note, this is done with the lock held */
825         DO_DISTRIBUTION(target,
826                         HTC_CREDIT_DIST_SEND_COMPLETE,
827                         "Send Complete",
828                         target->EpCreditDistributionListHead->pNext);
829     }
830
831     UNLOCK_HTC_TX(target);
832
833     if (totalCredits) {
834         HTCCheckEndpointTxQueues(target);
835     }
836
837     AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n"));
838 }
839
840 /* flush endpoint TX queue */
841 static void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag)
842 {
843     HTC_PACKET          *pPacket;
844     HTC_PACKET_QUEUE    discardQueue;
845     HTC_PACKET_QUEUE    container;
846
847         /* initialize the discard queue */
848     INIT_HTC_PACKET_QUEUE(&discardQueue);
849
850     LOCK_HTC_TX(target);
851
852         /* interate from the front of the TX queue and flush out packets */
853     ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket, HTC_PACKET, ListLink) {
854
855             /* check for removal */
856         if ((HTC_TX_PACKET_TAG_ALL == Tag) || (Tag == pPacket->PktInfo.AsTx.Tag)) {
857                 /* remove from queue */
858             HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket);
859                 /* add it to the discard pile */
860             HTC_PACKET_ENQUEUE(&discardQueue, pPacket);
861         }
862
863     } ITERATE_END;
864
865     UNLOCK_HTC_TX(target);
866
867         /* empty the discard queue */
868     while (1) {
869         pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
870         if (NULL == pPacket) {
871             break;
872         }
873         pPacket->Status = A_ECANCELED;
874         AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("  Flushing TX packet:0x%lX, length:%d, ep:%d tag:0x%X \n",
875                 (unsigned long)pPacket, pPacket->ActualLength, pPacket->Endpoint, pPacket->PktInfo.AsTx.Tag));
876         INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
877         DO_EP_TX_COMPLETION(pEndpoint,&container);
878     }
879
880 }
881
882 void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist)
883 {
884     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("--- EP : %d  ServiceID: 0x%X    --------------\n",
885                         pEPDist->Endpoint, pEPDist->ServiceID));
886     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" this:0x%lX next:0x%lX prev:0x%lX\n",
887                 (unsigned long)pEPDist, (unsigned long)pEPDist->pNext, (unsigned long)pEPDist->pPrev));
888     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" DistFlags          : 0x%X \n", pEPDist->DistFlags));
889     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsNorm      : %d \n", pEPDist->TxCreditsNorm));
890     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsMin       : %d \n", pEPDist->TxCreditsMin));
891     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCredits          : %d \n", pEPDist->TxCredits));
892     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsAssigned  : %d \n", pEPDist->TxCreditsAssigned));
893     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsSeek      : %d \n", pEPDist->TxCreditsSeek));
894     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditSize       : %d \n", pEPDist->TxCreditSize));
895     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsPerMaxMsg : %d \n", pEPDist->TxCreditsPerMaxMsg));
896     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsToDist    : %d \n", pEPDist->TxCreditsToDist));
897     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxQueueDepth       : %d \n", 
898                     HTC_PACKET_QUEUE_DEPTH(&((HTC_ENDPOINT *)pEPDist->pHTCReserved)->TxQueue)));                                      
899     AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("----------------------------------------------------\n"));
900 }
901
902 void DumpCreditDistStates(HTC_TARGET *target)
903 {
904     HTC_ENDPOINT_CREDIT_DIST *pEPList = target->EpCreditDistributionListHead;
905
906     while (pEPList != NULL) {
907         DumpCreditDist(pEPList);
908         pEPList = pEPList->pNext;
909     }
910
911     if (target->DistributeCredits != NULL) {
912         DO_DISTRIBUTION(target,
913                         HTC_DUMP_CREDIT_STATE,
914                         "Dump State",
915                         NULL);
916     }
917 }
918
919 /* flush all send packets from all endpoint queues */
920 void HTCFlushSendPkts(HTC_TARGET *target)
921 {
922     HTC_ENDPOINT    *pEndpoint;
923     int             i;
924
925     if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) {
926         DumpCreditDistStates(target);
927     }
928
929     for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
930         pEndpoint = &target->EndPoint[i];
931         if (pEndpoint->ServiceID == 0) {
932                 /* not in use.. */
933             continue;
934         }
935         HTCFlushEndpointTX(target,pEndpoint,HTC_TX_PACKET_TAG_ALL);
936     }
937
938
939 }
940
941 /* HTC API to flush an endpoint's TX queue*/
942 void HTCFlushEndpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, HTC_TX_TAG Tag)
943 {
944     HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
945     HTC_ENDPOINT    *pEndpoint = &target->EndPoint[Endpoint];
946
947     if (pEndpoint->ServiceID == 0) {
948         AR_DEBUG_ASSERT(FALSE);
949         /* not in use.. */
950         return;
951     }
952
953     HTCFlushEndpointTX(target, pEndpoint, Tag);
954 }
955
956 /* HTC API to indicate activity to the credit distribution function */
957 void HTCIndicateActivityChange(HTC_HANDLE      HTCHandle,
958                                HTC_ENDPOINT_ID Endpoint,
959                                A_BOOL          Active)
960 {
961     HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
962     HTC_ENDPOINT    *pEndpoint = &target->EndPoint[Endpoint];
963     A_BOOL          doDist = FALSE;
964
965     if (pEndpoint->ServiceID == 0) {
966         AR_DEBUG_ASSERT(FALSE);
967         /* not in use.. */
968         return;
969     }
970
971     LOCK_HTC_TX(target);
972
973     if (Active) {
974         if (!(pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE)) {
975                 /* mark active now */
976             pEndpoint->CreditDist.DistFlags |= HTC_EP_ACTIVE;
977             doDist = TRUE;
978         }
979     } else {
980         if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
981                 /* mark inactive now */
982             pEndpoint->CreditDist.DistFlags &= ~HTC_EP_ACTIVE;
983             doDist = TRUE;
984         }
985     }
986
987     if (doDist) {
988             /* indicate current Tx Queue depth to the credit distribution function */
989         pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
990         /* do distribution again based on activity change
991          * note, this is done with the lock held */
992         DO_DISTRIBUTION(target,
993                         HTC_CREDIT_DIST_ACTIVITY_CHANGE,
994                         "Activity Change",
995                         target->EpCreditDistributionListHead->pNext);
996     }
997
998     UNLOCK_HTC_TX(target);
999
1000     if (doDist && !Active) {
1001         /* if a stream went inactive and this resulted in a credit distribution change,
1002          * some credits may now be available for HTC packets that are stuck in
1003          * HTC queues */
1004         HTCCheckEndpointTxQueues(target);
1005     }
1006 }
1007
1008 A_BOOL HTCIsEndpointActive(HTC_HANDLE      HTCHandle,
1009                            HTC_ENDPOINT_ID Endpoint)
1010 {
1011     HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
1012     HTC_ENDPOINT    *pEndpoint = &target->EndPoint[Endpoint];
1013
1014     if (pEndpoint->ServiceID == 0) {
1015         return FALSE;
1016     }
1017     
1018     if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
1019         return TRUE;
1020     }
1021     
1022     return FALSE;
1023 }