Basic Management

This document describes how device drivers should manage shared chunks.

Creation

A shared chunk is created in kernel space. The user is given a handle to the shared chunk to access it, and can pass the handle to other process or other drivers. Chunks are represented by DChunk objects on the kernel side and by RChunk on the user side.

A shared chunk is created by using Kern::ChunkCreate(), which takes a TChunkCreateInfo argument that sets the chunk properties.

Chunk creation should be done in a critical section, created using NKern::ThreadEnterCS() and NKern::ThreadLeaveCS(). The size of the chunk should be in multiples of MMU pages, which can be calculated using Kern::RoundToSize(n), where n is the actual size that needs to be rounded.

/**
 Create a transmit shared chunk of specified size.
 
 @param    aChunkSize
         size of the chunk to be created
             
 @return    KErrNone on success, standard error code on failure
 */    
TInt DExUartPhysicalChannel::CreateTxChunk(TUint aChunkSize)
    {
    ...
    // Round up the transmit chunk size to the page size. 
    // Kern::RoundToPageSize() rounds up the argument to the size 
    // of a MMU page. The size of one MMU page can be found out by 
    // calling Kern::RoundToPageSize(1). 
 
    size=Kern::RoundToPageSize(aChunkSize);
        
    // Thread must be in critical section 
    NKern::ThreadEnterCS();
    ...
    // Create the chunk. Example is given in next code snippet
    ...
    // Commit the chunk. Example is given in following sections
    ...
    // Thread can leave the critical section 
    NKern::ThreadLeaveCS();
    }

TInt DExUartPhysicalChannel::CreateTxChunk(TUint aChunkSize)
    {
    ...
    NKern::ThreadLeaveCS(); // Enter the critical section
        
    // TChunkCreateInfo holds the parameters required to create a 
    // chunk and is used by Kern::ChunkCreate()
    TChunkCreateInfo info;
    
    // ESharedKernelMultiple specifies that a chunk which may be opened by 
    // any number of user side processes
    info.iType=TChunkCreateInfo::ESharedKernelMultiple;        
    info.iMaxSize= size;        // Chunk size
    
    // This specifies the caching attributes for the chunk. It can 
    // be no caching or fully caching (TMappingAttributes enum 
    // type). If the MMU does not support the requested attributes,  
    // then a lesser cached attribute will be used. The actual 
    // value used is returned in aMapAttr of Kern::ChunkCreate() 
    info.iMapAttr=EMapAttrFullyBlocking; // No Caching, suitable for DMA
    
    // Set to true if the chunk is to own its committed memory. 
    // In this case all memory committed to the chunk will come 
    // from the system's free pool and will be returned there when 
    // the chunk is destroyed. If the chunk will be committed to 
    // physical address, then EFalse can be set.
    info.iOwnsMemory=ETrue;            // using RAM pages
    
    // As chunk destruction is asynchronous we can have 
    // a DFC, if required, specifying a callback function to get 
    // the chunk destroy notification exactly. This is used if any 
    // follow up cleaning has to be done after chunk destruction.
    info.iDestroyedDfc=NULL;    // No chunk destroy DFC.
    
    DChunk* chunk;
    TUint32 mapAttr;
    // Creates a chunk that can be shared between a user thread and a 
    // kernel thread. This will be the initial step for a shared 
    // chunk. Once created, the chunk owns a region of linear 
    // address space of the requested size. This region is empty 
    // (uncommitted) so before it can be used either RAM or I/O 
    // devices must be mapped into it. This is achieved with the 
    // Commit functions.
    // Here iTxChunkKernAddr returns the linear address of the 
    // chunk created.
    //
    TInt r=Kern::ChunkCreate(info,chunk,iTxChunkKernAddr, mapAttr);
    if (r!=KErrNone)
        {
        // Thread can leave the critical section
        NKern::ThreadLeaveCS();
        return r;
        }
    ...
    }

Destruction

To allow a chunk to be properly cleaned up, a driver should close the chunk when it is no longer required. When a chunk is closed, the reference count is decremented by one. The chunk gets destroyed when the reference count becomes zero. Closing the chunk should be done within a critical section.

The destruction of the chunk happens asynchronously, and a notification of this can be requested. This is done using a DFC, by initialising TChunkCreateInfo::iDestroyDfc() with the DFC object.

/**
 Close the transmit chunk, that was already created. This is 
 called while closing the logical channel
 */
void DExUartPhysicalChannel::CloseTxChunk()
    {
    // Thread must be in critical section
    NKern::ThreadEnterCS();
    
    // Atomically get pointer to our chunk and NULL the iChunk 
    // member. Nkern::SafeSwap() atomically replaces the word 
    // referenced by aPtr with aNewValue, here NULL.
    //
 DChunk* chunk = (DChunk*)NKern::SafeSwap(NULL,(TAny*&)iTxChunk);
    
    if (chunk)
        {
        // Close the chunk that was created. This should be 
        // done in a critical section. This function decrements 
        // a chunk's access count, and, if the count reaches 
        // zero, the chunk is scheduled for destruction.
        // ChunkClose() has to be called the same number of times 
        // as chunk creation. A mismatch in this will result 
        // in either a memory leak or a panic.
        //
        Kern::ChunkClose(chunk);
        }
    // Thread can leave the critical section    
    NKern::ThreadLeaveCS();        
    }

Mapping

Shared chunks must be mapped to memory, which means that either RAM or an I/O device must be committed to a shared chunk before it can be used. This maps the chunk to a certain address. The memory can be physical contiguous RAM pages, an arbitrary set of RAM pages, a physical region, or a physical region with a list of physical addresses. The Kernel provides the following APIs for committing these types of memory:

// Commit RAM to a shared chunk. The memory pages to commit are 
// obtained from the system's free pool
TInt Kern::ChunkCommit(DChunk *aChunk, TInt aOffset, TInt aSize);

// Commit RAM to a shared chunk. The memory pages to commit are 
// obtained from the system's free pool and will have physically 
// contiguous addresses. Used when TChunkCreateInfo::iOwnsMemory 
// is ETrue
TInt Kern::ChunkCommitContiguous(DChunk *aChunk, TInt aOffset, 
                TInt aSize, TUint32 &aPhysicalAddress);

// Commit memory to a shared chunk. The physical region committed 
// is that which starts at the supplied physical address. 
// Typically, this region either represents memory mapped I/O, or 
// RAM that was set aside for special use at system boot time. 
// This is used when TChunkCreateInfo::iOwnsMemory is EFalse.
TInt Kern::ChunkCommitPhysical(DChunk *aChunk, TInt aOffset, 
                TInt aSize, TUint32 aPhysicalAddress);

// Commit memory to a shared chunk. The physical region committed
// is determined by the list of physical addresses supplied to 
// this function
TInt Kern::ChunkCommitPhysical(DChunk *aChunk, TInt aOffset, 
        TInt aSize, const TUint32 *aPhysicalAddressList);