|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32\drivers\soundsc\soundldd.cpp |
|
15 // LDD for the shared chunk sound driver. |
|
16 // |
|
17 // |
|
18 |
|
19 /** |
|
20 @file |
|
21 @internalTechnology |
|
22 @prototype |
|
23 */ |
|
24 |
|
25 #include <drivers/soundsc.h> |
|
26 #include <kernel/kern_priv.h> |
|
27 #include <kernel/cache.h> |
|
28 |
|
29 //#define USE_PLAY_EOF_TIMER |
|
30 |
|
31 // Define TEST_WITH_PAGING_CACHE_FLUSHES to flush the paging cache when testing read/writes to user thread in a data-paging system |
|
32 //#define TEST_WITH_PAGING_CACHE_FLUSHES |
|
33 |
|
34 static const char KSoundLddPanic[]="Sound LDD"; |
|
35 |
|
36 LOCAL_C TInt HighestCapabilitySupported(TUint32 aCapsBitField) |
|
37 { |
|
38 TInt n; |
|
39 for (n=31 ; n>=0 ; n--) |
|
40 { |
|
41 if (aCapsBitField&(1<<n)) |
|
42 break; |
|
43 } |
|
44 return(n); |
|
45 } |
|
46 |
|
47 /** |
|
48 Standard export function for LDDs. This creates a DLogicalDevice derived object, |
|
49 in this case, DSoundScLddFactory. |
|
50 */ |
|
51 DECLARE_STANDARD_LDD() |
|
52 { |
|
53 return new DSoundScLddFactory; |
|
54 } |
|
55 |
|
56 /** |
|
57 Constructor for the sound driver factory class. |
|
58 */ |
|
59 DSoundScLddFactory::DSoundScLddFactory() |
|
60 { |
|
61 // iUnitsOpenMask=0; |
|
62 |
|
63 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLddFactory::DSoundScLddFactory")); |
|
64 |
|
65 // Set version number for this device. |
|
66 iVersion=RSoundSc::VersionRequired(); |
|
67 |
|
68 // Indicate that units / PDD are supported. |
|
69 iParseMask=KDeviceAllowUnit|KDeviceAllowPhysicalDevice; |
|
70 |
|
71 // Leave the units decision to the PDD |
|
72 iUnitsMask=0xffffffff; |
|
73 } |
|
74 |
|
75 /** |
|
76 Second stage constructor for the sound driver factory class. |
|
77 This must at least set a name for the driver object. |
|
78 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
79 */ |
|
80 TInt DSoundScLddFactory::Install() |
|
81 { |
|
82 return(SetName(&KDevSoundScName)); |
|
83 } |
|
84 |
|
85 /** |
|
86 Return the 'capabilities' of the sound driver in general. |
|
87 Called in the response to an RDevice::GetCaps() request. |
|
88 @param aDes A user-side descriptor to write the capabilities information into. |
|
89 */ |
|
90 void DSoundScLddFactory::GetCaps(TDes8& aDes) const |
|
91 { |
|
92 // Create a capabilities object |
|
93 TCapsSoundScV01 caps; |
|
94 caps.iVersion=iVersion; |
|
95 |
|
96 // Write it back to user memory |
|
97 Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps)); |
|
98 } |
|
99 |
|
100 /** |
|
101 Called by the kernel's device driver framework to create a logical channel. |
|
102 This is called in the context of the client thread which requested the creation of a logical channel. |
|
103 The thread is in a critical section. |
|
104 @param aChannel Set by this function to point to the created logical channel. |
|
105 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
106 */ |
|
107 TInt DSoundScLddFactory::Create(DLogicalChannelBase*& aChannel) |
|
108 { |
|
109 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLddFactory::Create")); |
|
110 aChannel=new DSoundScLdd; |
|
111 if (!aChannel) |
|
112 return(KErrNoMemory); |
|
113 |
|
114 return(KErrNone); |
|
115 } |
|
116 |
|
117 /** |
|
118 Check whether a channel has is currently open on the specified unit. |
|
119 @param aUnit The number of the unit to be checked. |
|
120 @return ETrue if a channel is open on the specified channel, EFalse otherwise. |
|
121 @pre The unit info. mutex must be held. |
|
122 */ |
|
123 TBool DSoundScLddFactory::IsUnitOpen(TInt aUnit) |
|
124 { |
|
125 return(iUnitsOpenMask&(1<<aUnit)); |
|
126 } |
|
127 |
|
128 /** |
|
129 Attempt to change the state of the channel open status for a particular channel. |
|
130 @param aUnit The number of the unit to be updated. |
|
131 @param aIsOpenSetting The required new state for the channel open status: either ETrue to set the status to open or |
|
132 EFalse to set the status to closed. |
|
133 @return KErrNone if the status was updated successfully, KErrInUse if an attempt has been made to set the channnel status |
|
134 to open while it is already open. |
|
135 */ |
|
136 TInt DSoundScLddFactory::SetUnitOpen(TInt aUnit,TBool aIsOpenSetting) |
|
137 { |
|
138 NKern::FMWait(&iUnitInfoMutex); // Acquire the unit info. mutex. |
|
139 |
|
140 // Fail a request to open an channel that is already open |
|
141 if (aIsOpenSetting && IsUnitOpen(aUnit)) |
|
142 { |
|
143 NKern::FMSignal(&iUnitInfoMutex); // Release the unit info. mutex. |
|
144 return(KErrInUse); |
|
145 } |
|
146 |
|
147 // Update the open status as requested |
|
148 if (aIsOpenSetting) |
|
149 iUnitsOpenMask|=(1<<aUnit); |
|
150 else |
|
151 iUnitsOpenMask&=~(1<<aUnit); |
|
152 |
|
153 NKern::FMSignal(&iUnitInfoMutex); // Release the unit info. mutex. |
|
154 return(KErrNone); |
|
155 } |
|
156 |
|
157 /** |
|
158 Constructor for the sound driver logical channel. |
|
159 */ |
|
160 DSoundScLdd::DSoundScLdd() |
|
161 : iPowerDownDfc(DSoundScLdd::PowerDownDfc,this,3), |
|
162 iPowerUpDfc(DSoundScLdd::PowerUpDfc,this,3), |
|
163 iEofTimer(DSoundScLdd::PlayEofTimerExpired,this), |
|
164 iPlayEofDfc(DSoundScLdd::PlayEofTimerDfc,this,3) |
|
165 { |
|
166 // iDirection=ESoundDirRecord; |
|
167 // iState=EOpen; |
|
168 // iBufConfig=NULL; |
|
169 // iPowerHandler=NULL; |
|
170 // iSoundConfigFlags=0; |
|
171 // iBytesTransferred=0; |
|
172 // iBufManager=NULL; |
|
173 // iTestSettings=0; |
|
174 // iPlayEofTimerActive=EFalse; |
|
175 // iThreadOpenCount=0; |
|
176 |
|
177 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DSoundScLdd")); |
|
178 |
|
179 iUnit=-1; // Invalid unit number |
|
180 |
|
181 // Many drivers would open the client thread's DThread object here. However, since this driver allows a channel to be shared by multiple client |
|
182 // threads - we have to open and close the relevent DThread object for each request. |
|
183 } |
|
184 |
|
185 /** |
|
186 Destructor for the sound driver logical channel. |
|
187 */ |
|
188 DSoundScLdd::~DSoundScLdd() |
|
189 { |
|
190 |
|
191 if (iNotifyChangeOfHwClientRequest) |
|
192 Kern::DestroyClientRequest(iNotifyChangeOfHwClientRequest); |
|
193 |
|
194 // Free the TClientRequest structures associated with requests |
|
195 if (iClientRequests) |
|
196 { |
|
197 for (TInt index=0; index<RSoundSc::ERequestRecordData+1; ++index) |
|
198 if (iClientRequests[index]) |
|
199 Kern::DestroyClientRequest(iClientRequests[index]); |
|
200 |
|
201 delete[] iClientRequests; |
|
202 } |
|
203 |
|
204 // Check if we need to delete the shared chunk / audio buffers. |
|
205 if (iBufManager) |
|
206 delete iBufManager; |
|
207 |
|
208 // Delete any memory allocated to hold the current buffer configuration. |
|
209 if (iBufConfig) |
|
210 delete iBufConfig; |
|
211 |
|
212 // Remove and delete the power handler. |
|
213 if (iPowerHandler) |
|
214 { |
|
215 iPowerHandler->Remove(); |
|
216 delete iPowerHandler; |
|
217 } |
|
218 |
|
219 // Delete the request queue |
|
220 if (iReqQueue) |
|
221 delete iReqQueue; |
|
222 |
|
223 __ASSERT_DEBUG(iThreadOpenCount==0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
224 |
|
225 // Clear the 'units open mask' in the LDD factory. |
|
226 if (iUnit>=0) |
|
227 ((DSoundScLddFactory*)iDevice)->SetUnitOpen(iUnit,EFalse); |
|
228 } |
|
229 |
|
230 /** |
|
231 Second stage constructor for the sound driver - called by the kernel's device driver framework. |
|
232 This is called in the context of the client thread which requested the creation of a logical channel. |
|
233 The thread is in a critical section. |
|
234 @param aUnit The unit argument supplied by the client. |
|
235 @param aInfo The info argument supplied by the client. Always NULL in this case. |
|
236 @param aVer The version argument supplied by the client. |
|
237 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
238 */ |
|
239 TInt DSoundScLdd::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer) |
|
240 { |
|
241 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCreate")); |
|
242 |
|
243 // Check the client has ECapabilityMultimediaDD capability. |
|
244 if (!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)"))) |
|
245 return(KErrPermissionDenied); |
|
246 |
|
247 // Check that the sound driver version specified by the client is compatible. |
|
248 if (!Kern::QueryVersionSupported(RSoundSc::VersionRequired(),aVer)) |
|
249 return(KErrNotSupported); |
|
250 |
|
251 // Check that a channel hasn't already been opened on this unit. |
|
252 TInt r=((DSoundScLddFactory*)iDevice)->SetUnitOpen(aUnit,ETrue); // Try to update 'units open mask' in the LDD factory. |
|
253 if (r!=KErrNone) |
|
254 return(r); |
|
255 iUnit=aUnit; |
|
256 |
|
257 // Create a TClientRequest for each request that can be completed by the DFC thread. These TClientRequest |
|
258 // instances are separate to those embedded in the TSoundScRequest structures and are used for requests that |
|
259 // have no associated TSoundScRequest structure or which are completing prematurely before they can be |
|
260 // associated with a TSoundScRequest structure |
|
261 if ((iClientRequests=new TClientRequest*[RSoundSc::ERequestRecordData+1])==NULL) |
|
262 return KErrNoMemory; |
|
263 |
|
264 for (TInt index=0; index<RSoundSc::ERequestRecordData+1; ++index) |
|
265 if ((r=Kern::CreateClientRequest(iClientRequests[index]))!=KErrNone) |
|
266 return r; |
|
267 |
|
268 if ((r=Kern::CreateClientDataRequest(iNotifyChangeOfHwClientRequest))!=KErrNone) |
|
269 return r; |
|
270 |
|
271 // Initialise the PDD |
|
272 Pdd()->iLdd=this; |
|
273 |
|
274 // Read back the capabilities of this device from the PDD and determine the data transfer direction for this unit. |
|
275 TPckg<TSoundFormatsSupportedV02> capsBuf(iCaps); |
|
276 Pdd()->Caps(capsBuf); |
|
277 iDirection=iCaps.iDirection; |
|
278 |
|
279 // Check the client has UserEnvironment capability if recording. |
|
280 if(iDirection==ESoundDirRecord) |
|
281 { |
|
282 if (!Kern::CurrentThreadHasCapability(ECapabilityUserEnvironment,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)"))) |
|
283 return(KErrPermissionDenied); |
|
284 } |
|
285 |
|
286 // Create the appropriate request queue |
|
287 if (iDirection==ESoundDirPlayback) |
|
288 iReqQueue=new TSoundScPlayRequestQueue(this); |
|
289 else |
|
290 iReqQueue=new TSoundScRequestQueue(this); |
|
291 if (!iReqQueue) |
|
292 return(KErrNoMemory); |
|
293 r=iReqQueue->Create(); |
|
294 if (r!=KErrNone) |
|
295 return(r); |
|
296 |
|
297 // Setup the default audio configuration acording to these capabilities. |
|
298 iSoundConfig.iChannels=HighestCapabilitySupported(iCaps.iChannels)+1; |
|
299 __ASSERT_ALWAYS(iSoundConfig.iChannels>0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
300 iSoundConfig.iRate=(TSoundRate)HighestCapabilitySupported(iCaps.iRates); |
|
301 __ASSERT_ALWAYS(iSoundConfig.iRate>=0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
302 iSoundConfig.iEncoding=(TSoundEncoding)HighestCapabilitySupported(iCaps.iEncodings); |
|
303 __ASSERT_ALWAYS(iSoundConfig.iEncoding>=0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
304 iSoundConfig.iDataFormat=(TSoundDataFormat)HighestCapabilitySupported(iCaps.iDataFormats); |
|
305 __ASSERT_ALWAYS(iSoundConfig.iDataFormat>=0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
306 __ASSERT_ALWAYS(ValidateConfig(iSoundConfig)==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
307 iSoundConfigFlags=0; |
|
308 |
|
309 // Setup the default setting for the record level / play volume. |
|
310 iVolume=KSoundMaxVolume; |
|
311 |
|
312 // Set up the correct DFC queue |
|
313 TDfcQue* dfcq=((DSoundScPdd*)iPdd)->DfcQ(aUnit); |
|
314 SetDfcQ(dfcq); |
|
315 iPowerDownDfc.SetDfcQ(dfcq); |
|
316 iPowerUpDfc.SetDfcQ(dfcq); |
|
317 iMsgQ.Receive(); |
|
318 |
|
319 // Create the power handler |
|
320 iPowerHandler=new DSoundScPowerHandler(this); |
|
321 if (!iPowerHandler) |
|
322 return(KErrNoMemory); |
|
323 iPowerHandler->Add(); |
|
324 |
|
325 // Power up the hardware. |
|
326 r=Pdd()->PowerUp(); |
|
327 |
|
328 return(r); |
|
329 } |
|
330 |
|
331 /** |
|
332 Shutdown the audio device. |
|
333 Terminate all device activity and power down the hardware. |
|
334 */ |
|
335 void DSoundScLdd::Shutdown() |
|
336 { |
|
337 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Shutdown")); |
|
338 |
|
339 Pdd()->StopTransfer(); |
|
340 |
|
341 // Power down the hardware |
|
342 Pdd()->PowerDown(); |
|
343 |
|
344 // Cancel any requests that we may be handling |
|
345 DoCancel(RSoundSc::EAllRequests); |
|
346 |
|
347 iState=EOpen; |
|
348 |
|
349 // Make sure DFCs and timers are not queued. |
|
350 iPowerDownDfc.Cancel(); |
|
351 iPowerUpDfc.Cancel(); |
|
352 CancelPlayEofTimer(); |
|
353 } |
|
354 |
|
355 /** |
|
356 Process a request on this logical channel |
|
357 Called in the context of the client thread. |
|
358 @param aReqNo The request number: |
|
359 ==KMaxTInt: a 'DoCancel' message; |
|
360 >=0: a 'DoControl' message with function number equal to value. |
|
361 <0: a 'DoRequest' message with function number equal to ~value. |
|
362 @param a1 The first request argument. For DoRequest(), this is a pointer to the TRequestStatus. |
|
363 @param a2 The second request argument. For DoRequest(), this is a pointer to the 2 actual TAny* arguments. |
|
364 @return The result of the request. This is ignored by device driver framework for DoRequest(). |
|
365 */ |
|
366 TInt DSoundScLdd::Request(TInt aReqNo, TAny* a1, TAny* a2) |
|
367 { |
|
368 // __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Request(%d)",aReqNo)); |
|
369 TInt r; |
|
370 |
|
371 // Check for DoControl or DoRequest functions which are configured to execute in kernel thread context. This |
|
372 // also applies to DoCancel functions and ERequestRecordData requests where recording mode is not yet enabled. |
|
373 if ((aReqNo<RSoundSc::EMsgControlMax && aReqNo>(~RSoundSc::EMsgRequestMax)) || |
|
374 aReqNo==KMaxTInt || |
|
375 ((~aReqNo)==RSoundSc::ERequestRecordData && (iState==EOpen || iState==EConfigured)) |
|
376 ) |
|
377 { |
|
378 // Implement in the context of the kernel thread - prepare and issue a kernel message. |
|
379 r=DLogicalChannel::Request(aReqNo,a1,a2); |
|
380 } |
|
381 else |
|
382 { |
|
383 // Implement in the context of the client thread. |
|
384 // Decode the message type and dispatch it to the relevent handler function. |
|
385 if ((TUint)aReqNo<(TUint)KMaxTInt) |
|
386 r=DoControl(aReqNo,a1,a2,&Kern::CurrentThread()); // DoControl - process the request. |
|
387 |
|
388 else |
|
389 { |
|
390 // DoRequest - read the arguments from the client thread and process the request. |
|
391 TAny* a[2]; |
|
392 kumemget32(a,a2,sizeof(a)); |
|
393 TRequestStatus* status=(TRequestStatus*)a1; |
|
394 NKern::ThreadEnterCS(); // Need to be in critical section while manipulating the request/buffer list (for record). |
|
395 r=DoRequest(~aReqNo,status,a[0],a[1],&Kern::CurrentThread()); |
|
396 |
|
397 // Complete request if there was an error |
|
398 if (r!=KErrNone) |
|
399 CompleteRequest(&Kern::CurrentThread(),status,r); |
|
400 r=KErrNone; |
|
401 NKern::ThreadLeaveCS(); |
|
402 } |
|
403 } |
|
404 // __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::Request - %d",r)); |
|
405 return(r); |
|
406 } |
|
407 |
|
408 /** |
|
409 Send a message to the DFC thread for processing by HandleMsg(). |
|
410 |
|
411 This function is called in the context of the client thread. |
|
412 |
|
413 Overridden to ensure client data is copied kernel-side to avoid page-faults. |
|
414 |
|
415 @param aMsg The message to process. |
|
416 |
|
417 @return KErrNone if the message was send successfully, otherwise one of the other system-wide error |
|
418 codes. |
|
419 */ |
|
420 TInt DSoundScLdd::SendMsg(TMessageBase* aMsg) |
|
421 { |
|
422 // Executes in context of client thread |
|
423 |
|
424 TThreadMessage& m=*(TThreadMessage*)aMsg; |
|
425 TInt id = m.iValue; |
|
426 |
|
427 TInt r(KErrNone); |
|
428 if (id == ~RSoundSc::EMsgRequestPlayData) |
|
429 { |
|
430 r = PrePlay(aMsg); |
|
431 if (r!=KErrNone) |
|
432 { |
|
433 // This is an asynchronous request so need to return error through the TRequestStatus |
|
434 TRequestStatus* status = (TRequestStatus*)(m.Ptr0()); |
|
435 Kern::RequestComplete(status,r); |
|
436 return(r); |
|
437 } |
|
438 r = DLogicalChannel::SendMsg(aMsg); |
|
439 if (r!=KErrNone) |
|
440 { |
|
441 iReqQueue->Free((TSoundScPlayRequest*)m.iArg[1]); // Return the unused request object |
|
442 } |
|
443 return(r); |
|
444 } |
|
445 else if (id == RSoundSc::EMsgControlSetBufChunkCreate || id == RSoundSc::EMsgControlSetBufChunkOpen) |
|
446 { |
|
447 r = PreSetBufferChunkCreateOrOpen(aMsg); |
|
448 if (r!=KErrNone) |
|
449 { |
|
450 return(r); |
|
451 } |
|
452 } |
|
453 else if (id == RSoundSc::EMsgControlSetAudioFormat) |
|
454 { |
|
455 r = PreSetSoundConfig(aMsg); |
|
456 if (r!=KErrNone) |
|
457 { |
|
458 return(r); |
|
459 } |
|
460 } |
|
461 |
|
462 r = DLogicalChannel::SendMsg(aMsg); |
|
463 |
|
464 |
|
465 return(r); |
|
466 } |
|
467 |
|
468 /** |
|
469 PreProcess a play request on this logical channel |
|
470 Called in the context of the client thread. |
|
471 |
|
472 @param aMsg The message to process. |
|
473 |
|
474 @return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error. |
|
475 */ |
|
476 TInt DSoundScLdd::PrePlay(TMessageBase* aMsg) |
|
477 { |
|
478 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PrePlay")); |
|
479 |
|
480 // Executes in context of client thread |
|
481 |
|
482 TThreadMessage* m=(TThreadMessage*)aMsg; |
|
483 |
|
484 // Copy play information to kernel side before checking |
|
485 SRequestPlayDataInfo info; |
|
486 kumemget(&info,m->iArg[1],sizeof(info)); |
|
487 |
|
488 __KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PrePlay - off %x len %x flg %x ",info.iBufferOffset,info.iLength,info.iFlags)); |
|
489 |
|
490 // validate parameters in the play structure |
|
491 |
|
492 // Check that the offset argument is aligned correctly for the PDD. |
|
493 TUint32 alignmask=(1<<iCaps.iRequestAlignment)-1; // iRequestAlignment holds log to base 2 of alignment required |
|
494 if ((info.iBufferOffset & alignmask) != 0) |
|
495 return(KErrArgument); |
|
496 |
|
497 // Check that the length argument is compatible with the minimum request size required for the PDD. |
|
498 if (iCaps.iRequestMinSize && info.iLength%iCaps.iRequestMinSize) |
|
499 return(KErrArgument); |
|
500 |
|
501 // Check that the specified offset and length are valid in the chunk. If so, get a pointer to the corresponding |
|
502 // audio buffer object. |
|
503 TAudioBuffer* buf; |
|
504 if (iBufManager) |
|
505 { |
|
506 TInt r=iBufManager->ValidateRegion(info.iBufferOffset,info.iLength,buf); |
|
507 if (r!=KErrNone) |
|
508 return(r); |
|
509 } |
|
510 else |
|
511 { |
|
512 return(KErrNotReady); |
|
513 } |
|
514 |
|
515 // Acquire a free request object and add it to the queue of pending requests. |
|
516 TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->NextFree(); |
|
517 if (!req) |
|
518 return(KErrGeneral); // Must have exceeded KMaxSndScRequestsPending. |
|
519 req->iTf.Init((TUint)buf,info.iBufferOffset,info.iLength,buf); // Use pointer to audio buffer as unique ID |
|
520 req->iFlags=info.iFlags; |
|
521 |
|
522 // replace the argument with a pointer to the kernel-side structure |
|
523 m->iArg[1]=req; |
|
524 |
|
525 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::PrePlay")); |
|
526 |
|
527 return(KErrNone); |
|
528 } |
|
529 |
|
530 /** |
|
531 PreProcess a SetBufferChunkCreate and SetBufferChunkOpen on this logical channel |
|
532 Called in the context of the client thread. |
|
533 This is synchronous so only need one copy of the data on the kernel-side. |
|
534 |
|
535 @param aMsg The message to process. |
|
536 |
|
537 @return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error. |
|
538 */ |
|
539 TInt DSoundScLdd::PreSetBufferChunkCreateOrOpen(TMessageBase* aMsg) |
|
540 { |
|
541 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetBufferChunkCreateOrOpen")); |
|
542 TInt r(KErrNone); |
|
543 |
|
544 TThreadMessage* m=(TThreadMessage*)aMsg; |
|
545 |
|
546 TInt length, maxLength; |
|
547 const TDesC8* userDesc = (const TDesC8*)m->Ptr0(); |
|
548 const TUint8* configData = Kern::KUDesInfo(*userDesc,length,maxLength); |
|
549 |
|
550 //__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - len %x maxlen %x",length,maxLength)); |
|
551 |
|
552 // check the descriptor length is >= the base class size |
|
553 TInt minDesLen=sizeof(TSharedChunkBufConfigBase); |
|
554 if (length<minDesLen) |
|
555 return(KErrArgument); |
|
556 |
|
557 // Temporary copy of client-side buffer config structure |
|
558 TSharedChunkBufConfigBase chunkBufConfig; |
|
559 |
|
560 kumemget(&chunkBufConfig, configData, minDesLen); |
|
561 |
|
562 //__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - num %x size %x flg %x ",chunkBufConfig.iNumBuffers,chunkBufConfig.iBufferSizeInBytes,chunkBufConfig.iFlags)); |
|
563 |
|
564 // check the buffer argument |
|
565 if (chunkBufConfig.iNumBuffers<=0) |
|
566 return(KErrArgument); |
|
567 |
|
568 // Validate the rest of the configuration supplied. |
|
569 if (chunkBufConfig.iBufferSizeInBytes<=0) |
|
570 return(KErrArgument); |
|
571 |
|
572 if (iDirection==ESoundDirRecord) |
|
573 { |
|
574 // If this is a record channel then the size of each buffer must comply with the PDD contraints. |
|
575 if (iCaps.iRequestMinSize && chunkBufConfig.iBufferSizeInBytes%iCaps.iRequestMinSize) |
|
576 return(KErrArgument); |
|
577 } |
|
578 |
|
579 //Allocate space for the buffer list |
|
580 NKern::ThreadEnterCS(); |
|
581 r=ReAllocBufferConfigInfo(chunkBufConfig.iNumBuffers); |
|
582 NKern::ThreadLeaveCS(); |
|
583 if (r!=KErrNone) |
|
584 return(r); |
|
585 |
|
586 //__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - cfg %x size %x",iBufConfig,iBufConfigSize)); |
|
587 |
|
588 // copy all data into the buffer list |
|
589 kumemget(iBufConfig, configData, iBufConfigSize); |
|
590 |
|
591 return(r); |
|
592 } |
|
593 |
|
594 /** |
|
595 PreProcess a SetSoundConfig on this logical channel |
|
596 Called in the context of the client thread. |
|
597 This is synchronous so only need one copy of the data on the kernel-side. |
|
598 |
|
599 @param aMsg The message to process. |
|
600 |
|
601 @return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error. |
|
602 */ |
|
603 TInt DSoundScLdd::PreSetSoundConfig(TMessageBase* aMsg) |
|
604 { |
|
605 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetSoundConfig")); |
|
606 |
|
607 TThreadMessage* m=(TThreadMessage*)aMsg; |
|
608 |
|
609 TPtr8 localPtr((TUint8*)&iTempSoundConfig, sizeof(TCurrentSoundFormatV02)); |
|
610 |
|
611 Kern::KUDesGet(localPtr,*(const TDesC8*)m->Ptr0()); |
|
612 |
|
613 //__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetSoundConfig chan %x rate %x enc %x form %x", |
|
614 // iTempSoundConfig.iChannels,iTempSoundConfig.iRate,iTempSoundConfig.iEncoding,iTempSoundConfig.iDataFormat)); |
|
615 |
|
616 // Check that it is compatible with this sound device. |
|
617 TInt r=ValidateConfig(iTempSoundConfig); |
|
618 |
|
619 return(r); |
|
620 } |
|
621 |
|
622 /** |
|
623 Processes a message for this logical channel. |
|
624 This function is called in the context of a DFC thread. |
|
625 @param aMsg The message to process. |
|
626 The iValue member of this distinguishes the message type: |
|
627 iValue==ECloseMsg: channel close message. |
|
628 iValue==KMaxTInt: a 'DoCancel' message |
|
629 iValue>=0: a 'DoControl' message with function number equal to iValue |
|
630 iValue<0: a 'DoRequest' message with function number equal to ~iValue |
|
631 */ |
|
632 void DSoundScLdd::HandleMsg(TMessageBase* aMsg) |
|
633 { |
|
634 #ifdef _DEBUG |
|
635 #ifdef TEST_WITH_PAGING_CACHE_FLUSHES |
|
636 Kern::SetRealtimeState(ERealtimeStateOn); |
|
637 Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); |
|
638 #endif |
|
639 #endif |
|
640 |
|
641 TThreadMessage& m=*(TThreadMessage*)aMsg; |
|
642 TInt id=m.iValue; |
|
643 // __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::HandleMsg(%d)",id)); |
|
644 |
|
645 if (id==(TInt)ECloseMsg) |
|
646 { |
|
647 // Channel close. |
|
648 Shutdown(); |
|
649 m.Complete(KErrNone,EFalse); |
|
650 return; |
|
651 } |
|
652 else if (id==KMaxTInt) |
|
653 { |
|
654 // DoCancel |
|
655 DoCancel(m.Int0()); |
|
656 m.Complete(KErrNone,ETrue); |
|
657 return; |
|
658 } |
|
659 else if (id<0) |
|
660 { |
|
661 // DoRequest |
|
662 TRequestStatus* pS=(TRequestStatus*)m.Ptr0(); |
|
663 TInt r=DoRequest(~id,pS,m.Ptr1(),m.Ptr2(),m.Client()); |
|
664 if (r!=KErrNone) |
|
665 { |
|
666 iClientRequests[~id]->SetStatus(pS); |
|
667 CompleteRequest(m.Client(),NULL,r,iClientRequests[~id]); |
|
668 } |
|
669 m.Complete(KErrNone,ETrue); |
|
670 } |
|
671 else |
|
672 { |
|
673 // DoControl |
|
674 TInt r=DoControl(id,m.Ptr0(),m.Ptr1(),m.Client()); |
|
675 m.Complete(r,ETrue); |
|
676 } |
|
677 } |
|
678 |
|
679 /** |
|
680 Process a synchronous 'DoControl' request. |
|
681 @param aFunction The request number. |
|
682 @param a1 The first request argument. |
|
683 @param a2 The second request argument. |
|
684 @param aThread The client thread which issued the request. |
|
685 @return The result of the request. |
|
686 */ |
|
687 TInt DSoundScLdd::DoControl(TInt aFunction,TAny* a1,TAny* a2,DThread* aThread) |
|
688 { |
|
689 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoControl(%d)",aFunction)); |
|
690 |
|
691 TInt r=KErrNotSupported; |
|
692 switch (aFunction) |
|
693 { |
|
694 case RSoundSc::EControlGetCaps: |
|
695 { |
|
696 // Return the capabilities for this device. Read this from the PDD and |
|
697 // then write it to the client. |
|
698 TSoundFormatsSupportedV02Buf caps; |
|
699 Pdd()->Caps(caps); |
|
700 Kern::InfoCopy(*((TDes8*)a1),caps); |
|
701 r=KErrNone; |
|
702 break; |
|
703 } |
|
704 case RSoundSc::EControlGetAudioFormat: |
|
705 { |
|
706 // Write the current audio configuration back to the client. |
|
707 TPtrC8 ptr((const TUint8*)&iSoundConfig,sizeof(iSoundConfig)); |
|
708 Kern::InfoCopy(*((TDes8*)a1),ptr); |
|
709 r=KErrNone; |
|
710 break; |
|
711 } |
|
712 case RSoundSc::EMsgControlSetAudioFormat: |
|
713 { |
|
714 if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) |
|
715 { |
|
716 // If the play EOF timer is active then it is OK to change the audio configuration - but we |
|
717 // need to bring the PDD out of transfer mode first. |
|
718 if (iPlayEofTimerActive) |
|
719 { |
|
720 CancelPlayEofTimer(); |
|
721 Pdd()->StopTransfer(); |
|
722 } |
|
723 |
|
724 r=SetSoundConfig(); |
|
725 if (r==KErrNone && (iSoundConfigFlags&KSndScVolumeIsSetup) && iBufConfig) |
|
726 iState=EConfigured; |
|
727 } |
|
728 else |
|
729 r=KErrInUse; |
|
730 break; |
|
731 } |
|
732 case RSoundSc::EControlGetBufConfig: |
|
733 if (iBufConfig) |
|
734 { |
|
735 // Write the buffer config to the client. |
|
736 TPtrC8 ptr((const TUint8*)iBufConfig,iBufConfigSize); |
|
737 Kern::InfoCopy(*((TDes8*)a1),ptr); |
|
738 r=KErrNone; |
|
739 } |
|
740 break; |
|
741 case RSoundSc::EMsgControlSetBufChunkCreate: |
|
742 { |
|
743 if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) |
|
744 { |
|
745 // Need to be in critical section while deleting an exisiting config and creating a new one |
|
746 NKern::ThreadEnterCS(); |
|
747 r=SetBufferConfig(aThread); |
|
748 NKern::ThreadLeaveCS(); |
|
749 if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup)) |
|
750 iState=EConfigured; |
|
751 } |
|
752 else |
|
753 r=KErrInUse; |
|
754 break; |
|
755 } |
|
756 case RSoundSc::EMsgControlSetBufChunkOpen: |
|
757 { |
|
758 if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive) |
|
759 { |
|
760 // Need to be in critical section while deleting an exisiting config and creating a new one |
|
761 NKern::ThreadEnterCS(); |
|
762 r=SetBufferConfig((TInt)a2,aThread); |
|
763 NKern::ThreadLeaveCS(); |
|
764 if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup)) |
|
765 iState=EConfigured; |
|
766 } |
|
767 else |
|
768 r=KErrInUse; |
|
769 break; |
|
770 } |
|
771 case RSoundSc::EControlGetVolume: |
|
772 r=iVolume; |
|
773 break; |
|
774 case RSoundSc::EMsgControlSetVolume: |
|
775 { |
|
776 r=SetVolume((TInt)a1); |
|
777 if (r==KErrNone && iState==EOpen && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && iBufConfig) |
|
778 iState=EConfigured; |
|
779 break; |
|
780 } |
|
781 case RSoundSc::EMsgControlCancelSpecific: |
|
782 { |
|
783 if (iDirection==ESoundDirPlayback) |
|
784 { |
|
785 // Don't try to cancel a play transfer that has already started - let it complete in its own time. |
|
786 TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->Find((TRequestStatus*)a1); |
|
787 if (req && req->iTf.iTfState==TSndScTransfer::ETfNotStarted) |
|
788 { |
|
789 iReqQueue->Remove(req); |
|
790 CompleteRequest(req->iOwningThread,NULL,KErrCancel,req->iClientRequest); |
|
791 iReqQueue->Free(req); |
|
792 } |
|
793 } |
|
794 else |
|
795 { |
|
796 // Need to aquire the buffer/request list mutex when removing record requests - RecordData() runs in |
|
797 // client thread context and this may access the queue. Record requests a treated differently to play |
|
798 // requests and you don't have to worry about record requests already being in progress. |
|
799 NKern::FMWait(&iMutex); |
|
800 TSoundScRequest* req=iReqQueue->Find((TRequestStatus*)a1); |
|
801 if (req) |
|
802 { |
|
803 iReqQueue->Remove(req); |
|
804 DThread* thread=req->iOwningThread; // Take a copy before we free it. |
|
805 TClientRequest* clreq=req->iClientRequest; // Take a copy before we free it. |
|
806 NKern::FMSignal(&iMutex); |
|
807 iReqQueue->Free(req); |
|
808 CompleteRequest(thread,NULL,KErrCancel,clreq); |
|
809 } |
|
810 else |
|
811 NKern::FMSignal(&iMutex); |
|
812 } |
|
813 r=KErrNone; |
|
814 break; |
|
815 } |
|
816 case RSoundSc::EControlBytesTransferred: |
|
817 r=iBytesTransferred; |
|
818 break; |
|
819 case RSoundSc::EControlResetBytesTransferred: |
|
820 iBytesTransferred=0; |
|
821 r=KErrNone; |
|
822 break; |
|
823 case RSoundSc::EMsgControlPause: |
|
824 if (iState==EActive) |
|
825 { |
|
826 // Have to update the status early here because a record PDD may call us back with RecordCallback() in |
|
827 // handling PauseTransfer() - to complete a partially filled buffer. |
|
828 iState=EPaused; |
|
829 iCompletesWhilePausedCount=0; |
|
830 r=Pdd()->PauseTransfer(); |
|
831 if (r!=KErrNone) |
|
832 iState=EActive; |
|
833 else if (iDirection==ESoundDirRecord) |
|
834 { |
|
835 // For record, complete any pending record requests that are still outstanding following PauseTransfer(). |
|
836 iReqQueue->CompleteAll(KErrCancel); |
|
837 } |
|
838 } |
|
839 else |
|
840 r=KErrNotReady; |
|
841 break; |
|
842 case RSoundSc::EMsgControlResume: |
|
843 if (iState==EPaused) |
|
844 { |
|
845 r=Pdd()->ResumeTransfer(); |
|
846 if (r==KErrNone && iDirection==ESoundDirRecord) |
|
847 r=StartNextRecordTransfers(); |
|
848 if (r==KErrNone) |
|
849 iState=EActive; // Successfully resumed transfer - update the status. |
|
850 } |
|
851 else |
|
852 r=KErrNotReady; |
|
853 break; |
|
854 case RSoundSc::EControlReleaseBuffer: |
|
855 if (iDirection==ESoundDirRecord) |
|
856 r=ReleaseBuffer((TInt)a1); |
|
857 break; |
|
858 case RSoundSc::EMsgControlCustomConfig: |
|
859 r=CustomConfig((TInt)a1,a2); |
|
860 break; |
|
861 case RSoundSc::EControlTimePlayed: |
|
862 if (iDirection==ESoundDirPlayback) |
|
863 { |
|
864 TInt64 time=0; |
|
865 r=Pdd()->TimeTransferred(time,iState); |
|
866 TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64)); |
|
867 Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL); |
|
868 } |
|
869 else |
|
870 r=KErrNotSupported; |
|
871 break; |
|
872 case RSoundSc::EControlTimeRecorded: |
|
873 if (iDirection==ESoundDirRecord) |
|
874 { |
|
875 TInt64 time=0; |
|
876 r=Pdd()->TimeTransferred(time,iState); |
|
877 TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64)); |
|
878 Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL); |
|
879 } |
|
880 else |
|
881 r=KErrNotSupported; |
|
882 break; |
|
883 } |
|
884 |
|
885 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::DoControl - %d",r)); |
|
886 return(r); |
|
887 } |
|
888 |
|
889 /** |
|
890 Process an asynchronous 'DoRequest' request. |
|
891 @param aFunction The request number. |
|
892 @param aStatus A pointer to the TRequestStatus. |
|
893 @param a1 The first request argument. |
|
894 @param a2 The second request argument. |
|
895 @param aThread The client thread which issued the request. |
|
896 @return The result of the request. |
|
897 */ |
|
898 TInt DSoundScLdd::DoRequest(TInt aFunction, TRequestStatus* aStatus, TAny* a1, TAny* /*a2*/,DThread* aThread) |
|
899 { |
|
900 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoRequest(%d)",aFunction)); |
|
901 |
|
902 // 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. |
|
903 TInt r=aThread->Open(); |
|
904 __ASSERT_ALWAYS(r==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
905 #ifdef _DEBUG |
|
906 __e32_atomic_add_ord32(&iThreadOpenCount, 1); |
|
907 #endif |
|
908 |
|
909 r=KErrNotSupported; |
|
910 switch (aFunction) |
|
911 { |
|
912 case RSoundSc::EMsgRequestPlayData: |
|
913 { |
|
914 if (iDirection==ESoundDirPlayback) |
|
915 { |
|
916 if (iState==EOpen) |
|
917 { |
|
918 // Not yet fully configured - maybe we can use the default settings. |
|
919 r=KErrNone; |
|
920 if (!iBufConfig) |
|
921 r=KErrNotReady; // Can't guess a default buffer configuration. |
|
922 else |
|
923 { |
|
924 if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup)) |
|
925 r=DoSetSoundConfig(iSoundConfig); // Apply default sound configuration. |
|
926 if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup)) |
|
927 r=SetVolume(iVolume); // Apply default volume level |
|
928 } |
|
929 if (r!=KErrNone) |
|
930 break; |
|
931 else |
|
932 iState=EConfigured; |
|
933 } |
|
934 |
|
935 if (iState==EConfigured || iState==EActive || iState==EPaused) |
|
936 { |
|
937 r=PlayData(aStatus, (TSoundScPlayRequest*)a1,aThread); |
|
938 } |
|
939 else |
|
940 r=KErrNotReady; |
|
941 } |
|
942 break; |
|
943 } |
|
944 case RSoundSc::ERequestRecordData: |
|
945 if (iDirection==ESoundDirRecord) |
|
946 { |
|
947 // Check if the device has been configured yet |
|
948 if (iState==EOpen) |
|
949 { |
|
950 // Not yet fully configured - maybe we can use the default settings. |
|
951 r=KErrNone; |
|
952 if (!iBufConfig) |
|
953 r=KErrNotReady; // Can't guess a default buffer configuration. |
|
954 else |
|
955 { |
|
956 if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup)) |
|
957 r=DoSetSoundConfig(iSoundConfig); // Apply default sound configuration. |
|
958 if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup)) |
|
959 r=SetVolume(iVolume); // Apply default volume level |
|
960 } |
|
961 if (r!=KErrNone) |
|
962 break; |
|
963 else |
|
964 iState=EConfigured; |
|
965 } |
|
966 // Check if we need to start recording |
|
967 if (iState==EConfigured) |
|
968 { |
|
969 r=StartRecord(); |
|
970 if (r!=KErrNone) |
|
971 break; |
|
972 else |
|
973 iState=EActive; |
|
974 } |
|
975 |
|
976 // State must be either active or paused so process the record request as appropriate for these states. |
|
977 r=RecordData(aStatus,(TInt*)a1,aThread); |
|
978 } |
|
979 break; |
|
980 case RSoundSc::ERequestNotifyChangeOfHwConfig: |
|
981 { |
|
982 // Check if this device can detect changes in its hardware configuration. |
|
983 if (iCaps.iHwConfigNotificationSupport) |
|
984 { |
|
985 r=KErrNone; |
|
986 if (!iNotifyChangeOfHwClientRequest->IsReady()) |
|
987 { |
|
988 iChangeOfHwConfigThread=aThread; |
|
989 iNotifyChangeOfHwClientRequest->SetDestPtr((TBool*)a1); |
|
990 r = iNotifyChangeOfHwClientRequest->SetStatus(aStatus); |
|
991 } |
|
992 else |
|
993 r=KErrInUse; |
|
994 } |
|
995 else |
|
996 r=KErrNotSupported; |
|
997 break; |
|
998 } |
|
999 } |
|
1000 |
|
1001 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::DoRequest - %d",r)); |
|
1002 return(r); |
|
1003 } |
|
1004 |
|
1005 /** |
|
1006 Process the cancelling of asynchronous requests. |
|
1007 @param aMask A mask indicating which requests need to be cancelled. |
|
1008 @return The result of the cancel. |
|
1009 */ |
|
1010 TInt DSoundScLdd::DoCancel(TUint aMask) |
|
1011 { |
|
1012 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCancel(%08x)",aMask)); |
|
1013 |
|
1014 if (aMask&(1<<RSoundSc::EMsgRequestPlayData)) |
|
1015 { |
|
1016 Pdd()->StopTransfer(); |
|
1017 iReqQueue->CompleteAll(KErrCancel); // Cancel any outstanding play requests |
|
1018 if ((iState==EActive)||(iState==EPaused)) |
|
1019 iState=EConfigured; |
|
1020 } |
|
1021 if (aMask&(1<<RSoundSc::ERequestRecordData)) |
|
1022 { |
|
1023 Pdd()->StopTransfer(); |
|
1024 iReqQueue->CompleteAll(KErrCancel,&iMutex); // Cancel any outstanding record requests |
|
1025 if ((iState==EActive)||(iState==EPaused)) |
|
1026 iState=EConfigured; |
|
1027 } |
|
1028 if (aMask&(1<<RSoundSc::ERequestNotifyChangeOfHwConfig)) |
|
1029 { |
|
1030 // Complete any pending hardware change notifier with KErrCancel. |
|
1031 if (iNotifyChangeOfHwClientRequest->IsReady()) |
|
1032 CompleteRequest(iChangeOfHwConfigThread,NULL,KErrCancel,iNotifyChangeOfHwClientRequest); |
|
1033 } |
|
1034 return(KErrNone); |
|
1035 } |
|
1036 |
|
1037 /** |
|
1038 Set the current buffer configuration - creating a shared chunk. |
|
1039 @param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of |
|
1040 the shared chunk required. |
|
1041 @param aThread The client thread which has requested to own the chunk. |
|
1042 @return A handle to the shared chunk for the owning thread (a value >0), if successful; |
|
1043 otherwise one of the other system wide error codes, (a value <0). |
|
1044 @pre The thread must be in a critical section. |
|
1045 */ |
|
1046 TInt DSoundScLdd::SetBufferConfig(DThread* aThread) |
|
1047 { |
|
1048 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig")); |
|
1049 |
|
1050 TInt r(KErrNone); |
|
1051 |
|
1052 // Delete any existing buffers and the shared chunk. |
|
1053 if (iBufManager) |
|
1054 { |
|
1055 delete iBufManager; |
|
1056 iBufManager=NULL; |
|
1057 } |
|
1058 |
|
1059 // If a handle to the shared chunk was created, close it, using the handle of the thread on which |
|
1060 // it was created, in case a different thread is now calling us |
|
1061 if (iChunkHandle>0) |
|
1062 { |
|
1063 Kern::CloseHandle(iChunkHandleThread,iChunkHandle); |
|
1064 iChunkHandle=0; |
|
1065 } |
|
1066 |
|
1067 // Create the shared chunk, then create buffer objects for the committed buffers within it. This is |
|
1068 // done by creating a buffer manager - create the apppropraiate version according to the audio direction. |
|
1069 if (iDirection==ESoundDirPlayback) |
|
1070 iBufManager=new DBufferManager(this); |
|
1071 else |
|
1072 iBufManager=new DRecordBufferManager(this); |
|
1073 if (!iBufManager) |
|
1074 return(KErrNoMemory); |
|
1075 r=iBufManager->Create(iBufConfig); |
|
1076 if (r!=KErrNone) |
|
1077 { |
|
1078 delete iBufManager; |
|
1079 iBufManager=NULL; |
|
1080 return(r); |
|
1081 } |
|
1082 |
|
1083 // Create handle to the shared chunk for the owning thread. |
|
1084 r=Kern::MakeHandleAndOpen(aThread,iBufManager->iChunk); |
|
1085 |
|
1086 // And save the the chunk and thread handles for later. Normally the chunk handle will be closed when the chunk |
|
1087 // is closed, but if the chunk is re-allocated then it will need to be closed before re-allocation. |
|
1088 iChunkHandle=r; |
|
1089 iChunkHandleThread=aThread; |
|
1090 |
|
1091 return(r); |
|
1092 } |
|
1093 |
|
1094 /** |
|
1095 Set the current buffer configuration - using an existing shared chunk. |
|
1096 @param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of |
|
1097 the shared chunk supplied. |
|
1098 @param aChunkHandle A handle for the shared chunk supplied by the client. |
|
1099 @param aThread The thread in which the given handle is valid. |
|
1100 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1101 @pre The thread must be in a critical section. |
|
1102 */ |
|
1103 TInt DSoundScLdd::SetBufferConfig(TInt aChunkHandle,DThread* aThread) |
|
1104 { |
|
1105 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig(Handle-%d)",aChunkHandle)); |
|
1106 |
|
1107 TInt r(KErrNone); |
|
1108 |
|
1109 // Delete any existing buffers and the shared chunk. |
|
1110 if (iBufManager) |
|
1111 { |
|
1112 delete iBufManager; |
|
1113 iBufManager=NULL; |
|
1114 } |
|
1115 |
|
1116 // Open the shared chunk supplied and create buffer objects for the committed buffers within it. This is |
|
1117 // done by creating a buffer manager - create the apppropraiate version according to the audio direction. |
|
1118 if (iDirection==ESoundDirPlayback) |
|
1119 iBufManager=new DBufferManager(this); |
|
1120 else |
|
1121 iBufManager=new DRecordBufferManager(this); |
|
1122 if (!iBufManager) |
|
1123 return(KErrNoMemory); |
|
1124 r=iBufManager->Create(*iBufConfig,aChunkHandle,aThread); |
|
1125 if (r!=KErrNone) |
|
1126 { |
|
1127 delete iBufManager; |
|
1128 iBufManager=NULL; |
|
1129 } |
|
1130 return(r); |
|
1131 } |
|
1132 |
|
1133 /** |
|
1134 Set the current audio format configuration. |
|
1135 @param aSoundConfigBuf A packaged sound configuration object holding the new audio configuration settings to be used. |
|
1136 @param aThread The client thread which contains the sound configuration object. |
|
1137 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1138 */ |
|
1139 TInt DSoundScLdd::SetSoundConfig() |
|
1140 { |
|
1141 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetSoundConfig")); |
|
1142 |
|
1143 TInt r=DoSetSoundConfig(iTempSoundConfig); |
|
1144 |
|
1145 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::SetSoundConfig - %d",KErrNone)); |
|
1146 return(r); |
|
1147 } |
|
1148 |
|
1149 /** |
|
1150 Apply a new audio format configuration. |
|
1151 @param aSoundConfig A reference to a sound configuration object holding the new audio configuration settings to be applied. |
|
1152 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1153 */ |
|
1154 TInt DSoundScLdd::DoSetSoundConfig(const TCurrentSoundFormatV02& aSoundConfig) |
|
1155 { |
|
1156 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:DoSetSoundConfig")); |
|
1157 |
|
1158 // We're about to replace any previous configuration - so set the |
|
1159 // status back to un-configured in case we don't succeed with the new one. |
|
1160 iSoundConfigFlags&=~KSndScSoundConfigIsSetup; |
|
1161 |
|
1162 // Call the PDD to change the hardware configuration according to the new specification. |
|
1163 // Pass it as a descriptor - to support future changes to the config structure. |
|
1164 TPtrC8 ptr((TUint8*)&aSoundConfig,sizeof(aSoundConfig)); |
|
1165 TInt r=Pdd()->SetConfig(ptr); |
|
1166 if (r!=KErrNone) |
|
1167 return(r); |
|
1168 |
|
1169 // Setting up the new play configuration has succeeded so save the new configuration. |
|
1170 iSoundConfig=aSoundConfig; |
|
1171 iSoundConfigFlags|=KSndScSoundConfigIsSetup; |
|
1172 |
|
1173 // For some devices, the maximum transfer length supported will vary according to the configuration. |
|
1174 if (iBufManager) |
|
1175 iBufManager->iMaxTransferLen=Pdd()->MaxTransferLen(); |
|
1176 |
|
1177 return(r); |
|
1178 } |
|
1179 |
|
1180 /** |
|
1181 Set the current play volume or record level. |
|
1182 @param aVolume The play volume / record level to be set - a value in the range 0 to 255. The value 255 equates to |
|
1183 the maximum volume and each value below this equates to a 0.5dB step below it. |
|
1184 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1185 */ |
|
1186 TInt DSoundScLdd::SetVolume(TInt aVolume) |
|
1187 { |
|
1188 TInt r; |
|
1189 // Check if the volume specified is in range. |
|
1190 if (aVolume>=0 && aVolume<=255) |
|
1191 { |
|
1192 // Check if we need to change it. |
|
1193 if (!(iSoundConfigFlags&KSndScVolumeIsSetup) || aVolume!=iVolume) |
|
1194 { |
|
1195 // We're about to replace any previous volume setting - so set the |
|
1196 // status back to un-set in case we don't succeed with the new setting. |
|
1197 iSoundConfigFlags&=~KSndScVolumeIsSetup; |
|
1198 |
|
1199 r=Pdd()->SetVolume(aVolume); |
|
1200 if (r==KErrNone) |
|
1201 { |
|
1202 iVolume=aVolume; |
|
1203 iSoundConfigFlags|=KSndScVolumeIsSetup; |
|
1204 } |
|
1205 } |
|
1206 else |
|
1207 r=KErrNone; |
|
1208 } |
|
1209 else |
|
1210 r=KErrArgument; |
|
1211 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::SetVolume(%d) - %d",aVolume,r)); |
|
1212 return(r); |
|
1213 } |
|
1214 |
|
1215 /** |
|
1216 Handle a play request from the client. |
|
1217 @param aStatus The request status to be signalled when the play request is complete. |
|
1218 @param aChunkOffset Offset from the beginning of the play chunk for the start of data to be played. |
|
1219 @param aLength The number of bytes of data to be played. |
|
1220 @param aFlags The play request flags which were supplied by the client for this request. |
|
1221 @param aThread The client thread which issued the request and which supplied the request status. |
|
1222 @return KErrNone if successful; |
|
1223 KErrArgument if the offset or length arguments are not fully contained within a buffer or don't meet the |
|
1224 alignment contraints of the PDD; |
|
1225 KErrNoMemory if a memory error was ecountered in the handling of this request. |
|
1226 otherwise one of the other system-wide error codes. |
|
1227 */ |
|
1228 TInt DSoundScLdd::PlayData(TRequestStatus* aStatus,TSoundScPlayRequest* aRequest,DThread* aThread) |
|
1229 { |
|
1230 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:PlayData(off:%x len:%d)",aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen())); |
|
1231 |
|
1232 // Purge the region of the play chunk concerned. |
|
1233 iBufManager->FlushData(aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen(),DBufferManager::EFlushBeforeDmaWrite); |
|
1234 |
|
1235 |
|
1236 TInt r(KErrNone); |
|
1237 |
|
1238 // finalise the request data here |
|
1239 r = aRequest->iClientRequest->SetStatus(aStatus); |
|
1240 if (r!=KErrNone) |
|
1241 return(r); |
|
1242 |
|
1243 aRequest->iOwningThread = aThread; |
|
1244 |
|
1245 |
|
1246 // Check whether we have started the codec yet. |
|
1247 CancelPlayEofTimer(); |
|
1248 if (iState==EConfigured) |
|
1249 { |
|
1250 r=Pdd()->StartTransfer(); |
|
1251 |
|
1252 // Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer(). |
|
1253 #ifdef _DEBUG |
|
1254 if (iTestSettings & KSoundScTest_StartTransferError) |
|
1255 { |
|
1256 iTestSettings&=(~KSoundScTest_StartTransferError); |
|
1257 r=KErrTimedOut; |
|
1258 // Any time that StartTransfer() is called on the PDD it must have a matching StopTransfer() before |
|
1259 // it is called again |
|
1260 Pdd()->StopTransfer(); |
|
1261 } |
|
1262 #endif |
|
1263 } |
|
1264 |
|
1265 if (r==KErrNone) |
|
1266 { |
|
1267 // No further error is possible at this stage so add the request to the queue. |
|
1268 iReqQueue->Add(aRequest); |
|
1269 |
|
1270 if (iState!=EPaused) |
|
1271 { |
|
1272 iState=EActive; |
|
1273 StartNextPlayTransfers(); // Queue as many transfer requests on the PDD as it can accept. |
|
1274 } |
|
1275 } |
|
1276 else |
|
1277 iReqQueue->Free(aRequest); // Return the unused request object |
|
1278 |
|
1279 return(r); |
|
1280 } |
|
1281 |
|
1282 /** |
|
1283 @publishedPartner |
|
1284 @prototype |
|
1285 |
|
1286 Called from the PDD each time it has completed a data transfer from a play buffer. This function must be called |
|
1287 in the context of the DFC thread used for processing requests. |
|
1288 The function performed here is to check whether the entire transfer for the current request is now complete. Also to |
|
1289 queue further requests on the PDD which should now have the capability to accept more transfers. If the current |
|
1290 request is complete then we signal completion to the client. |
|
1291 @param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be |
|
1292 uniquely identified. |
|
1293 @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other |
|
1294 system wide error codes. |
|
1295 @param aBytesPlayed The number of bytes played from the play buffer. |
|
1296 */ |
|
1297 void DSoundScLdd::PlayCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesPlayed) |
|
1298 { |
|
1299 #ifdef _DEBUG |
|
1300 #ifdef TEST_WITH_PAGING_CACHE_FLUSHES |
|
1301 Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); |
|
1302 #endif |
|
1303 #endif |
|
1304 // Test settings - only possible in debug mode. |
|
1305 #ifdef _DEBUG |
|
1306 if (iTestSettings & KSoundScTest_TransferDataError) |
|
1307 { |
|
1308 iTestSettings&=(~KSoundScTest_TransferDataError); |
|
1309 aTransferResult=KErrTimedOut; |
|
1310 } |
|
1311 #endif |
|
1312 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PlayCallback(ID:%xH,Len:%d) - %d",aTransferID,aBytesPlayed,aTransferResult)); |
|
1313 |
|
1314 // The PDD has completed transfering a fragment. Find the associated request from its ID |
|
1315 TBool isNextToComplete; |
|
1316 TSoundScPlayRequest* req=((TSoundScPlayRequestQueue*)iReqQueue)->Find(aTransferID,isNextToComplete); |
|
1317 |
|
1318 // 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 |
|
1319 // (because it was already completed back to client) or if the request status is already set as 'done'. |
|
1320 if (req && req->iTf.iTfState!=TSndScTransfer::ETfDone) |
|
1321 { |
|
1322 __ASSERT_DEBUG(req->iTf.iTfState!=TSndScTransfer::ETfNotStarted,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
1323 |
|
1324 // Update the count of bytes played. |
|
1325 iBytesTransferred+=aBytesPlayed; |
|
1326 |
|
1327 if (aTransferResult!=KErrNone) |
|
1328 { |
|
1329 // Transfer failed - immediately mark the request as being complete. |
|
1330 req->SetFail(aTransferResult); |
|
1331 } |
|
1332 else |
|
1333 req->UpdateProgress(aBytesPlayed); // Transfer successful so update the progress of the request. |
|
1334 |
|
1335 // 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. |
|
1336 if (req->iTf.iTfState==TSndScTransfer::ETfDone && isNextToComplete) |
|
1337 CompleteAllDonePlayRequests(req); |
|
1338 } |
|
1339 |
|
1340 // PDD should now have the capacity to accept another transfer so queue as many transfers |
|
1341 // on it as it can accept. |
|
1342 StartNextPlayTransfers(); |
|
1343 |
|
1344 |
|
1345 return; |
|
1346 } |
|
1347 |
|
1348 /** |
|
1349 This function checks whether there are any outstanding play requests. While there are, it breaks these down into |
|
1350 transfers sizes which are compatible with the PDD and then repeatedly attempts to queue these data transfers on the |
|
1351 PDD until it indicates that it can accept no more for the moment. |
|
1352 @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. |
|
1353 */ |
|
1354 void DSoundScLdd::StartNextPlayTransfers() |
|
1355 { |
|
1356 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextPlayTransfers")); |
|
1357 |
|
1358 // Queue as many transfers on the PDD as it can accept. |
|
1359 TSoundScPlayRequest* req; |
|
1360 TInt r=KErrNone; |
|
1361 while (r==KErrNone && (req=((TSoundScPlayRequestQueue*)iReqQueue)->NextRequestForTransfer())!=NULL) |
|
1362 { |
|
1363 TInt pos=req->iTf.GetStartOffset(); |
|
1364 TPhysAddr physAddr; |
|
1365 TInt len=req->iTf.iAudioBuffer->GetFragmentLength(pos,req->iTf.GetNotStartedLen(),physAddr); |
|
1366 if (len>0) |
|
1367 { |
|
1368 r=Pdd()->TransferData(req->iTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); |
|
1369 __KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) - %d",pos,len,r)); |
|
1370 if (r==KErrNone) |
|
1371 req->iTf.SetStarted(len); // Successfully queued a transfer - update the request status. |
|
1372 else if (r!=KErrNotReady) |
|
1373 { |
|
1374 // Transfer error from PDD, fail the request straight away. (Might not be the one at the head of queue). |
|
1375 CompletePlayRequest(req,r); |
|
1376 } |
|
1377 } |
|
1378 else |
|
1379 { |
|
1380 // This can only be a zero length play request - just complete it straight away |
|
1381 CompletePlayRequest(req,KErrNone); |
|
1382 } |
|
1383 } |
|
1384 return; |
|
1385 } |
|
1386 |
|
1387 /** |
|
1388 Complete a client play request back to the client and remove it from the request queue. |
|
1389 @param aReq A pointer to the play request object to be completed. |
|
1390 @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. |
|
1391 */ |
|
1392 void DSoundScLdd::DoCompletePlayRequest(TSoundScPlayRequest* aReq) |
|
1393 { |
|
1394 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCompletePlayRequest(%x) - %d",aReq,aReq->iCompletionReason)); |
|
1395 |
|
1396 iReqQueue->Remove(aReq); |
|
1397 |
|
1398 // If the request queue is now empty then turn off the codec |
|
1399 if (iReqQueue->IsEmpty()) |
|
1400 { |
|
1401 #ifdef USE_PLAY_EOF_TIMER |
|
1402 StartPlayEofTimer(); |
|
1403 #else |
|
1404 Pdd()->StopTransfer(); |
|
1405 iState=EConfigured; |
|
1406 #endif |
|
1407 // This is an underflow situation. |
|
1408 if (aReq->iCompletionReason==KErrNone && aReq->iFlags!=KSndFlagLastSample) |
|
1409 aReq->iCompletionReason=KErrUnderflow; |
|
1410 } |
|
1411 |
|
1412 CompleteRequest(aReq->iOwningThread,NULL,aReq->iCompletionReason,aReq->iClientRequest); |
|
1413 iReqQueue->Free(aReq); |
|
1414 return; |
|
1415 } |
|
1416 |
|
1417 /** |
|
1418 Complete one or more play requests. This function completes the play request specified. It also completes any other play |
|
1419 requests which immediately follow the one specified in the play request queue and for which transfer has been completed by the PDD. |
|
1420 @param aReq A pointer to the play request object to be completed. |
|
1421 @post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured. |
|
1422 */ |
|
1423 void DSoundScLdd::CompleteAllDonePlayRequests(TSoundScPlayRequest* aReq) |
|
1424 { |
|
1425 TSoundScPlayRequest* nextReq=aReq; |
|
1426 TSoundScPlayRequest* req; |
|
1427 do |
|
1428 { |
|
1429 req=nextReq; |
|
1430 nextReq=(TSoundScPlayRequest*)req->iNext; |
|
1431 DoCompletePlayRequest(req); |
|
1432 } |
|
1433 while (!iReqQueue->IsAnchor(nextReq) && nextReq->iTf.iTfState==TSndScTransfer::ETfDone); |
|
1434 return; |
|
1435 } |
|
1436 |
|
1437 /** |
|
1438 Start the audio device recording data. |
|
1439 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1440 */ |
|
1441 TInt DSoundScLdd::StartRecord() |
|
1442 { |
|
1443 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartRecord")); |
|
1444 |
|
1445 // Reset all the audio buffer lists |
|
1446 NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. |
|
1447 ((DRecordBufferManager*)iBufManager)->Reset(); |
|
1448 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1449 |
|
1450 // Reset the transfer status for the current and pending record buffers. |
|
1451 TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer(); |
|
1452 iCurrentRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID |
|
1453 buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); |
|
1454 iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID |
|
1455 |
|
1456 // Call the PDD to prepare the hardware for recording. |
|
1457 TInt r=Pdd()->StartTransfer(); |
|
1458 |
|
1459 // Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer(). |
|
1460 #ifdef _DEBUG |
|
1461 if (iTestSettings & KSoundScTest_StartTransferError) |
|
1462 { |
|
1463 iTestSettings&=(~KSoundScTest_StartTransferError); |
|
1464 r=KErrTimedOut; |
|
1465 } |
|
1466 #endif |
|
1467 |
|
1468 // Initiate data transfer into the first record buffer(s). |
|
1469 if (r==KErrNone) |
|
1470 r=StartNextRecordTransfers(); |
|
1471 return(r); |
|
1472 } |
|
1473 |
|
1474 /** |
|
1475 Handle a record request from the client once data transfer has been intiated. |
|
1476 @param aStatus The request status to be signalled when the record request is complete. If the request is successful |
|
1477 then this is set to the offset within the shared chunk where the record data resides. Alternatively, if an error |
|
1478 occurs, it will be set to one of the system wide error values. |
|
1479 @param aLengthPtr A pointer to a TInt object in client memory. On completion, the number of bytes successfully |
|
1480 recorded are written to this object. |
|
1481 @param aThread The client thread which issued the request and which supplied the request status. |
|
1482 @return KErrNone if successful; |
|
1483 KErrInUse: if the client needs to free up record buffers before further record requests can be accepted; |
|
1484 KErrCancel: if the driver is in paused mode and there are no complete or partially full buffers to return. |
|
1485 otherwise one of the other system-wide error codes. |
|
1486 */ |
|
1487 TInt DSoundScLdd::RecordData(TRequestStatus* aStatus,TInt* aLengthPtr,DThread* aThread) |
|
1488 { |
|
1489 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:RecordData")); |
|
1490 |
|
1491 TInt r=KErrNone; |
|
1492 |
|
1493 NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. |
|
1494 |
|
1495 // Check if we have had an overflow since the last record request was completed. |
|
1496 if (((DRecordBufferManager*)iBufManager)->iBufOverflow) |
|
1497 { |
|
1498 ((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse; |
|
1499 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1500 return(KErrOverflow); |
|
1501 } |
|
1502 |
|
1503 // See if there is a buffer already available. |
|
1504 TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient(); |
|
1505 if (buf) |
|
1506 { |
|
1507 // There is an buffer available already - complete the request returning the offset of the buffer to the client. |
|
1508 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1509 |
|
1510 r=buf->iResult; |
|
1511 |
|
1512 if (r==KErrNone) |
|
1513 { |
|
1514 kumemput(aLengthPtr,&buf->iBytesAdded,sizeof(TInt)); |
|
1515 // Only complete if successful here. Errors will be completed on returning from this method. |
|
1516 CompleteRequest(aThread,aStatus,(buf->iChunkOffset)); |
|
1517 } |
|
1518 return(r); |
|
1519 } |
|
1520 |
|
1521 // If we are paused and there was no un-read data to return to the client then return KErrCancel to prompt them to resume. |
|
1522 if (iState==EPaused) |
|
1523 { |
|
1524 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1525 return(KErrCancel); |
|
1526 } |
|
1527 |
|
1528 // The buffer 'completed' list is empty. If the buffer 'free' list is empty too then the client needs |
|
1529 // to free some buffers up - return an error. |
|
1530 if (((DRecordBufferManager*)iBufManager)->iFreeBufferQ.IsEmpty()) |
|
1531 { |
|
1532 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1533 return(KErrInUse); |
|
1534 } |
|
1535 |
|
1536 // Acquire a new request object and add it to the queue of pending requests. The request will be completed |
|
1537 // from the PDD and the DFC thread when a buffer is available. |
|
1538 NKern::FMSignal(&iMutex); |
|
1539 TSoundScRequest* req=iReqQueue->NextFree(); |
|
1540 NKern::FMWait(&iMutex); |
|
1541 if (req) |
|
1542 { |
|
1543 r=req->iClientRequest->SetStatus(aStatus); |
|
1544 req->iOwningThread=aThread; |
|
1545 ((TClientDataRequest<TInt>*)req->iClientRequest)->SetDestPtr((TInt*)aLengthPtr); |
|
1546 // Add the request to the queue |
|
1547 iReqQueue->Add(req); |
|
1548 } |
|
1549 else |
|
1550 r=KErrGeneral; // Must have exceeded KMaxSndScRequestsPending. |
|
1551 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1552 |
|
1553 return(r); |
|
1554 } |
|
1555 |
|
1556 /** |
|
1557 Release a buffer which was being used by client. |
|
1558 @param aChunkOffset The chunk offset corresponding to the buffer to be freed. |
|
1559 @return KErrNone if successful; |
|
1560 KErrNotFound if no 'in use' buffer had the specified chunk offset. |
|
1561 KErrNotReady if the channel is not configured (either for audio or its buffer config). |
|
1562 */ |
|
1563 TInt DSoundScLdd::ReleaseBuffer(TInt aChunkOffset) |
|
1564 { |
|
1565 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ReleaseBuffer(%x)",aChunkOffset)); |
|
1566 |
|
1567 TInt r=KErrNotReady; |
|
1568 if (iState!=EOpen && iBufManager) |
|
1569 { |
|
1570 TAudioBuffer* buf=NULL; |
|
1571 NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. |
|
1572 buf=((DRecordBufferManager*)iBufManager)->ReleaseBuffer(aChunkOffset); |
|
1573 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1574 if (buf) |
|
1575 { |
|
1576 buf->Flush(DBufferManager::EFlushBeforeDmaRead); |
|
1577 r=KErrNone; |
|
1578 } |
|
1579 else |
|
1580 r=KErrNotFound; |
|
1581 } |
|
1582 return(r); |
|
1583 } |
|
1584 |
|
1585 /** |
|
1586 Handle a custom configuration request. |
|
1587 @param aFunction A number identifying the request. |
|
1588 @param aParam A 32-bit value passed to the driver. Its meaning depends on the request. |
|
1589 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
1590 */ |
|
1591 TInt DSoundScLdd::CustomConfig(TInt aFunction,TAny* aParam) |
|
1592 { |
|
1593 |
|
1594 TInt r; |
|
1595 if (aFunction>KSndCustomConfigMaxReserved) |
|
1596 r=Pdd()->CustomConfig(aFunction,aParam); |
|
1597 else |
|
1598 { |
|
1599 r=KErrNotSupported; |
|
1600 #ifdef _DEBUG |
|
1601 switch (aFunction) |
|
1602 { |
|
1603 case KSndCustom_ForceHwConfigNotifSupported: |
|
1604 iCaps.iHwConfigNotificationSupport=ETrue; |
|
1605 r=KErrNone; |
|
1606 break; |
|
1607 case KSndCustom_CompleteChangeOfHwConfig: |
|
1608 NotifyChangeOfHwConfigCallback((TBool)aParam); |
|
1609 r=KErrNone; |
|
1610 break; |
|
1611 case KSndCustom_ForceStartTransferError: |
|
1612 iTestSettings|=KSoundScTest_StartTransferError; |
|
1613 r=KErrNone; |
|
1614 break; |
|
1615 case KSndCustom_ForceTransferDataError: |
|
1616 iTestSettings|=KSoundScTest_TransferDataError; |
|
1617 r=KErrNone; |
|
1618 break; |
|
1619 case KSndCustom_ForceTransferTimeout: |
|
1620 iTestSettings|=KSoundScTest_TransferTimeout; |
|
1621 r=KErrNone; |
|
1622 break; |
|
1623 } |
|
1624 #endif |
|
1625 } |
|
1626 return(r); |
|
1627 } |
|
1628 |
|
1629 /** |
|
1630 @publishedPartner |
|
1631 @prototype |
|
1632 |
|
1633 Called from the PDD each time it has completed a data transfer into the current record buffer. |
|
1634 The function performed here is to check whether the transfer into the current buffer is now complete. Also to queue |
|
1635 further requests on the PDD which should now have the capability to accept more transfers. If transfer into the |
|
1636 current buffer is now complete then we need to update the buffer lists and possibly complete a request back the client. |
|
1637 While recording hasn't been paused and no error has occured then this completed buffer ought to be full. However, when |
|
1638 recording has just been paused, the PDD can also call this function to complete a partially filled record buffer. In fact |
|
1639 in some circumstances, pausing may result in the PDD calling this function where it turns out that no data has been |
|
1640 recorded into this buffer. In this case we don't want to signal a null transfer back to the client. |
|
1641 @param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be |
|
1642 uniquely identified. |
|
1643 @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other |
|
1644 system wide error codes. |
|
1645 @param aBytesRecorded The number of bytes recorded into the record buffer. |
|
1646 */ |
|
1647 void DSoundScLdd::RecordCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesRecorded) |
|
1648 { |
|
1649 #ifdef _DEBUG |
|
1650 #ifdef TEST_WITH_PAGING_CACHE_FLUSHES |
|
1651 Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); |
|
1652 #endif |
|
1653 #endif |
|
1654 |
|
1655 #ifdef _DEBUG |
|
1656 // Test settings - only possible in debug mode. |
|
1657 if (iTestSettings & KSoundScTest_TransferDataError) |
|
1658 { |
|
1659 iTestSettings&=(~KSoundScTest_TransferDataError); |
|
1660 aTransferResult=KErrTimedOut; |
|
1661 } |
|
1662 #endif |
|
1663 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::RecordCallback(ID:%xH,Len:%d) - %d (iCurrentRecBufTf.iTfState %d)",aTransferID,aBytesRecorded,aTransferResult, iCurrentRecBufTf.iTfState)); |
|
1664 |
|
1665 // If the transfer fragment is not for the current record buffer and were not paused then ignore it. Either the PDD |
|
1666 // has got very confused or more likely its a trailing fragment from an earlier buffer we have already failed. If |
|
1667 // we're paused, the PDD doesn't need to bother with a transfer ID, we assume its for the current buffer. |
|
1668 if (iCurrentRecBufTf.iTfState != TSndScTransfer::ETfDone && |
|
1669 (aTransferID==iCurrentRecBufTf.iId || (aTransferID == 0 && iState==EPaused))) |
|
1670 { |
|
1671 // Update the count of bytes recorded. |
|
1672 iBytesTransferred+=aBytesRecorded; |
|
1673 |
|
1674 // Update the transfer status of the current buffer. |
|
1675 if (aTransferResult!=KErrNone) |
|
1676 { |
|
1677 // Transfer failed. Mark the buffer as being complete. |
|
1678 iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone; |
|
1679 } |
|
1680 else |
|
1681 iCurrentRecBufTf.SetCompleted(aBytesRecorded); // Transfer successful so update the progress. |
|
1682 |
|
1683 // Check if this is the PDD completing a fragment due to record being paused. In this situation we only allow the |
|
1684 // PDD to complete one fragment. |
|
1685 TAudioBuffer* buf; |
|
1686 if (iState==EPaused && ++iCompletesWhilePausedCount<2) |
|
1687 { |
|
1688 // Complete (i.e. abort) the transfer to the current buffer. |
|
1689 iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone; |
|
1690 |
|
1691 // Reset the transfer status for the pending record buffer. This will be switched to the current buffer later |
|
1692 // in this function - ready for when record is resumed. |
|
1693 buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); |
|
1694 iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID |
|
1695 } |
|
1696 |
|
1697 // Check if we have just completed the transfer into the current buffer. |
|
1698 if (iCurrentRecBufTf.iTfState==TSndScTransfer::ETfDone) |
|
1699 HandleCurrentRecordBufferDone(aTransferResult); |
|
1700 } |
|
1701 |
|
1702 // If we're not paused then the PDD should now have the capacity to accept another transfer so queue as many |
|
1703 // transfers on it as it can accept. |
|
1704 if (iState==EActive) |
|
1705 { |
|
1706 #ifdef _DEBUG |
|
1707 // Test settings - only possible in debug mode. Test LDD being slow servicing transfer completes from PDD. Disabled. |
|
1708 /* if (iTestSettings & KSoundScTest_TransferTimeout) |
|
1709 { |
|
1710 iTestSettings&=(~KSoundScTest_TransferTimeout); |
|
1711 Kern::NanoWait(500000000); // Pause for 0.5 second |
|
1712 } */ |
|
1713 #endif |
|
1714 TInt r=StartNextRecordTransfers(); |
|
1715 if (r!=KErrNone) |
|
1716 { |
|
1717 // Problem starting the next transfer. That's fairly serious so complete all pending record requests and |
|
1718 // stop recording. |
|
1719 Pdd()->StopTransfer(); |
|
1720 iReqQueue->CompleteAll(r,&iMutex); |
|
1721 iState=EConfigured; |
|
1722 } |
|
1723 } |
|
1724 return; |
|
1725 } |
|
1726 |
|
1727 /** Perform the necessary processing required when transfer into the current buffer is complete. This involves updating |
|
1728 the buffer lists and possibly complete a request back the client. |
|
1729 @param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other |
|
1730 system wide error codes. |
|
1731 */ |
|
1732 void DSoundScLdd::HandleCurrentRecordBufferDone(TInt aTransferResult) |
|
1733 { |
|
1734 TAudioBuffer* buf; |
|
1735 |
|
1736 // Flush the buffer before acquiring the mutex. |
|
1737 buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer(); |
|
1738 buf->Flush(DBufferManager::EFlushAfterDmaRead); |
|
1739 |
|
1740 NKern::FMWait(&iMutex); // Acquire the buffer/request list mutex. |
|
1741 |
|
1742 // Update the buffer list (by either adding the current buffer to the completed list or the free list). |
|
1743 TInt bytesRecorded=iCurrentRecBufTf.GetLengthTransferred(); |
|
1744 ((DRecordBufferManager*)iBufManager)->SetBufferFilled(bytesRecorded,aTransferResult); |
|
1745 |
|
1746 // The pending buffer now becomes the current one and we need to get a new pending one. |
|
1747 iCurrentRecBufTf=iNextRecBufTf; |
|
1748 buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer(); |
|
1749 iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf); // Use pointer to record buffer as unique ID |
|
1750 |
|
1751 // Check if there is a client record request pending. |
|
1752 if (!iReqQueue->IsEmpty()) |
|
1753 { |
|
1754 // A record request is pending. Check if we have had an overflow since the last record request was completed. |
|
1755 if (((DRecordBufferManager*)iBufManager)->iBufOverflow) |
|
1756 { |
|
1757 TSoundScRequest* req=iReqQueue->Remove(); |
|
1758 DThread* thread=req->iOwningThread; // Take a copy before we free it. |
|
1759 TClientRequest* clreq = req->iClientRequest; // Take a copy before we free it. |
|
1760 ((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse; |
|
1761 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1762 iReqQueue->Free(req); |
|
1763 CompleteRequest(thread,NULL,KErrOverflow,clreq); // Complete the request. |
|
1764 } |
|
1765 else |
|
1766 { |
|
1767 // Check there really is a buffer available. (There's no guarentee the one just completed hasn't |
|
1768 // immediately been queued again: if the client has too many 'in-use' or the one completed was a NULL |
|
1769 // transfer due to pausing). |
|
1770 TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient(); |
|
1771 if (buf) |
|
1772 { |
|
1773 // There still a buffer available so complete the request. |
|
1774 TSoundScRequest* req=iReqQueue->Remove(); |
|
1775 DThread* thread=req->iOwningThread; // Take a copy before we free it. |
|
1776 TClientRequest* clreq = req->iClientRequest; // Take a copy before we free it. |
|
1777 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1778 iReqQueue->Free(req); |
|
1779 if (buf->iResult==KErrNone) |
|
1780 { |
|
1781 ((TClientDataRequest<TInt>*)clreq)->Data() = buf->iBytesAdded; |
|
1782 CompleteRequest(thread,NULL,buf->iChunkOffset,clreq); // Complete the request. |
|
1783 } |
|
1784 else |
|
1785 CompleteRequest(thread,NULL,buf->iResult,clreq); // Complete the request. |
|
1786 } |
|
1787 else |
|
1788 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1789 } |
|
1790 } |
|
1791 else |
|
1792 NKern::FMSignal(&iMutex); // Release the buffer/request list mutex. |
|
1793 } |
|
1794 |
|
1795 /** |
|
1796 This function starts the next record data transfer. It starts with the current record buffer - checking whether all of |
|
1797 this has now been transferred or queued for transfer. If not it breaks this down into transfers sizes which are |
|
1798 compatible with the PDD and then repeatedly attempts to queue these on the PDD until the PDF indicates that it can |
|
1799 accept no more transfers for the moment. If the record buffer is fully started in this way and the PDD still has the |
|
1800 capacity to accept more transfers then it moves on to start the pending record buffer. |
|
1801 @return Normally KErrNone unless the PDD incurs an error while attempting to start a new transfer. |
|
1802 */ |
|
1803 TInt DSoundScLdd::StartNextRecordTransfers() |
|
1804 { |
|
1805 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextRecordTransfers")); |
|
1806 |
|
1807 // First start with the current record buffer - keep queuing transfers either until this buffer is |
|
1808 // fully started or until the PDD can accept no more transfers. |
|
1809 TInt r=KErrNone; |
|
1810 while (r==KErrNone && iCurrentRecBufTf.iTfState<TSndScTransfer::ETfFullyStarted) |
|
1811 { |
|
1812 TInt pos=iCurrentRecBufTf.GetStartOffset(); |
|
1813 TPhysAddr physAddr; |
|
1814 TInt len=iCurrentRecBufTf.iAudioBuffer->GetFragmentLength(pos,iCurrentRecBufTf.GetNotStartedLen(),physAddr); |
|
1815 |
|
1816 r=Pdd()->TransferData(iCurrentRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); |
|
1817 __KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) A - %d",pos,len,r)); |
|
1818 if (r==KErrNone) |
|
1819 iCurrentRecBufTf.SetStarted(len); // Successfully queued a transfer - update the status. |
|
1820 } |
|
1821 |
|
1822 // Either the current record transfer is now fully started, or the PDD can accept no more transfers |
|
1823 // If the PDD can still accept more transfers then move on to the next record buffer - again, keep queuing |
|
1824 // transfers either until this buffer is fully started or until the PDD can accept no more. |
|
1825 while (r==KErrNone && iNextRecBufTf.iTfState<TSndScTransfer::ETfFullyStarted) |
|
1826 { |
|
1827 TInt pos=iNextRecBufTf.GetStartOffset(); |
|
1828 TPhysAddr physAddr; |
|
1829 TInt len=iNextRecBufTf.iAudioBuffer->GetFragmentLength(pos,iNextRecBufTf.GetNotStartedLen(),physAddr); |
|
1830 |
|
1831 r=Pdd()->TransferData(iNextRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len); |
|
1832 __KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) B - %d",pos,len,r)); |
|
1833 if (r==KErrNone) |
|
1834 iNextRecBufTf.SetStarted(len); // Successfully queued a transfer - update the status. |
|
1835 } |
|
1836 if (r==KErrNotReady) |
|
1837 r=KErrNone; // KErrNotReady means the PDD the cannot accept any more requests - this isn't an error. |
|
1838 return(r); |
|
1839 } |
|
1840 |
|
1841 /** |
|
1842 @publishedPartner |
|
1843 @prototype |
|
1844 |
|
1845 Called from the PDD each time it detects a change in the hardware configuration of the device. |
|
1846 @param aHeadsetPresent This is set by the PDD to ETrue if a microphone or headset socket is now present or EFalse if |
|
1847 such a device is not present. |
|
1848 */ |
|
1849 void DSoundScLdd::NotifyChangeOfHwConfigCallback(TBool aHeadsetPresent) |
|
1850 { |
|
1851 #ifdef _DEBUG |
|
1852 #ifdef TEST_WITH_PAGING_CACHE_FLUSHES |
|
1853 Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); |
|
1854 #endif |
|
1855 #endif |
|
1856 |
|
1857 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::NotifyChangeOfHwConfigCallback(Pres:%d)",aHeadsetPresent)); |
|
1858 |
|
1859 __ASSERT_DEBUG(iCaps.iHwConfigNotificationSupport,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
1860 |
|
1861 if (iNotifyChangeOfHwClientRequest->IsReady()) |
|
1862 { |
|
1863 iNotifyChangeOfHwClientRequest->Data() = aHeadsetPresent; |
|
1864 CompleteRequest(iChangeOfHwConfigThread,NULL,KErrNone,iNotifyChangeOfHwClientRequest); // Complete the request. |
|
1865 } |
|
1866 } |
|
1867 |
|
1868 /** |
|
1869 This function validates that a new sound format configuration is both sensible and supported by this device. |
|
1870 @param aConfig A reference to the new sound format configuration object. |
|
1871 */ |
|
1872 TInt DSoundScLdd::ValidateConfig(const TCurrentSoundFormatV02& aConfig) |
|
1873 { |
|
1874 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ValidateConfig")); |
|
1875 |
|
1876 // Check that the audio channel configuration requested is sensible and supported by this device. |
|
1877 if (aConfig.iChannels<0) |
|
1878 return(KErrNotSupported); |
|
1879 TInt chans=(aConfig.iChannels-1); |
|
1880 if (!(iCaps.iChannels & (1<<chans))) |
|
1881 return(KErrNotSupported); |
|
1882 |
|
1883 // Check that the sample rate requested is sensible and supported by this device. |
|
1884 if (aConfig.iRate<0 || !(iCaps.iRates & (1<<aConfig.iRate))) |
|
1885 return(KErrNotSupported); |
|
1886 |
|
1887 // Check that the encoding format requested is sensible and supported by this device. |
|
1888 if (aConfig.iEncoding<0 || !(iCaps.iEncodings & (1<<aConfig.iEncoding))) |
|
1889 return(KErrNotSupported); |
|
1890 |
|
1891 // Check that the data format requested is sensible and supported by this device. |
|
1892 if (aConfig.iDataFormat<0 || !(iCaps.iDataFormats & (1<<aConfig.iDataFormat))) |
|
1893 return(KErrNotSupported); |
|
1894 |
|
1895 __KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::ValidateConfig - %d",KErrNone)); |
|
1896 return(KErrNone); |
|
1897 } |
|
1898 |
|
1899 /** |
|
1900 Increase or decrease the memory area allocated to hold the current buffer configuration in the play/record chunk. |
|
1901 @param aNumBuffers The number of buffers within the new buffer configuration. This determines the size of the memory |
|
1902 area required. |
|
1903 @pre The thread must be in a critical section. |
|
1904 */ |
|
1905 TInt DSoundScLdd::ReAllocBufferConfigInfo(TInt aNumBuffers) |
|
1906 { |
|
1907 if (iBufConfig) |
|
1908 { |
|
1909 delete iBufConfig; |
|
1910 iBufConfig=NULL; |
|
1911 } |
|
1912 |
|
1913 iBufConfigSize=aNumBuffers*sizeof(TInt); |
|
1914 iBufConfigSize+=sizeof(TSharedChunkBufConfigBase); |
|
1915 iBufConfig=(TSoundSharedChunkBufConfig*)Kern::AllocZ(iBufConfigSize); |
|
1916 if (!iBufConfig) |
|
1917 return(KErrNoMemory); |
|
1918 |
|
1919 return(KErrNone); |
|
1920 } |
|
1921 |
|
1922 /** |
|
1923 Start the EOF play timer. A 2 second timer is queued each time the transfer of playback data ceases by the LLD and if it |
|
1924 is allowed to expire, then the PDD is called to release any resources in use for playback transfer. |
|
1925 */ |
|
1926 void DSoundScLdd::StartPlayEofTimer() |
|
1927 { |
|
1928 iEofTimer.Cancel(); |
|
1929 iPlayEofDfc.Cancel(); |
|
1930 iEofTimer.OneShot(NKern::TimerTicks(2000)); // Queue the 2 second EOF timer to stop transfer on the PDD. |
|
1931 iPlayEofTimerActive=ETrue; |
|
1932 } |
|
1933 |
|
1934 /** |
|
1935 Cancel the EOF play timer. |
|
1936 */ |
|
1937 void DSoundScLdd::CancelPlayEofTimer() |
|
1938 { |
|
1939 iEofTimer.Cancel(); |
|
1940 iPlayEofDfc.Cancel(); |
|
1941 iPlayEofTimerActive=EFalse; |
|
1942 } |
|
1943 |
|
1944 /** |
|
1945 @publishedPartner |
|
1946 @prototype |
|
1947 |
|
1948 Returns the buffer configuration of the play/record chunk. |
|
1949 @return A pointer to the current buffer configuration of the play/record chunk. |
|
1950 */ |
|
1951 TSoundSharedChunkBufConfig* DSoundScLdd::BufConfig() |
|
1952 { |
|
1953 return(iBufConfig); |
|
1954 } |
|
1955 |
|
1956 /** |
|
1957 @publishedPartner |
|
1958 @prototype |
|
1959 |
|
1960 Returns the address of the start of the play/record chunk. |
|
1961 @return The linear address of the start of the play/record chunk. |
|
1962 */ |
|
1963 TLinAddr DSoundScLdd::ChunkBase() |
|
1964 { |
|
1965 return(iBufManager->iChunkBase); |
|
1966 } |
|
1967 |
|
1968 /** |
|
1969 The ISR to handle the EOF play timer. |
|
1970 @param aChannel A pointer to the sound driver logical channel object. |
|
1971 */ |
|
1972 void DSoundScLdd::PlayEofTimerExpired(TAny* aChannel) |
|
1973 { |
|
1974 DSoundScLdd& drv=*(DSoundScLdd*)aChannel; |
|
1975 |
|
1976 drv.iPlayEofDfc.Add(); |
|
1977 } |
|
1978 |
|
1979 /** |
|
1980 The DFC used to handle the EOF play timer. |
|
1981 @param aChannel A pointer to the sound driver logical channel object. |
|
1982 */ |
|
1983 void DSoundScLdd::PlayEofTimerDfc(TAny* aChannel) |
|
1984 { |
|
1985 DSoundScLdd& drv=*(DSoundScLdd*)aChannel; |
|
1986 |
|
1987 drv.Pdd()->StopTransfer(); |
|
1988 drv.iState=EConfigured; |
|
1989 drv.iPlayEofTimerActive=EFalse; |
|
1990 } |
|
1991 |
|
1992 /** |
|
1993 The DFC used to handle power down requests from the power manager before a transition into system |
|
1994 shutdown/standby. |
|
1995 @param aChannel A pointer to the sound driver logical channel object. |
|
1996 */ |
|
1997 void DSoundScLdd::PowerDownDfc(TAny* aChannel) |
|
1998 { |
|
1999 DSoundScLdd& drv=*(DSoundScLdd*)aChannel; |
|
2000 drv.Shutdown(); |
|
2001 drv.iPowerHandler->PowerDownDone(); |
|
2002 } |
|
2003 |
|
2004 /** |
|
2005 The DFC used to handle power up requests from the power manager following a transition out of system standby. |
|
2006 @param aChannel A pointer to the sound driver logical channel object. |
|
2007 */ |
|
2008 void DSoundScLdd::PowerUpDfc(TAny* aChannel) |
|
2009 { |
|
2010 DSoundScLdd& drv=*(DSoundScLdd*)aChannel; |
|
2011 |
|
2012 // Restore the channel to a default state. |
|
2013 drv.DoCancel(RSoundSc::EAllRequests); |
|
2014 drv.Pdd()->PowerUp(); |
|
2015 drv.DoSetSoundConfig(drv.iSoundConfig); |
|
2016 drv.SetVolume(drv.iVolume); |
|
2017 drv.iState=(!drv.iBufConfig)?EOpen:EConfigured; |
|
2018 |
|
2019 drv.iPowerHandler->PowerUpDone(); |
|
2020 } |
|
2021 |
|
2022 /** |
|
2023 Complete an asynchronous request back to the client. |
|
2024 @param aThread The client thread which issued the request. |
|
2025 @param aStatus The TRequestStatus instance that will receive the request status code or NULL if aClientRequest used. |
|
2026 @param aReason The request status code. |
|
2027 @param aClientRequest The TClientRequest instance that will receive the request status code or NULL if aStatus used. |
|
2028 @pre The thread must be in a critical section. |
|
2029 */ |
|
2030 |
|
2031 void DSoundScLdd::CompleteRequest(DThread* aThread, TRequestStatus* aStatus, TInt aReason, TClientRequest* aClientRequest) |
|
2032 { |
|
2033 if (aClientRequest) |
|
2034 { |
|
2035 if (aClientRequest->IsReady()) |
|
2036 { |
|
2037 Kern::QueueRequestComplete(aThread,aClientRequest,aReason); |
|
2038 } |
|
2039 else |
|
2040 { |
|
2041 // should always be ready |
|
2042 __ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
2043 } |
|
2044 } |
|
2045 else if (aStatus) |
|
2046 { |
|
2047 Kern::RequestComplete(aStatus,aReason); // Complete the request back to the client. |
|
2048 } |
|
2049 else |
|
2050 { |
|
2051 // never get here - either aStatus or aClientRequest must be valid |
|
2052 __ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
2053 } |
|
2054 |
|
2055 aThread->AsyncClose(); // Asynchronously close our reference on the client thread - don't want to be blocked if this is final reference. |
|
2056 |
|
2057 #ifdef _DEBUG |
|
2058 __e32_atomic_add_ord32(&iThreadOpenCount, TUint32(-1)); |
|
2059 #endif |
|
2060 } |
|
2061 |
|
2062 /** |
|
2063 Constructor for the play request object. |
|
2064 */ |
|
2065 TSoundScPlayRequest::TSoundScPlayRequest() |
|
2066 : TSoundScRequest() |
|
2067 { |
|
2068 iFlags=0; |
|
2069 iCompletionReason=KErrGeneral; |
|
2070 } |
|
2071 |
|
2072 /* |
|
2073 Second phase construction of the requests |
|
2074 */ |
|
2075 TInt TSoundScPlayRequest::Construct() |
|
2076 { |
|
2077 return Kern::CreateClientRequest(iClientRequest); |
|
2078 } |
|
2079 |
|
2080 /* |
|
2081 Second phase construction of the requests |
|
2082 */ |
|
2083 TInt TSoundScRequest::Construct() |
|
2084 { |
|
2085 TClientDataRequest<TInt>* tempClientDataRequest=0; |
|
2086 TInt r = Kern::CreateClientDataRequest(tempClientDataRequest); |
|
2087 iClientRequest = tempClientDataRequest; |
|
2088 return r; |
|
2089 } |
|
2090 |
|
2091 /** |
|
2092 Destructor of play requests |
|
2093 */ |
|
2094 TSoundScRequest::~TSoundScRequest() |
|
2095 { |
|
2096 Kern::DestroyClientRequest(iClientRequest); |
|
2097 } |
|
2098 |
|
2099 /** |
|
2100 Constructor for the request object queue. |
|
2101 */ |
|
2102 TSoundScRequestQueue::TSoundScRequestQueue(DSoundScLdd* aLdd) |
|
2103 { |
|
2104 iLdd=aLdd; |
|
2105 memclr(&iRequest[0],sizeof(TSoundScRequest*)*KMaxSndScRequestsPending); |
|
2106 } |
|
2107 |
|
2108 /** |
|
2109 Destructor for the request object queue. |
|
2110 */ |
|
2111 TSoundScRequestQueue::~TSoundScRequestQueue() |
|
2112 { |
|
2113 for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++) |
|
2114 { |
|
2115 delete iRequest[i]; |
|
2116 } |
|
2117 } |
|
2118 |
|
2119 /** |
|
2120 Second stage constructor for the basic request object queue. |
|
2121 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2122 @pre The thread must be in a critical section. |
|
2123 */ |
|
2124 TInt TSoundScRequestQueue::Create() |
|
2125 { |
|
2126 // Create the set of available request objects and add them to the unused request queue. |
|
2127 for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++) |
|
2128 { |
|
2129 iRequest[i]=new TSoundScRequest; // Normal request object |
|
2130 if (!iRequest[i]) |
|
2131 return(KErrNoMemory); |
|
2132 TInt retConstruct = iRequest[i]->Construct(); |
|
2133 if ( retConstruct != KErrNone) |
|
2134 { |
|
2135 return(retConstruct); |
|
2136 } |
|
2137 iUnusedRequestQ.Add(iRequest[i]); |
|
2138 } |
|
2139 |
|
2140 return(KErrNone); |
|
2141 } |
|
2142 |
|
2143 /** |
|
2144 Second stage constructor for the play request object queue. |
|
2145 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2146 @pre The thread must be in a critical section. |
|
2147 */ |
|
2148 TInt TSoundScPlayRequestQueue::Create() |
|
2149 { |
|
2150 // Create the set of available play request objects and add them to the unused request queue. |
|
2151 for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++) |
|
2152 { |
|
2153 iRequest[i]=new TSoundScPlayRequest(); |
|
2154 if (!iRequest[i]) |
|
2155 return(KErrNoMemory); |
|
2156 TInt retConstruct = iRequest[i]->Construct(); |
|
2157 if ( retConstruct != KErrNone) |
|
2158 { |
|
2159 return(retConstruct); |
|
2160 } |
|
2161 iUnusedRequestQ.Add(iRequest[i]); |
|
2162 } |
|
2163 |
|
2164 return(KErrNone); |
|
2165 } |
|
2166 |
|
2167 /** |
|
2168 Get an unused request object. |
|
2169 @return A pointer to a free request object or NULL if there are none available. |
|
2170 */ |
|
2171 TSoundScRequest* TSoundScRequestQueue::NextFree() |
|
2172 { |
|
2173 NKern::FMWait(&iUnusedRequestQLock); |
|
2174 TSoundScRequest* req = (TSoundScRequest*)iUnusedRequestQ.GetFirst(); |
|
2175 NKern::FMSignal(&iUnusedRequestQLock); |
|
2176 return req; |
|
2177 } |
|
2178 |
|
2179 /** |
|
2180 Add a request object to the tail of the pending request queue. |
|
2181 @param aReq A pointer to the request object to be added to the queue. |
|
2182 */ |
|
2183 void TSoundScRequestQueue::Add(TSoundScRequest* aReq) |
|
2184 { |
|
2185 iPendRequestQ.Add(aReq); |
|
2186 } |
|
2187 |
|
2188 /** |
|
2189 If the pending request queue is not empty, remove the request object from the head of this queue. |
|
2190 @return A pointer to request object removed or NULL if the list was empty. |
|
2191 */ |
|
2192 TSoundScRequest* TSoundScRequestQueue::Remove() |
|
2193 { |
|
2194 return((TSoundScRequest*)iPendRequestQ.GetFirst()); |
|
2195 } |
|
2196 |
|
2197 /** |
|
2198 Remove a request object from anywhere in the pending request queue. |
|
2199 @param aReq A pointer to the request object to be removed from the queue. |
|
2200 @return A pointer to request object removed or NULL if it wasn't found. |
|
2201 */ |
|
2202 TSoundScRequest* TSoundScRequestQueue::Remove(TSoundScRequest* aReq) |
|
2203 { |
|
2204 TSoundScRequest* retReq; |
|
2205 |
|
2206 // Scan through the pending queue looking for a request object which matches. |
|
2207 retReq=(TSoundScRequest*)iPendRequestQ.First(); |
|
2208 while (!IsAnchor(retReq) && retReq!=aReq) |
|
2209 retReq=(TSoundScRequest*)retReq->iNext; |
|
2210 |
|
2211 // If we got a match then remove the request object from the queue and return it. |
|
2212 if (!IsAnchor(retReq)) |
|
2213 retReq->Deque(); |
|
2214 else |
|
2215 retReq=NULL; |
|
2216 return(retReq); |
|
2217 } |
|
2218 |
|
2219 /** |
|
2220 Free up a request object - making it available for further requests. |
|
2221 @param aReq A pointer to the request object being freed up. |
|
2222 */ |
|
2223 void TSoundScRequestQueue::Free(TSoundScRequest* aReq) |
|
2224 { |
|
2225 NKern::FMWait(&iUnusedRequestQLock); |
|
2226 iUnusedRequestQ.Add(aReq); |
|
2227 NKern::FMSignal(&iUnusedRequestQLock); |
|
2228 } |
|
2229 |
|
2230 /** |
|
2231 Find a request object (specified by its associated request status pointer) within in the pending request queue. |
|
2232 @param aStatus The request status pointer of the request object to be found in the queue. |
|
2233 @return A pointer to the request object if it was found or NULL if it wasn't found. |
|
2234 */ |
|
2235 TSoundScRequest* TSoundScRequestQueue::Find(TRequestStatus* aStatus) |
|
2236 { |
|
2237 TSoundScRequest* retReq; |
|
2238 |
|
2239 // Scan through the queue looking for a request object containing a TRequestStatus* which matches. |
|
2240 retReq=(TSoundScRequest*)iPendRequestQ.First(); |
|
2241 while (!IsAnchor(retReq) && retReq->iClientRequest->StatusPtr()!=aStatus) |
|
2242 retReq=(TSoundScRequest*)retReq->iNext; |
|
2243 |
|
2244 return((IsAnchor(retReq))?NULL:retReq); |
|
2245 } |
|
2246 |
|
2247 /** |
|
2248 Remove each request object from the pending request queue, completing each request removed with a specified completion |
|
2249 reason. |
|
2250 @param aCompletionReason The error value to be returned when completing any requests in the queue. |
|
2251 @param aMutex A pointer to a mutex to be aquired when removing requests from the queue. May be NULL. |
|
2252 */ |
|
2253 void TSoundScRequestQueue::CompleteAll(TInt aCompletionReason,NFastMutex* aMutex) |
|
2254 { |
|
2255 if (aMutex) |
|
2256 NKern::FMWait(aMutex); // Acquire the mutex. |
|
2257 |
|
2258 TSoundScRequest* req; |
|
2259 while ((req=Remove())!=NULL) |
|
2260 { |
|
2261 if (aMutex) |
|
2262 NKern::FMSignal(aMutex); // Release the mutex while we complete the request. |
|
2263 iLdd->CompleteRequest(req->iOwningThread,NULL,aCompletionReason,req->iClientRequest); |
|
2264 Free(req); |
|
2265 if (aMutex) |
|
2266 NKern::FMWait(aMutex); // Re-acquire the mutex. |
|
2267 |
|
2268 } |
|
2269 |
|
2270 if (aMutex) |
|
2271 NKern::FMSignal(aMutex); // Release mutex. |
|
2272 } |
|
2273 |
|
2274 /** |
|
2275 Constructor for the play request object queue. |
|
2276 */ |
|
2277 TSoundScPlayRequestQueue::TSoundScPlayRequestQueue(DSoundScLdd* aLdd) |
|
2278 : TSoundScRequestQueue(aLdd) |
|
2279 { |
|
2280 } |
|
2281 |
|
2282 /** |
|
2283 Return the play request object from the request queue which is next to be transferrred. If this |
|
2284 play request is being handled using multiple data transfers then the transfer of earlier parts of |
|
2285 this request may already be in progress. |
|
2286 @return Either a pointer to the next play request object for transfer, or NULL if no more are pending. |
|
2287 */ |
|
2288 TSoundScPlayRequest* TSoundScPlayRequestQueue::NextRequestForTransfer() |
|
2289 { |
|
2290 TSoundScPlayRequest* retReq; |
|
2291 |
|
2292 retReq=(TSoundScPlayRequest*)iPendRequestQ.First(); |
|
2293 while (!IsAnchor(retReq) && retReq->iTf.iTfState>TSndScTransfer::ETfPartlyStarted) |
|
2294 retReq=(TSoundScPlayRequest*)retReq->iNext; |
|
2295 |
|
2296 return((IsAnchor(retReq))?NULL:retReq); |
|
2297 } |
|
2298 |
|
2299 /** |
|
2300 Search the play request queue for a particular play request object specified by its transfer ID. |
|
2301 @param aTransferID The transfer ID of the particular play request object to be found. |
|
2302 @param aIsNextToComplete If the search is successful then this indicates whether the request |
|
2303 object found is the next in the queue to be completed to the client. ETrue if next to be |
|
2304 completed, EFalse otherwise. |
|
2305 @return Either a pointer to the specified request object, or NULL if it was not found. |
|
2306 */ |
|
2307 TSoundScPlayRequest* TSoundScPlayRequestQueue::Find(TUint aTransferID,TBool& aIsNextToComplete) |
|
2308 { |
|
2309 TSoundScPlayRequest* retReq; |
|
2310 TSoundScPlayRequest* nextToCompleteReq=NULL; |
|
2311 |
|
2312 retReq=(TSoundScPlayRequest*)iPendRequestQ.First(); |
|
2313 |
|
2314 // Walk all the way through the list either until we find the specified object or until we get to the end |
|
2315 for ( ; !IsAnchor(retReq) ; retReq=(TSoundScPlayRequest*)retReq->iNext ) |
|
2316 { |
|
2317 // The first request we find which isn't complete must be the next to complete |
|
2318 if (!nextToCompleteReq && retReq->iTf.iTfState!=TSndScTransfer::ETfDone) |
|
2319 nextToCompleteReq=retReq; |
|
2320 if (retReq->iTf.iId==aTransferID) |
|
2321 break; |
|
2322 } |
|
2323 |
|
2324 if (IsAnchor(retReq)) |
|
2325 return(NULL); // Object not found |
|
2326 else |
|
2327 { |
|
2328 aIsNextToComplete=(retReq==nextToCompleteReq); |
|
2329 return(retReq); // Object found |
|
2330 } |
|
2331 } |
|
2332 /** |
|
2333 Constructor for the audio data transfer class. |
|
2334 */ |
|
2335 TSndScTransfer::TSndScTransfer() |
|
2336 { |
|
2337 iId=0; |
|
2338 iTfState=ETfNotStarted; |
|
2339 iAudioBuffer=NULL; |
|
2340 iLengthTransferred=0; |
|
2341 iTransfersInProgress=0; |
|
2342 } |
|
2343 |
|
2344 /** |
|
2345 Initialisation function for the audio data transfer class. |
|
2346 @param aId A value to uniquely identify this particular transfer. |
|
2347 @param aChunkOffset The start postition of the transfer - an offset within the shared chunk. |
|
2348 @param aLength The total length of the transfer in bytes. |
|
2349 @param anAudioBuffer The audio buffer associated with the transfer. |
|
2350 */ |
|
2351 void TSndScTransfer::Init(TUint aId,TInt aChunkOffset,TInt aLength,TAudioBuffer* anAudioBuffer) |
|
2352 { |
|
2353 iId=aId; |
|
2354 iTfState=ETfNotStarted; |
|
2355 iAudioBuffer=anAudioBuffer; |
|
2356 iStartedOffset=aChunkOffset; |
|
2357 iEndOffset=aChunkOffset+aLength; |
|
2358 iLengthTransferred=0; |
|
2359 iTransfersInProgress=0; |
|
2360 } |
|
2361 |
|
2362 /** |
|
2363 Update the progress of the audio data transfer with the amount of data now queued for transfer on the audio device. |
|
2364 @param aLength The amount of data (in bytes) that has just been queued on the audio device. |
|
2365 */ |
|
2366 void TSndScTransfer::SetStarted(TInt aLength) |
|
2367 { |
|
2368 iTransfersInProgress++; |
|
2369 iStartedOffset+=aLength; |
|
2370 TInt notqueued=(iEndOffset - iStartedOffset); |
|
2371 __ASSERT_ALWAYS(notqueued>=0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
2372 iTfState=(notqueued) ? ETfPartlyStarted : ETfFullyStarted; |
|
2373 } |
|
2374 |
|
2375 /** |
|
2376 Update the progress of the audio data transfer with the amount of data now successfully transfered by the audio device. |
|
2377 @param aLength The amount of data (in bytes) that has just been transferred by the audio device. |
|
2378 @return ETrue if the transfer is now fully complete, otherwise EFalse. |
|
2379 */ |
|
2380 TBool TSndScTransfer::SetCompleted(TInt aLength) |
|
2381 { |
|
2382 iLengthTransferred+=aLength; |
|
2383 iTransfersInProgress--; |
|
2384 __ASSERT_ALWAYS(iTransfersInProgress>=0,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
2385 |
|
2386 if (GetNotStartedLen()==0 && iTransfersInProgress==0) |
|
2387 { |
|
2388 iTfState=ETfDone; // Transfer is now fully completed |
|
2389 return(ETrue); |
|
2390 } |
|
2391 else |
|
2392 return(EFalse); |
|
2393 } |
|
2394 |
|
2395 /** |
|
2396 Constructor for the sound driver power handler class. |
|
2397 @param aChannel A pointer to the sound driver logical channel which owns this power handler. |
|
2398 */ |
|
2399 DSoundScPowerHandler::DSoundScPowerHandler(DSoundScLdd* aChannel) |
|
2400 : DPowerHandler(KDevSoundScName), |
|
2401 iChannel(aChannel) |
|
2402 { |
|
2403 } |
|
2404 |
|
2405 /** |
|
2406 A request from the power manager for the power down of the audio device. |
|
2407 This is called during a transition of the phone into standby or power off. |
|
2408 @param aState The target power state; can be EPwStandby or EPwOff only. |
|
2409 */ |
|
2410 void DSoundScPowerHandler::PowerDown(TPowerState aPowerState) |
|
2411 { |
|
2412 (void)aPowerState; |
|
2413 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerDown(State-%d)",aPowerState)); |
|
2414 |
|
2415 // Power-down involves hardware access so queue a DFC to perform this from the driver thread. |
|
2416 iChannel->iPowerDownDfc.Enque(); |
|
2417 } |
|
2418 |
|
2419 /** |
|
2420 A request from the power manager for the power up of the audio device. |
|
2421 This is called during a transition of the phone out of standby. |
|
2422 */ |
|
2423 void DSoundScPowerHandler::PowerUp() |
|
2424 { |
|
2425 __KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerUp")); |
|
2426 |
|
2427 // Power-up involves hardware access so queue a DFC to perform this from the driver thread. |
|
2428 iChannel->iPowerUpDfc.Enque(); |
|
2429 } |
|
2430 |
|
2431 /** |
|
2432 Constructor for the buffer manager. |
|
2433 */ |
|
2434 DBufferManager::DBufferManager(DSoundScLdd* aLdd) |
|
2435 : iLdd(aLdd) |
|
2436 { |
|
2437 // iChunk=NULL; |
|
2438 // iNumBuffers=0; |
|
2439 // iAudioBuffers=NULL; |
|
2440 // iMaxTransferLen=0; |
|
2441 } |
|
2442 |
|
2443 /** |
|
2444 Destructor for the buffer manager. |
|
2445 @pre The thread must be in a critical section. |
|
2446 */ |
|
2447 DBufferManager::~DBufferManager() |
|
2448 { |
|
2449 if (iChunk) |
|
2450 Kern::ChunkClose(iChunk); |
|
2451 delete[] iAudioBuffers; |
|
2452 } |
|
2453 |
|
2454 /** |
|
2455 Second stage constructor for the buffer manager. This version creates a shared chunk and a buffer object for each |
|
2456 buffer specified within this. Then it commits memory within the chunk for each of these buffers. This also involves the |
|
2457 creation of a set of buffer lists to manage the buffers. |
|
2458 @param aBufConfig The shared chunk buffer configuration object specifying the geometry of the buffer configuration |
|
2459 required. |
|
2460 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2461 @pre The thread must be in a critical section. |
|
2462 */ |
|
2463 TInt DBufferManager::Create(TSoundSharedChunkBufConfig* aBufConfig) |
|
2464 { |
|
2465 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Create(Bufs-%d,Sz-%d)",aBufConfig->iNumBuffers,aBufConfig->iBufferSizeInBytes)); |
|
2466 |
|
2467 // Create the required number of buffer objects, and the buffer lists to manage these. |
|
2468 TInt r=CreateBufferLists(aBufConfig->iNumBuffers); |
|
2469 if (r!=KErrNone) |
|
2470 return(r); |
|
2471 |
|
2472 TInt chunkSz; |
|
2473 TInt bufferSz=aBufConfig->iBufferSizeInBytes; |
|
2474 TInt* bufferOffsetList=&aBufConfig->iBufferOffsetListStart; |
|
2475 |
|
2476 // Calculate the required size for the chunk and the buffer offsets. |
|
2477 if (aBufConfig->iFlags & KScFlagUseGuardPages) |
|
2478 { |
|
2479 // Commit each buffer separately with an uncommitted guard pages around each buffer. |
|
2480 TInt guardPageSize=Kern::RoundToPageSize(1); |
|
2481 bufferSz=Kern::RoundToPageSize(aBufConfig->iBufferSizeInBytes); // Commit size to be a multiple of the MMU page size. |
|
2482 chunkSz=guardPageSize; // Leave an un-committed guard page at the start. |
|
2483 for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++) |
|
2484 { |
|
2485 bufferOffsetList[i]=chunkSz; |
|
2486 chunkSz += (bufferSz + guardPageSize); // Leave an un-committed guard page after each buffer. |
|
2487 } |
|
2488 } |
|
2489 else |
|
2490 { |
|
2491 // Commit all the buffers contiguously into a single region (ie with no guard pages between each buffer). |
|
2492 chunkSz=0; |
|
2493 for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++) |
|
2494 { |
|
2495 bufferOffsetList[i]=chunkSz; |
|
2496 chunkSz += bufferSz; |
|
2497 } |
|
2498 chunkSz=Kern::RoundToPageSize(chunkSz); // Commit size to be a multiple of the MMU page size. |
|
2499 } |
|
2500 aBufConfig->iFlags|=KScFlagBufOffsetListInUse; |
|
2501 __KTRACE_OPT(KSOUND1, Kern::Printf("Chunk size is %d bytes",chunkSz)); |
|
2502 |
|
2503 // Create the shared chunk. The PDD supplies most of the chunk create info - but not the maximum size. |
|
2504 TChunkCreateInfo info; |
|
2505 info.iMaxSize=chunkSz; |
|
2506 iLdd->Pdd()->GetChunkCreateInfo(info); // Call down to the PDD for the rest. |
|
2507 |
|
2508 r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr); |
|
2509 __KTRACE_OPT(KSOUND1, Kern::Printf("Create chunk - %d",r)); |
|
2510 if (r!=KErrNone) |
|
2511 return(r); |
|
2512 |
|
2513 if (aBufConfig->iFlags & KScFlagUseGuardPages) |
|
2514 { |
|
2515 // Map each of the buffers into the chunk separately - try to allocate physically contiguous RAM pages. Create a buffer object for each buffer. |
|
2516 TBool isContiguous; |
|
2517 for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++) |
|
2518 { |
|
2519 r=CommitMemoryForBuffer(bufferOffsetList[i],bufferSz,isContiguous); |
|
2520 if (r!=KErrNone) |
|
2521 return(r); |
|
2522 r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this); |
|
2523 if (r!=KErrNone) |
|
2524 return(r); |
|
2525 } |
|
2526 } |
|
2527 else |
|
2528 { |
|
2529 // Map memory for the all buffers into the chunk - try to allocate physically contiguous RAM pages. |
|
2530 TBool isContiguous; |
|
2531 r=CommitMemoryForBuffer(0,chunkSz,isContiguous); |
|
2532 if (r!=KErrNone) |
|
2533 return(r); |
|
2534 |
|
2535 // Create a buffer object for each buffer. |
|
2536 for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++) |
|
2537 { |
|
2538 r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this); |
|
2539 if (r!=KErrNone) |
|
2540 return(r); |
|
2541 } |
|
2542 } |
|
2543 |
|
2544 // Read back and store the maximum transfer length supported by this device from the PDD. |
|
2545 iMaxTransferLen=iLdd->Pdd()->MaxTransferLen(); |
|
2546 |
|
2547 __KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::Create - %d",r)); |
|
2548 return(r); |
|
2549 } |
|
2550 |
|
2551 /** |
|
2552 Second stage constructor for the buffer manager. This version opens an existing shared chunk using a client supplied |
|
2553 handle. It then creates a buffer object for each buffer that exists within the chunk as well as creating a set of buffer |
|
2554 lists to manage the buffers. |
|
2555 @param aBufConfig The shared chunk buffer configuration object - specifying the geometry of the buffer configuration |
|
2556 within the shared chunk supplied. |
|
2557 @param aChunkHandle A handle for the shared chunk supplied by the client. |
|
2558 @param anOwningThread The thread in which the given handle is valid. |
|
2559 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2560 @pre The thread must be in a critical section. |
|
2561 */ |
|
2562 TInt DBufferManager::Create(TSoundSharedChunkBufConfig& aBufConfig,TInt aChunkHandle,DThread* anOwningThread) |
|
2563 { |
|
2564 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Create(Handle-%d)",aChunkHandle)); |
|
2565 |
|
2566 // Validate the buffer configuration information. |
|
2567 if (!aBufConfig.iFlags&KScFlagBufOffsetListInUse) |
|
2568 return(KErrArgument); |
|
2569 TInt numBuffers=aBufConfig.iNumBuffers; |
|
2570 TInt bufferSizeInBytes=aBufConfig.iBufferSizeInBytes; |
|
2571 TInt* bufferOffsetList=&aBufConfig.iBufferOffsetListStart; |
|
2572 TInt r=ValidateBufferOffsets(bufferOffsetList,numBuffers,bufferSizeInBytes); |
|
2573 if (r<0) |
|
2574 return(r); |
|
2575 |
|
2576 // Create the required number of buffer objects, and the buffer lists to manage these. |
|
2577 r=CreateBufferLists(numBuffers); |
|
2578 if (r!=KErrNone) |
|
2579 return(r); |
|
2580 |
|
2581 // Open the shared chunk. |
|
2582 DChunk* chunk; |
|
2583 chunk=Kern::OpenSharedChunk(anOwningThread,aChunkHandle,ETrue); |
|
2584 if (!chunk) |
|
2585 return(KErrBadHandle); |
|
2586 iChunk=chunk; |
|
2587 |
|
2588 // Read the physical address for the 1st buffer in order to determine the kernel address and the map attributes. |
|
2589 TInt offset=bufferOffsetList[0]; |
|
2590 TPhysAddr physAddr; |
|
2591 TLinAddr kernelAddress; |
|
2592 r=Kern::ChunkPhysicalAddress(iChunk,offset,bufferSizeInBytes,kernelAddress,iChunkMapAttr,physAddr,NULL); |
|
2593 if (r!=KErrNone) |
|
2594 return(r); |
|
2595 iChunkBase=(kernelAddress-offset); |
|
2596 |
|
2597 // For each buffer, validate that the buffer specified contains committed memory and store the buffer info. into each buffer object. |
|
2598 while (numBuffers) |
|
2599 { |
|
2600 numBuffers--; |
|
2601 offset=bufferOffsetList[numBuffers]; |
|
2602 // Assume it isn't contiguous here - Create() will detect and do the right thing if it is contiguous. |
|
2603 r=iAudioBuffers[numBuffers].Create(iChunk,offset,bufferSizeInBytes,EFalse,this); |
|
2604 if (r!=KErrNone) |
|
2605 return(r); |
|
2606 } |
|
2607 |
|
2608 // Read back and store the maximum transfer length supported by this device from the PDD. |
|
2609 iMaxTransferLen=iLdd->Pdd()->MaxTransferLen(); |
|
2610 |
|
2611 __KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::Create - %d",KErrNone)); |
|
2612 return(KErrNone); |
|
2613 } |
|
2614 |
|
2615 /** |
|
2616 Allocate an array of buffer objects, - one for each buffer contained within the shared chunk. |
|
2617 @param aNumBuffers The number of buffer objects required. |
|
2618 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2619 @pre The thread must be in a critical section. |
|
2620 */ |
|
2621 TInt DBufferManager::CreateBufferLists(TInt aNumBuffers) |
|
2622 { |
|
2623 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::CreateBufferLists(Bufs-%d)",aNumBuffers)); |
|
2624 |
|
2625 // Construct the array of buffers. |
|
2626 iNumBuffers=aNumBuffers; |
|
2627 iAudioBuffers=new TAudioBuffer[aNumBuffers]; |
|
2628 if (!iAudioBuffers) |
|
2629 return(KErrNoMemory); |
|
2630 |
|
2631 return(KErrNone); |
|
2632 } |
|
2633 |
|
2634 /** |
|
2635 Validate a shared chunk buffer offset list. |
|
2636 @param aBufferOffsetList The buffer offset list to be validated. |
|
2637 @param aNumBuffers The number of offsets that the list contains. |
|
2638 @param aBufferSizeInBytes The size in bytes of each buffer. |
|
2639 @return If the buffer list is found to be valid, the calculated minimum size of the corresponding chunk is returned |
|
2640 (i.e. a value>=0). Otherwise, KErrArgument is returned. |
|
2641 */ |
|
2642 TInt DBufferManager::ValidateBufferOffsets(TInt* aBufferOffsetList,TInt aNumBuffers,TInt aBufferSizeInBytes) |
|
2643 { |
|
2644 TUint32 alignmask=(1<<iLdd->iCaps.iRequestAlignment)-1; // iRequestAlignment holds log to base 2 of alignment required |
|
2645 |
|
2646 // Verify each of the buffer offsets supplied |
|
2647 TInt offset=0; |
|
2648 for (TInt i=0 ; i<aNumBuffers ; i++) |
|
2649 { |
|
2650 // If this is a record channel then the offset must comply with the PDD alignment constraints. |
|
2651 if (iLdd->iDirection==ESoundDirRecord && ((TUint32)aBufferOffsetList[i] & alignmask) != 0) |
|
2652 return(KErrArgument); |
|
2653 |
|
2654 // Check the offset doesn't overlap the previous buffer - offset holds the offset to next byte after |
|
2655 // the previous buffer. |
|
2656 if (aBufferOffsetList[i]<offset) |
|
2657 return(KErrArgument); |
|
2658 |
|
2659 offset=(aBufferOffsetList[i]+aBufferSizeInBytes); |
|
2660 } |
|
2661 return(offset); |
|
2662 } |
|
2663 |
|
2664 /** |
|
2665 Verify that a specified region of the shared chunk (specified by its offset and length) is valid within the |
|
2666 chunk and corresponds to a region of committed memory. |
|
2667 @param aChunkOffset Offset of the region from the beginning of the chunk. |
|
2668 @param aLength The length in bytes of the region. |
|
2669 @param anAudioBuffer A reference to a pointer to an audio buffer object. On return this will either contain a pointer |
|
2670 to the audio buffer object which corresonds to the specified region, or NULL if the region is invalid. |
|
2671 @return KErrNone if the region is valid, otherwise KErrArgument. |
|
2672 */ |
|
2673 TInt DBufferManager::ValidateRegion(TUint aChunkOffset,TUint aLength,TAudioBuffer*& anAudioBuffer) |
|
2674 { |
|
2675 |
|
2676 TUint bufStart; |
|
2677 TUint bufEnd; |
|
2678 TUint regEnd=(aChunkOffset+aLength); |
|
2679 for (TInt i=0 ; i<iNumBuffers ; i++) |
|
2680 { |
|
2681 bufStart=iAudioBuffers[i].iChunkOffset; |
|
2682 bufEnd=iAudioBuffers[i].iChunkOffset+iAudioBuffers[i].iSize; |
|
2683 if (aChunkOffset<bufStart || aChunkOffset>=bufEnd) |
|
2684 continue; |
|
2685 if (regEnd<=bufEnd) |
|
2686 { |
|
2687 anAudioBuffer=&iAudioBuffers[i]; |
|
2688 return(KErrNone); |
|
2689 } |
|
2690 } |
|
2691 return(KErrArgument); |
|
2692 } |
|
2693 |
|
2694 /** |
|
2695 Commit memory for a single buffer within the shared chunk. |
|
2696 @param aChunkOffset The offset (in bytes) from start of chunk, which indicates the start of the memory region to be |
|
2697 committed. Must be a multiple of the MMU page size. |
|
2698 @param aSize The number of bytes to commit. Must be a multiple of the MMU page size. |
|
2699 @param aIsContiguous On return, this is set to ETrue if the function succeeded in committing physically contiguous memory; |
|
2700 EFalse if the committed memory is not contiguous. |
|
2701 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2702 */ |
|
2703 TInt DBufferManager::CommitMemoryForBuffer(TInt aChunkOffset,TInt aSize,TBool& aIsContiguous) |
|
2704 { |
|
2705 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::CommitMemoryForBuffer(Offset-%x,Sz-%d)",aChunkOffset,aSize)); |
|
2706 |
|
2707 // Try for physically contiguous memory first. |
|
2708 TInt r; |
|
2709 TPhysAddr physicalAddress; |
|
2710 r=Kern::ChunkCommitContiguous(iChunk,aChunkOffset,aSize,physicalAddress); |
|
2711 if (r==KErrNone) |
|
2712 { |
|
2713 aIsContiguous=ETrue; |
|
2714 return(r); |
|
2715 } |
|
2716 |
|
2717 // Try to commit memory that isn't contiguous instead. |
|
2718 aIsContiguous=EFalse; |
|
2719 r=Kern::ChunkCommit(iChunk,aChunkOffset,aSize); |
|
2720 return(r); |
|
2721 } |
|
2722 |
|
2723 /** |
|
2724 Purge a region of the audio chunk. That is, if this region contains cacheable memory, flush it. |
|
2725 @param aChunkOffset The offset within the chunk for the start of the data to be flushed. |
|
2726 @param aLength The length in bytes of the region to be flushed. |
|
2727 @param aFlushOp The type of flush operation required - @see TFlushOp. |
|
2728 */ |
|
2729 void DBufferManager::FlushData(TInt aChunkOffset,TInt aLength,TFlushOp aFlushOp) |
|
2730 { |
|
2731 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::FlushData(%d)",aFlushOp)); |
|
2732 TLinAddr dataAddr=(iChunkBase+aChunkOffset); |
|
2733 switch (aFlushOp) |
|
2734 { |
|
2735 case EFlushBeforeDmaWrite: |
|
2736 Cache::SyncMemoryBeforeDmaWrite(dataAddr,aLength,iChunkMapAttr); |
|
2737 break; |
|
2738 case EFlushBeforeDmaRead: |
|
2739 Cache::SyncMemoryBeforeDmaRead(dataAddr,aLength,iChunkMapAttr); |
|
2740 break; |
|
2741 case EFlushAfterDmaRead: |
|
2742 Cache::SyncMemoryAfterDmaRead(dataAddr,aLength); |
|
2743 break; |
|
2744 default: |
|
2745 break; |
|
2746 } |
|
2747 } |
|
2748 |
|
2749 /** |
|
2750 Constructor for the record buffer manager. |
|
2751 */ |
|
2752 DRecordBufferManager::DRecordBufferManager(DSoundScLdd* aLdd) |
|
2753 : DBufferManager(aLdd) |
|
2754 { |
|
2755 } |
|
2756 |
|
2757 /** |
|
2758 Reset all the audio buffer lists to reflect the state at the start of the record capture process. |
|
2759 @pre The buffer/request queue mutex must be held. |
|
2760 */ |
|
2761 void DRecordBufferManager::Reset() |
|
2762 { |
|
2763 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Reset")); |
|
2764 TAudioBuffer* pBuf; |
|
2765 |
|
2766 // Before reseting buffer lists, purge the cache for all cached buffers currently in use by client. |
|
2767 pBuf=(TAudioBuffer*)iInUseBufferQ.First(); |
|
2768 SDblQueLink* anchor=&iInUseBufferQ.iA; |
|
2769 while (pBuf!=anchor) |
|
2770 { |
|
2771 pBuf->Flush(DBufferManager::EFlushBeforeDmaRead); |
|
2772 pBuf=(TAudioBuffer*)pBuf->iNext; |
|
2773 } |
|
2774 |
|
2775 // Start by reseting all the lists. |
|
2776 iFreeBufferQ.iA.iNext=iFreeBufferQ.iA.iPrev=&iFreeBufferQ.iA; |
|
2777 iCompletedBufferQ.iA.iNext=iCompletedBufferQ.iA.iPrev=&iCompletedBufferQ.iA; |
|
2778 iInUseBufferQ.iA.iNext=iInUseBufferQ.iA.iPrev=&iInUseBufferQ.iA; |
|
2779 |
|
2780 // Set the pointers to the current and the next record buffers. |
|
2781 pBuf=iAudioBuffers; // This is the first buffer |
|
2782 iCurrentBuffer=pBuf++; |
|
2783 iNextBuffer = pBuf++; |
|
2784 |
|
2785 // Add all other buffers to the free list. |
|
2786 TAudioBuffer* bufferLimit=iAudioBuffers+iNumBuffers; |
|
2787 while(pBuf<bufferLimit) |
|
2788 iFreeBufferQ.Add(pBuf++); |
|
2789 |
|
2790 iBufOverflow=EFalse; |
|
2791 } |
|
2792 |
|
2793 /** |
|
2794 Update buffer lists after a record buffer has been filled. |
|
2795 @param aBytesAdded The number of bytes added to the buffer to get it into the 'filled' state. Of course, this is |
|
2796 normally equal to the size of the buffer. The exception is when recording has been paused: in which |
|
2797 case the number of bytes added to 'fill' the buffer may be less than the buffer size. |
|
2798 @param aTransferResult The result of the transfer. |
|
2799 @return A pointer to the next buffer for recording. |
|
2800 @pre The buffer/request queue mutex must be held. |
|
2801 */ |
|
2802 TAudioBuffer* DRecordBufferManager::SetBufferFilled(TInt aBytesAdded,TInt aTransferResult) |
|
2803 { |
|
2804 // If record has been paused then its possible (depending on the PDD implementation) that although the current |
|
2805 // buffer is marked as being filled, no data has been added. If this is the case then there is no point in informing |
|
2806 // the client about it. Instead we need to return it to the free list. Otherwise the more normal course of action is |
|
2807 // to add the current buffer to the completed list ready for the client. If there is any amount of data in the record |
|
2808 // buffer, this needs to be passed to the client (and if we're not paused then each buffer should actually be full). |
|
2809 // If an error occured then we always add the buffer to the completed list. |
|
2810 TAudioBuffer* buffer=iCurrentBuffer; |
|
2811 if (aBytesAdded || aTransferResult) |
|
2812 { |
|
2813 buffer->iBytesAdded=aBytesAdded; |
|
2814 buffer->iResult=aTransferResult; |
|
2815 iCompletedBufferQ.Add(buffer); |
|
2816 } |
|
2817 else |
|
2818 iFreeBufferQ.Add(buffer); |
|
2819 |
|
2820 // Make the pending buffer the current one. |
|
2821 iCurrentBuffer=iNextBuffer; |
|
2822 |
|
2823 // Obtain the next pending buffer. If there are none left on the free list then we have to take one back |
|
2824 // from the completed list. |
|
2825 iNextBuffer=(TAudioBuffer*)iFreeBufferQ.GetFirst(); |
|
2826 if (!iNextBuffer) |
|
2827 { |
|
2828 iNextBuffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst(); |
|
2829 iBufOverflow=ETrue; // This is the buffer overflow situation. |
|
2830 } |
|
2831 __ASSERT_DEBUG(iNextBuffer,Kern::Fault(KSoundLddPanic,__LINE__)); |
|
2832 iNextBuffer->iBytesAdded=0; |
|
2833 |
|
2834 __KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::SetBufferFilled(buf=%08x len=%d)",buffer->iChunkOffset,buffer->iBytesAdded)); |
|
2835 return(iNextBuffer); |
|
2836 } |
|
2837 |
|
2838 /** |
|
2839 Get the next record buffer from the completed buffer list. If there is no error associated with the buffer, |
|
2840 make it 'in use' by the client. Otherwise, return the buffer to the free list. |
|
2841 @return A pointer to the next completed buffer or NULL if there isn't one available. |
|
2842 @pre The buffer/request queue mutex must be held. |
|
2843 */ |
|
2844 TAudioBuffer* DRecordBufferManager::GetBufferForClient() |
|
2845 { |
|
2846 TAudioBuffer* buffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst(); |
|
2847 if (buffer) |
|
2848 { |
|
2849 if (buffer->iResult==KErrNone) |
|
2850 iInUseBufferQ.Add(buffer); |
|
2851 else |
|
2852 iFreeBufferQ.Add(buffer); |
|
2853 } |
|
2854 |
|
2855 __KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::BufferForClient(buf=%08x)",(buffer ? buffer->iChunkOffset : -1))); |
|
2856 return(buffer); |
|
2857 } |
|
2858 |
|
2859 /** |
|
2860 Release (move to free list) the 'in use' record buffer specified by the given chunk offset. |
|
2861 @param aChunkOffset The chunk offset corresponding to the buffer to be freed. |
|
2862 @return The freed buffer, or NULL if no 'in use' buffer had the specified chunk offset. |
|
2863 @pre The buffer/request queue mutex must be held. |
|
2864 */ |
|
2865 TAudioBuffer* DRecordBufferManager::ReleaseBuffer(TInt aChunkOffset) |
|
2866 { |
|
2867 // Scan 'in use' list for the audio buffer |
|
2868 TAudioBuffer* pBuf; |
|
2869 pBuf=(TAudioBuffer*)iInUseBufferQ.First(); |
|
2870 SDblQueLink* anchor=&iInUseBufferQ.iA; |
|
2871 while (pBuf!=anchor && pBuf->iChunkOffset!=aChunkOffset) |
|
2872 pBuf=(TAudioBuffer*)pBuf->iNext; |
|
2873 |
|
2874 // Move buffer to the free list (if found) |
|
2875 if (pBuf!=anchor) |
|
2876 iFreeBufferQ.Add(pBuf->Deque()); |
|
2877 else |
|
2878 pBuf=NULL; |
|
2879 |
|
2880 __KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::BufferRelease(buf=%08x)",(pBuf ? pBuf->iChunkOffset : -1))); |
|
2881 return(pBuf); |
|
2882 } |
|
2883 |
|
2884 /** |
|
2885 Constructor for the audio buffer class. |
|
2886 Clears all member data |
|
2887 */ |
|
2888 TAudioBuffer::TAudioBuffer() |
|
2889 { |
|
2890 memclr(this,sizeof(*this)); |
|
2891 } |
|
2892 |
|
2893 /** |
|
2894 Destructor for the audio buffer class. |
|
2895 */ |
|
2896 TAudioBuffer::~TAudioBuffer() |
|
2897 { |
|
2898 delete[] iPhysicalPages; |
|
2899 } |
|
2900 |
|
2901 /** |
|
2902 Second stage constructor for the audio buffer class - validate and acquire information on the memory |
|
2903 allocated to this buffer. |
|
2904 @param aChunk The chunk in which this buffer belongs. |
|
2905 @param aChunkOffset The offset within aChunk to the start of the audio buffer. |
|
2906 @param aSize The size (in bytes) of the buffer. |
|
2907 @param aIsContiguous A boolean indicating whether the buffer contains physically contiguous memory. Set to ETrue if it |
|
2908 does physically contiguous memory, EFalse otherwise. |
|
2909 @param aBufManager A pointer to the buffer manager which owns this object. |
|
2910 @return KErrNone if successful, otherwise one of the other system wide error codes. |
|
2911 @pre The thread must be in a critical section. |
|
2912 */ |
|
2913 TInt TAudioBuffer::Create(DChunk* aChunk,TInt aChunkOffset,TInt aSize,TBool aIsContiguous,DBufferManager* aBufManager) |
|
2914 { |
|
2915 __KTRACE_OPT(KSOUND1, Kern::Printf(">TAudioBuffer::Create(Off-%x,Sz-%d,Contig-%d)",aChunkOffset,aSize,aIsContiguous)); |
|
2916 |
|
2917 // Save info. on the offset and size of the buffer. Also the buffer manager. |
|
2918 iChunkOffset=aChunkOffset; |
|
2919 iSize=aSize; |
|
2920 iBufManager=aBufManager; |
|
2921 |
|
2922 TInt r=KErrNone; |
|
2923 iPhysicalPages=NULL; |
|
2924 if (!aIsContiguous) |
|
2925 { |
|
2926 // Allocate an array for a list of the physical pages. |
|
2927 iPhysicalPages = new TPhysAddr[aSize/Kern::RoundToPageSize(1)+2]; |
|
2928 if (!iPhysicalPages) |
|
2929 r=KErrNoMemory; |
|
2930 } |
|
2931 |
|
2932 if (r==KErrNone) |
|
2933 { |
|
2934 // Check that the region of the chunk specified for the buffer contains committed memory. If so, get the physical addresses of the |
|
2935 // pages in this buffer. |
|
2936 TUint32 kernAddr; |
|
2937 TUint32 mapAttr; |
|
2938 r=Kern::ChunkPhysicalAddress(aChunk,aChunkOffset,aSize,kernAddr,mapAttr,iPhysicalAddress,iPhysicalPages); |
|
2939 // r = 0 or 1 on success. (1 meaning the physical pages are not contiguous). |
|
2940 if (r==1) |
|
2941 { |
|
2942 // The physical pages are not contiguous. |
|
2943 iPhysicalAddress=KPhysAddrInvalid; // Mark the physical address as invalid. |
|
2944 r=(aIsContiguous) ? KErrGeneral : KErrNone; |
|
2945 } |
|
2946 if (r==0) |
|
2947 { |
|
2948 delete[] iPhysicalPages; // We shouldn't retain this info. if the physical pages are contiguous. |
|
2949 iPhysicalPages=NULL; |
|
2950 } |
|
2951 |
|
2952 } |
|
2953 __KTRACE_OPT(KSOUND1, Kern::Printf("<TAudioBuffer::Create - %d",r)); |
|
2954 return(r); |
|
2955 } |
|
2956 |
|
2957 /** |
|
2958 Calculate the length for the next part of a data transfer request so that that the length returned specifies a physically |
|
2959 contiguous region and is also valid for the PDD. If necessary, return a truncated length that meets these criteria. Also, |
|
2960 return the physical address of the start of the region specified. |
|
2961 @param aChunkOffset The offset within the chunk for the start of the data transfer being fragmented. |
|
2962 @param aLengthRemaining The remaining length of the data transfer request. |
|
2963 @param aPhysAddr On return, this contains the physical address corresonding to aChunkOffset. |
|
2964 @return The length calculated. |
|
2965 */ |
|
2966 TInt TAudioBuffer::GetFragmentLength(TInt aChunkOffset,TInt aLengthRemaining,TPhysAddr& aPhysAddr) |
|
2967 { |
|
2968 TInt len; |
|
2969 TInt bufOffset=(aChunkOffset - iChunkOffset); // Convert from chunk offset to buffer offset. |
|
2970 |
|
2971 if (iPhysicalAddress==KPhysAddrInvalid) |
|
2972 { |
|
2973 // Buffer is not physically contiguous. Truncate length to the next page boundary. Then calculate physical addr. |
|
2974 // (This function doesn't look for pages which are contiguous within the physical page list - but it could). |
|
2975 TInt pageSize=Kern::RoundToPageSize(1); |
|
2976 TInt pageOffset=bufOffset%pageSize; |
|
2977 len=pageSize - pageOffset; |
|
2978 aPhysAddr=iPhysicalPages[bufOffset/pageSize] + pageOffset; |
|
2979 } |
|
2980 else |
|
2981 { |
|
2982 // Buffer is physically contiguous so no need to truncate the length. Then calculate physical address. |
|
2983 len=aLengthRemaining; |
|
2984 aPhysAddr=iPhysicalAddress + bufOffset; |
|
2985 } |
|
2986 |
|
2987 // Ensure length does not exceed the max. supported by the PDD. |
|
2988 len=Min(iBufManager->iMaxTransferLen,len); |
|
2989 return(len); |
|
2990 } |
|
2991 |
|
2992 /** |
|
2993 Purge the entire audio buffer. That is, if it contains cacheable memory, flush it. |
|
2994 @param aFlushOp The type of flush operation required - @see DBufferManager::TFlushOp. |
|
2995 */ |
|
2996 void TAudioBuffer::Flush(DBufferManager::TFlushOp aFlushOp) |
|
2997 { |
|
2998 iBufManager->FlushData(iChunkOffset,iSize,aFlushOp); |
|
2999 } |
|
3000 |