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