OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Aug 20, 2019 3:17 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: Reading only garbage from the DMA
PostPosted: Wed Apr 17, 2019 7:24 pm 
Offline
Member
Member
User avatar

Joined: Wed Dec 12, 2018 12:16 pm
Posts: 119
Location: Chile
Hi again, this time I'm trying to code an FDC (floppy disk controller) driver.
I'm trying to transfer data using the (ISA)DMA instead of PIO mode, but when I try to read it I'm only getting garbage.
First I tried with and blank floppy image. Not blank really, is a FAT12 image, so it haves the typical FAT12 BPB.
But the data that I read doesn't match with the data that I obtain if I hexdump (hd) the image.
I also tried another disk image (but with data) and I obtain the same data. That means that I'm reading only garbage data.
Here's my ugly code:
fpc.c
Code:
/* Floppy disk controller. hextakatt 2019 */
/* cylinder = track, head = platter, sector = cluster */
#include <stdint.h>
#include <stdbool.h>
#include <kernel/kernel.h>
#include <kernel/cpu/irq.h>
#include <kernel/timer.h>
#include <drivers/cmos/cmos.h>
#include <kernel/terminal.h>

/* for 1.44MB floppies */
#define FloppySectorsPerTrack 18
#define MaxFunctionAttempts 10

enum FloppyPorts
{
    StatusRegA = 0x3F0,
    StatusRegB = 0x3F1,
    FdcDOR     = 0x3F2,
    FdcMSR     = 0x3F4,
    FdcFIFO    = 0x3F5,
    FdcCCR     = 0x3F7
};

enum FloppyDOR
{
    FdcDORReset = 0x4,
    FdcDMA      = 0x8,
    FdcMotorA   = 0x10,
    FdcMotorB   = 0x20,
    FdcMotorC   = 0x40,
    FdcMotorD   = 0x80,
};

enum FloppyCommands
{
    FdcReadTrack        =         0x2,   // generates IRQ6
    FdcSpecify          =         0x3,      // * set drive parameters
    FdcSenseStat        =         0x4,
    FdcWriteData        =         0x5,      // * write to the disk
    FdcReadData         =         0x6,      // * read from the disk
    FdcRecalibrate      =         0x7,      // * seek to cylinder 0
    FdcSenseInt         =         0x8,      // * ack IRQ6, get status of last command
    FdcWriteDeletedData =         0x9,
    FdcReadID           =         0xA,   // generates IRQ6
    FdcReadDeletedData  =         0xC,
    FdcFormatTrack      =         0xD,     // *
    FdcDump             =         0xE,
    FdcSeek             =         0xF,     // * seek both heads to cylinder X
    FdcVersion          =         0x10,   // * used during initialization, once
    FdcScanEq           =         0x11,
    FdcPerpendicularM   =         0x12,   // * used during initialization, once, maybe
    FdcConf             =         0x13,     // * set controller parameters
    FdcLock             =         0x14,     // * protect controller params from a reset
    FdcVerify           =         0x16,
    FdcScanLoEq         =         0x19, // SCAN_LOW_OR_EQUAL
    FdcScanHiEq         =         0x1D, // SCAN_HIGH_OR_EQUAL
};

struct __system_stack* r;
size_t current_drive = 0;
/* Is any motor spining? */
bool __motor_spin = false;
volatile bool floppy_irq = false;

const uint8_t DMA_BUFFER = 0x1000;

void lba_chs(uint32_t lba, uint16_t* cyl, uint16_t* head, uint16_t* sector)
{
    *cyl    = lba / (2 * FloppySectorsPerTrack);
    *head   = ((lba % (2 * FloppySectorsPerTrack)) / FloppySectorsPerTrack);
    *sector = ((lba % (2 * FloppySectorsPerTrack)) % FloppySectorsPerTrack + 1);
}

void floppy_dma_init(void)
{
    outb(0x0A, 0x06);
    outb(0x0C, 0xFF);
    outb(0x04, 0);
    outb(0x04, 0x10);
    outb(0x0C, 0xFF);
    outb(0x05, 0xFF);
    outb(0x05, 0x23);
    outb(0x81, 0);
    outb(0x0A, 0x02);
}

void floppy_dma_write(void)
{
    outb(0x0A, 0x06);
    outb(0x0B, 0x5A);
    outb(0x0A, 0x02);
}

