Reading and Writing

This document describes how device drivers should open, read from and write to shared chunks.

Both user-side code and drivers can read and write to shared chunks. Once the chunk is created, opened, and memory committed to it, data can be written and read to the shared chunk using the base address.

Opening

If a shared chunk has already been created by a driver, then another driver can access that shared chunk through its handle by using one of the following functions:

DChunk* Kern::OpenSharedChunk(DThread* aThread, TInt aChunkHandle,
                    TBool aWrite);

DChunk* Kern::OpenSharedChunk(DThread *aThread, 
                    const TAny *aAddress, TBool aWrite, 
                    TInt &aOffset);

User-side access

RChunk is the user side representation of a shared chunk. The user gets a shared chunk handle from a driver, and initialises the RChunk object with it using RChunk::SetHandle(TInt aHandle).

The user can now obtain the base address of the chunk by calling RChunk::Base(). Data can be read or written to this memory location, in same way as any other memory.

// User side chunk object.     
 RChunk chunkTx; 
     
// Get the handle to the chunk. A driver creates the chunk and
// returns the handle to the user to access it. The handle is  
// assigned to the user side chunk object using RChunk::SetHandle(). 
// (here done in the GetTxChunkHandle function). The handle has to be a positive 
// value. It can be obtained using RChunk::Handle(), if required.
//
r=ldd.GetTxChunkHandle(chunkTx);     
test(r==KErrNone);        
     
// Create a constant descriptor with test data
_LIT8(KTestSendData,"<< TEST DATA FOR TUTORIAL DRIVER EXAMPLE >>");
     
TUint8* chunkbase;
// Retrieve the base address of the chunk. Using this address, the user 
// can access the shared chunk just like any memory pointer. 
// RChunk::Base() returns the linear address of the shared chunk.
//
chunkbase=chunkTx.Base(); 
    
// Write the data to the shared chunk, using the chunk base address.
// Note here we do not need to send data to the driver. We just write to 
// the buffer and the driver directly accesses the chunk from the kernel side.
//
TPtr8 inbuf(chunkbase,KTestSendData().Size());
inbuf = KTestSendData;
…

// Call the LDD interface TransmitData(). There is no need to send the 
// data, instead we send only a TRequestStatus object (as it's an  
// asynchronous function), and buffer size. If required, an offset in the shared chunk 
// can be passed to the driver.
r = ldd.TransmitData(txStatus,inbuf.Size());
 test(r==KErrNone);

Kernel-side access

On the kernel side, a chunk is represented by a DChunk object. A chunk is created, mapped, or opened as shown in earlier sections. The linear and physical addresses of the chunk are returned when this is done. At a later stage in the driver, use Kern::ChunkAddress() and Kern::ChunkPhysicalAddress() to get the linear and physical addresses respectively. A chunk is read or written to using this address pointer and an offset.

// Note: Following lines of code are located in different 
// functions and different files. Here they are shown together for 
// easy comprehension.

// Linear address of the Rx Shared chunk
TLinAddr iRxChunkKernAddr;
...

// iRxChunkKernAddr returns the linear address of the Rx chunk
TInt r=Kern::ChunkCreate(info,chunk,iRxChunkKernAddr, mapAttr);
...

// iRxBufPhysAddr returns the physical address of the Rx chunk
r=Kern::ChunkCommitContiguous(chunk, bufoffset, size, iRxBufPhysAddr);
...

// Here we are directly using the linear address of the shared 
// chunk, and so can get rid of any buffer copies. If the DMA 
// port supports physical address, then the physical address can be
// used instead of a linear address.
// 
TInt retval = iRxDmaRequest->Fragment(
            TUint32)iUartComm->iPortAddr+KHoUART_RHR,
            TUint32)(iUartComm->iRxChunkKernAddr), aCount, 
            KDmaMemDest|KDmaIncDest, 0 /* no HW Flags*/);

Synchronization between user access and kernel access to a shared chunk is handled by the shared chunk API.