www.pudn.com > vxworks_stp_src.rar > vxbufs.c, change:2005-10-19,size:20387b


/* 
 * Copyright (c) 2000-2002 Atheros Communications, Inc., All Rights Reserved 
 * 
 * $Id: //depot/sw/releases/4.0/src/ap/vxbufs.c#3 $ 
 * 
 * This module implements the conversion between mBlk and Atheros descriptors 
 * functions. 
 * 
 * mBlk are specially designed for VxWorks.  It is defined as a structure of 
 * clusters. 
 *  
 * typedef struct mBlk { 
 *        M_BLK_HDR   mBlkHdr;        // header 
 *        M_PKT_HDR   mBlkPktHdr;     // pkthdr 
 *        CL_BLK     *pClBLK;         // pointer to cluster blk 
 * } M_BLK; 
 * 
 * // header at beginning of each mBlk 
 * 
 * typedef struct mHdr { 
 *        struct mBlk *mNext;      // next buffer in chain 
 *        struct mBlk *mNextPkt;   // next chain in queue/record 
 *        char        *mData;      // location of data 
 *        int          mLen;       // amount of data in this mBlk 
 *        UCHAR        mType;      // type of data in this mBlk 
 *        UCHAR        mFlags;     // flags; see below 
 *        USHORT       reserved; 
 * } M_BLK_HDR; 
 * 
 * record/packet header in first mBlk of chain; valid if M_PKTHDR set 
 * 
 * typedef struct  pktHdr { 
 *        struct ifnet *rcvif;  // rcv interface 
 *        int           len;    // total packet length 
 * } M_PKT_HDR; 
 * 
 * description of external storage mapped into mBlk, valid if M_EXT set 
 * 
 * typedef struct clBlk { 
 *        CL_BLK_LIST clNode;         // union of next clBlk, buffer ptr 
 *        UINT        clSize;         // cluster size 
 *        int         clRefCnt;       // reference count of the cluster 
 *        FUNCPTR     pClFreeRtn;     // pointer to cluster free routine 
 *        int         clFreeArg1;     // free routine arg 1 
 *        int         clFreeArg2;     // free routine arg 2 
 *        int         clFreeArg3;     // free routine arg 3 
 *        struct netPool *pNetPool;   // pointer to the netPool 
 *  } CL_BLK; 
 * 
 * The Atheros descriptors, ATHEROS_DESC, is a combination of the MAC 
 * hardware DMA descriptor and software only fields. 
 * 
 * The header and frame body are put into separate descriptors.  Chances are the 
 * header will always be a contingous physical buffer and the frame body may 
 * occupy serveral smaller physical buffers. 
 *  
 * All clusters are allocated as received buffers for the incoming data. 
 * 
 * The MBlk and Atheros structures are the different way of the link for 
 * the input data buffers. 
 * 
 * To convert Atheros Descriptors to mBlk structures: XXX 
 * 
 * To convert mBlk structures to Atheros Descriptors: XXX 
 * 
 */ 
 
#include "vxbufs.h" 
#include "stdlib.h" 
#include "netBufLib.h" 
#include "private\timerLibP.h" 
 
#include "wlandrv.h" 
#include "wlanext.h" 
#include "wlansmeext.h" 
#include "wlanSend.h" 
#include "apdefs.h" 
#include "halApi.h" 
#include "ui.h" 
 
#define IS_ETHERTYPE(_typeOrLen)    ((_typeOrLen) >= 0x0600) 
 
int vxDescNoMblk; 
 
