First public contribution.
1 // Copyright (c) 1996-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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32test\pccd\t_atadrv.cpp
15 // Test the Compact Flash card (ATA) media driver
25 #include <e32def_private.h>
27 const TInt KAtaSectorSize=512;
28 const TInt KAtaSectorShift=9;
29 const TUint KAtaSectorMask=0xFFFFFE00;
30 const TInt KSectBufSizeInSectors=8;
31 const TInt KSectBufSizeInBytes=(KSectBufSizeInSectors<<KAtaSectorShift);
32 const TInt KRdWrBufLen=(KSectBufSizeInBytes+KAtaSectorSize); // 4.5K - exceeds driver local buffer size
34 const TInt KShortFormatInSectors=1;
35 const TInt KShortFormatInBytes=(KShortFormatInSectors<<KAtaSectorShift);
36 const TInt KLongFormatInSectors=KSectBufSizeInSectors+1; // 4.5K - exceeds driver local buffer size
37 const TInt KLongFormatInBytes=(KLongFormatInSectors<<KAtaSectorShift);
39 const TInt KHeapSize=0x4000;
40 const TInt KAtaIdleCurrentInMilliAmps=1;
42 #define PDD_NAME _L("MEDATA")
44 LOCAL_D RTest test(_L("T_ATADRV"));
45 LOCAL_D RTest nTest(_L("This thread doesn't disconnect"));
46 LOCAL_D TBool ChangeFlag;
47 LOCAL_D TBool SecThreadChangeFlag;
48 LOCAL_D TBuf8<KRdWrBufLen> wrBuf,rdBuf;
49 LOCAL_D TInt DriveNumber;
51 const TInt KSingSectorNo=1;
52 void singleSectorRdWrTest(TBusLocalDrive &aDrv,TInt aSectorOffset,TInt aLen)
54 // Perform a write / read test on a single sector (KSingSectorNo). Verify that the
55 // write / read back is successful and that the rest of the sector is unchanged.
59 TBuf8<KAtaSectorSize> saveBuf;
60 test.Start(_L("Single sector write/read test"));
61 test(aSectorOffset+aLen<=KAtaSectorSize);
63 // Now save state of sector before we write to it
64 TInt secStart=(KSingSectorNo<<KAtaSectorShift);
65 test(aDrv.Read(secStart,KAtaSectorSize,saveBuf)==KErrNone);
67 // Write zero's to another sector altogether (to ensure drivers
68 // local buffer hasn't already got test pattern we expect).
69 wrBuf.Fill(0,KAtaSectorSize);
70 test(aDrv.Write((KSingSectorNo+4)<<KAtaSectorShift,wrBuf)==KErrNone);
72 // Write / read back sector in question
73 wrBuf.SetLength(aLen);
74 for (TInt i=0;i<aLen;i++)
75 wrBuf[i]=(TUint8)(0xFF-i);
76 test(aDrv.Write((secStart+aSectorOffset),wrBuf)==KErrNone);
78 test(aDrv.Read((secStart+aSectorOffset),aLen,rdBuf)==KErrNone);
79 test(rdBuf.Compare(wrBuf)==0);
81 // Now check the rest of the sector is unchanged
82 rdBuf.Fill(0,KAtaSectorSize);
83 test(aDrv.Read(secStart,KAtaSectorSize,rdBuf)==KErrNone);
84 saveBuf.Replace(aSectorOffset,aLen,wrBuf);
85 test(rdBuf.Compare(saveBuf)==0);
89 const TInt KMultSectorNo=2;
90 void MultipleSectorRdWrTest(TBusLocalDrive &aDrv,TInt aFirstSectorOffset,TInt aLen)
92 // Perform a write / read test over multiple sectors (starting within sector KMultSectorNo).
93 // Verify that the write / read back is successful and that the remainder of the first and
94 // last sectors are not affected.
98 TBuf8<KAtaSectorSize> saveBuf1;
99 TBuf8<KAtaSectorSize> saveBuf2;
100 test.Start(_L("Multiple sector write/read test"));
101 test(aFirstSectorOffset<KAtaSectorSize&&aLen<=KRdWrBufLen);
103 // If not starting on sector boundary then save 1st sector to check rest of 1st sector is unchanged
104 TInt startSecPos=(KMultSectorNo<<KAtaSectorShift);
105 if (aFirstSectorOffset!=0)
106 test(aDrv.Read(startSecPos,KAtaSectorSize,saveBuf1)==KErrNone);
108 // If not ending on sector boundary then save last sector to check rest of last sector is unchanged
109 TInt endOffset=(aFirstSectorOffset+aLen)&(~KAtaSectorMask);
110 TInt endSecPos=((startSecPos+aFirstSectorOffset+aLen)&KAtaSectorMask);
112 test(aDrv.Read(endSecPos,KAtaSectorSize,saveBuf2)==KErrNone);
114 // Write zero's to another sector altogether (to ensure drivers
115 // local buffer hasn't already got test pattern we expect).
116 wrBuf.Fill(0,KSectBufSizeInBytes);
117 test(aDrv.Write((KMultSectorNo+20)<<KAtaSectorShift,wrBuf)==KErrNone);
119 wrBuf.SetLength(aLen);
120 for (TInt i=0;i<aLen;i++)
121 wrBuf[i]=(TUint8)(0xFF-i);
122 test(aDrv.Write((startSecPos+aFirstSectorOffset),wrBuf)==KErrNone);
124 test(aDrv.Read((startSecPos+aFirstSectorOffset),aLen,rdBuf)==KErrNone);
125 test(rdBuf.Compare(wrBuf)==0);
127 // Check rest of first sector involved is unchanged (if offset specified)
128 if (aFirstSectorOffset!=0)
130 rdBuf.Fill(0,KAtaSectorSize);
131 test(aDrv.Read(startSecPos,KAtaSectorSize,rdBuf)==KErrNone);
132 wrBuf.SetLength(KAtaSectorSize-aFirstSectorOffset);
133 saveBuf1.Replace(aFirstSectorOffset,(KAtaSectorSize-aFirstSectorOffset),wrBuf);
134 test(rdBuf.Compare(saveBuf1)==0);
137 // Check rest of last sector involved is unchanged (if not ending on sector boundary)
140 rdBuf.Fill(0,KAtaSectorSize);
141 test(aDrv.Read(endSecPos,KAtaSectorSize,rdBuf)==KErrNone);
142 wrBuf.SetLength(aLen);
143 wrBuf.Delete(0,aLen-endOffset);
144 saveBuf2.Replace(0,endOffset,wrBuf);
145 test(rdBuf.Compare(saveBuf2)==0);
150 LOCAL_C TInt dontDisconnectThread(TAny*)
153 TBusLocalDrive anotherAtaDrive;
156 nTest.Start(_L("Connect to internal drive"));
157 anotherAtaDrive.Connect(DriveNumber,SecThreadChangeFlag);
159 nTest.Next(_L("Capabilities"));
160 TLocalDriveCapsV2 info;
161 TPckg<TLocalDriveCapsV2> infoPckg(info);
162 nTest(anotherAtaDrive.Caps(infoPckg)==KErrNone);
163 nTest(info.iType==EMediaHardDisk);
169 LOCAL_C void ProgressBar(TInt aPos,TInt anEndPos,TInt anXPos)
171 // Display progress of local drive operation on screen (1-16 dots)
176 if ((curr=(aPos-1)/(anEndPos>>4))>prev)
177 { // Update progress bar
178 test.Console()->SetPos(anXPos);
179 for (TInt i=curr;i>=0;i--)
180 test.Printf(_L("."));
185 #pragma warning( disable : 4702 ) // unreachable code
187 GLDEF_C TInt E32Main()
192 TDriveInfoV1Buf diBuf;
193 UserHal::DriveInfo(diBuf);
194 TDriveInfoV1 &di=diBuf();
196 test.Start(_L("Test the Compact Flash card (ATA) media drive"));
197 test.Printf(_L("DRIVES PRESENT :%d\r\n"),di.iTotalSupportedDrives);
198 test.Printf(_L("1ST DRIVE NAME :%- 16S\r\n"),&di.iDriveName[0]);
199 test.Printf(_L("2ND DRIVE NAME :%- 16S\r\n"),&di.iDriveName[1]);
200 test.Printf(_L("3RD DRIVE NAME :%- 16S\r\n"),&di.iDriveName[2]);
201 test.Printf(_L("4TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[3]);
202 test.Printf(_L("5TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[4]);
203 test.Printf(_L("6TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[5]);
204 test.Printf(_L("7TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[6]);
205 test.Printf(_L("8TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[7]);
206 test.Printf(_L("9TH DRIVE NAME :%- 16S\r\n"),&di.iDriveName[8]);
208 test.Printf(_L("\r\nWarning - all data on removable drive will be lost.\r\n"));
209 test.Printf(_L("<<<Hit D to continue>>>\r\n"));
210 TChar c=(TUint)test.Getch();
212 DriveNumber=((TUint)c)-'C';
213 test(DriveNumber >= 1 && DriveNumber < di.iTotalSupportedDrives);
215 #if defined (__WINS__)
216 // Connect to all the local drives first as will be the case in ARM
217 TBusLocalDrive Drive[KMaxLocalDrives];
218 TBool DriveFlag[KMaxLocalDrives];
219 for (i=0;i<KMaxLocalDrives;i++)
220 Drive[i].Connect(i,DriveFlag[i]);
223 test.Next(_L("Load ATA Media Driver"));
224 TInt r=User::LoadPhysicalDevice(PDD_NAME);
225 test(r==KErrNone||r==KErrAlreadyExists);
227 test.Next(_L("Read machine information"));
229 r=HAL::Get(HAL::EMachineUid,mid);
231 TBool mediaChangeSupported=EFalse;
233 b.Format(_L("Connect to local drive (%c:)"),DriveNumber+'C');
235 TBusLocalDrive theAtaDrive;
237 test(theAtaDrive.Connect(DriveNumber,ChangeFlag)==KErrNone);
238 if (mediaChangeSupported)
240 theAtaDrive.ForceMediaChange(); // Generate media change to reset PC Card current consumption
241 User::After(300000); // Allow 0.3s after power down for controller to detect door closed.
243 // TSupplyInfoV1Buf supply1;
244 // test(UserHal::SupplyInfo(supply1)==KErrNone);
246 test.Next(_L("ATA drive: Capabilities"));
249 startTime.HomeTime();
250 TLocalDriveCapsV2 info;
251 TPckg<TLocalDriveCapsV2> infoPckg(info);
252 test(theAtaDrive.Caps(infoPckg)==KErrNone);
253 diskSize=I64LOW(info.iSize);
254 test.Printf( _L("Check drive size: %d\r\n"),diskSize);
255 #if defined (__WINS__)
256 test.Printf(_L("Check hidden sectors (=0): %d\r\n"),info.iHiddenSectors);
258 test.Printf(_L("Check hidden sectors (=16/32): %d\r\n"),info.iHiddenSectors);
261 test(info.iType==EMediaHardDisk);
262 test(info.iConnectionBusType==EConnectionBusInternal);
263 test(info.iDriveAtt==(TUint)(KDriveAttLocal|KDriveAttRemovable));
264 test(info.iMediaAtt==KMediaAttFormattable);
265 test(info.iFileSystemId==KDriveFileSysFAT);
266 // TSupplyInfoV1Buf supply2;
267 // test(UserHal::SupplyInfo(supply2)==KErrNone);
268 // if (mediaChangeSupported)
269 // test(supply2().iCurrentConsumptionMilliAmps==supply1().iCurrentConsumptionMilliAmps+KAtaIdleCurrentInMilliAmps); // Snowball idle current is zero
271 b.Format(_L("ATA drive: Sector RdWr(%d)"),KAtaSectorSize);
274 wrBuf.SetLength(KAtaSectorSize);
275 TUint *p=(TUint*)&wrBuf[0];
276 for (i=0;i<KAtaSectorSize;i++)
279 test.Printf(_L("Writing "));
280 for (i=0;i<diskSize;i+=len) // B - Sector wr/rd on sector boundary
282 ProgressBar(i,diskSize,11);
283 len=Min(KAtaSectorSize,(diskSize-i));
284 (*p)=(i/KAtaSectorSize);
285 wrBuf.SetLength(len);
286 test(theAtaDrive.Write(i,wrBuf)==KErrNone);
288 test.Printf(_L("\r\nReading "));
289 for (i=0;i<diskSize;i+=len)
291 ProgressBar(i,diskSize,11);
292 len=Min(KAtaSectorSize,(diskSize-i));
294 test(theAtaDrive.Read(i,len,rdBuf)==KErrNone);
295 (*p)=(i/KAtaSectorSize);
296 wrBuf.SetLength(len);
297 test(rdBuf.Compare(wrBuf)==0);
299 test.Printf(_L("\r\n"));
301 b.Format(_L("ATA drive: Short RdWr(1) (%dbytes at %d)"),25,0);
303 singleSectorRdWrTest(theAtaDrive,0,25); // A - Sub-sector wr/rd at sector start
305 b.Format(_L("ATA drive: Short RdWr(2) (%dbytes at %d)"),16,277);
307 singleSectorRdWrTest(theAtaDrive,277,16); // E - Sub-sector wr/rd in mid sector
309 b.Format(_L("ATA drive: Short RdWr(3) (%dbytes at %d)"),100,412);
311 singleSectorRdWrTest(theAtaDrive,412,100); // F - Sub-sector wr/rd at sector end
313 b.Format(_L("ATA drive: Long RdWr(1) (%dbytes at %d)"),KAtaSectorSize+15,0);
315 MultipleSectorRdWrTest(theAtaDrive,0,KAtaSectorSize+15); // C - Long wr/rd starting on sector boundary
317 b.Format(_L("ATA drive: Long RdWr(2) (%dbytes at %d)"),(KAtaSectorSize<<1),0);
319 MultipleSectorRdWrTest(theAtaDrive,0,(KAtaSectorSize<<1)); // D - Long wr/rd starting/ending on sector boundary
321 b.Format(_L("ATA drive: Long RdWr(3) (%dbytes at %d)"),KAtaSectorSize+3,509);
323 MultipleSectorRdWrTest(theAtaDrive,509,KAtaSectorSize+3); // H - - Long wr/rd ending on sector boundary
325 b.Format(_L("ATA drive: Long RdWr(4) (%dbytes at %d)"),(KAtaSectorSize<<1),508);
327 MultipleSectorRdWrTest(theAtaDrive,508,(KAtaSectorSize<<1));
329 b.Format(_L("ATA drive: Sector RdWr across sector boundary(%dbytes at %d)"),KAtaSectorSize,508);
331 MultipleSectorRdWrTest(theAtaDrive,508,KAtaSectorSize); // G - Sector wr/rd over sector boundary
333 b.Format(_L("ATA drive: Very long RdWr(1) (%dbytes at %d)"),KRdWrBufLen,0);
335 MultipleSectorRdWrTest(theAtaDrive,0,KRdWrBufLen); // Exceeds driver's buffer, starts/ends on sector boundary
337 b.Format(_L("ATA drive: Very long RdWr(2) (%dbytes at %d)"),(KRdWrBufLen-KAtaSectorSize+5),507);
339 MultipleSectorRdWrTest(theAtaDrive,507,(KRdWrBufLen-KAtaSectorSize+5)); // Exceeds driver's buffer, ends on sector boundary
341 b.Format(_L("ATA drive: Very long RdWr(3) (%dbytes at %d)"),KRdWrBufLen,10);
343 MultipleSectorRdWrTest(theAtaDrive,10,KRdWrBufLen); // Exceeds driver's buffer, starts/ends off sector boundary
345 b.Format(_L("ATA drive: Very long RdWr(4) (%dbytes at %d)"),(KRdWrBufLen-3),0);
347 MultipleSectorRdWrTest(theAtaDrive,0,KRdWrBufLen-3); // Exceeds driver's buffer, starts on sector boundary
349 b.Format(_L("ATA drive: Very long RdWr(5) (%dbytes at %d)"),(KRdWrBufLen-KAtaSectorSize),27);
351 MultipleSectorRdWrTest(theAtaDrive,27,(KRdWrBufLen-KAtaSectorSize)); // Exceeds driver's buffer (due to start offset), starts/ends off sector boundary
353 b.Format(_L("ATA drive: Very long RdWr(6) (%dbytes at %d)"),(KRdWrBufLen-KAtaSectorSize-3),0);
355 MultipleSectorRdWrTest(theAtaDrive,0,KRdWrBufLen-KAtaSectorSize-3); // Equals driver's buffer, starts on sector boundary
357 b.Format(_L("ATA drive: Very long RdWr(7) (%dbytes at %d)"),(KRdWrBufLen-3),3);
359 MultipleSectorRdWrTest(theAtaDrive,3,KRdWrBufLen-3); // Equals driver's buffer, ends on sector boundary
361 test.Next(_L("ATA drive: Inter-thread RdWr"));
363 dummyThread.Duplicate(RThread());
364 TInt threadHandle=dummyThread.Handle();
365 wrBuf.SetLength(KAtaSectorSize);
366 for (i=0;i<KAtaSectorSize;i++)
368 test(theAtaDrive.Write(10,KAtaSectorSize,&wrBuf,threadHandle,0)==KErrNone);
369 rdBuf.Fill(0,KAtaSectorSize);
370 test(theAtaDrive.Read(10,KAtaSectorSize,&rdBuf,threadHandle,0)==KErrNone);
371 test(rdBuf.Compare(wrBuf)==0);
374 test.Next(_L("ATA drive: Format sectors (short)"));
375 TBuf8<KAtaSectorSize> savBuf1,savBuf2;
376 TInt fmtTestPos=(10<<KAtaSectorShift);
377 // Save sectors surrounding those which will be formatted
378 test(theAtaDrive.Read((fmtTestPos-KAtaSectorSize),KAtaSectorSize,savBuf1)==KErrNone);
379 test(theAtaDrive.Read((fmtTestPos+KShortFormatInBytes),KAtaSectorSize,savBuf2)==KErrNone);
380 test(theAtaDrive.Format(fmtTestPos,KShortFormatInBytes)==KErrNone);
381 test(theAtaDrive.Read(fmtTestPos,KShortFormatInBytes,rdBuf)==KErrNone);
382 wrBuf.Fill(0xFF,KShortFormatInBytes);
383 test(rdBuf.Compare(wrBuf)==0);
384 // Check that surrounding sectors unaffected
385 test(theAtaDrive.Read((fmtTestPos-KAtaSectorSize),KAtaSectorSize,rdBuf)==KErrNone);
386 test(rdBuf.Compare(savBuf1)==0);
387 test(theAtaDrive.Read((fmtTestPos+KShortFormatInBytes),KAtaSectorSize,rdBuf)==KErrNone);
388 test(rdBuf.Compare(savBuf2)==0);
390 test.Next(_L("ATA drive: Format sectors (long)"));
391 fmtTestPos+=(4<<KAtaSectorShift);
392 // Save sectors surrounding those which will be formatted
393 test(theAtaDrive.Read((fmtTestPos-KAtaSectorSize),KAtaSectorSize,savBuf1)==KErrNone);
394 test(theAtaDrive.Read((fmtTestPos+KLongFormatInBytes),KAtaSectorSize,savBuf2)==KErrNone);
395 test(theAtaDrive.Format(fmtTestPos,KLongFormatInBytes)==KErrNone);
396 test(theAtaDrive.Read(fmtTestPos,KLongFormatInBytes,rdBuf)==KErrNone);
397 wrBuf.Fill(0xFF,KLongFormatInBytes);
398 test(rdBuf.Compare(wrBuf)==0);
399 // Check that surrounding sectors unaffected
400 test(theAtaDrive.Read((fmtTestPos-KAtaSectorSize),KAtaSectorSize,rdBuf)==KErrNone);
401 test(rdBuf.Compare(savBuf1)==0);
402 test(theAtaDrive.Read((fmtTestPos+KLongFormatInBytes),KAtaSectorSize,rdBuf)==KErrNone);
403 test(rdBuf.Compare(savBuf2)==0);
405 test.Next(_L("ATA drive: Format entire disk"));
407 test.Printf(_L("Formatting "));
409 while((ret=theAtaDrive.Format(fi))!=KErrEof)
411 ProgressBar((fi.i512ByteSectorsFormatted<<9),diskSize,11);
414 test.Printf(_L("\r\nReading "));
415 for (i=0;i<diskSize;i+=len)
417 ProgressBar(i,diskSize,11);
418 len=Min(KAtaSectorSize,(diskSize-i));
419 rdBuf.Fill(0x55,len);
420 test(theAtaDrive.Read(i,len,rdBuf)==KErrNone);
421 wrBuf.SetLength(len);
422 test(rdBuf.Compare(wrBuf)==0);
427 TTimeIntervalMicroSeconds elapsed=endTime.MicroSecondsFrom(startTime);
428 test.Printf(_L(" (Elapsed time: %dmS)\r\n"),(elapsed.Int64()/1000));
430 if (!mediaChangeSupported)
432 // Remainder of tests involve media change so stop now
437 test.Next(_L("ATA drive: Media change"));
438 #if defined (__WINS__)
439 test.Printf( _L("<<<Hit F5 - then any other key>>>\r\n"));
441 test.Printf( _L("<<<Generate Media change - then hit a key>>>\r\n"));
444 User::After(300000); // Allow 0.3s after power down for controller to detect door closed.
446 // test(UserHal::SupplyInfo(supply2)==KErrNone);
447 // test(supply2().iCurrentConsumptionMilliAmps==supply1().iCurrentConsumptionMilliAmps);
450 test.Next(_L("ATA drive: Caps following media change"));
451 test(theAtaDrive.Caps(infoPckg)==KErrNone);
452 test(info.iType==EMediaHardDisk);
453 // test(UserHal::SupplyInfo(supply2)==KErrNone);
454 // test(supply2().iCurrentConsumptionMilliAmps==supply1().iCurrentConsumptionMilliAmps+KAtaIdleCurrentInMilliAmps);
456 test.Next(_L("ATA drive: Caps while OOM"));
457 TInt err=KErrNoMemory;
458 test.Printf(_L("Mount returns:"));
459 for (TInt j=1; err!=KErrNone && j<16; j++)
461 theAtaDrive.ForceMediaChange(); // Generate media change
462 User::After(300000); // Allow 0.3s after power down for controller to detect door closed.
464 __KHEAP_SETFAIL(RHeap::EDeterministic,j);
465 err=theAtaDrive.Caps(infoPckg);
466 test.Printf(_L("(%d)"),err);
467 test(err==KErrNoMemory || err==KErrNone);
468 // __KHEAP_MARKEND; // fails because card functions only released by media change or power down
472 test.Printf(_L("\r\n"));
473 theAtaDrive.ForceMediaChange(); // Generate media change
474 User::After(300000); // Allow 0.3s after power down for controller to detect door closed.
475 __KHEAP_MARKEND; // test memory released after media change
478 test.Next(_L("ATA drive: Caps before power off"));
479 test(theAtaDrive.Caps(infoPckg)==KErrNone);
480 test(info.iType==EMediaHardDisk);
482 test.Next(_L("ATA drive: Machine power-off."));
485 test(timer.CreateLocal()==KErrNone);
486 TRequestStatus timerStat;
489 tim+=TTimeIntervalSeconds(8);
490 timer.At(timerStat,tim);
491 UserHal::SwitchOff();
492 User::WaitForRequest(timerStat);
493 test(!ChangeFlag); // ie machine power off hasn't updated it
495 // __KHEAP_MARKEND; // test memory released on power off
497 test.Next(_L("ATA drive: Caps following power off"));
498 test(theAtaDrive.Caps(infoPckg)==KErrNone);
499 test(info.iType==EMediaHardDisk);
501 test.Next(_L("Starting 2nd thread"));
502 SecThreadChangeFlag=EFalse;
505 test(thread.Create(_L("Thread"),dontDisconnectThread,KDefaultStackSize,KHeapSize,KHeapSize,NULL)==KErrNone);
508 User::WaitForRequest(stat);
509 test(stat==KErrNone);
510 CLOSE_AND_WAIT(thread);
512 test.Next(_L("ATA drive: 2nd media change"));
513 theAtaDrive.ForceMediaChange(); // Generate media change
515 test(!SecThreadChangeFlag); // Closed 2nd thread so shouldn't have been updated
517 b.Format(_L("Disconnect from local drive (%c:)"),DriveNumber+'C');
519 theAtaDrive.Disconnect();
523 #if defined (__WINS__)
524 for (i=0;i<KMaxLocalDrives;i++)
525 Drive[i].Disconnect();