Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelv...
[pandora-kernel.git] / drivers / staging / ath6kl / htc2 / htc_services.c
1 //------------------------------------------------------------------------------
2 // <copyright file="htc_services.c" company="Atheros">
3 //    Copyright (c) 2007-2010 Atheros Corporation.  All rights reserved.
4 // 
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Author(s): ="Atheros"
22 //==============================================================================
23 #include "htc_internal.h"
24
25 void HTCControlTxComplete(void *Context, HTC_PACKET *pPacket)
26 {
27         /* not implemented
28          * we do not send control TX frames during normal runtime, only during setup  */
29     AR_DEBUG_ASSERT(FALSE);
30 }
31
32     /* callback when a control message arrives on this endpoint */
33 void HTCControlRecv(void *Context, HTC_PACKET *pPacket)
34 {
35     AR_DEBUG_ASSERT(pPacket->Endpoint == ENDPOINT_0);
36
37     if (pPacket->Status == A_ECANCELED) {
38         /* this is a flush operation, return the control packet back to the pool */
39         HTC_FREE_CONTROL_RX((HTC_TARGET*)Context,pPacket);    
40         return;
41     }  
42     
43         /* the only control messages we are expecting are NULL messages (credit resports) */   
44     if (pPacket->ActualLength > 0) {
45         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
46                         ("HTCControlRecv, got message with length:%d \n",
47                         pPacket->ActualLength + (A_UINT32)HTC_HDR_LENGTH));
48
49 #ifdef ATH_DEBUG_MODULE
50             /* dump header and message */
51         DebugDumpBytes(pPacket->pBuffer - HTC_HDR_LENGTH,
52                        pPacket->ActualLength + HTC_HDR_LENGTH,
53                        "Unexpected ENDPOINT 0 Message");
54 #endif
55     }
56
57     HTC_RECYCLE_RX_PKT((HTC_TARGET*)Context,pPacket,&((HTC_TARGET*)Context)->EndPoint[0]);
58 }
59
60 A_STATUS HTCSendSetupComplete(HTC_TARGET *target)
61 {
62     HTC_PACKET             *pSendPacket = NULL;
63     A_STATUS                status;
64
65     do {
66            /* allocate a packet to send to the target */
67         pSendPacket = HTC_ALLOC_CONTROL_TX(target);
68
69         if (NULL == pSendPacket) {
70             status = A_NO_MEMORY;
71             break;
72         }
73
74         if (target->HTCTargetVersion >= HTC_VERSION_2P1) {
75             HTC_SETUP_COMPLETE_EX_MSG *pSetupCompleteEx;
76             A_UINT32                  setupFlags = 0;
77                    
78             pSetupCompleteEx = (HTC_SETUP_COMPLETE_EX_MSG *)pSendPacket->pBuffer;
79             A_MEMZERO(pSetupCompleteEx, sizeof(HTC_SETUP_COMPLETE_EX_MSG));
80             pSetupCompleteEx->MessageID = HTC_MSG_SETUP_COMPLETE_EX_ID;   
81             if (target->MaxMsgPerBundle > 0) {
82                     /* host can do HTC bundling, indicate this to the target */
83                 setupFlags |= HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV; 
84                 pSetupCompleteEx->MaxMsgsPerBundledRecv = target->MaxMsgPerBundle;
85             }    
86             A_MEMCPY(&pSetupCompleteEx->SetupFlags, &setupFlags, sizeof(pSetupCompleteEx->SetupFlags));            
87             SET_HTC_PACKET_INFO_TX(pSendPacket,
88                                    NULL,
89                                    (A_UINT8 *)pSetupCompleteEx,
90                                    sizeof(HTC_SETUP_COMPLETE_EX_MSG),
91                                    ENDPOINT_0,
92                                    HTC_SERVICE_TX_PACKET_TAG);
93       
94         }  else {            
95             HTC_SETUP_COMPLETE_MSG *pSetupComplete;
96                 /* assemble setup complete message */
97             pSetupComplete = (HTC_SETUP_COMPLETE_MSG *)pSendPacket->pBuffer;
98             A_MEMZERO(pSetupComplete, sizeof(HTC_SETUP_COMPLETE_MSG));
99             pSetupComplete->MessageID = HTC_MSG_SETUP_COMPLETE_ID;   
100             SET_HTC_PACKET_INFO_TX(pSendPacket,
101                                    NULL,
102                                    (A_UINT8 *)pSetupComplete,
103                                    sizeof(HTC_SETUP_COMPLETE_MSG),
104                                    ENDPOINT_0,
105                                    HTC_SERVICE_TX_PACKET_TAG);
106         }
107
108             /* we want synchronous operation */
109         pSendPacket->Completion = NULL;
110         HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0);
111             /* send the message */
112         status = HTCIssueSend(target,pSendPacket);
113
114     } while (FALSE);
115
116     if (pSendPacket != NULL) {
117         HTC_FREE_CONTROL_TX(target,pSendPacket);
118     }
119
120     return status;
121 }
122
123
124 A_STATUS HTCConnectService(HTC_HANDLE               HTCHandle,
125                            HTC_SERVICE_CONNECT_REQ  *pConnectReq,
126                            HTC_SERVICE_CONNECT_RESP *pConnectResp)
127 {
128     HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
129     A_STATUS                            status = A_OK;
130     HTC_PACKET                          *pRecvPacket = NULL;
131     HTC_PACKET                          *pSendPacket = NULL;
132     HTC_CONNECT_SERVICE_RESPONSE_MSG    *pResponseMsg;
133     HTC_CONNECT_SERVICE_MSG             *pConnectMsg;
134     HTC_ENDPOINT_ID                     assignedEndpoint = ENDPOINT_MAX;
135     HTC_ENDPOINT                        *pEndpoint;
136     unsigned int                        maxMsgSize = 0;
137
138     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:0x%lX SvcID:0x%X \n",
139                (unsigned long)target, pConnectReq->ServiceID));
140
141     do {
142
143         AR_DEBUG_ASSERT(pConnectReq->ServiceID != 0);
144
145         if (HTC_CTRL_RSVD_SVC == pConnectReq->ServiceID) {
146                 /* special case for pseudo control service */
147             assignedEndpoint = ENDPOINT_0;
148             maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH;
149         } else {
150                 /* allocate a packet to send to the target */
151             pSendPacket = HTC_ALLOC_CONTROL_TX(target);
152
153             if (NULL == pSendPacket) {
154                 AR_DEBUG_ASSERT(FALSE);
155                 status = A_NO_MEMORY;
156                 break;
157             }
158                 /* assemble connect service message */
159             pConnectMsg = (HTC_CONNECT_SERVICE_MSG *)pSendPacket->pBuffer;
160             AR_DEBUG_ASSERT(pConnectMsg != NULL);
161             A_MEMZERO(pConnectMsg,sizeof(HTC_CONNECT_SERVICE_MSG));
162             pConnectMsg->MessageID = HTC_MSG_CONNECT_SERVICE_ID;
163             pConnectMsg->ServiceID = pConnectReq->ServiceID;
164             pConnectMsg->ConnectionFlags = pConnectReq->ConnectionFlags;
165                 /* check caller if it wants to transfer meta data */
166             if ((pConnectReq->pMetaData != NULL) &&
167                 (pConnectReq->MetaDataLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
168                     /* copy meta data into message buffer (after header ) */
169                 A_MEMCPY((A_UINT8 *)pConnectMsg + sizeof(HTC_CONNECT_SERVICE_MSG),
170                          pConnectReq->pMetaData,
171                          pConnectReq->MetaDataLength);
172                 pConnectMsg->ServiceMetaLength = pConnectReq->MetaDataLength;
173             }
174
175             SET_HTC_PACKET_INFO_TX(pSendPacket,
176                                    NULL,
177                                    (A_UINT8 *)pConnectMsg,
178                                    sizeof(HTC_CONNECT_SERVICE_MSG) + pConnectMsg->ServiceMetaLength,
179                                    ENDPOINT_0,
180                                    HTC_SERVICE_TX_PACKET_TAG);
181
182                 /* we want synchronous operation */
183             pSendPacket->Completion = NULL;
184             HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0);
185             status = HTCIssueSend(target,pSendPacket);
186
187             if (A_FAILED(status)) {
188                 break;
189             }
190
191                 /* wait for response */
192             status = HTCWaitforControlMessage(target, &pRecvPacket);
193
194             if (A_FAILED(status)) {
195                 break;
196             }
197                 /* we controlled the buffer creation so it has to be properly aligned */
198             pResponseMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)pRecvPacket->pBuffer;
199
200             if ((pResponseMsg->MessageID != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) ||
201                 (pRecvPacket->ActualLength < sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) {
202                     /* this message is not valid */
203                 AR_DEBUG_ASSERT(FALSE);
204                 status = A_EPROTO;
205                 break;
206             }
207
208             pConnectResp->ConnectRespCode = pResponseMsg->Status;
209                 /* check response status */
210             if (pResponseMsg->Status != HTC_SERVICE_SUCCESS) {
211                 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
212                     (" Target failed service 0x%X connect request (status:%d)\n",
213                                 pResponseMsg->ServiceID, pResponseMsg->Status));
214                 status = A_EPROTO;
215                 break;
216             }
217
218             assignedEndpoint = (HTC_ENDPOINT_ID) pResponseMsg->EndpointID;
219             maxMsgSize = pResponseMsg->MaxMsgSize;
220
221             if ((pConnectResp->pMetaData != NULL) &&
222                 (pResponseMsg->ServiceMetaLength > 0) &&
223                 (pResponseMsg->ServiceMetaLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
224                     /* caller supplied a buffer and the target responded with data */
225                 int copyLength = min((int)pConnectResp->BufferLength, (int)pResponseMsg->ServiceMetaLength);
226                     /* copy the meta data */
227                 A_MEMCPY(pConnectResp->pMetaData,
228                          ((A_UINT8 *)pResponseMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG),
229                          copyLength);
230                 pConnectResp->ActualLength = copyLength;
231             }
232
233         }
234
235             /* the rest of these are parameter checks so set the error status */
236         status = A_EPROTO;
237
238         if (assignedEndpoint >= ENDPOINT_MAX) {
239             AR_DEBUG_ASSERT(FALSE);
240             break;
241         }
242
243         if (0 == maxMsgSize) {
244             AR_DEBUG_ASSERT(FALSE);
245             break;
246         }
247
248         pEndpoint = &target->EndPoint[assignedEndpoint];
249         pEndpoint->Id = assignedEndpoint;
250         if (pEndpoint->ServiceID != 0) {
251             /* endpoint already in use! */
252             AR_DEBUG_ASSERT(FALSE);
253             break;
254         }
255
256             /* return assigned endpoint to caller */
257         pConnectResp->Endpoint = assignedEndpoint;
258         pConnectResp->MaxMsgLength = maxMsgSize;
259
260             /* setup the endpoint */
261         pEndpoint->ServiceID = pConnectReq->ServiceID; /* this marks the endpoint in use */
262         pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth;
263         pEndpoint->MaxMsgLength = maxMsgSize;
264             /* copy all the callbacks */
265         pEndpoint->EpCallBacks = pConnectReq->EpCallbacks;
266             /* set the credit distribution info for this endpoint, this information is
267              * passed back to the credit distribution callback function */
268         pEndpoint->CreditDist.ServiceID = pConnectReq->ServiceID;
269         pEndpoint->CreditDist.pHTCReserved = pEndpoint;
270         pEndpoint->CreditDist.Endpoint = assignedEndpoint;
271         pEndpoint->CreditDist.TxCreditSize = target->TargetCreditSize;
272         
273         if (pConnectReq->MaxSendMsgSize != 0) {
274                 /* override TxCreditsPerMaxMsg calculation, this optimizes the credit-low indications
275                  * since the host will actually issue smaller messages in the Send path */
276             if (pConnectReq->MaxSendMsgSize > maxMsgSize) {
277                     /* can't be larger than the maximum the target can support */
278                 AR_DEBUG_ASSERT(FALSE);
279                 break;       
280             }
281             pEndpoint->CreditDist.TxCreditsPerMaxMsg = pConnectReq->MaxSendMsgSize / target->TargetCreditSize;
282         } else {
283             pEndpoint->CreditDist.TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize;
284         }
285         
286         if (0 == pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
287             pEndpoint->CreditDist.TxCreditsPerMaxMsg = 1;
288         }
289         
290             /* save local connection flags */
291         pEndpoint->LocalConnectionFlags = pConnectReq->LocalConnectionFlags;
292         
293         status = A_OK;
294
295     } while (FALSE);
296
297     if (pSendPacket != NULL) {
298         HTC_FREE_CONTROL_TX(target,pSendPacket);
299     }
300
301     if (pRecvPacket != NULL) {
302         HTC_FREE_CONTROL_RX(target,pRecvPacket);
303     }
304
305     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCConnectService \n"));
306
307     return status;
308 }
309
310 static void AddToEndpointDistList(HTC_TARGET *target, HTC_ENDPOINT_CREDIT_DIST *pEpDist)
311 {
312     HTC_ENDPOINT_CREDIT_DIST *pCurEntry,*pLastEntry;
313
314     if (NULL == target->EpCreditDistributionListHead) {
315         target->EpCreditDistributionListHead = pEpDist;
316         pEpDist->pNext = NULL;
317         pEpDist->pPrev = NULL;
318         return;
319     }
320
321         /* queue to the end of the list, this does not have to be very
322          * fast since this list is built at startup time */
323     pCurEntry = target->EpCreditDistributionListHead;
324
325     while (pCurEntry) {
326         pLastEntry = pCurEntry;
327         pCurEntry = pCurEntry->pNext;
328     }
329
330     pLastEntry->pNext = pEpDist;
331     pEpDist->pPrev = pLastEntry;
332     pEpDist->pNext = NULL;
333 }
334
335
336
337 /* default credit init callback */
338 static void HTCDefaultCreditInit(void                     *Context,
339                                  HTC_ENDPOINT_CREDIT_DIST *pEPList,
340                                  int                      TotalCredits)
341 {
342     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
343     int                      totalEps = 0;
344     int                      creditsPerEndpoint;
345
346     pCurEpDist = pEPList;
347         /* first run through the list and figure out how many endpoints we are dealing with */
348     while (pCurEpDist != NULL) {
349         pCurEpDist = pCurEpDist->pNext;
350         totalEps++;
351     }
352
353         /* even distribution */
354     creditsPerEndpoint = TotalCredits/totalEps;
355
356     pCurEpDist = pEPList;
357         /* run through the list and set minimum and normal credits and
358          * provide the endpoint with some credits to start */
359     while (pCurEpDist != NULL) {
360
361         if (creditsPerEndpoint < pCurEpDist->TxCreditsPerMaxMsg) {
362                 /* too many endpoints and not enough credits */
363             AR_DEBUG_ASSERT(FALSE);
364             break;
365         }
366             /* our minimum is set for at least 1 max message */
367         pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
368             /* this value is ignored by our credit alg, since we do
369              * not dynamically adjust credits, this is the policy of
370              * the "default" credit distribution, something simple and easy */
371         pCurEpDist->TxCreditsNorm = 0xFFFF;
372             /* give the endpoint minimum credits */
373         pCurEpDist->TxCredits = creditsPerEndpoint;
374         pCurEpDist->TxCreditsAssigned = creditsPerEndpoint;
375         pCurEpDist = pCurEpDist->pNext;
376     }
377
378 }
379
380 /* default credit distribution callback, NOTE, this callback holds the TX lock */
381 void HTCDefaultCreditDist(void                     *Context,
382                           HTC_ENDPOINT_CREDIT_DIST *pEPDistList,
383                           HTC_CREDIT_DIST_REASON   Reason)
384 {
385     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
386
387     if (Reason == HTC_CREDIT_DIST_SEND_COMPLETE) {
388         pCurEpDist = pEPDistList;
389             /* simple distribution */
390         while (pCurEpDist != NULL) {
391             if (pCurEpDist->TxCreditsToDist > 0) {
392                     /* just give the endpoint back the credits */
393                 pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
394                 pCurEpDist->TxCreditsToDist = 0;
395             }
396             pCurEpDist = pCurEpDist->pNext;
397         }
398     }
399
400     /* note we do not need to handle the other reason codes as this is a very
401      * simple distribution scheme, no need to seek for more credits or handle inactivity */
402 }
403
404 void HTCSetCreditDistribution(HTC_HANDLE               HTCHandle,
405                               void                     *pCreditDistContext,
406                               HTC_CREDIT_DIST_CALLBACK CreditDistFunc,
407                               HTC_CREDIT_INIT_CALLBACK CreditInitFunc,
408                               HTC_SERVICE_ID           ServicePriorityOrder[],
409                               int                      ListLength)
410 {
411     HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
412     int i;
413     int ep;
414
415     if (CreditInitFunc != NULL) {
416             /* caller has supplied their own distribution functions */
417         target->InitCredits = CreditInitFunc;
418         AR_DEBUG_ASSERT(CreditDistFunc != NULL);
419         target->DistributeCredits = CreditDistFunc;
420         target->pCredDistContext = pCreditDistContext;
421     } else {
422         /* caller wants HTC to do distribution */
423         /* if caller wants service to handle distributions then
424          * it must set both of these to NULL! */
425         AR_DEBUG_ASSERT(CreditDistFunc == NULL);
426         target->InitCredits = HTCDefaultCreditInit;
427         target->DistributeCredits = HTCDefaultCreditDist;
428         target->pCredDistContext = target;
429     }
430
431         /* always add HTC control endpoint first, we only expose the list after the
432          * first one, this is added for TX queue checking */
433     AddToEndpointDistList(target, &target->EndPoint[ENDPOINT_0].CreditDist);
434
435         /* build the list of credit distribution structures in priority order
436          * supplied by the caller, these will follow endpoint 0 */
437     for (i = 0; i < ListLength; i++) {
438             /* match services with endpoints and add the endpoints to the distribution list
439              * in FIFO order */
440         for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) {
441             if (target->EndPoint[ep].ServiceID == ServicePriorityOrder[i]) {
442                     /* queue this one to the list */
443                 AddToEndpointDistList(target, &target->EndPoint[ep].CreditDist);
444                 break;
445             }
446         }
447         AR_DEBUG_ASSERT(ep < ENDPOINT_MAX);
448     }
449
450 }