1/*
2 *
3 * Copyright (c) 2007 Atheros Communications Inc.
4 * All rights reserved.
5 *
6 *
7 *  This program is free software; you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License version 2 as
9 *  published by the Free Software Foundation;
10 *
11 *  Software distributed under the License is distributed on an "AS
12 *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13 *  implied. See the License for the specific language governing
14 *  rights and limitations under the License.
15 *
16 *
17 *
18 */
19
20#include "htc_internal.h"
21
22void HTCControlTxComplete(void *Context, HTC_PACKET *pPacket)
23{
24        /* not implemented
25         * we do not send control TX frames during normal runtime, only during setup  */
26    AR_DEBUG_ASSERT(FALSE);
27}
28
29    /* callback when a control message arrives on this endpoint */
30void HTCControlRecv(void *Context, HTC_PACKET *pPacket)
31{
32    AR_DEBUG_ASSERT(pPacket->Endpoint == ENDPOINT_0);
33
34        /* the only control messages we are expecting are NULL messages (credit resports), which should
35         * never get here */
36    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
37                    ("HTCControlRecv, got message with length:%d \n",
38                    pPacket->ActualLength + HTC_HDR_LENGTH));
39
40        /* dump header and message */
41    DebugDumpBytes(pPacket->pBuffer - HTC_HDR_LENGTH,
42                   pPacket->ActualLength + HTC_HDR_LENGTH,
43                   "Unexpected ENDPOINT 0 Message");
44
45    HTC_RECYCLE_RX_PKT((HTC_TARGET*)Context,pPacket);
46}
47
48A_STATUS HTCSendSetupComplete(HTC_TARGET *target)
49{
50    HTC_PACKET             *pSendPacket = NULL;
51    A_STATUS                status;
52    HTC_SETUP_COMPLETE_MSG *pSetupComplete;
53
54    do {
55           /* allocate a packet to send to the target */
56        pSendPacket = HTC_ALLOC_CONTROL_TX(target);
57
58        if (NULL == pSendPacket) {
59            status = A_NO_MEMORY;
60            break;
61        }
62
63            /* assemble setup complete message */
64        pSetupComplete = (HTC_SETUP_COMPLETE_MSG *)pSendPacket->pBuffer;
65        A_MEMZERO(pSetupComplete,sizeof(HTC_SETUP_COMPLETE_MSG));
66        pSetupComplete->MessageID = HTC_MSG_SETUP_COMPLETE_ID;
67
68        SET_HTC_PACKET_INFO_TX(pSendPacket,
69                               NULL,
70                               (A_UINT8 *)pSetupComplete,
71                               sizeof(HTC_SETUP_COMPLETE_MSG),
72                               ENDPOINT_0,
73                               HTC_SERVICE_TX_PACKET_TAG);
74
75            /* we want synchronous operation */
76        pSendPacket->Completion = NULL;
77            /* send the message */
78        status = HTCIssueSend(target,pSendPacket,0);
79
80    } while (FALSE);
81
82    if (pSendPacket != NULL) {
83        HTC_FREE_CONTROL_TX(target,pSendPacket);
84    }
85
86    return status;
87}
88
89
90A_STATUS HTCConnectService(HTC_HANDLE               HTCHandle,
91                           HTC_SERVICE_CONNECT_REQ  *pConnectReq,
92                           HTC_SERVICE_CONNECT_RESP *pConnectResp)
93{
94    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
95    A_STATUS                            status = A_OK;
96    HTC_PACKET                          *pRecvPacket = NULL;
97    HTC_PACKET                          *pSendPacket = NULL;
98    HTC_CONNECT_SERVICE_RESPONSE_MSG    *pResponseMsg;
99    HTC_CONNECT_SERVICE_MSG             *pConnectMsg;
100    HTC_ENDPOINT_ID                     assignedEndpoint = ENDPOINT_MAX;
101    HTC_ENDPOINT                        *pEndpoint;
102    int                                 maxMsgSize = 0;
103
104    AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:0x%X SvcID:0x%X \n",
105               (A_UINT32)target, pConnectReq->ServiceID));
106
107    do {
108
109        AR_DEBUG_ASSERT(pConnectReq->ServiceID != 0);
110
111        if (HTC_CTRL_RSVD_SVC == pConnectReq->ServiceID) {
112                /* special case for pseudo control service */
113            assignedEndpoint = ENDPOINT_0;
114            maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH;
115        } else {
116                /* allocate a packet to send to the target */
117            pSendPacket = HTC_ALLOC_CONTROL_TX(target);
118
119            if (NULL == pSendPacket) {
120                AR_DEBUG_ASSERT(FALSE);
121                status = A_NO_MEMORY;
122                break;
123            }
124                /* assemble connect service message */
125            pConnectMsg = (HTC_CONNECT_SERVICE_MSG *)pSendPacket->pBuffer;
126            AR_DEBUG_ASSERT(pConnectMsg != NULL);
127            A_MEMZERO(pConnectMsg,sizeof(HTC_CONNECT_SERVICE_MSG));
128            pConnectMsg->MessageID = HTC_MSG_CONNECT_SERVICE_ID;
129            pConnectMsg->ServiceID = pConnectReq->ServiceID;
130            pConnectMsg->ConnectionFlags = pConnectReq->ConnectionFlags;
131                /* check caller if it wants to transfer meta data */
132            if ((pConnectReq->pMetaData != NULL) &&
133                (pConnectReq->MetaDataLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
134                    /* copy meta data into message buffer (after header ) */
135                A_MEMCPY((A_UINT8 *)pConnectMsg + sizeof(HTC_CONNECT_SERVICE_MSG),
136                         pConnectReq->pMetaData,
137                         pConnectReq->MetaDataLength);
138                pConnectMsg->ServiceMetaLength = pConnectReq->MetaDataLength;
139            }
140
141            SET_HTC_PACKET_INFO_TX(pSendPacket,
142                                   NULL,
143                                   (A_UINT8 *)pConnectMsg,
144                                   sizeof(HTC_CONNECT_SERVICE_MSG) + pConnectMsg->ServiceMetaLength,
145                                   ENDPOINT_0,
146                                   HTC_SERVICE_TX_PACKET_TAG);
147
148                /* we want synchronous operation */
149            pSendPacket->Completion = NULL;
150
151            status = HTCIssueSend(target,pSendPacket,0);
152
153            if (A_FAILED(status)) {
154                break;
155            }
156
157                /* wait for response */
158            status = HTCWaitforControlMessage(target, &pRecvPacket);
159
160            if (A_FAILED(status)) {
161                break;
162            }
163                /* we controlled the buffer creation so it has to be properly aligned */
164            pResponseMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)pRecvPacket->pBuffer;
165
166            if ((pResponseMsg->MessageID != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) ||
167                (pRecvPacket->ActualLength < sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) {
168                    /* this message is not valid */
169                AR_DEBUG_ASSERT(FALSE);
170                status = A_EPROTO;
171                break;
172            }
173
174            pConnectResp->ConnectRespCode = pResponseMsg->Status;
175                /* check response status */
176            if (pResponseMsg->Status != HTC_SERVICE_SUCCESS) {
177                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
178                    (" Target failed service 0x%X connect request (status:%d)\n",
179                                pResponseMsg->ServiceID, pResponseMsg->Status));
180                status = A_EPROTO;
181                break;
182            }
183
184            assignedEndpoint = pResponseMsg->EndpointID;
185            maxMsgSize = pResponseMsg->MaxMsgSize;
186
187            if ((pConnectResp->pMetaData != NULL) &&
188                (pResponseMsg->ServiceMetaLength > 0) &&
189                (pResponseMsg->ServiceMetaLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
190                    /* caller supplied a buffer and the target responded with data */
191                int copyLength = min((int)pConnectResp->BufferLength, (int)pResponseMsg->ServiceMetaLength);
192                    /* copy the meta data */
193                A_MEMCPY(pConnectResp->pMetaData,
194                         ((A_UINT8 *)pResponseMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG),
195                         copyLength);
196                pConnectResp->ActualLength = copyLength;
197            }
198
199        }
200
201            /* the rest of these are parameter checks so set the error status */
202        status = A_EPROTO;
203
204        if (assignedEndpoint >= ENDPOINT_MAX) {
205            AR_DEBUG_ASSERT(FALSE);
206            break;
207        }
208
209        if (0 == maxMsgSize) {
210            AR_DEBUG_ASSERT(FALSE);
211            break;
212        }
213
214        pEndpoint = &target->EndPoint[assignedEndpoint];
215
216        if (pEndpoint->ServiceID != 0) {
217            /* endpoint already in use! */
218            AR_DEBUG_ASSERT(FALSE);
219            break;
220        }
221
222            /* return assigned endpoint to caller */
223        pConnectResp->Endpoint = assignedEndpoint;
224        pConnectResp->MaxMsgLength = maxMsgSize;
225
226            /* setup the endpoint */
227        pEndpoint->ServiceID = pConnectReq->ServiceID; /* this marks the endpoint in use */
228        pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth;
229        pEndpoint->MaxMsgLength = maxMsgSize;
230            /* copy all the callbacks */
231        pEndpoint->EpCallBacks = pConnectReq->EpCallbacks;
232        INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBuffers);
233        INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue);
234            /* set the credit distribution info for this endpoint, this information is
235             * passed back to the credit distribution callback function */
236        pEndpoint->CreditDist.ServiceID = pConnectReq->ServiceID;
237        pEndpoint->CreditDist.pHTCReserved = pEndpoint;
238        pEndpoint->CreditDist.Endpoint = assignedEndpoint;
239        pEndpoint->CreditDist.TxCreditSize = target->TargetCreditSize;
240        pEndpoint->CreditDist.TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize;
241
242        if (0 == pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
243            pEndpoint->CreditDist.TxCreditsPerMaxMsg = 1;
244        }
245
246        status = A_OK;
247
248    } while (FALSE);
249
250    if (pSendPacket != NULL) {
251        HTC_FREE_CONTROL_TX(target,pSendPacket);
252    }
253
254    if (pRecvPacket != NULL) {
255        HTC_FREE_CONTROL_RX(target,pRecvPacket);
256    }
257
258    AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCConnectService \n"));
259
260    return status;
261}
262
263static void AddToEndpointDistList(HTC_TARGET *target, HTC_ENDPOINT_CREDIT_DIST *pEpDist)
264{
265    HTC_ENDPOINT_CREDIT_DIST *pCurEntry,*pLastEntry;
266
267    if (NULL == target->EpCreditDistributionListHead) {
268        target->EpCreditDistributionListHead = pEpDist;
269        pEpDist->pNext = NULL;
270        pEpDist->pPrev = NULL;
271        return;
272    }
273
274        /* queue to the end of the list, this does not have to be very
275         * fast since this list is built at startup time */
276    pCurEntry = target->EpCreditDistributionListHead;
277
278    while (pCurEntry) {
279        pLastEntry = pCurEntry;
280        pCurEntry = pCurEntry->pNext;
281    }
282
283    pLastEntry->pNext = pEpDist;
284    pEpDist->pPrev = pLastEntry;
285    pEpDist->pNext = NULL;
286}
287
288
289
290/* default credit init callback */
291static void HTCDefaultCreditInit(void                     *Context,
292                                 HTC_ENDPOINT_CREDIT_DIST *pEPList,
293                                 int                      TotalCredits)
294{
295    HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
296    int                      totalEps = 0;
297    int                      creditsPerEndpoint;
298
299    pCurEpDist = pEPList;
300        /* first run through the list and figure out how many endpoints we are dealing with */
301    while (pCurEpDist != NULL) {
302        pCurEpDist = pCurEpDist->pNext;
303        totalEps++;
304    }
305
306        /* even distribution */
307    creditsPerEndpoint = TotalCredits/totalEps;
308
309    pCurEpDist = pEPList;
310        /* run through the list and set minimum and normal credits and
311         * provide the endpoint with some credits to start */
312    while (pCurEpDist != NULL) {
313
314        if (creditsPerEndpoint < pCurEpDist->TxCreditsPerMaxMsg) {
315                /* too many endpoints and not enough credits */
316            AR_DEBUG_ASSERT(FALSE);
317            break;
318        }
319            /* our minimum is set for at least 1 max message */
320        pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
321            /* this value is ignored by our credit alg, since we do
322             * not dynamically adjust credits, this is the policy of
323             * the "default" credit distribution, something simple and easy */
324        pCurEpDist->TxCreditsNorm = 0xFFFF;
325            /* give the endpoint minimum credits */
326        pCurEpDist->TxCredits = creditsPerEndpoint;
327        pCurEpDist->TxCreditsAssigned = creditsPerEndpoint;
328        pCurEpDist = pCurEpDist->pNext;
329    }
330
331}
332
333/* default credit distribution callback, NOTE, this callback holds the TX lock */
334void HTCDefaultCreditDist(void                     *Context,
335                          HTC_ENDPOINT_CREDIT_DIST *pEPDistList,
336                          HTC_CREDIT_DIST_REASON   Reason)
337{
338    HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
339
340    if (Reason == HTC_CREDIT_DIST_SEND_COMPLETE) {
341        pCurEpDist = pEPDistList;
342            /* simple distribution */
343        while (pCurEpDist != NULL) {
344            if (pCurEpDist->TxCreditsToDist > 0) {
345                    /* just give the endpoint back the credits */
346                pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
347                pCurEpDist->TxCreditsToDist = 0;
348            }
349            pCurEpDist = pCurEpDist->pNext;
350        }
351    }
352
353    /* note we do not need to handle the other reason codes as this is a very
354     * simple distribution scheme, no need to seek for more credits or handle inactivity */
355}
356
357void HTCSetCreditDistribution(HTC_HANDLE               HTCHandle,
358                              void                     *pCreditDistContext,
359                              HTC_CREDIT_DIST_CALLBACK CreditDistFunc,
360                              HTC_CREDIT_INIT_CALLBACK CreditInitFunc,
361                              HTC_SERVICE_ID           ServicePriorityOrder[],
362                              int                      ListLength)
363{
364    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
365    int i;
366    int ep;
367
368    if (CreditInitFunc != NULL) {
369            /* caller has supplied their own distribution functions */
370        target->InitCredits = CreditInitFunc;
371        AR_DEBUG_ASSERT(CreditDistFunc != NULL);
372        target->DistributeCredits = CreditDistFunc;
373        target->pCredDistContext = pCreditDistContext;
374    } else {
375        /* caller wants HTC to do distribution */
376        /* if caller wants service to handle distributions then
377         * it must set both of these to NULL! */
378        AR_DEBUG_ASSERT(CreditDistFunc == NULL);
379        target->InitCredits = HTCDefaultCreditInit;
380        target->DistributeCredits = HTCDefaultCreditDist;
381        target->pCredDistContext = target;
382    }
383
384        /* always add HTC control endpoint first, we only expose the list after the
385         * first one, this is added for TX queue checking */
386    AddToEndpointDistList(target, &target->EndPoint[ENDPOINT_0].CreditDist);
387
388        /* build the list of credit distribution structures in priority order
389         * supplied by the caller, these will follow endpoint 0 */
390    for (i = 0; i < ListLength; i++) {
391            /* match services with endpoints and add the endpoints to the distribution list
392             * in FIFO order */
393        for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) {
394            if (target->EndPoint[ep].ServiceID == ServicePriorityOrder[i]) {
395                    /* queue this one to the list */
396                AddToEndpointDistList(target, &target->EndPoint[ep].CreditDist);
397                break;
398            }
399        }
400        AR_DEBUG_ASSERT(ep < ENDPOINT_MAX);
401    }
402
403}
404