--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/graphicshwdrivers/surfacemgr/src/extension.cpp Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,1699 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+
+#include <kernel/kern_priv.h>
+#include <graphics/surface.h>
+#include <graphics/surfacetypes.h>
+#include <graphics/surfacemanager.h>
+#include "surfacemanager_dev.h"
+#include <kernel/cache.h>
+
+/**
+Convert the surface Id to an index of the array
+based on the least significant 4 bits of the first word of the ID
+@param aSurfaceId Const reference to the surface Id
+@internalTechnology
+*/
+static TInt SurfaceIdToIndex(const TSurfaceId& aSurfaceId)
+ {
+ return static_cast<TInt>(aSurfaceId.iInternal[0]&(KMaxLists-1));
+ }
+
+/**
+Removes an item from a linked list
+@param aList Pointer to the head of a linked list of type T
+@param aOwner Pointer to the object to be removed
+@internalTechnology
+*/
+template<class T>
+static void UnlinkListItem(T** aList, const T* aItem)
+ {
+ TRACE(Kern::Printf("SM UnlinkListItem list %08x object %08x \n", aList, aItem);)
+
+ __ASSERT_DEBUG(aItem != NULL, Kern::Fault("Surface Manager", __LINE__));
+ __ASSERT_DEBUG(*aList != NULL, Kern::Fault("Surface Manager", __LINE__));
+
+ if (*aList == aItem) //one we want is at the head of the list
+ {
+ *aList = aItem->iNext;
+ return;
+ }
+
+ T* p = *aList;
+ T* q = (*aList)->iNext;
+ while (q)
+ {
+ if (q == aItem)
+ {
+ p->iNext = q->iNext;
+ return;
+ }
+ p = q;
+ q = q->iNext;
+ }
+ }
+
+
+
+
+/**
+Returns a pointer to the surface owner object for the specified process, for this surface.
+@param aProcess Pointer to the process object
+@return pointer to the surface owner object if found, else NULL
+@internalTechnology
+*/
+
+TProcessListItem* TSurface::ProcessOwnerInfo(const DProcess* aProcess)
+ {
+ TProcessListItem* so = iOwners;
+ while(so)
+ {
+ if (aProcess == so->iOwningProcess)
+ {
+ break;
+ }
+ so = so->iNext;
+ }
+ return so;
+ }
+
+/**
+Creates a shared chunk surface.
+@param aParams Package buffer containing the surface creation parameters.
+@param aId Will be set to the surface id of the newly created surface.
+@return KErrNone if successful, KErrArgument if the creation attributes were incorrect,
+otherwise one of the other system wide error codes.
+@see RSurfaceManager::TSurfaceCreationAttributes
+@internalTechnology
+*/
+
+TInt DSurfaceManager::CreateSurface(const TDesC8* aParams, TSurfaceId* aId)
+ {
+
+ RSurfaceManager::TSurfaceCreationAttributesBuf buf;
+ RSurfaceManager::TSurfaceCreationAttributes& attribs = buf();
+
+ Kern::KUDesGet(buf, *aParams); //fetch input parameters
+ if( (attribs.iHintCount > KMaxHintsPerSurface) || (attribs.iHintCount<0) )
+ {
+ return KErrArgument;
+ }
+
+ RSurfaceManager::THintPair tempSurfaceHints[KMaxHintsPerSurface];
+ if( (attribs.iHintCount>0) && attribs.iSurfaceHints)
+ {
+ kumemget(tempSurfaceHints, attribs.iSurfaceHints, attribs.iHintCount*sizeof(RSurfaceManager::THintPair));
+ attribs.iSurfaceHints = tempSurfaceHints;
+ }
+ else
+ {
+ attribs.iSurfaceHints=NULL;
+ }
+
+ //validate input parameters and calculate chunk size
+ TInt roundedBufferSize = attribs.iOffsetBetweenBuffers;
+ TUint dummyActualSize = 0;
+
+ TInt chunkSize = ValidateAndCalculateChunkSize(attribs, roundedBufferSize, dummyActualSize, ETrue);
+ if (chunkSize == 0)
+ {
+ return KErrArgument;
+ }
+
+ TSurfaceId sid;
+ TInt r = KErrNone;
+ TSurface* surface = NULL;
+ do //in the unlikely event that we generate a duplicate surface id, try again.
+ {
+ GenerateSurfaceId(sid);
+
+ NKern::FMWait(&iMutex);
+ surface = FindSurfaceById(sid);
+ NKern::FMSignal(&iMutex);
+ }
+ while (surface);
+
+ //create a shared chunk for the surface memory
+ TChunkCreateInfo info;
+ info.iType = TChunkCreateInfo::ESharedKernelMultiple; //multi process mappable
+ info.iMaxSize = chunkSize;
+ info.iOwnsMemory = ETrue;
+
+//iMapAttr is valid only for hardware devices and will not make any effect in wins
+#ifndef __WINS__
+ info.iMapAttr = (attribs.iCacheAttrib == RSurfaceManager::ECached) ? EMapAttrCachedMax : EMapAttrL1Uncached;
+#else
+ info.iMapAttr = 0;
+#endif
+
+ TLinAddr kernAddr;
+ TUint32 mapAttr;
+
+ NKern::ThreadEnterCS();
+ DChunk* chunk;
+ r = Kern::ChunkCreate(info, chunk, kernAddr, mapAttr);
+ if (KErrNone != r)
+ {
+ NKern::ThreadLeaveCS();
+ return r;
+ }
+
+ //commit the memory
+ TUint32 paddr;
+ if (attribs.iContiguous)
+ {
+ r = Kern::ChunkCommitContiguous(chunk, 0, chunkSize, paddr);
+ }
+ else
+ {
+ r = Kern::ChunkCommit(chunk, 0, chunkSize);
+ }
+
+ if (KErrNone != r)
+ {
+ //problem committing the memory,
+ //destroy the chunk and cleanup
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ return r;
+ }
+
+ //create a surface structure for the new surface
+ surface = new TSurface;
+ TRACE(Kern::Printf("SM A %08x TSurface CreateSurface",surface);)
+ if (!surface)
+ {
+ //destroy the chunk and cleanup, out of memory
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ surface->iId = sid;
+ surface->iSize = attribs.iSize;
+ surface->iBuffers = attribs.iBuffers;
+ surface->iPixelFormat = attribs.iPixelFormat;
+ surface->iStride = attribs.iStride;
+ surface->iOffsetToFirstBuffer = attribs.iOffsetToFirstBuffer;
+ surface->iAlignment = attribs.iAlignment;
+ surface->iContiguous = attribs.iContiguous;
+ surface->iChunk = chunk;
+ surface->iOffsetBetweenBuffers = roundedBufferSize;
+ surface->iCacheAttrib = attribs.iCacheAttrib;
+ surface->iMappable = attribs.iMappable;
+ if(attribs.iHintCount>0)
+ {
+ memcpy(surface->iSurfaceHints,tempSurfaceHints,attribs.iHintCount*sizeof(RSurfaceManager::THintPair));
+ }
+ memclr(surface->iSurfaceHints+attribs.iHintCount, (KMaxHintsPerSurface-attribs.iHintCount)*sizeof(RSurfaceManager::THintPair));
+
+ //create a surface owner for this surface
+ TProcessListItem* owner = new TProcessListItem;
+ TRACE(Kern::Printf("SM A %08x TProcessListItem CreateSurface",owner);)
+
+ if (!owner)
+ {
+ //destroy the chunk and cleanup, out of memory
+ Kern::ChunkClose(chunk);
+ delete(surface);
+ TRACE(Kern::Printf("SM D %08x TSurface CreateSurface",surface);)
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ owner->iCount = 1; //mark it as open in this process
+ owner->iOwningProcess = &Kern::CurrentProcess();
+ owner->iNext = NULL;
+
+ surface->iOwners = owner; //only 1 owner at creation time
+
+ //at this point we have a fully constructed TSurface
+
+ //add surface to head of surfaces list
+ NKern::FMWait(&iMutex);
+ //Mask off the bottom log2(KMaxLists) bits of the first word of the surfaceID as an index
+ //add the new surface to the beginning of the list
+ TInt index = SurfaceIdToIndex(sid);
+ surface->iNext = iSurfacesIndex[index];
+ iSurfacesIndex[index] = surface;
+
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+
+ //write surface id back to user side
+ kumemput(aId, &sid, sizeof (TSurfaceId));
+ return KErrNone;
+ }
+
+
+/**
+Validate that a chunk contains physical memory for the used areas.
+
+This function should be called in Critical Section in order to be completed even if the thread
+or process is killed and so be able to free the memory allocated (TUint32[pageList])
+
+@param aChunk Chunk that the user supplied.
+@param aAttribs Surface Creation Attributes.
+@param aBuffersize Calculated size of each buffer.
+@param aMapAttr Filled in with the mapping attributes of the memory.
+@param aIsContiguous Lets the caller know if the surface is physically contiguous or not.
+@return KErrNone if successful, KErrArgument if the creation attributes were incorrect,
+KErrBadHandle if aChunkHandle is of an invalid shared chunk memory,
+otherwise one of the other system wide error codes.
+@see RSurfaceManager::TSurfaceCreationAttributes
+@internalTechnology
+*/
+TInt DSurfaceManager::ValidatePhysicalMemory(DChunk* aChunk, const RSurfaceManager::TSurfaceCreationAttributes& aAttribs,
+ TUint aBuffersize, TUint32& aMapAttr, TBool &aIsContiguous)
+ {
+ TLinAddr kernAddr;
+ TUint32 physAddr;
+
+ //Get the physical address for a region in a shared chunk
+ TInt pageSize = Kern::RoundToPageSize(1);
+ TInt pageList = 1 + (aChunk->iSize + pageSize - 2) / pageSize;
+ TUint32* physAddr2 = new TUint32[pageList];
+ if(!physAddr2)
+ {
+ return KErrNoMemory;
+ }
+
+ // Unless proven otherwise, the memory is not contiguous.
+ aIsContiguous = EFalse;
+ TInt r = Kern::ChunkPhysicalAddress(aChunk, 0, aChunk->iSize, kernAddr, aMapAttr, physAddr, physAddr2);
+ if (KErrNone == r)
+ {
+ aIsContiguous = ETrue;
+ }
+
+
+ TRACE(Kern::Printf("SM CreateSurface ChunkPhysicalAddress r %d chunk %08x chunk size %d", r, aChunk, aChunk->iSize);)
+ TRACE(Kern::Printf("SM CreateSurface kernAddr %08x", kernAddr);)
+ TRACE(Kern::Printf("SM CreateSurface mapAttr %08x", aMapAttr);)
+ TRACE(Kern::Printf("SM CreateSurface physAddr %08x", physAddr);)
+ TRACE(Kern::Printf("SM CreateSurface physAddr2 %08x", physAddr2);)
+
+ if(r < KErrNone)
+ {
+ // Error means that there isn't memory in the whole chunk - so check the
+ // relevant areas - it is allowed to have gaps between the buffers, but not
+ // within the actual regions that are used for buffers.
+
+ // So, we first check the area before first buffer up to "offsettofirstbuffer", which all should be valid
+ if (aAttribs.iOffsetToFirstBuffer != 0)
+ {
+ r = Kern::ChunkPhysicalAddress(aChunk, 0, aAttribs.iOffsetToFirstBuffer,
+ kernAddr, aMapAttr, physAddr, physAddr2);
+ }
+ else
+ {
+ r = KErrNone;
+ }
+
+ // If that's a pass, loop through and check the actual buffers (leave loop if it fails).
+ for(TInt i = 0; i < aAttribs.iBuffers && KErrNone <= r; i++)
+ {
+ r = Kern::ChunkPhysicalAddress(aChunk,
+ aAttribs.iOffsetToFirstBuffer + aAttribs.iOffsetBetweenBuffers * i,
+ aBuffersize, kernAddr, aMapAttr, physAddr, physAddr2);
+ }
+ }
+
+ // Fix up weird ChunkPhysicalAddress behaviour - it returns 1 to indicate that memory is non-contiguous.
+ if (1 == r)
+ {
+ r = KErrNone;
+ }
+
+ delete[] physAddr2;
+ return r;
+ }
+
+
+/**
+Creates a surface in an existing shared chunk.
+@param aParam Package buf containing the surface creation parameters and id to be set to the surface id of the newly created surface.
+@param aChunkHandle Existing valid shared chunk handle.
+@return KErrNone if successful, KErrArgument if the creation attributes were incorrect,
+KErrBadHandle if aChunkHandle is of an invalid shared chunk memory,
+otherwise one of the other system wide error codes.
+@see RSurfaceManager::TSurfaceCreationAttributes
+@internalTechnology
+*/
+TInt DSurfaceManager::CreateSurface(RSurfaceManagerDriver::TDeviceParam* aParam, TInt aChunkHandle)
+ {
+ RSurfaceManager::TSurfaceCreationAttributesBuf buf;
+ RSurfaceManager::TSurfaceCreationAttributes& attribs = buf();
+
+ //Get the input parameters
+ RSurfaceManagerDriver::TDeviceParam param;
+ kumemget(¶m, aParam, sizeof(RSurfaceManagerDriver::TDeviceParam));
+ Kern::KUDesGet(buf, *(reinterpret_cast<const TDesC8*>(param.iBuffer)));
+ if( (attribs.iHintCount > KMaxHintsPerSurface) || (attribs.iHintCount<0) )
+ {
+ return KErrArgument;
+ }
+
+ RSurfaceManager::THintPair tempSurfaceHints[KMaxHintsPerSurface];
+ if( (attribs.iHintCount>0) && attribs.iSurfaceHints)
+ {
+ kumemget(tempSurfaceHints, attribs.iSurfaceHints, attribs.iHintCount*sizeof(RSurfaceManager::THintPair));
+ attribs.iSurfaceHints = tempSurfaceHints;
+ }
+ else
+ {
+ attribs.iSurfaceHints=NULL;
+ }
+
+ //validate input parameters and calc size
+ TInt roundedBufferSize = attribs.iOffsetBetweenBuffers;
+ TUint actualBufferSize = 0;
+ TInt chunkSize = ValidateAndCalculateChunkSize(attribs, roundedBufferSize, actualBufferSize);
+ if (chunkSize == 0)
+ {
+ return KErrArgument;
+ }
+
+ NKern::ThreadEnterCS();
+
+ //Open an existing shared chunk
+ DChunk* chunk = Kern::OpenSharedChunk(NULL, aChunkHandle, EFalse);
+ if(chunk == NULL)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrBadHandle;
+ }
+
+ //Check for chunk type as kernel multiple
+ if(chunk->iChunkType != ESharedKernelMultiple)
+ {
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ return KErrBadHandle;
+ }
+
+ //Check for enough chunk size to create surface for requested attributes
+ if (chunk->iSize < attribs.iOffsetToFirstBuffer + attribs.iBuffers * actualBufferSize)
+ {
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ TSurfaceId sid;
+ TSurface* surface = NULL;
+ do //in the unlikely event that we generate a duplicate surface id, try again.
+ {
+ GenerateSurfaceId(sid);
+
+ NKern::FMWait(&iMutex);
+ surface = FindSurfaceById(sid);
+ NKern::FMSignal(&iMutex);
+ }
+ while (surface);
+
+ //create a surface structure for the new surface
+ surface = new TSurface;
+ TRACE(Kern::Printf("SM A %08x TSurface CreateSurface",surface);)
+ if (!surface)
+ {
+ //destroy the chunk and cleanup, out of memory
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ TUint32 mapAttr = 0;
+ TBool isContiguous;
+ TInt r = ValidatePhysicalMemory(chunk, attribs, actualBufferSize, mapAttr, isContiguous);
+ if (r != KErrNone)
+ {
+ //destroy the surface and close the chunk
+ delete(surface);
+ Kern::ChunkClose(chunk);
+ NKern::ThreadLeaveCS();
+ if (r != KErrNoMemory)
+ {
+ r = KErrArgument;
+ }
+ return r;
+ }
+
+ surface->iId = sid;
+ surface->iSize = attribs.iSize;
+ surface->iBuffers = attribs.iBuffers;
+ surface->iPixelFormat = attribs.iPixelFormat;
+ surface->iStride = attribs.iStride;
+ surface->iOffsetToFirstBuffer = attribs.iOffsetToFirstBuffer;
+ surface->iAlignment = attribs.iAlignment;
+ surface->iContiguous = isContiguous;
+ surface->iChunk = chunk;
+ surface->iOffsetBetweenBuffers = (attribs.iOffsetBetweenBuffers) ? attribs.iOffsetBetweenBuffers : roundedBufferSize;
+ surface->iMappable = attribs.iMappable;
+#ifndef __WINS__ //Creation attribute field will not considered for iCacheAttrib
+ TUint32 level1Info = mapAttr & EMapAttrL1CacheMask;
+ TUint32 level2Info = mapAttr & EMapAttrL2CacheMask;
+ TBool chunkIsNotcached = ((level2Info == EMapAttrL2Uncached) &&
+ ((level1Info == EMapAttrFullyBlocking) || (level1Info == EMapAttrBufferedNC) ||
+ (level1Info == EMapAttrBufferedC) || (level1Info == EMapAttrL1Uncached)));
+ surface->iCacheAttrib = (chunkIsNotcached) ? RSurfaceManager::ENotCached : RSurfaceManager::ECached;
+#else
+ surface->iCacheAttrib = RSurfaceManager::ENotCached;
+#endif
+
+ if(attribs.iHintCount>0)
+ {
+ memcpy(surface->iSurfaceHints,tempSurfaceHints,attribs.iHintCount*sizeof(RSurfaceManager::THintPair));
+ }
+ memclr(surface->iSurfaceHints+attribs.iHintCount, (KMaxHintsPerSurface-attribs.iHintCount)*sizeof(RSurfaceManager::THintPair));
+
+ //create a surface owner for this surface
+ TProcessListItem* owner = new TProcessListItem;
+ TRACE(Kern::Printf("SM A %08x TProcessListItem CreateSurface",owner);)
+
+ if (!owner)
+ {
+ //destroy the chunk and cleanup, out of memory
+ Kern::ChunkClose(chunk);
+ delete(surface);
+ TRACE(Kern::Printf("SM D %08x TSurface CreateSurface",surface);)
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ owner->iCount = 1; //mark it as open in this process
+ owner->iOwningProcess = &Kern::CurrentProcess();
+ owner->iNext = NULL;
+
+ surface->iOwners = owner; //only 1 owner at creation time
+
+ NKern::FMWait(&iMutex);
+ //at this point we have a fully constructed TSurface
+ //add surface to head of surfaces list
+
+ //Mask off the bottom log2(KMaxLists) bits of the first word of the surfaceID as an index
+ //add the new surface to the beginning of the list
+ TInt index = SurfaceIdToIndex(sid);
+ surface->iNext = iSurfacesIndex[index];
+ iSurfacesIndex[index] = surface;
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+
+ //write surface id back to user side
+ kumemput(reinterpret_cast<TSurfaceId*>(param.iSurfaceId), &sid, sizeof (TSurfaceId));
+ return KErrNone;
+ }
+
+
+/**
+Opens a surface. If the current process already is in the owners list, its usage count is
+incremented. If this is an open from a different process, a new surface owner object is added
+to the surface's list of owners and its usage count is set to 1.
+@param aId The surface id of the surface to be opened.
+@return KErrNone if successful, otherwise a system error code
+@internalTechnology
+*/
+TInt DSurfaceManager::OpenSurface(const TSurfaceId* aId)
+ {
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, aId, sizeof (TSurfaceId));
+ NKern::ThreadEnterCS();
+ TProcessListItem* owner = new TProcessListItem; //speculative creation
+ TRACE(Kern::Printf("SM A %08x TProcessListItem OpenSurface", owner);)
+
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ delete owner; //free the memory just allocated
+ TRACE(Kern::Printf("SM D %08x TProcessListItem OpenSurface", owner);)
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (so)
+ {
+ //already an owner so inc the ref count
+ ++so->iCount;
+ }
+ else
+ {
+ //new process trying to open it
+ if (!owner)
+ {
+ //the creation of the owner information object failed, out of memory
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ owner->iCount = 1; //mark it open in this process
+ owner->iOwningProcess = &Kern::CurrentProcess();
+
+ //add the owner to the list of owners
+ owner->iNext = surface->iOwners;
+ surface->iOwners = owner;
+ owner = NULL;
+ }
+ NKern::FMSignal(&iMutex);
+ delete owner; //free if not used.
+ TRACE(Kern::Printf("SM D %08x TProcessListItem OpenSurface", owner);)
+ NKern::ThreadLeaveCS();
+ return KErrNone;
+ }
+
+
+
+/**
+Closes a surface. Decrements the usage count in the surface owner object
+if the usage count is then zero, removes this surface owner from the surface.
+If this results in a surface with no owners, the surface is deleted and the
+surface shared chunk is closed.
+@param aId The id of the surface to be closed
+@return KErrNone if successful, KErrArgument if the surface ID does not refer to a surface,
+KErrAccessDenied if the surface is not open in the current process, otherwise a system wide
+error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::CloseSurface(const TSurfaceId* aId)
+ {
+
+ TSurfaceId sid;
+ kumemget(&sid, aId, sizeof (TSurfaceId)); //fetch surface id from user memory
+ //look it up
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied;
+ }
+
+ //current process is a surface owner so decrement the open count
+ TSurface* surfaceToDelete = NULL;
+ TProcessListItem* ownerToDelete = NULL;
+ DChunk* chunkToClose = NULL;
+ if (--so->iCount == 0)
+ {
+ //if count is now zero remove the owner
+ //surface->RemoveOwner(so);
+ UnlinkListItem(&surface->iOwners, so);
+ ownerToDelete = so;
+
+ //check to see if the surface has any owners
+ if (!surface->iOwners)
+ {
+ //no more owners of the surface
+ chunkToClose = surface->iChunk;
+
+ //remove the surface from the list
+ UnlinkListItem(&(iSurfacesIndex[SurfaceIdToIndex(surface->iId)]), surface);
+ surfaceToDelete = surface;
+ }
+ }
+
+ NKern::FMSignal(&iMutex);
+
+ if (chunkToClose)
+ {
+ //surface has no more owners so close the chunk
+ Kern::ChunkClose(chunkToClose);
+ }
+
+ delete surfaceToDelete;
+ TRACE(Kern::Printf("SM D %08x TSurface CloseSurface",surfaceToDelete);)
+ delete ownerToDelete;
+ TRACE(Kern::Printf("SM D %08x TProcessListItem CloseSurface",ownerToDelete);)
+ NKern::ThreadLeaveCS();
+
+ return KErrNone;
+ }
+
+
+/**
+Maps the surface memory into the process of the calling thread. This will fail if
+the surface is not open in this process, or if the handle to the chunk cannot be created.
+@param aId The id of the surface to be mapped in.
+@return KErrNone if successful, KErrArgument if the surface ID does not refer to a
+surface, KErrAccessDenied if the surface is not open in the current process,
+KErrNotSupported if the surface is not mappable, KErrOverflow if the chunk limit has been
+exceeded in the moving memory model, otherwise a system wide error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::MapSurface(const TSurfaceId* aId)
+ {
+ TSurfaceId sid;
+ kumemget(&sid, aId, sizeof (TSurfaceId)); //fetch surface id from user memory
+
+ //look it up
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument; //surface id is not valid or in the list of surfaces
+ }
+ if(!surface->iMappable)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrNotSupported;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied; //can't map it in, it's not open in this process
+ }
+
+ DChunk* chunk = surface->iChunk;
+ TInt r = chunk->Open();
+ NKern::FMSignal(&iMutex);
+ TRACE(Kern::Printf("SM MapSurface chunk open r %d\n",r);)
+
+ if (r == KErrGeneral)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied;
+ }
+
+ //if we are here, got the surface and we are the owner.
+ //if we are the owner we must have it open at least once
+
+ r = Kern::MakeHandleAndOpen(NULL, chunk);
+ chunk->Close(NULL);
+ TRACE(Kern::Printf("SM MapSurface handle open r: %d\n",r);)
+
+ NKern::ThreadLeaveCS();
+
+ return r;
+ }
+
+
+/**
+Record a new connection to the driver.
+Adds an element to the reference counted list of connected processes if the connection
+is from a new process, otherwise it increments the reference count.
+@param aProcess The process which has opened a driver channel.
+@internalTechnology
+*/
+TInt DSurfaceManager::AddConnection(const DProcess* aProcess)
+ {
+ TRACE(Kern::Printf("SM AddConnection process %08x\n", aProcess);)
+ NKern::ThreadEnterCS();
+ TProcessListItem* connectedProcess = new TProcessListItem; //speculative creation
+ TRACE(Kern::Printf("SM A %08x TProcessListItem AddConnection", connectedProcess);)
+ NKern::FMWait(&iMutex);
+ TProcessListItem* p = FindConnectedProcess(aProcess);
+ if (p) //already connected, found the process
+ {
+ ++p->iCount;
+ }
+ else
+ {
+ //add a new connected process
+ if (!connectedProcess)
+ {
+ //the creation of the owner information object failed, out of memory
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+ connectedProcess->iOwningProcess = (DProcess*)aProcess;
+ connectedProcess->iCount=1;
+
+ connectedProcess->iNext = iConnectedProcesses;
+ iConnectedProcesses = connectedProcess;
+ connectedProcess = NULL;
+ }
+ NKern::FMSignal(&iMutex);
+ delete connectedProcess;
+ TRACE(Kern::Printf("SM D %08x TProcessListItem AddConnection", connectedProcess);)
+ NKern::ThreadLeaveCS();
+ return KErrNone;
+ }
+
+
+
+/**
+Called when the driver channel is closed.
+Decrements the reference count for the connected process, if the last connection
+for this process is closed (reference count reaches 0) it removes the process from the list.
+@param aProcess The process which has closed the driver channel.
+@internalTechnology
+*/
+void DSurfaceManager::RemoveConnection(const DProcess* aProcess)
+ {
+ TRACE(Kern::Printf("SM RemoveConnection process %08x\n", aProcess);)
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ TProcessListItem* p =FindConnectedProcess(aProcess);
+ TProcessListItem* toDelete = NULL;
+ if (p) //already connected, found the process
+ {
+ if (--p->iCount == 0) //last connection in process has disconnected
+ {
+ //remove the process from the list and cleanup
+ UnlinkListItem(&iConnectedProcesses, p);
+ toDelete = p;
+ }
+ }
+ NKern::FMSignal(&iMutex);
+ delete toDelete;
+ TRACE(Kern::Printf("SM D %08x TProcessListItem RemoveConnection ", toDelete);)
+
+
+ if (toDelete) // if a process has closed its last channel, remove process from the surface owners.
+ {
+ CloseSurfaceHandlesForProcess(aProcess);
+ }
+
+ NKern::ThreadLeaveCS();
+ }
+
+
+
+
+/**
+Closes all the surfaces belonging to the process which has just terminated.
+If this is the only owner of a surface, delete the surface.
+@param aProcess The process which has terminated.
+@pre must be called in critical section
+@internalTechnology
+*/
+void DSurfaceManager::CloseSurfaceHandlesForProcess(const DProcess* aProcess)
+ {
+
+ NKern::FMWait(&iMutex);
+
+ TSurface* p = NULL;
+ TSurface* surfacesTodelete = NULL;
+ TProcessListItem* ownersTodelete = NULL;
+ TProcessListItem* so;
+ // There are 16 doubly linked lists managed by Surface Manager
+ for (TInt index = 0; index < KMaxLists; index++)
+ {
+ p = iSurfacesIndex[index];
+ while(p)
+ {
+ //see if the process which has just died is an owner of any surfaces
+ TSurface* surface = p;
+ p=p->iNext;
+ so = surface->ProcessOwnerInfo(aProcess);
+ if (so)
+ {
+ UnlinkListItem(&surface->iOwners, so);
+ so->iNext = ownersTodelete; //add the owner to the list of owner objects to remove
+ ownersTodelete = so;
+
+ if (!surface->iOwners) //if the surface hasn't any owners
+ {
+ //remove the surface from the list
+ UnlinkListItem(&iSurfacesIndex[index], surface);
+ surface->iNext = surfacesTodelete; //add the surface to the list of surfaces to remove
+ surfacesTodelete = surface;
+ }
+ }
+ }
+ }
+ NKern::FMSignal(&iMutex);
+
+ while(surfacesTodelete)
+ {
+ p = surfacesTodelete->iNext;
+ Kern::ChunkClose(surfacesTodelete->iChunk);
+ TRACE(Kern::Printf("SM Close chunk %08x CloseSurfaceHandlesForProcess",surfacesTodelete->iChunk);)
+ delete surfacesTodelete;
+ TRACE(Kern::Printf("SM D %08x TSurface CloseSurfaceHandlesForProcess",surfacesTodelete);)
+ surfacesTodelete = p;
+ }
+
+ while(ownersTodelete)
+ {
+ so = ownersTodelete->iNext;
+ delete ownersTodelete;
+ TRACE(Kern::Printf("SM D %08x TProcessListItem CloseSurfaceHandlesForProcess",ownersTodelete);)
+ ownersTodelete = so;
+ }
+ }
+
+
+/**
+Returns the metadata information about the specified surface.
+@param aId The id of the surface.
+@param aInfo Pointer to user side descriptor to receive the information.
+@return KErrNone if successful, KErrArgument if the surface ID does not refer to a surface,
+KErrAccessDenied if the surface is not open in the current process, otherwise a system wide
+error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::SurfaceInfo(const TSurfaceId* aId, TDes8* aInfo)
+ {
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, aId, sizeof (TSurfaceId));
+
+ RSurfaceManager::TInfoBuf buf;
+ RSurfaceManager::TSurfaceInfoV01& info = buf();
+
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrAccessDenied; //can do this, not open
+ }
+
+ //at this point, we have a surface, we are the owner and it's mapped in
+ info.iSize = surface->iSize; // Visible width/height in pixels
+ info.iBuffers = surface->iBuffers; // Number of Buffers
+ info.iPixelFormat = surface->iPixelFormat; // pixel format
+ info.iStride = surface->iStride; // Number of bytes between start of one line and start of next
+ info.iContiguous = surface->iContiguous; // is it physically contiguous
+ info.iCacheAttrib = surface->iCacheAttrib; // Underlying chunk is CPU cached or not
+ info.iMappable = surface->iMappable; // Is the surface Mappable
+ NKern::FMSignal(&iMutex);
+
+ //copy it back to user side
+ Kern::InfoCopy(*aInfo, buf);
+ return KErrNone;
+ }
+
+
+
+/**
+Generates a unique surface id
+@param aId Surface id reference to receive the generated id.
+@internalTechnology
+*/
+void DSurfaceManager::GenerateSurfaceId(TSurfaceId& aId)
+ {
+ TSurfaceId id;
+
+ for (TInt x = 0; x < 4; ++x)
+ {
+ id.iInternal[x] = Kern::Random();
+ };
+
+ //package up the handle,
+ //set the type identifier
+ id.iInternal[3] &= 0x00FFFFFF;
+ id.iInternal[3] |= TSurfaceTypes::ESurfaceManagerSurface << 24;
+ aId = id;
+ TRACE(Kern::Printf("SM GenerateSurfaceId id = %u %u %u %u\n",id.iInternal[0],id.iInternal[1],id.iInternal[2],id.iInternal[3]);)
+ };
+
+
+
+/**
+Validates the surface creation attributes and calculates the size of the chunk required.
+@param aAttribs The surface creation attributes used to specify the surface requirements.
+@param aOffset Set to the offset between buffers on successfull return.
+@param aNewChunk If this is true, surface is created in a new chunk otherwise the surface is created in an existing chunk
+@return The size of chunk required. A size of 0 indicates a problem.
+*/
+TInt DSurfaceManager::ValidateAndCalculateChunkSize(RSurfaceManager::TSurfaceCreationAttributes& aAttribs,
+ TInt& aOffset, TUint &aActualBufferSize, const TBool aNewChunk)
+ {
+/*
+ TRACE(Kern::Printf("SM width = %d height = %d\n", aAttribs.iSize.iWidth, aAttribs.iSize.iHeight);)
+ TRACE(Kern::Printf("SM buffers = %d\n", aAttribs.iBuffers);)
+ TRACE(Kern::Printf("SM format = %d\n", aAttribs.iPixelFormat);)
+ TRACE(Kern::Printf("SM stride = %d\n", aAttribs.iStride);)
+ TRACE(Kern::Printf("SM offset to first buffer = %d\n", aAttribs.iOffsetToFirstBuffer);)
+ TRACE(Kern::Printf("SM offset between buffer = %d\n", aOffset);)
+ TRACE(Kern::Printf("SM alignment = %d\n", aAttribs.iAlignment);)
+ TRACE(Kern::Printf("SM contiguous = %d\n\n", aAttribs.iContiguous);)
+ TRACE(Kern::Printf("SM cacheAttrib = %d\n\n", aAttribs.iCacheAttrib);)
+*/
+ //check for negative values
+ if(aAttribs.iOffsetToFirstBuffer < 0 || aOffset < 0 )
+ {
+ TRACE(Kern::Printf("SM Validate offset for negative value");)
+ return 0;
+ }
+
+ //check aligment is sensible
+ TInt alignmentMask = 0;
+ switch(aAttribs.iAlignment)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ alignmentMask = 31;
+ break;
+ case 64:
+ alignmentMask = 63;
+ break;
+ case RSurfaceManager::EPageAligned:
+ break;
+ default:
+ TRACE(Kern::Printf("SM Validate alignment");)
+ return 0;
+ }
+
+ //check alignment issues.
+ if(aAttribs.iAlignment != RSurfaceManager::EPageAligned)
+ {
+ if(aNewChunk)
+ {
+ if(aAttribs.iCacheAttrib == RSurfaceManager::ECached) // Surface is CPU cached, so the alignment will be based on either 32 or 64 byte
+ {
+ //offset to first buffer needs to fit alignment
+ aAttribs.iOffsetToFirstBuffer = aAttribs.iOffsetToFirstBuffer + alignmentMask & ~alignmentMask;
+ //alignment with respect to offsetbetweenbuffers
+ aOffset = aOffset + alignmentMask & ~alignmentMask;
+ }
+ else // Surface is NOT CPU cached, so the alignment will be based on surface attribute alignment
+ {
+ TUint alignMask = aAttribs.iAlignment-1;
+ //offset to first buffer needs to fit alignment
+ aAttribs.iOffsetToFirstBuffer = aAttribs.iOffsetToFirstBuffer + alignMask & ~alignMask;
+ //alignment with respect to offsetbetweenbuffers
+ aOffset = aOffset + alignMask & ~alignMask;
+ }
+ }
+ else // existing chunk
+ {
+ TUint alignMask = aAttribs.iAlignment-1;
+ //check alignment issues. offset to first buffer needs to fit alignment
+ if (aAttribs.iOffsetToFirstBuffer & alignMask)
+ {
+ TRACE(Kern::Printf("SM Validate offset to first pixel misaligned");)
+ return 0;
+ }
+
+ //check alignment for offsetbetweenbuffers. offset between buffer needs to fit alignment for existing chunks
+ if (aOffset & alignMask)
+ {
+ TRACE(Kern::Printf("SM Validate offset between buffers misaligned");)
+ return 0;
+ }
+ }
+ }
+ else //page aligned
+ {
+ if(aNewChunk)// if its a new chunks and doesn't match exact alignment then do the rounding
+ {
+ TUint32 pageSize = Kern::RoundToPageSize(1);
+ //offset to first buffer needs to fit alignment
+ aAttribs.iOffsetToFirstBuffer = (aAttribs.iOffsetToFirstBuffer + (pageSize - 1)) & ~(pageSize - 1);
+ //alignment with respect to offsetbetweenbuffers
+ aOffset = (aOffset + (pageSize - 1)) & ~((pageSize - 1));
+ }
+ else // for existing chunks don't do any rounding operation
+ {
+ TUint32 pageSize = Kern::RoundToPageSize(1);
+ TUint alignmask = aAttribs.iOffsetToFirstBuffer & (pageSize - 1);
+ if (alignmask)
+ {
+ TRACE(Kern::Printf("SM Validate offset to first pixel misaligned");)
+ return 0;
+ }
+
+ alignmask = aOffset & (pageSize - 1);
+ if (alignmask)
+ {
+ TRACE(Kern::Printf("SM Validate offset between buffers misaligned");)
+ return 0;
+ }
+ }
+ }
+
+ //check width and height
+ if(aAttribs.iSize.iWidth <= 0 || aAttribs.iSize.iHeight <= 0)
+ {
+ TRACE(Kern::Printf("SM Validate width/height");)
+ return 0;
+ }
+
+
+ //check there is at least 1 buffer
+ if (aAttribs.iBuffers <= 0)
+ {
+ TRACE(Kern::Printf("SM Validate buffers");)
+ return 0;
+ }
+
+ //Sort the array and also check for duplication
+ if (!SortHints(aAttribs.iSurfaceHints,aAttribs.iHintCount))
+ {
+ TRACE(Kern::Printf("SM Validate Duplicate hint key");)
+ return 0;
+ }
+
+ TUint size = 0;
+ //calculate buffer size and round it to alignment or to page size
+ TInt64 bufferSize = aAttribs.iStride;
+ bufferSize *= aAttribs.iSize.iHeight;
+
+ if (I64HIGH(bufferSize) > 0) //too big
+ {
+ TRACE(Kern::Printf("SM Validate chunk buffer size is out of range");)
+ return 0;
+ }
+
+ TUint bsize = I64LOW(bufferSize);
+ if (bsize > KMaxTInt)
+ {
+ TRACE(Kern::Printf("SM Validate buffer size is out of range for TInt");)
+ return 0;
+ }
+
+ if(aAttribs.iAlignment == RSurfaceManager::EPageAligned)
+ {
+ bsize = Kern::RoundToPageSize(bsize); //page alignment
+ }
+ else if(aAttribs.iCacheAttrib == RSurfaceManager::ECached)
+ {
+ bsize = bsize + alignmentMask & ~alignmentMask; //CPU cached byte alignment, for minimum of the specified alignment(32 or 64)
+ }
+ else
+ {
+ bsize = bsize + (aAttribs.iAlignment-1) & ~(aAttribs.iAlignment-1); //NON CPU cached byte alignment for 1, 2, 4, 8, 16, 32 and 64
+ }
+
+ bufferSize = bsize;
+ // Remember the actual size.
+ aActualBufferSize = bsize;
+
+ //if offset between buffers is zero, then assign the calculated value as offset between buffers
+ if(aOffset == 0)
+ {
+ //buffer size rounded to alignment as offset between buffers
+ aOffset = I64INT(bufferSize);
+ }
+ else if(aOffset < I64INT(bufferSize))
+ {
+ TRACE(Kern::Printf("SM Offset between the buffer is less than the required size");)
+ return 0;
+ }
+ else
+ {
+ //use the buffer size specified
+ bufferSize = aOffset;
+ }
+
+
+ TInt64 totalSize = aAttribs.iOffsetToFirstBuffer + (aAttribs.iBuffers * bufferSize);
+
+ if (I64HIGH(totalSize) > 0) //too big
+ {
+ TRACE(Kern::Printf("SM Validate chunk size is out of range for RoundToPageSize");)
+ return 0;
+ }
+
+ size = I64LOW(totalSize);
+ if (size > KMaxTInt)
+ {
+ TRACE(Kern::Printf("SM Validate size is out of range for TInt");)
+ return 0;
+ }
+
+ size = Kern::RoundToPageSize(size);
+
+ //check the size isn't greater than will fit in a TInt
+ if (size > KMaxTInt)
+ {
+ TRACE(Kern::Printf("SM Rounded size is out of range for TInt");)
+ return 0;
+ }
+
+ TRACE(Kern::Printf("SM After validate - offset to first buffer = %d\n", aAttribs.iOffsetToFirstBuffer);)
+ TRACE(Kern::Printf("SM After validate - offset between buffer = %d\n", aOffset);)
+ TRACE(Kern::Printf("SM CalculateChunkSize size = %d\n", size);)
+ return size;
+ }
+
+
+/**
+Find the surface in the list.
+@param aId The surface id of the surface to find in the surface list
+@return pointer to the surface object
+@internalTechnology
+*/
+TSurface* DSurfaceManager::FindSurfaceById(const TSurfaceId& aId)
+ {
+ TSurface *p = iSurfacesIndex[SurfaceIdToIndex(aId)];
+ while (p)
+ {
+ if (aId == p->iId)
+ {
+ //found it
+ return p;
+ }
+
+ p = p->iNext;
+ }
+ return NULL;
+ }
+
+
+/**
+Find the index of the hint key from the surface list using binary search.
+@param aHintsArray Pointer to the first element in the array of surface hints
+@param aKey The surface hint key uid value to search in the surface list
+@return index of the hint pair key in the surface list, KErrNotFound if key not found
+@internalTechnology
+*/
+TInt DSurfaceManager::FindHintKey(const RSurfaceManager::THintPair* aHintsArray, TUint32 aKey) const
+ {
+ __ASSERT_DEBUG(aHintsArray != NULL, Kern::Fault("Surface Manager", __LINE__));
+
+ TInt bottom = 0;
+ TInt top = KMaxHintsPerSurface - 1;
+ TInt mid;
+ while (bottom <= top)
+ {
+ mid = (bottom + top) / 2;
+ if((TUint) aHintsArray[mid].iKey.iUid == aKey)
+ {
+ return mid;
+ }
+ else if ((TUint)aHintsArray[mid].iKey.iUid < aKey)
+ {
+ top = mid - 1;
+ }
+ else
+ {
+ bottom = mid + 1;
+ }
+ }
+ return KErrNotFound; //Hint key not found
+ }
+
+TProcessListItem* DSurfaceManager::FindConnectedProcess(const DProcess* aProcess)
+ {
+ TProcessListItem * p = iConnectedProcesses;
+ while (p)
+ {
+ if (aProcess == p->iOwningProcess)
+ {
+ //found it
+ return p;
+ }
+
+ p = p->iNext;
+ }
+ return NULL;
+ }
+
+/**
+Searches for a right place to insert the new hint pair in a sorted array.
+@param aHintsArray Pointer to the first element in the sorted array
+@param aKey The surface hint key uid value to search in the surface list
+@pre, there is at least one empty place in the array
+@return KErrNone if a new hint pair key inserted in the surface list, KErrAlreadyExists if duplicated
+@internalTechnology
+*/
+TInt DSurfaceManager::InsertHintKey(RSurfaceManager::THintPair* aHintsArray, const RSurfaceManager::THintPair& aHintPair) const
+ {
+ __ASSERT_DEBUG(aHintsArray != NULL, Kern::Fault("Surface Manager", __LINE__));
+ __ASSERT_DEBUG(aHintsArray[KMaxHintsPerSurface-1].iKey.iUid == NULL, Kern::Fault("Surface Manager", __LINE__));
+
+ TInt pos = 0;
+ if (aHintsArray[pos].iKey.iUid != 0)
+ {
+ while((TUint)aHintsArray[pos].iKey.iUid>(TUint)aHintPair.iKey.iUid && pos < KMaxHintsPerSurface-1)
+ {// find the right place to insert
+ ++pos;
+ }
+
+ if((TUint)aHintsArray[pos].iKey.iUid==(TUint)aHintPair.iKey.iUid)
+ {
+ //Duplicate key
+ return KErrAlreadyExists;
+ }
+ else
+ {
+ // Shift right
+ memmove(aHintsArray+pos+1, aHintsArray+pos, (KMaxHintsPerSurface-pos-1)*sizeof(RSurfaceManager::THintPair));
+ }
+ }
+ aHintsArray[pos] = aHintPair;
+ return KErrNone;
+ }
+
+/**
+Sort the surface hint array in descending order.
+@param aHintsArray The surface hintpair in the surface list
+@param aNumberOfHints The number of hints
+@return ETrue if sorting is finished or it is an empty array, EFalse if key duplicated
+@internalTechnology
+*/
+TBool DSurfaceManager::SortHints(RSurfaceManager::THintPair* aHintsArray, TInt aNumberOfHints) const
+ {
+ TInt in = 0;
+ TInt out = 0;
+ RSurfaceManager::THintPair temp;
+ if(!aHintsArray)
+ {
+ return ETrue;
+ }
+ for(out = 0; out < aNumberOfHints; ++out)
+ {
+ if(aHintsArray[out].iKey.iUid != 0)
+ {
+ temp = aHintsArray[out];
+ in = out; // start shifting at out
+ while(in > 0 && (TUint)aHintsArray[in-1].iKey.iUid <= (TUint)temp.iKey.iUid)
+ {
+ if ((TUint)aHintsArray[in-1].iKey.iUid == (TUint)temp.iKey.iUid)
+ {
+ return EFalse; //duplicate hint keys are not allowed
+ }
+ aHintsArray[in] = aHintsArray[in-1]; // shift item to the right
+ --in; // go left one position
+ }
+ aHintsArray[in] = temp; // insert marked item
+ }
+ }
+ return ETrue;
+ }
+
+
+/**
+Ensures the memory is updated consistently before/after triggering non CPU hardware access.
+@param aParam The suface id and buffer number (0 based).
+@param aOperation The type of the synchronize operation.
+@return KErrNone if successful, KErrArgument if the surface ID is invalid or
+buffer number is invalid, KErrAccessDenied if the surface is not open in this
+process, otherwise a system wide error code.
+@see RSurfaceManager::TSyncOperation
+@internalTechnology
+*/
+TInt DSurfaceManager::SynchronizeCache(RSurfaceManagerDriver::TDeviceParam* aParam, RSurfaceManager::TSyncOperation aOperation)
+ {
+ //Parse the parameters
+ RSurfaceManagerDriver::TDeviceParam param;
+ kumemget(¶m, aParam, sizeof(RSurfaceManagerDriver::TDeviceParam));
+ TSurfaceId sid;
+ kumemget(&sid, param.iSurfaceId, sizeof(TSurfaceId));
+ TInt buffer = (TInt)param.iBuffer;
+
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied;
+ }
+
+ // surfaces have to have at least one buffer
+ __ASSERT_DEBUG(surface->iBuffers > 0, Kern::Fault("Surface Manager", __LINE__));
+
+ //Validate the buffer number is within range
+ if((buffer >= surface->iBuffers) || (buffer < 0))
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ DChunk* chunk = surface->iChunk;
+ TInt offsetBetweenBuffers = surface->iOffsetBetweenBuffers;
+ NKern::FMSignal(&iMutex);
+
+ TUint32 kernAddr;
+ TUint32 mapAttr;
+ TUint32 physAddr;
+ TInt pageList = chunk->iSize / Kern::RoundToPageSize(1) + 1;
+ TUint32* physAddr2 = new TUint32[pageList];
+ if(!physAddr2)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ TRACE(Kern::Printf("SM %08x DChunk SynchronizeCache", chunk);)
+
+ //Retrieve the kernel address and mapping attribute from the chunk
+ TInt err = Kern::ChunkPhysicalAddress(chunk, surface->iOffsetToFirstBuffer + (buffer * offsetBetweenBuffers), offsetBetweenBuffers, kernAddr, mapAttr, physAddr, physAddr2);
+ delete[] physAddr2;
+ if(err >= KErrNone)
+ {
+ TRACE(Kern::Printf("SM %08x kernAddr SynchronizeCache", kernAddr);)
+ TRACE(Kern::Printf("SM %08x mapAttr SynchronizeCache", mapAttr);)
+ err = KErrNone;
+
+ // Do the sync operation
+ switch(aOperation)
+ {
+ case RSurfaceManager::ESyncBeforeNonCPURead:
+ Cache::SyncMemoryBeforeDmaWrite(kernAddr, offsetBetweenBuffers, mapAttr);
+ break;
+ case RSurfaceManager::ESyncBeforeNonCPUWrite:
+ Cache::SyncMemoryBeforeDmaRead(kernAddr, offsetBetweenBuffers, mapAttr);
+ break;
+ case RSurfaceManager::ESyncAfterNonCPUWrite:
+ Cache::SyncMemoryAfterDmaRead(kernAddr, offsetBetweenBuffers);
+ break;
+ default:
+ err = KErrArgument;
+ break;
+ }
+ }
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+
+/**
+Get the surface hint value for the given surface ID and hint pair key.
+@param aSurfaceId The surface identifier originally returned when the surface was created.
+@param aHintPair The hint value for the requested hint pair key.
+@return KErrNone if successful, KErrArgument if the surface ID is invalid or
+invalid hint pair key used, KErrAccessDenied if the surface is not open in the
+current process, otherwise a system wide error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::GetSurfaceHint(const TSurfaceId* aSurfaceId, RSurfaceManager::THintPair* aHintPair)
+ {
+ RSurfaceManager::THintPair hintPair;
+ kumemget(&hintPair, aHintPair, sizeof(RSurfaceManager::THintPair));
+
+ if (hintPair.iKey.iUid == 0)
+ {
+ TRACE(Kern::Printf("SM GetSurfaceHint Hint key is invalid");)
+ return KErrArgument; //Invalid Hint key
+ }
+
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, aSurfaceId, sizeof (TSurfaceId));
+
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrAccessDenied;
+ }
+
+ //at this point, we have a surface, we have to find the hint value based on the hint pair key
+ TInt index = FindHintKey(surface->iSurfaceHints, hintPair.iKey.iUid);
+
+ if (index == KErrNotFound)
+ {
+ TRACE(Kern::Printf("SM GetSurfaceHint Hint key not found");)
+ NKern::FMSignal(&iMutex);
+ return KErrArgument; //Hint key not found
+ }
+
+ RSurfaceManager::THintPair hint = surface->iSurfaceHints[index];
+ NKern::FMSignal(&iMutex);
+
+ TRACE(Kern::Printf("SM GetSurfaceHint Hint value %d", hint.iValue);)
+ //write it back to user side
+ kumemput(aHintPair, &hint, sizeof(RSurfaceManager::THintPair));
+ return KErrNone;
+ }
+
+
+/**
+Set the surface hint value for an existing surface hint key of the surface Id.
+@param aSurfaceId The surface identifier originally returned when the surface was created.
+@param aHintPair The value of the hint pair to set.
+@return KErrNone if successful, KErrArgument if the surface ID is invalid or if invalid
+hint key used, KErrAccessDenied if the hint pair is immutable or the surface is not open
+in the current process, otherwise a system wide error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::SetSurfaceHint(const TSurfaceId* aSurfaceId, const RSurfaceManager::THintPair* aHintPair)
+ {
+ RSurfaceManager::THintPair hintPair;
+ kumemget(&hintPair, aHintPair, sizeof(RSurfaceManager::THintPair));
+
+ //Check for valid hint key
+ if (!hintPair.iKey.iUid)
+ {
+ TRACE(Kern::Printf("SM SetSurfaceHint Hint key is invalid");)
+ return KErrArgument; //Invalid Hint key
+ }
+
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, aSurfaceId, sizeof (TSurfaceId));
+
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied;
+ }
+
+ //at this point, we have a surface, we have to find the hint value based on the hint pair key
+ TInt index = FindHintKey(surface->iSurfaceHints, hintPair.iKey.iUid);
+ if (index == KErrNotFound)
+ {
+ TRACE(Kern::Printf("SM SetSurfaceHint Hint key not found or invalid");)
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument; //Hint key not found or invalid
+ }
+
+ //Check for mutability
+ if(!surface->iSurfaceHints[index].iMutable)
+ {
+ TRACE(Kern::Printf("SM SetSurfaceHint Hint is immutable");)
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied; //Hint pair is immutable
+ }
+ TRACE(Kern::Printf("SM SetSurfaceHint Hint key found and updated its value %d for the surface %08x \n", aHintPair->iValue, &sid);)
+
+ //set the hint pair value now
+ memcpy(&surface->iSurfaceHints[index], &hintPair, sizeof(RSurfaceManager::THintPair));
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+
+ return KErrNone;
+ }
+
+/**
+Add a new surface hint value for the surface Id.
+@param aSurfaceId The surface identifier originally returned when the surface was created.
+@param aHintPair The value of the hint pair to Add.
+@return Returns KErrNone if successful, KErrArgument if the surface ID is invalid or the
+hint pair has invalid key UID, KErrAccessDenied if the surface is not open in the current
+process, KErrAlreadyExists if duplicate hint key used, KErrOverflow if no space to add new
+pair, otherwise a system wide error code.
+@internalTechnology
+*/
+TInt DSurfaceManager::AddSurfaceHint(const TSurfaceId* aSurfaceId, const RSurfaceManager::THintPair* aHintPair)
+ {
+ RSurfaceManager::THintPair hintPair;
+ kumemget(&hintPair, aHintPair, sizeof(RSurfaceManager::THintPair));
+
+ //Check for valid hint key
+ if (hintPair.iKey.iUid == 0)
+ {
+ TRACE(Kern::Printf("SM AddSurfaceHint Hint key is invalid");)
+ return KErrArgument; //Invalid Hint key
+ }
+
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, aSurfaceId, sizeof (TSurfaceId));
+
+ NKern::ThreadEnterCS();
+ NKern::FMWait(&iMutex);
+ //look it up
+ TSurface* surface = FindSurfaceById(sid);
+ if (!surface)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrArgument;
+ }
+
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrAccessDenied;
+ }
+
+
+ //Check for empty hint pair
+ if(surface->iSurfaceHints[KMaxHintsPerSurface - 1].iKey.iUid != 0)//at least end of sorted hint array should be 0 to add a new hint
+ {
+ TRACE(Kern::Printf("SM AddSurfaceHint there is no room to add the hint");)
+ NKern::FMSignal(&iMutex);
+ NKern::ThreadLeaveCS();
+ return KErrOverflow; //No room for new hint
+ }
+ //We found room for a new hint pair, so insert it in the array
+ // Meanwhile, we check for duplication, if it is, return KErrAlreadyExists
+ TInt err = InsertHintKey(surface->iSurfaceHints,hintPair);
+ NKern::FMSignal(&iMutex);
+ TRACE(Kern::Printf("SM AddSurfaceHint Added new key ");)
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+/**
+Get the offset of the specified buffer from the base address of the underlying
+chunk.
+
+To obtain the address of the buffer, the offset returned must be added onto the
+base address of the RChunk returned in a call to MapSurface(). Note that
+buffer offsets are immutable during the lifetime of the surface.
+@param aParam The input parameters including the surface ID and buffer index.
+@pre The surface is open in the calling process.
+@return KErrNone if successful, KErrArgument if aSurfaceId or aBuffer are invalid,
+KErrAccessDenied if the surface is not open in the current process, KErrNotSupported if
+the surface is not mappable, otherwise a system wide error code.
+*/
+TInt DSurfaceManager::GetBufferOffset(RSurfaceManagerDriver::TDeviceParam* aParam,TUint* aOffset)
+ {
+ //Get the input parameters
+ RSurfaceManagerDriver::TDeviceParam param;
+ kumemget(¶m, aParam, sizeof(RSurfaceManagerDriver::TDeviceParam));
+ TSurfaceId sid;
+ //fetch surface id from user memory
+ kumemget(&sid, param.iSurfaceId, sizeof(TSurfaceId));
+ //(TAny*)iBuffer holds the buffer number in its value
+ TInt bufferNumber = (TInt) param.iBuffer;
+
+ TSurface* surface = NULL;
+ NKern::FMWait(&iMutex);
+ surface = FindSurfaceById(sid);
+ if(NULL == surface || (bufferNumber >= surface->iBuffers))
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrArgument;
+ }
+ if(!surface->iMappable)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrNotSupported;
+ }
+ //find the owner
+ TProcessListItem* so = surface->ProcessOwnerInfo(&Kern::CurrentProcess());
+ if (!so)
+ {
+ NKern::FMSignal(&iMutex);
+ return KErrAccessDenied;
+ }
+ TInt bufferOffset = surface->iOffsetToFirstBuffer + bufferNumber*surface->iOffsetBetweenBuffers;
+ NKern::FMSignal(&iMutex);
+
+ kumemput(aOffset, &bufferOffset, sizeof (TInt));
+ return KErrNone;
+ }
+
+/**
+Returns information specific to the Surface Manager implementation.
+@param aAttrib: Attribute to retrieve
+@param aValue : Output parameter where we write the value for the specified attribute
+@return KErrNone if successful or KErrArgument if the attribute UID is not recognized
+@internalTechnology
+*/
+TInt DSurfaceManager::GetSurfaceManagerAttrib(RSurfaceManager::TSurfaceManagerAttrib* aAttrib,TInt* aValue)
+ {
+ RSurfaceManager::TSurfaceManagerAttrib attrib;
+ kumemget(&attrib, aAttrib, sizeof(RSurfaceManager::TSurfaceManagerAttrib));
+
+ TInt out=KErrNone;
+ TInt value;
+ switch (attrib)
+ {
+ case RSurfaceManager::EMaxNumberOfHints:
+ value=KMaxHintsPerSurface;
+ break;
+
+ default:
+ out=KErrArgument;
+ break;
+ };
+
+ if (out==KErrNone)
+ {
+ kumemput(aValue, &value, sizeof (TInt));
+ }
+ return out;
+ }