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 ( [email protected] ) 5+ * COPYRIGHT: Copyright 2019-2026 Dmitry Borisov < [email protected] > 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,37 @@ 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+
173+ /* Try to clear the DRQ indication */
174+ for (i = 0 ; i < 0x10000 / sizeof (USHORT ); ++ i )
175+ {
176+ IdeStatus = ATA_READ (Registers -> Status );
177+ if (!(IdeStatus & IDE_STATUS_DRQ ))
178+ break ;
179+
180+ READ_PORT_USHORT ((PUSHORT )(ULONG_PTR )Registers -> Data );
181+ }
182+ }
183+
236184static
237185BOOLEAN
238186AtapPerformSoftwareReset (
239187 _In_ PHW_DEVICE_UNIT DeviceUnit )
240188{
241189 PIDE_REGISTERS Registers = & DeviceUnit -> Registers ;
190+ UCHAR IdeStatus ;
242191
243192 ERR ("Reset device at %X:%u\n" , Registers -> Data , DeviceUnit -> DeviceNumber );
244193
194+ AtapDrainDeviceBuffer (Registers );
195+
245196 /* Perform a software reset */
246197 AtapSoftwareReset (Registers );
247198
@@ -253,7 +204,8 @@ AtapPerformSoftwareReset(
253204 }
254205
255206 /* Now wait for busy to clear */
256- if (!AtapWaitForNotBusy (Registers , ATA_TIME_BUSY_RESET , NULL ))
207+ IdeStatus = AtapWait (Registers , ATA_TIME_BUSY_RESET , IDE_STATUS_BUSY , 0 );
208+ if (IdeStatus & IDE_STATUS_BUSY )
257209 return FALSE;
258210
259211 return TRUE;
@@ -272,6 +224,23 @@ AtapProcessAtapiRequest(
272224 InterruptReason &= ATAPI_INT_REASON_MASK ;
273225 InterruptReason |= IdeStatus & IDE_STATUS_DRQ ;
274226
227+ /*
228+ * The NEC CDR-C251 drive is not fully ATAPI-compliant
229+ * and clears BSY before raising the DRQ bit and updating the interrupt reason register.
230+ * As a workaround, we will wait a bit more in the case the valid IR is not quite there yet.
231+ */
232+ if ((InterruptReason == ATAPI_INT_REASON_COD ) || (InterruptReason == ATAPI_INT_REASON_IO ))
233+ {
234+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
235+ ATA_TIME_DRQ_ASSERT ,
236+ IDE_STATUS_DRQ ,
237+ IDE_STATUS_DRQ );
238+
239+ InterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
240+ InterruptReason &= ATAPI_INT_REASON_MASK ;
241+ InterruptReason |= IdeStatus & IDE_STATUS_DRQ ;
242+ }
243+
275244 switch (InterruptReason )
276245 {
277246 case ATAPI_INT_REASON_AWAIT_CDB :
@@ -341,8 +310,20 @@ AtapProcessAtaRequest(
341310 /* Read command */
342311 if (Request -> DataBuffer )
343312 {
313+ /*
314+ * The NEC CDR-C251 drive clears BSY before raising the DRQ bit
315+ * while processing the ATAPI identify command.
316+ * Give the device a chance to assert that bit.
317+ */
344318 if (!(IdeStatus & IDE_STATUS_DRQ ))
345- return ATA_STATUS_RESET ;
319+ {
320+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
321+ ATA_TIME_DRQ_ASSERT ,
322+ IDE_STATUS_DRQ ,
323+ IDE_STATUS_DRQ );
324+ if (!(IdeStatus & IDE_STATUS_DRQ ))
325+ return ATA_STATUS_RESET ;
326+ }
346327
347328 /* Read the next data block */
348329 AtapPioDataIn (DeviceUnit , DeviceUnit -> DrqByteCount );
@@ -351,7 +332,11 @@ AtapProcessAtaRequest(
351332 return ATA_STATUS_PENDING ;
352333
353334 /* All data has been transferred, wait for DRQ to clear */
354- if (!AtapWaitForIdle (& DeviceUnit -> Registers , & IdeStatus ))
335+ IdeStatus = AtapWait (& DeviceUnit -> Registers ,
336+ ATA_TIME_DRQ_CLEAR ,
337+ IDE_STATUS_BUSY | IDE_STATUS_DRQ ,
338+ 0 );
339+ if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ ))
355340 return ATA_STATUS_RESET ;
356341
357342 if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT ))
@@ -363,16 +348,49 @@ AtapProcessAtaRequest(
363348}
364349
365350static
366- BOOLEAN
351+ UCHAR
367352AtapProcessRequest (
368353 _In_ PHW_DEVICE_UNIT DeviceUnit ,
369354 _In_ PATA_DEVICE_REQUEST Request ,
370355 _In_ UCHAR IdeStatus )
371356{
372- if ( Request -> Flags & REQUEST_FLAG_PACKET_COMMAND )
373- return AtapProcessAtapiRequest ( DeviceUnit , Request , IdeStatus );
374- else
357+ UCHAR AtaStatus ;
358+
359+ if (!( Request -> Flags & REQUEST_FLAG_PACKET_COMMAND ))
375360 return AtapProcessAtaRequest (DeviceUnit , Request , IdeStatus );
361+
362+ AtaStatus = AtapProcessAtapiRequest (DeviceUnit , Request , IdeStatus );
363+
364+ /*
365+ * In polled mode (interrupts disabled), the NEC CDR-260 drive behaves quite differently
366+ * that other devices. This drive does not raise BSY immediately
367+ * in response to a CDB or buffer write. The status and interrupt reason registers
368+ * remain invalid and unchanged for the time of media access.
369+ * As a workaround, we will wait for the phase change.
370+ */
371+ if ((DeviceUnit -> P .Flags & ATA_DEVICE_IS_NEC_CDR260 ) && (AtaStatus == ATA_STATUS_PENDING ))
372+ {
373+ UCHAR OldInterruptReason , NewInterruptReason ;
374+ ULONG i ;
375+
376+ OldInterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
377+
378+ /* Set a long timeout on purpose */
379+ for (i = ATA_TIME_BUSY_POLL ; i > 0 ; i -- )
380+ {
381+ NewInterruptReason = ATA_READ (DeviceUnit -> Registers .InterruptReason );
382+ if (NewInterruptReason != OldInterruptReason )
383+ break ;
384+
385+ StallExecutionProcessor (10 );
386+ }
387+ if (i == 0 )
388+ {
389+ AtaStatus = ATA_STATUS_RESET ;
390+ }
391+ }
392+
393+ return AtaStatus ;
376394}
377395
378396static
@@ -438,14 +456,15 @@ AtapSendCommand(
438456 _In_ PHW_DEVICE_UNIT DeviceUnit ,
439457 _In_ PATA_DEVICE_REQUEST Request )
440458{
441- UCHAR AtaStatus ;
459+ UCHAR IdeStatus , AtaStatus ;
442460
443461 DeviceUnit -> BytesToTransfer = Request -> DataTransferLength ;
444462 DeviceUnit -> DataBuffer = Request -> DataBuffer ;
445463
446464 /* Select the device */
447465 AtapSelectDevice (& DeviceUnit -> Registers , DeviceUnit -> DeviceNumber );
448- if (!AtapWaitForNotBusy (& DeviceUnit -> Registers , ATA_TIME_BUSY_SELECT , NULL ))
466+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_SELECT , IDE_STATUS_BUSY , 0 );
467+ if (IdeStatus & IDE_STATUS_BUSY )
449468 return ATA_STATUS_RETRY ;
450469
451470 /* Always disable interrupts, otherwise it may lead to problems in underlying BIOS firmware */
@@ -458,11 +477,10 @@ AtapSendCommand(
458477
459478 while (TRUE)
460479 {
461- UCHAR IdeStatus ;
462-
463480 ATA_IO_WAIT ();
464481
465- if (!AtapWaitForNotBusy (& DeviceUnit -> Registers , ATA_TIME_BUSY_POLL , & IdeStatus ))
482+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_POLL , IDE_STATUS_BUSY , 0 );
483+ if (IdeStatus & IDE_STATUS_BUSY )
466484 return ATA_STATUS_RESET ;
467485
468486 AtaStatus = AtapProcessRequest (DeviceUnit , Request , IdeStatus );
@@ -555,10 +573,6 @@ AtapIssueCommand(
555573 return FALSE;
556574 }
557575
558- /* Turn off various things and retry the command */
559- DeviceUnit -> MultiSectorTransfer = 0 ;
560- DeviceUnit -> P .Flags &= ~ATA_DEVICE_FLAG_IO32 ;
561-
562576 if (!AtapPerformSoftwareReset (DeviceUnit ))
563577 return FALSE;
564578
@@ -738,7 +752,8 @@ AtapIsDevicePresent(
738752 if (ATA_READ (Registers -> ByteCountHigh ) != 0xAA )
739753 return FALSE;
740754
741- if (!AtapWaitForNotBusy (Registers , ATA_TIME_BUSY_ENUM , & IdeStatus ))
755+ IdeStatus = AtapWait (& DeviceUnit -> Registers , ATA_TIME_BUSY_ENUM , IDE_STATUS_BUSY , 0 );
756+ if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ ))
742757 {
743758 ERR ("Device %X:%u is busy %02x\n" , Registers -> Data , DeviceUnit -> DeviceNumber , IdeStatus );
744759
@@ -1090,7 +1105,7 @@ AtapAtaInitDevice(
10901105 else
10911106 {
10921107 /* Using CHS addressing mode */
1093- TotalSectors = Cylinders * Heads * SectorsPerTrack ;
1108+ TotalSectors = ( ULONG64 ) Cylinders * Heads * SectorsPerTrack ;
10941109 }
10951110
10961111 if (TotalSectors == 0 )
@@ -1221,6 +1236,7 @@ AtaReadLogicalSectors(
12211236 PHW_DEVICE_UNIT Unit = (PHW_DEVICE_UNIT )DeviceUnit ;
12221237 ATA_DEVICE_REQUEST Request = { 0 };
12231238
1239+ ASSERT (Unit );
12241240 ASSERT ((SectorNumber + SectorCount ) <= Unit -> P .TotalSectors );
12251241 ASSERT (SectorCount != 0 );
12261242
0 commit comments