/******************************************************************************* 
* 
* vxDescToMBlk - converts Atheros Descriptors to VxWorks mBlk structures 
* 
* The input data from MAC has to be put into clusters directly. The clusters 
* are reserved for the receive function during initialization phase by calling 
* netClusterGet(). And the receive descriptors are pre-allocated and built up 
* using clusters. 
* After the incoming frame is received and put into the descriptor, i.e. cluster 
* Then the clusters are linked using Mblk structures chain and passed to the 
* protocol or DSM layer. 
* 
* the input descriptor is kept for DS purpose. The calling part should free the 
* input descriptor when it is no longer needed or error condition 
* 
* 
* RETURNS:  NULL if unsuccesful 
*           pointer of mBlk structures if successful 
*/ 
M_BLK* 
vxDescToMblk(WLAN_DEV_INFO *pdevInfo, ATHEROS_DESC *pDesc, A_BOOL dup) 
{ 
    NET_POOL_ID     pNetPool = pdevInfo->pNetPool; 
    ATHEROS_DESC    *pCurrDesc; 
    M_BLK           *pMblk = NULL; 
    M_BLK           *pPrevMblk = NULL; 
    M_BLK           *pVarMblk, *pAllocMblk; 
    ATHEROS_DESC    *pHeadDesc = NULL; 
 
    pHeadDesc = pDesc; 
 
    /* any field can we use to check the integrity of descriptor ??? */ 
 
    while (pDesc != NULL) { 
        /* reserve a Mblk */ 
        if ( (pAllocMblk = netMblkGet(pNetPool, M_DONTWAIT, MT_DATA)) == NULL ) { 
            /* ran out of mblks, a receive resource => loaned resources aren't 
             * being returned in time; slow ethernet? slow stack? 
             */ 
            LOG_DROP_FRAME(pdevInfo, rx); 
            if (pMblk != NULL) { 
                netMblkClChainFree(pMblk); 
            } 
            ++vxDescNoMblk; 
            freeBuffandDescChain(pdevInfo, dup ? pHeadDesc : pDesc); 
            return NULL; 
        } 
 
        /* join Mblk */ 
        if ((pVarMblk = netMblkClJoin(pAllocMblk, pDesc->pOSDescPtr)) == NULL ) { 
            /* generally shouldn't happen */ 
            uiPrintf("\nvxDescToMblk: error in join mBlk\n"); 
            netMblkFree(pNetPool, pAllocMblk); 
            if (pMblk != NULL) { 
                netMblkClChainFree(pMblk); 
            } 
            freeBuffandDescChain(pdevInfo, dup ? pHeadDesc : pDesc); 
            return NULL; 
        } 
 
        /* assign the first M_BLK */ 
        if (pMblk == NULL) { 
            pMblk = pVarMblk; 
            pMblk->mBlkHdr.mFlags |= M_PKTHDR; 
            pMblk->mBlkPktHdr.len = 0; 
        } 
 
        ASSERT((void *)A_DATA_P2V(pDesc->bufferPhysPtr) == pDesc->pBufferVirtPtr.ptr); 
 
        pVarMblk->mBlkHdr.mNext    = NULL; 
        pVarMblk->mBlkHdr.mNextPkt = NULL; 
        pVarMblk->mBlkHdr.mData    = pDesc->pBufferVirtPtr.byte; 
        pVarMblk->mBlkHdr.mLen     = pDesc->hw.txControl.bufferLength; 
 
        /* calculate the total length of the frame */ 
        pMblk->mBlkPktHdr.len += pVarMblk->mBlkHdr.mLen; 
 
        /* get the previous mblk in the chain */ 
        if (pPrevMblk != NULL) { 
            pPrevMblk->mBlkHdr.mNext = pVarMblk; 
        } 
 
        /* next descriptor */ 
        pCurrDesc = pDesc; 
        pDesc = pDesc->pNextVirtPtr; 
        pPrevMblk = pVarMblk; 
 
        /* free the current descriptor */ 
        if (dup) { 
            pCurrDesc->pOSDescPtr->clRefCnt += 1; 
        } else { 
            memFreeDescriptor(pdevInfo, pCurrDesc); 
        } 
    } 
 
    return pMblk; 
} 
 
