⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xp_osi_queue.c

📁 IBM source for pallas/vulcan/vesta
💻 C
📖 第 1 页 / 共 4 页
字号:
/*----------------------------------------------------------------------------+|       This source code has been made available to you by IBM on an AS-IS|       basis.  Anyone receiving this source is licensed under IBM|       copyrights to use it in any way he or she deems fit, including|       copying it, modifying it, compiling it, and redistributing it either|       with or without modifications.  No license under IBM patents or|       patent applications is to be implied by the copyright license.||       Any user of this software should understand that IBM cannot provide|       technical support for this software and will not be responsible for|       any consequences resulting from the use of this software.||       Any person who transfers this source code or any derivative work|       must include the IBM copyright notice, this paragraph, and the|       preceding two paragraphs in the transferred software.||       COPYRIGHT   I B M   CORPORATION 1998|       LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M+----------------------------------------------------------------------------*//*----------------------------------------------------------------------------+||   Author    :  Ian Govett|   Component :  xp|   File      :  xp_osi_queue.c|   Purpose   :  Queue Management|   Changes   :|   Date       By   Comments|   ---------  ---  -----------------------------------------------------|   15-Jan-98       Created|   30-Sep-01   LGH Ported to linux+----------------------------------------------------------------------------*//*----------------------------------------------------------------------------+|                        DRAM Queue Management+-----------------------------------------------------------------------------+||   The following functions provide queue management services for the|   transport driver.  The xp0_queue_allocate() function allocates a region|   of space (queue_size) for a specific channel, and xp0_queue_free()|   disables the queue, and releases the space.  Since the transport has a|   fixed amount of memory available, the space is partitioned|   among all the channels in use.  The driver maintains a free list for|   the management of this limited resource.|       The state of the queue is enabled, disabled, or reset using the|   xp0_queue_control() function.  The queue must be enabled before any data|   can be written to the queue.       When the queue is allocated, the|   queue state is disabled.  A queue reset is used to reset the write|   pointer in hardware to the bottom of the queue.  The queue disable|   operation writes the "stops" register to notify the hardware.|       The hardware notifies the driver when data is available using the|   DATA_AVAILABLE interrupt.                  The application registers|   a function to call when data is available for a specific channel.  The|   interrupt routine forwards the DATA_AVAILABLE interrupt to the demux|   thread to process the request.  The demux thread then calls the user|   callback function when data is available.  The callback function|   provides a structure containing the channel_id, filter_match word|   starting address of the data in the queue, and the length (in bytes)|   of the data.  The memory region described with the notification|   is "locked" until the application "releases" the memory region using|   the xp0_queue_unlock_data().|       A read pointer is used by the hardware to lock memory regions from|   being overwritten.  Basically, there is a circular queue which contains|   addresses for the top and bottom of the queue, a read pointer, and a|   write pointer (ie. writeStart in the hardware spec.)  The|   addresses used by the hardware will not write beyond the address|   specified by the read_addr.  The write_start pointer contains the|   starting location for the data region delivered with the next interrupt.|       Using the queue_bottom, queue_top, read pointer, and write pointer,|   the driver manages the movement of the read pointer.  As records|   are "unlocked" by the application, the driver advances the read pointer|   (read_addr).|       This driver does not require sections to be unlocked in the order|   they are delivered, however, the region being unlocked must be the same|   size as the region delivered to the application.  The region is locked|   when is is delivered to the application, and unlocked (and removed)|   when the application unlocks the region.|       An application may "unregister" a notification function but never|   stop the data (channel or queue disable).  In this case, the hardware|   continues to save the data until either (1) the application registers|   a notification function, or (2) a read pointer error occurs.||   The queue management functions support memory management within each|   queue.  Since table sections may be unlocked in any order, memory|   fragmentation may occur.  The read pointer interrupt is delivered to|   the driver when the write pointer value would match the read pointer|   value.  If a notification function is registered, the queue management|   routines relocate the read and write pointers and restarts the channel.|   The read and write pointers are relocated as follows.  The driver finds|   the largest available region within the queue for the channel.  If the *|   region is large enough (>256) the read and write pointers are updated,|   and the channel is restarted.|+----------------------------------------------------------------------------*/#if 0/* The necessary header files */#include <linux/config.h>#include <linux/version.h>#ifdef MODVERSIONS#include <linux/modversions.h>#endif#define  __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#endif#include "xp_osi_global.h"#include "xp_atom_reg.h"/*----------------------------------------------------------------------------+| Local Defines+----------------------------------------------------------------------------*/#define QUEUE_BLOCK_SIZE            4096#define QUEUE_REGION                0xff000000#define XPORT_BANK_REGISTER     0x00000000#define REGION_PLB_BYTE_OFFSET  ((unsigned long) XPORT_BANK_REGISTER)#define REGION_PLB              (XPORT_BANK_REGISTER >> 2)/*----------------------------------------------------------------------------+| Define macros to generate the addresses to access DRAM| These macros convert between a word index, and a byte address+----------------------------------------------------------------------------*/#define MAKE_PLB_ADDR(addr)     ((unsigned char *) (addr | pGlobal->QueueInfo.ulXpQueueBAddr))#define MAKE_PLB_INDEX(addr)    (((unsigned long) addr) & (~pGlobal->QueueInfo.ulXpQueueBAddr))//Internalstatic SHORT update_read_pointer(GLOBAL_RESOURCES *pGlobal, SHORT wChannelId,                                 UCHAR *ppAddr);static void process_read_pointer(GLOBAL_RESOURCES *pGlobal, SHORT wChannelId);static SHORT unlock_data(GLOBAL_RESOURCES *pGlobal,SHORT wChannelId,                         UCHAR *ppBAddr,UCHAR *ppEAddr);static short queue_valid(GLOBAL_RESOURCES *pGlobal, SHORT wChannelId);/*----------------------------------------------------------------------------+| XXXX   XX   XX   XXXXXX  XXXXXXX  XXXXXX   XX   XX     XX    XXXX|  XX    XXX  XX   X XX X   XX   X   XX  XX  XXX  XX    XXXX    XX|  XX    XXXX XX     XX     XX X     XX  XX  XXXX XX   XX  XX   XX|  XX    XX XXXX     XX     XXXX     XXXXX   XX XXXX   XX  XX   XX|  XX    XX  XXX     XX     XX X     XX XX   XX  XXX   XXXXXX   XX|  XX    XX   XX     XX     XX   X   XX  XX  XX   XX   XX  XX   XX  XX| XXXX   XX   XX    XXXX   XXXXXXX  XXX  XX  XX   XX   XX  XX  XXXXXXX+----------------------------------------------------------------------------*//*----------------------------------------------------------------------------+|  update_read_pointer+----------------------------------------------------------------------------*/static SHORT update_read_pointer(GLOBAL_RESOURCES *pGlobal, SHORT wChannelId,                                 UCHAR *ppAddr){    SHORT wRc;    ULONG ulIndex;    QUEUE_PTR pChannel;    UINT32  flag;    /*------------------------------------------------------------------------+    |  Convert address to an index aligned on a 256 byte boundary    |  The hardware requires the read pointer to not wrap onto the first    |  word of the queue    +------------------------------------------------------------------------*/    pChannel = &pGlobal->QueueInfo.XpQueueChData[wChannelId];    ulIndex = MAKE_PLB_INDEX(ppAddr) & 0xffffff00;    //phisical address    if(ulIndex == pChannel->ulQueueAddr)    {        ulIndex += pChannel->ulQueueSize;    }    /*------------------------------------------------------------------------+    |  Now update the read pointer.  if there was a read pointer error which    |  occurred then reset, and restart the queue    +------------------------------------------------------------------------*/    flag = os_enter_critical_section();    xp_atom_dcr_write_register_channel(pGlobal->uDeviceIndex,XP_QCONFIGB_REG_RPTR, wChannelId, ulIndex);    os_leave_critical_section(flag);    if(pChannel->wRpiStatus)    {        pChannel->wRpiStatus = 0;        wRc = xp_osi_channel_restart(pGlobal,wChannelId);        PDEBUG("xp_osi_channel_restart return\n");        if(wRc)        {            return(wRc);        }    }    return(0);}/*----------------------------------------------------------------------------+|  process_read_pointer+----------------------------------------------------------------------------*/static void process_read_pointer(GLOBAL_RESOURCES *pGlobal, SHORT wChannelId){    short i;    ULONG ulData;    UCHAR *ppBAddr;    UCHAR *ppEAddr;    ULONG ulFreeSize;         /* region of locked data                */    ULONG ulMaxSize;          /* largest region of unlocked space     */    UCHAR *ppBFree;           /* start address of the free space      */    UCHAR *ppEFree;           /* ending address of the free space     */    ULONG ulBIndex;           /* word index of starting address       */    ULONG ulEIndex;           /* word index of ending address         */    ULONG ulBQueue;           /* starting index of the queue          */    ULONG ulEQueue;           /* ending index of the queue            */    QUEUE_PTR pChannel;    UINT32  flag;    pChannel = &pGlobal->QueueInfo.XpQueueChData[wChannelId];    /*------------------------------------------------------------------------+    |  Make sure there is at least one lock record    |  Move hardware read pointer to the address last processed.    +------------------------------------------------------------------------*/    if(pChannel->wLockInuse <= 0)    {        update_read_pointer(pGlobal,wChannelId, pChannel->ppProcessAddr);        pChannel->Errors.uwProcessRpi++;        return;    }    /*------------------------------------------------------------------------+    |  Find the largest block of space, so we can move the hardware write    |  pointer, and restart the queue. To find the largest block, we have to    |  look at the collection of locked records get the starting address of    |  the first lock record and the ending address of the last lock record    |  to calculate any free space across the queue wrap.    +------------------------------------------------------------------------*/    else    {        ulBQueue = pChannel->ulQueueAddr;        ulEQueue = ulBQueue + pChannel->ulQueueSize;        ppBAddr = pChannel->pLockData[0].ppBAddr;        ppEAddr = pChannel->pLockData[pChannel->wLockInuse-1].ppEAddr;        /*--------------------------------------------------------------------+        |  Calculate the total region locked.  Factor in the possible        |  queue wrap.        +--------------------------------------------------------------------*/        if(ppBAddr <= ppEAddr)        {            ulFreeSize = pChannel->ulQueueSize - ((unsigned long) (ppEAddr - ppBAddr));        }        else        {            ulFreeSize = ((unsigned long) (ppBAddr - ppEAddr));        }        ulMaxSize = ulFreeSize;        ppBFree = ppEAddr;        ppEFree = ppBAddr;        /*--------------------------------------------------------------------+        |  Now scan the skip records to see if any are larger regions        +--------------------------------------------------------------------*/        for(i=0; i<pChannel->wLockInuse-1; i++)        {            ulFreeSize = pChannel->pLockData[i+1].ppBAddr -                        pChannel->pLockData[i].ppEAddr;            if(ulFreeSize > ulMaxSize)            {                ulMaxSize = ulFreeSize;                ppBFree = pChannel->pLockData[i].ppEAddr;                ppEFree = pChannel->pLockData[i+1].ppBAddr;            }        }        ulBIndex = MAKE_PLB_INDEX(ppBFree);        ulEIndex = MAKE_PLB_INDEX(ppEFree);        /*--------------------------------------------------------------------+        |  Make sure the address range is inside the queue bounds        +--------------------------------------------------------------------*/        if((ulBIndex < ulBQueue) || (ulBIndex > ulEQueue))        {            pChannel->Errors.uwProcessRpi++;            return;        }        if((ulEIndex < ulBQueue) || (ulEIndex > ulEQueue))        {            pChannel->Errors.uwProcessRpi++;            return;        }        /*--------------------------------------------------------------------+        |  The read pointer is on a 256 byte boundary, so if the available        |  size is less, then leave the interrupt on, otherwise use the        |  available space        +--------------------------------------------------------------------*/        if(ulMaxSize >= 256)        {            /*----------------------------------------------------------------+            |  Convert to a an index, and adjust the start address to align            |  on a word boundary            +----------------------------------------------------------------*/            i = ulBIndex % 4;            if(i)            {                ulBIndex += (4 - i);            }            if(ulBIndex == ulEQueue)            {                ulBIndex = ulBQueue;            }            /*----------------------------------------------------------------+            |  Write the "stops" register to stop any "in-process" packets            |  from being delivered, and stop the read/write cycle in hw.            +----------------------------------------------------------------*/            ulData = (1 << (31 - wChannelId));            flag = os_enter_critical_section();            xp_atom_dcr_write(pGlobal->uDeviceIndex,XP_DCR_ADDR_QSTOPS, ulData);            /*----------------------------------------------------------------+            |  Update hardware registers for writeStart & writeNow            +----------------------------------------------------------------*/            xp_atom_dcr_write_register_channel(pGlobal->uDeviceIndex,XP_QSTATD_REG_WSTART,                                           wChannelId, ulBIndex);            xp_atom_dcr_write_register_channel(pGlobal->uDeviceIndex,XP_QSTATB_REG_WPTR,                                           wChannelId, ulBIndex);            os_leave_critical_section(flag);            /*----------------------------------------------------------------+            |  Re-write the read pointer, and restart the queue            +----------------------------------------------------------------*/            update_read_pointer(pGlobal,wChannelId, ppEFree);/*          update_read_pointer(wChannel_Id, channel->process_addr); */            /*----------------------------------------------------------------+            |  Change the read pointer in the driver, and clear the read            |  pointer indicator            +----------------------------------------------------------------*/            pChannel->ppProcessAddr = MAKE_PLB_ADDR(ulBIndex);            pChannel->ppReadAddr    = MAKE_PLB_ADDR(ulBIndex);            pChannel->ppLockAddr    = ppEFree;        }    }}/*----------------------------------------------------------------------------+|  unlock_data+----------------------------------------------------------------------------*/static SHORT unlock_data(GLOBAL_RESOURCES *pGlobal,SHORT wChannelId,                         UCHAR *ppBAddr,UCHAR *ppEAddr){    short i;    short j;    SHORT wIndex;    QUEUE_PTR pChannel;    pChannel = &pGlobal->QueueInfo.XpQueueChData[wChannelId];    if((pChannel->wLockInuse == 0) || (pChannel->pLockData == NULL))    {        return(XP_ERROR_QUEUE_ADDRESS);    }    /*------------------------------------------------------------------------+    |  Find the index which contains both the start and ending addresses    +------------------------------------------------------------------------*/    for(wIndex=0; wIndex < pChannel->wLockInuse; wIndex++)    {        if((ppBAddr == pChannel->pLockData[wIndex].ppBAddr) ||           (ppEAddr == pChannel->pLockData[wIndex].ppEAddr))        {            break;        }    }    /*------------------------------------------------------------------------+    |  Check if we couldn't find the block to unlock remove the lock entry    |  if we've an exact match    +------------------------------------------------------------------------*/    if(wIndex == pChannel->wLockInuse)    {        pChannel->Errors.uwRegionNotFound++;        return(XP_ERROR_QUEUE_ADDRESS);    }    if((ppBAddr == pChannel->pLockData[wIndex].ppBAddr) && (ppEAddr == pChannel->pLockData[wIndex].ppEAddr))    {        for(i=wIndex, j=i+1; j<pChannel->wLockInuse; i++, j++)        {            pChannel->pLockData[i] = pChannel->pLockData[j];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -