|
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 "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 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @publishedPartner |
|
19 */ |
|
20 |
|
21 #include <mtp/cmtptypefile.h> |
|
22 #include <mtp/mtpdatatypeconstants.h> |
|
23 |
|
24 // File type constants. |
|
25 const TInt KMTPFileChunkSizeForLargeFile(0x00080000); // 512K |
|
26 |
|
27 //For file size less than 512K, we will use this smaller chunk size to reduce the heap usage. |
|
28 const TInt KMTPFileChunkSizeForSmallFile(0x00010000); //64K |
|
29 |
|
30 //For file size larger than it, we will split one setSize() to several smaller one, each with the following size. |
|
31 const TInt64 KMTPFileSetSizeChunk(1<<30); //1G |
|
32 |
|
33 const TUint KUSBHeaderLen = 12; |
|
34 |
|
35 /** |
|
36 MTP file object data type factory method. |
|
37 @param aFs The handle of an active file server session. |
|
38 @param aName The name of the file. Any path components (i.e. drive letter |
|
39 or directory), which are not specified, are taken from the session path. |
|
40 @param aMode The mode in which the file is opened (@see TFileMode). |
|
41 @return A pointer to the MTP file object data type. Ownership IS transfered. |
|
42 @leave One of the system wide error codes, if a processing failure occurs. |
|
43 @see TFileMode |
|
44 */ |
|
45 EXPORT_C CMTPTypeFile* CMTPTypeFile::NewL(RFs& aFs, const TDesC& aName, TFileMode aMode) |
|
46 { |
|
47 CMTPTypeFile* self = NewLC(aFs, aName, aMode); |
|
48 CleanupStack::Pop(self); |
|
49 return self; |
|
50 } |
|
51 |
|
52 /** |
|
53 MTP file object data type factory method. A pointer to the MTP file object data |
|
54 type is placed on the cleanup stack. |
|
55 @param aFs The handle of an active file server session. |
|
56 @param aName The name of the file. Any path components (i.e. drive letter |
|
57 or directory), which are not specified, are taken from the session path. |
|
58 @param aMode The mode in which the file is opened (@see TFileMode). |
|
59 @return A pointer to the MTP file object data type. Ownership IS transfered. |
|
60 @leave One of the system wide error codes, if a processing failure occurs. |
|
61 @see TFileMode |
|
62 */ |
|
63 EXPORT_C CMTPTypeFile* CMTPTypeFile::NewLC(RFs& aFs, const TDesC& aName, TFileMode aMode) |
|
64 { |
|
65 CMTPTypeFile* self = new(ELeave) CMTPTypeFile; |
|
66 CleanupStack::PushL(self); |
|
67 self->ConstructL(aFs, aName, aMode); |
|
68 return self; |
|
69 } |
|
70 |
|
71 EXPORT_C CMTPTypeFile* CMTPTypeFile::NewL(RFs& aFs, const TDesC& aName, TFileMode aMode, TInt64 aRequiredSize, TInt64 aOffSet) |
|
72 { |
|
73 CMTPTypeFile* self = NewLC(aFs, aName, aMode,aRequiredSize,aOffSet); |
|
74 CleanupStack::Pop(self); |
|
75 return self; |
|
76 } |
|
77 |
|
78 EXPORT_C CMTPTypeFile* CMTPTypeFile::NewLC(RFs& aFs, const TDesC& aName, TFileMode aMode, TInt64 aRequiredSize, TInt64 aOffSet) |
|
79 { |
|
80 CMTPTypeFile* self = new(ELeave) CMTPTypeFile; |
|
81 CleanupStack::PushL(self); |
|
82 self->ConstructL(aFs, aName, aMode, aRequiredSize, aOffSet); |
|
83 return self; |
|
84 } |
|
85 |
|
86 /** |
|
87 Destructor |
|
88 */ |
|
89 EXPORT_C CMTPTypeFile::~CMTPTypeFile() |
|
90 { |
|
91 if(iCurrentCommitChunk.Length() != 0) |
|
92 { |
|
93 ToggleRdWrBuffer(); |
|
94 } |
|
95 |
|
96 iFile.Close(); |
|
97 |
|
98 iBuffer1.Close(); |
|
99 iBuffer2.Close(); |
|
100 Cancel(); |
|
101 } |
|
102 |
|
103 /** |
|
104 Sets the size of the file, this function must be called in case of file writting/receiving. related resouce |
|
105 will be allocated in this function to prepare to receive the incoming data. |
|
106 @param aSize The new size of the file (in bytes). |
|
107 @leave One of the system wide error codes, if a processing failure occurs. |
|
108 */ |
|
109 EXPORT_C void CMTPTypeFile::SetSizeL(TUint64 aSize) |
|
110 { |
|
111 iTargetFileSize = (TInt64)aSize; //keep a record for the target file size |
|
112 |
|
113 iRemainingDataSize = (TInt64)aSize;//Current implemenation does not support file size with 2 x64 |
|
114 |
|
115 if(iRemainingDataSize> KMTPFileChunkSizeForLargeFile) //512K |
|
116 { |
|
117 iBuffer1.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
118 iBuffer2.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
119 } |
|
120 else |
|
121 { |
|
122 iBuffer1.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
123 iBuffer2.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
124 } |
|
125 if(iRemainingDataSize> KMTPFileSetSizeChunk) |
|
126 { |
|
127 //split the setSize to multiple calling of 512M |
|
128 User::LeaveIfError(iFile.SetSize(KMTPFileSetSizeChunk)); |
|
129 iCurrentFileSetSize = KMTPFileSetSizeChunk; |
|
130 } |
|
131 else |
|
132 { |
|
133 User::LeaveIfError(iFile.SetSize(aSize)); |
|
134 iCurrentFileSetSize = aSize; |
|
135 } |
|
136 } |
|
137 |
|
138 /** |
|
139 Provides a reference to the native file object encapsulate by the MTP file |
|
140 object data type. |
|
141 @return The native file object reference. |
|
142 */ |
|
143 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
144 EXPORT_C RFile64& CMTPTypeFile::File() |
|
145 #else |
|
146 EXPORT_C RFile& CMTPTypeFile::File() |
|
147 #endif |
|
148 { |
|
149 return iFile; |
|
150 } |
|
151 |
|
152 EXPORT_C TInt CMTPTypeFile::FirstReadChunk(TPtrC8& aChunk) const |
|
153 { |
|
154 aChunk.Set(NULL, 0); |
|
155 iReadSequenceState = EIdle; |
|
156 iBuffer1.Zero(); |
|
157 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
158 TInt64 pos =iOffSet; |
|
159 #else |
|
160 TInt pos = static_cast<TInt>(iOffSet); |
|
161 #endif |
|
162 TInt err(iFile.Seek(ESeekStart, pos)); |
|
163 if (err == KErrNone) |
|
164 { |
|
165 // The USB SIC header is 12 bytes long. If the first chunk is 128K - 12 bytes, |
|
166 // the USB SIC transport will not buffer data, which will improve the transfer rate. |
|
167 err = iFile.Read(iBuffer1, iBuffer1.MaxLength() - KUSBHeaderLen); |
|
168 if (err == KErrNone) |
|
169 { |
|
170 //this chunk is going to be used by USB to read data from it, only CMTPTypefile::RunL() can toggle this flag |
|
171 //When it finishe reading data into Buffer2. |
|
172 iBuffer1AvailForWrite = EFalse; |
|
173 |
|
174 aChunk.Set(iBuffer1.Ptr(), iBuffer1.Length()); |
|
175 |
|
176 //Set the commit chunk to be filled in by CMTPTypeFile::RunL() |
|
177 iCurrentCommitChunk.Set(&iBuffer2[0], 0, iBuffer2.MaxLength()); |
|
178 |
|
179 iRemainingDataSize -= aChunk.Length(); |
|
180 |
|
181 if (aChunk.Length() == 0) |
|
182 { |
|
183 // Empty File. |
|
184 iReadSequenceState = EIdle; |
|
185 err = KMTPChunkSequenceCompletion; |
|
186 } |
|
187 else |
|
188 { |
|
189 if (iRemainingDataSize <= 0) |
|
190 { |
|
191 // EOF. |
|
192 iReadSequenceState = EIdle; |
|
193 aChunk.Set(aChunk.Ptr(), aChunk.Length() + iRemainingDataSize); //for partial |
|
194 err = KMTPChunkSequenceCompletion; |
|
195 } |
|
196 else |
|
197 { |
|
198 iReadSequenceState = EInProgress; |
|
199 //This is NOT the last chunk, issue more CMTPTypeFile::RunL() |
|
200 if (!IsActive()) |
|
201 { |
|
202 //Since the writting data into file sever will take a long time, will issue a dedicated Active Object to do that. |
|
203 const_cast<CMTPTypeFile*>(this)->SetActive(); |
|
204 TRequestStatus* status = (TRequestStatus*)&iStatus; |
|
205 User::RequestComplete(status, KErrNone); |
|
206 } |
|
207 else |
|
208 { |
|
209 //This is a very extreme cases, it only occurs when the following assumption is met |
|
210 //1. USB already took buffer1 and already issue CMTPTypeFileRunL(), therefore, the ActiveObject has completed itself, |
|
211 //2. Somehow, this active object is not scheduled to be running even after the USB already use out the other buffer. |
|
212 //3. USB's active object is scheduled to be running prior to the last File active object(this should not happen if ActiveScheduler follow the priority scheduler). |
|
213 //4. USB call this function again to get the other data buffer. |
|
214 //5. Then it find the previous active is not scheduled to run. |
|
215 //in single-core platform, the code rely on the CActiveScheduler to guarantee the first active call which has higher priority to be running firstly before |
|
216 //the 2nd USB active. but for multi-core platform, this should be re-evaluated . |
|
217 iReadSequenceState = EIdle; |
|
218 err = KMTPChunkSequenceCompletion; |
|
219 } |
|
220 } |
|
221 } |
|
222 } |
|
223 else |
|
224 { |
|
225 iReadSequenceState = EIdle; |
|
226 iFileRdWrError = ETrue; |
|
227 } |
|
228 } |
|
229 iByteSent += aChunk.Length(); |
|
230 return err; |
|
231 } |
|
232 |
|
233 EXPORT_C TInt CMTPTypeFile::NextReadChunk(TPtrC8& aChunk) const |
|
234 { |
|
235 TInt err(KErrNone); |
|
236 |
|
237 if((iReadSequenceState != EInProgress) || (iFileRdWrError)) |
|
238 { |
|
239 aChunk.Set(NULL, 0); |
|
240 return KErrNotReady; |
|
241 } |
|
242 |
|
243 //This is called by USB's RunL(), here, the only possible scenarios is that the CMTPTypleFile::RunL() issued in FirReadChunk or last NextReadChunk must |
|
244 //have already finished. Now take the buffer which is filled in by data in CMTPTypleFile::RunL(). |
|
245 aChunk.Set(iCurrentCommitChunk.Ptr(), iCurrentCommitChunk.Length()); |
|
246 if(iBuffer1AvailForWrite) |
|
247 {//We have already used buffer_1, now buffer2 contains data read into by CMTPTypeFile::RunL(); |
|
248 //Set the commit chunk to be filled in by CMTPTypeFile::RunL() |
|
249 iCurrentCommitChunk.Set(&iBuffer1[0], 0, iBuffer1.MaxLength()); |
|
250 } |
|
251 else |
|
252 { |
|
253 //Set the commit chunk to be filled in by CMTPTypeFile::RunL() |
|
254 iCurrentCommitChunk.Set(&iBuffer2[0], 0, iBuffer2.MaxLength()); |
|
255 } |
|
256 |
|
257 iRemainingDataSize -= aChunk.Length(); |
|
258 |
|
259 if(aChunk.Length() == 0) |
|
260 { |
|
261 iReadSequenceState = EIdle; |
|
262 err = KMTPChunkSequenceCompletion; |
|
263 } |
|
264 else if(iRemainingDataSize> 0) |
|
265 { |
|
266 //This is NOT the last chunk, issue more CMTPTypeFile::RunL() |
|
267 if (!IsActive()) |
|
268 { |
|
269 //Since the writting data into file sever will take a long time, will issue a dedicated Active Object to do that. |
|
270 ((CMTPTypeFile*)this)->SetActive(); |
|
271 TRequestStatus* status = (TRequestStatus*)&iStatus; |
|
272 User::RequestComplete(status, KErrNone); |
|
273 } |
|
274 else |
|
275 { |
|
276 //This is a very extreme cases, it only occurs when the following assumption is met |
|
277 //1. USB already took buffer1 and already issue CMTPTypeFileRunL(), therefore, the ActiveObject has completed itself, |
|
278 //2. Somehow, this active object is not scheduled to be running even after the USB already use out the other buffer. |
|
279 //3. USB's active object is scheduled to be running prior to the last File active object(this should not happen if ActiveScheduler follow the priority scheduler). |
|
280 //4. USB call this function again to get the other data buffer. |
|
281 //5. Then it find the previous active is not scheduled to run. |
|
282 //in single-core platform, the code rely on the CActiveScheduler to guarantee the first active call which has higher priority to be running firstly before |
|
283 //the 2nd USB active. but for multi-core platform, this should be re-evaluated . |
|
284 iReadSequenceState = EIdle; |
|
285 err = KMTPChunkSequenceCompletion; |
|
286 } |
|
287 } |
|
288 else |
|
289 {//Last Chunk. Do not issue Active object. and indicate this completion of the chunk |
|
290 iReadSequenceState = EIdle; |
|
291 aChunk.Set(aChunk.Ptr(), aChunk.Length() + iRemainingDataSize); //for partial |
|
292 err = KMTPChunkSequenceCompletion; |
|
293 } |
|
294 iByteSent += aChunk.Length(); |
|
295 return err; |
|
296 } |
|
297 |
|
298 EXPORT_C TInt CMTPTypeFile::FirstWriteChunk(TPtr8& aChunk) |
|
299 { |
|
300 __ASSERT_DEBUG(iBuffer1AvailForWrite, User::Invariant()); |
|
301 __ASSERT_DEBUG(!iFileRdWrError, User::Invariant()); |
|
302 |
|
303 aChunk.Set(NULL, 0, 0); |
|
304 iWriteSequenceState = EIdle; |
|
305 |
|
306 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
307 TInt64 pos =0; |
|
308 #else |
|
309 TInt pos =0; |
|
310 #endif |
|
311 TInt err(iFile.Seek(ESeekStart, pos)); |
|
312 if (err == KErrNone) |
|
313 { |
|
314 //Because USB HS's transmission rate is several time faster than the rate of writting data into File System. |
|
315 //If the first packet is a full chunk size packet, then the writting of that data will not start until the full-chunk |
|
316 //sized packet is received. Here we intentionly reduce the first packet size to 1/4 of the full chunk size, therefore, |
|
317 //the start of writting data into File system will start only after 1/4 of the full chunk size data is received. |
|
318 //This can make the writting of data to FS start earlier. |
|
319 aChunk.Set(&iBuffer1[0], 0, iBuffer1.MaxLength()); |
|
320 iWriteSequenceState = EInProgress; |
|
321 |
|
322 //this chunk is going to be used by Transport to write data into it, and when it is full, transport |
|
323 //will call back CommitChunkL(), at that time, the EFalse means it already contains data in it. |
|
324 //it is ready for reading data from it. |
|
325 //This is a initial value for it to trigger the double-buffering mechanism. |
|
326 iBuffer1AvailForWrite = EFalse; |
|
327 } |
|
328 |
|
329 return err; |
|
330 } |
|
331 |
|
332 EXPORT_C TInt CMTPTypeFile::NextWriteChunk(TPtr8& aChunk) |
|
333 { |
|
334 TInt err(KErrNone); |
|
335 aChunk.Set(NULL, 0, 0); |
|
336 |
|
337 if (iWriteSequenceState != EInProgress) |
|
338 { |
|
339 err = KErrNotReady; |
|
340 } |
|
341 else |
|
342 {//toggle between buffer 1 and buffer 2 here. |
|
343 if(iBuffer1AvailForWrite) |
|
344 { |
|
345 aChunk.Set(&iBuffer1[0], 0, iBuffer1.MaxLength()); |
|
346 } |
|
347 else |
|
348 { |
|
349 aChunk.Set(&iBuffer2[0], 0, iBuffer2.MaxLength()); |
|
350 } |
|
351 } |
|
352 |
|
353 return err; |
|
354 } |
|
355 |
|
356 EXPORT_C TUint64 CMTPTypeFile::Size() const |
|
357 { |
|
358 //The USB transport layer uses USB Container Length to determine the total size of data to be |
|
359 //transfered. In USB protocol, the Container Length is 32 bits long which is up to 4G-1, so |
|
360 //for synchronization of a large file >=4G-12 bytes (the USB header is 12 bytes long), the |
|
361 //Container Length can't be used to determine the total size of data any more. In this kind of |
|
362 //case, our USB transport layer implementation will call this function to get the actual data size. |
|
363 |
|
364 //The RFile::SetSize() method may take over 40 seconds if we create a file and set its size |
|
365 //to a very large value, and this will cause timeout in MTP protocol layer. To avoid this |
|
366 //timeout, when creating a large file(over 512MB), instead of setting its size directly to |
|
367 //the target size by one singile RFile::SetSize() call, we'll call RFile::SetSize() multiple |
|
368 //times and set the file size step by step acumulately. For example, for a 2GB file, its |
|
369 //size will be set to 0.5G first, and then 1G, 1.5G and at last 2G. |
|
370 |
|
371 //So if a file is transfering to device, the size of the file that returned by RFile::Size() is |
|
372 //just a temporary value and means nothing. In this case, let's return the target file size instead. |
|
373 if(!iFileOpenForRead && iRemainingDataSize) |
|
374 { |
|
375 return iTargetFileSize; |
|
376 } |
|
377 |
|
378 //If the initiator get partial of the file, return the requested partial size |
|
379 if (iFileOpenForRead && iTargetFileSize) |
|
380 { |
|
381 return iTargetFileSize; |
|
382 } |
|
383 |
|
384 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
385 TInt64 size; |
|
386 #else |
|
387 TInt size; |
|
388 #endif |
|
389 iFile.Size(size); |
|
390 return size; |
|
391 } |
|
392 |
|
393 EXPORT_C TUint CMTPTypeFile::Type() const |
|
394 { |
|
395 return EMTPTypeFile; |
|
396 } |
|
397 |
|
398 EXPORT_C TBool CMTPTypeFile::CommitRequired() const |
|
399 { |
|
400 return ETrue; |
|
401 } |
|
402 |
|
403 EXPORT_C MMTPType* CMTPTypeFile::CommitChunkL(TPtr8& aChunk) |
|
404 { |
|
405 if(iFileRdWrError) |
|
406 { |
|
407 return NULL; |
|
408 } |
|
409 if(0 == aChunk.Length()) |
|
410 { |
|
411 ToggleRdWrBuffer(); |
|
412 return NULL; |
|
413 } |
|
414 iCurrentCommitChunk.Set(aChunk); |
|
415 |
|
416 if(iRemainingDataSize> iCurrentCommitChunk.Length()) |
|
417 {//This is NOT the last chunk, we issue an active object to commit it to File system. |
|
418 iRemainingDataSize -= iCurrentCommitChunk.Length(); |
|
419 /* |
|
420 if (!IsActive()) |
|
421 { |
|
422 //Since the writting data into file sever will take a long time, will issue a dedicated Active Object to do that. |
|
423 SetActive(); |
|
424 TRequestStatus* thisAO = &iStatus; |
|
425 User::RequestComplete(thisAO, KErrNone); |
|
426 } |
|
427 else |
|
428 { |
|
429 //This is a very extreme cases, it only occurs when the following assumption is met |
|
430 //1. USB received buffer1 and already call this CommitChunkL(), therefore, the ActiveObject has completed itself, and USB then use another buffer to |
|
431 //receive the data. |
|
432 //2. Somehow, this active object is not scheduled to be running even after the USB already fill out the other buffer. |
|
433 //3. USB's active object is scheduled to be running prior to the last File active object(this should not happen if ActiveScheduler follow the priority scheduler). |
|
434 //4. USB call this function again to commit the other data buffer. |
|
435 //5. Then it find the previous active is not scheduled to run. |
|
436 //in single-core platform, the code rely on the CActiveScheduler to guarantee the first active call which has higher priority to be running firstly before |
|
437 //the 2nd USB active. but for multi-core platform, this should be re-evaluated . |
|
438 iFileRdWrError = ETrue;//if it really discard the incoming recevied file. |
|
439 //__FLOG(_L8("\nThe program should not arrive here !!!!!\n")); |
|
440 } |
|
441 */ |
|
442 } |
|
443 else |
|
444 {//This is the last chunk, we synchronous commit it |
|
445 iRemainingDataSize = 0; |
|
446 ToggleRdWrBuffer(); |
|
447 return NULL; |
|
448 } |
|
449 return this; |
|
450 } |
|
451 |
|
452 //for partial |
|
453 EXPORT_C Int64 CMTPTypeFile::GetByteSent() |
|
454 { |
|
455 return iByteSent; |
|
456 } |
|
457 |
|
458 CMTPTypeFile::CMTPTypeFile() : |
|
459 CActive(EPriorityUserInput), iBuffer1AvailForWrite(ETrue), |
|
460 iFileRdWrError(EFalse), iCurrentCommitChunk(NULL, 0) |
|
461 { |
|
462 CActiveScheduler::Add(this); |
|
463 } |
|
464 |
|
465 void CMTPTypeFile::ConstructL(RFs& aFs, const TDesC& aName, TFileMode aMode) |
|
466 { |
|
467 if (aMode & EFileWrite) |
|
468 { |
|
469 iFileOpenForRead = EFalse; |
|
470 User::LeaveIfError(iFile.Replace(aFs, aName, aMode|EFileWriteDirectIO)); |
|
471 } |
|
472 else |
|
473 { |
|
474 iFileOpenForRead = ETrue; |
|
475 User::LeaveIfError(iFile.Open(aFs, aName, aMode|EFileReadDirectIO|EFileShareReadersOnly)); |
|
476 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
477 TInt64 size = 0; |
|
478 #else |
|
479 TInt size = 0; |
|
480 #endif |
|
481 User::LeaveIfError(iFile.Size(size)); |
|
482 iRemainingDataSize = size; |
|
483 |
|
484 //For File reading, NO "SetSizeL()" will be called, therefore, create the buffer here. |
|
485 if (iRemainingDataSize > KMTPFileChunkSizeForLargeFile) //512K |
|
486 { |
|
487 iBuffer1.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
488 iBuffer2.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
489 } |
|
490 else |
|
491 { |
|
492 iBuffer1.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
493 iBuffer2.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
494 } |
|
495 } |
|
496 } |
|
497 |
|
498 void CMTPTypeFile::ConstructL(RFs& aFs, const TDesC& aName, TFileMode aMode, TInt64 aRequiredSize, TInt64 aOffSet) |
|
499 { |
|
500 if (aMode & EFileWrite) |
|
501 { |
|
502 iFileOpenForRead = EFalse; |
|
503 User::LeaveIfError(iFile.Replace(aFs, aName, aMode|EFileWriteDirectIO)); |
|
504 } |
|
505 else |
|
506 { |
|
507 iFileOpenForRead = ETrue; |
|
508 iOffSet = aOffSet; |
|
509 User::LeaveIfError(iFile.Open(aFs, aName, aMode|EFileReadDirectIO|EFileShareReadersOnly)); |
|
510 #ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
511 TInt64 size = 0; |
|
512 #else |
|
513 TInt size = 0; |
|
514 #endif |
|
515 User::LeaveIfError(iFile.Size(size)); |
|
516 |
|
517 if(aRequiredSize < size) |
|
518 { |
|
519 iTargetFileSize = aRequiredSize; |
|
520 } |
|
521 else |
|
522 { |
|
523 iTargetFileSize = size; |
|
524 } |
|
525 iRemainingDataSize = iTargetFileSize; |
|
526 |
|
527 //For File reading, NO "SetSizeL()" will be called, therefore, create the buffer here. |
|
528 if (iRemainingDataSize > KMTPFileChunkSizeForLargeFile) //512K |
|
529 { |
|
530 iBuffer1.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
531 iBuffer2.CreateMaxL(KMTPFileChunkSizeForLargeFile); |
|
532 } |
|
533 else |
|
534 { |
|
535 iBuffer1.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
536 iBuffer2.CreateMaxL(KMTPFileChunkSizeForSmallFile); |
|
537 } |
|
538 } |
|
539 } |
|
540 |
|
541 void CMTPTypeFile::DoCancel() |
|
542 { |
|
543 // Nothing to cancel here because this Active object does not issue any asynchronous call to others. |
|
544 } |
|
545 |
|
546 // Catch any leaves - the CActiveScheduler can't handle it. |
|
547 TInt CMTPTypeFile::RunError(TInt /* aError*/) |
|
548 { |
|
549 //We did not throw exception in RunL() in reality, therefore, we need not to cope with it. |
|
550 return KErrNone; |
|
551 } |
|
552 |
|
553 void CMTPTypeFile::RunL() |
|
554 { |
|
555 ToggleRdWrBuffer(); |
|
556 } |
|
557 |
|
558 void CMTPTypeFile::ToggleRdWrBuffer() |
|
559 { |
|
560 //This is triggered by CommitChunkL(), this will write the received data into File system synchronously. |
|
561 //Since someone trigger this RunL(), therefore, there must be one of 2 buffer which is full of data to wait for writing buffer data into File system. |
|
562 //Each RunL(), only need to commit one chunk because transport only prepare one chunk for file system in one RunL(). |
|
563 |
|
564 TInt err = KErrNone; |
|
565 |
|
566 if (!iFileOpenForRead) |
|
567 { |
|
568 if (!iFileRdWrError) |
|
569 { |
|
570 TInt64 temp = iCurrentCommitChunk.Length(); |
|
571 iTotalReceivedSize += temp; |
|
572 if (iTotalReceivedSize > iCurrentFileSetSize) |
|
573 { |
|
574 //temp += iRemainingDataSize;//Total uncommitted file size. |
|
575 temp = iTotalReceivedSize-iCurrentFileSetSize+iRemainingDataSize; |
|
576 if (temp >= KMTPFileSetSizeChunk) |
|
577 { |
|
578 iCurrentFileSetSize += KMTPFileSetSizeChunk; |
|
579 } |
|
580 else |
|
581 { |
|
582 iCurrentFileSetSize += temp; |
|
583 } |
|
584 err = iFile.SetSize(iCurrentFileSetSize); |
|
585 } |
|
586 |
|
587 if (err != KErrNone) |
|
588 { |
|
589 iFileRdWrError = ETrue; |
|
590 } |
|
591 else |
|
592 { |
|
593 err = iFile.Write(iCurrentCommitChunk); |
|
594 if (err != KErrNone) |
|
595 {// file Write failed, this means we cannot successfully received this file but however, we cannot disrupt a current DIOR phase according to MTP spec. |
|
596 // We should continue to receive the data and discard this data, only after the data phase is finished can we send back an error response |
|
597 //to Initiator. Therefore, we pretend to continue to write this data into file, and let final processor to check the file size and then give back a |
|
598 //corresponding error code to MTP initiator. |
|
599 iFileRdWrError = ETrue; |
|
600 iFile.SetSize(0); |
|
601 } |
|
602 } |
|
603 } |
|
604 iCurrentCommitChunk.Zero(); |
|
605 } |
|
606 else |
|
607 { |
|
608 if (!iFileRdWrError) |
|
609 { |
|
610 err = iFile.Read(iCurrentCommitChunk, |
|
611 iCurrentCommitChunk.MaxLength()); |
|
612 if (err != KErrNone) |
|
613 {//Error, abort the current file reading. |
|
614 iFileRdWrError = ETrue; |
|
615 } |
|
616 } |
|
617 } |
|
618 |
|
619 iBuffer1AvailForWrite = !iBuffer1AvailForWrite;//toggle the flag. |
|
620 } |