author | mikek |
Sun, 27 Jun 2010 21:43:55 +0100 | |
branch | GCC_SURGE |
changeset 181 | bd8f1e65581b |
parent 0 | a41df078684a |
permissions | -rw-r--r-- |
// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). // All rights reserved. // This component and the accompanying materials are made available // under the terms of the License "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: // its implementation. // // /** @file An example data converter device driver which uses Shared Chunks in @publishedPartner @prototype 9.1 */ #include <kernel/kern_priv.h> #include <kernel/cache.h> #include "convert1.h" #include "convert1_dev.h" #if 0 // Set true for tracing #define TRACE(x) x #else #define TRACE(x) #endif _LIT(KConvert1PanicCategory,"CONVERT1"); // // DConvert1Factory // /** Number of hardware 'resources' available to driver. E.g. the number of simultaneous channels it can support. */ const TInt KTotalConvert1Resources = 4; /** A resource ID representing no resources */ const TInt KNullConvert1ResourceId = -1; /** Standard export function for LDDs. This creates a DLogicalDevice derived object, in this case, our DConvert1Factory */ DECLARE_STANDARD_LDD() { return new DConvert1Factory; } /** Constructor */ DConvert1Factory::DConvert1Factory() { // Set version number for this device iVersion=RConvert1::VersionRequired(); // Indicate that do support units or a PDD iParseMask=0; // Mark all resources available iResourceFlags = (1<<KTotalConvert1Resources)-1; } /** Second stage constructor for DConvert1Factory. This must at least set a name for the driver object. @return KErrNone if successful, otherwise one of the other system wide error codes. */ TInt DConvert1Factory::Install() { return SetName(&RConvert1::Name()); } /** Destructor */ DConvert1Factory::~DConvert1Factory() { } /** Return the drivers capabilities. Called in the response to an RDevice::GetCaps() request. @param aDes User-side descriptor to write capabilities information into */ void DConvert1Factory::GetCaps(TDes8& aDes) const { // Create a capabilities object RConvert1::TCaps caps; caps.iVersion = iVersion; caps.iMaxChannels = KTotalConvert1Resources; // Write it back to user memory Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps)); } /** Called by the kernel's device driver framework to create a Logical Channel. This is called in the context of the user thread (client) which requested the creation of a Logical Channel (E.g. through a call to RBusLogicalChannel::DoCreate) The thread is in a critical section. @param aChannel Set to point to the created Logical Channel @return KErrNone if successful, otherwise one of the other system wide error codes. */ TInt DConvert1Factory::Create(DLogicalChannelBase*& aChannel) { aChannel=new DConvert1Channel(this); if(!aChannel) return KErrNoMemory; return KErrNone; } /** Claim a hardware resource. This example driver has KTotalConvert1Resources 'hardware resources' and returns the ID of the next unallocated one. */ TInt DConvert1Factory::ClaimResource(TInt& aResourceId) { // Wait on mutex protecting resource allocation NKern::FMWait(&iResourceMutex); // Search for a free resource TUint resourceFlags = iResourceFlags; TUint mask = 1; TInt id = 0; do { if(resourceFlags&mask) break; mask <<= 1; } while(++id<KTotalConvert1Resources); if(resourceFlags&mask) iResourceFlags = resourceFlags&~mask; // Found resource, so mark it in use else id = KNullConvert1ResourceId; // No resource available // Set returned resource id aResourceId = id; // Release mutex protecting resource allocation NKern::FMSignal(&iResourceMutex); return id<0 ? KErrInUse : KErrNone; } /** Released the hardware resource indicated by the given id */ void DConvert1Factory::ReleaseResource(TInt aResourceId) { // Do nothing if the null id was given if(aResourceId==KNullConvert1ResourceId) return; // Wait on mutex protecting resource allocation NKern::FMWait(&iResourceMutex); // Check for valid resource and that it is not already free __NK_ASSERT_DEBUG(TUint(aResourceId)<TUint(KTotalConvert1Resources)); __NK_ASSERT_DEBUG((iResourceFlags&(1<<aResourceId))==0); // Flag resource free again iResourceFlags |= 1<<aResourceId; // Release mutex protecting resource allocation NKern::FMSignal(&iResourceMutex); } // // Logical Channel // /** Default configuration (4k buffer, No Input Chunk, 1MB/sec speed) */ static const RConvert1::TConfig DefaultConfig = {4<<10,EFalse,1<<20}; /** Constructor */ DConvert1Channel::DConvert1Channel(DConvert1Factory* aFactory) : iFactory(aFactory), iResourceId(KNullConvert1ResourceId), iConfig(DefaultConfig), iConvertTimer(ConvertDfcTrampoline,this) { } /** Second stage constructor called by the kernel's device driver framework. This is called in the context of the user thread (client) which requested the creation of a Logical Channel (E.g. through a call to RBusLogicalChannel::DoCreate) The thread is in a critical section. @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate @param aVer The version argument supplied by the client to RBusLogicalChannel::DoCreate @return KErrNone if successful, otherwise one of the other system wide error codes. */ TInt DConvert1Channel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer) { // Check client has EMultimediaDD capability if(!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by CAPTURE1"))) return KErrPermissionDenied; // Check version if (!Kern::QueryVersionSupported(RConvert1::VersionRequired(),aVer)) return KErrNotSupported; // Claim ownership of a hardware resource TInt r=iFactory->ClaimResource(iResourceId); if(r!=KErrNone) return r; // Set client thread with which channel will be used by iClient = &Kern::CurrentThread(); // Done return KErrNone; } /** Destructor */ DConvert1Channel::~DConvert1Channel() { // Cancel outsatnding requests DoCancel(RConvert1::EAllRequests); // Release hardware resource which we own iFactory->ReleaseResource(iResourceId); } /** Called when a user thread requests a handle to this channel. */ TInt DConvert1Channel::RequestUserHandle(DThread* aThread, TOwnerType aType) { // Make sure that only our client can get a handle if (aType!=EOwnerThread || aThread!=iClient) return KErrAccessDenied; return KErrNone; } /** Process a request on this logical channel. @param aReqNo Request number: ==KMaxTInt, a 'DoCancel' message >=0, a 'DoControl' message with function number equal to iValue <0, a 'DoRequest' message with function number equal to ~iValue @param a1 First argument. For DoRequest requests this is a pointer to the TRequestStatus. @param a2 Second argument. For DoRequest this is a pointer to the 2 actual TAny* arguments. @return Result. Ignored by device driver framework for DoRequest requests. */ TInt DConvert1Channel::Request(TInt aReqNo, TAny* a1, TAny* a2) { // Decode the message type and dispatch it to the relevent handler function... if ((TUint)aReqNo<(TUint)KMaxTInt) return DoControl(aReqNo,a1,a2); if(aReqNo==KMaxTInt) return DoCancel((TInt)a1); return DoRequest(aReqNo,a1,a2); } /** Process synchronous 'control' requests */ TInt DConvert1Channel::DoControl(TInt aFunction, TAny* a1, TAny* a2) { TRACE(Kern::Printf(">DConvert1Channel::DoControl fn=%d\n",aFunction);) TInt r = KErrNotSupported; switch (aFunction) { case RConvert1::EGetConfig: r = GetConfig((TDes8*)a1); break; case RConvert1::ESetConfig: r = SetConfig((const TDesC8*)a1,(RConvert1::TBufferInfo*)a2); break; case RConvert1::EConvertDes: ConvertDes((const TDesC8*)a1,(TRequestStatus*)a2); break; case RConvert1::EConvertChunk: ConvertChunk((const RConvert1::TConvertArgs*)a1,(TRequestStatus*)a2); break; case RConvert1::EConvertInChunk: ConvertInChunk((TInt)a1,(TRequestStatus*)a2); break; } TRACE(Kern::Printf("<DConvert1Channel::DoControl result=%d\n",r);) return r; } /** Process asynchronous requests. This driver doesn't have any 'DoRequest' requests because we handle asyncronous requests using 'DoControl' for performance reasons. I.e. to avoid having to read the arguments with kumemget() */ TInt DConvert1Channel::DoRequest(TInt aNotReqNo, TAny* a1, TAny* a2) { TRACE(Kern::Printf(">DConvert1Channel::DoRequest req=%d\n",aNotReqNo);) // Get arguments TAny* a[2]; kumemget32(a,a2,sizeof(a)); TRequestStatus* status=(TRequestStatus*)a1; TInt reqNo = ~aNotReqNo; // Do the request TInt r; switch(reqNo) { case RConvert1::EConvertDes: case RConvert1::EConvertChunk: case RConvert1::EConvertInChunk: // Not used because we do these asyncronous request as a // DoControl rather than a DoRequest for performance reasons. default: r = KErrNotSupported; break; } // Complete request if there was an error if (r!=KErrNone) Kern::RequestComplete(&Kern::CurrentThread(),status,r); TRACE(Kern::Printf("<DConvert1Channel::DoRequest result=%d\n",r);) return KErrNone; // Result is ignored by device driver framework for DoRequest requests } /** Process cancelling of asynchronous requests. */ TInt DConvert1Channel::DoCancel(TUint aMask) { TRACE(Kern::Printf(">DConvert1Channel::DoCancel mask=%08x\n",aMask);) if(aMask&( (1<<RConvert1::EConvertDes) | (1<<RConvert1::EConvertChunk) | (1<<RConvert1::EConvertInChunk) ) ) ConvertCancel(); TRACE(Kern::Printf("<DConvert1Channel::DoCancel\n");) return KErrNone; } // // Methods for processing configuration control messages // /** Process a GetConfig control message. This writes the current driver configuration to a RConvert1::TConfigBuf supplied by the client. */ TInt DConvert1Channel::GetConfig(TDes8* aConfigBuf) { // Write the config to the client Kern::InfoCopy(*aConfigBuf,(const TUint8*)&iConfig,sizeof(iConfig)); return KErrNone; } /** Process a SetConfig control message. This sets the driver configuration using a RConvert1::TConfigBuf supplied by the client. */ TInt DConvert1Channel::SetConfig(const TDesC8* aConfigBuf,RConvert1::TBufferInfo* aBufferInfo) { // Create a config structure. RConvert1::TConfig config(DefaultConfig); // Note: We have constructed a config using DefaultConfig, this is to allow // backwards compatibility when a client gives us an old (and shorter) version // of the config structure. // Read the config structure from client TPtr8 ptr((TUint8*)&config,sizeof(config)); Kern::KUDesGet(ptr,*aConfigBuf); // 'info' is the data we will return to client at the end RConvert1::TBufferInfo info; memclr(&info,sizeof(info)); TInt r; // Need to be in critical section whilst allocating objects NKern::ThreadEnterCS(); // Check we aren't in the middle of converting data if(iConvertRequestStatus) { r = KErrInUse; goto done; } // Note: The above check is enough to ensure we have exclusive access // to this channels buffer and hardware resources because: // 1. The covert DFC can't run because we haven't started converting yet. // 2. No other request can come in because the channel only allows one // client thread to use it. See DConvert1Channel::Request() // 3. The channel destructor can't be called whilst we are processing a request. // For some settings we allow zero to mean default... if(!config.iBufferSize) config.iBufferSize = DefaultConfig.iBufferSize; if(!config.iSpeed) config.iSpeed = DefaultConfig.iSpeed; // Validate configuration if(config.iBufferSize<=0) { r = KErrArgument; goto done; } if(config.iSpeed<=0) { r = KErrArgument; goto done; } // Change the config iConfig = config; { // Calculate buffer size TInt bufferSize = Kern::RoundToPageSize(config.iBufferSize); // Destroy old buffers iClientBuffer.Destroy(); iInBuffer.Destroy(); iOutBuffer.Destroy(); // Setup iClientBuffer r = iClientBuffer.SetMaxSize(bufferSize); if(r!=KErrNone) goto done; // Create output buffer r = iOutBuffer.Create(bufferSize); if(r!=KErrNone) goto done; // Make handle for output buffer r = Kern::MakeHandleAndOpen(NULL, iOutBuffer.iChunk); if(r<0) // -ve value is error, +ve value is a handle goto done; info.iOutChunkHandle = r; r = KErrNone; // Create input buffer if requested if(iConfig.iCreateInputChunk) { r = iInBuffer.Create(bufferSize); if(r!=KErrNone) goto done; // Make handle for input buffer r = Kern::MakeHandleAndOpen(NULL, iInBuffer.iChunk); if(r<0) // -ve value is error, +ve value is a handle goto done; info.iInChunkHandle = r; r = KErrNone; // Set info about input buffer // // Note we don't set iInBufferPtr because this is the address in // client process which it must set for itself info.iInBufferOffset = iInBuffer.iChunkOffset; info.iInBufferSize = iInBuffer.iMaxSize; } } done: // Cleanup if there was an error if(r!=KErrNone) { iClientBuffer.Destroy(); iInBuffer.Destroy(); iOutBuffer.Destroy(); if(info.iOutChunkHandle) Kern::CloseHandle(NULL,info.iOutChunkHandle); if(info.iInChunkHandle) Kern::CloseHandle(NULL,info.iInChunkHandle); memclr(&info,sizeof(info)); } NKern::ThreadLeaveCS(); // Write chunk handles and other info back to client memory kumemput32(aBufferInfo,&info,sizeof(info)); return r; } // // Methods for processing Convert requests // /** Process Convert request where the source data is specified by a descriptor */ void DConvert1Channel::ConvertDes(const TDesC8* aSrc,TRequestStatus* aRequestStatus) { TInt r; // Get descriptor info TInt len; TInt maxLen; TAny* uptr = (TAny*)Kern::KUDesInfo(*aSrc,len,maxLen); // Check there isn't an outstanding request if(iConvertRequestStatus) Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory); // Check output buffer has been created if(!iOutBuffer.iChunk) { r = KErrNotReady; goto done; } // Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called) if(!iInBuffer.iChunk) { r = KErrNotSupported; goto done; } // See if client data is in a shared chunk r = iClientBuffer.Open(uptr,len); if(r==KErrNone) iSource = &iClientBuffer; // use iClientBuffer as input buffer else { // Copy data from client descriptor into our iInBuffer r = iInBuffer.Copy(uptr,len); if(r==KErrNone) iSource = &iInBuffer; // use iInBuffer as input buffer } // Start convert if no error if(r==KErrNone) { iConvertRequestStatus = aRequestStatus; DoConvertStart(0,len); } done: // Complete request if there was an error if (r!=KErrNone) Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r); } /** Process Convert request where the source data is specified by a chunk */ void DConvert1Channel::ConvertChunk(const RConvert1::TConvertArgs* aSrcArgs,TRequestStatus* aRequestStatus) { TInt r; // Check there isn't an outstanding request if(iConvertRequestStatus) Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory); // Check output buffer has been created if(!iOutBuffer.iChunk) { r = KErrNotReady; goto done; } // Unpackage arguments RConvert1::TConvertArgs args; kumemget32(&args,aSrcArgs,sizeof(args)); // Make buffer by opening chunk r=iClientBuffer.Open(args.iChunkHandle,args.iOffset,args.iSize); // Start convert if no error if(r==KErrNone) { iSource = &iClientBuffer; iConvertRequestStatus = aRequestStatus; DoConvertStart(0,args.iSize); } done: // Complete request if there was an error if (r!=KErrNone) Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r); } /** Process Convert request where the source data is contained in the input chunk */ void DConvert1Channel::ConvertInChunk(TInt aSize,TRequestStatus* aRequestStatus) { TInt r; // Check there isn't an outstanding request if(iConvertRequestStatus) Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory); // Check output buffer has been created if(!iOutBuffer.iChunk) { r = KErrNotReady; goto done; } // Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called) if(!iInBuffer.iChunk) { r = KErrNotSupported; goto done; } // Check size of data really fits within chunk if(TUint(aSize)>=TUint(iInBuffer.iMaxSize)) { r = KErrArgument; goto done; } // Start the convert iSource = &iInBuffer; iConvertRequestStatus = aRequestStatus; DoConvertStart(iInBuffer.iChunkOffset,aSize); r = KErrNone; done: // Complete request if there was an error if (r!=KErrNone) Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r); } /** Signal ConvertData request completed */ void DConvert1Channel::ConvertCancel() { // Tell hardware to stop DoConvertCancel(); // Complete client request NKern::ThreadEnterCS(); ConvertComplete(KErrCancel); NKern::ThreadLeaveCS(); } /** DFC callback called after data has been converted. */ void DConvert1Channel::ConvertDfcTrampoline(TAny* aSelf) { // Just call non-static method ((DConvert1Channel*)aSelf)->ConvertDfc(); } /** DFC callback called after data has been converted */ void DConvert1Channel::ConvertDfc() { TRACE(Kern::Printf(">DConvert1Channel::ConvertDfc\n");) // The result value will be the chunk offset of the data we've converted TInt result = iOutBuffer.iChunkOffset; ConvertComplete(result); TRACE(Kern::Printf("<DConvert1Channel::ConvertDfc\n");) } /** Complete a Convert request @pre In thread critical section or DFC thread */ void DConvert1Channel::ConvertComplete(TInt aResult) { // Hold mutex to avoid concurrency NKern::FMWait(&iConvertMutex); // Claim the client request TRequestStatus* status = iConvertRequestStatus; iConvertRequestStatus = NULL; // Claim chunk handle if we need to close it DChunk* chunk = NULL; if(status && iSource==&iClientBuffer) { chunk = iClientBuffer.iChunk; iClientBuffer.iChunk = NULL; } // Clear iSource to help show up bugs iSource = NULL; // Can release mutex now we own the pointers NKern::FMSignal(&iConvertMutex); // Must be in a critical section so we can't die whilst owning 'chunk' and 'status' __ASSERT_CRITICAL; // Close chunk if required if(chunk) Kern::ChunkClose(chunk); // Complete the request if(status) Kern::RequestComplete(iClient,status,aResult); } // // DChunkBuffer // /** Constructor */ DChunkBuffer::DChunkBuffer() : iChunk(NULL), iPhysicalPages(NULL) { } /** Create chunk and commit memory for buffer */ TInt DChunkBuffer::Create(TInt aBufferSize) { // Initialise member data for the size of buffer we want TInt r=SetMaxSize(aBufferSize); if(r!=KErrNone) return r; // Create chunk __NK_ASSERT_DEBUG(!iChunk); TChunkCreateInfo info; info.iType = TChunkCreateInfo::ESharedKernelMultiple; info.iMaxSize = (TInt)aBufferSize; #ifndef __WINS__ info.iMapAttr = EMapAttrCachedMax; #else info.iMapAttr = 0; #endif info.iOwnsMemory = ETrue; r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr); if(r==KErrNone) { // Commit memory to chunk iChunkOffset = 0; r = Kern::ChunkCommit(iChunk,iChunkOffset,Kern::RoundToPageSize(iMaxSize)); if(r==KErrNone) { // Setup physical address info for memory in the buffer r = SetPhysicalAddresses(iMaxSize); } } if(r!=KErrNone) Destroy(); // Cleanup return r; } /** Free all resources */ void DChunkBuffer::Destroy() { delete [] iPhysicalPages; iPhysicalPages = 0; Close(); } /** Destructor */ DChunkBuffer::~DChunkBuffer() { Destroy(); } /** Set maximum size for buffer. (Allocates heap resources for this max size.) */ TInt DChunkBuffer::SetMaxSize(TInt aMaxSize) { // Create array to hold address of physical pages __NK_ASSERT_DEBUG(!iPhysicalPages); iPhysicalPages = new TPhysAddr[Kern::RoundToPageSize(aMaxSize)/Kern::RoundToPageSize(1)+1]; if(!iPhysicalPages) return KErrNoMemory; iMaxSize = aMaxSize; return KErrNone; } /** Open a shared chunk given an user address and siae */ TInt DChunkBuffer::Open(TAny* aAddress, TInt aSize, TBool aWrite) { TInt r; // Check size if(aSize>iMaxSize) return KErrTooBig; NKern::ThreadEnterCS(); // Attempt to open chunk iChunk = Kern::OpenSharedChunk(NULL,aAddress,aWrite,iChunkOffset); if(!iChunk) r = KErrArgument; else { // Get physical addresses r = SetPhysicalAddresses(aSize); if(r!=KErrNone) Close(); } NKern::ThreadLeaveCS(); return r; } /** Open a specified shared chunk */ TInt DChunkBuffer::Open(TInt aChunkHandle, TInt aOffset, TInt aSize, TBool aWrite) { TInt r; // Check size if(aSize>iMaxSize) return KErrTooBig; iChunkOffset = aOffset; NKern::ThreadEnterCS(); // Attempt to open chunk iChunk = Kern::OpenSharedChunk(NULL,aChunkHandle,aWrite); if(!iChunk) r = KErrArgument; else { // Get physical addresses r = SetPhysicalAddresses(aSize); if(r!=KErrNone) Close(); } NKern::ThreadLeaveCS(); return r; } /** Close chunk */ void DChunkBuffer::Close() { __ASSERT_CRITICAL; if(iChunk) { Kern::ChunkClose(iChunk); iChunk = NULL; } } /** Fill buffer by copying data from the given user address */ TInt DChunkBuffer::Copy(TAny* aAddress, TInt aSize) { // Check size if(aSize>iMaxSize) return KErrTooBig; // Copy data kumemget((TAny*)(iChunkBase+iChunkOffset),aAddress,aSize); return KErrNone; } /** Setup physical address info for memory in the buffer */ TInt DChunkBuffer::SetPhysicalAddresses(TInt aSize) { // Assert that the iPhysicalPages array already allocated will be big enough __NK_ASSERT_DEBUG(aSize<=iMaxSize); // Get physical addresses TLinAddr kaddr; TInt r=Kern::ChunkPhysicalAddress(iChunk,iChunkOffset,aSize,kaddr,iChunkMapAttr,iPhysicalAddress,iPhysicalPages); // r = 0 or 1 on success. (1 meaning the physical pages are not contiguous) if(r>=0) { iChunkBase = kaddr-iChunkOffset; // Calculate start of chunk in kernel process address space r = KErrNone; } return r; } // // Program converter hardware // /** Initialise hardware to start converting data. Input data is in iSource. Output data to be placed in iOutBuffer. */ void DConvert1Channel::DoConvertStart(TInt aOffset,TInt aSize) { // For this example test... TRACE(Kern::Printf("DConvert1Channel::DoConvertStart\n");) // 'Convert' data by xoring with 1 TUint8* src = (TUint8*)iSource->iChunkBase+iSource->iChunkOffset+aOffset; TUint8* end = src+aSize; TUint8* dst = (TUint8*)iOutBuffer.iChunkBase+iOutBuffer.iChunkOffset; while(src<end) *dst++ = TUint8(*src++^1); // Start the timer TInt ticks = TInt((TInt64)1000000*(TInt64)aSize/(TInt64)iConfig.iSpeed) /NKern::TickPeriod(); if(ticks<1) ticks = 1; #ifdef _DEBUG TInt r= #endif iConvertTimer.OneShot(ticks,ETrue); __NK_ASSERT_DEBUG(r==KErrNone); } /** Tell hardware to stop converting. */ void DConvert1Channel::DoConvertCancel() { // For this example test... TRACE(Kern::Printf("DConvert1Channel::DoConvertCancel\n");) // Cancel the timer iConvertTimer.Cancel(); }