/******************************************************************************* 
* 
* vxMblkToDesc - converts VxWorks mBlk structures to Atheros Descriptors 
* 
* This routine allocates a descriptor to put the 802.3 header in it and then 
* walk through all the strucures to link the data.  this routine could be part 
* of the driver sending routine 
* 
* The calling routine is responsible for the releasing of input mBlk due to DS 
* 
* Ref: Tornado BSP Developer's kit for VxWorks pp 366 
* 
* RETURNS:  NULL if unsuccesful 
*           pointer of Atheros Descriptors if successful 
*/ 
ATHEROS_DESC* 
vxMblkToDesc(WLAN_DEV_INFO* pdevInfo, M_BLK* pHeadMblk) 
{ 
    ATHEROS_DESC *pHeadDesc = NULL; 
    ATHEROS_DESC *pLastDesc = NULL; 
    ATHEROS_DESC *pDesc; 
    M_BLK        *pMblk; 
 
    /* verify the first MBLK contains the header */ 
    if ((pHeadMblk->mBlkHdr.mFlags & M_PKTHDR) != M_PKTHDR) { 
        uiPrintf("vxMblkToDesc: M_PKTHDR not set on first mblk\n"); 
        netMblkClChainFree(pHeadMblk); 
        return NULL; 
    } 
 
    /* generate a descriptor for each of the Mblk */ 
    for (pMblk = pHeadMblk; pMblk; pMblk = pMblk->mBlkHdr.mNext) { 
        /* vxWorks protocol stack is known to send empty mblks */ 
        if (pMblk->mBlkHdr.mLen == 0) { 
            continue; 
        } 
 
        /* allocate a descriptor for Mblk, i.e. frame data */ 
        pDesc = memAllocateDescriptor(pdevInfo); 
        if (!pDesc) { 
            /* clean up before leaving */ 
            freeBuffandDescChain(pdevInfo, pHeadDesc); 
            netMblkClChainFree(pHeadMblk); 
 
            LOG_DROP_FRAME(pdevInfo, tx); 
            return NULL; 
        } 
  
 
 
        /* Initialize the fields of descriptor */ 
        pDesc->pOSDescPtr                = pMblk->pClBlk; 
        pDesc->pBufferVirtPtr.ptr        = pMblk->mBlkHdr.mData; 
        pDesc->bufferPhysPtr             = (A_UINT32)A_DATA_V2P(pDesc->pBufferVirtPtr.ptr); 
        pDesc->pOrigBufferVirtPtr        = pDesc->pOSDescPtr->clNode.pClBuf; 
        pDesc->pOrigbufferPhysPtr        = (A_UINT32)A_DATA_V2P(pDesc->pOrigBufferVirtPtr); 
        pDesc->hw.txControl.bufferLength = pMblk->mBlkHdr.mLen; 
        pDesc->pOSDescPtr->clRefCnt     += 1; 
   
      if (pHeadDesc) { 
            /* Link to last descriptor */ 
            ASSERT(pLastDesc); 
            pLastDesc->nextPhysPtr       = pDesc->thisPhysPtr; 
            pLastDesc->pNextVirtPtr      = pDesc; 
            pLastDesc->hw.txControl.more = 1; 
        } else { 
            pHeadDesc = pDesc; 
            pHeadDesc->hw.txControl.frameLength =  pMblk->mBlkPktHdr.len; 
  
   
        } 
        pLastDesc = pDesc; 
    } 
 
    netMblkClChainFree(pHeadMblk); 
  
    return pHeadDesc; 
} 
 
/****************************************************************************** 
* 
* wlanPacketDataGet - return the beginning of the packet data 
* 
* This routine fills the given <pLinkHdrInfo> with the appropriate offsets. 
* We call this in the MUX, only after decapsulation from dot11 to to 802.3 
* or DIX. 
* 
* RETURNS: OK or ERROR. 
*/ 
 
