Problem with floppy driver - IRQ6 not firing
Posted: Thu Aug 05, 2010 2:39 pm
				
				Okay so I tried to make a floppy driver based on the two tutorials about floppy and dma, and on osdev wiki. I tried to support up to two drives, and all densities for 3.5" floppies. I would love to support up to 4 drives, and also 5" floppies, but not sure how to do it.
Anyway, below is my driver. The problem I have is that IRQ6 isn't firing. What could be the problem? I checked twice the code... and I also tried a timeout and apparently it did read properly... but timeout is ugly, since the minimum timeout should be around 3 seconds...
floppy.c
dma.c
			Anyway, below is my driver. The problem I have is that IRQ6 isn't firing. What could be the problem? I checked twice the code... and I also tried a timeout and apparently it did read properly... but timeout is ugly, since the minimum timeout should be around 3 seconds...
floppy.c
Code: Select all
/***** floppy.c ******************************************************
 * (c) 2010 CTA Systems Inc. All rights reserved. Glory To God       *
 *                                                                   *
 *         Floppy Drive I/O Routines                                 *
 *         =========================                                 *
 *                                                                   *
 ************************************************************ cta os */
#include "../irq/irq.h"
#include "../dma/dma.h"
#include <system.h>
#include <conio.h>
#define i86_floppy_wait() { while (!i86_floppy_new_interrupt); i86_floppy_new_interrupt = 0; }
// Used ports:
// ***********
// Digital Output Register (DOR):         0x3f2
// Main Status Register (MSR):            0x3f4
// Data Register (FIFO):                  0x3f5
// Configuration Control Register (CTRL): 0x3f7
unsigned char floppy_drives_installed;
volatile unsigned char i86_floppy_new_interrupt;
struct {
     unsigned char type;
     unsigned char data_rate;
     unsigned char step_rate_time;
     unsigned char head_load_time;
     unsigned char head_unload_time;
     unsigned char sectors_per_track;
} fd[2];
// Initialize DMA
unsigned char i86_floppy_initialize_dma(unsigned char* buffer, unsigned length)
{
    union { unsigned char byt[4]; // Low[0], Mid[1], Hi[2]
            unsigned long l;
    } a, c;
    a.l = (unsigned) buffer;
    c.l = (unsigned) length-1;
    // Check for buffer issues
    if ((a.l >> 24) || (c.l >> 16) || (((a.l & 0xffff)+c.l) >> 16)) return 0;
    i86_dma_reset();
    i86_dma_mask_channel(2);                    // Mask channel 2
    i86_dma_reset_flipflop(1);                  // FlipFlop reset on DMA1
    i86_dma_set_address(2, a.byt[0], a.byt[1]); // Buffer address
    i86_dma_reset_flipflop(1);                  // FlipFlop reset on DMA2
    i86_dma_set_count(2, c.byt[0], c.byt[1]);   // Set count
    i86_dma_set_read(2);
    i86_dma_unmask_all();
    return 1;
}
inline void i86_floppy_disable_controller() {
    outportb (0x3F2, 0);
}
inline void i86_floppy_enable_controller() {
    outportb (0x3F2, 4 | 8);
}
inline unsigned char i86_floppy_send_command (unsigned char command)
{
    int i;
    for (i = 0; i < 750; i++)
        if (inportb(0x3F4) & 128) {
            outportb(0x3F5, command); return 1;
        }
    return 0;
}
inline unsigned char i86_floppy_read_data ()
{
    int i;
    for (i = 0; i < 750; i++)
        if (inportb(0x3F4) & 0x80)
            return inportb(0x3F5);
    return 0;
}
/*inline unsigned char i86_floppy_wait()
{
    unsigned temp = 2000000000;
    
    for (;i86_floppy_new_interrupt==0; temp--)
           if (!temp) return 0;
    i86_floppy_new_interrupt = 0;
    return 1;
}*/
inline void i86_floppy_check_int(unsigned* st0, unsigned* cyl)
{
    i86_floppy_send_command(0x8);
    *st0 = i86_floppy_read_data();
    *cyl = i86_floppy_read_data();
}
extern unsigned i86_pit_get_tick_count();
extern unsigned i86_pit_get_frequency();
void i86_floppy_motor (unsigned char drive, unsigned char on)
{
    if (drive >= floppy_drives_installed) return;
    // Read DOR register
    unsigned char dor = inportb(0x3F2);
    // Un/set selected drive motor
    if (on) dor |= drive << 4;
    else dor &= ~(drive << 4);
    // Write DOR
    outportb (0x3F2, dor);
    // Wait a fifth of a second for motor to turn on
    unsigned temp = i86_pit_get_tick_count();
    unsigned freq = i86_pit_get_frequency();
    while (temp + (freq/5) > i86_pit_get_tick_count());
}
void i86_floppy_handler(ISR_stack_regs *r)
{
    i86_floppy_new_interrupt = 1;
    printf ("[FLOPPY] [INT]\n");
}
void i86_floppy_drive_data (unsigned char drv, unsigned char dma)
{
    unsigned data = 0;
    if (drv >= floppy_drives_installed) return;
    outportb(0x3F7, fd[drv].data_rate);
    i86_floppy_send_command (0x3);
    data = ((fd[drv].step_rate_time & 0xf) << 4) | (fd[drv].head_unload_time & 0xf);
    i86_floppy_send_command (data);
    data = (fd[drv].head_load_time <<1 ) | (dma) ? 1 : 0;
    i86_floppy_send_command (data);
}
inline void i86_floppy_select(unsigned char drive)
{
      if (drive >= floppy_drives_installed) return;
      
      // Send mechanical drive data
      i86_floppy_drive_data(drive, 1);
      // Select drive in DOR register
      outportb (0x3F2, 4 | 8 | drive);
}
unsigned char i86_floppy_calibrate(unsigned drive)
{
    unsigned st0, cyl;
    if (drive >= floppy_drives_installed) return 0;
    i86_floppy_motor (drive, 1);
    int i;
    for (i = 0; i < 15; i++) {
        i86_floppy_new_interrupt = 0;
        i86_floppy_send_command(0x7);
        i86_floppy_send_command(drive);
        i86_floppy_wait();
        i86_floppy_check_int(&st0, &cyl);
        if (!cyl) {
            i86_floppy_motor(drive, 0);
            return 1;
        }
    }
    i86_floppy_motor(drive, 0);
    return 0;
}
void i86_floppy_reset()
{
    unsigned st0, cyl;
    printf ("[Floppy] [Reset] Reset routine started...\n");
    i86_floppy_new_interrupt = 0;
    i86_floppy_disable_controller();
    i86_floppy_enable_controller();
    i86_floppy_wait();
    printf ("[Floppy] [Reset] Interrupt received. Continuing...\n");
    
    int i;
    for (i = 0; i < 4; i++)
        i86_floppy_check_int(&st0, &cyl);
    unsigned char drive;
    for (drive = 0; drive < floppy_drives_installed; drive++) {
      i86_floppy_drive_data(drive, 1);
      if (i86_floppy_calibrate(drive)) printf ("Drive %u calibrated.", drive);
      else printf ("Drive %u failed to calibrate.", drive);
    }
}
void i86_floppy_read_sector_imp (unsigned* where, unsigned char drive, unsigned char head, unsigned char track, unsigned char sector)
{
    unsigned st0, cyl;
    i86_floppy_select (drive);
    i86_floppy_initialize_dma((unsigned char*) where, 512);
    i86_dma_set_read(2);
    i86_floppy_new_interrupt = 0;
    i86_floppy_send_command(0x06 | 0x80 | 0x40 );
    i86_floppy_send_command(head<<2 | drive);
    i86_floppy_send_command(track);
    i86_floppy_send_command(head);
    i86_floppy_send_command(sector);
    i86_floppy_send_command(0x02);
    i86_floppy_send_command( ((sector+1) >= fd[drive].sectors_per_track) ? fd[drive].sectors_per_track : sector+1);
    i86_floppy_send_command(0x1b);
    i86_floppy_send_command(0xff);
    i86_floppy_wait();
    int i;
    for (i = 0; i < 7; i++) i86_floppy_read_data();
    i86_floppy_check_int (&st0, &cyl);
}
unsigned char i86_floppy_seek (unsigned drive, unsigned cyl, unsigned head)
{
    unsigned st0, cyl0;
    if (drive >= floppy_drives_installed) return 0;
    i86_floppy_select (drive);
    int i;
    for (i = 0; i < 20; i++) {
        i86_floppy_new_interrupt = 0;
        i86_floppy_send_command (0xF);
        i86_floppy_send_command ( (head) << 2 | drive);
        i86_floppy_send_command (cyl);
        i86_floppy_wait();
        i86_floppy_check_int(&st0, &cyl0);
        if (cyl0 == cyl) return 1;
    }
    return 0;
}
inline void i86_floppy_lba_to_chs (int lba, unsigned char drive, unsigned char *head, unsigned char *track, unsigned char *sectors)
{
    *head = (lba % (fd[drive].sectors_per_track * 2)) / fd[drive].sectors_per_track;
    *track = lba / (fd[drive].sectors_per_track * 2);
    *sectors = lba % fd[drive].sectors_per_track + 1;
}
extern unsigned char i86_cmos_read_floppy_drives();
const char* types[] = {
      "Nonexistant", "5.25\", unsupported.", "5.25\", unsupported.",
      "3.5\", 720kb", "3.5\", 1.44mb", "3.5\", 2.88 mb"};
