// === MACROS ===
#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
+// === Constants ===
+#define IDE_PRI_BASE 0x1F0
+#define IDE_PRI_CTRL 0x3F6
+#define IDE_SEC_BASE 0x170
+#define IDE_SEC_CTRL 0x376
+
+#define IDE_PRDT_LAST 0x8000
+/**
+ \enum HddControls
+ \brief Commands to be sent to HDD_CMD
+*/
+enum HddControls {
+ HDD_PIO_R28 = 0x20,
+ HDD_PIO_R48 = 0x24,
+ HDD_DMA_R48 = 0x25,
+ HDD_PIO_W28 = 0x30,
+ HDD_PIO_W48 = 0x34,
+ HDD_DMA_W48 = 0x35,
+ HDD_DMA_R28 = 0xC8,
+ HDD_DMA_W28 = 0xCA,
+ HDD_IDENTIFY = 0xEC
+};
+
// === TYPES ===
/**
* \brief PRDT Entry
int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer);
int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer);
// IRQs
-void ATA_IRQHandlerPri(int UNUSED(IRQ));
-void ATA_IRQHandlerSec(int UNUSED(IRQ));
+void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr));
+void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr));
// Controller IO
Uint8 ATA_int_BusMasterReadByte(int Ofs);
Uint32 ATA_int_BusMasterReadDWord(int Ofs);
int gATA_IRQSec = 15;
volatile int gaATA_IRQs[2] = {0};
// - Locks to avoid tripping
-tSpinlock giaATA_ControllerLock[2];
+tMutex glaATA_ControllerLock[2];
// - Buffers!
Uint8 gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata")));
// - PRDTs
// Get IDE Controller's PCI Entry
ent = PCI_GetDeviceByClass(0x0101, 0xFFFF, -1);
LOG("ent = %i", ent);
- gATA_BusMasterBase = PCI_GetBAR4( ent );
+ gATA_BusMasterBase = PCI_GetBAR(ent, 4);
if( gATA_BusMasterBase == 0 ) {
Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one");
// TODO: Use PIO mode instead
return MODULE_ERR_NOTNEEDED;
}
+ LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5));
+ LOG("IRQ = %i", PCI_GetIRQ(ent));
+
// Map memory
if( !(gATA_BusMasterBase & 1) )
{
}
else {
// Bit 0 is left set as a flag to other functions
- LOG("gATA_BusMasterBase = 0x%x", gATA_BusMasterBase & ~1);
+ LOG("gATA_BusMasterBase = IO 0x%x", gATA_BusMasterBase & ~1);
}
// Register IRQs and get Buffers
- IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri );
- IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec );
+ IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri, NULL );
+ IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec, NULL );
gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[0] );
gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[1] );
// Enable controllers
outb(IDE_PRI_BASE+1, 1);
outb(IDE_SEC_BASE+1, 1);
+ outb(IDE_PRI_CTRL, 0);
+ outb(IDE_SEC_CTRL, 0);
+
+ // Make sure interrupts are ACKed
+ ATA_int_BusMasterWriteByte(2, 0x4);
+ ATA_int_BusMasterWriteByte(10, 0x4);
// return
LEAVE('i', MODULE_ERR_OK);
return 0; // Disk does not exist
}
- // Poll until BSY clears and DRQ sets or ERR is set
- while( ((val & 0x80) || !(val & 0x08)) && !(val & 1))
+ // Poll until BSY clears or ERR is set
+ // TODO: Timeout?
+ while( (val & 0x80) && !(val & 1) )
+ val = inb(base+7);
+ LOG("BSY unset (0x%x)", val);
+ // and, wait for DRQ to set
+ while( !(val & 0x08) && !(val & 1))
val = inb(base+7);
+ LOG("DRQ set (0x%x)", val);
// Check for an error
if(val & 1) {
data.buf[i] = inw(base);
// Return the disk size
- if(data.identify.Sectors48 != 0)
+ if(data.identify.Sectors48 != 0) {
+ LEAVE('X', data.identify.Sectors48);
return data.identify.Sectors48;
- else
+ }
+ else {
+ LEAVE('x', data.identify.Sectors28);
return data.identify.Sectors28;
+ }
}
/**
/**
* \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
+ * \return Boolean Failure
*/
int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
{
int cont = (Disk>>1)&1; // Controller ID
int disk = Disk & 1;
Uint16 base;
- Uint8 val;
+ Sint64 timeoutTime;
ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer);
// Check if the count is small enough
if(Count > MAX_DMA_SECTORS) {
- Warning("Passed too many sectors for a bulk DMA read (%i > %i)",
+ Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)",
Count, MAX_DMA_SECTORS);
LEAVE('i');
- return 0;
+ return 1;
}
+
+ // Hack to make debug hexdump noticable
+ #if 1
+ memset(Buffer, 0xFF, Count*SECTOR_SIZE);
+ #endif
// Get exclusive access to the disk controller
- LOCK( &giaATA_ControllerLock[ cont ] );
+ Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
// Set Size
gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
// Reset IRQ Flag
gaATA_IRQs[cont] = 0;
+ #if 1
+ if( cont == 0 ) {
+ outb(IDE_PRI_CTRL, 4);
+ IO_DELAY();
+ outb(IDE_PRI_CTRL, 0);
+ }
+ else {
+ outb(IDE_SEC_CTRL, 4);
+ IO_DELAY();
+ outb(IDE_SEC_CTRL, 0);
+ }
+ #endif
+
// Set up transfer
if( Address > 0x0FFFFFFF ) // Use LBA48
{
outb(base+0x6, 0x40 | (disk << 4));
+ IO_DELAY();
outb(base+0x2, 0 >> 8); // Upper Sector Count
outb(base+0x3, Address >> 24); // Low 2 Addr
outb(base+0x4, Address >> 28); // Mid 2 Addr
{
// Magic, Disk, High Address nibble
outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
+ //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F));
+ IO_DELAY();
}
- outb(base+0x01, 0x01); //?
- outb(base+0x02, (Uint8) Count); // Sector Count
- outb(base+0x03, (Uint8) Address); // Low Addr
- outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr
- outb(base+0x05, (Uint8) (Address >> 16)); // High Addr
+ //outb(base+0x01, 0x01); //?
+ outb(base+0x02, Count & 0xFF); // Sector Count
+ outb(base+0x03, Address & 0xFF); // Low Addr
+ outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr
+ outb(base+0x05, (Address >> 16) & 0xFF); // High Addr
LOG("Starting Transfer");
// HACK: Ensure the PRDT is reset
ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]);
-
+ ATA_int_BusMasterWriteByte(cont*8, 4); // Reset IRQ
+
LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
if( Address > 0x0FFFFFFF )
outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48)
outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28)
// Start transfer
- ATA_int_BusMasterWriteByte( cont << 3, 9 ); // Read and start
+ ATA_int_BusMasterWriteByte( cont * 8, 9 ); // Read and start
// Wait for transfer to complete
- while( gaATA_IRQs[cont] == 0 ) Threads_Yield();
+ timeoutTime = now() + ATA_TIMEOUT;
+ while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+ {
+ HALT();
+ }
// Complete Transfer
- ATA_int_BusMasterWriteByte( cont << 3, 8 ); // Read and stop
+ ATA_int_BusMasterWriteByte( cont * 8, 8 ); // Read and stop
- val = inb(base+0x7);
- LOG("Status byte = 0x%02x", val);
-
- LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
- LOG("Transfer Completed & Acknowledged");
-
- // Copy to destination buffer
- memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+ #if DEBUG
+ {
+ Uint8 val = inb(base+0x7);
+ LOG("Status byte = 0x%02x, Controller Status = 0x%02x",
+ val, ATA_int_BusMasterReadByte(cont * 8 + 2));
+ }
+ #else
+ inb(base+0x7);
+ #endif
- // Release controller lock
- RELEASE( &giaATA_ControllerLock[ cont ] );
+ if( gaATA_IRQs[cont] == 0 )
+ {
+ if( ATA_int_BusMasterReadByte(cont * 8 + 2) & 0x4 ) {
+ Log_Error("ATA", "BM Status reports an interrupt, but none recieved");
+ ATA_int_BusMasterWriteByte(cont*8 + 2, 4); // Clear interrupt
+ memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ #if 1
+ Debug_HexDump("ATA", Buffer, 512);
+ #endif
+
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ Log_Warning("ATA",
+ "Read timeout on disk %i (Reading sector 0x%llx)",
+ Disk, Address);
+ // Return error
+ LEAVE('i', 1);
+ return 1;
+ }
+ else {
+ LOG("Transfer Completed & Acknowledged");
+ // Copy to destination buffer
+ memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
- LEAVE('i', 1);
- return 1;
+ LEAVE('i', 0);
+ return 0;
+ }
}
/**
* \param Address LBA of first sector
* \param Count Number of sectors to write (must be >= \a MAX_DMA_SECTORS)
* \param Buffer Source buffer for data
+ * \return Boolean Failure
*/
int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
{
int cont = (Disk>>1)&1; // Controller ID
int disk = Disk & 1;
Uint16 base;
+ Sint64 timeoutTime;
// Check if the count is small enough
- if(Count > MAX_DMA_SECTORS) return 0;
+ if(Count > MAX_DMA_SECTORS) return 1;
// Get exclusive access to the disk controller
- LOCK( &giaATA_ControllerLock[ cont ] );
+ Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
// Set Size
gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
// Get Port Base
base = ATA_GetBasePort(Disk);
+ // Reset IRQ Flag
+ gaATA_IRQs[cont] = 0;
+
// Set up transfer
outb(base+0x01, 0x00);
if( Address > 0x0FFFFFFF ) // Use LBA48
else
outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28)
- // Reset IRQ Flag
- gaATA_IRQs[cont] = 0;
-
// Copy to output buffer
memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE );
ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start
// Wait for transfer to complete
- while( gaATA_IRQs[cont] == 0 ) Threads_Yield();
+ timeoutTime = now() + ATA_TIMEOUT;
+ while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+ {
+ HALT();
+ }
// Complete Transfer
ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop
- // Release controller lock
- RELEASE( &giaATA_ControllerLock[ cont ] );
-
- return 1;
+ // If the IRQ is unset, return error
+ if( gaATA_IRQs[cont] == 0 ) {
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ return 1; // Error
+ }
+ else {
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ return 0;
+ }
}
/**
* \brief Primary ATA Channel IRQ handler
*/
-void ATA_IRQHandlerPri(int UNUSED(IRQ))
+void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr))
{
Uint8 val;
/**
* \brief Second ATA Channel IRQ handler
*/
-void ATA_IRQHandlerSec(int UNUSED(IRQ))
+void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr))
{
Uint8 val;
// IRQ bit set for Secondary Controller