A_STATUS 
wlanPacketDataGet(M_BLK_ID pMblk, LL_HDR_INFO *pLinkHdrInfo) 
{ 
    LAN_FRAME_HEADER      *pEnetHdr; 
    LAN_FRAME_HEADER       enetHdr; 
 
    struct llc *           pLLCHdr; 
    struct llc             llcHdr; 
 
    A_UINT16               etherType; 
 
    pLinkHdrInfo->destAddrOffset        = 0; 
    pLinkHdrInfo->destSize              = 6; 
    pLinkHdrInfo->srcAddrOffset         = 6; 
    pLinkHdrInfo->srcSize               = 6; 
 
    /* Try for RFC 894 first as it's the most common. */ 
 
    /*  
     * make sure entire ether_header is in first M_BLK  
     * if not then copy the data to a temporary buffer  
     */ 
 
    if (pMblk->mBlkHdr.mLen < sizeof(LAN_FRAME_HEADER)) { 
        pEnetHdr = &enetHdr; 
        if (netMblkOffsetToBufCopy (pMblk, 0, (char *) pEnetHdr,  
                                    sizeof(LAN_FRAME_HEADER), (FUNCPTR) bcopy)  
            < sizeof(LAN_FRAME_HEADER)) 
        { 
            return (ERROR); 
        } 
    } else { 
        pEnetHdr = (LAN_FRAME_HEADER *)pMblk->mBlkHdr.mData; 
    } 
 
    etherType = be2cpu16(pEnetHdr->lanTypeOrLen); 
 
    /* Deal with 802.3 addressing. */ 
 
    /* Here is the algorithm. */ 
    /* If the etherType is less than the MTU then we know that */ 
    /* this is an 802.x address from RFC 1700. */ 
   /*if (etherType < sizeof(LAN_FRAME_HEADER)) */ 
	if(!IS_ETHERTYPE(etherType)){ 
 
        /* 
         * make sure entire ether_header + llc_hdr is in first M_BLK  
         * if not then copy the data to a temporary buffer  
         */ 
        if (pMblk->mBlkHdr.mLen < sizeof(LAN_FRAME_HEADER) + 
            LLC_SNAP_FRAMELEN) 
        { 
            pLLCHdr = &llcHdr; 
            if (netMblkOffsetToBufCopy (pMblk, sizeof(LAN_FRAME_HEADER),  
                                        (char *) pLLCHdr, etherType, 
                                        (FUNCPTR) bcopy) < etherType) 
            { 
                return (A_ERROR); 
            } 
        } else { 
            pLLCHdr = (struct llc *)((char *)pEnetHdr + 
                                     sizeof(LAN_FRAME_HEADER)); 
        } 
        /* Now it may be IP over 802.x so we check to see if the */ 
        /* destination SAP is IP, if so we snag the ethertype from the */ 
        /* proper place. */ 
         
        /* Now if it's NOT IP over 802.x then we just used the DSAP as */ 
        /* the etherType.  */ 
 
        if (pLLCHdr->llc_dsap == LLC_SNAP_LSAP) { 
            etherType = ntohs(pLLCHdr->llc_un.type_snap.ether_type); 
            pLinkHdrInfo->dataOffset = sizeof(LAN_FRAME_HEADER) + 8; 
        } else if (isL2UF((A_UINT8 *)pLLCHdr)) { 
            pLinkHdrInfo->dataOffset = 0; // No data in this frame 
        } else { /* no SNAP header */ 
            etherType = pLLCHdr->llc_dsap; 
            pLinkHdrInfo->dataOffset = sizeof(LAN_FRAME_HEADER) + 3; 
        } 
    } else { 
        pLinkHdrInfo->dataOffset        = sizeof(LAN_FRAME_HEADER); 
    } 
    pLinkHdrInfo->pktType               = etherType; 
 
    return (A_OK); 
} 
 
/************************************************************************** 
* hwAllocatePhysBuffer - allocate a physically contiguous buffer 
* 
* This routine will create a physically contiguous buffer and get 
* the virtual and physical address of it. 
* 
* RETURNS: A_OK if got buffer, A_ERROR otherwise 
*/ 
A_STATUS 
hwAllocatePhysBuffer(WLAN_DEV_INFO *pdevInfo, 
                     A_UINT16 buffSize,         /* size to create */ 
                     A_UINT32 *pPhysAddress,    /* return physical address */ 
                     void **ppVirtAddress,      /* return virtual address */ 
                     CL_BLK_ID *ppOSDescPtr)    /* return OS desc address */ 
{ 
    NET_POOL_ID pNetPoolId;             /* pointer to the net pool */ 
    CL_POOL_ID  pClPoolId;              /* pointer to the cluster pool */ 
    CL_BLK_ID   pClBlk;                 /* pointer to the CLBlk */ 
 
    /* Get the net Pool ID */ 
    pNetPoolId = pdevInfo->pNetPool; 
 
    /* Get the cluster pool ID */ 
    if ((pClPoolId = netClPoolIdGet(pNetPoolId, buffSize, FALSE)) == NULL) { 
        /* this shouldn't fail generally; the latter fails when the 
         * pool is empty 
         */ 
        uiPrintf("\nnetClPoolIdGet error!\n"); 
        return A_NO_MEMORY; 
    } 
 
    /* Get the cluster */ 
    if ((*ppVirtAddress = netClusterGet(pNetPoolId, pClPoolId)) == NULL) { 
        return A_NO_MEMORY; 
    } 
 
    *pPhysAddress = (A_UINT32)A_DATA_V2P(*ppVirtAddress); 
 
    /* For VxWorks, the os descriptor is the ClBlk */ 
    if ((pClBlk = netClBlkGet(pNetPoolId, M_DONTWAIT)) == NULL) { 
        uiPrintf("\nClBlk reserve error!\n"); 
        netClFree(pNetPoolId, *ppVirtAddress); 
        return A_NO_MEMORY; 
    } 
 
    /* join the cluster with the clBlk */ 
    if ((*ppOSDescPtr = netClBlkJoin(pClBlk, *ppVirtAddress, buffSize, NULL, 
                                     0, 0, 0)) == NULL) 
    { 
        uiPrintf("\nClBLK join error!\n"); 
        netClFree(pNetPoolId, *ppVirtAddress); 
        netClBlkFree(pNetPoolId, pClBlk); 
        return A_NO_MEMORY; 
    } 
 
    return A_OK; 
} 
 
