diff --git a/kernel/src/ata.cpp b/kernel/src/ata.cpp index 013e161f..5c8e9364 100644 --- a/kernel/src/ata.cpp +++ b/kernel/src/ata.cpp @@ -5,50 +5,9 @@ namespace { -bool detected = false; -drive_descriptor* drives; - -} //end of anonymous namespace - -#define MASTER_BIT 0 -#define SLAVE_BIT 1 - -void detect_disks(){ - if(!detected){ - drives = reinterpret_cast(k_malloc(4 * sizeof(drive_descriptor))); - - drives[0] = {0x1F0, 0xE0, false, MASTER_BIT}; - drives[1] = {0x1F0, 0xF0, false, SLAVE_BIT}; - drives[2] = {0x170, 0xE0, false, MASTER_BIT}; - drives[3] = {0x170, 0xF0, false, SLAVE_BIT}; - - for(uint8_t i = 0; i < 4; ++i){ - auto& drive = drives[i]; - - out_byte(drive.controller + 0x6, drive.drive); - sleep_ms(4); - drive.present = in_byte(drive.controller + 0x7) & 0x40; - } - - detected = true; - } -} - -uint8_t number_of_disks(){ - if(!detected){ - detect_disks(); - } - - return 4; -} - -drive_descriptor& drive(uint8_t disk){ - if(!detected){ - detect_disks(); - } - - return drives[disk]; -} +//IDE Controllers +#define ATA_PRIMARY 0x1F0 +#define ATA_SECONDARY 0x170 // I/O Controllers ports #define ATA_DATA 0 @@ -74,6 +33,90 @@ drive_descriptor& drive(uint8_t disk){ #define ATA_READ_BLOCK 0x20 #define ATA_WRITE_BLOCK 0x30 +//Master/Slave on devices +#define MASTER_BIT 0 +#define SLAVE_BIT 1 + +bool detected = false; +drive_descriptor* drives; + +volatile bool primary_invoked = false; +volatile bool secondary_invoked = false; + +void primary_controller_handler(){ + primary_invoked = true; +} + +void secondary_controller_handler(){ + secondary_invoked = true; +} + +void ata_wait_irq_primary(){ + while(!primary_invoked){ + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + } + + primary_invoked = false; +} + +void ata_wait_irq_secondary(){ + while(!secondary_invoked){ + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + } + + secondary_invoked = false; +} + +} //end of anonymous namespace + +void detect_disks(){ + if(!detected){ + drives = reinterpret_cast(k_malloc(4 * sizeof(drive_descriptor))); + + drives[0] = {ATA_PRIMARY, 0xE0, false, MASTER_BIT}; + drives[1] = {ATA_PRIMARY, 0xF0, false, SLAVE_BIT}; + drives[2] = {ATA_SECONDARY, 0xE0, false, MASTER_BIT}; + drives[3] = {ATA_SECONDARY, 0xF0, false, SLAVE_BIT}; + + for(uint8_t i = 0; i < 4; ++i){ + auto& drive = drives[i]; + + out_byte(drive.controller + 0x6, drive.drive); + sleep_ms(4); + drive.present = in_byte(drive.controller + 0x7) & 0x40; + } + + register_irq_handler<14>(primary_controller_handler); + register_irq_handler<15>(secondary_controller_handler); + + detected = true; + } +} + +uint8_t number_of_disks(){ + if(!detected){ + detect_disks(); + } + + return 4; +} + +drive_descriptor& drive(uint8_t disk){ + if(!detected){ + detect_disks(); + } + + return drives[disk]; +} + //TODO MOVE to anonymous static uint8_t wait_for_controller(uint16_t controller, uint8_t mask, uint8_t value, uint16_t timeout){ @@ -104,6 +147,7 @@ bool select_device(drive_descriptor& drive){ } bool ata_read_sectors(drive_descriptor& drive, std::size_t start, uint8_t count, void* destination){ + //Select the device if(!select_device(drive)){ return false; } @@ -115,6 +159,7 @@ bool ata_read_sectors(drive_descriptor& drive, std::size_t start, uint8_t count, uint8_t ch = (start >> 16) & 0xFF; uint8_t hd = (start >> 24) & 0x0F; + //Process the command out_byte(controller + ATA_NSECTOR, count); out_byte(controller + ATA_SECTOR, sc); out_byte(controller + ATA_LCYL, cl); @@ -124,23 +169,32 @@ bool ata_read_sectors(drive_descriptor& drive, std::size_t start, uint8_t count, sleep_ms(1); + //Wait at most 30 seconds for BSY flag to be cleared if(!wait_for_controller(controller, ATA_STATUS_BSY, 0, 30000)){ return false; } + //Verify if there are errors + if(in_byte(controller + ATA_STATUS) & ATA_STATUS_ERR){ + return false; + } + + //Wait the IRQ to happen + if(controller == ATA_PRIMARY){ + ata_wait_irq_primary(); + } else { + ata_wait_irq_secondary(); + } + + //The device can report an error after the IRQ if(in_byte(controller + ATA_STATUS) & ATA_STATUS_ERR){ return false; } uint16_t* buffer = reinterpret_cast(destination); + //Read the disk sectors for(uint8_t sector = 0; sector < count; ++sector){ - sleep_ms(1); - - while (!(in_byte(controller + ATA_STATUS) & ATA_STATUS_DRQ)) { - __asm__ __volatile__ ("nop; nop;"); - } - for(int i = 0; i < 256; ++i){ *buffer++ = in_word(controller + ATA_DATA); }