may someone help me find the problem? i am really out of ideas.
My polling base ata driver:
Code: Select all
#include "drivers/ata_driver.h"
#include "kernel/print.h"
#include "io/port.h"
/*
* Static helper functions
*/
static void delay_400ns(ata_drive_t *drive);
static uint8_t read_status_reg(ata_drive_t *drive);
static uint8_t ata_read28_one_sector_request(ata_drive_t *drive, uint32_t sector, uint8_t *buffer);
static uint32_t ata_response_handler(cpu_status_t *regs);
static void delay_400ns(ata_drive_t *drive) {
inb(drive->device_id.io_base + ATA_REG_ALTSTATUS);
inb(drive->device_id.io_base + ATA_REG_ALTSTATUS);
inb(drive->device_id.io_base + ATA_REG_ALTSTATUS);
inb(drive->device_id.io_base + ATA_REG_ALTSTATUS);
}
static uint8_t read_status_reg(ata_drive_t *drive) {
uint8_t status;
status = inb(drive->device_id.io_base + ATA_REG_STATUS);
return status;
}
static uint8_t ata_read28_one_sector_request(ata_drive_t *drive, uint32_t sector, uint8_t *buffer) {
outb(drive->device_id.io_base + ATA_REG_HDDEVSEL, 0xE0 | (drive->device_id.master<<4) | ((sector>>24)&0x0F));
outb(drive->device_id.io_base + ATA_REG_FEATURES, 0); // send Null (0) to the feature register (don't know why)
outb(drive->device_id.io_base + ATA_REG_SECCOUNT, 1);
outb(drive->device_id.io_base + ATA_REG_LBA_LOW, sector & 0xFF);
outb(drive->device_id.io_base + ATA_REG_LBA_MID, (sector >> 8) & 0xFF);
outb(drive->device_id.io_base + ATA_REG_LBA_HIGH,(sector >>16) & 0xFF);
/* send the command */
outb(drive->device_id.io_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
/* check if disk is ready */
ata_wait_bsy_clear(drive);
/* "You may also need to ignore ERR and DF the first four times that you read the Status, if you are polling" */
delay_400ns(drive);
ata_wait_drq_ready(drive);
/* check if we got an error */
if (ata_check_err(drive))
return ata_get_err(drive);
for(int i = 0; i < ATA_SECTOR_SIZE/2; i++) {
uint16_t data = inw(drive->device_id.io_base + ATA_REG_DATA);
((uint16_t *)buffer)[i] = data;
}
/* Note 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).
*/
delay_400ns(drive);
return 0;
}
static uint8_t ata_write28_one_sector_request(ata_drive_t *drive, uint32_t sector, uint8_t *buffer) {
outb(drive->device_id.io_base + ATA_REG_HDDEVSEL, 0xE0 | (drive->device_id.master<<4) | ((sector>>24)&0x0F));
outb(drive->device_id.io_base + ATA_REG_FEATURES, 0); // send Null (0) to the feature register (don't know why)
outb(drive->device_id.io_base + ATA_REG_SECCOUNT, 1);
outb(drive->device_id.io_base + ATA_REG_LBA_LOW, sector & 0xFF);
outb(drive->device_id.io_base + ATA_REG_LBA_MID, (sector >> 8) & 0xFF);
outb(drive->device_id.io_base + ATA_REG_LBA_HIGH,(sector >>16) & 0xFF);
/* send the command */
outb(drive->device_id.io_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
/* check if disk is ready */
ata_wait_bsy_clear(drive);
/* "You may also need to ignore ERR and DF the first four times that you read the Status, if you are polling" */
delay_400ns(drive);
ata_wait_drq_ready(drive);
/* check if we got an error */
uint8_t err = ata_get_err(drive);
if (err != 0) return ata_get_err(drive);
for(int i = 0; i < ATA_SECTOR_SIZE/2; i++) {
outw(drive->device_id.io_base + ATA_REG_DATA, ((uint16_t *)buffer)[i]);
asm volatile("jmp .+2"); // short mandatory delay
}
/* may or may not be needed, not so sure */
delay_400ns(drive);
return 0;
}
static uint32_t ata_response_handler(cpu_status_t *regs) {
return 0;
}
/*
* Helper functions
*/
void ata_wait_bsy_clear(ata_drive_t *drive) {
uint8_t status;
do {
status = read_status_reg(drive);
} while (status & ATA_SR_BSY);
}
void ata_wait_drq_ready(ata_drive_t *drive) {
uint8_t status;
do {
status = read_status_reg(drive);
if (status & ATA_SR_ERR)
return;
} while (!(status & ATA_SR_DRQ));
}
uint8_t ata_check_err(ata_drive_t *drive) {
uint8_t status;
status = read_status_reg(drive);
return status & ATA_SR_ERR;
}
uint8_t ata_get_err(ata_drive_t *drive) {
uint8_t err;
err = inb(drive->device_id.io_base + ATA_REG_ERROR);
return err;
}
/*
* Driver functions
*/
void ata_driver_init() {
register_interrupt_handler(46, ata_response_handler);
register_interrupt_handler(47, ata_response_handler);
}
uint8_t ata_read28_request(ata_drive_t *drive, uint32_t sector, uint8_t count, uint8_t *buffer) {
uint8_t err;
for (uint32_t i = 0; i < count; i++) {
err = ata_read28_one_sector_request(drive, sector + i, buffer);
if (err != 0) return err;
buffer += ATA_SECTOR_SIZE;
}
return 0;
}
uint8_t ata_write28_request(ata_drive_t *drive, uint32_t sector, uint8_t count, uint8_t *buffer) {
uint8_t err;
for (uint32_t i = 0; i < count; i++) {
err = ata_write28_one_sector_request(drive, sector + i, buffer);
if (err != 0) return err;
buffer += ATA_SECTOR_SIZE;
}
return ata_flush_cache(drive);
}
uint8_t ata_send_identify_command(ata_drive_t *drive, identify_device_data_t *buffer) {
outb(drive->device_id.io_base + ATA_REG_HDDEVSEL, 0xE0 | (drive->device_id.master << 4));
outb(drive->device_id.io_base + ATA_REG_LBA_LOW, 0);
outb(drive->device_id.io_base + ATA_REG_LBA_MID, 0);
outb(drive->device_id.io_base + ATA_REG_LBA_HIGH, 0);
/* send the command */
outb(drive->device_id.io_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
uint8_t status = read_status_reg(drive);
if (status == 0) {
printf("ATA: No device detected on the port\n");
return 1;
}
/* wait for drive to not be busy */
ata_wait_bsy_clear(drive);
uint8_t LBAmid = inb(drive->device_id.io_base + ATA_REG_LBA_MID);
uint8_t LBAhi = inb(drive->device_id.io_base + ATA_REG_LBA_HIGH);
if (LBAmid != 0 || LBAhi != 0) {
printf("ATA: Device is not ATA compatible\n");
return 1;
}
ata_wait_drq_ready(drive);
if (ata_check_err(drive)) {
printf("ATA: IDENTIFY failed, error flag raised\n");
return ata_get_err(drive);
}
// Read IDENTIFY data
for (uint32_t i = 0; i < ATA_SECTOR_SIZE/2; i++)
((uint16_t *)buffer)[i] = inw(drive->device_id.io_base + ATA_REG_DATA);
/* may or may not be needed, not so sure */
delay_400ns(drive);
}
uint8_t ata_flush_cache(ata_drive_t *drive) {
outb(drive->device_id.io_base + ATA_REG_HDDEVSEL, 0xE0 | (drive->device_id.master << 4));
outb(drive->device_id.io_base + ATA_REG_COMMAND, ATA_CMD_FLUSH);
/* "sending the 0xE7 command to the Command Register (then waiting for BSY to clear)" */
ata_wait_bsy_clear(drive);
/* check if we got an error */
uint8_t err = ata_check_err(drive);
if (err != 0) return ata_get_err(drive);
return 0;
}
/*
* Print helper functions
*/
void print_identify_device_data(const identify_device_data_t *id) {
printf("=== ATA IDENTIFY DEVICE DATA ===\n");
printf("Device type: %s\n",
id->GeneralConfiguration.DeviceType ? "Non-disk device" : "ATA Hard Disk");
printf("LBA28 Sector count: %u (0x%x)\n",
id->UserAddressableSectors, id->UserAddressableSectors);
printf("LBA48 Sector count: %s\n",
id->Max48BitLBA[0] ? "Supported" : "Not supported");
printf("UDMA supported modes: ");
for (int i = 0; i < 8; i++)
if (id->UltraDMASupport & (1 << i))
printf("%d ", i);
printf("\n80-wire cable detected: %s\n",
(id->HardwareResetResult & (1 << 11)) ? "Yes" : "No");
printf("================================\n");
}Code: Select all
void ata_test_write_read_3_sectors(ata_drive_t *drive, uint32_t start_sector)
{
uint8_t write_buf[ATA_SECTOR_SIZE * 3];
uint8_t read_buf[ATA_SECTOR_SIZE * 3];
memset(write_buf, 0, sizeof(write_buf));
memset(read_buf, 0, sizeof(read_buf));
/* Fill each sector with a different pattern */
for (uint32_t i = 0; i < ATA_SECTOR_SIZE; i++) {
write_buf[i] = 0xAA; /* sector 0 */
write_buf[ATA_SECTOR_SIZE + i] = 0xBB; /* sector 1 */
write_buf[ATA_SECTOR_SIZE * 2 + i] = 0xCC; /* sector 2 */
}
printf("ATA TEST: writing 3 sectors at LBA %u\n", start_sector);
uint8_t err = ata_write28_request(drive, start_sector, 3, write_buf);
if (err != 0) {
printf("ATA TEST: write failed, err=%u\n", err);
return;
}
err = ata_flush_cache(drive);
if (err != 0) {
printf("ATA TEST: flush failed, err=%u\n", err);
return;
}
printf("ATA TEST: reading 3 sectors back\n");
err = ata_read28_request(drive, start_sector, 3, read_buf);
if (err != 0) {
printf("ATA TEST: read failed, err=%u\n", err);
return;
}
for (uint32_t i = 0; i < sizeof(write_buf); i++) {
if (write_buf[i] != read_buf[i]) {
printf("ATA TEST: mismatch at byte %u\n", i);
printf("expected=0x%x actual=0x%x\n",
write_buf[i],
read_buf[i]);
return;
}
}
printf("ATA TEST: PASS - 3 sectors written and read correctly\n");
}