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
HTCControlTxComplete(void * Context,struct htc_packet * pPacket)25 void HTCControlTxComplete(void *Context, struct 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 */
HTCControlRecv(void * Context,struct htc_packet * pPacket)33 void HTCControlRecv(void *Context, struct 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((struct 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 + (u32)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((struct htc_target*)Context,pPacket,&((struct htc_target*)Context)->EndPoint[0]);
58 }
59
HTCSendSetupComplete(struct htc_target * target)60 int HTCSendSetupComplete(struct htc_target *target)
61 {
62 struct htc_packet *pSendPacket = NULL;
63 int 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 u32 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 memcpy(&pSetupCompleteEx->SetupFlags, &setupFlags, sizeof(pSetupCompleteEx->SetupFlags));
87 SET_HTC_PACKET_INFO_TX(pSendPacket,
88 NULL,
89 (u8 *)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 (u8 *)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
HTCConnectService(HTC_HANDLE HTCHandle,struct htc_service_connect_req * pConnectReq,struct htc_service_connect_resp * pConnectResp)124 int HTCConnectService(HTC_HANDLE HTCHandle,
125 struct htc_service_connect_req *pConnectReq,
126 struct htc_service_connect_resp *pConnectResp)
127 {
128 struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
129 int status = 0;
130 struct htc_packet *pRecvPacket = NULL;
131 struct 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 struct 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 memcpy((u8 *)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 (u8 *)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 (status) {
188 break;
189 }
190
191 /* wait for response */
192 status = HTCWaitforControlMessage(target, &pRecvPacket);
193
194 if (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 memcpy(pConnectResp->pMetaData,
228 ((u8 *)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 = 0;
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
AddToEndpointDistList(struct htc_target * target,struct htc_endpoint_credit_dist * pEpDist)310 static void AddToEndpointDistList(struct htc_target *target, struct htc_endpoint_credit_dist *pEpDist)
311 {
312 struct 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 */
HTCDefaultCreditInit(void * Context,struct htc_endpoint_credit_dist * pEPList,int TotalCredits)338 static void HTCDefaultCreditInit(void *Context,
339 struct htc_endpoint_credit_dist *pEPList,
340 int TotalCredits)
341 {
342 struct 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 */
HTCDefaultCreditDist(void * Context,struct htc_endpoint_credit_dist * pEPDistList,HTC_CREDIT_DIST_REASON Reason)381 void HTCDefaultCreditDist(void *Context,
382 struct htc_endpoint_credit_dist *pEPDistList,
383 HTC_CREDIT_DIST_REASON Reason)
384 {
385 struct 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
HTCSetCreditDistribution(HTC_HANDLE HTCHandle,void * pCreditDistContext,HTC_CREDIT_DIST_CALLBACK CreditDistFunc,HTC_CREDIT_INIT_CALLBACK CreditInitFunc,HTC_SERVICE_ID ServicePriorityOrder[],int ListLength)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 struct 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 }
451