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