/************************************************************************** 
* hwFreePhysBuffer - free the physically contiguous buffer 
* 
* This routine will free the physically contiguous buffer previously 
* allocated by hwAllocatePhysBuffer() 
* 
* RETURNS: N/A 
*/ 
void 
hwFreePhysBuffer(WLAN_DEV_INFO *pdevInfo, void *pVirtAddress, 
                 CL_BLK_ID pOSDescPtr) 
{ 
    NET_POOL_ID pNetPoolId; 
 
    /* get the net Pool ID */ 
    pNetPoolId = pOSDescPtr->pNetPool; 
 
    /* free the cluster */ 
    netClBlkFree(pNetPoolId, pOSDescPtr); 
 
    return; 
} 
 
 
/************************************************************************** 
* Ideally these are system utilities and do not belong in this 
* file - but we don't have another file! 
*/ 
 
LOCAL void 
vxDelayedExecISR(DELAYED_EXEC_RECORD *pRecord) 
{ 
    pRecord->func(pRecord->pdevInfo); 
    wdDelete(pRecord->wdog); 
    A_DRIVER_FREE(pRecord, sizeof(DELAYED_EXEC_RECORD)); 
} 
 
A_STATUS 
vxDelayedExec(WLAN_DEV_INFO* pdevInfo, A_UINT32 delay, 
              void (*func)(WLAN_DEV_INFO *)) 
{ 
    DELAYED_EXEC_RECORD *pRecord; 
 
    pRecord = (DELAYED_EXEC_RECORD *) A_DRIVER_MALLOC(sizeof(*pRecord)); 
    if (!pRecord) { 
        return A_ERROR; 
    } 
 
    pRecord->func     = func; 
    pRecord->pdevInfo = pdevInfo; 
    pRecord->wdog     = wdCreate(); 
    if (!pRecord->wdog) { 
        A_DRIVER_FREE(pRecord, sizeof(DELAYED_EXEC_RECORD)); 
        return A_ERROR; 
    } 
 
    delay = (delay * sysClkRateGet()) >> 10; 
    if (wdStart(pRecord->wdog, delay, 
                (FUNCPTR)vxDelayedExecISR, (int)pRecord) == ERROR) 
    { 
        wdDelete(pRecord->wdog); 
        A_DRIVER_FREE(pRecord, sizeof(DELAYED_EXEC_RECORD)); 
        return A_ERROR; 
    } 
 
    return OK; 
} 
 
/************************************************************************** 
* timer routines, os dependent 
* 
*/ 
 
LOCAL void 
drvVxTimerHandler(timer_t timerId, int arg) 
{ 
    A_TIMER *pTimer = (A_TIMER *)arg; 
     
    ASSERT(pTimer); 
    ASSERT(pTimer->pFunc); 
     
    pTimer->pFunc((void *)pTimer->param); 
} 
 
/* drvVxTimerInit - Initialize Atheros timer in vxWorks environment 
 * This function stores parameters in the Atheros timer object and 
 * and initializes vxWorks timer object part of Atheros timer using 
 * vxWorks call. 
 * Called by A_TIMER_INIT() macro. 
 */ 
