sl@0: // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\drivers\soundsc\soundldd.cpp sl@0: // LDD for the shared chunk sound driver. sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: @internalTechnology sl@0: @prototype sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: //#define USE_PLAY_EOF_TIMER sl@0: sl@0: // Define TEST_WITH_PAGING_CACHE_FLUSHES to flush the paging cache when testing read/writes to user thread in a data-paging system sl@0: //#define TEST_WITH_PAGING_CACHE_FLUSHES sl@0: sl@0: static const char KSoundLddPanic[]="Sound LDD"; sl@0: sl@0: LOCAL_C TInt HighestCapabilitySupported(TUint32 aCapsBitField) sl@0: { sl@0: TInt n; sl@0: for (n=31 ; n>=0 ; n--) sl@0: { sl@0: if (aCapsBitField&(1<DSoundScLddFactory::DSoundScLddFactory")); sl@0: sl@0: // Set version number for this device. sl@0: iVersion=RSoundSc::VersionRequired(); sl@0: sl@0: // Indicate that units / PDD are supported. sl@0: iParseMask=KDeviceAllowUnit|KDeviceAllowPhysicalDevice; sl@0: sl@0: // Leave the units decision to the PDD sl@0: iUnitsMask=0xffffffff; sl@0: } sl@0: sl@0: /** sl@0: Second stage constructor for the sound driver factory class. sl@0: This must at least set a name for the driver object. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLddFactory::Install() sl@0: { sl@0: return(SetName(&KDevSoundScName)); sl@0: } sl@0: sl@0: /** sl@0: Return the 'capabilities' of the sound driver in general. sl@0: Called in the response to an RDevice::GetCaps() request. sl@0: @param aDes A user-side descriptor to write the capabilities information into. sl@0: */ sl@0: void DSoundScLddFactory::GetCaps(TDes8& aDes) const sl@0: { sl@0: // Create a capabilities object sl@0: TCapsSoundScV01 caps; sl@0: caps.iVersion=iVersion; sl@0: sl@0: // Write it back to user memory sl@0: Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps)); sl@0: } sl@0: sl@0: /** sl@0: Called by the kernel's device driver framework to create a logical channel. sl@0: This is called in the context of the client thread which requested the creation of a logical channel. sl@0: The thread is in a critical section. sl@0: @param aChannel Set by this function to point to the created logical channel. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLddFactory::Create(DLogicalChannelBase*& aChannel) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLddFactory::Create")); sl@0: aChannel=new DSoundScLdd; sl@0: if (!aChannel) sl@0: return(KErrNoMemory); sl@0: sl@0: return(KErrNone); sl@0: } sl@0: sl@0: /** sl@0: Check whether a channel has is currently open on the specified unit. sl@0: @param aUnit The number of the unit to be checked. sl@0: @return ETrue if a channel is open on the specified channel, EFalse otherwise. sl@0: @pre The unit info. mutex must be held. sl@0: */ sl@0: TBool DSoundScLddFactory::IsUnitOpen(TInt aUnit) sl@0: { sl@0: return(iUnitsOpenMask&(1<DSoundScLdd::DSoundScLdd")); sl@0: sl@0: iUnit=-1; // Invalid unit number sl@0: sl@0: // Many drivers would open the client thread's DThread object here. However, since this driver allows a channel to be shared by multiple client sl@0: // threads - we have to open and close the relevent DThread object for each request. sl@0: } sl@0: sl@0: /** sl@0: Destructor for the sound driver logical channel. sl@0: */ sl@0: DSoundScLdd::~DSoundScLdd() sl@0: { sl@0: sl@0: if (iNotifyChangeOfHwClientRequest) sl@0: Kern::DestroyClientRequest(iNotifyChangeOfHwClientRequest); sl@0: sl@0: // Free the TClientRequest structures associated with requests sl@0: if (iClientRequests) sl@0: { sl@0: for (TInt index=0; indexRemove(); sl@0: delete iPowerHandler; sl@0: } sl@0: sl@0: // Delete the request queue sl@0: if (iReqQueue) sl@0: delete iReqQueue; sl@0: sl@0: __ASSERT_DEBUG(iThreadOpenCount==0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: sl@0: // Clear the 'units open mask' in the LDD factory. sl@0: if (iUnit>=0) sl@0: ((DSoundScLddFactory*)iDevice)->SetUnitOpen(iUnit,EFalse); sl@0: } sl@0: sl@0: /** sl@0: Second stage constructor for the sound driver - called by the kernel's device driver framework. sl@0: This is called in the context of the client thread which requested the creation of a logical channel. sl@0: The thread is in a critical section. sl@0: @param aUnit The unit argument supplied by the client. sl@0: @param aInfo The info argument supplied by the client. Always NULL in this case. sl@0: @param aVer The version argument supplied by the client. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCreate")); sl@0: sl@0: // Check the client has ECapabilityMultimediaDD capability. sl@0: if (!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)"))) sl@0: return(KErrPermissionDenied); sl@0: sl@0: // Check that the sound driver version specified by the client is compatible. sl@0: if (!Kern::QueryVersionSupported(RSoundSc::VersionRequired(),aVer)) sl@0: return(KErrNotSupported); sl@0: sl@0: // Check that a channel hasn't already been opened on this unit. sl@0: TInt r=((DSoundScLddFactory*)iDevice)->SetUnitOpen(aUnit,ETrue); // Try to update 'units open mask' in the LDD factory. sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: iUnit=aUnit; sl@0: sl@0: // Create a TClientRequest for each request that can be completed by the DFC thread. These TClientRequest sl@0: // instances are separate to those embedded in the TSoundScRequest structures and are used for requests that sl@0: // have no associated TSoundScRequest structure or which are completing prematurely before they can be sl@0: // associated with a TSoundScRequest structure sl@0: if ((iClientRequests=new TClientRequest*[RSoundSc::ERequestRecordData+1])==NULL) sl@0: return KErrNoMemory; sl@0: sl@0: for (TInt index=0; indexiLdd=this; sl@0: sl@0: // Read back the capabilities of this device from the PDD and determine the data transfer direction for this unit. sl@0: TPckg capsBuf(iCaps); sl@0: Pdd()->Caps(capsBuf); sl@0: iDirection=iCaps.iDirection; sl@0: sl@0: // Check the client has UserEnvironment capability if recording. sl@0: if(iDirection==ESoundDirRecord) sl@0: { sl@0: if (!Kern::CurrentThreadHasCapability(ECapabilityUserEnvironment,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)"))) sl@0: return(KErrPermissionDenied); sl@0: } sl@0: sl@0: // Create the appropriate request queue sl@0: if (iDirection==ESoundDirPlayback) sl@0: iReqQueue=new TSoundScPlayRequestQueue(this); sl@0: else sl@0: iReqQueue=new TSoundScRequestQueue(this); sl@0: if (!iReqQueue) sl@0: return(KErrNoMemory); sl@0: r=iReqQueue->Create(); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: // Setup the default audio configuration acording to these capabilities. sl@0: iSoundConfig.iChannels=HighestCapabilitySupported(iCaps.iChannels)+1; sl@0: __ASSERT_ALWAYS(iSoundConfig.iChannels>0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iSoundConfig.iRate=(TSoundRate)HighestCapabilitySupported(iCaps.iRates); sl@0: __ASSERT_ALWAYS(iSoundConfig.iRate>=0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iSoundConfig.iEncoding=(TSoundEncoding)HighestCapabilitySupported(iCaps.iEncodings); sl@0: __ASSERT_ALWAYS(iSoundConfig.iEncoding>=0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iSoundConfig.iDataFormat=(TSoundDataFormat)HighestCapabilitySupported(iCaps.iDataFormats); sl@0: __ASSERT_ALWAYS(iSoundConfig.iDataFormat>=0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: __ASSERT_ALWAYS(ValidateConfig(iSoundConfig)==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iSoundConfigFlags=0; sl@0: sl@0: // Setup the default setting for the record level / play volume. sl@0: iVolume=KSoundMaxVolume; sl@0: sl@0: // Set up the correct DFC queue sl@0: TDfcQue* dfcq=((DSoundScPdd*)iPdd)->DfcQ(aUnit); sl@0: SetDfcQ(dfcq); sl@0: iPowerDownDfc.SetDfcQ(dfcq); sl@0: iPowerUpDfc.SetDfcQ(dfcq); sl@0: iMsgQ.Receive(); sl@0: sl@0: // Create the power handler sl@0: iPowerHandler=new DSoundScPowerHandler(this); sl@0: if (!iPowerHandler) sl@0: return(KErrNoMemory); sl@0: iPowerHandler->Add(); sl@0: sl@0: // Power up the hardware. sl@0: r=Pdd()->PowerUp(); sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Shutdown the audio device. sl@0: Terminate all device activity and power down the hardware. sl@0: */ sl@0: void DSoundScLdd::Shutdown() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Shutdown")); sl@0: sl@0: Pdd()->StopTransfer(); sl@0: sl@0: // Power down the hardware sl@0: Pdd()->PowerDown(); sl@0: sl@0: // Cancel any requests that we may be handling sl@0: DoCancel(RSoundSc::EAllRequests); sl@0: sl@0: iState=EOpen; sl@0: sl@0: // Make sure DFCs and timers are not queued. sl@0: iPowerDownDfc.Cancel(); sl@0: iPowerUpDfc.Cancel(); sl@0: CancelPlayEofTimer(); sl@0: } sl@0: sl@0: /** sl@0: Process a request on this logical channel sl@0: Called in the context of the client thread. sl@0: @param aReqNo The request number: sl@0: ==KMaxTInt: a 'DoCancel' message; sl@0: >=0: a 'DoControl' message with function number equal to value. sl@0: <0: a 'DoRequest' message with function number equal to ~value. sl@0: @param a1 The first request argument. For DoRequest(), this is a pointer to the TRequestStatus. sl@0: @param a2 The second request argument. For DoRequest(), this is a pointer to the 2 actual TAny* arguments. sl@0: @return The result of the request. This is ignored by device driver framework for DoRequest(). sl@0: */ sl@0: TInt DSoundScLdd::Request(TInt aReqNo, TAny* a1, TAny* a2) sl@0: { sl@0: // __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Request(%d)",aReqNo)); sl@0: TInt r; sl@0: sl@0: // Check for DoControl or DoRequest functions which are configured to execute in kernel thread context. This sl@0: // also applies to DoCancel functions and ERequestRecordData requests where recording mode is not yet enabled. sl@0: if ((aReqNo(~RSoundSc::EMsgRequestMax)) || sl@0: aReqNo==KMaxTInt || sl@0: ((~aReqNo)==RSoundSc::ERequestRecordData && (iState==EOpen || iState==EConfigured)) sl@0: ) sl@0: { sl@0: // Implement in the context of the kernel thread - prepare and issue a kernel message. sl@0: r=DLogicalChannel::Request(aReqNo,a1,a2); sl@0: } sl@0: else sl@0: { sl@0: // Implement in the context of the client thread. sl@0: // Decode the message type and dispatch it to the relevent handler function. sl@0: if ((TUint)aReqNo<(TUint)KMaxTInt) sl@0: r=DoControl(aReqNo,a1,a2,&Kern::CurrentThread()); // DoControl - process the request. sl@0: sl@0: else sl@0: { sl@0: // DoRequest - read the arguments from the client thread and process the request. sl@0: TAny* a[2]; sl@0: kumemget32(a,a2,sizeof(a)); sl@0: TRequestStatus* status=(TRequestStatus*)a1; sl@0: NKern::ThreadEnterCS(); // Need to be in critical section while manipulating the request/buffer list (for record). sl@0: r=DoRequest(~aReqNo,status,a[0],a[1],&Kern::CurrentThread()); sl@0: sl@0: // Complete request if there was an error sl@0: if (r!=KErrNone) sl@0: CompleteRequest(&Kern::CurrentThread(),status,r); sl@0: r=KErrNone; sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: } sl@0: // __KTRACE_OPT(KSOUND1, Kern::Printf("Free((TSoundScPlayRequest*)m.iArg[1]); // Return the unused request object sl@0: } sl@0: return(r); sl@0: } sl@0: else if (id == RSoundSc::EMsgControlSetBufChunkCreate || id == RSoundSc::EMsgControlSetBufChunkOpen) sl@0: { sl@0: r = PreSetBufferChunkCreateOrOpen(aMsg); sl@0: if (r!=KErrNone) sl@0: { sl@0: return(r); sl@0: } sl@0: } sl@0: else if (id == RSoundSc::EMsgControlSetAudioFormat) sl@0: { sl@0: r = PreSetSoundConfig(aMsg); sl@0: if (r!=KErrNone) sl@0: { sl@0: return(r); sl@0: } sl@0: } sl@0: sl@0: r = DLogicalChannel::SendMsg(aMsg); sl@0: sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: PreProcess a play request on this logical channel sl@0: Called in the context of the client thread. sl@0: sl@0: @param aMsg The message to process. sl@0: sl@0: @return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error. sl@0: */ sl@0: TInt DSoundScLdd::PrePlay(TMessageBase* aMsg) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PrePlay")); sl@0: sl@0: // Executes in context of client thread sl@0: sl@0: TThreadMessage* m=(TThreadMessage*)aMsg; sl@0: sl@0: // Copy play information to kernel side before checking sl@0: SRequestPlayDataInfo info; sl@0: kumemget(&info,m->iArg[1],sizeof(info)); sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PrePlay - off %x len %x flg %x ",info.iBufferOffset,info.iLength,info.iFlags)); sl@0: sl@0: // validate parameters in the play structure sl@0: sl@0: // Check that the offset argument is aligned correctly for the PDD. sl@0: TUint32 alignmask=(1<ValidateRegion(info.iBufferOffset,info.iLength,buf); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: } sl@0: else sl@0: { sl@0: return(KErrNotReady); sl@0: } sl@0: sl@0: // Acquire a free request object and add it to the queue of pending requests. sl@0: TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->NextFree(); sl@0: if (!req) sl@0: return(KErrGeneral); // Must have exceeded KMaxSndScRequestsPending. sl@0: req->iTf.Init((TUint)buf,info.iBufferOffset,info.iLength,buf); // Use pointer to audio buffer as unique ID sl@0: req->iFlags=info.iFlags; sl@0: sl@0: // replace the argument with a pointer to the kernel-side structure sl@0: m->iArg[1]=req; sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen")); sl@0: TInt r(KErrNone); sl@0: sl@0: TThreadMessage* m=(TThreadMessage*)aMsg; sl@0: sl@0: TInt length, maxLength; sl@0: const TDesC8* userDesc = (const TDesC8*)m->Ptr0(); sl@0: const TUint8* configData = Kern::KUDesInfo(*userDesc,length,maxLength); sl@0: sl@0: //__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - len %x maxlen %x",length,maxLength)); sl@0: sl@0: // check the descriptor length is >= the base class size sl@0: TInt minDesLen=sizeof(TSharedChunkBufConfigBase); sl@0: if (lengthDSoundScLdd::PreSetSoundConfig")); sl@0: sl@0: TThreadMessage* m=(TThreadMessage*)aMsg; sl@0: sl@0: TPtr8 localPtr((TUint8*)&iTempSoundConfig, sizeof(TCurrentSoundFormatV02)); sl@0: sl@0: Kern::KUDesGet(localPtr,*(const TDesC8*)m->Ptr0()); sl@0: sl@0: //__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetSoundConfig chan %x rate %x enc %x form %x", sl@0: // iTempSoundConfig.iChannels,iTempSoundConfig.iRate,iTempSoundConfig.iEncoding,iTempSoundConfig.iDataFormat)); sl@0: sl@0: // Check that it is compatible with this sound device. sl@0: TInt r=ValidateConfig(iTempSoundConfig); sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Processes a message for this logical channel. sl@0: This function is called in the context of a DFC thread. sl@0: @param aMsg The message to process. sl@0: The iValue member of this distinguishes the message type: sl@0: iValue==ECloseMsg: channel close message. sl@0: iValue==KMaxTInt: a 'DoCancel' message sl@0: iValue>=0: a 'DoControl' message with function number equal to iValue sl@0: iValue<0: a 'DoRequest' message with function number equal to ~iValue sl@0: */ sl@0: void DSoundScLdd::HandleMsg(TMessageBase* aMsg) sl@0: { sl@0: #ifdef _DEBUG sl@0: #ifdef TEST_WITH_PAGING_CACHE_FLUSHES sl@0: Kern::SetRealtimeState(ERealtimeStateOn); sl@0: Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); sl@0: #endif sl@0: #endif sl@0: sl@0: TThreadMessage& m=*(TThreadMessage*)aMsg; sl@0: TInt id=m.iValue; sl@0: // __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::HandleMsg(%d)",id)); sl@0: sl@0: if (id==(TInt)ECloseMsg) sl@0: { sl@0: // Channel close. sl@0: Shutdown(); sl@0: m.Complete(KErrNone,EFalse); sl@0: return; sl@0: } sl@0: else if (id==KMaxTInt) sl@0: { sl@0: // DoCancel sl@0: DoCancel(m.Int0()); sl@0: m.Complete(KErrNone,ETrue); sl@0: return; sl@0: } sl@0: else if (id<0) sl@0: { sl@0: // DoRequest sl@0: TRequestStatus* pS=(TRequestStatus*)m.Ptr0(); sl@0: TInt r=DoRequest(~id,pS,m.Ptr1(),m.Ptr2(),m.Client()); sl@0: if (r!=KErrNone) sl@0: { sl@0: iClientRequests[~id]->SetStatus(pS); sl@0: CompleteRequest(m.Client(),NULL,r,iClientRequests[~id]); sl@0: } sl@0: m.Complete(KErrNone,ETrue); sl@0: } sl@0: else sl@0: { sl@0: // DoControl sl@0: TInt r=DoControl(id,m.Ptr0(),m.Ptr1(),m.Client()); sl@0: m.Complete(r,ETrue); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Process a synchronous 'DoControl' request. sl@0: @param aFunction The request number. sl@0: @param a1 The first request argument. sl@0: @param a2 The second request argument. sl@0: @param aThread The client thread which issued the request. sl@0: @return The result of the request. sl@0: */ sl@0: TInt DSoundScLdd::DoControl(TInt aFunction,TAny* a1,TAny* a2,DThread* aThread) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoControl(%d)",aFunction)); sl@0: sl@0: TInt r=KErrNotSupported; sl@0: switch (aFunction) sl@0: { sl@0: case RSoundSc::EControlGetCaps: sl@0: { sl@0: // Return the capabilities for this device. Read this from the PDD and sl@0: // then write it to the client. sl@0: TSoundFormatsSupportedV02Buf caps; sl@0: Pdd()->Caps(caps); sl@0: Kern::InfoCopy(*((TDes8*)a1),caps); sl@0: r=KErrNone; sl@0: break; sl@0: } sl@0: case RSoundSc::EControlGetAudioFormat: sl@0: { sl@0: // Write the current audio configuration back to the client. sl@0: TPtrC8 ptr((const TUint8*)&iSoundConfig,sizeof(iSoundConfig)); sl@0: Kern::InfoCopy(*((TDes8*)a1),ptr); sl@0: r=KErrNone; sl@0: break; sl@0: } sl@0: case RSoundSc::EMsgControlSetAudioFormat: sl@0: { sl@0: if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) sl@0: { sl@0: // If the play EOF timer is active then it is OK to change the audio configuration - but we sl@0: // need to bring the PDD out of transfer mode first. sl@0: if (iPlayEofTimerActive) sl@0: { sl@0: CancelPlayEofTimer(); sl@0: Pdd()->StopTransfer(); sl@0: } sl@0: sl@0: r=SetSoundConfig(); sl@0: if (r==KErrNone && (iSoundConfigFlags&KSndScVolumeIsSetup) && iBufConfig) sl@0: iState=EConfigured; sl@0: } sl@0: else sl@0: r=KErrInUse; sl@0: break; sl@0: } sl@0: case RSoundSc::EControlGetBufConfig: sl@0: if (iBufConfig) sl@0: { sl@0: // Write the buffer config to the client. sl@0: TPtrC8 ptr((const TUint8*)iBufConfig,iBufConfigSize); sl@0: Kern::InfoCopy(*((TDes8*)a1),ptr); sl@0: r=KErrNone; sl@0: } sl@0: break; sl@0: case RSoundSc::EMsgControlSetBufChunkCreate: sl@0: { sl@0: if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) sl@0: { sl@0: // Need to be in critical section while deleting an exisiting config and creating a new one sl@0: NKern::ThreadEnterCS(); sl@0: r=SetBufferConfig(aThread); sl@0: NKern::ThreadLeaveCS(); sl@0: if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup)) sl@0: iState=EConfigured; sl@0: } sl@0: else sl@0: r=KErrInUse; sl@0: break; sl@0: } sl@0: case RSoundSc::EMsgControlSetBufChunkOpen: sl@0: { sl@0: if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) sl@0: { sl@0: // Need to be in critical section while deleting an exisiting config and creating a new one sl@0: NKern::ThreadEnterCS(); sl@0: r=SetBufferConfig((TInt)a2,aThread); sl@0: NKern::ThreadLeaveCS(); sl@0: if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup)) sl@0: iState=EConfigured; sl@0: } sl@0: else sl@0: r=KErrInUse; sl@0: break; sl@0: } sl@0: case RSoundSc::EControlGetVolume: sl@0: r=iVolume; sl@0: break; sl@0: case RSoundSc::EMsgControlSetVolume: sl@0: { sl@0: r=SetVolume((TInt)a1); sl@0: if (r==KErrNone && iState==EOpen && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && iBufConfig) sl@0: iState=EConfigured; sl@0: break; sl@0: } sl@0: case RSoundSc::EMsgControlCancelSpecific: sl@0: { sl@0: if (iDirection==ESoundDirPlayback) sl@0: { sl@0: // Don't try to cancel a play transfer that has already started - let it complete in its own time. sl@0: TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->Find((TRequestStatus*)a1); sl@0: if (req && req->iTf.iTfState==TSndScTransfer::ETfNotStarted) sl@0: { sl@0: iReqQueue->Remove(req); sl@0: CompleteRequest(req->iOwningThread,NULL,KErrCancel,req->iClientRequest); sl@0: iReqQueue->Free(req); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Need to aquire the buffer/request list mutex when removing record requests - RecordData() runs in sl@0: // client thread context and this may access the queue. Record requests a treated differently to play sl@0: // requests and you don't have to worry about record requests already being in progress. sl@0: NKern::FMWait(&iMutex); sl@0: TSoundScRequest* req=iReqQueue->Find((TRequestStatus*)a1); sl@0: if (req) sl@0: { sl@0: iReqQueue->Remove(req); sl@0: DThread* thread=req->iOwningThread; // Take a copy before we free it. sl@0: TClientRequest* clreq=req->iClientRequest; // Take a copy before we free it. sl@0: NKern::FMSignal(&iMutex); sl@0: iReqQueue->Free(req); sl@0: CompleteRequest(thread,NULL,KErrCancel,clreq); sl@0: } sl@0: else sl@0: NKern::FMSignal(&iMutex); sl@0: } sl@0: r=KErrNone; sl@0: break; sl@0: } sl@0: case RSoundSc::EControlBytesTransferred: sl@0: r=iBytesTransferred; sl@0: break; sl@0: case RSoundSc::EControlResetBytesTransferred: sl@0: iBytesTransferred=0; sl@0: r=KErrNone; sl@0: break; sl@0: case RSoundSc::EMsgControlPause: sl@0: if (iState==EActive) sl@0: { sl@0: // Have to update the status early here because a record PDD may call us back with RecordCallback() in sl@0: // handling PauseTransfer() - to complete a partially filled buffer. sl@0: iState=EPaused; sl@0: iCompletesWhilePausedCount=0; sl@0: r=Pdd()->PauseTransfer(); sl@0: if (r!=KErrNone) sl@0: iState=EActive; sl@0: else if (iDirection==ESoundDirRecord) sl@0: { sl@0: // For record, complete any pending record requests that are still outstanding following PauseTransfer(). sl@0: iReqQueue->CompleteAll(KErrCancel); sl@0: } sl@0: } sl@0: else sl@0: r=KErrNotReady; sl@0: break; sl@0: case RSoundSc::EMsgControlResume: sl@0: if (iState==EPaused) sl@0: { sl@0: r=Pdd()->ResumeTransfer(); sl@0: if (r==KErrNone && iDirection==ESoundDirRecord) sl@0: r=StartNextRecordTransfers(); sl@0: if (r==KErrNone) sl@0: iState=EActive; // Successfully resumed transfer - update the status. sl@0: } sl@0: else sl@0: r=KErrNotReady; sl@0: break; sl@0: case RSoundSc::EControlReleaseBuffer: sl@0: if (iDirection==ESoundDirRecord) sl@0: r=ReleaseBuffer((TInt)a1); sl@0: break; sl@0: case RSoundSc::EMsgControlCustomConfig: sl@0: r=CustomConfig((TInt)a1,a2); sl@0: break; sl@0: case RSoundSc::EControlTimePlayed: sl@0: if (iDirection==ESoundDirPlayback) sl@0: { sl@0: TInt64 time=0; sl@0: r=Pdd()->TimeTransferred(time,iState); sl@0: TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64)); sl@0: Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL); sl@0: } sl@0: else sl@0: r=KErrNotSupported; sl@0: break; sl@0: case RSoundSc::EControlTimeRecorded: sl@0: if (iDirection==ESoundDirRecord) sl@0: { sl@0: TInt64 time=0; sl@0: r=Pdd()->TimeTransferred(time,iState); sl@0: TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64)); sl@0: Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL); sl@0: } sl@0: else sl@0: r=KErrNotSupported; sl@0: break; sl@0: } sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::DoRequest(%d)",aFunction)); sl@0: sl@0: // Open a reference on the client thread while the request is pending so it's control block can't disappear until this driver has finished with it. sl@0: TInt r=aThread->Open(); sl@0: __ASSERT_ALWAYS(r==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: #ifdef _DEBUG sl@0: __e32_atomic_add_ord32(&iThreadOpenCount, 1); sl@0: #endif sl@0: sl@0: r=KErrNotSupported; sl@0: switch (aFunction) sl@0: { sl@0: case RSoundSc::EMsgRequestPlayData: sl@0: { sl@0: if (iDirection==ESoundDirPlayback) sl@0: { sl@0: if (iState==EOpen) sl@0: { sl@0: // Not yet fully configured - maybe we can use the default settings. sl@0: r=KErrNone; sl@0: if (!iBufConfig) sl@0: r=KErrNotReady; // Can't guess a default buffer configuration. sl@0: else sl@0: { sl@0: if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup)) sl@0: r=DoSetSoundConfig(iSoundConfig); // Apply default sound configuration. sl@0: if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup)) sl@0: r=SetVolume(iVolume); // Apply default volume level sl@0: } sl@0: if (r!=KErrNone) sl@0: break; sl@0: else sl@0: iState=EConfigured; sl@0: } sl@0: sl@0: if (iState==EConfigured || iState==EActive || iState==EPaused) sl@0: { sl@0: r=PlayData(aStatus, (TSoundScPlayRequest*)a1,aThread); sl@0: } sl@0: else sl@0: r=KErrNotReady; sl@0: } sl@0: break; sl@0: } sl@0: case RSoundSc::ERequestRecordData: sl@0: if (iDirection==ESoundDirRecord) sl@0: { sl@0: // Check if the device has been configured yet sl@0: if (iState==EOpen) sl@0: { sl@0: // Not yet fully configured - maybe we can use the default settings. sl@0: r=KErrNone; sl@0: if (!iBufConfig) sl@0: r=KErrNotReady; // Can't guess a default buffer configuration. sl@0: else sl@0: { sl@0: if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup)) sl@0: r=DoSetSoundConfig(iSoundConfig); // Apply default sound configuration. sl@0: if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup)) sl@0: r=SetVolume(iVolume); // Apply default volume level sl@0: } sl@0: if (r!=KErrNone) sl@0: break; sl@0: else sl@0: iState=EConfigured; sl@0: } sl@0: // Check if we need to start recording sl@0: if (iState==EConfigured) sl@0: { sl@0: r=StartRecord(); sl@0: if (r!=KErrNone) sl@0: break; sl@0: else sl@0: iState=EActive; sl@0: } sl@0: sl@0: // State must be either active or paused so process the record request as appropriate for these states. sl@0: r=RecordData(aStatus,(TInt*)a1,aThread); sl@0: } sl@0: break; sl@0: case RSoundSc::ERequestNotifyChangeOfHwConfig: sl@0: { sl@0: // Check if this device can detect changes in its hardware configuration. sl@0: if (iCaps.iHwConfigNotificationSupport) sl@0: { sl@0: r=KErrNone; sl@0: if (!iNotifyChangeOfHwClientRequest->IsReady()) sl@0: { sl@0: iChangeOfHwConfigThread=aThread; sl@0: iNotifyChangeOfHwClientRequest->SetDestPtr((TBool*)a1); sl@0: r = iNotifyChangeOfHwClientRequest->SetStatus(aStatus); sl@0: } sl@0: else sl@0: r=KErrInUse; sl@0: } sl@0: else sl@0: r=KErrNotSupported; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::DoCancel(%08x)",aMask)); sl@0: sl@0: if (aMask&(1<StopTransfer(); sl@0: iReqQueue->CompleteAll(KErrCancel); // Cancel any outstanding play requests sl@0: if ((iState==EActive)||(iState==EPaused)) sl@0: iState=EConfigured; sl@0: } sl@0: if (aMask&(1<StopTransfer(); sl@0: iReqQueue->CompleteAll(KErrCancel,&iMutex); // Cancel any outstanding record requests sl@0: if ((iState==EActive)||(iState==EPaused)) sl@0: iState=EConfigured; sl@0: } sl@0: if (aMask&(1<IsReady()) sl@0: CompleteRequest(iChangeOfHwConfigThread,NULL,KErrCancel,iNotifyChangeOfHwClientRequest); sl@0: } sl@0: return(KErrNone); sl@0: } sl@0: sl@0: /** sl@0: Set the current buffer configuration - creating a shared chunk. sl@0: @param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of sl@0: the shared chunk required. sl@0: @param aThread The client thread which has requested to own the chunk. sl@0: @return A handle to the shared chunk for the owning thread (a value >0), if successful; sl@0: otherwise one of the other system wide error codes, (a value <0). sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: TInt DSoundScLdd::SetBufferConfig(DThread* aThread) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig")); sl@0: sl@0: TInt r(KErrNone); sl@0: sl@0: // Delete any existing buffers and the shared chunk. sl@0: if (iBufManager) sl@0: { sl@0: delete iBufManager; sl@0: iBufManager=NULL; sl@0: } sl@0: sl@0: // If a handle to the shared chunk was created, close it, using the handle of the thread on which sl@0: // it was created, in case a different thread is now calling us sl@0: if (iChunkHandle>0) sl@0: { sl@0: Kern::CloseHandle(iChunkHandleThread,iChunkHandle); sl@0: iChunkHandle=0; sl@0: } sl@0: sl@0: // Create the shared chunk, then create buffer objects for the committed buffers within it. This is sl@0: // done by creating a buffer manager - create the apppropraiate version according to the audio direction. sl@0: if (iDirection==ESoundDirPlayback) sl@0: iBufManager=new DBufferManager(this); sl@0: else sl@0: iBufManager=new DRecordBufferManager(this); sl@0: if (!iBufManager) sl@0: return(KErrNoMemory); sl@0: r=iBufManager->Create(iBufConfig); sl@0: if (r!=KErrNone) sl@0: { sl@0: delete iBufManager; sl@0: iBufManager=NULL; sl@0: return(r); sl@0: } sl@0: sl@0: // Create handle to the shared chunk for the owning thread. sl@0: r=Kern::MakeHandleAndOpen(aThread,iBufManager->iChunk); sl@0: sl@0: // And save the the chunk and thread handles for later. Normally the chunk handle will be closed when the chunk sl@0: // is closed, but if the chunk is re-allocated then it will need to be closed before re-allocation. sl@0: iChunkHandle=r; sl@0: iChunkHandleThread=aThread; sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Set the current buffer configuration - using an existing shared chunk. sl@0: @param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of sl@0: the shared chunk supplied. sl@0: @param aChunkHandle A handle for the shared chunk supplied by the client. sl@0: @param aThread The thread in which the given handle is valid. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: TInt DSoundScLdd::SetBufferConfig(TInt aChunkHandle,DThread* aThread) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig(Handle-%d)",aChunkHandle)); sl@0: sl@0: TInt r(KErrNone); sl@0: sl@0: // Delete any existing buffers and the shared chunk. sl@0: if (iBufManager) sl@0: { sl@0: delete iBufManager; sl@0: iBufManager=NULL; sl@0: } sl@0: sl@0: // Open the shared chunk supplied and create buffer objects for the committed buffers within it. This is sl@0: // done by creating a buffer manager - create the apppropraiate version according to the audio direction. sl@0: if (iDirection==ESoundDirPlayback) sl@0: iBufManager=new DBufferManager(this); sl@0: else sl@0: iBufManager=new DRecordBufferManager(this); sl@0: if (!iBufManager) sl@0: return(KErrNoMemory); sl@0: r=iBufManager->Create(*iBufConfig,aChunkHandle,aThread); sl@0: if (r!=KErrNone) sl@0: { sl@0: delete iBufManager; sl@0: iBufManager=NULL; sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Set the current audio format configuration. sl@0: @param aSoundConfigBuf A packaged sound configuration object holding the new audio configuration settings to be used. sl@0: @param aThread The client thread which contains the sound configuration object. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::SetSoundConfig() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetSoundConfig")); sl@0: sl@0: TInt r=DoSetSoundConfig(iTempSoundConfig); sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd:DoSetSoundConfig")); sl@0: sl@0: // We're about to replace any previous configuration - so set the sl@0: // status back to un-configured in case we don't succeed with the new one. sl@0: iSoundConfigFlags&=~KSndScSoundConfigIsSetup; sl@0: sl@0: // Call the PDD to change the hardware configuration according to the new specification. sl@0: // Pass it as a descriptor - to support future changes to the config structure. sl@0: TPtrC8 ptr((TUint8*)&aSoundConfig,sizeof(aSoundConfig)); sl@0: TInt r=Pdd()->SetConfig(ptr); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: // Setting up the new play configuration has succeeded so save the new configuration. sl@0: iSoundConfig=aSoundConfig; sl@0: iSoundConfigFlags|=KSndScSoundConfigIsSetup; sl@0: sl@0: // For some devices, the maximum transfer length supported will vary according to the configuration. sl@0: if (iBufManager) sl@0: iBufManager->iMaxTransferLen=Pdd()->MaxTransferLen(); sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Set the current play volume or record level. sl@0: @param aVolume The play volume / record level to be set - a value in the range 0 to 255. The value 255 equates to sl@0: the maximum volume and each value below this equates to a 0.5dB step below it. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::SetVolume(TInt aVolume) sl@0: { sl@0: TInt r; sl@0: // Check if the volume specified is in range. sl@0: if (aVolume>=0 && aVolume<=255) sl@0: { sl@0: // Check if we need to change it. sl@0: if (!(iSoundConfigFlags&KSndScVolumeIsSetup) || aVolume!=iVolume) sl@0: { sl@0: // We're about to replace any previous volume setting - so set the sl@0: // status back to un-set in case we don't succeed with the new setting. sl@0: iSoundConfigFlags&=~KSndScVolumeIsSetup; sl@0: sl@0: r=Pdd()->SetVolume(aVolume); sl@0: if (r==KErrNone) sl@0: { sl@0: iVolume=aVolume; sl@0: iSoundConfigFlags|=KSndScVolumeIsSetup; sl@0: } sl@0: } sl@0: else sl@0: r=KErrNone; sl@0: } sl@0: else sl@0: r=KErrArgument; sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd:PlayData(off:%x len:%d)",aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen())); sl@0: sl@0: // Purge the region of the play chunk concerned. sl@0: iBufManager->FlushData(aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen(),DBufferManager::EFlushBeforeDmaWrite); sl@0: sl@0: sl@0: TInt r(KErrNone); sl@0: sl@0: // finalise the request data here sl@0: r = aRequest->iClientRequest->SetStatus(aStatus); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: aRequest->iOwningThread = aThread; sl@0: sl@0: sl@0: // Check whether we have started the codec yet. sl@0: CancelPlayEofTimer(); sl@0: if (iState==EConfigured) sl@0: { sl@0: r=Pdd()->StartTransfer(); sl@0: sl@0: // Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer(). sl@0: #ifdef _DEBUG sl@0: if (iTestSettings & KSoundScTest_StartTransferError) sl@0: { sl@0: iTestSettings&=(~KSoundScTest_StartTransferError); sl@0: r=KErrTimedOut; sl@0: // Any time that StartTransfer() is called on the PDD it must have a matching StopTransfer() before sl@0: // it is called again sl@0: Pdd()->StopTransfer(); sl@0: } sl@0: #endif sl@0: } sl@0: sl@0: if (r==KErrNone) sl@0: { sl@0: // No further error is possible at this stage so add the request to the queue. sl@0: iReqQueue->Add(aRequest); sl@0: sl@0: if (iState!=EPaused) sl@0: { sl@0: iState=EActive; sl@0: StartNextPlayTransfers(); // Queue as many transfer requests on the PDD as it can accept. sl@0: } sl@0: } sl@0: else sl@0: iReqQueue->Free(aRequest); // Return the unused request object sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: @publishedPartner sl@0: @prototype sl@0: sl@0: Called from the PDD each time it has completed a data transfer from a play buffer. This function must be called sl@0: in the context of the DFC thread used for processing requests. sl@0: The function performed here is to check whether the entire transfer for the current request is now complete. Also to sl@0: queue further requests on the PDD which should now have the capability to accept more transfers. If the current sl@0: request is complete then we signal completion to the client. sl@0: @param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be sl@0: uniquely identified. sl@0: @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other sl@0: system wide error codes. sl@0: @param aBytesPlayed The number of bytes played from the play buffer. sl@0: */ sl@0: void DSoundScLdd::PlayCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesPlayed) sl@0: { sl@0: #ifdef _DEBUG sl@0: #ifdef TEST_WITH_PAGING_CACHE_FLUSHES sl@0: Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); sl@0: #endif sl@0: #endif sl@0: // Test settings - only possible in debug mode. sl@0: #ifdef _DEBUG sl@0: if (iTestSettings & KSoundScTest_TransferDataError) sl@0: { sl@0: iTestSettings&=(~KSoundScTest_TransferDataError); sl@0: aTransferResult=KErrTimedOut; sl@0: } sl@0: #endif sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PlayCallback(ID:%xH,Len:%d) - %d",aTransferID,aBytesPlayed,aTransferResult)); sl@0: sl@0: // The PDD has completed transfering a fragment. Find the associated request from its ID sl@0: TBool isNextToComplete; sl@0: TSoundScPlayRequest* req=((TSoundScPlayRequestQueue*)iReqQueue)->Find(aTransferID,isNextToComplete); sl@0: sl@0: // Check if this is a fragment from an earlier request which failed - which we should ignore. This is the case if the request cannot be found sl@0: // (because it was already completed back to client) or if the request status is already set as 'done'. sl@0: if (req && req->iTf.iTfState!=TSndScTransfer::ETfDone) sl@0: { sl@0: __ASSERT_DEBUG(req->iTf.iTfState!=TSndScTransfer::ETfNotStarted,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: sl@0: // Update the count of bytes played. sl@0: iBytesTransferred+=aBytesPlayed; sl@0: sl@0: if (aTransferResult!=KErrNone) sl@0: { sl@0: // Transfer failed - immediately mark the request as being complete. sl@0: req->SetFail(aTransferResult); sl@0: } sl@0: else sl@0: req->UpdateProgress(aBytesPlayed); // Transfer successful so update the progress of the request. sl@0: sl@0: // If we have just played an entire request and the PDD has not signalled it ahead of any earlier unfinished ones then complete it back to client. sl@0: if (req->iTf.iTfState==TSndScTransfer::ETfDone && isNextToComplete) sl@0: CompleteAllDonePlayRequests(req); sl@0: } sl@0: sl@0: // PDD should now have the capacity to accept another transfer so queue as many transfers sl@0: // on it as it can accept. sl@0: StartNextPlayTransfers(); sl@0: sl@0: sl@0: return; sl@0: } sl@0: sl@0: /** sl@0: This function checks whether there are any outstanding play requests. While there are, it breaks these down into sl@0: transfers sizes which are compatible with the PDD and then repeatedly attempts to queue these data transfers on the sl@0: PDD until it indicates that it can accept no more for the moment. sl@0: @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. sl@0: */ sl@0: void DSoundScLdd::StartNextPlayTransfers() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextPlayTransfers")); sl@0: sl@0: // Queue as many transfers on the PDD as it can accept. sl@0: TSoundScPlayRequest* req; sl@0: TInt r=KErrNone; sl@0: while (r==KErrNone && (req=((TSoundScPlayRequestQueue*)iReqQueue)->NextRequestForTransfer())!=NULL) sl@0: { sl@0: TInt pos=req->iTf.GetStartOffset(); sl@0: TPhysAddr physAddr; sl@0: TInt len=req->iTf.iAudioBuffer->GetFragmentLength(pos,req->iTf.GetNotStartedLen(),physAddr); sl@0: if (len>0) sl@0: { sl@0: r=Pdd()->TransferData(req->iTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("iTf.SetStarted(len); // Successfully queued a transfer - update the request status. sl@0: else if (r!=KErrNotReady) sl@0: { sl@0: // Transfer error from PDD, fail the request straight away. (Might not be the one at the head of queue). sl@0: CompletePlayRequest(req,r); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // This can only be a zero length play request - just complete it straight away sl@0: CompletePlayRequest(req,KErrNone); sl@0: } sl@0: } sl@0: return; sl@0: } sl@0: sl@0: /** sl@0: Complete a client play request back to the client and remove it from the request queue. sl@0: @param aReq A pointer to the play request object to be completed. sl@0: @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. sl@0: */ sl@0: void DSoundScLdd::DoCompletePlayRequest(TSoundScPlayRequest* aReq) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCompletePlayRequest(%x) - %d",aReq,aReq->iCompletionReason)); sl@0: sl@0: iReqQueue->Remove(aReq); sl@0: sl@0: // If the request queue is now empty then turn off the codec sl@0: if (iReqQueue->IsEmpty()) sl@0: { sl@0: #ifdef USE_PLAY_EOF_TIMER sl@0: StartPlayEofTimer(); sl@0: #else sl@0: Pdd()->StopTransfer(); sl@0: iState=EConfigured; sl@0: #endif sl@0: // This is an underflow situation. sl@0: if (aReq->iCompletionReason==KErrNone && aReq->iFlags!=KSndFlagLastSample) sl@0: aReq->iCompletionReason=KErrUnderflow; sl@0: } sl@0: sl@0: CompleteRequest(aReq->iOwningThread,NULL,aReq->iCompletionReason,aReq->iClientRequest); sl@0: iReqQueue->Free(aReq); sl@0: return; sl@0: } sl@0: sl@0: /** sl@0: Complete one or more play requests. This function completes the play request specified. It also completes any other play sl@0: requests which immediately follow the one specified in the play request queue and for which transfer has been completed by the PDD. sl@0: @param aReq A pointer to the play request object to be completed. sl@0: @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. sl@0: */ sl@0: void DSoundScLdd::CompleteAllDonePlayRequests(TSoundScPlayRequest* aReq) sl@0: { sl@0: TSoundScPlayRequest* nextReq=aReq; sl@0: TSoundScPlayRequest* req; sl@0: do sl@0: { sl@0: req=nextReq; sl@0: nextReq=(TSoundScPlayRequest*)req->iNext; sl@0: DoCompletePlayRequest(req); sl@0: } sl@0: while (!iReqQueue->IsAnchor(nextReq) && nextReq->iTf.iTfState==TSndScTransfer::ETfDone); sl@0: return; sl@0: } sl@0: sl@0: /** sl@0: Start the audio device recording data. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::StartRecord() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartRecord")); sl@0: sl@0: // Reset all the audio buffer lists sl@0: NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. sl@0: ((DRecordBufferManager*)iBufManager)->Reset(); sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: sl@0: // Reset the transfer status for the current and pending record buffers. sl@0: TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer(); sl@0: iCurrentRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID sl@0: buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); sl@0: iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID sl@0: sl@0: // Call the PDD to prepare the hardware for recording. sl@0: TInt r=Pdd()->StartTransfer(); sl@0: sl@0: // Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer(). sl@0: #ifdef _DEBUG sl@0: if (iTestSettings & KSoundScTest_StartTransferError) sl@0: { sl@0: iTestSettings&=(~KSoundScTest_StartTransferError); sl@0: r=KErrTimedOut; sl@0: } sl@0: #endif sl@0: sl@0: // Initiate data transfer into the first record buffer(s). sl@0: if (r==KErrNone) sl@0: r=StartNextRecordTransfers(); sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Handle a record request from the client once data transfer has been intiated. sl@0: @param aStatus The request status to be signalled when the record request is complete. If the request is successful sl@0: then this is set to the offset within the shared chunk where the record data resides. Alternatively, if an error sl@0: occurs, it will be set to one of the system wide error values. sl@0: @param aLengthPtr A pointer to a TInt object in client memory. On completion, the number of bytes successfully sl@0: recorded are written to this object. sl@0: @param aThread The client thread which issued the request and which supplied the request status. sl@0: @return KErrNone if successful; sl@0: KErrInUse: if the client needs to free up record buffers before further record requests can be accepted; sl@0: KErrCancel: if the driver is in paused mode and there are no complete or partially full buffers to return. sl@0: otherwise one of the other system-wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::RecordData(TRequestStatus* aStatus,TInt* aLengthPtr,DThread* aThread) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:RecordData")); sl@0: sl@0: TInt r=KErrNone; sl@0: sl@0: NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. sl@0: sl@0: // Check if we have had an overflow since the last record request was completed. sl@0: if (((DRecordBufferManager*)iBufManager)->iBufOverflow) sl@0: { sl@0: ((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse; sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: return(KErrOverflow); sl@0: } sl@0: sl@0: // See if there is a buffer already available. sl@0: TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient(); sl@0: if (buf) sl@0: { sl@0: // There is an buffer available already - complete the request returning the offset of the buffer to the client. sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: sl@0: r=buf->iResult; sl@0: sl@0: if (r==KErrNone) sl@0: { sl@0: kumemput(aLengthPtr,&buf->iBytesAdded,sizeof(TInt)); sl@0: // Only complete if successful here. Errors will be completed on returning from this method. sl@0: CompleteRequest(aThread,aStatus,(buf->iChunkOffset)); sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: // If we are paused and there was no un-read data to return to the client then return KErrCancel to prompt them to resume. sl@0: if (iState==EPaused) sl@0: { sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: return(KErrCancel); sl@0: } sl@0: sl@0: // The buffer 'completed' list is empty. If the buffer 'free' list is empty too then the client needs sl@0: // to free some buffers up - return an error. sl@0: if (((DRecordBufferManager*)iBufManager)->iFreeBufferQ.IsEmpty()) sl@0: { sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: return(KErrInUse); sl@0: } sl@0: sl@0: // Acquire a new request object and add it to the queue of pending requests. The request will be completed sl@0: // from the PDD and the DFC thread when a buffer is available. sl@0: NKern::FMSignal(&iMutex); sl@0: TSoundScRequest* req=iReqQueue->NextFree(); sl@0: NKern::FMWait(&iMutex); sl@0: if (req) sl@0: { sl@0: r=req->iClientRequest->SetStatus(aStatus); sl@0: req->iOwningThread=aThread; sl@0: ((TClientDataRequest*)req->iClientRequest)->SetDestPtr((TInt*)aLengthPtr); sl@0: // Add the request to the queue sl@0: iReqQueue->Add(req); sl@0: } sl@0: else sl@0: r=KErrGeneral; // Must have exceeded KMaxSndScRequestsPending. sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Release a buffer which was being used by client. sl@0: @param aChunkOffset The chunk offset corresponding to the buffer to be freed. sl@0: @return KErrNone if successful; sl@0: KErrNotFound if no 'in use' buffer had the specified chunk offset. sl@0: KErrNotReady if the channel is not configured (either for audio or its buffer config). sl@0: */ sl@0: TInt DSoundScLdd::ReleaseBuffer(TInt aChunkOffset) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ReleaseBuffer(%x)",aChunkOffset)); sl@0: sl@0: TInt r=KErrNotReady; sl@0: if (iState!=EOpen && iBufManager) sl@0: { sl@0: TAudioBuffer* buf=NULL; sl@0: NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. sl@0: buf=((DRecordBufferManager*)iBufManager)->ReleaseBuffer(aChunkOffset); sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: if (buf) sl@0: { sl@0: buf->Flush(DBufferManager::EFlushBeforeDmaRead); sl@0: r=KErrNone; sl@0: } sl@0: else sl@0: r=KErrNotFound; sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Handle a custom configuration request. sl@0: @param aFunction A number identifying the request. sl@0: @param aParam A 32-bit value passed to the driver. Its meaning depends on the request. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DSoundScLdd::CustomConfig(TInt aFunction,TAny* aParam) sl@0: { sl@0: sl@0: TInt r; sl@0: if (aFunction>KSndCustomConfigMaxReserved) sl@0: r=Pdd()->CustomConfig(aFunction,aParam); sl@0: else sl@0: { sl@0: r=KErrNotSupported; sl@0: #ifdef _DEBUG sl@0: switch (aFunction) sl@0: { sl@0: case KSndCustom_ForceHwConfigNotifSupported: sl@0: iCaps.iHwConfigNotificationSupport=ETrue; sl@0: r=KErrNone; sl@0: break; sl@0: case KSndCustom_CompleteChangeOfHwConfig: sl@0: NotifyChangeOfHwConfigCallback((TBool)aParam); sl@0: r=KErrNone; sl@0: break; sl@0: case KSndCustom_ForceStartTransferError: sl@0: iTestSettings|=KSoundScTest_StartTransferError; sl@0: r=KErrNone; sl@0: break; sl@0: case KSndCustom_ForceTransferDataError: sl@0: iTestSettings|=KSoundScTest_TransferDataError; sl@0: r=KErrNone; sl@0: break; sl@0: case KSndCustom_ForceTransferTimeout: sl@0: iTestSettings|=KSoundScTest_TransferTimeout; sl@0: r=KErrNone; sl@0: break; sl@0: } sl@0: #endif sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: @publishedPartner sl@0: @prototype sl@0: sl@0: Called from the PDD each time it has completed a data transfer into the current record buffer. sl@0: The function performed here is to check whether the transfer into the current buffer is now complete. Also to queue sl@0: further requests on the PDD which should now have the capability to accept more transfers. If transfer into the sl@0: current buffer is now complete then we need to update the buffer lists and possibly complete a request back the client. sl@0: While recording hasn't been paused and no error has occured then this completed buffer ought to be full. However, when sl@0: recording has just been paused, the PDD can also call this function to complete a partially filled record buffer. In fact sl@0: in some circumstances, pausing may result in the PDD calling this function where it turns out that no data has been sl@0: recorded into this buffer. In this case we don't want to signal a null transfer back to the client. sl@0: @param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be sl@0: uniquely identified. sl@0: @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other sl@0: system wide error codes. sl@0: @param aBytesRecorded The number of bytes recorded into the record buffer. sl@0: */ sl@0: void DSoundScLdd::RecordCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesRecorded) sl@0: { sl@0: #ifdef _DEBUG sl@0: #ifdef TEST_WITH_PAGING_CACHE_FLUSHES sl@0: Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); sl@0: #endif sl@0: #endif sl@0: sl@0: #ifdef _DEBUG sl@0: // Test settings - only possible in debug mode. sl@0: if (iTestSettings & KSoundScTest_TransferDataError) sl@0: { sl@0: iTestSettings&=(~KSoundScTest_TransferDataError); sl@0: aTransferResult=KErrTimedOut; sl@0: } sl@0: #endif sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::RecordCallback(ID:%xH,Len:%d) - %d (iCurrentRecBufTf.iTfState %d)",aTransferID,aBytesRecorded,aTransferResult, iCurrentRecBufTf.iTfState)); sl@0: sl@0: // If the transfer fragment is not for the current record buffer and were not paused then ignore it. Either the PDD sl@0: // has got very confused or more likely its a trailing fragment from an earlier buffer we have already failed. If sl@0: // we're paused, the PDD doesn't need to bother with a transfer ID, we assume its for the current buffer. sl@0: if (iCurrentRecBufTf.iTfState != TSndScTransfer::ETfDone && sl@0: (aTransferID==iCurrentRecBufTf.iId || (aTransferID == 0 && iState==EPaused))) sl@0: { sl@0: // Update the count of bytes recorded. sl@0: iBytesTransferred+=aBytesRecorded; sl@0: sl@0: // Update the transfer status of the current buffer. sl@0: if (aTransferResult!=KErrNone) sl@0: { sl@0: // Transfer failed. Mark the buffer as being complete. sl@0: iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone; sl@0: } sl@0: else sl@0: iCurrentRecBufTf.SetCompleted(aBytesRecorded); // Transfer successful so update the progress. sl@0: sl@0: // Check if this is the PDD completing a fragment due to record being paused. In this situation we only allow the sl@0: // PDD to complete one fragment. sl@0: TAudioBuffer* buf; sl@0: if (iState==EPaused && ++iCompletesWhilePausedCount<2) sl@0: { sl@0: // Complete (i.e. abort) the transfer to the current buffer. sl@0: iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone; sl@0: sl@0: // Reset the transfer status for the pending record buffer. This will be switched to the current buffer later sl@0: // in this function - ready for when record is resumed. sl@0: buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); sl@0: iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID sl@0: } sl@0: sl@0: // Check if we have just completed the transfer into the current buffer. sl@0: if (iCurrentRecBufTf.iTfState==TSndScTransfer::ETfDone) sl@0: HandleCurrentRecordBufferDone(aTransferResult); sl@0: } sl@0: sl@0: // If we're not paused then the PDD should now have the capacity to accept another transfer so queue as many sl@0: // transfers on it as it can accept. sl@0: if (iState==EActive) sl@0: { sl@0: #ifdef _DEBUG sl@0: // Test settings - only possible in debug mode. Test LDD being slow servicing transfer completes from PDD. Disabled. sl@0: /* if (iTestSettings & KSoundScTest_TransferTimeout) sl@0: { sl@0: iTestSettings&=(~KSoundScTest_TransferTimeout); sl@0: Kern::NanoWait(500000000); // Pause for 0.5 second sl@0: } */ sl@0: #endif sl@0: TInt r=StartNextRecordTransfers(); sl@0: if (r!=KErrNone) sl@0: { sl@0: // Problem starting the next transfer. That's fairly serious so complete all pending record requests and sl@0: // stop recording. sl@0: Pdd()->StopTransfer(); sl@0: iReqQueue->CompleteAll(r,&iMutex); sl@0: iState=EConfigured; sl@0: } sl@0: } sl@0: return; sl@0: } sl@0: sl@0: /** Perform the necessary processing required when transfer into the current buffer is complete. This involves updating sl@0: the buffer lists and possibly complete a request back the client. sl@0: @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other sl@0: system wide error codes. sl@0: */ sl@0: void DSoundScLdd::HandleCurrentRecordBufferDone(TInt aTransferResult) sl@0: { sl@0: TAudioBuffer* buf; sl@0: sl@0: // Flush the buffer before acquiring the mutex. sl@0: buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer(); sl@0: buf->Flush(DBufferManager::EFlushAfterDmaRead); sl@0: sl@0: NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. sl@0: sl@0: // Update the buffer list (by either adding the current buffer to the completed list or the free list). sl@0: TInt bytesRecorded=iCurrentRecBufTf.GetLengthTransferred(); sl@0: ((DRecordBufferManager*)iBufManager)->SetBufferFilled(bytesRecorded,aTransferResult); sl@0: sl@0: // The pending buffer now becomes the current one and we need to get a new pending one. sl@0: iCurrentRecBufTf=iNextRecBufTf; sl@0: buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); sl@0: iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID sl@0: sl@0: // Check if there is a client record request pending. sl@0: if (!iReqQueue->IsEmpty()) sl@0: { sl@0: // A record request is pending. Check if we have had an overflow since the last record request was completed. sl@0: if (((DRecordBufferManager*)iBufManager)->iBufOverflow) sl@0: { sl@0: TSoundScRequest* req=iReqQueue->Remove(); sl@0: DThread* thread=req->iOwningThread; // Take a copy before we free it. sl@0: TClientRequest* clreq = req->iClientRequest; // Take a copy before we free it. sl@0: ((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse; sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: iReqQueue->Free(req); sl@0: CompleteRequest(thread,NULL,KErrOverflow,clreq); // Complete the request. sl@0: } sl@0: else sl@0: { sl@0: // Check there really is a buffer available. (There's no guarentee the one just completed hasn't sl@0: // immediately been queued again: if the client has too many 'in-use' or the one completed was a NULL sl@0: // transfer due to pausing). sl@0: TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient(); sl@0: if (buf) sl@0: { sl@0: // There still a buffer available so complete the request. sl@0: TSoundScRequest* req=iReqQueue->Remove(); sl@0: DThread* thread=req->iOwningThread; // Take a copy before we free it. sl@0: TClientRequest* clreq = req->iClientRequest; // Take a copy before we free it. sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: iReqQueue->Free(req); sl@0: if (buf->iResult==KErrNone) sl@0: { sl@0: ((TClientDataRequest*)clreq)->Data() = buf->iBytesAdded; sl@0: CompleteRequest(thread,NULL,buf->iChunkOffset,clreq); // Complete the request. sl@0: } sl@0: else sl@0: CompleteRequest(thread,NULL,buf->iResult,clreq); // Complete the request. sl@0: } sl@0: else sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: } sl@0: } sl@0: else sl@0: NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. sl@0: } sl@0: sl@0: /** sl@0: This function starts the next record data transfer. It starts with the current record buffer - checking whether all of sl@0: this has now been transferred or queued for transfer. If not it breaks this down into transfers sizes which are sl@0: compatible with the PDD and then repeatedly attempts to queue these on the PDD until the PDF indicates that it can sl@0: accept no more transfers for the moment. If the record buffer is fully started in this way and the PDD still has the sl@0: capacity to accept more transfers then it moves on to start the pending record buffer. sl@0: @return Normally KErrNone unless the PDD incurs an error while attempting to start a new transfer. sl@0: */ sl@0: TInt DSoundScLdd::StartNextRecordTransfers() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextRecordTransfers")); sl@0: sl@0: // First start with the current record buffer - keep queuing transfers either until this buffer is sl@0: // fully started or until the PDD can accept no more transfers. sl@0: TInt r=KErrNone; sl@0: while (r==KErrNone && iCurrentRecBufTf.iTfStateGetFragmentLength(pos,iCurrentRecBufTf.GetNotStartedLen(),physAddr); sl@0: sl@0: r=Pdd()->TransferData(iCurrentRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("GetFragmentLength(pos,iNextRecBufTf.GetNotStartedLen(),physAddr); sl@0: sl@0: r=Pdd()->TransferData(iNextRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::NotifyChangeOfHwConfigCallback(Pres:%d)",aHeadsetPresent)); sl@0: sl@0: __ASSERT_DEBUG(iCaps.iHwConfigNotificationSupport,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: sl@0: if (iNotifyChangeOfHwClientRequest->IsReady()) sl@0: { sl@0: iNotifyChangeOfHwClientRequest->Data() = aHeadsetPresent; sl@0: CompleteRequest(iChangeOfHwConfigThread,NULL,KErrNone,iNotifyChangeOfHwClientRequest); // Complete the request. sl@0: } sl@0: } sl@0: sl@0: /** sl@0: This function validates that a new sound format configuration is both sensible and supported by this device. sl@0: @param aConfig A reference to the new sound format configuration object. sl@0: */ sl@0: TInt DSoundScLdd::ValidateConfig(const TCurrentSoundFormatV02& aConfig) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ValidateConfig")); sl@0: sl@0: // Check that the audio channel configuration requested is sensible and supported by this device. sl@0: if (aConfig.iChannels<0) sl@0: return(KErrNotSupported); sl@0: TInt chans=(aConfig.iChannels-1); sl@0: if (!(iCaps.iChannels & (1<iChunkBase); sl@0: } sl@0: sl@0: /** sl@0: The ISR to handle the EOF play timer. sl@0: @param aChannel A pointer to the sound driver logical channel object. sl@0: */ sl@0: void DSoundScLdd::PlayEofTimerExpired(TAny* aChannel) sl@0: { sl@0: DSoundScLdd& drv=*(DSoundScLdd*)aChannel; sl@0: sl@0: drv.iPlayEofDfc.Add(); sl@0: } sl@0: sl@0: /** sl@0: The DFC used to handle the EOF play timer. sl@0: @param aChannel A pointer to the sound driver logical channel object. sl@0: */ sl@0: void DSoundScLdd::PlayEofTimerDfc(TAny* aChannel) sl@0: { sl@0: DSoundScLdd& drv=*(DSoundScLdd*)aChannel; sl@0: sl@0: drv.Pdd()->StopTransfer(); sl@0: drv.iState=EConfigured; sl@0: drv.iPlayEofTimerActive=EFalse; sl@0: } sl@0: sl@0: /** sl@0: The DFC used to handle power down requests from the power manager before a transition into system sl@0: shutdown/standby. sl@0: @param aChannel A pointer to the sound driver logical channel object. sl@0: */ sl@0: void DSoundScLdd::PowerDownDfc(TAny* aChannel) sl@0: { sl@0: DSoundScLdd& drv=*(DSoundScLdd*)aChannel; sl@0: drv.Shutdown(); sl@0: drv.iPowerHandler->PowerDownDone(); sl@0: } sl@0: sl@0: /** sl@0: The DFC used to handle power up requests from the power manager following a transition out of system standby. sl@0: @param aChannel A pointer to the sound driver logical channel object. sl@0: */ sl@0: void DSoundScLdd::PowerUpDfc(TAny* aChannel) sl@0: { sl@0: DSoundScLdd& drv=*(DSoundScLdd*)aChannel; sl@0: sl@0: // Restore the channel to a default state. sl@0: drv.DoCancel(RSoundSc::EAllRequests); sl@0: drv.Pdd()->PowerUp(); sl@0: drv.DoSetSoundConfig(drv.iSoundConfig); sl@0: drv.SetVolume(drv.iVolume); sl@0: drv.iState=(!drv.iBufConfig)?EOpen:EConfigured; sl@0: sl@0: drv.iPowerHandler->PowerUpDone(); sl@0: } sl@0: sl@0: /** sl@0: Complete an asynchronous request back to the client. sl@0: @param aThread The client thread which issued the request. sl@0: @param aStatus The TRequestStatus instance that will receive the request status code or NULL if aClientRequest used. sl@0: @param aReason The request status code. sl@0: @param aClientRequest The TClientRequest instance that will receive the request status code or NULL if aStatus used. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: sl@0: void DSoundScLdd::CompleteRequest(DThread* aThread, TRequestStatus* aStatus, TInt aReason, TClientRequest* aClientRequest) sl@0: { sl@0: if (aClientRequest) sl@0: { sl@0: if (aClientRequest->IsReady()) sl@0: { sl@0: Kern::QueueRequestComplete(aThread,aClientRequest,aReason); sl@0: } sl@0: else sl@0: { sl@0: // should always be ready sl@0: __ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: } sl@0: } sl@0: else if (aStatus) sl@0: { sl@0: Kern::RequestComplete(aStatus,aReason); // Complete the request back to the client. sl@0: } sl@0: else sl@0: { sl@0: // never get here - either aStatus or aClientRequest must be valid sl@0: __ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: } sl@0: sl@0: aThread->AsyncClose(); // Asynchronously close our reference on the client thread - don't want to be blocked if this is final reference. sl@0: sl@0: #ifdef _DEBUG sl@0: __e32_atomic_add_ord32(&iThreadOpenCount, TUint32(-1)); sl@0: #endif sl@0: } sl@0: sl@0: /** sl@0: Constructor for the play request object. sl@0: */ sl@0: TSoundScPlayRequest::TSoundScPlayRequest() sl@0: : TSoundScRequest() sl@0: { sl@0: iFlags=0; sl@0: iCompletionReason=KErrGeneral; sl@0: } sl@0: sl@0: /* sl@0: Second phase construction of the requests sl@0: */ sl@0: TInt TSoundScPlayRequest::Construct() sl@0: { sl@0: return Kern::CreateClientRequest(iClientRequest); sl@0: } sl@0: sl@0: /* sl@0: Second phase construction of the requests sl@0: */ sl@0: TInt TSoundScRequest::Construct() sl@0: { sl@0: TClientDataRequest* tempClientDataRequest=0; sl@0: TInt r = Kern::CreateClientDataRequest(tempClientDataRequest); sl@0: iClientRequest = tempClientDataRequest; sl@0: return r; sl@0: } sl@0: sl@0: /** sl@0: Destructor of play requests sl@0: */ sl@0: TSoundScRequest::~TSoundScRequest() sl@0: { sl@0: Kern::DestroyClientRequest(iClientRequest); sl@0: } sl@0: sl@0: /** sl@0: Constructor for the request object queue. sl@0: */ sl@0: TSoundScRequestQueue::TSoundScRequestQueue(DSoundScLdd* aLdd) sl@0: { sl@0: iLdd=aLdd; sl@0: memclr(&iRequest[0],sizeof(TSoundScRequest*)*KMaxSndScRequestsPending); sl@0: } sl@0: sl@0: /** sl@0: Destructor for the request object queue. sl@0: */ sl@0: TSoundScRequestQueue::~TSoundScRequestQueue() sl@0: { sl@0: for (TInt i=0 ; iConstruct(); sl@0: if ( retConstruct != KErrNone) sl@0: { sl@0: return(retConstruct); sl@0: } sl@0: iUnusedRequestQ.Add(iRequest[i]); sl@0: } sl@0: sl@0: return(KErrNone); sl@0: } sl@0: sl@0: /** sl@0: Second stage constructor for the play request object queue. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: TInt TSoundScPlayRequestQueue::Create() sl@0: { sl@0: // Create the set of available play request objects and add them to the unused request queue. sl@0: for (TInt i=0 ; iConstruct(); sl@0: if ( retConstruct != KErrNone) sl@0: { sl@0: return(retConstruct); sl@0: } sl@0: iUnusedRequestQ.Add(iRequest[i]); sl@0: } sl@0: sl@0: return(KErrNone); sl@0: } sl@0: sl@0: /** sl@0: Get an unused request object. sl@0: @return A pointer to a free request object or NULL if there are none available. sl@0: */ sl@0: TSoundScRequest* TSoundScRequestQueue::NextFree() sl@0: { sl@0: NKern::FMWait(&iUnusedRequestQLock); sl@0: TSoundScRequest* req = (TSoundScRequest*)iUnusedRequestQ.GetFirst(); sl@0: NKern::FMSignal(&iUnusedRequestQLock); sl@0: return req; sl@0: } sl@0: sl@0: /** sl@0: Add a request object to the tail of the pending request queue. sl@0: @param aReq A pointer to the request object to be added to the queue. sl@0: */ sl@0: void TSoundScRequestQueue::Add(TSoundScRequest* aReq) sl@0: { sl@0: iPendRequestQ.Add(aReq); sl@0: } sl@0: sl@0: /** sl@0: If the pending request queue is not empty, remove the request object from the head of this queue. sl@0: @return A pointer to request object removed or NULL if the list was empty. sl@0: */ sl@0: TSoundScRequest* TSoundScRequestQueue::Remove() sl@0: { sl@0: return((TSoundScRequest*)iPendRequestQ.GetFirst()); sl@0: } sl@0: sl@0: /** sl@0: Remove a request object from anywhere in the pending request queue. sl@0: @param aReq A pointer to the request object to be removed from the queue. sl@0: @return A pointer to request object removed or NULL if it wasn't found. sl@0: */ sl@0: TSoundScRequest* TSoundScRequestQueue::Remove(TSoundScRequest* aReq) sl@0: { sl@0: TSoundScRequest* retReq; sl@0: sl@0: // Scan through the pending queue looking for a request object which matches. sl@0: retReq=(TSoundScRequest*)iPendRequestQ.First(); sl@0: while (!IsAnchor(retReq) && retReq!=aReq) sl@0: retReq=(TSoundScRequest*)retReq->iNext; sl@0: sl@0: // If we got a match then remove the request object from the queue and return it. sl@0: if (!IsAnchor(retReq)) sl@0: retReq->Deque(); sl@0: else sl@0: retReq=NULL; sl@0: return(retReq); sl@0: } sl@0: sl@0: /** sl@0: Free up a request object - making it available for further requests. sl@0: @param aReq A pointer to the request object being freed up. sl@0: */ sl@0: void TSoundScRequestQueue::Free(TSoundScRequest* aReq) sl@0: { sl@0: NKern::FMWait(&iUnusedRequestQLock); sl@0: iUnusedRequestQ.Add(aReq); sl@0: NKern::FMSignal(&iUnusedRequestQLock); sl@0: } sl@0: sl@0: /** sl@0: Find a request object (specified by its associated request status pointer) within in the pending request queue. sl@0: @param aStatus The request status pointer of the request object to be found in the queue. sl@0: @return A pointer to the request object if it was found or NULL if it wasn't found. sl@0: */ sl@0: TSoundScRequest* TSoundScRequestQueue::Find(TRequestStatus* aStatus) sl@0: { sl@0: TSoundScRequest* retReq; sl@0: sl@0: // Scan through the queue looking for a request object containing a TRequestStatus* which matches. sl@0: retReq=(TSoundScRequest*)iPendRequestQ.First(); sl@0: while (!IsAnchor(retReq) && retReq->iClientRequest->StatusPtr()!=aStatus) sl@0: retReq=(TSoundScRequest*)retReq->iNext; sl@0: sl@0: return((IsAnchor(retReq))?NULL:retReq); sl@0: } sl@0: sl@0: /** sl@0: Remove each request object from the pending request queue, completing each request removed with a specified completion sl@0: reason. sl@0: @param aCompletionReason The error value to be returned when completing any requests in the queue. sl@0: @param aMutex A pointer to a mutex to be aquired when removing requests from the queue. May be NULL. sl@0: */ sl@0: void TSoundScRequestQueue::CompleteAll(TInt aCompletionReason,NFastMutex* aMutex) sl@0: { sl@0: if (aMutex) sl@0: NKern::FMWait(aMutex); // Acquire the mutex. sl@0: sl@0: TSoundScRequest* req; sl@0: while ((req=Remove())!=NULL) sl@0: { sl@0: if (aMutex) sl@0: NKern::FMSignal(aMutex); // Release the mutex while we complete the request. sl@0: iLdd->CompleteRequest(req->iOwningThread,NULL,aCompletionReason,req->iClientRequest); sl@0: Free(req); sl@0: if (aMutex) sl@0: NKern::FMWait(aMutex); // Re-acquire the mutex. sl@0: sl@0: } sl@0: sl@0: if (aMutex) sl@0: NKern::FMSignal(aMutex); // Release mutex. sl@0: } sl@0: sl@0: /** sl@0: Constructor for the play request object queue. sl@0: */ sl@0: TSoundScPlayRequestQueue::TSoundScPlayRequestQueue(DSoundScLdd* aLdd) sl@0: : TSoundScRequestQueue(aLdd) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Return the play request object from the request queue which is next to be transferrred. If this sl@0: play request is being handled using multiple data transfers then the transfer of earlier parts of sl@0: this request may already be in progress. sl@0: @return Either a pointer to the next play request object for transfer, or NULL if no more are pending. sl@0: */ sl@0: TSoundScPlayRequest* TSoundScPlayRequestQueue::NextRequestForTransfer() sl@0: { sl@0: TSoundScPlayRequest* retReq; sl@0: sl@0: retReq=(TSoundScPlayRequest*)iPendRequestQ.First(); sl@0: while (!IsAnchor(retReq) && retReq->iTf.iTfState>TSndScTransfer::ETfPartlyStarted) sl@0: retReq=(TSoundScPlayRequest*)retReq->iNext; sl@0: sl@0: return((IsAnchor(retReq))?NULL:retReq); sl@0: } sl@0: sl@0: /** sl@0: Search the play request queue for a particular play request object specified by its transfer ID. sl@0: @param aTransferID The transfer ID of the particular play request object to be found. sl@0: @param aIsNextToComplete If the search is successful then this indicates whether the request sl@0: object found is the next in the queue to be completed to the client. ETrue if next to be sl@0: completed, EFalse otherwise. sl@0: @return Either a pointer to the specified request object, or NULL if it was not found. sl@0: */ sl@0: TSoundScPlayRequest* TSoundScPlayRequestQueue::Find(TUint aTransferID,TBool& aIsNextToComplete) sl@0: { sl@0: TSoundScPlayRequest* retReq; sl@0: TSoundScPlayRequest* nextToCompleteReq=NULL; sl@0: sl@0: retReq=(TSoundScPlayRequest*)iPendRequestQ.First(); sl@0: sl@0: // Walk all the way through the list either until we find the specified object or until we get to the end sl@0: for ( ; !IsAnchor(retReq) ; retReq=(TSoundScPlayRequest*)retReq->iNext ) sl@0: { sl@0: // The first request we find which isn't complete must be the next to complete sl@0: if (!nextToCompleteReq && retReq->iTf.iTfState!=TSndScTransfer::ETfDone) sl@0: nextToCompleteReq=retReq; sl@0: if (retReq->iTf.iId==aTransferID) sl@0: break; sl@0: } sl@0: sl@0: if (IsAnchor(retReq)) sl@0: return(NULL); // Object not found sl@0: else sl@0: { sl@0: aIsNextToComplete=(retReq==nextToCompleteReq); sl@0: return(retReq); // Object found sl@0: } sl@0: } sl@0: /** sl@0: Constructor for the audio data transfer class. sl@0: */ sl@0: TSndScTransfer::TSndScTransfer() sl@0: { sl@0: iId=0; sl@0: iTfState=ETfNotStarted; sl@0: iAudioBuffer=NULL; sl@0: iLengthTransferred=0; sl@0: iTransfersInProgress=0; sl@0: } sl@0: sl@0: /** sl@0: Initialisation function for the audio data transfer class. sl@0: @param aId A value to uniquely identify this particular transfer. sl@0: @param aChunkOffset The start postition of the transfer - an offset within the shared chunk. sl@0: @param aLength The total length of the transfer in bytes. sl@0: @param anAudioBuffer The audio buffer associated with the transfer. sl@0: */ sl@0: void TSndScTransfer::Init(TUint aId,TInt aChunkOffset,TInt aLength,TAudioBuffer* anAudioBuffer) sl@0: { sl@0: iId=aId; sl@0: iTfState=ETfNotStarted; sl@0: iAudioBuffer=anAudioBuffer; sl@0: iStartedOffset=aChunkOffset; sl@0: iEndOffset=aChunkOffset+aLength; sl@0: iLengthTransferred=0; sl@0: iTransfersInProgress=0; sl@0: } sl@0: sl@0: /** sl@0: Update the progress of the audio data transfer with the amount of data now queued for transfer on the audio device. sl@0: @param aLength The amount of data (in bytes) that has just been queued on the audio device. sl@0: */ sl@0: void TSndScTransfer::SetStarted(TInt aLength) sl@0: { sl@0: iTransfersInProgress++; sl@0: iStartedOffset+=aLength; sl@0: TInt notqueued=(iEndOffset - iStartedOffset); sl@0: __ASSERT_ALWAYS(notqueued>=0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iTfState=(notqueued) ? ETfPartlyStarted : ETfFullyStarted; sl@0: } sl@0: sl@0: /** sl@0: Update the progress of the audio data transfer with the amount of data now successfully transfered by the audio device. sl@0: @param aLength The amount of data (in bytes) that has just been transferred by the audio device. sl@0: @return ETrue if the transfer is now fully complete, otherwise EFalse. sl@0: */ sl@0: TBool TSndScTransfer::SetCompleted(TInt aLength) sl@0: { sl@0: iLengthTransferred+=aLength; sl@0: iTransfersInProgress--; sl@0: __ASSERT_ALWAYS(iTransfersInProgress>=0,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: sl@0: if (GetNotStartedLen()==0 && iTransfersInProgress==0) sl@0: { sl@0: iTfState=ETfDone; // Transfer is now fully completed sl@0: return(ETrue); sl@0: } sl@0: else sl@0: return(EFalse); sl@0: } sl@0: sl@0: /** sl@0: Constructor for the sound driver power handler class. sl@0: @param aChannel A pointer to the sound driver logical channel which owns this power handler. sl@0: */ sl@0: DSoundScPowerHandler::DSoundScPowerHandler(DSoundScLdd* aChannel) sl@0: : DPowerHandler(KDevSoundScName), sl@0: iChannel(aChannel) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: A request from the power manager for the power down of the audio device. sl@0: This is called during a transition of the phone into standby or power off. sl@0: @param aState The target power state; can be EPwStandby or EPwOff only. sl@0: */ sl@0: void DSoundScPowerHandler::PowerDown(TPowerState aPowerState) sl@0: { sl@0: (void)aPowerState; sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerDown(State-%d)",aPowerState)); sl@0: sl@0: // Power-down involves hardware access so queue a DFC to perform this from the driver thread. sl@0: iChannel->iPowerDownDfc.Enque(); sl@0: } sl@0: sl@0: /** sl@0: A request from the power manager for the power up of the audio device. sl@0: This is called during a transition of the phone out of standby. sl@0: */ sl@0: void DSoundScPowerHandler::PowerUp() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerUp")); sl@0: sl@0: // Power-up involves hardware access so queue a DFC to perform this from the driver thread. sl@0: iChannel->iPowerUpDfc.Enque(); sl@0: } sl@0: sl@0: /** sl@0: Constructor for the buffer manager. sl@0: */ sl@0: DBufferManager::DBufferManager(DSoundScLdd* aLdd) sl@0: : iLdd(aLdd) sl@0: { sl@0: // iChunk=NULL; sl@0: // iNumBuffers=0; sl@0: // iAudioBuffers=NULL; sl@0: // iMaxTransferLen=0; sl@0: } sl@0: sl@0: /** sl@0: Destructor for the buffer manager. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: DBufferManager::~DBufferManager() sl@0: { sl@0: if (iChunk) sl@0: Kern::ChunkClose(iChunk); sl@0: delete[] iAudioBuffers; sl@0: } sl@0: sl@0: /** sl@0: Second stage constructor for the buffer manager. This version creates a shared chunk and a buffer object for each sl@0: buffer specified within this. Then it commits memory within the chunk for each of these buffers. This also involves the sl@0: creation of a set of buffer lists to manage the buffers. sl@0: @param aBufConfig The shared chunk buffer configuration object specifying the geometry of the buffer configuration sl@0: required. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: TInt DBufferManager::Create(TSoundSharedChunkBufConfig* aBufConfig) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Create(Bufs-%d,Sz-%d)",aBufConfig->iNumBuffers,aBufConfig->iBufferSizeInBytes)); sl@0: sl@0: // Create the required number of buffer objects, and the buffer lists to manage these. sl@0: TInt r=CreateBufferLists(aBufConfig->iNumBuffers); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: TInt chunkSz; sl@0: TInt bufferSz=aBufConfig->iBufferSizeInBytes; sl@0: TInt* bufferOffsetList=&aBufConfig->iBufferOffsetListStart; sl@0: sl@0: // Calculate the required size for the chunk and the buffer offsets. sl@0: if (aBufConfig->iFlags & KScFlagUseGuardPages) sl@0: { sl@0: // Commit each buffer separately with an uncommitted guard pages around each buffer. sl@0: TInt guardPageSize=Kern::RoundToPageSize(1); sl@0: bufferSz=Kern::RoundToPageSize(aBufConfig->iBufferSizeInBytes); // Commit size to be a multiple of the MMU page size. sl@0: chunkSz=guardPageSize; // Leave an un-committed guard page at the start. sl@0: for (TInt i=0 ; iiNumBuffers ; i++) sl@0: { sl@0: bufferOffsetList[i]=chunkSz; sl@0: chunkSz += (bufferSz + guardPageSize); // Leave an un-committed guard page after each buffer. sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Commit all the buffers contiguously into a single region (ie with no guard pages between each buffer). sl@0: chunkSz=0; sl@0: for (TInt i=0 ; iiNumBuffers ; i++) sl@0: { sl@0: bufferOffsetList[i]=chunkSz; sl@0: chunkSz += bufferSz; sl@0: } sl@0: chunkSz=Kern::RoundToPageSize(chunkSz); // Commit size to be a multiple of the MMU page size. sl@0: } sl@0: aBufConfig->iFlags|=KScFlagBufOffsetListInUse; sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("Chunk size is %d bytes",chunkSz)); sl@0: sl@0: // Create the shared chunk. The PDD supplies most of the chunk create info - but not the maximum size. sl@0: TChunkCreateInfo info; sl@0: info.iMaxSize=chunkSz; sl@0: iLdd->Pdd()->GetChunkCreateInfo(info); // Call down to the PDD for the rest. sl@0: sl@0: r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr); sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("Create chunk - %d",r)); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: if (aBufConfig->iFlags & KScFlagUseGuardPages) sl@0: { sl@0: // Map each of the buffers into the chunk separately - try to allocate physically contiguous RAM pages. Create a buffer object for each buffer. sl@0: TBool isContiguous; sl@0: for (TInt i=0 ; iiNumBuffers ; i++) sl@0: { sl@0: r=CommitMemoryForBuffer(bufferOffsetList[i],bufferSz,isContiguous); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Map memory for the all buffers into the chunk - try to allocate physically contiguous RAM pages. sl@0: TBool isContiguous; sl@0: r=CommitMemoryForBuffer(0,chunkSz,isContiguous); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: // Create a buffer object for each buffer. sl@0: for (TInt i=0 ; iiNumBuffers ; i++) sl@0: { sl@0: r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: } sl@0: } sl@0: sl@0: // Read back and store the maximum transfer length supported by this device from the PDD. sl@0: iMaxTransferLen=iLdd->Pdd()->MaxTransferLen(); sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DBufferManager::Create(Handle-%d)",aChunkHandle)); sl@0: sl@0: // Validate the buffer configuration information. sl@0: if (!aBufConfig.iFlags&KScFlagBufOffsetListInUse) sl@0: return(KErrArgument); sl@0: TInt numBuffers=aBufConfig.iNumBuffers; sl@0: TInt bufferSizeInBytes=aBufConfig.iBufferSizeInBytes; sl@0: TInt* bufferOffsetList=&aBufConfig.iBufferOffsetListStart; sl@0: TInt r=ValidateBufferOffsets(bufferOffsetList,numBuffers,bufferSizeInBytes); sl@0: if (r<0) sl@0: return(r); sl@0: sl@0: // Create the required number of buffer objects, and the buffer lists to manage these. sl@0: r=CreateBufferLists(numBuffers); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: sl@0: // Open the shared chunk. sl@0: DChunk* chunk; sl@0: chunk=Kern::OpenSharedChunk(anOwningThread,aChunkHandle,ETrue); sl@0: if (!chunk) sl@0: return(KErrBadHandle); sl@0: iChunk=chunk; sl@0: sl@0: // Read the physical address for the 1st buffer in order to determine the kernel address and the map attributes. sl@0: TInt offset=bufferOffsetList[0]; sl@0: TPhysAddr physAddr; sl@0: TLinAddr kernelAddress; sl@0: r=Kern::ChunkPhysicalAddress(iChunk,offset,bufferSizeInBytes,kernelAddress,iChunkMapAttr,physAddr,NULL); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: iChunkBase=(kernelAddress-offset); sl@0: sl@0: // For each buffer, validate that the buffer specified contains committed memory and store the buffer info. into each buffer object. sl@0: while (numBuffers) sl@0: { sl@0: numBuffers--; sl@0: offset=bufferOffsetList[numBuffers]; sl@0: // Assume it isn't contiguous here - Create() will detect and do the right thing if it is contiguous. sl@0: r=iAudioBuffers[numBuffers].Create(iChunk,offset,bufferSizeInBytes,EFalse,this); sl@0: if (r!=KErrNone) sl@0: return(r); sl@0: } sl@0: sl@0: // Read back and store the maximum transfer length supported by this device from the PDD. sl@0: iMaxTransferLen=iLdd->Pdd()->MaxTransferLen(); sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("DBufferManager::CreateBufferLists(Bufs-%d)",aNumBuffers)); sl@0: sl@0: // Construct the array of buffers. sl@0: iNumBuffers=aNumBuffers; sl@0: iAudioBuffers=new TAudioBuffer[aNumBuffers]; sl@0: if (!iAudioBuffers) sl@0: return(KErrNoMemory); sl@0: sl@0: return(KErrNone); sl@0: } sl@0: sl@0: /** sl@0: Validate a shared chunk buffer offset list. sl@0: @param aBufferOffsetList The buffer offset list to be validated. sl@0: @param aNumBuffers The number of offsets that the list contains. sl@0: @param aBufferSizeInBytes The size in bytes of each buffer. sl@0: @return If the buffer list is found to be valid, the calculated minimum size of the corresponding chunk is returned sl@0: (i.e. a value>=0). Otherwise, KErrArgument is returned. sl@0: */ sl@0: TInt DBufferManager::ValidateBufferOffsets(TInt* aBufferOffsetList,TInt aNumBuffers,TInt aBufferSizeInBytes) sl@0: { sl@0: TUint32 alignmask=(1<iCaps.iRequestAlignment)-1; // iRequestAlignment holds log to base 2 of alignment required sl@0: sl@0: // Verify each of the buffer offsets supplied sl@0: TInt offset=0; sl@0: for (TInt i=0 ; iiDirection==ESoundDirRecord && ((TUint32)aBufferOffsetList[i] & alignmask) != 0) sl@0: return(KErrArgument); sl@0: sl@0: // Check the offset doesn't overlap the previous buffer - offset holds the offset to next byte after sl@0: // the previous buffer. sl@0: if (aBufferOffsetList[i]=bufEnd) sl@0: continue; sl@0: if (regEnd<=bufEnd) sl@0: { sl@0: anAudioBuffer=&iAudioBuffers[i]; sl@0: return(KErrNone); sl@0: } sl@0: } sl@0: return(KErrArgument); sl@0: } sl@0: sl@0: /** sl@0: Commit memory for a single buffer within the shared chunk. sl@0: @param aChunkOffset The offset (in bytes) from start of chunk, which indicates the start of the memory region to be sl@0: committed. Must be a multiple of the MMU page size. sl@0: @param aSize The number of bytes to commit. Must be a multiple of the MMU page size. sl@0: @param aIsContiguous On return, this is set to ETrue if the function succeeded in committing physically contiguous memory; sl@0: EFalse if the committed memory is not contiguous. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt DBufferManager::CommitMemoryForBuffer(TInt aChunkOffset,TInt aSize,TBool& aIsContiguous) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::CommitMemoryForBuffer(Offset-%x,Sz-%d)",aChunkOffset,aSize)); sl@0: sl@0: // Try for physically contiguous memory first. sl@0: TInt r; sl@0: TPhysAddr physicalAddress; sl@0: r=Kern::ChunkCommitContiguous(iChunk,aChunkOffset,aSize,physicalAddress); sl@0: if (r==KErrNone) sl@0: { sl@0: aIsContiguous=ETrue; sl@0: return(r); sl@0: } sl@0: sl@0: // Try to commit memory that isn't contiguous instead. sl@0: aIsContiguous=EFalse; sl@0: r=Kern::ChunkCommit(iChunk,aChunkOffset,aSize); sl@0: return(r); sl@0: } sl@0: sl@0: /** sl@0: Purge a region of the audio chunk. That is, if this region contains cacheable memory, flush it. sl@0: @param aChunkOffset The offset within the chunk for the start of the data to be flushed. sl@0: @param aLength The length in bytes of the region to be flushed. sl@0: @param aFlushOp The type of flush operation required - @see TFlushOp. sl@0: */ sl@0: void DBufferManager::FlushData(TInt aChunkOffset,TInt aLength,TFlushOp aFlushOp) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::FlushData(%d)",aFlushOp)); sl@0: TLinAddr dataAddr=(iChunkBase+aChunkOffset); sl@0: switch (aFlushOp) sl@0: { sl@0: case EFlushBeforeDmaWrite: sl@0: Cache::SyncMemoryBeforeDmaWrite(dataAddr,aLength,iChunkMapAttr); sl@0: break; sl@0: case EFlushBeforeDmaRead: sl@0: Cache::SyncMemoryBeforeDmaRead(dataAddr,aLength,iChunkMapAttr); sl@0: break; sl@0: case EFlushAfterDmaRead: sl@0: Cache::SyncMemoryAfterDmaRead(dataAddr,aLength); sl@0: break; sl@0: default: sl@0: break; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Constructor for the record buffer manager. sl@0: */ sl@0: DRecordBufferManager::DRecordBufferManager(DSoundScLdd* aLdd) sl@0: : DBufferManager(aLdd) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Reset all the audio buffer lists to reflect the state at the start of the record capture process. sl@0: @pre The buffer/request queue mutex must be held. sl@0: */ sl@0: void DRecordBufferManager::Reset() sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Reset")); sl@0: TAudioBuffer* pBuf; sl@0: sl@0: // Before reseting buffer lists, purge the cache for all cached buffers currently in use by client. sl@0: pBuf=(TAudioBuffer*)iInUseBufferQ.First(); sl@0: SDblQueLink* anchor=&iInUseBufferQ.iA; sl@0: while (pBuf!=anchor) sl@0: { sl@0: pBuf->Flush(DBufferManager::EFlushBeforeDmaRead); sl@0: pBuf=(TAudioBuffer*)pBuf->iNext; sl@0: } sl@0: sl@0: // Start by reseting all the lists. sl@0: iFreeBufferQ.iA.iNext=iFreeBufferQ.iA.iPrev=&iFreeBufferQ.iA; sl@0: iCompletedBufferQ.iA.iNext=iCompletedBufferQ.iA.iPrev=&iCompletedBufferQ.iA; sl@0: iInUseBufferQ.iA.iNext=iInUseBufferQ.iA.iPrev=&iInUseBufferQ.iA; sl@0: sl@0: // Set the pointers to the current and the next record buffers. sl@0: pBuf=iAudioBuffers; // This is the first buffer sl@0: iCurrentBuffer=pBuf++; sl@0: iNextBuffer = pBuf++; sl@0: sl@0: // Add all other buffers to the free list. sl@0: TAudioBuffer* bufferLimit=iAudioBuffers+iNumBuffers; sl@0: while(pBufiBytesAdded=aBytesAdded; sl@0: buffer->iResult=aTransferResult; sl@0: iCompletedBufferQ.Add(buffer); sl@0: } sl@0: else sl@0: iFreeBufferQ.Add(buffer); sl@0: sl@0: // Make the pending buffer the current one. sl@0: iCurrentBuffer=iNextBuffer; sl@0: sl@0: // Obtain the next pending buffer. If there are none left on the free list then we have to take one back sl@0: // from the completed list. sl@0: iNextBuffer=(TAudioBuffer*)iFreeBufferQ.GetFirst(); sl@0: if (!iNextBuffer) sl@0: { sl@0: iNextBuffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst(); sl@0: iBufOverflow=ETrue; // This is the buffer overflow situation. sl@0: } sl@0: __ASSERT_DEBUG(iNextBuffer,Kern::Fault(KSoundLddPanic,__LINE__)); sl@0: iNextBuffer->iBytesAdded=0; sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("iChunkOffset,buffer->iBytesAdded)); sl@0: return(iNextBuffer); sl@0: } sl@0: sl@0: /** sl@0: Get the next record buffer from the completed buffer list. If there is no error associated with the buffer, sl@0: make it 'in use' by the client. Otherwise, return the buffer to the free list. sl@0: @return A pointer to the next completed buffer or NULL if there isn't one available. sl@0: @pre The buffer/request queue mutex must be held. sl@0: */ sl@0: TAudioBuffer* DRecordBufferManager::GetBufferForClient() sl@0: { sl@0: TAudioBuffer* buffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst(); sl@0: if (buffer) sl@0: { sl@0: if (buffer->iResult==KErrNone) sl@0: iInUseBufferQ.Add(buffer); sl@0: else sl@0: iFreeBufferQ.Add(buffer); sl@0: } sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("iChunkOffset : -1))); sl@0: return(buffer); sl@0: } sl@0: sl@0: /** sl@0: Release (move to free list) the 'in use' record buffer specified by the given chunk offset. sl@0: @param aChunkOffset The chunk offset corresponding to the buffer to be freed. sl@0: @return The freed buffer, or NULL if no 'in use' buffer had the specified chunk offset. sl@0: @pre The buffer/request queue mutex must be held. sl@0: */ sl@0: TAudioBuffer* DRecordBufferManager::ReleaseBuffer(TInt aChunkOffset) sl@0: { sl@0: // Scan 'in use' list for the audio buffer sl@0: TAudioBuffer* pBuf; sl@0: pBuf=(TAudioBuffer*)iInUseBufferQ.First(); sl@0: SDblQueLink* anchor=&iInUseBufferQ.iA; sl@0: while (pBuf!=anchor && pBuf->iChunkOffset!=aChunkOffset) sl@0: pBuf=(TAudioBuffer*)pBuf->iNext; sl@0: sl@0: // Move buffer to the free list (if found) sl@0: if (pBuf!=anchor) sl@0: iFreeBufferQ.Add(pBuf->Deque()); sl@0: else sl@0: pBuf=NULL; sl@0: sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::BufferRelease(buf=%08x)",(pBuf ? pBuf->iChunkOffset : -1))); sl@0: return(pBuf); sl@0: } sl@0: sl@0: /** sl@0: Constructor for the audio buffer class. sl@0: Clears all member data sl@0: */ sl@0: TAudioBuffer::TAudioBuffer() sl@0: { sl@0: memclr(this,sizeof(*this)); sl@0: } sl@0: sl@0: /** sl@0: Destructor for the audio buffer class. sl@0: */ sl@0: TAudioBuffer::~TAudioBuffer() sl@0: { sl@0: delete[] iPhysicalPages; sl@0: } sl@0: sl@0: /** sl@0: Second stage constructor for the audio buffer class - validate and acquire information on the memory sl@0: allocated to this buffer. sl@0: @param aChunk The chunk in which this buffer belongs. sl@0: @param aChunkOffset The offset within aChunk to the start of the audio buffer. sl@0: @param aSize The size (in bytes) of the buffer. sl@0: @param aIsContiguous A boolean indicating whether the buffer contains physically contiguous memory. Set to ETrue if it sl@0: does physically contiguous memory, EFalse otherwise. sl@0: @param aBufManager A pointer to the buffer manager which owns this object. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: @pre The thread must be in a critical section. sl@0: */ sl@0: TInt TAudioBuffer::Create(DChunk* aChunk,TInt aChunkOffset,TInt aSize,TBool aIsContiguous,DBufferManager* aBufManager) sl@0: { sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf(">TAudioBuffer::Create(Off-%x,Sz-%d,Contig-%d)",aChunkOffset,aSize,aIsContiguous)); sl@0: sl@0: // Save info. on the offset and size of the buffer. Also the buffer manager. sl@0: iChunkOffset=aChunkOffset; sl@0: iSize=aSize; sl@0: iBufManager=aBufManager; sl@0: sl@0: TInt r=KErrNone; sl@0: iPhysicalPages=NULL; sl@0: if (!aIsContiguous) sl@0: { sl@0: // Allocate an array for a list of the physical pages. sl@0: iPhysicalPages = new TPhysAddr[aSize/Kern::RoundToPageSize(1)+2]; sl@0: if (!iPhysicalPages) sl@0: r=KErrNoMemory; sl@0: } sl@0: sl@0: if (r==KErrNone) sl@0: { sl@0: // Check that the region of the chunk specified for the buffer contains committed memory. If so, get the physical addresses of the sl@0: // pages in this buffer. sl@0: TUint32 kernAddr; sl@0: TUint32 mapAttr; sl@0: r=Kern::ChunkPhysicalAddress(aChunk,aChunkOffset,aSize,kernAddr,mapAttr,iPhysicalAddress,iPhysicalPages); sl@0: // r = 0 or 1 on success. (1 meaning the physical pages are not contiguous). sl@0: if (r==1) sl@0: { sl@0: // The physical pages are not contiguous. sl@0: iPhysicalAddress=KPhysAddrInvalid; // Mark the physical address as invalid. sl@0: r=(aIsContiguous) ? KErrGeneral : KErrNone; sl@0: } sl@0: if (r==0) sl@0: { sl@0: delete[] iPhysicalPages; // We shouldn't retain this info. if the physical pages are contiguous. sl@0: iPhysicalPages=NULL; sl@0: } sl@0: sl@0: } sl@0: __KTRACE_OPT(KSOUND1, Kern::Printf("iMaxTransferLen,len); sl@0: return(len); sl@0: } sl@0: sl@0: /** sl@0: Purge the entire audio buffer. That is, if it contains cacheable memory, flush it. sl@0: @param aFlushOp The type of flush operation required - @see DBufferManager::TFlushOp. sl@0: */ sl@0: void TAudioBuffer::Flush(DBufferManager::TFlushOp aFlushOp) sl@0: { sl@0: iBufManager->FlushData(iChunkOffset,iSize,aFlushOp); sl@0: } sl@0: