// 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;
}