os/kernelhwsrv/kerneltest/e32test/examples/convert1/convert1_ldd.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
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".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // its implementation.
    15 // 
    16 //
    17 
    18 /**
    19  @file An example data converter device driver which uses Shared Chunks in
    20  @publishedPartner
    21  @prototype 9.1
    22 */
    23 
    24 #include <kernel/kern_priv.h>
    25 #include <kernel/cache.h>
    26 #include "convert1.h"
    27 #include "convert1_dev.h"
    28 
    29 
    30 #if 0  // Set true for tracing
    31 #define TRACE(x) x
    32 #else
    33 #define TRACE(x)
    34 #endif
    35 
    36 
    37 _LIT(KConvert1PanicCategory,"CONVERT1");
    38 
    39 
    40 //
    41 // DConvert1Factory
    42 //
    43 
    44 /**
    45   Number of hardware 'resources' available to driver.
    46   E.g. the number of simultaneous channels it can support.
    47 */
    48 const TInt KTotalConvert1Resources = 4;
    49 
    50 /**
    51   A resource ID representing no resources
    52 */
    53 const TInt KNullConvert1ResourceId = -1;
    54 
    55 /**
    56   Standard export function for LDDs. This creates a DLogicalDevice derived object,
    57   in this case, our DConvert1Factory
    58 */
    59 DECLARE_STANDARD_LDD()
    60 	{
    61 	return new DConvert1Factory;
    62 	}
    63 
    64 /**
    65   Constructor
    66 */
    67 DConvert1Factory::DConvert1Factory()
    68 	{
    69 	// Set version number for this device
    70 	iVersion=RConvert1::VersionRequired();
    71 	// Indicate that do support units or a PDD
    72 	iParseMask=0;
    73 	// Mark all resources available
    74 	iResourceFlags = (1<<KTotalConvert1Resources)-1;
    75 	}
    76 
    77 /**
    78   Second stage constructor for DConvert1Factory.
    79   This must at least set a name for the driver object.
    80 
    81   @return KErrNone if successful, otherwise one of the other system wide error codes.
    82 */
    83 TInt DConvert1Factory::Install()
    84 	{
    85 	return SetName(&RConvert1::Name());
    86 	}
    87 
    88 /**
    89   Destructor
    90 */
    91 DConvert1Factory::~DConvert1Factory()
    92 	{
    93 	}
    94 
    95 /**
    96   Return the drivers capabilities.
    97   Called in the response to an RDevice::GetCaps() request.
    98 
    99   @param aDes User-side descriptor to write capabilities information into
   100 */
   101 void DConvert1Factory::GetCaps(TDes8& aDes) const
   102 	{
   103 	// Create a capabilities object
   104 	RConvert1::TCaps caps;
   105 	caps.iVersion = iVersion;
   106 	caps.iMaxChannels = KTotalConvert1Resources;
   107 
   108 	// Write it back to user memory
   109 	Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps));
   110 	}
   111 
   112 /**
   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.
   117 
   118   @param aChannel Set to point to the created Logical Channel
   119 
   120   @return KErrNone if successful, otherwise one of the other system wide error codes.
   121 */
   122 TInt DConvert1Factory::Create(DLogicalChannelBase*& aChannel)
   123 	{
   124 	aChannel=new DConvert1Channel(this);
   125 	if(!aChannel)
   126 		return KErrNoMemory;
   127 
   128 	return KErrNone;
   129 	}
   130 
   131 /**
   132   Claim a hardware resource. This example driver has KTotalConvert1Resources
   133   'hardware resources' and returns the ID of the next unallocated one.
   134 */
   135 TInt DConvert1Factory::ClaimResource(TInt& aResourceId)
   136 	{
   137 	// Wait on mutex protecting resource allocation
   138 	NKern::FMWait(&iResourceMutex);
   139 
   140 	// Search for a free resource
   141 	TUint resourceFlags = iResourceFlags;
   142 	TUint mask = 1;
   143 	TInt id = 0;
   144 	do
   145 		{
   146 		if(resourceFlags&mask)
   147 			break;
   148 		mask <<= 1;
   149 		}
   150 	while(++id<KTotalConvert1Resources);
   151 
   152 	if(resourceFlags&mask)
   153 		iResourceFlags = resourceFlags&~mask; // Found resource, so mark it in use
   154 	else
   155 		id = KNullConvert1ResourceId; // No resource available
   156 
   157 	// Set returned resource id
   158 	aResourceId = id;
   159 
   160 	// Release mutex protecting resource allocation
   161 	NKern::FMSignal(&iResourceMutex);
   162 
   163 	return id<0 ? KErrInUse : KErrNone;
   164 	}
   165 
   166 /**
   167   Released the hardware resource indicated by the given id
   168 */
   169 void DConvert1Factory::ReleaseResource(TInt aResourceId)
   170 	{
   171 	// Do nothing if the null id was given
   172 	if(aResourceId==KNullConvert1ResourceId)
   173 		return;
   174 
   175 	// Wait on mutex protecting resource allocation
   176 	NKern::FMWait(&iResourceMutex);
   177 
   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);
   181 
   182 	// Flag resource free again
   183 	iResourceFlags |= 1<<aResourceId;
   184 
   185 	// Release mutex protecting resource allocation
   186 	NKern::FMSignal(&iResourceMutex);
   187 	}
   188 
   189 //
   190 // Logical Channel
   191 //
   192 
   193 /**
   194   Default configuration (4k buffer, No Input Chunk, 1MB/sec speed)
   195 */
   196 static const RConvert1::TConfig DefaultConfig = {4<<10,EFalse,1<<20};
   197 
   198 /**
   199   Constructor
   200 */
   201 DConvert1Channel::DConvert1Channel(DConvert1Factory* aFactory)
   202 	:	iFactory(aFactory), 
   203 		iResourceId(KNullConvert1ResourceId),
   204 		iConfig(DefaultConfig),
   205 		iConvertTimer(ConvertDfcTrampoline,this)
   206 	{
   207 	}
   208 
   209 /**
   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.
   214 
   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
   218 
   219   @return KErrNone if successful, otherwise one of the other system wide error codes.
   220 */
   221 TInt DConvert1Channel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
   222 	{
   223 	// Check client has EMultimediaDD capability
   224 	if(!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by CAPTURE1")))
   225 		return KErrPermissionDenied;
   226 
   227 	// Check version
   228 	if (!Kern::QueryVersionSupported(RConvert1::VersionRequired(),aVer))
   229 		return KErrNotSupported;
   230 
   231 	// Claim ownership of a hardware resource
   232 	TInt r=iFactory->ClaimResource(iResourceId);
   233 	if(r!=KErrNone)
   234 		return r;
   235 
   236 	// Set client thread with which channel will be used by
   237 	iClient = &Kern::CurrentThread();
   238 
   239 	// Done
   240 	return KErrNone;
   241 	}
   242 
   243 /**
   244   Destructor
   245 */
   246 DConvert1Channel::~DConvert1Channel()
   247 	{
   248 	// Cancel outsatnding requests
   249 	DoCancel(RConvert1::EAllRequests);
   250 
   251 	// Release hardware resource which we own
   252 	iFactory->ReleaseResource(iResourceId);
   253 	}
   254 
   255 /**
   256   Called when a user thread requests a handle to this channel.
   257 */
   258 TInt DConvert1Channel::RequestUserHandle(DThread* aThread, TOwnerType aType)
   259 	{
   260 	// Make sure that only our client can get a handle
   261 	if (aType!=EOwnerThread || aThread!=iClient)
   262 		return KErrAccessDenied;
   263 	return KErrNone;
   264 	}
   265 
   266 /**
   267   Process a request on this logical channel.
   268 
   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.
   275 
   276   @return       Result. Ignored by device driver framework for DoRequest requests.
   277 */
   278 TInt DConvert1Channel::Request(TInt aReqNo, TAny* a1, TAny* a2)
   279 	{
   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);
   283 	if(aReqNo==KMaxTInt)
   284 		return DoCancel((TInt)a1);
   285 	return DoRequest(aReqNo,a1,a2);
   286 	}
   287 
   288 /**
   289   Process synchronous 'control' requests
   290 */
   291 TInt DConvert1Channel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
   292 	{
   293 	TRACE(Kern::Printf(">DConvert1Channel::DoControl fn=%d\n",aFunction);)
   294 
   295 	TInt r = KErrNotSupported;
   296 	switch (aFunction)
   297 		{
   298 		case RConvert1::EGetConfig:
   299 			r = GetConfig((TDes8*)a1);
   300 			break;
   301 
   302 		case RConvert1::ESetConfig:
   303 			r = SetConfig((const TDesC8*)a1,(RConvert1::TBufferInfo*)a2);
   304 			break;
   305 
   306 		case RConvert1::EConvertDes:
   307 			ConvertDes((const TDesC8*)a1,(TRequestStatus*)a2);
   308 			break;
   309 
   310 		case RConvert1::EConvertChunk:
   311 			ConvertChunk((const RConvert1::TConvertArgs*)a1,(TRequestStatus*)a2);
   312 			break;
   313 
   314 		case RConvert1::EConvertInChunk:
   315 			ConvertInChunk((TInt)a1,(TRequestStatus*)a2);
   316 			break;
   317 		}
   318 
   319 	TRACE(Kern::Printf("<DConvert1Channel::DoControl result=%d\n",r);)
   320 
   321 	return r;
   322 	}
   323 
   324 /**
   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()
   329 */
   330 TInt DConvert1Channel::DoRequest(TInt aNotReqNo, TAny* a1, TAny* a2)
   331 	{
   332 	TRACE(Kern::Printf(">DConvert1Channel::DoRequest req=%d\n",aNotReqNo);)
   333 
   334 	// Get arguments
   335 	TAny* a[2];
   336 	kumemget32(a,a2,sizeof(a)); 
   337 	TRequestStatus* status=(TRequestStatus*)a1;
   338 	TInt reqNo = ~aNotReqNo;
   339 
   340 	// Do the request
   341 	TInt r;
   342 	switch(reqNo)
   343 		{
   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.
   349 
   350 		default:
   351 			r = KErrNotSupported;
   352 			break;
   353 		}
   354 
   355 	// Complete request if there was an error
   356 	if (r!=KErrNone)
   357 		Kern::RequestComplete(&Kern::CurrentThread(),status,r);
   358 
   359 	TRACE(Kern::Printf("<DConvert1Channel::DoRequest result=%d\n",r);)
   360 
   361 	return KErrNone;  // Result is ignored by device driver framework for DoRequest requests
   362 	}
   363 
   364 /**
   365   Process cancelling of asynchronous requests.
   366 */
   367 TInt DConvert1Channel::DoCancel(TUint aMask)
   368 	{
   369 	TRACE(Kern::Printf(">DConvert1Channel::DoCancel mask=%08x\n",aMask);)
   370 
   371 	if(aMask&( (1<<RConvert1::EConvertDes) | (1<<RConvert1::EConvertChunk) | (1<<RConvert1::EConvertInChunk) ) )
   372 		ConvertCancel();
   373 
   374 	TRACE(Kern::Printf("<DConvert1Channel::DoCancel\n");)
   375 
   376 	return KErrNone;
   377 	}
   378 
   379 //
   380 // Methods for processing configuration control messages
   381 //
   382 
   383 /**
   384   Process a GetConfig control message. This writes the current driver configuration to a
   385   RConvert1::TConfigBuf supplied by the client.
   386 */
   387 TInt DConvert1Channel::GetConfig(TDes8* aConfigBuf)
   388 	{
   389 	// Write the config to the client
   390 	Kern::InfoCopy(*aConfigBuf,(const TUint8*)&iConfig,sizeof(iConfig));
   391 	return KErrNone;
   392 	}
   393 
   394 /**
   395   Process a SetConfig control message. This sets the driver configuration using a
   396   RConvert1::TConfigBuf supplied by the client.
   397 */
   398 TInt DConvert1Channel::SetConfig(const TDesC8* aConfigBuf,RConvert1::TBufferInfo* aBufferInfo)
   399 	{
   400 	// Create a config structure.
   401 	RConvert1::TConfig config(DefaultConfig);
   402 
   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.
   406 
   407 	// Read the config structure from client
   408 	TPtr8 ptr((TUint8*)&config,sizeof(config));
   409 	Kern::KUDesGet(ptr,*aConfigBuf);
   410 
   411 	// 'info' is the data we will return to client at the end
   412 	RConvert1::TBufferInfo info;
   413 	memclr(&info,sizeof(info));
   414 
   415 	TInt r;
   416 
   417 	// Need to be in critical section whilst allocating objects
   418 	NKern::ThreadEnterCS();
   419 
   420 	// Check we aren't in the middle of converting data
   421 	if(iConvertRequestStatus)
   422 		{
   423 		r = KErrInUse;
   424 		goto done;
   425 		}
   426 
   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.
   433 
   434 
   435 	// For some settings we allow zero to mean default...
   436 	if(!config.iBufferSize)
   437 		config.iBufferSize = DefaultConfig.iBufferSize;
   438 	if(!config.iSpeed)
   439 		config.iSpeed = DefaultConfig.iSpeed;
   440 
   441 	// Validate configuration
   442 	if(config.iBufferSize<=0)
   443 		{
   444 		r = KErrArgument;
   445 		goto done;
   446 		}
   447 	if(config.iSpeed<=0)
   448 		{
   449 		r = KErrArgument;
   450 		goto done;
   451 		}
   452 
   453 	// Change the config
   454 	iConfig = config; 
   455 
   456 	{
   457 
   458 	// Calculate buffer size
   459 	TInt bufferSize = Kern::RoundToPageSize(config.iBufferSize);
   460 
   461 	// Destroy old buffers
   462 	iClientBuffer.Destroy();
   463 	iInBuffer.Destroy();
   464 	iOutBuffer.Destroy();
   465 
   466 	// Setup iClientBuffer
   467 	r = iClientBuffer.SetMaxSize(bufferSize);
   468 	if(r!=KErrNone)
   469 		goto done;
   470 
   471 	// Create output buffer
   472 	r = iOutBuffer.Create(bufferSize);
   473 	if(r!=KErrNone)
   474 		goto done;
   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
   478 		goto done;
   479 	info.iOutChunkHandle = r;
   480 	r = KErrNone;
   481 
   482 	// Create input buffer if requested
   483 	if(iConfig.iCreateInputChunk)
   484 		{
   485 		r = iInBuffer.Create(bufferSize);
   486 		if(r!=KErrNone)
   487 			goto done;
   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
   491 			goto done;
   492 		info.iInChunkHandle = r;
   493 		r = KErrNone;
   494 		// Set info about input buffer
   495 		//
   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;
   500 		}
   501 	}
   502 done:
   503 	// Cleanup if there was an error
   504 	if(r!=KErrNone)
   505 		{
   506 		iClientBuffer.Destroy();
   507 		iInBuffer.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));
   514 		}
   515 
   516 	NKern::ThreadLeaveCS();
   517 
   518 	// Write chunk handles and other info back to client memory
   519 	kumemput32(aBufferInfo,&info,sizeof(info));
   520 
   521 	return r;
   522 	}
   523 
   524 //
   525 // Methods for processing Convert requests
   526 //
   527 
   528 /**
   529   Process Convert request where the source data is specified by a descriptor
   530 */
   531 void DConvert1Channel::ConvertDes(const TDesC8* aSrc,TRequestStatus* aRequestStatus)
   532 	{
   533 	TInt r;
   534 
   535 	// Get descriptor info
   536 	TInt len;
   537 	TInt maxLen;
   538 	TAny* uptr = (TAny*)Kern::KUDesInfo(*aSrc,len,maxLen);
   539 
   540 	// Check there isn't an outstanding request
   541 	if(iConvertRequestStatus)
   542 		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
   543 
   544 	// Check output buffer has been created
   545 	if(!iOutBuffer.iChunk)
   546 		{
   547 		r = KErrNotReady;
   548 		goto done;
   549 		}
   550 
   551 	// Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
   552 	if(!iInBuffer.iChunk)
   553 		{
   554 		r = KErrNotSupported;
   555 		goto done;
   556 		}
   557 
   558 	// See if client data is in a shared chunk
   559 	r = iClientBuffer.Open(uptr,len);
   560 	if(r==KErrNone)
   561 		iSource = &iClientBuffer; // use iClientBuffer as input buffer
   562 	else
   563 		{
   564 		// Copy data from client descriptor into our iInBuffer
   565 		r = iInBuffer.Copy(uptr,len);
   566 		if(r==KErrNone)
   567 			iSource = &iInBuffer; // use iInBuffer as input buffer
   568 		}
   569 
   570 	// Start convert if no error
   571 	if(r==KErrNone)
   572 		{
   573 		iConvertRequestStatus = aRequestStatus;
   574 		DoConvertStart(0,len);
   575 		}
   576 done:
   577 	// Complete request if there was an error
   578 	if (r!=KErrNone)
   579 		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
   580 	}
   581 
   582 /**
   583   Process Convert request where the source data is specified by a chunk
   584 */
   585 void DConvert1Channel::ConvertChunk(const RConvert1::TConvertArgs* aSrcArgs,TRequestStatus* aRequestStatus)
   586 	{
   587 	TInt r;
   588 
   589 	// Check there isn't an outstanding request
   590 	if(iConvertRequestStatus)
   591 		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
   592 
   593 	// Check output buffer has been created
   594 	if(!iOutBuffer.iChunk)
   595 		{
   596 		r = KErrNotReady;
   597 		goto done;
   598 		}
   599 
   600 	// Unpackage arguments
   601 	RConvert1::TConvertArgs args;
   602 	kumemget32(&args,aSrcArgs,sizeof(args));
   603 
   604 	// Make buffer by opening chunk
   605 	r=iClientBuffer.Open(args.iChunkHandle,args.iOffset,args.iSize);
   606 
   607 	// Start convert if no error
   608 	if(r==KErrNone)
   609 		{
   610 		iSource = &iClientBuffer;
   611 		iConvertRequestStatus = aRequestStatus;
   612 		DoConvertStart(0,args.iSize);
   613 		}
   614 done:
   615 	// Complete request if there was an error
   616 	if (r!=KErrNone)
   617 		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
   618 	}
   619 
   620 /**
   621   Process Convert request where the source data is contained in the input chunk
   622 */
   623 void DConvert1Channel::ConvertInChunk(TInt aSize,TRequestStatus* aRequestStatus)
   624 	{
   625 	TInt r;
   626 
   627 	// Check there isn't an outstanding request
   628 	if(iConvertRequestStatus)
   629 		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
   630 
   631 	// Check output buffer has been created
   632 	if(!iOutBuffer.iChunk)
   633 		{
   634 		r = KErrNotReady;
   635 		goto done;
   636 		}
   637 
   638 	// Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
   639 	if(!iInBuffer.iChunk)
   640 		{
   641 		r = KErrNotSupported;
   642 		goto done;
   643 		}
   644 
   645 	// Check size of data really fits within chunk
   646 	if(TUint(aSize)>=TUint(iInBuffer.iMaxSize))
   647 		{
   648 		r = KErrArgument;
   649 		goto done;
   650 		}
   651 
   652 	// Start the convert 
   653 	iSource = &iInBuffer;
   654 	iConvertRequestStatus = aRequestStatus;
   655 	DoConvertStart(iInBuffer.iChunkOffset,aSize);
   656 	r = KErrNone;
   657 
   658 done:
   659 	// Complete request if there was an error
   660 	if (r!=KErrNone)
   661 		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
   662 	}
   663 
   664 /**
   665   Signal ConvertData request completed
   666 */
   667 void DConvert1Channel::ConvertCancel()
   668 	{
   669 	// Tell hardware to stop
   670 	DoConvertCancel();
   671 
   672 	// Complete client request
   673 	NKern::ThreadEnterCS();
   674 	ConvertComplete(KErrCancel);
   675 	NKern::ThreadLeaveCS();
   676 	}
   677 
   678 /**
   679   DFC callback called after data has been converted.
   680 */
   681 void DConvert1Channel::ConvertDfcTrampoline(TAny* aSelf)
   682 	{
   683 	// Just call non-static method
   684 	((DConvert1Channel*)aSelf)->ConvertDfc();
   685 	}
   686 
   687 /**
   688   DFC callback called after data has been converted
   689 */
   690 void DConvert1Channel::ConvertDfc()
   691 	{
   692 	TRACE(Kern::Printf(">DConvert1Channel::ConvertDfc\n");)
   693 
   694 	// The result value will be the chunk offset of the data we've converted
   695 	TInt result = iOutBuffer.iChunkOffset;
   696 	ConvertComplete(result);
   697 
   698 	TRACE(Kern::Printf("<DConvert1Channel::ConvertDfc\n");)
   699 	}
   700 
   701 /**
   702   Complete a Convert request
   703   @pre In thread critical section or DFC thread
   704 */
   705 void DConvert1Channel::ConvertComplete(TInt aResult)
   706 	{
   707 	// Hold mutex to avoid concurrency
   708 	NKern::FMWait(&iConvertMutex);
   709 
   710 	// Claim the client request
   711 	TRequestStatus* status = iConvertRequestStatus;
   712 	iConvertRequestStatus = NULL;
   713 
   714 	// Claim chunk handle if we need to close it
   715 	DChunk* chunk = NULL;
   716 	if(status && iSource==&iClientBuffer)
   717 		{
   718 		chunk = iClientBuffer.iChunk;
   719 		iClientBuffer.iChunk = NULL;
   720 		}
   721 
   722 	// Clear iSource to help show up bugs
   723 	iSource = NULL;
   724 
   725 	// Can release mutex now we own the pointers
   726 	NKern::FMSignal(&iConvertMutex);
   727 
   728 	// Must be in a critical section so we can't die whilst owning 'chunk' and 'status'
   729 	__ASSERT_CRITICAL;
   730 
   731 	// Close chunk if required
   732 	if(chunk)
   733 		Kern::ChunkClose(chunk);
   734 
   735 	// Complete the request
   736 	if(status)
   737 		Kern::RequestComplete(iClient,status,aResult);
   738 	}
   739 
   740 
   741 //
   742 // DChunkBuffer
   743 //
   744 
   745 /**
   746   Constructor
   747 */
   748 DChunkBuffer::DChunkBuffer()
   749 	: iChunk(NULL), iPhysicalPages(NULL)
   750 	{
   751 	}
   752 
   753 /**
   754   Create chunk and commit memory for buffer
   755 */
   756 TInt DChunkBuffer::Create(TInt aBufferSize)
   757 	{
   758 	// Initialise member data for the size of buffer we want
   759 	TInt r=SetMaxSize(aBufferSize);
   760 	if(r!=KErrNone)
   761 		return r;
   762 
   763 	// Create chunk
   764 	__NK_ASSERT_DEBUG(!iChunk);
   765 	TChunkCreateInfo info;
   766 	info.iType = TChunkCreateInfo::ESharedKernelMultiple;
   767 	info.iMaxSize = (TInt)aBufferSize;
   768 #ifndef __WINS__
   769 	info.iMapAttr = EMapAttrCachedMax;
   770 #else
   771 	info.iMapAttr = 0;
   772 #endif
   773 	info.iOwnsMemory = ETrue;
   774 	r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr);
   775 
   776 	if(r==KErrNone)
   777 		{
   778 		// Commit memory to chunk
   779 		iChunkOffset = 0;
   780 		r = Kern::ChunkCommit(iChunk,iChunkOffset,Kern::RoundToPageSize(iMaxSize));
   781 		if(r==KErrNone)
   782 			{
   783 			// Setup physical address info for memory in the buffer
   784 			r = SetPhysicalAddresses(iMaxSize);
   785 			}
   786 		}
   787 
   788 	if(r!=KErrNone)
   789 		Destroy(); // Cleanup
   790 
   791 	return r;
   792 	}
   793 
   794 /**
   795   Free all resources
   796 */
   797 void DChunkBuffer::Destroy()
   798 	{
   799 	delete [] iPhysicalPages;
   800 	iPhysicalPages = 0;
   801 	Close();
   802 	}
   803 
   804 /**
   805   Destructor
   806 */
   807 DChunkBuffer::~DChunkBuffer()
   808 	{
   809 	Destroy();
   810 	}
   811 
   812 /**
   813   Set maximum size for buffer.
   814   (Allocates heap resources for this max size.)
   815 */
   816 TInt DChunkBuffer::SetMaxSize(TInt aMaxSize)
   817 	{
   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];
   821 	if(!iPhysicalPages)
   822 		return KErrNoMemory;
   823 
   824 	iMaxSize = aMaxSize;
   825 	return KErrNone;
   826 	}
   827 
   828 /**
   829   Open a shared chunk given an user address and siae
   830 */
   831 TInt DChunkBuffer::Open(TAny* aAddress, TInt aSize, TBool aWrite)
   832 	{
   833 	TInt r;
   834 
   835 	// Check size
   836 	if(aSize>iMaxSize)
   837 		return KErrTooBig;
   838 
   839 	NKern::ThreadEnterCS();
   840 
   841 	// Attempt to open chunk
   842 	iChunk = Kern::OpenSharedChunk(NULL,aAddress,aWrite,iChunkOffset);
   843 	if(!iChunk)
   844 		r = KErrArgument;
   845 	else
   846 		{
   847 		// Get physical addresses
   848 		r = SetPhysicalAddresses(aSize);
   849 		if(r!=KErrNone)
   850 			Close();
   851 		}
   852 
   853 	NKern::ThreadLeaveCS();
   854 
   855 	return r;
   856 	}
   857 
   858 /**
   859   Open a specified shared chunk
   860 */
   861 TInt DChunkBuffer::Open(TInt aChunkHandle, TInt aOffset, TInt aSize, TBool aWrite)
   862 	{
   863 	TInt r;
   864 
   865 	// Check size
   866 	if(aSize>iMaxSize)
   867 		return KErrTooBig;
   868 	iChunkOffset = aOffset;
   869 
   870 	NKern::ThreadEnterCS();
   871 
   872 	// Attempt to open chunk
   873 	iChunk = Kern::OpenSharedChunk(NULL,aChunkHandle,aWrite);
   874 	if(!iChunk)
   875 		r = KErrArgument;
   876 	else
   877 		{
   878 		// Get physical addresses
   879 		r = SetPhysicalAddresses(aSize);
   880 		if(r!=KErrNone)
   881 			Close();
   882 		}
   883 
   884 	NKern::ThreadLeaveCS();
   885 
   886 	return r;
   887 	}
   888 
   889 /**
   890   Close chunk
   891 */
   892 void DChunkBuffer::Close()
   893 	{
   894 	__ASSERT_CRITICAL;
   895 	if(iChunk)
   896 		{
   897 		Kern::ChunkClose(iChunk);
   898 		iChunk = NULL;
   899 		}
   900 	}
   901 
   902 /**
   903   Fill buffer by copying data from the given user address
   904 */
   905 TInt DChunkBuffer::Copy(TAny* aAddress, TInt aSize)
   906 	{
   907 	// Check size
   908 	if(aSize>iMaxSize)
   909 		return KErrTooBig;
   910 
   911 	// Copy data
   912 	kumemget((TAny*)(iChunkBase+iChunkOffset),aAddress,aSize);
   913 
   914 	return KErrNone;
   915 	}
   916 
   917 /**
   918   Setup physical address info for memory in the buffer
   919 */
   920 TInt DChunkBuffer::SetPhysicalAddresses(TInt aSize)
   921 	{
   922 	// Assert that the iPhysicalPages array already allocated will be big enough
   923 	__NK_ASSERT_DEBUG(aSize<=iMaxSize);
   924 
   925 	// Get physical addresses
   926 	TLinAddr kaddr;
   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)
   929 	if(r>=0) 
   930 		{
   931 		iChunkBase = kaddr-iChunkOffset; // Calculate start of chunk in kernel process address space
   932 		r = KErrNone;
   933 		}
   934 	return r;
   935 	}
   936 
   937 //
   938 // Program converter hardware
   939 //
   940 
   941 /**
   942   Initialise hardware to start converting data.
   943   Input data is in iSource.
   944   Output data to be placed in iOutBuffer.
   945 */
   946 void DConvert1Channel::DoConvertStart(TInt aOffset,TInt aSize)
   947 	{
   948 	// For this example test...
   949 	
   950 	TRACE(Kern::Printf("DConvert1Channel::DoConvertStart\n");)
   951 
   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;
   956 	while(src<end)
   957 		*dst++ = TUint8(*src++^1);
   958 
   959 	// Start the timer
   960 	TInt ticks = TInt((TInt64)1000000*(TInt64)aSize/(TInt64)iConfig.iSpeed)
   961 					/NKern::TickPeriod();
   962 	if(ticks<1)
   963 		ticks = 1;
   964 #ifdef _DEBUG
   965 	TInt r=
   966 #endif
   967 		iConvertTimer.OneShot(ticks,ETrue);
   968 	__NK_ASSERT_DEBUG(r==KErrNone);
   969 	}
   970 
   971 /**
   972   Tell hardware to stop converting.
   973 */
   974 void DConvert1Channel::DoConvertCancel()
   975 	{
   976 	// For this example test...
   977 	
   978 	TRACE(Kern::Printf("DConvert1Channel::DoConvertCancel\n");)
   979 
   980 	// Cancel the timer
   981 	iConvertTimer.Cancel();
   982 	}