📄 xp_osi_queue.c
字号:
/*----------------------------------------------------------------------------+| 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 + -