Initial cut of the open ath9k htc firmware.
[open-ath9k-htc-firmware.git] / target_firmware / magpie_fw_dev / target / htc / htc.c
1 /*
2  * @File: 
3  * 
4  * @Abstract: host target communications
5  * 
6  * @Notes: 
7  * 
8  * 
9  * Copyright (c) 2007 Atheros Communications Inc.
10  * All rights reserved.
11  *
12  */
13 #include <osapi.h>
14 #include <Magpie_api.h> 
15 #include <htc.h>
16 #include <htc_api.h>
17 #include <hif_api.h>
18 #include <adf_os_mem.h> 
19 #include <adf_os_io.h> 
20
21 #include "htc_internal.h" 
22
23 #define A_UNCACHED_ADDR(addr) addr
24
25 /* prototypes */
26 LOCAL void HTCControlSvcProcessMsg(HTC_ENDPOINT_ID EndpointID, adf_nbuf_t hdr_buf, adf_nbuf_t pBuffers, void *arg);
27 LOCAL void HTCControlSvcProcessSendComplete(HTC_ENDPOINT_ID EndpointID, adf_nbuf_t pBuffers, void *arg);
28 LOCAL void HTCMsgRecvHandler(adf_nbuf_t hdr_buf, adf_nbuf_t buf, void *context);
29 LOCAL void HTCSendDoneHandler(adf_nbuf_t buf, void *context);
30 LOCAL void HTCFreeMsgBuffer(HTC_CONTEXT *pHTC, adf_nbuf_t pBuffer);
31 LOCAL adf_nbuf_t HTCAllocMsgBuffer(HTC_CONTEXT *pHTC);
32 LOCAL void HTCCheckAndSendCreditReport(HTC_CONTEXT *pHTC, A_UINT32 EpMask, HTC_ENDPOINT *pEndpoint, HTC_ENDPOINT_ID Id);
33 LOCAL void AdjustCreditThreshold(HTC_ENDPOINT  *pEndpoint);
34 LOCAL void HTC_AssembleBuffers(HTC_CONTEXT *pHTC, int Count, int Size);
35 LOCAL htc_handle_t _HTC_Init(/*A_UINT32 dataAddr,*/
36         HTC_SETUP_COMPLETE_CB SetupComplete,
37         HTC_CONFIG *pConfig);
38 LOCAL void _HTC_RegisterService(htc_handle_t handle, HTC_SERVICE *pService);
39 LOCAL void _HTC_Ready(htc_handle_t handle);
40 LOCAL void ReturnBuffers(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID, adf_nbuf_t pBuffers, A_BOOL sendCreditFlag);
41 LOCAL void _HTC_ReturnBuffers(htc_handle_t handle, HTC_ENDPOINT_ID EndpointID, adf_nbuf_t pBuffers);
42 LOCAL void _HTC_ReturnBuffersList(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID, adf_nbuf_queue_t bufHead);
43 LOCAL void _HTC_SendMsg(htc_handle_t handle, HTC_ENDPOINT_ID EndpointID, adf_nbuf_t pBuffers);
44 void _HTC_PauseRecv(HTC_ENDPOINT_ID EndpointID);
45 void _HTC_ResumeRecv(HTC_ENDPOINT_ID EndpointID);
46 LOCAL void HTCProcessConnectMsg(HTC_CONTEXT *pHTC, HTC_CONNECT_SERVICE_MSG *pMsg);
47 LOCAL void HTCProcessConfigPipeMsg(HTC_CONTEXT *pHTC, HTC_CONFIG_PIPE_MSG *pMsg);
48 LOCAL void RedistributeCredit(adf_nbuf_t buf, int toPipeId);                         
49 LOCAL void _HTC_Shutdown(htc_handle_t htcHandle);
50
51 /* macro to check if the service wants to prevent credit dribbling by using 
52    a dynamic threshold */
53 #define CHECK_AND_ADJUST_CREDIT_THRESHOLD(pEndpoint)                    \
54         if ((pEndpoint)->ConnectionFlags & HTC_CONNECT_FLAGS_REDUCE_CREDIT_DRIBBLE) { \
55                 AdjustCreditThreshold((pEndpoint));                     \
56         }    
57
58 LOCAL void HTC_AssembleBuffers(HTC_CONTEXT *pHTC, int Count, int Size)
59 {
60         BUF_Pool_create_pool(pHTC->PoolHandle, POOL_ID_HTC_CONTROL, Count, Size);       
61 }
62
63 LOCAL htc_handle_t _HTC_Init(HTC_SETUP_COMPLETE_CB SetupComplete,
64                              HTC_CONFIG *pConfig)
65 {
66         HIF_CALLBACK hifCBConfig;
67         HTC_CONTEXT *pHTC;    
68     
69         pHTC = (HTC_CONTEXT *)adf_os_mem_alloc(sizeof(HTC_CONTEXT));
70     
71         adf_os_mem_zero(pHTC, sizeof(HTC_CONTEXT));
72
73         pHTC->OSHandle = pConfig->OSHandle;
74         pHTC->PoolHandle = pConfig->PoolHandle;
75         pHTC->hifHandle = pConfig->HIFHandle;
76                         
77         hifCBConfig.send_buf_done = A_INDIR(htc._HTC_SendDoneHandler);
78         hifCBConfig.recv_buf = A_INDIR(htc._HTC_MsgRecvHandler);
79         hifCBConfig.context = pHTC;
80     
81         /* initialize hardware layer */
82         HIF_register_callback(pConfig->HIFHandle, &hifCBConfig);
83                              
84         /* see if the host wants us to override the number of ctrl buffers */
85         pHTC->NumBuffersForCreditRpts = 0;
86     
87         if (0 == pHTC->NumBuffersForCreditRpts) {
88                 /* nothing to override, simply set default */
89                 pHTC->NumBuffersForCreditRpts = HTC_DEFAULT_NUM_CTRL_BUFFERS; 
90         }    
91     
92         pHTC->MaxEpPendingCreditRpts = 0;
93     
94         if (0 == pHTC->MaxEpPendingCreditRpts) {
95                 pHTC->MaxEpPendingCreditRpts = HTC_DEFAULT_MAX_EP_PENDING_CREDIT_REPORTS;    
96         }
97         /* calculate the total allocation size based on the number of credit report buffers */
98         pHTC->CtrlBufferAllocSize = MIN_CREDIT_BUFFER_ALLOC_SIZE * pHTC->NumBuffersForCreditRpts;
99         /* we need at least enough buffer space for 1 ctrl message */
100         pHTC->CtrlBufferAllocSize = A_MAX(pHTC->CtrlBufferAllocSize,MAX_HTC_SETUP_MSG_SIZE);
101     
102         /* save the size of each buffer/credit we will receive */
103         pHTC->RecvBufferSize = pConfig->CreditSize; //RecvBufferSize;
104         pHTC->TotalCredits = pConfig->CreditNumber;
105         pHTC->TotalCreditsAssigned = 0;
106      
107         /* setup the pseudo service that handles HTC control messages */
108         pHTC->HTCControlService.ProcessRecvMsg = A_INDIR(htc._HTC_ControlSvcProcessMsg);
109         pHTC->HTCControlService.ProcessSendBufferComplete = A_INDIR(htc._HTC_ControlSvcProcessSendComplete);
110         pHTC->HTCControlService.TrailerSpcCheckLimit = HTC_CTRL_BUFFER_CHECK_SIZE;
111         pHTC->HTCControlService.MaxSvcMsgSize = MAX_HTC_SETUP_MSG_SIZE;
112         pHTC->HTCControlService.ServiceCtx = pHTC;
113     
114         /* automatically register this pseudo service to endpoint 1 */
115         pHTC->Endpoints[ENDPOINT0].pService = &pHTC->HTCControlService;
116         HIF_get_default_pipe(pHTC->hifHandle, &pHTC->Endpoints[ENDPOINT0].UpLinkPipeID, 
117                              &pHTC->Endpoints[ENDPOINT0].DownLinkPipeID);
118     
119         /* Initialize control pipe so we could receive the HTC control packets */
120         // @TODO: msg size!
121         HIF_config_pipe(pHTC->hifHandle, pHTC->Endpoints[ENDPOINT0].UpLinkPipeID, 1);    
122     
123         /* set the first free endpoint */
124         pHTC->CurrentEpIndex = ENDPOINT1;
125         pHTC->SetupCompleteCb = SetupComplete;
126     
127         /* setup buffers for just the setup phase, we only need 1 buffer to handle
128          * setup */
129         HTC_AssembleBuffers(pHTC, 4, MAX_HTC_SETUP_MSG_SIZE);
130    
131         /* start hardware layer so that we can queue buffers */
132         HIF_start(pHTC->hifHandle);
133     
134         return pHTC;
135 }
136
137 LOCAL void _HTC_Shutdown(htc_handle_t htcHandle)
138 {
139         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;
140     
141         adf_os_mem_free(pHTC);
142 }
143
144 LOCAL void _HTC_RegisterService(htc_handle_t htcHandle, HTC_SERVICE *pService)
145 {
146         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;
147     
148         /* add it to the list */
149         pService->pNext = pHTC->pServiceList;
150         pHTC->pServiceList = pService;
151 }
152
153 LOCAL void _HTC_Ready(htc_handle_t htcHandle)
154 {
155         adf_nbuf_t pBuffer;
156         HTC_READY_MSG *pReady;
157         a_uint8_t *addr;
158         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;
159     
160         pBuffer = HTCAllocMsgBuffer(pHTC);
161        
162         /* an optimization... the header length is chosen to
163          * be aligned on a 16 bit bounday, the fields in the message are designed to
164          * be aligned */
165         addr = adf_nbuf_put_tail(pBuffer, sizeof(HTC_READY_MSG));       
166         pReady = (HTC_READY_MSG *)addr;     
167         A_MEMZERO(pReady,sizeof(HTC_READY_MSG));  
168         pReady->MessageID = adf_os_htons(HTC_MSG_READY_ID);
169         pReady->CreditSize = adf_os_htons((A_UINT16)pHTC->RecvBufferSize);
170         pReady->CreditCount = adf_os_htons((A_UINT16)pHTC->TotalCredits);
171         pReady->MaxEndpoints = ENDPOINT_MAX;
172        
173         /* send out the message */
174         HTC_SendMsg(pHTC, ENDPOINT0, pBuffer);
175         /* now we need to wait for service connection requests */
176 }
177
178 LOCAL void ReturnBuffers(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID,
179                          adf_nbuf_t pBuffers, A_BOOL sendCreditFlag)
180 {   
181         int         nbufs = 1;
182         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;
183     
184         /* supply some head-room again */
185         adf_nbuf_push_head(pBuffers, HTC_HDR_LENGTH);
186               
187         /* enqueue all buffers to the single mailbox */
188         HIF_return_recv_buf(pHTC->hifHandle, pHTC->Endpoints[EndpointID].UpLinkPipeID, pBuffers);    
189      
190         if (pHTC->StateFlags & HTC_STATE_SETUP_COMPLETE) {       
191                 A_UINT32    epCreditMask = (1 << EndpointID);
192                 /* we are running normally */
193                 /* update pending credit counts with the number of buffers that were added */
194                 pHTC->Endpoints[EndpointID].CreditsToReturn += (A_INT16)nbufs;
195                 pHTC->Endpoints[EndpointID].CreditsConsumed -= (A_INT16)nbufs;  
196                 /* update bit map that this endpoint has non-zero credits */
197                 pHTC->EpCreditPendingMap |= epCreditMask; 
198
199                 if (sendCreditFlag) {
200                         HTCCheckAndSendCreditReport(pHTC, epCreditMask,&pHTC->Endpoints[EndpointID],EndpointID);
201                 }
202
203         } else {
204                 /* we have not started yet so all return operations are simply adding buffers
205                  * to the interface at startup, so we can keep track of how many total 
206                  * credits we get */
207                 /* update global count that will be returned to the host */
208                 pHTC->TotalCredits += nbufs;
209         }     
210 }
211
212 LOCAL void _HTC_ReturnBuffersList(htc_handle_t htcHandle,
213                                   HTC_ENDPOINT_ID EndpointID,
214                                   adf_nbuf_queue_t bufHead)
215 {
216         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;
217         adf_nbuf_t netbuf, tmpNbuf;
218
219         /* retrieve each nbuf in the queue */
220         netbuf = adf_nbuf_queue_first(&bufHead);
221
222         while (netbuf) {
223
224                 tmpNbuf = netbuf;
225                 netbuf = adf_nbuf_queue_next(netbuf);
226
227                 ReturnBuffers(htcHandle, EndpointID, tmpNbuf, FALSE);
228         }
229
230         HTCCheckAndSendCreditReport(pHTC, (1 << EndpointID),&pHTC->Endpoints[EndpointID],EndpointID);
231 }
232
233 LOCAL void _HTC_ReturnBuffers(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID,
234                               adf_nbuf_t pBuffers)
235 {
236         ReturnBuffers(htcHandle, EndpointID, pBuffers, TRUE);
237 }
238  
239 LOCAL void _HTC_SendMsg(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID,
240                         adf_nbuf_t pBuffers)
241 {
242         HTC_FRAME_HDR *pHTCHdr;
243         int totsz;
244         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;  
245         HTC_BUF_CONTEXT *ctx;
246     
247         ctx = (HTC_BUF_CONTEXT *)adf_nbuf_get_priv(pBuffers);
248     
249         /* init total size (this does not include the space we will put in for the HTC header) */
250         totsz = adf_nbuf_len(pBuffers);
251     
252         /* the first buffer stores the header */
253         /* back up buffer by a header size when we pass it down, by agreed upon convention the caller
254          * points the buffer to it's payload and leaves head room for the HTC header  
255          * Note: in HTCSendDoneHandler(), we undo this so that the caller get's it's buffer
256          * back untainted */   
257         pHTCHdr = (HTC_FRAME_HDR *)adf_nbuf_push_head(pBuffers, HTC_HDR_LENGTH);
258     
259         /* flag that this is the header buffer that was modified */
260         ctx->htc_flags |= HTC_FLAGS_BUF_HDR;   
261         /* mark where this buffer came from */
262         ctx->end_point = EndpointID;      
263         /* the header start is ALWAYS aligned since we DMA it directly */
264
265         /* set some fields, the rest of them will be filled below when we check for
266          * trailer space */
267         pHTCHdr->Flags = 0;
268         pHTCHdr->EndpointID = EndpointID;    
269        
270         /* check opportunistically if we can return any reports via a trailer */
271         do {
272                 int               room,i,totalReportBytes;
273                 A_UINT32          creditsPendingMap, compareMask;
274                 HTC_CREDIT_REPORT *pCreditRpt;
275                 HTC_RECORD_HDR    *pRecHdr;
276                 int               pipeMaxLen;
277                 A_UINT32          roomForPipeMaxLen;
278                           
279                 /* figure out how much room the last buffer can spare */
280                 pipeMaxLen = HIF_get_max_msg_len(pHTC->hifHandle,
281                                                  pHTC->Endpoints[EndpointID].DownLinkPipeID);
282                 roomForPipeMaxLen = pipeMaxLen - adf_nbuf_headroom(pBuffers) - adf_nbuf_len(pBuffers);
283                 if ( roomForPipeMaxLen < 0 ) {
284                         roomForPipeMaxLen = 0;
285                 }
286                         
287                 room = adf_os_min( adf_nbuf_tailroom(pBuffers), roomForPipeMaxLen);
288                 if (room < (int)(sizeof(HTC_CREDIT_REPORT) + sizeof(HTC_RECORD_HDR))) {
289                         /* no room for any reports */
290                         break;    
291                 }   
292                 /* note, a record header only has 8 bit fields, so this is safe.
293                  * we need an uncached pointer here too */            
294                 totalReportBytes = 0;
295         
296                 /* get a copy */        
297                 creditsPendingMap = pHTC->EpCreditPendingMap;   
298                            
299                 /* test pending map to see if we can send a report , if any
300                  * credits are available, we might as well send them on the 
301                  * unused space in the buffer */
302                 if (creditsPendingMap) { 
303             
304                         pRecHdr = (HTC_RECORD_HDR *)adf_nbuf_put_tail(pBuffers,
305                                                               sizeof(HTC_RECORD_HDR));
306             
307                         /* set the ID, the length will be updated with the number of credit reports we
308                          * can fit (see below) */
309                         pRecHdr->RecordID = HTC_RECORD_CREDITS;
310                         pRecHdr->Length = 0;
311                         /* the credit report follows the record header */         
312                         totalReportBytes += sizeof(HTC_RECORD_HDR);
313                         room -= sizeof(HTC_RECORD_HDR);
314             
315                         /* walkthrough pending credits map and build the records */
316                         for (i = 0; 
317                              (creditsPendingMap != 0) && (room >= (int)sizeof(HTC_CREDIT_REPORT)); 
318                              i++) {                
319                                 compareMask = (1 << i);
320                                 if (compareMask & creditsPendingMap) {
321                         
322                                         pCreditRpt = (HTC_CREDIT_REPORT *)adf_nbuf_put_tail(pBuffers,
323                                                                             sizeof(HTC_CREDIT_REPORT));
324                                     
325                                         /* clear pending mask, we are going to return all these credits */
326                                         creditsPendingMap &= ~(compareMask);
327                                         /* add this record */
328                                         pCreditRpt->EndpointID = i;
329                                         pCreditRpt->Credits = (A_UINT8)pHTC->Endpoints[i].CreditsToReturn;
330                                         /* remove pending credits, we always send deltas */
331                                         pHTC->Endpoints[i].CreditsToReturn = 0; 
332                                         /* adjust new threshold for this endpoint if needed */
333                                         CHECK_AND_ADJUST_CREDIT_THRESHOLD(&pHTC->Endpoints[i]);
334                                         /* update this record length */
335                                         pRecHdr->Length += sizeof(HTC_CREDIT_REPORT);
336                                         room -= sizeof(HTC_CREDIT_REPORT);
337                                         totalReportBytes += sizeof(HTC_CREDIT_REPORT);
338
339                                         if ( room < sizeof(HTC_CREDIT_REPORT) ) {
340                                                 break;
341                                         }
342                                 }
343                         }
344             
345                         /* update new pending credits map */       
346                         pHTC->EpCreditPendingMap = creditsPendingMap;
347                 }
348         
349                 if (totalReportBytes <= 0) {
350                         break;
351                 }
352         
353                 /* must fit into a byte, this should never actually happen since
354                  * the maximum possible number of endpoints is 32. 
355                  * The trailer can have at most 1 credit record with up to 32  reports in the record.
356                  * The trailer can have at most 1 lookahead record with only 1 lookahead report in the record.
357                  */
358         
359                 /* set header option bytes */ 
360                 pHTCHdr->ControlBytes[0] = totalReportBytes;
361                 /* HTC frame contains a trailer */
362                 pHTCHdr->Flags |= HTC_FLAGS_RECV_TRAILER;
363                 /* increment total size by the reports we added */
364                 totsz += totalReportBytes;
365                 /* adjust the last buffer we used for adding on the trailer */                                 
366         } while (FALSE);
367           
368         if (totsz == 0) {
369         }
370     
371         /* set length for message (this includes any reports that were added above) */
372         pHTCHdr->PayloadLen = adf_os_htons(totsz);  
373         HIF_send_buffer(pHTC->hifHandle, pHTC->Endpoints[EndpointID].DownLinkPipeID, pBuffers);       
374 }
375
376 void _HTC_PauseRecv(HTC_ENDPOINT_ID EndpointID)
377 {
378 }
379
380 void _HTC_ResumeRecv(HTC_ENDPOINT_ID EndpointID)
381 {
382 }
383
384 int _HTC_GetReservedHeadroom(htc_handle_t htcHandle)
385 {
386         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle;  
387     
388         return HTC_HDR_LENGTH + HIF_get_reserved_headroom(pHTC->hifHandle);
389 }
390
391 void htc_module_install(struct htc_apis *pAPIs)
392 {   
393         pAPIs->_HTC_Init = _HTC_Init;
394         pAPIs->_HTC_ReturnBuffers = _HTC_ReturnBuffers;
395         pAPIs->_HTC_ReturnBuffersList = _HTC_ReturnBuffersList;
396         pAPIs->_HTC_Ready = _HTC_Ready;
397         pAPIs->_HTC_RegisterService = _HTC_RegisterService;
398         pAPIs->_HTC_SendMsg = _HTC_SendMsg;   
399         pAPIs->_HTC_Shutdown = _HTC_Shutdown;
400         pAPIs->_HTC_GetReservedHeadroom = _HTC_GetReservedHeadroom;
401         pAPIs->_HTC_MsgRecvHandler = HTCMsgRecvHandler;
402         pAPIs->_HTC_SendDoneHandler = HTCSendDoneHandler;
403         pAPIs->_HTC_ControlSvcProcessMsg = HTCControlSvcProcessMsg;
404         pAPIs->_HTC_ControlSvcProcessSendComplete = HTCControlSvcProcessSendComplete;
405 }
406
407 /* free message to the free list */
408 LOCAL void HTCFreeMsgBuffer(HTC_CONTEXT *pHTC, adf_nbuf_t buf) 
409 {
410         BUF_Pool_free_buf(pHTC->PoolHandle, POOL_ID_HTC_CONTROL, buf);      
411 }
412
413 /* HTC control message allocator (also used for empty frames to send trailer options) */
414 LOCAL adf_nbuf_t HTCAllocMsgBuffer(HTC_CONTEXT *pHTC)
415 {
416         return BUF_Pool_alloc_buf(pHTC->PoolHandle,
417                                   POOL_ID_HTC_CONTROL,
418                                   HTC_GetReservedHeadroom(pHTC));   
419 }
420
421 LOCAL void HTCCheckAndSendCreditReport(HTC_CONTEXT *pHTC, A_UINT32 EpMask,
422                                        HTC_ENDPOINT *pEndpoint, HTC_ENDPOINT_ID Eid)
423 {
424         adf_nbuf_t pCredBuffer;
425         HTC_BUF_CONTEXT *ctx;    
426         
427         do {
428                 /* check if host needs credits */
429                 if (!(pHTC->EpHostNeedsCreditMap & EpMask)) {
430                         /* host does not need any credits for this set */
431                         break;    
432                 }
433                 /* check if any are pending */
434                 if (!(pHTC->EpCreditPendingMap & EpMask)) {
435                         /* nothing to send up */
436                         break;    
437                 }  
438                 /* was an endpoint specified? */
439                 if (pEndpoint != NULL) {
440                         /* see if a threshold is in effect for this endpoint */
441                         if (pEndpoint->CreditReturnThreshhold != 0) {
442                                 if (pEndpoint->CreditsToReturn < pEndpoint->CreditReturnThreshhold) {
443                                         /* this endpoint is using a threshold to prevent credits from dribbling
444                                          * back to the host */
445                                         break;
446                                 }
447                         }
448          
449                         if (pEndpoint->PendingCreditReports >= pHTC->MaxEpPendingCreditRpts) {
450                                 /* this endpoint already has some reports outstanding */
451                                 /* flag that as soon as a buffer is reaped, we issue a credit update to
452                                  * pick up this credit that is being held up because the endpoint has already
453                                  * exceeded the max outstanding credit report limit */    
454                                 pHTC->StateFlags |= HTC_SEND_CREDIT_UPDATE_SOON;
455                                 break;    
456                         }                         
457                 }
458         
459                 /* if we get here we have some credits to send up */
460                         
461                 /* allocate a message buffer for the trailer */
462                 pCredBuffer = HTCAllocMsgBuffer(pHTC);
463                 if (NULL == pCredBuffer) {
464                         /* no buffers left to send an empty message with trailers, host will just
465                          * have to wait until we get our endpoint 0 messages back.. */
466                         /* mark that we need to send an update as soon as we can get a buffer back */
467                         pHTC->StateFlags |= HTC_SEND_CREDIT_UPDATE_SOON;
468                         break;    
469                 }
470         
471                 ctx = (HTC_BUF_CONTEXT *)adf_nbuf_get_priv(pCredBuffer);
472                 if (pEndpoint != NULL) {
473                         /* keep track of pending reports */
474                         pEndpoint->PendingCreditReports++; 
475                         /* save the endpoint in order to decrement the count when the send completes */
476                         ctx->htc_flags = Eid | HTC_FLAGS_CREDIT_RPT;
477                 }   
478             
479                 /* this is an empty message, the HTC_SendMsg will tack on a trailer in the remaining
480                  * space, NOTE: no need to flush the cache, the header and trailers are assembled
481                  * using uncached addresses */
482                 HTC_SendMsg(pHTC, ENDPOINT0, pCredBuffer);    
483     
484         } while (FALSE);      
485 }
486         
487 /* called in response to the arrival of a service connection message */
488 LOCAL void HTCProcessConnectMsg(HTC_CONTEXT *pHTC, HTC_CONNECT_SERVICE_MSG *pMsg)
489 {
490         HTC_SERVICE *pService = pHTC->pServiceList;
491         A_UINT8 connectStatus = HTC_SERVICE_NOT_FOUND;
492         adf_nbuf_t pBuffer;
493         HTC_CONNECT_SERVICE_RESPONSE_MSG *pRspMsg;
494         int metaDataOutLen = 0;
495         A_UINT16 serviceId = adf_os_ntohs(pMsg->ServiceID);
496     
497         pBuffer = HTCAllocMsgBuffer(pHTC);
498         /* note : this will be aligned */
499         pRspMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)
500                 adf_nbuf_put_tail(pBuffer, sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG));
501                                  
502         A_MEMZERO(pRspMsg,sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG));
503         pRspMsg->MessageID = adf_os_htons(HTC_MSG_CONNECT_SERVICE_RESPONSE_ID);
504         /* reflect the service ID for this connect attempt */
505         pRspMsg->ServiceID = adf_os_htons(serviceId);
506
507         while (pService) {
508         
509                 if (pHTC->CurrentEpIndex >= ENDPOINT_MAX) {
510                         /* no more endpoints */
511                         connectStatus = HTC_SERVICE_NO_RESOURCES;
512                         break;    
513                 }
514
515                 if (serviceId == pService->ServiceID) {
516                         /* we found a match */             
517                         A_UINT8 *pMetaDataIN = NULL; 
518                         A_UINT8 *pMetaDataOut;
519             
520                         /* outgoing meta data resides in the space after the response message */
521                         pMetaDataOut = ((A_UINT8 *)pRspMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG);
522             
523                         if (pMsg->ServiceMetaLength != 0) {
524                                 /* the meta data follows the connect service message */
525                                 pMetaDataIN = ((A_UINT8 *)pMsg) + sizeof(HTC_CONNECT_SERVICE_MSG);
526                         }
527
528                         /* call the connect callback with the endpoint to use and pointers to meta data */
529                         connectStatus = pService->ProcessConnect(pService,
530                                                                  pHTC->CurrentEpIndex,
531                                                                  pMetaDataIN,
532                                                                  pMsg->ServiceMetaLength,
533                                                                  pMetaDataOut,
534                                                                  &metaDataOutLen);
535             
536                         /* check if the service accepted this connection request */
537                         if (HTC_SERVICE_SUCCESS == connectStatus) {
538                                 /* set the length of the response meta data going back to the host */
539                                 pRspMsg->ServiceMetaLength = (A_UINT8)metaDataOutLen;
540                                 /* set the endpoint ID the host will now communicate over */
541                                 pRspMsg->EndpointID = pHTC->CurrentEpIndex;
542                                 /* return the maximum message size for this service */
543                                 pRspMsg->MaxMsgSize = adf_os_htons((A_UINT16)pService->MaxSvcMsgSize);
544                                 /* assign this endpoint to this service, this will be used in routing messages */
545                                 pHTC->Endpoints[pHTC->CurrentEpIndex].pService = pService;
546                                 /* set connection flags */
547                                 pHTC->Endpoints[pHTC->CurrentEpIndex].ConnectionFlags = pMsg->ConnectionFlags;
548                 
549                                 pHTC->Endpoints[pHTC->CurrentEpIndex].DownLinkPipeID = pMsg->DownLinkPipeID;
550                                 pHTC->Endpoints[pHTC->CurrentEpIndex].UpLinkPipeID = pMsg->UpLinkPipeID;
551                 
552                                 /* mark that we are now connected */
553                                 pService->ServiceFlags |= HTC_SERVICE_FLAGS_CONNECTED;
554                                 /* bump up our index, this EP is now in use */
555                                 pHTC->CurrentEpIndex++;   
556                         }
557
558                         break;
559                 }       
560         
561                 pService = pService->pNext;   
562         }
563                    
564         pRspMsg->Status = connectStatus;    
565     
566         /* send out the response message */
567         HTC_SendMsg(pHTC, ENDPOINT0, pBuffer); 
568 }
569
570 LOCAL void HTCProcessConfigPipeMsg(HTC_CONTEXT *pHTC, HTC_CONFIG_PIPE_MSG *pMsg)
571 {
572         adf_nbuf_t pBuffer;
573         HTC_CONFIG_PIPE_RESPONSE_MSG *pRspMsg;
574         
575         pBuffer = HTCAllocMsgBuffer(pHTC);
576        
577         /* note : this will be aligned */
578         pRspMsg = (HTC_CONFIG_PIPE_RESPONSE_MSG *)
579                 adf_nbuf_put_tail(pBuffer, sizeof(HTC_CONFIG_PIPE_RESPONSE_MSG));    
580               
581         A_MEMZERO(pRspMsg,sizeof(HTC_CONFIG_PIPE_RESPONSE_MSG));
582     
583         pRspMsg->MessageID = adf_os_htons(HTC_MSG_CONFIG_PIPE_RESPONSE_ID);
584         /* reflect the service ID for this connect attempt */
585         pRspMsg->PipeID = pMsg->PipeID;
586
587         if ( HIF_is_pipe_supported(pHTC->hifHandle, pMsg->PipeID) ) {
588                 pRspMsg->Status = 0;            
589         } else {
590                 pRspMsg->Status = 1; 
591                 goto config_done;
592         }
593
594         if ( (pHTC->TotalCreditsAssigned + pMsg->CreditCount) <= pHTC->TotalCredits ) {
595                 pHTC->TotalCreditsAssigned += pMsg->CreditCount;
596         } else {
597                 pRspMsg->Status = 2;
598                 goto config_done;
599         }
600     
601         HIF_config_pipe(pHTC->hifHandle, pMsg->PipeID, pMsg->CreditCount);
602     
603 config_done:      
604         /* send out the response message */
605         HTC_SendMsg(pHTC, ENDPOINT0, pBuffer);             
606 }
607
608 /* process an incomming control message from the host */
609 LOCAL void HTCControlSvcProcessMsg(HTC_ENDPOINT_ID EndpointID, adf_nbuf_t hdr_buf,
610                                    adf_nbuf_t pBuffers, void *arg)
611 {  
612         A_BOOL setupComplete = FALSE;
613         a_uint8_t *anbdata;
614         a_uint32_t anblen;
615         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)arg;
616         HTC_UNKNOWN_MSG  *pMsg;
617         
618         adf_os_assert(hdr_buf == ADF_NBUF_NULL);
619
620         /* we assume buffers are aligned such that we can access the message
621          * parameters directly*/
622         adf_nbuf_peek_header(pBuffers, &anbdata, &anblen);
623         pMsg = (HTC_UNKNOWN_MSG *)anbdata;
624     
625         /* we cannot handle fragmented messages across buffers */
626     
627         switch ( adf_os_ntohs(pMsg->MessageID) ) {        
628         case HTC_MSG_CONNECT_SERVICE_ID:
629                 HTCProcessConnectMsg(pHTC, (HTC_CONNECT_SERVICE_MSG *)pMsg); 
630                 break;
631         case HTC_MSG_CONFIG_PIPE_ID:
632                 HTCProcessConfigPipeMsg(pHTC, (HTC_CONFIG_PIPE_MSG *)pMsg); 
633                 break;            
634         case HTC_MSG_SETUP_COMPLETE_ID:
635                 /* the host has indicated that it has completed all
636                    setup tasks and we can now let the services take over to
637                    run the rest of the application */
638                 setupComplete = TRUE;  
639                 /* can't get this more than once */
640                 break;
641         default:
642                 ;
643         }  
644         
645         if (pHTC->StateFlags & HTC_STATE_SETUP_COMPLETE) {
646                 /* recycle buffer only if we are fully running */
647                 HTC_ReturnBuffers(pHTC, ENDPOINT0,pBuffers);
648         } else {
649                 /* supply some head-room again */
650                 adf_nbuf_push_head(pBuffers, HTC_HDR_LENGTH);
651             
652                 /* otherwise return the packet back to mbox */
653                 HIF_return_recv_buf(pHTC->hifHandle, pHTC->Endpoints[EndpointID].UpLinkPipeID, pBuffers);        
654         }
655
656         if (setupComplete) {        
657                 /* mark that setup has completed */
658                 pHTC->StateFlags |= HTC_STATE_SETUP_COMPLETE; 
659                 if (pHTC->SetupCompleteCb != NULL) {
660                         pHTC->SetupCompleteCb();
661                 }
662         }
663 }
664
665 /* callback when endpoint 0 send buffers are completed */
666 LOCAL void HTCControlSvcProcessSendComplete(HTC_ENDPOINT_ID EndpointID,
667                                             adf_nbuf_t pBuffers, void *arg)
668 {
669         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)arg;
670         HTC_BUF_CONTEXT *ctx;
671         HTC_ENDPOINT_ID creditRptEndpoint;
672     
673         ctx = (HTC_BUF_CONTEXT *)adf_nbuf_get_priv(pBuffers);       
674     
675         /* put them back into the pool */
676         if ( ctx->htc_flags & HTC_FLAGS_CREDIT_RPT ) {   
677                 /* extract the endpoint number that requested this credit report */ 
678                 creditRptEndpoint = ctx->htc_flags & HTC_FLAGS_CRPT_EP_MASK;    
679                 pHTC->Endpoints[creditRptEndpoint].PendingCreditReports--;  
680         }
681     
682         HTCFreeMsgBuffer(pHTC, pBuffers);
683    
684         if (pHTC->StateFlags & HTC_SEND_CREDIT_UPDATE_SOON) {
685                 /* this flag is set when the host could not send a credit report
686                  * because we ran out of HTC control buffers */
687                 pHTC->StateFlags &= ~HTC_SEND_CREDIT_UPDATE_SOON;
688                 /* send out a report if anything is pending */
689                 HTCCheckAndSendCreditReport(pHTC, HTC_ANY_ENDPOINT_MASK,NULL,ENDPOINT_MAX);
690         }  
691 }
692
693 LOCAL void HTCSendDoneHandler(adf_nbuf_t buf, void *context)
694 {
695         A_UINT8 current_eid;
696         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)context;
697         HTC_BUF_CONTEXT *ctx;
698       
699         ctx = (HTC_BUF_CONTEXT *)adf_nbuf_get_priv(buf);
700         current_eid = ctx->end_point;
701         
702         /* Walk through the buffers and fixup the ones we used for HTC headers.
703          * The buffer list may contain more than one string of HTC buffers comprising of an
704          * HTC message so we need to check every buffer */            
705         adf_nbuf_pull_head(buf, HTC_HDR_LENGTH);
706                    
707         pHTC->Endpoints[current_eid].pService->
708                 ProcessSendBufferComplete(current_eid, 
709                                           buf, 
710                                           pHTC->Endpoints[current_eid].pService->ServiceCtx);
711 }
712
713 LOCAL void AdjustCreditThreshold(HTC_ENDPOINT  *pEndpoint)
714 {
715         A_INT16 creditsOutstanding = pEndpoint->CreditsToReturn + pEndpoint->CreditsConsumed;
716         /* set the new threshold based on the number of credits that have been consumed
717          * and which have not been returned by the app.
718          * Note: it is okay for this threshold to be zero which indicates no threshold 
719          * is in use */    
720         switch (pEndpoint->ConnectionFlags & HTC_CONNECT_FLAGS_THRESHOLD_LEVEL_MASK) {
721         case HTC_CONNECT_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH :
722                 creditsOutstanding >>= 2;
723                 break;                    
724         case HTC_CONNECT_FLAGS_THRESHOLD_LEVEL_ONE_HALF :
725                 creditsOutstanding >>= 1;
726                 break;
727         case HTC_CONNECT_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS :  
728                 creditsOutstanding = (creditsOutstanding * 3) >> 2;                  
729                 break;
730                 /* default case is unity */    
731         }
732     
733         pEndpoint->CreditReturnThreshhold = creditsOutstanding;
734     
735 }
736
737 LOCAL void RedistributeCredit(adf_nbuf_t buf, int toPipeId)
738 {
739
740 }
741             
742 /* callback from the mailbox hardware layer when a full message arrives */
743 LOCAL void HTCMsgRecvHandler(adf_nbuf_t hdr_buf, adf_nbuf_t buffer, void *context)
744 {
745         A_UINT16 totsz;
746         HTC_ENDPOINT  *pEndpoint;
747         A_UINT32 eidMask;
748         int eid;    
749         a_uint8_t *anbdata;
750         a_uint32_t anblen;
751         HTC_FRAME_HDR *pHTCHdr;
752         HTC_CONTEXT *pHTC = (HTC_CONTEXT *)context;
753         adf_nbuf_t tmp_nbuf;
754                 
755         if (hdr_buf == ADF_NBUF_NULL) {
756                 /* HTC hdr is not in the hdr_buf */
757                 tmp_nbuf = buffer;
758         }
759         else {
760                 tmp_nbuf = hdr_buf;
761         }
762                 
763         adf_nbuf_peek_header(tmp_nbuf, &anbdata, &anblen);        
764         pHTCHdr = (HTC_FRAME_HDR *)anbdata; 
765       
766         totsz = adf_os_ntohs(pHTCHdr->PayloadLen); 
767     
768         eid = pHTCHdr->EndpointID; 
769     
770         pEndpoint = &pHTC->Endpoints[eid];
771         eidMask = 1 << eid;
772
773         if (pHTCHdr->Flags & HTC_FLAGS_CREDIT_REDISTRIBUTION) {
774                 /* The pipe id where the credit is redistributed to is carried in Control
775                  * Byte 0 */
776                 RedistributeCredit(tmp_nbuf, pHTCHdr->ControlBytes[0]);
777                 return;
778         }
779
780         if (pHTC->StateFlags & HTC_STATE_SETUP_COMPLETE) {
781                 /* after setup we keep track of credit consumption to allow us to
782                  * adjust thresholds to reduce credit dribbling */  
783                 pEndpoint->CreditsConsumed ++;
784         }
785
786         /* from the design document, we put the endpoint into a "host-needs-credit" state
787          * when we receive a frame with the NEED_CREDIT_UPDATE flag set .
788          * if the host received credits through an opportunistic path, then it can
789          * issue a another frame with this bit cleared, this signals the target to clear
790          * the "host-needs-credit" state */    
791         if (pHTCHdr->Flags & HTC_FLAGS_NEED_CREDIT_UPDATE) {
792                 /* the host is running low (or is out) of credits on this
793                  * endpoint, update mask */
794                 pHTC->EpHostNeedsCreditMap |= eidMask; 
795                 /* check and set new threshold since host has reached a low credit situation */
796                 CHECK_AND_ADJUST_CREDIT_THRESHOLD(pEndpoint);                          
797         } else {
798                 /* clear the flag */
799                 pHTC->EpHostNeedsCreditMap &= ~(eidMask);       
800                 pEndpoint->CreditReturnThreshhold = 0; 
801         }
802
803         /* Adjust the first buffer to point to the start of the actual 
804            payload, the first buffer contains the header */
805         adf_nbuf_pull_head(tmp_nbuf, HTC_HDR_LENGTH);
806                     
807         /* NOTE : This callback could re-queue the recv buffers within this calling context.
808          *        The callback could also send a response message within the context of this callback
809          *        as the result of parsing this message.  In either case, if there are
810          *        pending credits and the host needs them, a credit report will be sent either through 
811          *        the response message trailer or a NULL message through HTC_ReturnBuffers().
812          */       
813         
814         pEndpoint->pService->ProcessRecvMsg(eid, hdr_buf, buffer, pEndpoint->pService->ServiceCtx);
815
816         /* Calls to HTC_ReturnBuffers drives the endpoint credit reporting state machine. 
817          * We do not want to delay credits for too long in the event that the application is 
818          * holding onto buffers for excessive periods of time.  This gives us "some" better
819          * opportunities to send up credits. */
820         HTCCheckAndSendCreditReport(pHTC, eidMask, pEndpoint, eid); 
821 }