void i86_floppy_install()
{
    unsigned char temp = i86_cmos_read_floppy_drives();
    int i;
    printf ("[Floppy] Init started...\n");
    // Set fd0 and fd1 types
    fd[1].type = temp & 0xf;
    fd[0].type = temp >> 4;
    
    printf ("[Floppy] Drives detected: (fd0) %s, (fd1) %s\n", types[fd[0].type], types[fd[1].type]);
    // SRT = 16 - (ms * datarate / 500000);
    // HLT = ms * datarate / 1000000
    // HUT = ms * datarate / 8000000
    // Set up
    for (i = 0; i < 2; i++) {
        if (fd[i].type >= 3) floppy_drives_installed++; // 5.25" drives unsupported
        if (fd[i].type == 3) {  // 720 kb, DD
                fd[i].data_rate = 2;      // speed = 250 kbps
                fd[i].step_rate_time = 12; // 16 - (ms * 250000 / 500000), ms = 8
                fd[i].head_load_time = 7;
                fd[i].head_unload_time = 7;
                fd[i].sectors_per_track = 9;
        }
        else if (fd[i].type == 4) { // 1.44 MB, HD
                fd[i].data_rate = 0;      // speed = 500 kbps
                fd[i].step_rate_time = 8;
                fd[i].head_load_time = 15;
                fd[i].head_unload_time = 15;
                fd[i].sectors_per_track = 18;
        }
        else if (fd[i].type == 5) { // 2.88 MB, ED
                fd[i].data_rate = 3;      // speed = 1000 kbps;
                fd[i].step_rate_time = 0;
                fd[i].head_load_time = 30;
                fd[i].head_unload_time = 30;
                fd[i].sectors_per_track = 36;
        }
    }
    if (floppy_drives_installed == 0) return;       // No drives to set
    printf ("[Floppy] Set up arrays:\n");
    printf ("     (fd0) data rate = %u\n", fd[0].data_rate);
    printf ("     (fd0) step rate time = %u\n", fd[0].step_rate_time);
    printf ("     (fd0) head load time = %u\n", fd[0].head_load_time);
    printf ("     (fd0) head_unload_time = %u\n", fd[0].head_unload_time);
    printf ("     (fd0) sectors_per_track = %u\n", fd[0].head_unload_time);
    printf ("[Floppy] Set up arrays:\n");
    printf ("     (fd1) data rate = %u\n", fd[1].data_rate);
    printf ("     (fd1) step rate time = %u\n", fd[1].step_rate_time);
    printf ("     (fd1) head load time = %u\n", fd[1].head_load_time);
    printf ("     (fd1) head_unload_time = %u\n", fd[1].head_unload_time);
    printf ("     (fd1) sectors_per_track = %u\n", fd[1].head_unload_time);
    
    // Install handler
    i86_irq_install_handler(6, i86_floppy_handler);
    printf ("[Floppy] Installed IRQ6.\n");
    i86_floppy_reset();
}
unsigned char i86_floppy_driver_enabled()
{
    return (floppy_drives_installed>0);
}
unsigned* i86_read_sector (unsigned* where, unsigned char drive, int sectorLBA)
{
    if (drive >= floppy_drives_installed) return 0;
    if ((unsigned)(where) > (0xFFFF - 513)) return 0;
    // convert lba to chs
    unsigned head, track, sector;
    i86_floppy_lba_to_chs(sectorLBA, drive, (unsigned char*)&head, (unsigned char*)&track, (unsigned char*) §or);
    // start motor
    i86_floppy_motor(drive, 1);
    if (!i86_floppy_seek(drive, track, head)) return 0;
    i86_floppy_read_sector_imp(where, drive, head, track, sector);
    i86_floppy_motor(drive, 0);
    return (unsigned*)where;
}
Code: Select all
/***** dma.c *********************************************************
 * (c) 2010 CTA Systems Inc. All rights reserved. Glory To God       *
 *                                                                   *
 *         Direct Memory Access (DMA) Routines                       *
 *         ===================================                       *
 *                                                                   *
 ************************************************************ cta os */
