30 //For file size larger than it, we will split one setSize() to several smaller one, each with the following size. |
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 |
31 const TInt64 KMTPFileSetSizeChunk(1<<30); //1G |
32 |
32 |
33 const TUint KUSBHeaderLen = 12; |
33 const TUint KUSBHeaderLen = 12; |
34 |
34 |
|
35 |
|
36 |
|
37 CMTPTypeFile::CFileWriter* CMTPTypeFile::CFileWriter::NewL(RFile& aFile, RBuf8& aWriteBuf) |
|
38 { |
|
39 CFileWriter *self = new(ELeave)CFileWriter(aFile, aWriteBuf); |
|
40 CleanupStack::PushL(self); |
|
41 self->ConstructL(); |
|
42 CleanupStack::Pop(self); |
|
43 return self; |
|
44 } |
|
45 |
|
46 void CMTPTypeFile::CFileWriter::GetWriteBuf(TPtr8& aChunk) |
|
47 { |
|
48 WaitForWriteComplete(); |
|
49 aChunk.Set(&iBuf[0], 0, iBuf.MaxLength()); |
|
50 } |
|
51 |
|
52 TInt CMTPTypeFile::CFileWriter::GetResult() const |
|
53 { |
|
54 return iWriteResult; |
|
55 } |
|
56 |
|
57 void CMTPTypeFile::CFileWriter::Write(TInt aLength) |
|
58 { |
|
59 iFile.Write(iBuf, aLength, iStatus); |
|
60 SetActive(); |
|
61 } |
|
62 |
|
63 void CMTPTypeFile::CFileWriter::WaitForWriteComplete() |
|
64 { |
|
65 /* |
|
66 * We didn't want to cancel the file write here. |
|
67 * But we need to wait until the file write complete. |
|
68 * The Cancel() function of CActive will do the wait until the file write complete. |
|
69 * If the Write already complete and the RunL() invoked there's nothing happened in the Cancel(). |
|
70 */ |
|
71 Cancel(); |
|
72 //Have to save the result. |
|
73 iWriteResult = iStatus.Int(); |
|
74 } |
|
75 |
|
76 void CMTPTypeFile::CFileWriter::RunL() |
|
77 { |
|
78 //Have to save the result. |
|
79 iWriteResult = iStatus.Int(); |
|
80 } |
|
81 |
|
82 |
|
83 CMTPTypeFile::CFileWriter::~CFileWriter() |
|
84 { |
|
85 WaitForWriteComplete(); //make sure all async request complete |
|
86 if(iWriteResult != KErrNone) |
|
87 { |
|
88 iFile.SetSize(0); |
|
89 } |
|
90 } |
|
91 |
|
92 |
|
93 void CMTPTypeFile::CFileWriter::DoCancel() |
|
94 { |
|
95 //We didn't really want to cancel the file write, so we do nothing here |
|
96 } |
|
97 |
|
98 |
|
99 CMTPTypeFile::CFileWriter::CFileWriter(RFile& aFile, RBuf8& aBuf):CActive(EPriorityStandard), iWriteResult(KErrNone), iFile(aFile), iBuf(aBuf) |
|
100 { |
|
101 } |
|
102 |
|
103 |
|
104 void CMTPTypeFile::CFileWriter::ConstructL() |
|
105 { |
|
106 CActiveScheduler::Add(this); |
|
107 } |
|
108 |
35 /** |
109 /** |
36 MTP file object data type factory method. |
110 MTP file object data type factory method. |
37 @param aFs The handle of an active file server session. |
111 @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 |
112 @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. |
113 or directory), which are not specified, are taken from the session path. |
309 TInt pos =0; |
381 TInt pos =0; |
310 #endif |
382 #endif |
311 TInt err(iFile.Seek(ESeekStart, pos)); |
383 TInt err(iFile.Seek(ESeekStart, pos)); |
312 if (err == KErrNone) |
384 if (err == KErrNone) |
313 { |
385 { |
314 //Because USB HS's transmission rate is several time faster than the rate of writting data into File System. |
386 iFileWriter1->GetWriteBuf(aChunk); |
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; |
387 iWriteSequenceState = EInProgress; |
321 |
388 |
322 //this chunk is going to be used by Transport to write data into it, and when it is full, transport |
389 //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. |
390 //will call back CommitChunkL(), at that time, the ETrue means it already contains data in it. |
324 //it is ready for reading data from it. |
391 //it is ready for reading data from it. |
325 //This is a initial value for it to trigger the double-buffering mechanism. |
392 //This is a initial value for it to trigger the double-buffering mechanism. |
326 iBuffer1AvailForWrite = EFalse; |
393 iBuffer1AvailForWrite = ETrue; |
327 } |
394 } |
328 |
395 |
329 return err; |
396 return err; |
330 } |
397 } |
331 |
398 |
400 return ETrue; |
467 return ETrue; |
401 } |
468 } |
402 |
469 |
403 EXPORT_C MMTPType* CMTPTypeFile::CommitChunkL(TPtr8& aChunk) |
470 EXPORT_C MMTPType* CMTPTypeFile::CommitChunkL(TPtr8& aChunk) |
404 { |
471 { |
405 if(iFileRdWrError) |
472 if(iFileRdWrError) |
406 { |
473 { |
407 return NULL; |
474 return NULL; |
408 } |
475 } |
409 if(0 == aChunk.Length()) |
|
410 { |
|
411 ToggleRdWrBuffer(); |
|
412 return NULL; |
|
413 } |
|
414 iCurrentCommitChunk.Set(aChunk); |
476 iCurrentCommitChunk.Set(aChunk); |
415 |
|
416 if(iRemainingDataSize> iCurrentCommitChunk.Length()) |
477 if(iRemainingDataSize> iCurrentCommitChunk.Length()) |
417 {//This is NOT the last chunk, we issue an active object to commit it to File system. |
478 { |
418 iRemainingDataSize -= iCurrentCommitChunk.Length(); |
479 iRemainingDataSize -= iCurrentCommitChunk.Length(); |
419 /* |
480 } |
420 if (!IsActive()) |
481 else |
421 { |
482 { |
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; |
483 iRemainingDataSize = 0; |
446 ToggleRdWrBuffer(); |
484 } |
447 return NULL; |
485 //wait until previous write complete |
448 } |
486 if(iBuffer1AvailForWrite) |
449 return this; |
487 { |
|
488 iFileWriter2->WaitForWriteComplete(); |
|
489 iFileRdWrError = (iFileWriter2->GetResult() != KErrNone); |
|
490 } |
|
491 else |
|
492 { |
|
493 iFileWriter1->WaitForWriteComplete(); |
|
494 iFileRdWrError = (iFileWriter1->GetResult() != KErrNone); |
|
495 } |
|
496 ToggleRdWrBuffer(); |
|
497 if(iRemainingDataSize <= 0) //last chunk need wait the write complete |
|
498 { |
|
499 iFileWriter1->WaitForWriteComplete(); |
|
500 iFileWriter2->WaitForWriteComplete(); |
|
501 if(iFileWriter1->GetResult() != KErrNone || iFileWriter2->GetResult() != KErrNone) |
|
502 { |
|
503 iFile.SetSize(0); |
|
504 iFileRdWrError = ETrue; |
|
505 } |
|
506 } |
|
507 |
|
508 return NULL; |
450 } |
509 } |
451 |
510 |
452 //for partial |
511 //for partial |
453 EXPORT_C Int64 CMTPTypeFile::GetByteSent() |
512 EXPORT_C Int64 CMTPTypeFile::GetByteSent() |
454 { |
513 { |
588 { |
647 { |
589 iFileRdWrError = ETrue; |
648 iFileRdWrError = ETrue; |
590 } |
649 } |
591 else |
650 else |
592 { |
651 { |
593 err = iFile.Write(iCurrentCommitChunk); |
652 if(iBuffer1AvailForWrite) |
594 if (err != KErrNone) |
653 { |
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. |
654 iFileWriter1->Write(iCurrentCommitChunk.Length()); |
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 |
655 } |
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 |
656 else |
598 //corresponding error code to MTP initiator. |
657 { |
599 iFileRdWrError = ETrue; |
658 iFileWriter2->Write(iCurrentCommitChunk.Length()); |
600 iFile.SetSize(0); |
|
601 } |
659 } |
602 } |
660 } |
|
661 } |
|
662 else |
|
663 { |
|
664 iFile.SetSize(0); |
603 } |
665 } |
604 iCurrentCommitChunk.Zero(); |
666 iCurrentCommitChunk.Zero(); |
605 } |
667 } |
606 else |
668 else |
607 { |
669 { |