2 Copyright © 2004-2009, The AROS Development Team. All rights reserved
11 * ---------- ------------------ -------------------------------------------------------------------
12 * 2008-01-25 T. Wiszkowski Rebuilt, rearranged and partially fixed 60% of the code here
13 * Enabled implementation to scan for other PCI IDE controllers
14 * Implemented ATAPI Packet Support for both read and write
15 * Corrected ATAPI DMA handling
16 * Fixed major IDE enumeration bugs severely handicapping transfers with more than one controller
17 * Compacted source and implemented major ATA support procedure
18 * Improved DMA and Interrupt management
19 * Removed obsolete code
20 * 2008-03-30 T. Wiszkowski Added workaround for interrupt collision handling; fixed SATA in LEGACY mode.
21 * nForce and Intel SATA chipsets should now be operational.
22 * 2008-04-03 T. Wiszkowski Fixed IRQ flood issue, eliminated and reduced obsolete / redundant code
23 * 2008-04-05 T. Wiszkowski Improved IRQ management
24 * 2008-04-07 T. Wiszkowski Changed bus timeout mechanism
25 * 2008-04-20 T. Wiszkowski Corrected the flaw in drive identification routines leading to ocassional system hangups
26 * 2008-05-11 T. Wiszkowski Remade the ata trannsfers altogether, corrected the pio/irq handling
27 * medium removal, device detection, bus management and much more
28 * 2009-03-05 T. Wiszkowski remade timeouts, added timer-based and benchmark-based delays.
32 #include <aros/debug.h>
34 #include <exec/types.h>
35 #include <exec/exec.h>
36 #include <exec/resident.h>
37 #include <utility/utility.h>
38 #include <utility/tagitem.h>
44 #include <proto/exec.h>
45 #include <proto/oop.h>
48 #include LC_LIBDEFS_FILE
50 //---------------------------IO Commands---------------------------------------
52 /* Invalid comand does nothing, complains only. */
53 static void cmd_Invalid(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
55 D(bug("[ATA%02ld] cmd_Invalid: %d\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, io
->io_Command
));
56 io
->io_Error
= IOERR_NOCMD
;
59 /* Don't need to reset the drive? */
60 static void cmd_Reset(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
62 IOStdReq(io
)->io_Actual
= 0;
65 /* CMD_READ implementation */
66 static void cmd_Read32(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
68 struct ata_Unit
*unit
= (struct ata_Unit
*)IOStdReq(io
)->io_Unit
;
70 if (AF_Removable
== (unit
->au_Flags
& (AF_Removable
| AF_DiscPresent
)))
72 bug("[ATA%02ld] cmd_Read32: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit
->au_UnitNum
);
73 io
->io_Error
= TDERR_DiskChanged
;
77 ULONG block
= IOStdReq(io
)->io_Offset
;
78 ULONG count
= IOStdReq(io
)->io_Length
;
80 D(bug("[ATA%02ld] cmd_Read32()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
82 ULONG mask
= (1 << unit
->au_SectorShift
) - 1;
85 During this IO call it should be sure that both offset and
86 length are already aligned properly to sector boundaries.
88 if ((block
& mask
) | (count
& mask
))
90 D(bug("[ATA%02ld] cmd_Read32: offset or length not sector-aligned.\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
91 cmd_Invalid(io
, LIBBASE
);
95 block
>>= unit
->au_SectorShift
;
96 count
>>= unit
->au_SectorShift
;
99 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
)) && ((block
+ count
) > unit
->au_Capacity
))
101 bug("[ATA%02ld] cmd_Read32: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, block
, count
, unit
->au_Capacity
);
102 io
->io_Error
= IOERR_BADADDRESS
;
106 /* Call the Unit's access funtion */
107 io
->io_Error
= unit
->au_Read32(unit
, block
, count
,
108 IOStdReq(io
)->io_Data
, &cnt
);
110 IOStdReq(io
)->io_Actual
= cnt
;
115 NSCMD_TD_READ64, TD_READ64 implementation. Basically the same, just packs
116 the 64 bit offset in both io_Offset (31:0) and io_Actual (63:32)
118 static void cmd_Read64(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
120 struct ata_Unit
*unit
= (struct ata_Unit
*)IOStdReq(io
)->io_Unit
;
122 if (AF_Removable
== (unit
->au_Flags
& (AF_Removable
| AF_DiscPresent
)))
124 bug("[ATA%02ld] cmd_Read64: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit
->au_UnitNum
);
125 io
->io_Error
= TDERR_DiskChanged
;
129 UQUAD block
= (UQUAD
)(IOStdReq(io
)->io_Offset
& 0xffffffff) |
130 ((UQUAD
)(IOStdReq(io
)->io_Actual
)) << 32;
131 ULONG count
= IOStdReq(io
)->io_Length
;
133 D(bug("[ATA%02ld] cmd_Read64()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
135 ULONG mask
= (1 << unit
->au_SectorShift
) - 1;
137 if ((block
& (UQUAD
)mask
) | (count
& mask
) | (count
== 0))
139 D(bug("[ATA%02ld] cmd_Read64: offset or length not sector-aligned.\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
140 cmd_Invalid(io
, LIBBASE
);
144 block
>>= unit
->au_SectorShift
;
145 count
>>= unit
->au_SectorShift
;
149 If the sum of sector offset and the sector count doesn't overflow
150 the 28-bit LBA address, use 32-bit access for speed and simplicity.
151 Otherwise do the 48-bit LBA addressing.
153 if ((block
+ count
) < 0x0fffffff)
155 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
)) && ((block
+ count
) > unit
->au_Capacity
))
157 bug("[ATA%02ld] cmd_Read64: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, block
, count
, unit
->au_Capacity
);
158 io
->io_Error
= IOERR_BADADDRESS
;
161 io
->io_Error
= unit
->au_Read32(unit
, (ULONG
)(block
& 0x0fffffff), count
, IOStdReq(io
)->io_Data
, &cnt
);
165 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
)) && ((block
+ count
) > unit
->au_Capacity48
))
167 bug("[ATA%02ld] cmd_Read64: Requested block (%lx:%08lx;%ld) outside disk range (%lx:%08lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, block
>>32, block
&0xfffffffful
, count
, unit
->au_Capacity48
>>32, unit
->au_Capacity48
& 0xfffffffful
);
168 io
->io_Error
= IOERR_BADADDRESS
;
172 io
->io_Error
= unit
->au_Read64(unit
, block
, count
, IOStdReq(io
)->io_Data
, &cnt
);
175 IOStdReq(io
)->io_Actual
= cnt
;
179 /* CMD_WRITE implementation */
180 static void cmd_Write32(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
182 struct ata_Unit
*unit
= (struct ata_Unit
*)IOStdReq(io
)->io_Unit
;
184 if (AF_Removable
== (unit
->au_Flags
& (AF_Removable
| AF_DiscPresent
)))
186 bug("[ATA%02ld] cmd_Write32: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit
->au_UnitNum
);
187 io
->io_Error
= TDERR_DiskChanged
;
191 ULONG block
= IOStdReq(io
)->io_Offset
;
192 ULONG count
= IOStdReq(io
)->io_Length
;
194 D(bug("[ATA%02ld] cmd_Write32()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
196 ULONG mask
= (1 << unit
->au_SectorShift
) - 1;
199 During this IO call it should be sure that both offset and
200 length are already aligned properly to sector boundaries.
202 if ((block
& mask
) | (count
& mask
))
204 D(bug("[ATA%02ld] cmd_Write32: offset or length not sector-aligned.\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
205 cmd_Invalid(io
, LIBBASE
);
209 block
>>= unit
->au_SectorShift
;
210 count
>>= unit
->au_SectorShift
;
213 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
))
214 && ((block
+ count
) > unit
->au_Capacity
))
216 bug("[ATA%02ld] cmd_Write32: Requested block (%lx;%ld) outside disk range (%lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
,
217 block
, count
, unit
->au_Capacity
);
218 io
->io_Error
= IOERR_BADADDRESS
;
222 /* Call the Unit's access funtion */
223 io
->io_Error
= unit
->au_Write32(unit
, block
, count
,
224 IOStdReq(io
)->io_Data
, &cnt
);
226 IOStdReq(io
)->io_Actual
= cnt
;
231 NSCMD_TD_WRITE64, TD_WRITE64 implementation. Basically the same, just packs
232 the 64 bit offset in both io_Offset (31:0) and io_Actual (63:32)
234 static void cmd_Write64(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
236 struct ata_Unit
*unit
= (struct ata_Unit
*)IOStdReq(io
)->io_Unit
;
238 if (AF_Removable
== (unit
->au_Flags
& (AF_Removable
| AF_DiscPresent
)))
240 bug("[ATA%02ld] cmd_Write64: USUALLY YOU'D WANT TO CHECK IF DISC IS PRESENT FIRST\n", unit
->au_UnitNum
);
241 io
->io_Error
= TDERR_DiskChanged
;
246 UQUAD block
= IOStdReq(io
)->io_Offset
| (UQUAD
)(IOStdReq(io
)->io_Actual
) << 32;
247 ULONG count
= IOStdReq(io
)->io_Length
;
249 D(bug("[ATA%02ld] cmd_Write64()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
251 ULONG mask
= (1 << unit
->au_SectorShift
) - 1;
253 if ((block
& mask
) | (count
& mask
) | (count
==0))
255 D(bug("[ATA%02ld] cmd_Write64: offset or length not sector-aligned.\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
256 cmd_Invalid(io
, LIBBASE
);
260 block
>>= unit
->au_SectorShift
;
261 count
>>= unit
->au_SectorShift
;
265 If the sum of sector offset and the sector count doesn't overflow
266 the 28-bit LBA address, use 32-bit access for speed and simplicity.
267 Otherwise do the 48-bit LBA addressing.
269 if ((block
+ count
) < 0x0fffffff)
271 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
))
272 && ((block
+ count
) > unit
->au_Capacity
))
274 bug("[ATA%02ld] cmd_Write64: Requested block (%lx;%ld) outside disk range "
275 "(%lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, block
, count
, unit
->au_Capacity
);
276 io
->io_Error
= IOERR_BADADDRESS
;
279 io
->io_Error
= unit
->au_Write32(unit
, (ULONG
)(block
& 0x0fffffff),
280 count
, IOStdReq(io
)->io_Data
, &cnt
);
284 if ((0 == (unit
->au_XferModes
& AF_XFER_PACKET
))
285 && ((block
+ count
) > unit
->au_Capacity48
))
287 bug("[ATA%02ld] cmd_Write64: Requested block (%lx:%08lx;%ld) outside disk "
288 "range (%lx:%08lx)\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
,
289 block
>>32, block
&0xfffffffful
,
290 count
, unit
->au_Capacity48
>>32,
291 unit
->au_Capacity48
& 0xfffffffful
);
292 io
->io_Error
= IOERR_BADADDRESS
;
296 io
->io_Error
= unit
->au_Write64(unit
, block
, count
,
297 IOStdReq(io
)->io_Data
, &cnt
);
299 IOStdReq(io
)->io_Actual
= cnt
;
304 /* use CMD_FLUSH to force all IO waiting commands to abort */
305 static void cmd_Flush(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
307 struct IORequest
*msg
;
308 struct ata_Bus
*bus
= ((struct ata_Unit
*)io
->io_Unit
)->au_Bus
;
310 D(bug("[ATA%02ld] cmd_Flush()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
314 while((msg
= (struct IORequest
*)GetMsg((struct MsgPort
*)bus
->ab_MsgPort
)))
316 msg
->io_Error
= IOERR_ABORTED
;
317 ReplyMsg((struct Message
*)msg
);
324 Internal command used to check whether the media in drive has been changed
325 since last call. If so, the handlers given by user are called.
327 static void cmd_TestChanged(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
329 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
330 struct IORequest
*msg
;
332 D(bug("[ATA%02ld] cmd_TestChanged()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
334 if ((unit
->au_XferModes
& AF_XFER_PACKET
) && (unit
->au_Flags
& AF_Removable
))
336 atapi_TestUnitOK(unit
);
337 if (unit
->au_Flags
& AF_DiscChanged
)
339 unit
->au_ChangeNum
++;
343 /* old-fashioned RemoveInt call first */
344 if (unit
->au_RemoveInt
)
345 Cause(unit
->au_RemoveInt
);
347 /* And now the whole list of possible calls */
348 ForeachNode(&unit
->au_SoftList
, msg
)
350 Cause((struct Interrupt
*)IOStdReq(msg
)->io_Data
);
353 unit
->au_Flags
&= ~AF_DiscChanged
;
360 static void cmd_Update(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
362 /* Do nothing now. In near future there should be drive cache flush though */
363 D(bug("[ATA%02ld] cmd_Update()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
366 static void cmd_Remove(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
368 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
370 D(bug("[ATA%02ld] cmd_Remove()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
372 if (unit
->au_RemoveInt
)
373 io
->io_Error
= TDERR_DriveInUse
;
375 unit
->au_RemoveInt
= IOStdReq(io
)->io_Data
;
378 static void cmd_ChangeNum(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
380 D(bug("[ATA%02ld] cmd_ChangeNum()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
382 IOStdReq(io
)->io_Actual
= ((struct ata_Unit
*)io
->io_Unit
)->au_ChangeNum
;
385 static void cmd_ChangeState(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
387 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
389 D(bug("[ATA%02ld] cmd_ChangeState()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
391 if (unit
->au_Flags
& AF_DiscPresent
)
392 IOStdReq(io
)->io_Actual
= 0;
394 IOStdReq(io
)->io_Actual
= 1;
396 D(bug("[ATA%02ld] cmd_ChangeState: Media %s\n", unit
->au_UnitNum
, IOStdReq(io
)->io_Actual
? "ABSENT" : "PRESENT"));
399 static void cmd_ProtStatus(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
401 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
403 D(bug("[ATA%02ld] cmd_ProtStatus()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
405 if (unit
->au_DevType
)
406 IOStdReq(io
)->io_Actual
= -1;
408 IOStdReq(io
)->io_Actual
= 0;
412 static void cmd_GetNumTracks(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
414 D(bug("[ATA%02ld] cmd_GetNumTracks()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
416 IOStdReq(io
)->io_Actual
= ((struct ata_Unit
*)io
->io_Unit
)->au_Cylinders
;
419 static void cmd_AddChangeInt(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
421 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
423 D(bug("[ATA%02ld] cmd_AddChangeInt()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
426 AddHead(&unit
->au_SoftList
, (struct Node
*)io
);
429 io
->io_Flags
&= ~IOF_QUICK
;
430 unit
->au_Unit
.unit_flags
&= ~UNITF_ACTIVE
;
433 static void cmd_RemChangeInt(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
435 D(bug("[ATA%02ld] cmd_RemChangeInt()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
438 Remove((struct Node
*)io
);
442 static void cmd_Eject(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
444 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
446 D(bug("[ATA%02ld] cmd_Eject()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
448 IOStdReq(io
)->io_Error
= unit
->au_Eject(unit
);
451 static void cmd_GetGeometry(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
453 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
455 D(bug("[ATA%02ld] cmd_GetGeometry()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
457 if (IOStdReq(io
)->io_Length
== sizeof(struct DriveGeometry
))
459 struct DriveGeometry
*dg
= (struct DriveGeometry
*)IOStdReq(io
)->io_Data
;
461 dg
->dg_SectorSize
= 1 << unit
->au_SectorShift
;
463 if (unit
->au_Capacity48
!= 0)
465 if ((unit
->au_Capacity48
>> 32) != 0)
466 dg
->dg_TotalSectors
= 0xffffffff;
468 dg
->dg_TotalSectors
= unit
->au_Capacity48
;
471 dg
->dg_TotalSectors
= unit
->au_Capacity
;
473 dg
->dg_Cylinders
= unit
->au_Cylinders
;
474 dg
->dg_CylSectors
= unit
->au_Sectors
* unit
->au_Heads
;
475 dg
->dg_Heads
= unit
->au_Heads
;
476 dg
->dg_TrackSectors
= unit
->au_Sectors
;
477 dg
->dg_BufMemType
= MEMF_PUBLIC
;
478 dg
->dg_DeviceType
= unit
->au_DevType
;
479 dg
->dg_Flags
= (unit
->au_Flags
& AF_Removable
) ? DGF_REMOVABLE
: 0;
482 IOStdReq(io
)->io_Actual
= sizeof(struct DriveGeometry
);
484 else if (IOStdReq(io
)->io_Length
== 514)
486 CopyMemQuick(unit
->au_Drive
, IOStdReq(io
)->io_Data
, 512);
488 else io
->io_Error
= TDERR_NotSpecified
;
491 static void cmd_DirectScsi(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
493 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
495 D(bug("[ATA%02ld] cmd_DirectScsi()\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
497 IOStdReq(io
)->io_Actual
= sizeof(struct SCSICmd
);
498 if (unit
->au_XferModes
& AF_XFER_PACKET
)
500 io
->io_Error
= unit
->au_DirectSCSI(unit
, (struct SCSICmd
*)IOStdReq(io
)->io_Data
);
502 else io
->io_Error
= IOERR_BADADDRESS
;
505 //-----------------------------------------------------------------------------
508 command translation tables - used to call proper IO functions.
511 #define N_TD_READ64 0
512 #define N_TD_WRITE64 1
513 #define N_TD_SEEK64 2
514 #define N_TD_FORMAT64 3
516 static void (*map64
[])(struct IORequest
*, LIBBASETYPEPTR
) = {
517 [N_TD_READ64
] = cmd_Read64
,
518 [N_TD_WRITE64
] = cmd_Write64
,
519 [N_TD_SEEK64
] = cmd_Reset
,
520 [N_TD_FORMAT64
] = cmd_Write64
523 static void (*map32
[])(struct IORequest
*, LIBBASETYPEPTR
) = {
524 [CMD_INVALID
] = cmd_Invalid
,
525 [CMD_RESET
] = cmd_Reset
,
526 [CMD_READ
] = cmd_Read32
,
527 [CMD_WRITE
] = cmd_Write32
,
528 [CMD_UPDATE
] = cmd_Update
,
529 [CMD_CLEAR
] = cmd_Reset
,
530 [CMD_STOP
] = cmd_Reset
,
531 [CMD_START
] = cmd_Reset
,
532 [CMD_FLUSH
] = cmd_Flush
,
533 [TD_MOTOR
] = cmd_Reset
,
534 [TD_SEEK
] = cmd_Reset
,
535 [TD_FORMAT
] = cmd_Write32
,
536 [TD_REMOVE
] = cmd_Remove
,
537 [TD_CHANGENUM
] = cmd_ChangeNum
,
538 [TD_CHANGESTATE
]= cmd_ChangeState
,
539 [TD_PROTSTATUS
] = cmd_ProtStatus
,
540 [TD_RAWREAD
] = cmd_Invalid
,
541 [TD_RAWWRITE
] = cmd_Invalid
,
542 [TD_GETNUMTRACKS
] = cmd_GetNumTracks
,
543 [TD_ADDCHANGEINT
] = cmd_AddChangeInt
,
544 [TD_REMCHANGEINT
] = cmd_RemChangeInt
,
545 [TD_GETGEOMETRY
]= cmd_GetGeometry
,
546 [TD_EJECT
] = cmd_Eject
,
547 [TD_READ64
] = cmd_Read64
,
548 [TD_WRITE64
] = cmd_Write64
,
549 [TD_SEEK64
] = cmd_Reset
,
550 [TD_FORMAT64
] = cmd_Write64
,
551 [HD_SCSICMD
] = cmd_DirectScsi
,
552 [HD_SCSICMD
+1] = cmd_TestChanged
,
555 static const UWORD NSDSupported
[] = {
591 Do proper IO actions depending on the request. It's called from the bus
592 tasks and from BeginIO in case of immediate commands.
594 static void HandleIO(struct IORequest
*io
, LIBBASETYPEPTR LIBBASE
)
598 /* Handle few commands directly here */
599 switch (io
->io_Command
)
602 New Style Devices query. Introduce self as trackdisk and provide list of
605 case NSCMD_DEVICEQUERY
:
607 struct NSDeviceQueryResult
*nsdq
= (struct NSDeviceQueryResult
*)IOStdReq(io
)->io_Data
;
608 nsdq
->DevQueryFormat
= 0;
609 nsdq
->SizeAvailable
= sizeof(struct NSDeviceQueryResult
);
610 nsdq
->DeviceType
= NSDEVTYPE_TRACKDISK
;
611 nsdq
->DeviceSubType
= 0;
612 nsdq
->SupportedCommands
= (UWORD
*)NSDSupported
;
614 IOStdReq(io
)->io_Actual
= sizeof(struct NSDeviceQueryResult
);
618 New Style Devices report here the 'NSTY' - only if such value is
619 returned here, the NSCMD_DEVICEQUERY might be called. Otherwice it should
622 case TD_GETDRIVETYPE
:
623 IOStdReq(io
)->io_Actual
= DRIVE_NEWSTYLE
;
627 Call all other commands using the command pointer tables for 32- and
628 64-bit accesses. If requested function is defined call it, otherwise
629 make the function cmd_Invalid.
632 if (io
->io_Command
<= (HD_SCSICMD
+1))
634 if (map32
[io
->io_Command
])
635 map32
[io
->io_Command
](io
, LIBBASE
);
637 cmd_Invalid(io
, LIBBASE
);
639 else if (io
->io_Command
>= NSCMD_TD_READ64
&& io
->io_Command
<= NSCMD_TD_FORMAT64
)
641 if (map64
[io
->io_Command
- NSCMD_TD_READ64
])
642 map64
[io
->io_Command
- NSCMD_TD_READ64
](io
, LIBBASE
);
644 cmd_Invalid(io
, LIBBASE
);
646 else cmd_Invalid(io
, LIBBASE
);
652 static const ULONG IMMEDIATE_COMMANDS
= 0x803ff1e3; // 10000000001111111111000111100011
654 /* See whether the command can be done quick */
655 BOOL
isSlow(ULONG comm
)
657 BOOL slow
= TRUE
; /* Assume always slow command */
659 /* For commands with numbers <= 31 check the mask */
662 if (IMMEDIATE_COMMANDS
& (1 << comm
))
665 else if (comm
== NSCMD_TD_SEEK64
) slow
= FALSE
;
671 Try to do IO commands. All commands which require talking with ata devices
672 will be handled slow, that is they will be passed to bus task which will
673 execute them as soon as hardware will be free.
675 AROS_LH1(void, BeginIO
,
676 AROS_LHA(struct IORequest
*, io
, A1
),
677 LIBBASETYPEPTR
, LIBBASE
, 5, ata
)
681 struct ata_Unit
*unit
= (struct ata_Unit
*)io
->io_Unit
;
683 io
->io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
685 /* Disable interrupts for a while to modify message flags */
688 D(bug("[ATA%02ld] BeginIO: Executing IO Command %lx\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
, io
->io_Command
));
691 If the command is not-immediate, or presence of disc is still unknown,
692 let the bus task do the job.
694 if (isSlow(io
->io_Command
))
696 unit
->au_Unit
.unit_flags
|= UNITF_ACTIVE
| UNITF_INTASK
;
697 io
->io_Flags
&= ~IOF_QUICK
;
700 /* Put the message to the bus */
701 PutMsg(unit
->au_Bus
->ab_MsgPort
, (struct Message
*)io
);
705 /* Immediate command. Mark unit as active and do the command directly */
706 unit
->au_Unit
.unit_flags
|= UNITF_ACTIVE
;
708 HandleIO(io
, LIBBASE
);
710 unit
->au_Unit
.unit_flags
&= ~UNITF_ACTIVE
;
713 If the command was not intended to be immediate and it was not the
714 TD_ADDCHANGEINT, reply to confirm command execution now.
716 if (!(io
->io_Flags
& IOF_QUICK
) && (io
->io_Command
!= TD_ADDCHANGEINT
))
718 ReplyMsg((struct Message
*)io
);
722 D(bug("[ATA%02ld] BeginIO: Done\n", ((struct ata_Unit
*)io
->io_Unit
)->au_UnitNum
));
726 AROS_LH1(LONG
, AbortIO
,
727 AROS_LHA(struct IORequest
*, io
, A1
),
728 LIBBASETYPEPTR
, LIBBASE
, 6, ata
)
732 /* Cannot Abort IO */
738 AROS_LH1(ULONG
, GetRdskLba
,
739 AROS_LHA(struct IORequest
*, io
, A1
),
740 LIBBASETYPEPTR
, LIBBASE
, 7, ata
)
749 AROS_LH1(ULONG
, GetBlkSize
,
750 AROS_LHA(struct IORequest
*, io
, A1
),
751 LIBBASETYPEPTR
, LIBBASE
, 8, ata
)
755 return Unit(io
)->au_SectorShift
;
761 The daemon of ata.device first opens all ATAPI devices and then enters
762 endless loop. Every 3 seconds it tells ATAPI units to check the media
763 presence. In case of any state change they will rise user-specified
766 static void DaemonCode(LIBBASETYPEPTR LIBBASE
);
768 /* Create the daemon task */
769 int ata_InitDaemonTask(LIBBASETYPEPTR LIBBASE
)
774 struct TagItem tags
[] = {
775 { TASKTAG_ARG1
, (IPTR
)LIBBASE
},
779 /* Get some memory */
780 t
= AllocMem(sizeof(struct Task
), MEMF_PUBLIC
| MEMF_CLEAR
);
781 ml
= AllocMem(sizeof(struct MemList
) + sizeof(struct MemEntry
), MEMF_PUBLIC
| MEMF_CLEAR
);
785 UBYTE
*sp
= AllocMem(STACK_SIZE
, MEMF_PUBLIC
| MEMF_CLEAR
);
787 t
->tc_SPUpper
= sp
+ STACK_SIZE
;
788 t
->tc_SPReg
= (UBYTE
*)t
->tc_SPUpper
- SP_OFFSET
- sizeof(APTR
);
790 ml
->ml_NumEntries
= 2;
791 ml
->ml_ME
[0].me_Addr
= t
;
792 ml
->ml_ME
[0].me_Length
= sizeof(struct Task
);
793 ml
->ml_ME
[1].me_Addr
= sp
;
794 ml
->ml_ME
[1].me_Length
= STACK_SIZE
;
796 NEWLIST(&t
->tc_MemEntry
);
797 AddHead(&t
->tc_MemEntry
, &ml
->ml_Node
);
799 t
->tc_Node
.ln_Name
= "ATA.daemon";
800 t
->tc_Node
.ln_Type
= NT_TASK
;
801 t
->tc_Node
.ln_Pri
= TASK_PRI
- 1; /* The daemon should have a little bit lower Pri as handler tasks */
803 LIBBASE
->ata_Daemon
= t
;
805 NewAddTask(t
, DaemonCode
, NULL
, (struct TagItem
*)&tags
);
812 * The daemon tries to send HD_SCSICMD+1 command (internal testchanged
813 * command) to all ATAPI devices in the system. They should already handle
814 * the command further.
816 void DaemonCode(LIBBASETYPEPTR LIBBASE
)
818 struct MsgPort
*myport
; // Message port used with ata.device
819 struct IORequest
*timer
; // timer
820 struct IOStdReq
*ios
[64]; // Placeholer for unit messages
824 D(bug("[ATA**] You woke up DAEMON\n"));
827 * Prepare message ports and timer.device's request
829 myport
= CreateMsgPort();
830 timer
= ata_OpenTimer();
831 bus
= (struct ata_Bus
*)LIBBASE
->ata_Buses
.mlh_Head
;
834 * grab all buses, see if there is an atapi cdrom connected or anything alike
835 * TODO: good thing to consider putting extra code here for future hotplug support *if any*
837 while (bus
->ab_Node
.mln_Succ
!= NULL
)
840 D(bug("[ATA++] Checking bus %d\n", b
));
842 for (d
=0; d
< MAX_BUSUNITS
; d
++)
847 D(bug("[ATA++] Unit %d is of type %x\n", (b
<<1)+d
, bus
->ab_Dev
[d
]));
849 if (bus
->ab_Dev
[d
] >= DEV_ATAPI
)
852 * Atapi device found. Create IOStdReq for it
854 ios
[count
] = (struct IOStdReq
*) CreateIORequest(myport
, sizeof(struct IOStdReq
));
856 ios
[count
]->io_Command
= HD_SCSICMD
+ 1;
859 * And force OpenDevice call. Don't use direct call as it's unsafe
860 * and not nice at all.
862 * so THIS is an OpenDevice().....
864 D(bug("[ATA++] Opening ATAPI device, unit %d\n", (b
<<1)|d
));
866 AROS_LCA(struct IORequest
*, (struct IORequest
*)(ios
[count
]), A1
),
867 AROS_LCA(ULONG
, (b
<< 1) | d
, D0
),
868 AROS_LCA(ULONG
, 0, D1
),
869 LIBBASETYPEPTR
, LIBBASE
, 1, ata
);
872 * increase amount of ATAPI devices in system
878 * INFO: we are only handling up to 64 atapi devices here
880 if (count
== sizeof(ios
) / sizeof(*ios
))
884 bus
= (struct ata_Bus
*)bus
->ab_Node
.mln_Succ
;
887 D(bug("[ATA++] Starting sweep medium presence detection\n"));
895 * call separate IORequest for every ATAPI device
896 * we're calling HD_SCSICMD+1 command here -- anything like test unit ready?
900 D(bug("[ATA++] Detecting media presence\n"));
901 for (d
=0; d
< count
; d
++)
902 DoIO((struct IORequest
*)ios
[d
]);
906 * check / trigger all buses waiting for an irq
908 ForeachNode(&LIBBASE
->ata_Buses
, bus
)
910 if (bus
->ab_Timeout
>= 0)
912 if (0 > (--bus
->ab_Timeout
))
914 Signal(bus
->ab_Task
, SIGBREAKF_CTRL_C
);
920 * And then hide and wait for 1 second
922 ata_WaitTO(timer
, 1, 0, 0);
926 static void TaskCode(struct ata_Bus
*, struct Task
*, struct SignalSemaphore
*);
927 static void ata_Interrupt(HIDDT_IRQ_Handler
*, HIDDT_IRQ_HwInfo
*);
930 * Make a task for given bus alive.
932 int ata_InitBusTask(struct ata_Bus
*bus
, struct SignalSemaphore
*ready
)
937 struct TagItem tags
[] = {
938 { TASKTAG_ARG1
, (IPTR
)bus
},
939 { TASKTAG_ARG2
, (IPTR
)FindTask(0) },
940 { TASKTAG_ARG3
, (IPTR
)ready
},
945 Need some memory. I don't know however, whether it wouldn't be better
946 to take some RAM from device's memory pool.
948 t
= AllocMem(sizeof(struct Task
), MEMF_PUBLIC
| MEMF_CLEAR
);
949 ml
= AllocMem(sizeof(struct MemList
) + 2*sizeof(struct MemEntry
), MEMF_PUBLIC
| MEMF_CLEAR
);
953 /* Setup stack and put the pointer to the bus as the only parameter */
954 UBYTE
*sp
= AllocMem(STACK_SIZE
, MEMF_PUBLIC
| MEMF_CLEAR
);
956 t
->tc_SPUpper
= sp
+ STACK_SIZE
;
957 t
->tc_SPReg
= (UBYTE
*)t
->tc_SPUpper
- SP_OFFSET
- sizeof(APTR
);
959 /* Message port receiving all the IO requests */
960 bus
->ab_MsgPort
= AllocMem(sizeof(struct MsgPort
), MEMF_PUBLIC
| MEMF_CLEAR
);
961 NEWLIST(&bus
->ab_MsgPort
->mp_MsgList
);
962 bus
->ab_MsgPort
->mp_Node
.ln_Type
= NT_MSGPORT
;
963 bus
->ab_MsgPort
->mp_Flags
= PA_SIGNAL
;
964 bus
->ab_MsgPort
->mp_SigBit
= SIGBREAKB_CTRL_F
;
965 bus
->ab_MsgPort
->mp_SigTask
= t
;
966 bus
->ab_MsgPort
->mp_Node
.ln_Name
= "ATA[PI] Subsystem";
968 /* Tell the System which memory regions are to be freed upon a task completion */
969 ml
->ml_NumEntries
= 3;
970 ml
->ml_ME
[0].me_Addr
= t
;
971 ml
->ml_ME
[0].me_Length
= sizeof(struct Task
);
972 ml
->ml_ME
[1].me_Addr
= sp
;
973 ml
->ml_ME
[1].me_Length
= STACK_SIZE
;
974 ml
->ml_ME
[2].me_Addr
= bus
->ab_MsgPort
;
975 ml
->ml_ME
[2].me_Length
= sizeof(struct MsgPort
);
977 NEWLIST(&t
->tc_MemEntry
);
978 AddHead(&t
->tc_MemEntry
, &ml
->ml_Node
);
980 t
->tc_Node
.ln_Name
= "ATA[PI] Subsystem";
981 t
->tc_Node
.ln_Type
= NT_TASK
;
982 t
->tc_Node
.ln_Pri
= TASK_PRI
;
984 /* Wake up the task */
985 NewAddTask(t
, TaskCode
, NULL
, (struct TagItem
*)&tags
);
986 Wait(SIGBREAKF_CTRL_C
);
992 static int CreateInterrupt(struct ata_Bus
*bus
)
994 struct OOP_Object
*o
;
997 if (bus
->ab_IntHandler
)
1000 Prepare nice interrupt for our bus. Even if interrupt sharing is enabled,
1001 it should work quite well
1003 bus
->ab_IntHandler
->h_Node
.ln_Pri
= 10;
1004 bus
->ab_IntHandler
->h_Node
.ln_Name
= bus
->ab_Task
->tc_Node
.ln_Name
;
1005 bus
->ab_IntHandler
->h_Code
= ata_Interrupt
;
1006 bus
->ab_IntHandler
->h_Data
= bus
;
1008 o
= OOP_NewObject(NULL
, CLID_Hidd_IRQ
, NULL
);
1011 struct pHidd_IRQ_AddHandler __msg__
= {
1012 mID
: OOP_GetMethodID(IID_Hidd_IRQ
, moHidd_IRQ_AddHandler
),
1013 handlerinfo
: bus
->ab_IntHandler
,
1017 if (OOP_DoMethod((OOP_Object
*)o
, (OOP_Msg
)msg
))
1020 OOP_DisposeObject((OOP_Object
*)o
);
1028 Bus task body. It doesn't really do much. It receives simply all IORequests
1029 in endless loop and calls proper handling function. The IO is Semaphore-
1030 protected within a bus.
1032 static void TaskCode(struct ata_Bus
*bus
, struct Task
* parent
, struct SignalSemaphore
*ssem
)
1036 struct IORequest
*msg
;
1037 struct ata_Unit
*unit
;
1039 D(bug("[ATA**] Task started (IO: 0x%x)\n", bus
->ab_Port
));
1042 * don't hold parent while we analyze devices.
1043 * keep things as parallel as they can be
1045 ObtainSemaphoreShared(ssem
);
1046 Signal(parent
, SIGBREAKF_CTRL_C
);
1048 bus
->ab_Timer
= ata_OpenTimer();
1050 /* Get the signal used for sleeping */
1051 bus
->ab_Task
= FindTask(0);
1052 bus
->ab_SleepySignal
= AllocSignal(-1);
1053 /* Failed to get it? Use SIGBREAKB_CTRL_E instead */
1054 if (bus
->ab_SleepySignal
< 0)
1055 bus
->ab_SleepySignal
= SIGBREAKB_CTRL_E
;
1058 * set up irq handler now. all irqs are disabled, so prepare them one by one
1060 if (!CreateInterrupt(bus
))
1062 D(bug("[ATA ] Something wrong with creating interrupt?\n"));
1065 sig
= 1L << bus
->ab_MsgPort
->mp_SigBit
;
1067 for (iter
=0; iter
<MAX_BUSUNITS
; ++iter
)
1069 if (ata_setup_unit(bus
, iter
))
1071 unit
= bus
->ab_Units
[iter
];
1072 if (unit
->au_XferModes
& AF_XFER_PACKET
)
1073 ata_RegisterVolume(0, 0, unit
);
1075 ata_RegisterVolume(0, unit
->au_Cylinders
- 1, unit
);
1080 * ok, we're done with init here.
1081 * tell the parent task we're ready
1083 ReleaseSemaphore(ssem
);
1085 /* Wait forever and process messages */
1090 /* Even if you get new signal, do not process it until Unit is not active */
1091 if (!(bus
->ab_Flags
& UNITF_ACTIVE
))
1093 bus
->ab_Flags
|= UNITF_ACTIVE
;
1095 /* Empty the request queue */
1096 while ((msg
= (struct IORequest
*)GetMsg(bus
->ab_MsgPort
)))
1099 HandleIO(msg
, bus
->ab_Base
);
1100 /* TD_ADDCHANGEINT doesn't require reply */
1101 if (msg
->io_Command
!= TD_ADDCHANGEINT
)
1103 ReplyMsg((struct Message
*)msg
);
1107 bus
->ab_Flags
&= ~(UNITF_INTASK
| UNITF_ACTIVE
);
1112 static void ata_Interrupt(HIDDT_IRQ_Handler
*irq
, HIDDT_IRQ_HwInfo
*hw
)
1114 struct ata_Bus
*bus
= (struct ata_Bus
*)irq
->h_Data
;
1119 /* vim: set ts=4 sw=4 :*/