Added a work-around for SATA controllers that seem to pretend to have two
[cake.git] / arch / common / ata.device / ata.c
blobd5ccfeeb0760aaebfea09810f6044a7458467dc4
1 /*
2 Copyright © 2004-2009, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
8 /*
9 * PARTIAL CHANGELOG:
10 * DATE NAME ENTRY
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.
31 #define DEBUG 0
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>
39 #include <oop/oop.h>
40 #include "timer.h"
42 #include <dos/bptr.h>
44 #include <proto/exec.h>
45 #include <proto/oop.h>
47 #include "ata.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;
74 return;
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);
93 else
95 block >>= unit->au_SectorShift;
96 count >>= unit->au_SectorShift;
97 ULONG cnt = 0;
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;
103 return;
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;
126 return;
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);
142 else
144 block >>= unit->au_SectorShift;
145 count >>= unit->au_SectorShift;
146 ULONG cnt = 0;
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;
159 return;
161 io->io_Error = unit->au_Read32(unit, (ULONG)(block & 0x0fffffff), count, IOStdReq(io)->io_Data, &cnt);
163 else
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;
169 return;
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;
188 return;
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);
207 else
209 block >>= unit->au_SectorShift;
210 count >>= unit->au_SectorShift;
211 ULONG cnt = 0;
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;
219 return;
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;
242 return;
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);
258 else
260 block >>= unit->au_SectorShift;
261 count >>= unit->au_SectorShift;
262 ULONG cnt = 0;
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;
277 return;
279 io->io_Error = unit->au_Write32(unit, (ULONG)(block & 0x0fffffff),
280 count, IOStdReq(io)->io_Data, &cnt);
282 else
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;
293 return;
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));
312 Forbid();
314 while((msg = (struct IORequest *)GetMsg((struct MsgPort *)bus->ab_MsgPort)))
316 msg->io_Error = IOERR_ABORTED;
317 ReplyMsg((struct Message *)msg);
320 Permit();
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++;
341 Forbid();
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;
355 Permit();
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;
374 else
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;
393 else
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;
407 else
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));
425 Forbid();
426 AddHead(&unit->au_SoftList, (struct Node *)io);
427 Permit();
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));
437 Forbid();
438 Remove((struct Node *)io);
439 Permit();
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;
467 else
468 dg->dg_TotalSectors = unit->au_Capacity48;
470 else
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;
480 dg->dg_Reserved = 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[] = {
556 CMD_RESET,
557 CMD_READ,
558 CMD_WRITE,
559 CMD_UPDATE,
560 CMD_CLEAR,
561 CMD_STOP,
562 CMD_START,
563 CMD_FLUSH,
564 TD_MOTOR,
565 TD_SEEK,
566 TD_FORMAT,
567 TD_REMOVE,
568 TD_CHANGENUM,
569 TD_CHANGESTATE,
570 TD_PROTSTATUS,
571 TD_GETNUMTRACKS,
572 TD_ADDCHANGEINT,
573 TD_REMCHANGEINT,
574 TD_GETGEOMETRY,
575 TD_EJECT,
576 TD_READ64,
577 TD_WRITE64,
578 TD_SEEK64,
579 TD_FORMAT64,
580 HD_SCSICMD,
581 TD_GETDRIVETYPE,
582 NSCMD_DEVICEQUERY,
583 NSCMD_TD_READ64,
584 NSCMD_TD_WRITE64,
585 NSCMD_TD_SEEK64,
586 NSCMD_TD_FORMAT64,
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)
596 io->io_Error = 0;
598 /* Handle few commands directly here */
599 switch (io->io_Command)
602 New Style Devices query. Introduce self as trackdisk and provide list of
603 commands supported
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);
615 break;
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
620 report error.
622 case TD_GETDRIVETYPE:
623 IOStdReq(io)->io_Actual = DRIVE_NEWSTYLE;
624 break;
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.
631 default:
632 if (io->io_Command <= (HD_SCSICMD+1))
634 if (map32[io->io_Command])
635 map32[io->io_Command](io, LIBBASE);
636 else
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);
643 else
644 cmd_Invalid(io, LIBBASE);
646 else cmd_Invalid(io, LIBBASE);
647 break;
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 */
660 if (comm <= 31)
662 if (IMMEDIATE_COMMANDS & (1 << comm))
663 slow = FALSE;
665 else if (comm == NSCMD_TD_SEEK64) slow = FALSE;
667 return slow;
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)
679 AROS_LIBFUNC_INIT
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 */
686 Disable();
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;
698 Enable();
700 /* Put the message to the bus */
701 PutMsg(unit->au_Bus->ab_MsgPort, (struct Message *)io);
703 else
705 /* Immediate command. Mark unit as active and do the command directly */
706 unit->au_Unit.unit_flags |= UNITF_ACTIVE;
707 Enable();
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));
723 AROS_LIBFUNC_EXIT
726 AROS_LH1(LONG, AbortIO,
727 AROS_LHA(struct IORequest *, io, A1),
728 LIBBASETYPEPTR, LIBBASE, 6, ata)
730 AROS_LIBFUNC_INIT
732 /* Cannot Abort IO */
733 return 0;
735 AROS_LIBFUNC_EXIT
738 AROS_LH1(ULONG, GetRdskLba,
739 AROS_LHA(struct IORequest *, io, A1),
740 LIBBASETYPEPTR, LIBBASE, 7, ata)
742 AROS_LIBFUNC_INIT
744 return 0;
746 AROS_LIBFUNC_EXIT
749 AROS_LH1(ULONG, GetBlkSize,
750 AROS_LHA(struct IORequest *, io, A1),
751 LIBBASETYPEPTR, LIBBASE, 8, ata)
753 AROS_LIBFUNC_INIT
755 return Unit(io)->au_SectorShift;
757 AROS_LIBFUNC_EXIT
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
764 functions.
766 static void DaemonCode(LIBBASETYPEPTR LIBBASE);
768 /* Create the daemon task */
769 int ata_InitDaemonTask(LIBBASETYPEPTR LIBBASE)
771 struct Task *t;
772 struct MemList *ml;
774 struct TagItem tags[] = {
775 { TASKTAG_ARG1, (IPTR)LIBBASE },
776 { TAG_DONE, 0 }
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);
783 if (t && ml)
785 UBYTE *sp = AllocMem(STACK_SIZE, MEMF_PUBLIC | MEMF_CLEAR);
786 t->tc_SPLower = sp;
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);
808 return (t != NULL);
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
821 int count = 0,b,d;
822 struct ata_Bus *bus;
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)
839 b = bus->ab_BusNum;
840 D(bug("[ATA++] Checking bus %d\n", b));
842 for (d=0; d < MAX_BUSUNITS; d++)
845 * Is a device ATAPI?
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));
865 AROS_LVO_CALL3(void,
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
874 count++;
878 * INFO: we are only handling up to 64 atapi devices here
880 if (count == sizeof(ios) / sizeof(*ios))
881 break;
884 bus = (struct ata_Bus*)bus->ab_Node.mln_Succ;
887 D(bug("[ATA++] Starting sweep medium presence detection\n"));
890 * Endless loop
892 for (b=0;;++b)
895 * call separate IORequest for every ATAPI device
896 * we're calling HD_SCSICMD+1 command here -- anything like test unit ready?
898 if (0 == (b & 3))
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)
934 struct Task *t;
935 struct MemList *ml;
937 struct TagItem tags[] = {
938 { TASKTAG_ARG1, (IPTR)bus },
939 { TASKTAG_ARG2, (IPTR)FindTask(0) },
940 { TASKTAG_ARG3, (IPTR)ready },
941 { TAG_DONE, 0 }
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);
951 if (t && ml)
953 /* Setup stack and put the pointer to the bus as the only parameter */
954 UBYTE *sp = AllocMem(STACK_SIZE, MEMF_PUBLIC | MEMF_CLEAR);
955 t->tc_SPLower = sp;
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);
989 return (t != NULL);
992 static int CreateInterrupt(struct ata_Bus *bus)
994 struct OOP_Object *o;
995 int retval = 0;
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);
1009 if (o)
1011 struct pHidd_IRQ_AddHandler __msg__ = {
1012 mID: OOP_GetMethodID(IID_Hidd_IRQ, moHidd_IRQ_AddHandler),
1013 handlerinfo: bus->ab_IntHandler,
1014 id: bus->ab_IRQ,
1015 }, *msg = &__msg__;
1017 if (OOP_DoMethod((OOP_Object *)o, (OOP_Msg)msg))
1018 retval = 1;
1020 OOP_DisposeObject((OOP_Object *)o);
1024 return retval;
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)
1034 ULONG sig;
1035 int iter;
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 unit = bus->ab_Units[iter];
1070 if (ata_setup_unit(bus, iter))
1072 if (unit->au_XferModes & AF_XFER_PACKET)
1073 ata_RegisterVolume(0, 0, unit);
1074 else
1075 ata_RegisterVolume(0, unit->au_Cylinders - 1, unit);
1077 else
1079 /* Destroy unit that couldn't be initialised */
1080 FreePooled(bus->ab_Base->ata_MemPool, unit, sizeof(struct ata_Unit));
1081 bus->ab_Units[iter] = NULL;
1082 bus->ab_Dev[iter] = DEV_NONE;
1087 * ok, we're done with init here.
1088 * tell the parent task we're ready
1090 ReleaseSemaphore(ssem);
1092 /* Wait forever and process messages */
1093 for (;;)
1095 Wait(sig);
1097 /* Even if you get new signal, do not process it until Unit is not active */
1098 if (!(bus->ab_Flags & UNITF_ACTIVE))
1100 bus->ab_Flags |= UNITF_ACTIVE;
1102 /* Empty the request queue */
1103 while ((msg = (struct IORequest *)GetMsg(bus->ab_MsgPort)))
1105 /* And do IO's */
1106 HandleIO(msg, bus->ab_Base);
1107 /* TD_ADDCHANGEINT doesn't require reply */
1108 if (msg->io_Command != TD_ADDCHANGEINT)
1110 ReplyMsg((struct Message *)msg);
1114 bus->ab_Flags &= ~(UNITF_INTASK | UNITF_ACTIVE);
1119 static void ata_Interrupt(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
1121 struct ata_Bus *bus = (struct ata_Bus *)irq->h_Data;
1123 ata_HandleIRQ(bus);
1126 /* vim: set ts=4 sw=4 :*/