void floppy_dma_read(void)
{
    outb(0x0A, 0x06);
    outb(0x0B, 0x56);
    outb(0x0A, 0x02);
}

uint8_t floppy_status(void)
{
    return inb(FdcMSR);
}

void floppy_ccr(uint8_t value)
{
    outb(FdcCCR, value);
}

inline void floppy_wait_irq(void)
{
    /* Poll until we're ready for send I/O data */
    //while ((floppy_status() & 0x80) == 0);
    while (!floppy_irq);
}

/* Send commands to FIFO port */
void floppy_send_command(uint8_t value)
{
    for (int i = 0; i < 255; ++i) {
        if ((floppy_status() & 0x80) == 0)
            outb(FdcFIFO, value);
    }
}

/* The same, we poll, but we read data instead of send */
uint8_t floppy_read_command(void)
{
    for (int i = 0; i < 255; ++i) {
        if ((floppy_status() & 0x80) == 0)
            return inb(FdcFIFO);
    }
}

void floppy_sense(uint32_t* cyl, uint32_t* st0)
{
    *cyl = floppy_read_command();
    *st0 = floppy_read_command();
}

/* Write data to the digital output register */
void floppy_dor(uint8_t value)
{
    outb(FdcDOR, value);
}

/*
* Toggle the actual floppy motor
* Sorry for using goto... But I think that this is an apropiate use of it ;)
*/
void floppy_motor(bool motorstat)
{
    uint8_t motor;
    if (current_drive > 4)
        goto err;
    switch (current_drive) {
        case 0:
            motor = FdcMotorA;
            break;
        case 1:
            motor = FdcMotorB;
            break;
        case 2:
            motor = FdcMotorC;
            break;
        case 3:
            motor = FdcMotorD;
            break;
        default:
            goto err;
            break;
    }
    if (motorstat) {
        /* Turning on the selected motor */
        floppy_dor(current_drive | FdcDORReset | FdcDMA | motor);
        __motor_spin = true;
    } else {
        floppy_dor(FdcDORReset);
        __motor_spin = false;
    }
    /* Wait until the floppy motor gets enough speed, because software runs faster than hardware. (like in this case) */
    usleep(50);
    return;
    err:
        kputs("Invalid drive number (%i)!\n", current_drive);
        return;
}

/* Detect if floppy disk door is open. (true = open, false = closed) */
bool floppy_detect_swap(void)
{
    floppy_motor(true);
    if (inb(floppy_ccr) & 0x80) {
        floppy_motor(false);
        return true;
    }
    floppy_motor(false);
    return false;
}

/* Seek to an given cylinder and head */
int floppy_seek(uint32_t cylinder, uint32_t head)
{
    uint32_t st0, cyl;
    for (int i = 0; i < MaxFunctionAttempts; ++i) {
        floppy_send_command(FdcSeek);
        floppy_send_command((head << 2) | current_drive);
        floppy_send_command(cylinder);
        floppy_wait_irq();
        floppy_sense(&st0, &cyl);
        if (cyl == cylinder) {
            kputs("\nSeek finished\n");
            return 0;
        }
    }
    return 1;
}

/* Reads data in an certain LBA position */
uint8_t* floppy_read(uint32_t lba)
{
    if (floppy_detect_swap())
        return 0;
    uint32_t st0, cyl;
    uint8_t* cylinder, head, sector;
    lba_chs(lba, &cylinder, &head, &sector);
    /* Prepare DMA IC to data read */
    floppy_dma_read();
    while (floppy_read_command() & 0x8);

    floppy_motor(true);
    floppy_seek(cylinder, head);

    /* Send all the requested data to the FDC */
    floppy_send_command(FdcReadData | 0x80 | 0x40 | 0x6);
    floppy_send_command((head << 2) | current_drive);
    floppy_send_command(cylinder);
    floppy_send_command(head);
    floppy_send_command(sector);
    floppy_send_command(0x2);
    floppy_send_command(FloppySectorsPerTrack);
    floppy_send_command(0x1B);
    floppy_send_command(0xFF);
    floppy_motor(false);

    floppy_wait_irq();
    floppy_send_command(FdcSenseInt);
    return (uint8_t*)DMA_BUFFER;
}