A_STATUS 
drvVxTimerInit( 
    WLAN_DEV_INFO   *pDevInfo, 
    A_TIMER         *pTimer,    /* Pointer to timer object */ 
    void            *pFunc,     /* timer function */ 
    A_BOOL          repeat)     /* 0 if one shot timer, 1 if periodic */ 
{ 
 
    pTimer->pdevInfo   = pDevInfo; 
    pTimer->pFunc      = pFunc; 
    pTimer->repeat     = repeat; 
    pTimer->state      = FALSE; 
     
 
    /* Init a timer in vxWorks */ 
    if (timer_create(CLOCK_REALTIME, NULL, (timer_t *)&pTimer->osTimerObj) != 
        A_OK) 
    { 
        uiPrintf("Timer allocate fails!\n"); 
        return A_ERROR; 
    } 
 
    if (timer_connect((timer_t)pTimer->osTimerObj, (VOIDFUNCPTR)drvVxTimerHandler, 
                      (int)pTimer) != A_OK) 
    { 
        uiPrintf("Timer binding fails!\n"); 
        return A_ERROR; 
    } 
    return A_OK; 
} 
 
/* drvVxTimerSet - Fire off the timer that is already initialized. 
 * This function stores timer parameter in the fixed location in the 
 * timer object and sets the vx timer with given timeout period value. 
 * Called by A_TIMEOUT() macro. 
 */ 
A_STATUS 
drvVxTimerSet(A_TIMER *pTimer, A_UINT32 timeVal, 
              A_UINT32 param, A_UINT32 oper) 
{ 
    struct itimerspec timerValue; 
    A_UINT32          tv_sec = 0, tv_msec = timeVal; 
 
    if (timeVal > 1000) { 
        tv_sec = timeVal/1000; 
        tv_msec = timeVal - tv_sec * 1000; 
    } 
 
    pTimer->param     = param; 
    pTimer->operation = oper; 
    pTimer->timeVal   = timeVal; 
    pTimer->state     = FALSE; 
    timerValue.it_value.tv_sec      = tv_sec; 
    timerValue.it_value.tv_nsec     = tv_msec*1000*1000; 
    timerValue.it_interval.tv_sec   = 0; 
 
    if (pTimer->osTimerObj == NULL) { 
        return A_ERROR; 
    } 
 
    if (pTimer->repeat) { 
        timerValue.it_interval.tv_sec = tv_sec; 
        timerValue.it_interval.tv_nsec = tv_msec*1000*1000; 
        timer_settime((timer_t)pTimer->osTimerObj, 0, 
                      &timerValue, NULL); 
    } else { 
        timerValue.it_interval.tv_nsec = 0; 
        timer_settime((timer_t)pTimer->osTimerObj, 0, 
                      &timerValue, NULL); 
    } 
 
    return A_OK; 
} 
 
/* drvVxTimerCancel - This function cancels a timer that is already 
 * fired off. It doesn't know if the timer has expired, it cancels the timeout 
 * anyway. 
 * Called by A_UNTIMEOUT() macro. 
 */ 
A_BOOL 
drvVxTimerCancel(A_TIMER *pTimer) 
{ 
    A_BOOL cancelled = FALSE; 
 
    ASSERT(pTimer); 
 
    if (pTimer->osTimerObj && pTimer->state == FALSE) { 
        if (timer_cancel((timer_t)pTimer->osTimerObj) == A_OK) { 
            cancelled = TRUE; 
        } 
    } 
     
    pTimer->operation = INVALID_TIMEOUT; 
 
    return cancelled; 
} 
 
/* drvVxTimerDelete - This function deletes a timer. 
 * It doesn't know if the timer has expired, it cancels the timeout 
 * anyway. 
 * Called by A_DELETE_TIMER() macro. 
 */ 
A_STATUS 
drvVxTimerDelete(A_TIMER *pTimer) 
{ 
    A_BOOL cancelled = FALSE; 
 
    if (pTimer && pTimer->osTimerObj) { 
        if (pTimer->state == FALSE) { 
            if (timer_delete((timer_t)pTimer->osTimerObj) == A_OK) { 
                cancelled     = TRUE; 
                pTimer->state = TRUE; 
            } 
        } 
 
        /* delete the object now. */ 
        pTimer->osTimerObj = NULL; 
    } 
     
    return A_OK; 
}