www.pudn.com > sdio-2.6.18-full.rar > htc_events.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 different events and callbacks
* from the hardware interface layer.
*/
#include "htc_internal.h"
/* ------ Global Variable Declarations ------- */
extern A_MUTEX_T instanceCS, counterCS, creditCS;
extern A_WAITQUEUE_HEAD htcEvent;
#ifdef DEBUG
extern A_UINT32 debughtc;
extern A_UINT32 txcreditsavailable[HTC_MAILBOX_NUM_MAX];
extern A_UINT32 txcreditsconsumed[HTC_MAILBOX_NUM_MAX];
extern A_UINT32 txcreditintrenable[HTC_MAILBOX_NUM_MAX];
extern A_UINT32 txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX];
#endif
extern A_UINT32 tx_complete[HTC_MAILBOX_NUM_MAX]; /* Num of tx complete */
/* ------ Static Variables ------ */
/* ------ Functions ------ */
#ifdef CF
A_STATUS htcInterruptEnabler(HIF_DEVICE *device) {
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
HTC_TARGET *target;
target = getTargetInstance(device);
AR_DEBUG_ASSERT(target != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("htcInterruptEnabler Enter target: 0x%p\n", target));
target->table.int_status_enable = INT_STATUS_ENABLE_ERROR_SET(0x01) |
INT_STATUS_ENABLE_CPU_SET(0x01) |
INT_STATUS_ENABLE_COUNTER_SET(0x01) |
INT_STATUS_ENABLE_MBOX_DATA_SET(0x0F);
/* Reenable Dragon Interrupts */
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.int_status_enable, 1,
&request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("htcInterruptEnabler Exit\n"));
return A_OK;
}
#endif /* CF */
A_STATUS
htcRWCompletionHandler(void *context,
A_STATUS status)
{
HTC_QUEUE_ELEMENT *element;
element = (HTC_QUEUE_ELEMENT *)context;
AR_DEBUG_ASSERT(element != NULL);
return(element->completionCB(element, status));
}
A_STATUS
htcTxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
A_STATUS status)
{
HTC_TARGET *target;
HTC_ENDPOINT_ID endPointId;
HTC_ENDPOINT *endPoint;
HTC_EVENT_INFO eventInfo;
HTC_MBOX_BUFFER *mboxBuffer;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("htcTxCompletionCB - Enter\n"));
/* Get the context */
mboxBuffer = GET_MBOX_BUFFER(element);
AR_DEBUG_ASSERT(mboxBuffer != NULL);
endPoint = mboxBuffer->endPoint;
AR_DEBUG_ASSERT(endPoint != NULL);
target = endPoint->target;
AR_DEBUG_ASSERT(target != NULL);
endPointId = GET_ENDPOINT_ID(endPoint);
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("mboxBuffer: 0x%p, buffer: 0x%p, endPoint(%d): 0x%p, target: 0x%p\n", mboxBuffer, mboxBuffer->buffer, endPointId, endPoint, target));
/* Return the buffer to the user if the transmission was not successful */
if (status != A_OK) {
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)));
/*
* In the failure case it is possible that while queueing of the
* request itself it returned an error status in which case we
* would have dispatched an event and freed the element there
* itself. Ideally if it failed to queue the request then it
* should not generate a callback but we are being a little
* conservative.
*/
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,
("htcTxCompletionCB - Exit\n"));
}
return A_OK;
}
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
("Frame transmission complete\n"));
/*
* The user should see the actual length and buffer length
* to be the same. In case of block mode, we use the actual length
* parameter to reflect the total number of bytes transmitted after
* padding.
*/
mboxBuffer->actualLength = mboxBuffer->bufferLength;
mboxBuffer->buffer += HTC_HEADER_LEN;
/*
* Return the transmit buffer to the user through the HTC_BUFFER_SENT
* event indicating that the frame was transmitted successfully.
*/
FRAME_EVENT(eventInfo, mboxBuffer->buffer, mboxBuffer->bufferLength,
mboxBuffer->actualLength, A_OK, mboxBuffer->cookie);
RECYCLE_DATA_REQUEST_ELEMENT(element);
tx_complete[endPointId] += 1;
dispatchEvent(target, endPointId, HTC_BUFFER_SENT, &eventInfo);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("htcTxCompletionCB - Exit\n"));
return A_OK;
}
A_STATUS
htcBlkSzNegCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
A_STATUS status)
{
HTC_TARGET *target;
HTC_ENDPOINT *endPoint;
HIF_REQUEST request;
HTC_MBOX_BUFFER *mboxBuffer;
HTC_REG_REQUEST_ELEMENT *regElement;
A_UINT32 address;
/* Get the context */
mboxBuffer = GET_MBOX_BUFFER(element);
AR_DEBUG_ASSERT(mboxBuffer != NULL);
endPoint = mboxBuffer->endPoint;
AR_DEBUG_ASSERT(endPoint != NULL);
target = endPoint->target;
AR_DEBUG_ASSERT(target != NULL);
/* Recycle the request element */
RECYCLE_DATA_REQUEST_ELEMENT(element);
element->completionCB = htcTxCompletionCB;
if (status == A_OK) {
/* Mark the state to be ready */
endPoint->enabled = TRUE;
/* Set the state of the target as ready */
if (target->endPoint[ENDPOINT1].enabled &&
target->endPoint[ENDPOINT2].enabled &&
target->endPoint[ENDPOINT3].enabled &&
target->endPoint[ENDPOINT4].enabled )
{
/* Send the INT_WLAN interrupt to the target */
target->table.int_wlan = 1;
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
HIF_FIXED_ADDRESS);
address = getRegAddr(INT_WLAN_REG, ENDPOINT_UNUSED);
regElement = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(regElement != NULL);
FILL_REG_BUFFER(regElement, &target->table.int_wlan, 1,
INT_WLAN_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.int_wlan,
1, &request, regElement);
#ifndef HTC_SYNC
AR_DEBUG_ASSERT(status == A_OK);
#else
AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
if(status == A_OK) {
regElement->completionCB(regElement, status);
}
#endif
}
}
return A_OK;
}
A_STATUS
htcRxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
A_STATUS status)
{
HTC_TARGET *target;
HTC_ENDPOINT *endPoint;
HTC_EVENT_INFO eventInfo;
HTC_ENDPOINT_ID endPointId;
HTC_MBOX_BUFFER *mboxBuffer;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
("htcRxCompletionCB - Enter\n"));
/* Get the context */
mboxBuffer = GET_MBOX_BUFFER(element);
AR_DEBUG_ASSERT(mboxBuffer != NULL);
endPoint = mboxBuffer->endPoint;
AR_DEBUG_ASSERT(endPoint != NULL);
target = endPoint->target;
AR_DEBUG_ASSERT(target != NULL);
endPointId = GET_ENDPOINT_ID(endPoint);
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV,
("mboxBuffer: 0x%p, buffer: 0x%p, endPoint(%d): 0x%p, target: 0x%p\n", mboxBuffer, mboxBuffer->buffer, endPointId, endPoint, target));
/* Return the buffer to the user if the reception was not successful */
if (status != A_OK) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_RECV,
("Frame reception failed\n"));
/*
* In the failure case it is possible that while queueing of the
* request itself it returned an error status in which case we
* would have dispatched an event and freed the element there
* itself. Ideally if it failed to queue the request then it
* should not generate a callback but we are being a little
* conservative.
*/
if (!(IS_ELEMENT_FREE(element))) {
mboxBuffer->actualLength = 0;
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_RECEIVED, &eventInfo);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
("htcRxCompletionCB - Exit\n"));
}
return A_OK;
}
AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV,
("Frame reception complete\n"));
AR_DEBUG_PRINTBUF(mboxBuffer->buffer, mboxBuffer->actualLength);
/*
* Advance the pointer by the size of HTC header and pass the payload
* pointer to the upper layer.
*/
mboxBuffer->actualLength = ((mboxBuffer->buffer[0] << 0) |
(mboxBuffer->buffer[1] << 8));
mboxBuffer->buffer += HTC_HEADER_LEN;
/*
* Frame the HTC_BUFFER_RECEIVED to the upper layer indicating that the
* packet has been succesfully received.
*/
FRAME_EVENT(eventInfo, mboxBuffer->buffer, mboxBuffer->bufferLength,
mboxBuffer->actualLength, A_OK, mboxBuffer->cookie);
/* Recycle the bufferElement structure */
RECYCLE_DATA_REQUEST_ELEMENT(element);
/* Dispatch the event */
dispatchEvent(target, endPointId, HTC_BUFFER_RECEIVED, &eventInfo);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
("htcRxCompletion - Exit\n"));
return A_OK;
}
A_STATUS
htcRegCompletionCB(HTC_REG_REQUEST_ELEMENT *element,
A_STATUS status)
{
A_STATUS ret;
HTC_TARGET *target;
HTC_ENDPOINT *endPoint;
HTC_REG_BUFFER *regBuffer;
A_UINT8 txCreditsConsumed;
A_UINT8 txCreditsAvailable;
HTC_ENDPOINT_ID endPointId;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV | ATH_DEBUG_SEND,
("htcRegCompletion - Enter\n"));
AR_DEBUG_ASSERT(status == A_OK);
/* Get the context */
AR_DEBUG_ASSERT(element != NULL);
regBuffer = GET_REG_BUFFER(element);
AR_DEBUG_ASSERT(regBuffer != NULL);
target = regBuffer->target;
AR_DEBUG_ASSERT(target != NULL);
/* Identify the register and the operation responsible for the callback */
ret = A_OK;
switch(regBuffer->base) {
case TX_CREDIT_COUNTER_DECREMENT_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("TX_CREDIT_COUNTER_DECREMENT_REG\n"));
endPointId = regBuffer->offset;
endPoint = &target->endPoint[endPointId];
AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
A_MUTEX_LOCK(&creditCS);
/* Calculate the number of credits available */
AR_DEBUG_ASSERT(GET_TX_CREDITS_CONSUMED(endPoint) == regBuffer->length);
AR_DEBUG_ASSERT(regBuffer->buffer[0] >=
GET_TX_CREDITS_CONSUMED(endPoint));
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 */
if (txCreditsAvailable) {
htcSendFrame(endPoint);
} else {
/*
* Enable the Tx credit counter interrupt so that we can get the
* credits posted by the target.
*/
htcEnableCreditCounterInterrupt(target, endPointId);
#ifdef DEBUG
txcreditintrenable[endPointId] += 1;
txcreditintrenableaggregate[endPointId] += 1;
#endif /* DEBUG */
}
break;
case TX_CREDIT_COUNTER_RESET_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("TX_CREDIT_COUNTER_RESET_REG\n"));
endPointId = regBuffer->offset;
/*
* Enable the Tx credit counter interrupt so that we can get the
* credits posted by the target.
*/
htcEnableCreditCounterInterrupt(target, endPointId);
#ifdef DEBUG
txcreditintrenable[endPointId] += 1;
txcreditintrenableaggregate[endPointId] += 1;
#endif /* DEBUG */
break;
case COUNTER_INT_STATUS_ENABLE_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("COUNTER_INT_STATUS_ENABLE: 0x%x\n",
target->table.counter_int_status_enable));
break;
case COUNTER_INT_STATUS_DISABLE_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("COUNTER_INT_STATUS_DISABLE:0x%x\n",
target->table.counter_int_status_enable));
HIFAckInterrupt(target->device);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler - ACK\n"));
break;
case INT_WLAN_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("INT_WLAN: 0x%x\n",
target->table.int_wlan));
target->table.int_wlan = 0;
/* Mark the target state as ready and signal the waiting sem */
target->ready = TRUE;
A_WAKE_UP(&htcEvent);
break;
case INT_STATUS_ENABLE_REG:
AR_DEBUG_PRINTF(ATH_DEBUG_INF,("INT_STATUS_ENABLE: 0x%x\n",
target->table.int_status_enable));
break;
default:
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid register address: %d\n", regBuffer->base));
}
/* Free the register request structure */
freeRegRequestElement(element);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcRegCompletion - Exit\n"));
return ret;
}
A_STATUS
htcTargetInsertedHandler(HIF_DEVICE *device)
{
HTC_TARGET *target;
HTC_ENDPOINT *endPoint;
A_UINT8 count1, count2;
HTC_EVENT_INFO eventInfo;
HTC_REG_BUFFER *regBuffer;
HTC_QUEUE_ELEMENT *element;
HTC_MBOX_BUFFER *mboxBuffer;
HTC_REG_REQUEST_LIST *regList;
HTC_DATA_REQUEST_QUEUE *sendQueue, *recvQueue;
A_UINT32 mboxAddress[HTC_MAILBOX_NUM_MAX];
A_UINT32 blockSize[HTC_MAILBOX_NUM_MAX];
#ifdef CF
HIF_REQUEST request;
A_STATUS status;
A_UINT32 address;
#endif /* CF */
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Enter\n"));
/* Initialize the locks */
A_MUTEX_INIT(&instanceCS);
A_MUTEX_INIT(&creditCS);
A_MUTEX_INIT(&counterCS);
/* Allocate target memory */
if ((target = (HTC_TARGET *)A_MALLOC(sizeof(HTC_TARGET))) == NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
return A_ERROR;
}
A_MEMZERO(target, sizeof(HTC_TARGET));
target->device = device;
target->ready = FALSE;
/* Initialize the endpoints, mbox queues, event table */
for (count1 = ENDPOINT1; count1 <= ENDPOINT4; count1 ++) {
endPoint = &target->endPoint[count1];
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("endPoint[%d]: %p\n", count1, endPoint));
A_MEMZERO(endPoint->txCreditsAvailable, HTC_TX_CREDITS_NUM_MAX);
endPoint->txCreditsConsumed = 0;
endPoint->txCreditsIntrEnable = FALSE;
endPoint->rxLengthPending = 0;
endPoint->target = target;
endPoint->enabled = FALSE;
for (count2 = 0; count2sendQueue;
sendQueue->head = sendQueue->size = 0;
element = &sendQueue->element[count2];
A_MEMZERO(element, sizeof(HTC_DATA_REQUEST_ELEMENT));
element->buffer.free = TRUE;
element->completionCB = htcTxCompletionCB;
mboxBuffer = GET_MBOX_BUFFER(element);
mboxBuffer->endPoint = endPoint;
/* Receive Queue */
recvQueue = &endPoint->recvQueue;
recvQueue->head = recvQueue->size = 0;
element = &recvQueue->element[count2];
A_MEMZERO(element, sizeof(HTC_DATA_REQUEST_ELEMENT));
element->buffer.free = TRUE;
element->completionCB = htcRxCompletionCB;
mboxBuffer = GET_MBOX_BUFFER(element);
mboxBuffer->endPoint = endPoint;
}
A_MEMZERO(&target->endPoint[count1].eventTable,
sizeof(HTC_ENDPOINT_EVENT_TABLE));
}
/* Populate the block size for each of the end points */
HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
&blockSize, sizeof(blockSize));
HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_ADDR,
&mboxAddress, sizeof(mboxAddress));
for (count1 = ENDPOINT1; count1 <= ENDPOINT4; count1 ++) {
endPoint = &target->endPoint[count1];
endPoint->blockSize = blockSize[count1];
endPoint->address = mboxAddress[count1];
}
/* Initialize the shadow copy of the target register table */
A_MEMZERO(&target->table, sizeof(HTC_REGISTER_TABLE));
/* Initialize the register request list */
regList = &target->regList;
for (count1 = 0; count1 < HTC_REG_REQUEST_LIST_SIZE; count1 ++) {
element = ®List->element[count1];
A_MEMZERO(element, sizeof(HTC_REG_REQUEST_ELEMENT));
element->buffer.free = TRUE;
element->completionCB = htcRegCompletionCB;
regBuffer = GET_REG_BUFFER(element);
regBuffer->target = target;
}
/* Add the target instance to the global list */
addTargetInstance(target);
#ifdef CF
/* Disable all the dragon interrupts */
target->table.int_status_enable = 0;
target->table.cpu_int_status_enable = 0;
target->table.error_status_enable = 0;
target->table.counter_int_status_enable = 0;
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.int_status_enable, 4, &request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
#endif /* CF */
/*
* Frame a TARGET_AVAILABLE event and send it to the host. Return the
* HIF_DEVICE handle as a parameter with the event.
*/
FRAME_EVENT(eventInfo, (A_UCHAR *)device, sizeof(HIF_DEVICE *),
sizeof(HIF_DEVICE *), A_OK, NULL);
dispatchEvent(target, ENDPOINT_UNUSED, HTC_TARGET_AVAILABLE, &eventInfo);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Exit\n"));
return A_OK;
}
A_STATUS
htcTargetRemovedHandler(HIF_DEVICE *device)
{
HTC_TARGET *target;
HTC_EVENT_INFO eventInfo;
/* Get the target instance bound to this device */
target = getTargetInstance(device);
if (target != NULL) {
/* Frame a TARGET_UNAVAILABLE event and send it to the host */
FRAME_EVENT(eventInfo, NULL, 0, 0, A_OK, NULL);
dispatchEvent(target, ENDPOINT_UNUSED, HTC_TARGET_UNAVAILABLE,
&eventInfo);
}
return A_OK;
}
#ifdef CF
A_STATUS
htcInterruptDisabler(HIF_DEVICE *device,A_BOOL *callDsr)
{
A_STATUS status;
A_UINT32 address;
HTC_TARGET *target;
HIF_REQUEST request;
target = getTargetInstance(device);
AR_DEBUG_ASSERT(target != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("htcInterruptDisabler Enter target: 0x%p\n", target));
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.host_int_status, 1, &request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
/* Handle Suprise removal of CF card. Upon removal of the card the
* host_int_status reads 0xFF
*/
if (target->table.host_int_status == 0xFF) {
*callDsr=FALSE;
return A_OK;
}
if ((target->table.int_status_enable & target->table.host_int_status) == 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("htcInterruptDisabler: MisRouted / Spurious interrupt : 0x%p\n", target));
*callDsr=FALSE;
} else {
/*
* Disable the interrupts from Dragon.
* We do the interrupt servicing in the bottom half and reenable the
* Dragon interrupts at the end of the bottom-half
*/
target->table.int_status_enable = 0;
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.int_status_enable, 1, &request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
*callDsr=TRUE;
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcInterruptDisabler: Exit\n"));
return A_OK;
}
#endif /* CF */
A_STATUS
htcDSRHandler(HIF_DEVICE *device)
{
A_STATUS status;
A_UINT32 address;
HTC_TARGET *target;
HIF_REQUEST request;
A_UCHAR host_int_status;
target = getTargetInstance(device);
AR_DEBUG_ASSERT(target != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("htcDsrHandler: Enter (target: 0x%p\n", target));
/*
* Read the first 28 bytes of the HTC register table. This will yield us
* the value of different int status registers and the lookahead
* registers.
* length = sizeof(int_status) + sizeof(cpu_int_status) +
* sizeof(error_int_status) + sizeof(counter_int_status) +
* sizeof(mbox_frame) + sizeof(rx_lookahead_valid) +
* sizeof(hole) + sizeof(rx_lookahead) +
* sizeof(int_status_enable) + sizeof(cpu_int_status_enable) +
* sizeof(error_status_enable) +
* sizeof(counter_int_status_enable);
*/
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.host_int_status, 28,
&request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
#ifdef DEBUG
dumpRegisters(target);
#endif /* DEBUG */
#ifdef CF
/* Update only those registers that are enabled */
/* This is not required as we have already checked for spurious interrupt
* in htcInterruptDisabler
*/
host_int_status = target->table.host_int_status;
#else
/* Update only those registers that are enabled */
host_int_status = target->table.host_int_status &
target->table.int_status_enable;
#endif /* CF */
AR_DEBUG_ASSERT(host_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("Valid interrupt source(s) in INT_STATUS: 0x%x\n",
host_int_status));
if (HOST_INT_STATUS_CPU_GET(host_int_status)) {
/* CPU Interrupt */
htcServiceCPUInterrupt(target);
}
if (HOST_INT_STATUS_ERROR_GET(host_int_status)) {
/* Error Interrupt */
htcServiceErrorInterrupt(target);
}
if (HOST_INT_STATUS_MBOX_DATA_GET(host_int_status)) {
/* Mailbox Interrupt */
htcServiceMailboxInterrupt(target);
}
if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) {
/* Counter Interrupt */
htcServiceCounterInterrupt(target);
} else {
/* Ack the interrupt */
HIFAckInterrupt(target->device);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler - ACK\n"));
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler: Exit\n"));
return A_OK;
}
void
htcServiceCPUInterrupt(HTC_TARGET *target)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
A_UINT8 cpu_int_status;
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("CPU Interrupt\n"));
cpu_int_status = target->table.cpu_int_status &
target->table.cpu_int_status_enable;
AR_DEBUG_ASSERT(cpu_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("Valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
cpu_int_status));
/* Figure out the interrupt number */
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Interrupt Number: 0x%x\n",
htcGetBitNumSet(cpu_int_status)));
/* Clear the interrupt */
target->table.cpu_int_status = cpu_int_status; /* W1C */
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(CPU_INT_STATUS_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.cpu_int_status, 1, &request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
}
void
htcServiceErrorInterrupt(HTC_TARGET *target)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
A_UINT8 error_int_status;
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Error Interrupt\n"));
error_int_status = target->table.error_int_status &
target->table.error_status_enable;
AR_DEBUG_ASSERT(error_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("Valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
error_int_status));
if (ERROR_INT_STATUS_WAKEUP_GET(error_int_status)) {
/* Wakeup */
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Wakeup\n"));
}
if (ERROR_INT_STATUS_RX_UNDERFLOW_GET(error_int_status)) {
/* Rx Underflow */
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Rx Underflow\n"));
}
if (ERROR_INT_STATUS_TX_OVERFLOW_GET(error_int_status)) {
/* Tx Overflow */
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Tx Overflow\n"));
}
/* Clear the interrupt */
target->table.error_int_status = error_int_status; /* W1C */
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(ERROR_INT_STATUS_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.error_int_status, 1,
&request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
}
void
htcServiceCounterInterrupt(HTC_TARGET *target)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
HTC_ENDPOINT *endPoint;
HTC_ENDPOINT_ID endPointId;
A_UINT8 counter_int_status;
A_UINT8 reset_credit_int_status;
A_UINT8 update_credit_int_status;
HTC_REG_REQUEST_ELEMENT *element;
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Counter Interrupt\n"));
counter_int_status = target->table.counter_int_status &
target->table.counter_int_status_enable;
AR_DEBUG_ASSERT(counter_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("Valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
counter_int_status));
/* Service the reset credit counter interrupt */
reset_credit_int_status = (counter_int_status & 0x0F);
while(reset_credit_int_status) {
endPointId = htcGetBitNumSet(reset_credit_int_status);
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("endPoint(%d): %p\n", endPointId, endPoint));
/* Initialize the number of credits available to zero */
SET_TX_CREDITS_AVAILABLE(endPoint, 0);
/* Clear the interrupt */
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
HIF_ASYNCHRONOUS, HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(TX_CREDIT_COUNTER_RESET_REG, endPointId);
element = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(element != NULL);
FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1], 1,
TX_CREDIT_COUNTER_RESET_REG, endPointId);
status = HIFReadWrite(target->device, address,
&endPoint->txCreditsAvailable[1],
1, &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) {
/* Enable the Tx credit counter interrupt so that we can get the
* credits posted by the target */
htcEnableCreditCounterInterrupt(target, endPointId);
}
#endif
reset_credit_int_status &=
~(1 << htcGetBitNumSet(reset_credit_int_status));
}
/* Disable the credit counter interrupt */
htcDisableCreditCounterInterrupt(target, ENDPOINT_UNUSED);
/* Service the credit counter interrupt */
update_credit_int_status = counter_int_status & 0xF0;
while(update_credit_int_status) {
endPointId = htcGetBitNumSet(update_credit_int_status) -
HTC_MAILBOX_NUM_MAX;
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
/* This is the minimum number of credits that we would have got */
AR_DEBUG_ASSERT(GET_TX_CREDITS_AVAILABLE(endPoint) == 0);
SET_TX_CREDITS_AVAILABLE(endPoint, 1);
#ifdef DEBUG
txcreditsavailable[endPointId] = GET_TX_CREDITS_AVAILABLE(endPoint);
txcreditintrenable[endPointId] -= 1;
#endif /* DEBUG */
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Tx Credits Available: %d\n",
GET_TX_CREDITS_AVAILABLE(endPoint)));
if (!target->ready) {
htcSendBlkSize(endPoint);
} else {
htcSendFrame(endPoint);
}
update_credit_int_status &=
~(1 << htcGetBitNumSet(update_credit_int_status));
}
}
void
htcEnableCreditCounterInterrupt(HTC_TARGET *target,
HTC_ENDPOINT_ID endPointId)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
HTC_ENDPOINT *endPoint;
HTC_REG_REQUEST_ELEMENT *element;
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
A_MUTEX_LOCK(&counterCS);
endPoint->txCreditsIntrEnable = TRUE;
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
HIF_FIXED_ADDRESS);
address = getRegAddr(COUNTER_INT_STATUS_ENABLE_REG,
ENDPOINT_UNUSED);
element = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(element != NULL);
FILL_REG_BUFFER(element, NULL, 1, COUNTER_INT_STATUS_ENABLE_REG,
(target->endPoint[0].txCreditsIntrEnable << (4)) |
(target->endPoint[1].txCreditsIntrEnable << (5)) |
(target->endPoint[2].txCreditsIntrEnable << (6)) |
(target->endPoint[3].txCreditsIntrEnable << (7)) | 0x0F);
status = HIFReadWrite(target->device, address,
(A_UCHAR *)&((GET_REG_BUFFER(element))->offset),
1, &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
A_MUTEX_UNLOCK(&counterCS);
}
void
htcDisableCreditCounterInterrupt(HTC_TARGET *target,
HTC_ENDPOINT_ID unused)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
HTC_ENDPOINT *endPoint;
HTC_ENDPOINT_ID endPointId;
A_UINT8 counter_int_status;
A_UINT8 update_credit_int_status;
HTC_REG_REQUEST_ELEMENT *element;
A_MUTEX_LOCK(&counterCS);
/* The Tx credit counter update bits are reflected in the upper nibble */
counter_int_status = target->table.counter_int_status &
target->table.counter_int_status_enable;
update_credit_int_status = counter_int_status & 0xF0;
while(update_credit_int_status) {
endPointId = htcGetBitNumSet(update_credit_int_status) -
HTC_MAILBOX_NUM_MAX;
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("endPoint(%d): %p\n", endPointId, endPoint));
/* Disable the tx credit interrupt */
endPoint->txCreditsIntrEnable = FALSE;
update_credit_int_status &=
~(1 << htcGetBitNumSet(update_credit_int_status));
}
HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
address = getRegAddr(COUNTER_INT_STATUS_DISABLE_REG, ENDPOINT_UNUSED);
element = allocateRegRequestElement(target);
AR_DEBUG_ASSERT(element != NULL);
FILL_REG_BUFFER(element, NULL, 1,
COUNTER_INT_STATUS_DISABLE_REG,
(target->endPoint[0].txCreditsIntrEnable << (4)) |
(target->endPoint[1].txCreditsIntrEnable << (5)) |
(target->endPoint[2].txCreditsIntrEnable << (6)) |
(target->endPoint[3].txCreditsIntrEnable << (7)) | 0x0F);
status = HIFReadWrite(target->device, address,
(A_UCHAR *)&((GET_REG_BUFFER(element))->offset),
1, &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
A_MUTEX_UNLOCK(&counterCS);
}
void
htcServiceMailboxInterrupt(HTC_TARGET *target)
{
A_STATUS status;
A_UINT32 address;
HIF_REQUEST request;
HTC_ENDPOINT *endPoint;
HTC_ENDPOINT_ID endPointId;
A_UINT8 mailbox_int_status;
AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Mailbox Interrupt\n"));
/* The Rx interrupt bits are reflected in the lower nibble */
mailbox_int_status = target->table.host_int_status &
HOST_INT_STATUS_MBOX_DATA_MASK;
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("Valid mailbox interrupt source(s) in INT_STATUS: 0x%x\n",
mailbox_int_status));
/* Disable the receive interrupt for all four mailboxes */
target->table.int_status_enable &= ~(HOST_INT_STATUS_MBOX_DATA_MASK);
do {
while(mailbox_int_status) {
endPointId = htcGetBitNumSet(mailbox_int_status);
endPoint = &target->endPoint[endPointId];
AR_DEBUG_ASSERT(endPoint != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_INF,
("endPoint(%d): %p\n", endPointId, endPoint));
/* Service the Rx interrupt */
htcReceiveFrame(endPoint);
mailbox_int_status &= ~(1 << htcGetBitNumSet(mailbox_int_status));
}
/*
* Read the register table again. Repeat the process until there are
* no more valid packets queued up on receive. It is assumed that
* the following request will be serialized along with the request
* above and will be completed in the order in which it is received
* by the bus driver.
*/
HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
HIF_SYNCHRONOUS, HIF_BYTE_BASIS,
HIF_INCREMENTAL_ADDRESS);
address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
status = HIFReadWrite(target->device, address,
&target->table.host_int_status,
24, &request, NULL);
AR_DEBUG_ASSERT(status == A_OK);
mailbox_int_status = target->table.host_int_status &
HOST_INT_STATUS_MBOX_DATA_MASK;
} while (mailbox_int_status);
target->table.int_status_enable |= HOST_INT_STATUS_MBOX_DATA_MASK;
}