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));
}