uint8_t floppy_type(void)
{
    uint8_t c = read_cmos(0x10);
    return c >> 4;
}

void floppy_recalibrate(void)
{
    uint32_t cyl, st0;
    floppy_motor(true);
    for (int i = 0; i < MaxFunctionAttempts; ++i) {
        floppy_send_command(FdcRecalibrate);
        floppy_send_command(current_drive);
        floppy_wait_irq();
        floppy_sense(&cyl, &st0);
        if (cyl == 0) {
            floppy_motor(false);
            return;
        } else if ((floppy_read_command() & 0x20) == 0) {
            continue;
        }
    }
    floppy_motor(false);
    kputs("\nError while trying to calibrate\n");
}

void floppy_mech_data(uint32_t step, uint32_t load, uint32_t unload)
{
   floppy_send_command(((step & 0xF) << 4) | (unload & 0xF));
   floppy_send_command((load << 1) | 1);
}

/* The BIOS usually leaves the FDC in an undefined state */
void floppy_reset(void)
{
    floppy_send_command(FdcVersion);
    /* instead of having 0x90 I get 0x00... */
    uint8_t aaa = floppy_read_command();
    if (aaa != 0x90)
        kputs("what???->%x", aaa);
   
    floppy_dor(0);
    floppy_dor(0x4 | 0x8);
    floppy_wait_irq();
    /* Send SENSE to all four drives */
    for (int i = 0; i < 4; ++i)
        floppy_send_command(FdcSenseInt);

    floppy_send_command(FdcSpecify);
    floppy_mech_data(0x3, 0x10, 0xF0);
    /* Recalibrate our disk */
    floppy_recalibrate();
}

void floppy_handler(struct __system_stack* r)
{
    floppy_irq = true;
}

void floppy_install(void)
{
    uint8_t a = floppy_type();
    if (a == 4) {
        irq_install_handler(6, floppy_handler);
        floppy_reset();
        floppy_dma_init();
    } else {
        kputs("Current floppy type not supported. (%i)\n", a);
    }
}

Thanks for the patience. :)


Top
 Profile  
 
 Post subject: Re: Reading only garbage from the DMA
PostPosted: Wed Apr 17, 2019 10:41 pm 
Offline
Member
Member
User avatar

Joined: Mon Jul 13, 2009 5:52 am
Posts: 98
Location: Denmark
In your code, you are never resetting the "floppy_irq" variable, which means that after the first IRQ has fired, your variable is always true. For example, in your "floppy_read" you should set this variable to false before sending the commands.

This is not necessarily the cause of your problems, but it certainly has the potential to be.


Top
 Profile  
 
 Post subject: Re: Reading only garbage from the DMA
PostPosted: Thu Apr 18, 2019 7:21 pm 
Offline
Member
Member
User avatar

Joined: Wed Dec 12, 2018 12:16 pm
Posts: 119
Location: Chile
zity wrote:
In your code, you are never resetting the "floppy_irq" variable, which means that after the first IRQ has fired, your variable is always true. For example, in your "floppy_read" you should set this variable to false before sending the commands.

This is not necessarily the cause of your problems, but it certainly has the potential to be.

Tried that, I'm now getting an infinte loop. My code is not based in the typical bug that leads to a infinite loop.
This also happened to me before when I was writing my first FDC controller, so now I'm trying to rewrite it, but I obtain the same bug. #-o


Top
 Profile  
 
 Post subject: Re: Reading only garbage from the DMA
PostPosted: Fri Apr 19, 2019 1:18 am 
Offline
Member
Member
User avatar

Joined: Mon Jul 13, 2009 5:52 am
Posts: 98
Location: Denmark
If you are getting stuck in an infinite loop, then probably your IRQ handler is never called, either because the IRQ handler is not properly installed, or because the FDC simply fails send the IRQ.

Unfortunately I never bothered writing a driver for the FDC so I am not in a position to judge the correctness of the code.


Top
 Profile  
 
 Post subject: Re: Reading only garbage from the DMA
PostPosted: Fri Apr 19, 2019 9:13 am 
Offline
Member
Member
User avatar

Joined: Wed Dec 12, 2018 12:16 pm
Posts: 119
Location: Chile
Oh, I see. So I will need to forget the FDC. :(


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: BaconWraith, Bing [Bot] and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group