#include "dma.h"
#include <system.h>
enum DMA0_IO {
    DMA0_STATUS_REG = 0x08,
    DMA0_COMMAND_REG = 0x08,
    DMA0_REQUEST_REG = 0x09,
    DMA0_CHANMASK_REG = 0x0a,
    DMA0_MODE_REG = 0x0b,
    DMA0_CLEARBYTE_FLIPFLOP_REG = 0x0c,
    DMA0_TEMP_REG = 0x0d,
    DMA0_MASTER_CLEAR_REG = 0x0d,
    DMA0_CLEAR_MASK_REG = 0x0e,
    DMA0_MASK_REG = 0x0f
};
enum DMA1_IO {
    DMA1_STATUS_REG = 0xd0,
    DMA1_COMMAND_REG = 0xd0,
    DMA1_REQUEST_REG = 0xd2,
    DMA1_CHANMASK_REG = 0xd4,
    DMA1_MODE_REG = 0xd6,
    DMA1_CLEARBYTE_FLIPFLOP_REG = 0xd8,
    DMA1_INTER_REG = 0xda,
    DMA1_UNMASK_ALL_REG = 0xdc,
    DMA1_MASK_REG = 0xde
};
void i86_dma_set_address(unsigned short channel, unsigned char low, unsigned char high)
{
    if (channel > 7) return;   // Ignore if channel > 7
    // Calculate port
    unsigned short port = (channel >= 4) ? 4*(channel - 4) + 0xc0 : 2*channel;
    // Set address
    outportb (port, low);
    outportb (port, high);
}
void i86_dma_set_count (unsigned short channel, unsigned char low, unsigned char high)
{
    if (channel > 7) return;   // Ignore if channel > 7
    // Calculate port
    unsigned short port = (channel >= 4) ?  4*(channel - 4) + 0xc2
                                         :  (2*channel) + 1;
    // Set count
    outportb (port, low);
    outportb (port, high);
}
void i86_dma_set_external_page_registers (unsigned char channel, unsigned char val)
{
    unsigned short port = 0;
    switch (channel) {
        case 1: port = 0x83; break;
        case 2: port = 0x81; break;
        case 3: port = 0x82; break;
        // <- nothing should ever write to chan 4
        case 5: port = 0x89; break;
        case 6: port = 0x87; break;
        case 7: port = 0x88; break;
        default: if (channel == 4 || channel > 14) return;
    }
    outportb(port, val);
}
void i86_dma_mask_channel (unsigned char channel)
{
    if (channel <= 4) outportb (DMA0_CHANMASK_REG, (1<< (channel -1)));
    else outportb (DMA1_CHANMASK_REG, (1<< (channel -5)));
}
void i86_dma_unmask_channel (unsigned char channel)
{
    if (channel <= 4) outportb (DMA0_CHANMASK_REG, channel);
    else outportb (DMA1_CHANMASK_REG, channel);
}
void i86_dma_unmask_all()
{
    outportb (DMA1_UNMASK_ALL_REG, 0xff);
}
void i86_dma_reset_flipflop (unsigned char dma)
{
    switch (dma) {
        case 0: outportb (DMA0_CLEARBYTE_FLIPFLOP_REG, 0xff);
        case 1: outportb (DMA1_CLEARBYTE_FLIPFLOP_REG, 0xff);
    }
}
void i86_dma_reset ()
{
    outportb (DMA0_TEMP_REG, 0xff);
}
void i86_dma_set_mode(unsigned char channel, unsigned char mode)
{
    unsigned char dma = (channel < 4) ? 0:1;
    unsigned char chan = (dma == 0) ? channel : channel-4;
    i86_dma_mask_channel (channel);
    outportb ((channel < 4) ? DMA0_MODE_REG : DMA1_MODE_REG, chan | mode);
    i86_dma_unmask_all ();
}
void i86_dma_set_read (unsigned char channel)
{
    i86_dma_set_mode (channel, DMA_MODE_READ_TRANSFER | DMA_MODE_TRANSFER_SINGLE);
}
void i86_dma_set_write (unsigned char channel)
{
    i86_dma_set_mode (channel, DMA_MODE_WRITE_TRANSFER | DMA_MODE_TRANSFER_SINGLE);
}
