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