From bd5d3f5d49e2c047bc29c1433a79be7e5ad5bcac Mon Sep 17 00:00:00 2001 From: Nigel Date: Thu, 23 Dec 2021 17:41:07 +0100 Subject: [PATCH] Basic PIO ATA driver which can read and identify ata drives --- src/kernel/drivers/ata/ataDevice.cpp | 206 +++++++++++++++++---------- src/kernel/drivers/ata/ataDevice.h | 24 ++-- 2 files changed, 147 insertions(+), 83 deletions(-) diff --git a/src/kernel/drivers/ata/ataDevice.cpp b/src/kernel/drivers/ata/ataDevice.cpp index bd5f7d1..246638f 100644 --- a/src/kernel/drivers/ata/ataDevice.cpp +++ b/src/kernel/drivers/ata/ataDevice.cpp @@ -1,4 +1,4 @@ -#include "atapiDevice.h" +#include "ataDevice.h" #define IS_BIT_SET(x, bit) ((x >> bit & 0x1) == 1) void ATA_DEVICE::Soft_Reset(uint8_t DEVICE_CHANNEL,DEVICE_DRIVE drive){ @@ -13,7 +13,7 @@ void ATA_DEVICE::Soft_Reset(uint8_t DEVICE_CHANNEL,DEVICE_DRIVE drive){ } -void ATA_DEVICE::Identify(uint8_t DEVICE_CHANNEL,DEVICE_DRIVE drive ){ +void ATA_DEVICE::Identify(uint16_t DEVICE_CHANNEL,DEVICE_DRIVE drive ){ // lets ignore which port we actually want to check for now ! /* @@ -41,91 +41,153 @@ void ATA_DEVICE::Identify(uint8_t DEVICE_CHANNEL,DEVICE_DRIVE drive ){ */ - + + //printf("channel selected: 0x%x", DEVICE_CHANNEL); // Assuming Master here // Select the target drive - outb(0x176, 0x0A); // on the primary bus select the master drive - - // Set the Sectorcount, LBAlo, LBAmid and LBAhi IO ports to 0 - outb(0x172, 0); - - outb(0x173, 0); - - outb(0x174, 0); - - outb(0x175, 0); + outb(DEVICE_CHANNEL | 6, drive); // on the primary bus select the master drive + outb(DEVICE_CHANNEL | 6 , 0x0); // write 0 to the controlport for some reason + outb(DEVICE_CHANNEL | 6, drive); + uint8_t status = inb(DEVICE_CHANNEL | 7 ); + if(status == 0x00){ + printf("No drive\n"); + return; + } // send the identify command; - outb(0x177, 0xA1); + outb(DEVICE_CHANNEL | 7, 0xEC); - // read the status port - uint8_t status = inb(0x177); + + // set the sectorCount, LBAlo, LBAmid, LBA,hi IO ports to 0 + outb(DEVICE_CHANNEL | 2, 0); + + outb(DEVICE_CHANNEL | 3, 0); + + outb(DEVICE_CHANNEL | 4, 0); + + outb(DEVICE_CHANNEL | 5, 0); + + // send the identify command ; + //printf("command sent!\n"); + outb(DEVICE_CHANNEL | 7 , 0xEC); + + // read the status port + uint8_t status2 = inb(DEVICE_CHANNEL | 7); + if( status2 == 0x00){ + printf("No drive\n"); + return; + } + + //printf("Waiting until ready...\n"); + while(((status2 & 0x80 == 0x80) + && (status2 & 0x01) != 0x01) + ) status2 = inb(DEVICE_CHANNEL | 7); + + if( status2 & 0x01){ + printf("Error!\n"); + return ; + } + + uint16_t deviceIdentify [256] = {0}; + + for ( int i = 0; i < 256; i++){ + uint16_t data; + asm volatile ("inw %1, %0" : "=a"(data): "Nd"(DEVICE_CHANNEL)); + + deviceIdentify[i] = data; + } + + + printf("Model-label (ASCII hex): "); + for(int i = 27; i < 47; i++){ + kterm_put((char)(deviceIdentify[i] >> 8)); + kterm_put((char)(deviceIdentify[i] & 0x00FF)); + } + kterm_put('\n'); + +} + +void ATA_DEVICE::Read(uint16_t DEVICE_CHANNEL, DEVICE_DRIVE drive, uint32_t LBA, uint16_t* buffer) { + /* + Assume you have a sectorcount byte and a 28 bit LBA value. A sectorcount of 0 means 256 sectors = 128K. + + Notes: - When you send a command byte and the RDY bit of the Status Registers is clear, you may have to wait (technically up to 30 seconds) for the drive to spin up, before DRQ sets. You may also need to ignore ERR and DF the first four times that you read the Status, if you are polling. + - for polling PIO drivers: After transferring the last uint16_t of a PIO data block to the data IO port, give the drive a 400ns delay to reset its DRQ bit (and possibly set BSY again, while emptying/filling its buffer to/from the drive). + - on the "magic bits" sent to port 0x1f6: Bit 6 (value = 0x40) is the LBA bit. This must be set for either LBA28 or LBA48 transfers. It must be clear for CHS transfers. Bits 7 and 5 are obsolete for current ATA drives, but must be set for backwards compatibility with very old (ATA1) drives. + + An example of a 28 bit LBA PIO mode read on the Primary bus: + + */ + + const int sectorCount = 1; + + // Floating bus check + uint8_t floatingBus = inb(DEVICE_CHANNEL | 7); + if (floatingBus == 0xFF){ + printf("Floating bus!!"); + return ; + } + + printf("Read LBA: 0x%x\n", LBA); + // Send 0xE0 for the "master" or 0xF0 for the "slave", ORed with the highest 4 bits of the LBA to port 0x1F6: outb(0x1F6, 0xE0 | (slavebit << 4) | ((LBA >> 24) & 0x0F)) + outb(DEVICE_CHANNEL | 6 , ( 0xE0 | (LBA >>28) ) ); + // Send a NULL byte to port 0x1F1, if you like (it is ignored and wastes lots of CPU time): outb(0x1F1, 0x00) + outb(DEVICE_CHANNEL | 1, 0x0000 ); + //Send the sectorcount to port 0x1F2: outb(0x1F2, (unsigned char) count) + outb(DEVICE_CHANNEL | 2, sectorCount); + //Send the low 8 bits of the LBA to port 0x1F3: outb(0x1F3, (unsigned char) LBA)) + outb(DEVICE_CHANNEL | 3, LBA); + //Send the next 8 bits of the LBA to port 0x1F4: outb(0x1F4, (unsigned char)(LBA >> 8)) + outb(DEVICE_CHANNEL | 4, (LBA >> 8)); + //Send the next 8 bits of the LBA to port 0x1F5: outb(0x1F5, (unsigned char)(LBA >> 16)) + outb(DEVICE_CHANNEL | 5, (LBA >> 16)); + //Send the "READ SECTORS" command (0x20) to port 0x1F7: outb(0x1F7, 0x20) + outb(DEVICE_CHANNEL | 7, 0x20); + + volatile int i,j; + for(i=0;i<2000;i++) + for(j=0;j<25000;j++) + asm("NOP"); + + //Wait for an IRQ or poll. + uint8_t status = inb(DEVICE_CHANNEL | 7); if( status == 0x00){ printf("No drive\n"); return; } - while(true){ - status = inb(0x177); - - if( status & (~8)) - break; + printf("Status: %x\n", status); + // Check if busy! + while((status & 0x80) == 0x80){ + printf("Reading....\r"); + status = inb(DEVICE_CHANNEL | 7); + } + + + if ((status & 0x01) == 0x01){ + printf("Error occured during read!\n"); + return; } - printf("Is this an ATA device?\n"); - uint8_t LBAmid = inb(0x174); - uint8_t LBAhi = inb(0x175); - - printf("LBAmid: 0x%x, LBAhi: 0x%x\n", LBAmid, LBAhi); - - - if( LBAhi != 0x0 || LBAmid != 0x0){ - printf("Not ATA device.. Stopping..\n"); - // return; - } - - while(true){ - status = inb(0x177); - printf( "Status bit: 0x%x\n", status); - if ( IS_BIT_SET(status, 3)){ - printf("Status: ready!\n"); - break; - - } - - if( IS_BIT_SET(status, 0)){ - printf("Status: error!\n"); - break; - } + //Transfer 256 16-bit values, a uint16_t at a time, into your buffer from I/O port 0x1F0. + if( status & 0x01){ + printf("Error!\n"); + printf("Status: 0x%x\n", status); + uint16_t error_register = inb(DEVICE_CHANNEL | 1); + printf("Error register 0x%x\n",error_register ); + return ; } - - if( IS_BIT_SET(status, 0) == false){ - // READ DATA - - uint16_t deviceIdentify [256]; - - for (int i= 0; i < 256; i++){ - uint8_t data = inb(0x170); - uint8_t data2 = inb(0x170); - - deviceIdentify[i] = (uint16_t) ( (uint16_t) data << 8 | (uint16_t) data2 ); - - } - - - printf("Data received!\n"); + for ( int i = 0; i < 256; i++){ + uint16_t data; + asm volatile ("inw %1, %0" : "=a"(data): "Nd"(DEVICE_CHANNEL)); + printf (" %x ", data); + buffer[i] = data; } - - printf("Error bit was set!\n"); - + + //Then loop back to waiting for the next IRQ (or poll again -- see next note) for each successive sector. } - -void ATA_DEVICE::Read(uint8_t DEVICE_CHANNEL, DEVICE_DRIVE drive) { - printf("Not implemented"); -} - -void ATA_DEVICE::Write(uint8_t DEVICE_CHANNEL, DEVICE_DRIVE drive) { - printf("Not implemented"); +void ATA_DEVICE::Write(uint16_t DEVICE_CHANNEL, DEVICE_DRIVE drive) { + printf("Not implemented\n"); } \ No newline at end of file diff --git a/src/kernel/drivers/ata/ataDevice.h b/src/kernel/drivers/ata/ataDevice.h index cccefa4..0c8abd7 100644 --- a/src/kernel/drivers/ata/ataDevice.h +++ b/src/kernel/drivers/ata/ataDevice.h @@ -1,10 +1,10 @@ #pragma once #include -#include "../io.h" -#include "../ide/ideCommands.h" -#include "../ide/sampleIDE.definitions.h" +#include "../../io.h" +#include "../../ide/ideCommands.h" +#include "../../ide/sampleIDE.definitions.h" -#include "../tty/kterm.h" +#include "../../tty/kterm.h" /* * This first driver wil make use of IO ports. @@ -19,11 +19,13 @@ enum DEVICE_DRIVE{ }; -namespace ATA_DEVICE -{ - void Identify ( uint8_t, DEVICE_DRIVE ); - void Read ( uint8_t, DEVICE_DRIVE ); - void Write ( uint8_t, DEVICE_DRIVE ); - void Soft_Reset ( uint8_t, DEVICE_DRIVE ); - + + +namespace ATA_DEVICE{ + void Identify(uint16_t, DEVICE_DRIVE); + void Read (uint16_t, DEVICE_DRIVE, uint32_t, uint16_t*); + void Write(uint16_t, DEVICE_DRIVE); + void Soft_Reset(uint8_t ,DEVICE_DRIVE ); }; + +