Update contrib.
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // its implementation.
19 @file An example data converter device driver which uses Shared Chunks in
24 #include <kernel/kern_priv.h>
25 #include <kernel/cache.h>
27 #include "convert1_dev.h"
30 #if 0 // Set true for tracing
37 _LIT(KConvert1PanicCategory,"CONVERT1");
45 Number of hardware 'resources' available to driver.
46 E.g. the number of simultaneous channels it can support.
48 const TInt KTotalConvert1Resources = 4;
51 A resource ID representing no resources
53 const TInt KNullConvert1ResourceId = -1;
56 Standard export function for LDDs. This creates a DLogicalDevice derived object,
57 in this case, our DConvert1Factory
59 DECLARE_STANDARD_LDD()
61 return new DConvert1Factory;
67 DConvert1Factory::DConvert1Factory()
69 // Set version number for this device
70 iVersion=RConvert1::VersionRequired();
71 // Indicate that do support units or a PDD
73 // Mark all resources available
74 iResourceFlags = (1<<KTotalConvert1Resources)-1;
78 Second stage constructor for DConvert1Factory.
79 This must at least set a name for the driver object.
81 @return KErrNone if successful, otherwise one of the other system wide error codes.
83 TInt DConvert1Factory::Install()
85 return SetName(&RConvert1::Name());
91 DConvert1Factory::~DConvert1Factory()
96 Return the drivers capabilities.
97 Called in the response to an RDevice::GetCaps() request.
99 @param aDes User-side descriptor to write capabilities information into
101 void DConvert1Factory::GetCaps(TDes8& aDes) const
103 // Create a capabilities object
104 RConvert1::TCaps caps;
105 caps.iVersion = iVersion;
106 caps.iMaxChannels = KTotalConvert1Resources;
108 // Write it back to user memory
109 Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps));
113 Called by the kernel's device driver framework to create a Logical Channel.
114 This is called in the context of the user thread (client) which requested the creation of a Logical Channel
115 (E.g. through a call to RBusLogicalChannel::DoCreate)
116 The thread is in a critical section.
118 @param aChannel Set to point to the created Logical Channel
120 @return KErrNone if successful, otherwise one of the other system wide error codes.
122 TInt DConvert1Factory::Create(DLogicalChannelBase*& aChannel)
124 aChannel=new DConvert1Channel(this);
132 Claim a hardware resource. This example driver has KTotalConvert1Resources
133 'hardware resources' and returns the ID of the next unallocated one.
135 TInt DConvert1Factory::ClaimResource(TInt& aResourceId)
137 // Wait on mutex protecting resource allocation
138 NKern::FMWait(&iResourceMutex);
140 // Search for a free resource
141 TUint resourceFlags = iResourceFlags;
146 if(resourceFlags&mask)
150 while(++id<KTotalConvert1Resources);
152 if(resourceFlags&mask)
153 iResourceFlags = resourceFlags&~mask; // Found resource, so mark it in use
155 id = KNullConvert1ResourceId; // No resource available
157 // Set returned resource id
160 // Release mutex protecting resource allocation
161 NKern::FMSignal(&iResourceMutex);
163 return id<0 ? KErrInUse : KErrNone;
167 Released the hardware resource indicated by the given id
169 void DConvert1Factory::ReleaseResource(TInt aResourceId)
171 // Do nothing if the null id was given
172 if(aResourceId==KNullConvert1ResourceId)
175 // Wait on mutex protecting resource allocation
176 NKern::FMWait(&iResourceMutex);
178 // Check for valid resource and that it is not already free
179 __NK_ASSERT_DEBUG(TUint(aResourceId)<TUint(KTotalConvert1Resources));
180 __NK_ASSERT_DEBUG((iResourceFlags&(1<<aResourceId))==0);
182 // Flag resource free again
183 iResourceFlags |= 1<<aResourceId;
185 // Release mutex protecting resource allocation
186 NKern::FMSignal(&iResourceMutex);
194 Default configuration (4k buffer, No Input Chunk, 1MB/sec speed)
196 static const RConvert1::TConfig DefaultConfig = {4<<10,EFalse,1<<20};
201 DConvert1Channel::DConvert1Channel(DConvert1Factory* aFactory)
202 : iFactory(aFactory),
203 iResourceId(KNullConvert1ResourceId),
204 iConfig(DefaultConfig),
205 iConvertTimer(ConvertDfcTrampoline,this)
210 Second stage constructor called by the kernel's device driver framework.
211 This is called in the context of the user thread (client) which requested the creation of a Logical Channel
212 (E.g. through a call to RBusLogicalChannel::DoCreate)
213 The thread is in a critical section.
215 @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate
216 @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate
217 @param aVer The version argument supplied by the client to RBusLogicalChannel::DoCreate
219 @return KErrNone if successful, otherwise one of the other system wide error codes.
221 TInt DConvert1Channel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
223 // Check client has EMultimediaDD capability
224 if(!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by CAPTURE1")))
225 return KErrPermissionDenied;
228 if (!Kern::QueryVersionSupported(RConvert1::VersionRequired(),aVer))
229 return KErrNotSupported;
231 // Claim ownership of a hardware resource
232 TInt r=iFactory->ClaimResource(iResourceId);
236 // Set client thread with which channel will be used by
237 iClient = &Kern::CurrentThread();
246 DConvert1Channel::~DConvert1Channel()
248 // Cancel outsatnding requests
249 DoCancel(RConvert1::EAllRequests);
251 // Release hardware resource which we own
252 iFactory->ReleaseResource(iResourceId);
256 Called when a user thread requests a handle to this channel.
258 TInt DConvert1Channel::RequestUserHandle(DThread* aThread, TOwnerType aType)
260 // Make sure that only our client can get a handle
261 if (aType!=EOwnerThread || aThread!=iClient)
262 return KErrAccessDenied;
267 Process a request on this logical channel.
269 @param aReqNo Request number:
270 ==KMaxTInt, a 'DoCancel' message
271 >=0, a 'DoControl' message with function number equal to iValue
272 <0, a 'DoRequest' message with function number equal to ~iValue
273 @param a1 First argument. For DoRequest requests this is a pointer to the TRequestStatus.
274 @param a2 Second argument. For DoRequest this is a pointer to the 2 actual TAny* arguments.
276 @return Result. Ignored by device driver framework for DoRequest requests.
278 TInt DConvert1Channel::Request(TInt aReqNo, TAny* a1, TAny* a2)
280 // Decode the message type and dispatch it to the relevent handler function...
281 if ((TUint)aReqNo<(TUint)KMaxTInt)
282 return DoControl(aReqNo,a1,a2);
284 return DoCancel((TInt)a1);
285 return DoRequest(aReqNo,a1,a2);
289 Process synchronous 'control' requests
291 TInt DConvert1Channel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
293 TRACE(Kern::Printf(">DConvert1Channel::DoControl fn=%d\n",aFunction);)
295 TInt r = KErrNotSupported;
298 case RConvert1::EGetConfig:
299 r = GetConfig((TDes8*)a1);
302 case RConvert1::ESetConfig:
303 r = SetConfig((const TDesC8*)a1,(RConvert1::TBufferInfo*)a2);
306 case RConvert1::EConvertDes:
307 ConvertDes((const TDesC8*)a1,(TRequestStatus*)a2);
310 case RConvert1::EConvertChunk:
311 ConvertChunk((const RConvert1::TConvertArgs*)a1,(TRequestStatus*)a2);
314 case RConvert1::EConvertInChunk:
315 ConvertInChunk((TInt)a1,(TRequestStatus*)a2);
319 TRACE(Kern::Printf("<DConvert1Channel::DoControl result=%d\n",r);)
325 Process asynchronous requests.
326 This driver doesn't have any 'DoRequest' requests because we handle asyncronous
327 requests using 'DoControl' for performance reasons. I.e. to avoid having to read
328 the arguments with kumemget()
330 TInt DConvert1Channel::DoRequest(TInt aNotReqNo, TAny* a1, TAny* a2)
332 TRACE(Kern::Printf(">DConvert1Channel::DoRequest req=%d\n",aNotReqNo);)
336 kumemget32(a,a2,sizeof(a));
337 TRequestStatus* status=(TRequestStatus*)a1;
338 TInt reqNo = ~aNotReqNo;
344 case RConvert1::EConvertDes:
345 case RConvert1::EConvertChunk:
346 case RConvert1::EConvertInChunk:
347 // Not used because we do these asyncronous request as a
348 // DoControl rather than a DoRequest for performance reasons.
351 r = KErrNotSupported;
355 // Complete request if there was an error
357 Kern::RequestComplete(&Kern::CurrentThread(),status,r);
359 TRACE(Kern::Printf("<DConvert1Channel::DoRequest result=%d\n",r);)
361 return KErrNone; // Result is ignored by device driver framework for DoRequest requests
365 Process cancelling of asynchronous requests.
367 TInt DConvert1Channel::DoCancel(TUint aMask)
369 TRACE(Kern::Printf(">DConvert1Channel::DoCancel mask=%08x\n",aMask);)
371 if(aMask&( (1<<RConvert1::EConvertDes) | (1<<RConvert1::EConvertChunk) | (1<<RConvert1::EConvertInChunk) ) )
374 TRACE(Kern::Printf("<DConvert1Channel::DoCancel\n");)
380 // Methods for processing configuration control messages
384 Process a GetConfig control message. This writes the current driver configuration to a
385 RConvert1::TConfigBuf supplied by the client.
387 TInt DConvert1Channel::GetConfig(TDes8* aConfigBuf)
389 // Write the config to the client
390 Kern::InfoCopy(*aConfigBuf,(const TUint8*)&iConfig,sizeof(iConfig));
395 Process a SetConfig control message. This sets the driver configuration using a
396 RConvert1::TConfigBuf supplied by the client.
398 TInt DConvert1Channel::SetConfig(const TDesC8* aConfigBuf,RConvert1::TBufferInfo* aBufferInfo)
400 // Create a config structure.
401 RConvert1::TConfig config(DefaultConfig);
403 // Note: We have constructed a config using DefaultConfig, this is to allow
404 // backwards compatibility when a client gives us an old (and shorter) version
405 // of the config structure.
407 // Read the config structure from client
408 TPtr8 ptr((TUint8*)&config,sizeof(config));
409 Kern::KUDesGet(ptr,*aConfigBuf);
411 // 'info' is the data we will return to client at the end
412 RConvert1::TBufferInfo info;
413 memclr(&info,sizeof(info));
417 // Need to be in critical section whilst allocating objects
418 NKern::ThreadEnterCS();
420 // Check we aren't in the middle of converting data
421 if(iConvertRequestStatus)
427 // Note: The above check is enough to ensure we have exclusive access
428 // to this channels buffer and hardware resources because:
429 // 1. The covert DFC can't run because we haven't started converting yet.
430 // 2. No other request can come in because the channel only allows one
431 // client thread to use it. See DConvert1Channel::Request()
432 // 3. The channel destructor can't be called whilst we are processing a request.
435 // For some settings we allow zero to mean default...
436 if(!config.iBufferSize)
437 config.iBufferSize = DefaultConfig.iBufferSize;
439 config.iSpeed = DefaultConfig.iSpeed;
441 // Validate configuration
442 if(config.iBufferSize<=0)
458 // Calculate buffer size
459 TInt bufferSize = Kern::RoundToPageSize(config.iBufferSize);
461 // Destroy old buffers
462 iClientBuffer.Destroy();
464 iOutBuffer.Destroy();
466 // Setup iClientBuffer
467 r = iClientBuffer.SetMaxSize(bufferSize);
471 // Create output buffer
472 r = iOutBuffer.Create(bufferSize);
475 // Make handle for output buffer
476 r = Kern::MakeHandleAndOpen(NULL, iOutBuffer.iChunk);
477 if(r<0) // -ve value is error, +ve value is a handle
479 info.iOutChunkHandle = r;
482 // Create input buffer if requested
483 if(iConfig.iCreateInputChunk)
485 r = iInBuffer.Create(bufferSize);
488 // Make handle for input buffer
489 r = Kern::MakeHandleAndOpen(NULL, iInBuffer.iChunk);
490 if(r<0) // -ve value is error, +ve value is a handle
492 info.iInChunkHandle = r;
494 // Set info about input buffer
496 // Note we don't set iInBufferPtr because this is the address in
497 // client process which it must set for itself
498 info.iInBufferOffset = iInBuffer.iChunkOffset;
499 info.iInBufferSize = iInBuffer.iMaxSize;
503 // Cleanup if there was an error
506 iClientBuffer.Destroy();
508 iOutBuffer.Destroy();
509 if(info.iOutChunkHandle)
510 Kern::CloseHandle(NULL,info.iOutChunkHandle);
511 if(info.iInChunkHandle)
512 Kern::CloseHandle(NULL,info.iInChunkHandle);
513 memclr(&info,sizeof(info));
516 NKern::ThreadLeaveCS();
518 // Write chunk handles and other info back to client memory
519 kumemput32(aBufferInfo,&info,sizeof(info));
525 // Methods for processing Convert requests
529 Process Convert request where the source data is specified by a descriptor
531 void DConvert1Channel::ConvertDes(const TDesC8* aSrc,TRequestStatus* aRequestStatus)
535 // Get descriptor info
538 TAny* uptr = (TAny*)Kern::KUDesInfo(*aSrc,len,maxLen);
540 // Check there isn't an outstanding request
541 if(iConvertRequestStatus)
542 Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
544 // Check output buffer has been created
545 if(!iOutBuffer.iChunk)
551 // Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
552 if(!iInBuffer.iChunk)
554 r = KErrNotSupported;
558 // See if client data is in a shared chunk
559 r = iClientBuffer.Open(uptr,len);
561 iSource = &iClientBuffer; // use iClientBuffer as input buffer
564 // Copy data from client descriptor into our iInBuffer
565 r = iInBuffer.Copy(uptr,len);
567 iSource = &iInBuffer; // use iInBuffer as input buffer
570 // Start convert if no error
573 iConvertRequestStatus = aRequestStatus;
574 DoConvertStart(0,len);
577 // Complete request if there was an error
579 Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
583 Process Convert request where the source data is specified by a chunk
585 void DConvert1Channel::ConvertChunk(const RConvert1::TConvertArgs* aSrcArgs,TRequestStatus* aRequestStatus)
589 // Check there isn't an outstanding request
590 if(iConvertRequestStatus)
591 Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
593 // Check output buffer has been created
594 if(!iOutBuffer.iChunk)
600 // Unpackage arguments
601 RConvert1::TConvertArgs args;
602 kumemget32(&args,aSrcArgs,sizeof(args));
604 // Make buffer by opening chunk
605 r=iClientBuffer.Open(args.iChunkHandle,args.iOffset,args.iSize);
607 // Start convert if no error
610 iSource = &iClientBuffer;
611 iConvertRequestStatus = aRequestStatus;
612 DoConvertStart(0,args.iSize);
615 // Complete request if there was an error
617 Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
621 Process Convert request where the source data is contained in the input chunk
623 void DConvert1Channel::ConvertInChunk(TInt aSize,TRequestStatus* aRequestStatus)
627 // Check there isn't an outstanding request
628 if(iConvertRequestStatus)
629 Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
631 // Check output buffer has been created
632 if(!iOutBuffer.iChunk)
638 // Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
639 if(!iInBuffer.iChunk)
641 r = KErrNotSupported;
645 // Check size of data really fits within chunk
646 if(TUint(aSize)>=TUint(iInBuffer.iMaxSize))
653 iSource = &iInBuffer;
654 iConvertRequestStatus = aRequestStatus;
655 DoConvertStart(iInBuffer.iChunkOffset,aSize);
659 // Complete request if there was an error
661 Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
665 Signal ConvertData request completed
667 void DConvert1Channel::ConvertCancel()
669 // Tell hardware to stop
672 // Complete client request
673 NKern::ThreadEnterCS();
674 ConvertComplete(KErrCancel);
675 NKern::ThreadLeaveCS();
679 DFC callback called after data has been converted.
681 void DConvert1Channel::ConvertDfcTrampoline(TAny* aSelf)
683 // Just call non-static method
684 ((DConvert1Channel*)aSelf)->ConvertDfc();
688 DFC callback called after data has been converted
690 void DConvert1Channel::ConvertDfc()
692 TRACE(Kern::Printf(">DConvert1Channel::ConvertDfc\n");)
694 // The result value will be the chunk offset of the data we've converted
695 TInt result = iOutBuffer.iChunkOffset;
696 ConvertComplete(result);
698 TRACE(Kern::Printf("<DConvert1Channel::ConvertDfc\n");)
702 Complete a Convert request
703 @pre In thread critical section or DFC thread
705 void DConvert1Channel::ConvertComplete(TInt aResult)
707 // Hold mutex to avoid concurrency
708 NKern::FMWait(&iConvertMutex);
710 // Claim the client request
711 TRequestStatus* status = iConvertRequestStatus;
712 iConvertRequestStatus = NULL;
714 // Claim chunk handle if we need to close it
715 DChunk* chunk = NULL;
716 if(status && iSource==&iClientBuffer)
718 chunk = iClientBuffer.iChunk;
719 iClientBuffer.iChunk = NULL;
722 // Clear iSource to help show up bugs
725 // Can release mutex now we own the pointers
726 NKern::FMSignal(&iConvertMutex);
728 // Must be in a critical section so we can't die whilst owning 'chunk' and 'status'
731 // Close chunk if required
733 Kern::ChunkClose(chunk);
735 // Complete the request
737 Kern::RequestComplete(iClient,status,aResult);
748 DChunkBuffer::DChunkBuffer()
749 : iChunk(NULL), iPhysicalPages(NULL)
754 Create chunk and commit memory for buffer
756 TInt DChunkBuffer::Create(TInt aBufferSize)
758 // Initialise member data for the size of buffer we want
759 TInt r=SetMaxSize(aBufferSize);
764 __NK_ASSERT_DEBUG(!iChunk);
765 TChunkCreateInfo info;
766 info.iType = TChunkCreateInfo::ESharedKernelMultiple;
767 info.iMaxSize = (TInt)aBufferSize;
769 info.iMapAttr = EMapAttrCachedMax;
773 info.iOwnsMemory = ETrue;
774 r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr);
778 // Commit memory to chunk
780 r = Kern::ChunkCommit(iChunk,iChunkOffset,Kern::RoundToPageSize(iMaxSize));
783 // Setup physical address info for memory in the buffer
784 r = SetPhysicalAddresses(iMaxSize);
789 Destroy(); // Cleanup
797 void DChunkBuffer::Destroy()
799 delete [] iPhysicalPages;
807 DChunkBuffer::~DChunkBuffer()
813 Set maximum size for buffer.
814 (Allocates heap resources for this max size.)
816 TInt DChunkBuffer::SetMaxSize(TInt aMaxSize)
818 // Create array to hold address of physical pages
819 __NK_ASSERT_DEBUG(!iPhysicalPages);
820 iPhysicalPages = new TPhysAddr[Kern::RoundToPageSize(aMaxSize)/Kern::RoundToPageSize(1)+1];
829 Open a shared chunk given an user address and siae
831 TInt DChunkBuffer::Open(TAny* aAddress, TInt aSize, TBool aWrite)
839 NKern::ThreadEnterCS();
841 // Attempt to open chunk
842 iChunk = Kern::OpenSharedChunk(NULL,aAddress,aWrite,iChunkOffset);
847 // Get physical addresses
848 r = SetPhysicalAddresses(aSize);
853 NKern::ThreadLeaveCS();
859 Open a specified shared chunk
861 TInt DChunkBuffer::Open(TInt aChunkHandle, TInt aOffset, TInt aSize, TBool aWrite)
868 iChunkOffset = aOffset;
870 NKern::ThreadEnterCS();
872 // Attempt to open chunk
873 iChunk = Kern::OpenSharedChunk(NULL,aChunkHandle,aWrite);
878 // Get physical addresses
879 r = SetPhysicalAddresses(aSize);
884 NKern::ThreadLeaveCS();
892 void DChunkBuffer::Close()
897 Kern::ChunkClose(iChunk);
903 Fill buffer by copying data from the given user address
905 TInt DChunkBuffer::Copy(TAny* aAddress, TInt aSize)
912 kumemget((TAny*)(iChunkBase+iChunkOffset),aAddress,aSize);
918 Setup physical address info for memory in the buffer
920 TInt DChunkBuffer::SetPhysicalAddresses(TInt aSize)
922 // Assert that the iPhysicalPages array already allocated will be big enough
923 __NK_ASSERT_DEBUG(aSize<=iMaxSize);
925 // Get physical addresses
927 TInt r=Kern::ChunkPhysicalAddress(iChunk,iChunkOffset,aSize,kaddr,iChunkMapAttr,iPhysicalAddress,iPhysicalPages);
928 // r = 0 or 1 on success. (1 meaning the physical pages are not contiguous)
931 iChunkBase = kaddr-iChunkOffset; // Calculate start of chunk in kernel process address space
938 // Program converter hardware
942 Initialise hardware to start converting data.
943 Input data is in iSource.
944 Output data to be placed in iOutBuffer.
946 void DConvert1Channel::DoConvertStart(TInt aOffset,TInt aSize)
948 // For this example test...
950 TRACE(Kern::Printf("DConvert1Channel::DoConvertStart\n");)
952 // 'Convert' data by xoring with 1
953 TUint8* src = (TUint8*)iSource->iChunkBase+iSource->iChunkOffset+aOffset;
954 TUint8* end = src+aSize;
955 TUint8* dst = (TUint8*)iOutBuffer.iChunkBase+iOutBuffer.iChunkOffset;
957 *dst++ = TUint8(*src++^1);
960 TInt ticks = TInt((TInt64)1000000*(TInt64)aSize/(TInt64)iConfig.iSpeed)
961 /NKern::TickPeriod();
967 iConvertTimer.OneShot(ticks,ETrue);
968 __NK_ASSERT_DEBUG(r==KErrNone);
972 Tell hardware to stop converting.
974 void DConvert1Channel::DoConvertCancel()
976 // For this example test...
978 TRACE(Kern::Printf("DConvert1Channel::DoConvertCancel\n");)
981 iConvertTimer.Cancel();