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