www.pudn.com > sdio-2.6.18-full.rar > htc_send.c
/*
* Copyright (c) 2004-2006 Atheros Communications Inc.
*
* Wireless Network driver for Atheros AR6001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* This file contains the routines handling the transmit path.
*/
#include "htc_internal.h"
/* ------ Global Variable Declarations ------- */
extern A_MUTEX_T creditCS;
#ifdef DEBUG
extern A_UINT32 debughtc;
extern A_UINT32 txcreditsavailable[HTC_MAILBOX_NUM_MAX];
extern A_UINT32 txcreditsconsumed[HTC_MAILBOX_NUM_MAX];
#ifdef HTC_SYNC
extern A_UINT32 txcreditintrenable[HTC_MAILBOX_NUM_MAX];
extern A_UINT32 txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX];
#endif
#endif
extern A_UINT32 tx_attempt[HTC_MAILBOX_NUM_MAX]; /* Num of attempts to add */
extern A_UINT32 tx_post[HTC_MAILBOX_NUM_MAX]; /* Num of attemps succeded */
extern A_UINT32 tx_complete[HTC_MAILBOX_NUM_MAX]; /* Num of tx complete */
/* ------ Functions ------ */
A_STATUS
HTCBufferSend(HTC_TARGET *target,
HTC_ENDPOINT_ID endPointId,
A_UCHAR *buffer,
A_UINT32 length,
void *cookie)
{
A_STATUS status;
HTC_ENDPOINT *endPoint;
HTC_DATA_REQUEST_QUEUE *sendQueue;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("HTCBufferSend: Enter (endPointId: %d, buffer: 0x%p, length: %d, cookie: 0x%p)\n", endPointId, buffer, length, cookie));
AR_DEBUG_ASSERT((endPointId >= ENDPOINT1) && (endPointId <= ENDPOINT4));
AR_DEBUG_ASSERT(length);
/* Extract the end point instance */
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
sendQueue = &endPoint->sendQueue;
AR_DEBUG_ASSERT(sendQueue != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("mboxQueue: %p\n", sendQueue));
/*
* Add this posted buffer to the pending send queue. We need to allocate
* a bufferElement to store the packet information and we borrow that
* buffer from the pending send queue. If circumstances allow us to
* transmit it right away then we dequeue it otherwise we let it remain
* to be picked in the interrupt handler context.
*/
tx_attempt[endPointId] += 1;
if (!endPoint->enabled) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Endpoint not enabled: %d\n",
GET_ENDPOINT_ID(endPoint)));
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("tx_attempt[%d] = %d, tx_post[%d] = %d, tx_complete[%d] = %d\n", endPointId, tx_attempt[endPointId], endPointId, tx_post[endPointId], endPointId, tx_complete[endPointId]));
return A_ERROR;
}
status = addToMboxQueue(sendQueue, buffer, length, 0, cookie);
if (status != A_OK) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
("Mailbox (%d) PSQ full. Unable to add buffer\n",
endPointId));
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("tx_attempt[%d] = %d, tx_post[%d] = %d, tx_complete[%d] = %d\n", endPointId, tx_attempt[endPointId], endPointId, tx_post[endPointId], endPointId, tx_complete[endPointId]));
return A_ERROR;
}
tx_post[endPointId] += 1;
/*
* The frame shall be dequeued and sent if there are any credits
* available.
*/
htcSendFrame(endPoint);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("HTCBufferSend: Exit\n"));
return A_OK;
}
void
htcSendFrame(HTC_ENDPOINT *endPoint)
{
A_STATUS status;
A_UINT32 address;
HIF_DATAMODE dmode;
HTC_TARGET *target;
HIF_REQUEST request;
A_UINT32 frameLength;
A_UINT32 paddedLength;
HTC_EVENT_INFO eventInfo;
A_UINT8 txCreditsConsumed;
A_UINT8 txCreditsAvailable;
HTC_ENDPOINT_ID endPointId;
HTC_QUEUE_ELEMENT *element;
HTC_MBOX_BUFFER *mboxBuffer;
HTC_REG_REQUEST_LIST *regList;
HTC_DATA_REQUEST_QUEUE *sendQueue;
#ifdef HTC_SYNC
HTC_REG_BUFFER *regBuffer;
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("htcSendFrame - Enter\n"));
/* Get the context */
AR_DEBUG_ASSERT(endPoint != NULL);
endPointId = GET_ENDPOINT_ID(endPoint);
target = endPoint->target;
AR_DEBUG_ASSERT(target != NULL);
sendQueue = &endPoint->sendQueue;
AR_DEBUG_ASSERT(sendQueue != NULL);
regList = &target->regList;
AR_DEBUG_ASSERT(regList != NULL);
/*
* Transmit the frames as long as we have the credits available and
* the queue is not out of them
*/
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_LOCK(&creditCS);
txCreditsAvailable = GET_TX_CREDITS_AVAILABLE(endPoint);
txCreditsConsumed = GET_TX_CREDITS_CONSUMED(endPoint);
SET_TX_CREDITS_AVAILABLE(endPoint, 0);
SET_TX_CREDITS_CONSUMED(endPoint, txCreditsConsumed + txCreditsAvailable);
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_UNLOCK(&creditCS);
/*
* Send the packet only when there are packets to be sent and there
* are positive number of credits available.
*/
while((!IS_DATA_QUEUE_EMPTY(sendQueue)) && txCreditsAvailable)
{
/* Get the request buffer from the Pending Send Queue */
element = removeFromMboxQueue(sendQueue);
mboxBuffer = GET_MBOX_BUFFER(element);
/*
* Prepend the actual length in the first 2 bytes of the outgoing
* packet.
*/
mboxBuffer->buffer -= HTC_HEADER_LEN;
A_MEMCPY(mboxBuffer->buffer, &mboxBuffer->bufferLength, HTC_HEADER_LEN);
/*
* Adjust the length in the block mode only when its not an integral
* multiple of the block size. Assumption is that the block size is
* a power of 2.
*/
frameLength = mboxBuffer->bufferLength + HTC_HEADER_LEN;
paddedLength = (frameLength + (endPoint->blockSize - 1)) &
(~(endPoint->blockSize - 1));
mboxBuffer->actualLength = paddedLength;
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("Original frame length: %d, Padded frame length: %d\n", frameLength, paddedLength));
AR_DEBUG_PRINTBUF(mboxBuffer->buffer, mboxBuffer->actualLength);
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("Available Tx credits: %d\n", txCreditsAvailable));
/* Frame the interface request */
dmode = (endPoint->blockSize > 1) ? HIF_BLOCK_BASIS : HIF_BYTE_BASIS;
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
HIF_ASYNCHRONOUS, dmode, HIF_INCREMENTAL_ADDRESS);
address = endPoint->address;
/* Send the data to the bus driver */
status = HIFReadWrite(target->device, address, mboxBuffer->buffer,
mboxBuffer->actualLength, &request, element);
#ifndef HTC_SYNC
if (status != A_OK) {
#else
if (status != A_OK && status != A_PENDING) {
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
("Frame transmission failed\n"));
AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
("EndPoint: %d, Tx credits available: %d\n",
endPointId, GET_TX_CREDITS_AVAILABLE(endPoint)));
/*
* We need to check just in case the callback routine was called
* with the error status before we reach this point and in that
* context we fee up the buffer so its just a conservative design.
*/
if (!IS_ELEMENT_FREE(element)) {
mboxBuffer->buffer += HTC_HEADER_LEN;
FRAME_EVENT(eventInfo, mboxBuffer->buffer,
mboxBuffer->bufferLength,
mboxBuffer->actualLength,
A_ECANCELED, mboxBuffer->cookie);
RECYCLE_DATA_REQUEST_ELEMENT(element);
dispatchEvent(target, endPointId, HTC_BUFFER_SENT, &eventInfo);
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("htcSendFrame - Exit\n"));
return;
}
#ifdef HTC_SYNC
else if (status == A_OK) {
element->completionCB(element, status);
}
#endif
txCreditsAvailable -= 1;
txCreditsConsumed += 1;
#ifdef DEBUG
txcreditsavailable[endPointId] = txCreditsAvailable;
txcreditsconsumed[endPointId] = txCreditsConsumed;
#endif /* DEBUG */
if (!txCreditsAvailable) {
AR_DEBUG_ASSERT(txCreditsConsumed);
/*
* Instead of taking an interrupt we can just poll for more
* credits that might have been queued up by now.
*/
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
HIF_FIXED_ADDRESS);
address = getRegAddr(TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
element = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(element != NULL);
FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1],
txCreditsConsumed, TX_CREDIT_COUNTER_DECREMENT_REG,
endPointId);
status = HIFReadWrite(target->device, address,
&endPoint->txCreditsAvailable[1],
txCreditsConsumed, &request, element);
#ifndef HTC_SYNC
AR_DEBUG_ASSERT(status == A_OK);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("htcSendFrame - Exit\n"));
return;
#else
AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
if ( status == A_OK ) {
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): LOCK at line %d in file %s \n", __LINE__, __FILE__));
A_MUTEX_LOCK(&creditCS);
regBuffer = GET_REG_BUFFER(element);
/* Calculate the number of credits available */
AR_DEBUG_ASSERT(GET_TX_CREDITS_CONSUMED(endPoint) == \
regBuffer->length);
SET_TX_CREDITS_AVAILABLE(endPoint, regBuffer->buffer[0] -
GET_TX_CREDITS_CONSUMED(endPoint));
SET_TX_CREDITS_CONSUMED(endPoint, 0);
txCreditsAvailable = GET_TX_CREDITS_AVAILABLE(endPoint);
txCreditsConsumed = GET_TX_CREDITS_CONSUMED(endPoint);
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_UNLOCK(&creditCS);
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("Pulling %d tx credits from the target\n",
txCreditsAvailable));
#ifdef DEBUG
txcreditsavailable[endPointId] = txCreditsAvailable;
txcreditsconsumed[endPointId] = txCreditsConsumed;
#endif /* DEBUG */
freeRegRequestElement(element);
if (!txCreditsAvailable) {
/* Enable the Tx credit counter interrupt so that we can get
* the credits posted by the target */
htcEnableCreditCounterInterrupt(target, endPointId);
/* Counter Interrupts have been enabled if
* txCreditsAvailable is still 0 after polling. We need to
* return here as there is nothing we can send till we get
* a Counter Interrupt.
*/
return;
}
}
#endif
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_LOCK(&creditCS);
SET_TX_CREDITS_AVAILABLE(endPoint, txCreditsAvailable);
SET_TX_CREDITS_CONSUMED(endPoint, txCreditsConsumed);
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_UNLOCK(&creditCS);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("htcSendFrame - Exit\n"));
}
void
htcSendBlkSize(HTC_ENDPOINT *endPoint)
{
A_STATUS status;
A_UINT32 address;
HTC_TARGET *target;
HIF_REQUEST request;
HTC_ENDPOINT_ID endPointId;
HTC_QUEUE_ELEMENT *element;
HTC_MBOX_BUFFER *mboxBuffer;
HTC_DATA_REQUEST_QUEUE *sendQueue;
HTC_REG_REQUEST_LIST *regList;
/* Get the context */
AR_DEBUG_ASSERT(endPoint != NULL);
target = endPoint->target;
AR_DEBUG_ASSERT(target != NULL);
regList = &target->regList;
AR_DEBUG_ASSERT(regList != NULL);
sendQueue = &endPoint->sendQueue;
AR_DEBUG_ASSERT(sendQueue != NULL);
endPointId = GET_ENDPOINT_ID(endPoint);
/* Decrement the tx credit count */
AR_DEBUG_ASSERT(endPoint->txCreditsConsumed == 0);
endPoint->txCreditsConsumed = 1;
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
element = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(element != NULL);
FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1],
endPoint->txCreditsConsumed,
TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
status = HIFReadWrite(target->device, address,
&endPoint->txCreditsAvailable[1],
endPoint->txCreditsConsumed, &request, element);
#ifndef HTC_SYNC
AR_DEBUG_ASSERT(status == A_OK);
#else
AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
if (status == A_OK) {
element->completionCB(element, status);
}
#endif
/* Negotiate the maximum block size for the endpoint */
addToMboxQueue(sendQueue, (A_UCHAR *)&endPoint->blockSize,
sizeof(endPoint->blockSize), sizeof(endPoint->blockSize),
NULL);
element = removeFromMboxQueue(sendQueue);
element->completionCB = htcBlkSzNegCompletionCB;
mboxBuffer = GET_MBOX_BUFFER(element);
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
address = endPoint->address;
status = HIFReadWrite(target->device, address, mboxBuffer->buffer,
mboxBuffer->bufferLength, &request, element);
#ifndef HTC_SYNC
AR_DEBUG_ASSERT(status == A_OK);
#else
AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
if (status == A_OK) {
element->completionCB(element, status);
}
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("Mailbox(%d), Block size: %d\n",
endPointId, endPoint->blockSize));
}