22 * PROJECT: FreeLoader
33 * LICENSE: MIT (https://spdx.org/licenses/MIT)
44 * PURPOSE: ATA/ATAPI programmed I/O driver.
5- * COPYRIGHT: Copyright 2019-2025 Dmitry Borisov ( di.sean@protonmail.com)
5+ * COPYRIGHT: Copyright 2019-2026 Dmitry Borisov < di.sean@protonmail.com>
66 */
77
88/* INCLUDES *******************************************************************/
@@ -38,21 +38,6 @@ static PHW_DEVICE_UNIT AtapUnits[CHANNEL_MAX_CHANNELS * CHANNEL_MAX_DEVICES];
3838
3939/* PRIVATE FUNCTIONS **********************************************************/
4040
41- #if defined(ATA_SUPPORT_32_BIT_IO )
42- static
43- inline
44- BOOLEAN
45- AtapIs32BitIoSupported (
46- _In_ PHW_DEVICE_UNIT DeviceUnit )
47- {
48- #if defined(ATA_ALWAYS_DO_32_BIT_IO )
49- return TRUE;
50- #else
51- return !!(DeviceUnit -> P .Flags & ATA_DEVICE_FLAG_IO32 );
52- #endif
53- }
54- #endif
55-
5641static
5742VOID
5843AtapSelectDevice (
@@ -69,11 +54,12 @@ AtapSelectDevice(
6954}
7055
7156static
72- BOOLEAN
73- AtapWaitForNotBusy (
57+ UCHAR
58+ AtapWait (
7459 _In_ PIDE_REGISTERS Registers ,
7560 _In_range_ (> , 0 ) ULONG Timeout ,
76- _Out_opt_ PUCHAR Result )
61+ _In_ UCHAR Mask ,
62+ _In_ UCHAR Value )
7763{
7864 UCHAR IdeStatus ;
7965 ULONG i ;
@@ -83,47 +69,16 @@ AtapWaitForNotBusy(
8369 for (i = 0 ; i < Timeout ; ++ i )
8470 {
8571 IdeStatus = ATA_READ (Registers -> Status );
86- if (!(IdeStatus & IDE_STATUS_BUSY ))
87- {
88- if (Result )
89- * Result = IdeStatus ;
90- return TRUE;
91- }
72+ if ((IdeStatus & Mask ) == Value )
73+ break ;
9274
9375 if (IdeStatus == 0xFF )
9476 break ;
9577
9678 StallExecutionProcessor (10 );
9779 }
9880
99- if (Result )
100- * Result = IdeStatus ;
101- return FALSE;
102- }
103-
104- static
105- BOOLEAN
106- AtapWaitForIdle (
107- _In_ PIDE_REGISTERS Registers ,
108- _Out_ PUCHAR Result )
109- {
110- UCHAR IdeStatus ;
111- ULONG i ;
112-
113- for (i = 0 ; i < ATA_TIME_DRQ_CLEAR ; ++ i )
114- {
115- IdeStatus = ATA_READ (Registers -> Status );
116- if (!(IdeStatus & (IDE_STATUS_DRQ | IDE_STATUS_BUSY )))
117- {
118- * Result = IdeStatus ;
119- return TRUE;
120- }
121-
122- StallExecutionProcessor (2 );
123- }
124-
125- * Result = IdeStatus ;
126- return FALSE;
81+ return IdeStatus ;
12782}
12883
12984static
@@ -133,40 +88,14 @@ AtapSendCdb(
13388 _In_ PATA_DEVICE_REQUEST Request )
13489{
13590#if defined(ATA_SUPPORT_32_BIT_IO )
136- if ( AtapIs32BitIoSupported ( DeviceUnit ))
137- {
138- ATA_WRITE_BLOCK_32 ( DeviceUnit -> Registers . Data ,
139- Request -> Cdb ,
140- DeviceUnit -> CdbSize / sizeof ( USHORT ));
141- }
142- else
91+ ATA_WRITE_BLOCK_32 ( DeviceUnit -> Registers . Data ,
92+ Request -> Cdb ,
93+ DeviceUnit -> CdbSize / sizeof ( USHORT ));
94+ #else
95+ ATA_WRITE_BLOCK_16 ( DeviceUnit -> Registers . Data ,
96+ Request -> Cdb ,
97+ DeviceUnit -> CdbSize );
14398#endif
144- {
145- ATA_WRITE_BLOCK_16 (DeviceUnit -> Registers .Data ,
146- Request -> Cdb ,
147- DeviceUnit -> CdbSize );
148- }
149-
150- /*
151- * In polled mode (interrupts disabled)
152- * the NEC CDR-260 drive clears BSY before updating the interrupt reason register.
153- * As a workaround, we will wait for the phase change.
154- */
155- if (DeviceUnit -> P .Flags & ATA_DEVICE_IS_NEC_CDR260 )
156- {
157- ULONG i ;
158-
159- ATA_IO_WAIT ();
160-
161- for (i = 0 ; i < ATA_TIME_PHASE_CHANGE ; ++ i )
162- {
163- UCHAR InterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
164- if (InterruptReason != ATAPI_INT_REASON_COD )
165- break ;
166-
167- StallExecutionProcessor (10 );
168- }
169- }
17099}
171100
172101static
@@ -178,7 +107,7 @@ AtapPioDataIn(
178107 ByteCount = min (ByteCount , DeviceUnit -> BytesToTransfer );
179108
180109#if defined(ATA_SUPPORT_32_BIT_IO )
181- if (AtapIs32BitIoSupported ( DeviceUnit ))
110+ if (!( ByteCount & ( sizeof ( ULONG ) - 1 ) ))
182111 {
183112 ATA_READ_BLOCK_32 (DeviceUnit -> Registers .Data ,
184113 (PULONG )DeviceUnit -> DataBuffer ,
@@ -233,15 +162,38 @@ AtapSoftwareReset(
233162 StallExecutionProcessor (20 );
234163}
235164
165+ static
166+ VOID
167+ AtapDrainDeviceBuffer (
168+ _In_ PIDE_REGISTERS Registers )
169+ {
170+ UCHAR IdeStatus ;
171+ ULONG i ;
172+ DECLSPEC_ALIGN (2 ) UCHAR Buffer [2 ];
173+
174+ /* Try to clear the DRQ indication */
175+ for (i = 0 ; i < 0x10000 / sizeof (USHORT ); ++ i )
176+ {
177+ IdeStatus = ATA_READ (Registers -> Status );
178+ if (!(IdeStatus & IDE_STATUS_DRQ ))
179+ break ;
180+
181+ ATA_READ_BLOCK_16 (DeviceUnit -> Registers .Data , (PUSHORT )Buffer , 1 );
182+ }
183+ }
184+
236185static
237186BOOLEAN
238187AtapPerformSoftwareReset (
239188 _In_ PHW_DEVICE_UNIT DeviceUnit )
240189{
241190 PIDE_REGISTERS Registers = & DeviceUnit -> Registers ;
191+ UCHAR IdeStatus ;
242192
243193 ERR ("Reset device at %X:%u\n" , Registers -> Data , DeviceUnit -> DeviceNumber );
244194
195+ AtapDrainDeviceBuffer (Registers );
196+
245197 /* Perform a software reset */
246198 AtapSoftwareReset (Registers );
247199
@@ -253,7 +205,8 @@ AtapPerformSoftwareReset(
253205 }
254206
255207 /* Now wait for busy to clear */
256- if (!AtapWaitForNotBusy (Registers , ATA_TIME_BUSY_RESET , NULL ))
208+ IdeStatus = AtapWait (Registers , ATA_TIME_BUSY_RESET , IDE_STATUS_BUSY , 0 );
209+ if (IdeStatus & IDE_STATUS_BUSY )
257210 return FALSE;
258211
259212 return TRUE;
@@ -272,6 +225,23 @@ AtapProcessAtapiRequest(
272225 InterruptReason &= ATAPI_INT_REASON_MASK ;
273226 InterruptReason |= IdeStatus & IDE_STATUS_DRQ ;
274227
228+ /*
229+ * The NEC CDR-C251 drive is not fully ATAPI-compliant
230+ * and clears BSY before raising the DRQ bit and updating the interrupt reason register.
231+ * As a workaround, we will wait a bit more in the case the valid IR is not quite there yet.
232+ */
233+ if ((InterruptReason == ATAPI_INT_REASON_COD ) || (InterruptReason == ATAPI_INT_REASON_IO ))
234+ {
235+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
236+ ATA_TIME_DRQ_ASSERT ,
237+ IDE_STATUS_DRQ ,
238+ IDE_STATUS_DRQ );
239+
240+ InterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
241+ InterruptReason &= ATAPI_INT_REASON_MASK ;
242+ InterruptReason |= IdeStatus & IDE_STATUS_DRQ ;
243+ }
244+
275245 switch (InterruptReason )
276246 {
277247 case ATAPI_INT_REASON_AWAIT_CDB :
@@ -341,8 +311,20 @@ AtapProcessAtaRequest(
341311 /* Read command */
342312 if (Request -> DataBuffer )
343313 {
314+ /*
315+ * The NEC CDR-C251 drive clears BSY before raising the DRQ bit
316+ * while processing the ATAPI identify command.
317+ * Give the device a chance to assert that bit.
318+ */
344319 if (!(IdeStatus & IDE_STATUS_DRQ ))
345- return ATA_STATUS_RESET ;
320+ {
321+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
322+ ATA_TIME_DRQ_ASSERT ,
323+ IDE_STATUS_DRQ ,
324+ IDE_STATUS_DRQ );
325+ if (!(IdeStatus & IDE_STATUS_DRQ ))
326+ return ATA_STATUS_RESET ;
327+ }
346328
347329 /* Read the next data block */
348330 AtapPioDataIn (DeviceUnit , DeviceUnit -> DrqByteCount );
@@ -351,7 +333,11 @@ AtapProcessAtaRequest(
351333 return ATA_STATUS_PENDING ;
352334
353335 /* All data has been transferred, wait for DRQ to clear */
354- if (!AtapWaitForIdle (& DeviceUnit -> Registers , & IdeStatus ))
336+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
337+ ATA_TIME_DRQ_CLEAR ,
338+ IDE_STATUS_BUSY | IDE_STATUS_DRQ ,
339+ 0 );
340+ if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ ))
355341 return ATA_STATUS_RESET ;
356342
357343 if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT ))
@@ -363,16 +349,49 @@ AtapProcessAtaRequest(
363349}
364350
365351static
366- BOOLEAN
352+ UCHAR
367353AtapProcessRequest (
368354 _In_ PHW_DEVICE_UNIT DeviceUnit ,
369355 _In_ PATA_DEVICE_REQUEST Request ,
370356 _In_ UCHAR IdeStatus )
371357{
372- if ( Request -> Flags & REQUEST_FLAG_PACKET_COMMAND )
373- return AtapProcessAtapiRequest ( DeviceUnit , Request , IdeStatus );
374- else
358+ UCHAR AtaStatus ;
359+
360+ if (!( Request -> Flags & REQUEST_FLAG_PACKET_COMMAND ))
375361 return AtapProcessAtaRequest (DeviceUnit , Request , IdeStatus );
362+
363+ AtaStatus = AtapProcessAtapiRequest (DeviceUnit , Request , IdeStatus );
364+
365+ /*
366+ * In polled mode (interrupts disabled), the NEC CDR-260 drive behaves quite differently
367+ * that other devices. This drive does not raise BSY immediately
368+ * in response to a CDB or buffer write. The status and interrupt reason registers
369+ * remain invalid and unchanged for the time of media access.
370+ * As a workaround, we will wait for the phase change.
371+ */
372+ if ((DeviceUnit -> P .Flags & ATA_DEVICE_IS_NEC_CDR260 ) && (AtaStatus == ATA_STATUS_PENDING ))
373+ {
374+ UCHAR OldInterruptReason , NewInterruptReason ;
375+ ULONG i ;
376+
377+ OldInterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
378+
379+ /* Set a long timeout on purpose */
380+ for (i = ATA_TIME_BUSY_POLL ; i > 0 ; i -- )
381+ {
382+ NewInterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
383+ if (NewInterruptReason != OldInterruptReason )
384+ break ;
385+
386+ StallExecutionProcessor (10 );
387+ }
388+ if (i == 0 )
389+ {
390+ AtaStatus = ATA_STATUS_RESET ;
391+ }
392+ }
393+
394+ return AtaStatus ;
376395}
377396
378397static
@@ -438,14 +457,15 @@ AtapSendCommand(
438457 _In_ PHW_DEVICE_UNIT DeviceUnit ,
439458 _In_ PATA_DEVICE_REQUEST Request )
440459{
441- UCHAR AtaStatus ;
460+ UCHAR IdeStatus , AtaStatus ;
442461
443462 DeviceUnit -> BytesToTransfer = Request -> DataTransferLength ;
444463 DeviceUnit -> DataBuffer = Request -> DataBuffer ;
445464
446465 /* Select the device */
447466 AtapSelectDevice (& DeviceUnit -> Registers , DeviceUnit -> DeviceNumber );
448- if (!AtapWaitForNotBusy (& DeviceUnit -> Registers , ATA_TIME_BUSY_SELECT , NULL ))
467+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_SELECT , IDE_STATUS_BUSY , 0 );
468+ if (IdeStatus & IDE_STATUS_BUSY )
449469 return ATA_STATUS_RETRY ;
450470
451471 /* Always disable interrupts, otherwise it may lead to problems in underlying BIOS firmware */
@@ -458,11 +478,10 @@ AtapSendCommand(
458478
459479 while (TRUE)
460480 {
461- UCHAR IdeStatus ;
462-
463481 ATA_IO_WAIT ();
464482
465- if (!AtapWaitForNotBusy (& DeviceUnit -> Registers , ATA_TIME_BUSY_POLL , & IdeStatus ))
483+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_POLL , IDE_STATUS_BUSY , 0 );
484+ if (IdeStatus & IDE_STATUS_BUSY )
466485 return ATA_STATUS_RESET ;
467486
468487 AtaStatus = AtapProcessRequest (DeviceUnit , Request , IdeStatus );
@@ -555,10 +574,6 @@ AtapIssueCommand(
555574 return FALSE;
556575 }
557576
558- /* Turn off various things and retry the command */
559- DeviceUnit -> MultiSectorTransfer = 0 ;
560- DeviceUnit -> P .Flags &= ~ATA_DEVICE_FLAG_IO32 ;
561-
562577 if (!AtapPerformSoftwareReset (DeviceUnit ))
563578 return FALSE;
564579
@@ -738,7 +753,8 @@ AtapIsDevicePresent(
738753 if (ATA_READ (Registers -> ByteCountHigh ) != 0xAA )
739754 return FALSE;
740755
741- if (!AtapWaitForNotBusy (Registers , ATA_TIME_BUSY_ENUM , & IdeStatus ))
756+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_ENUM , IDE_STATUS_BUSY , 0 );
757+ if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ ))
742758 {
743759 ERR ("Device %X:%u is busy %02x\n" , Registers -> Data , DeviceUnit -> DeviceNumber , IdeStatus );
744760
@@ -1090,7 +1106,7 @@ AtapAtaInitDevice(
10901106 else
10911107 {
10921108 /* Using CHS addressing mode */
1093- TotalSectors = Cylinders * Heads * SectorsPerTrack ;
1109+ TotalSectors = UInt32x32To64 ( Cylinders , Heads ) * SectorsPerTrack ;
10941110 }
10951111
10961112 if (TotalSectors == 0 )
@@ -1221,6 +1237,7 @@ AtaReadLogicalSectors(
12211237 PHW_DEVICE_UNIT Unit = (PHW_DEVICE_UNIT )DeviceUnit ;
12221238 ATA_DEVICE_REQUEST Request = { 0 };
12231239
1240+ ASSERT (Unit );
12241241 ASSERT ((SectorNumber + SectorCount ) <= Unit -> P .TotalSectors );
12251242 ASSERT (SectorCount != 0 );
12261243
0 commit comments