This is getting a wonderfull(sarcastic) first experience writing an actual driver. Managed to get bochs floppy controller to lock up and my real hardware to report success without actually having it (YAY).
I'm trying to read logical sector 0 (cylinder 0 head 0 sector 1) using dma. I have the nagging suspicion the problem is somewhere in me setting up the DMA wrong but so far I ain't seeing it.
Here is the floppy drivers code:
Code: Select all
#include <util.h>
#include <cmos.h>
#include <display.h>
#include <irq.h>
#include <idt.h>
#include <sched.h>
#include <proc.h>
#include <kcall_internal.h>
#include <isadma.h>
#include <memory.h>
#define SRA 0x3F0
#define SRB 0x3F1
#define DOR 0x3F2
#define TDR 0x3F3
#define MSR 0x3F4
#define DSR 0x3F4
#define DATA_FIFO 0x3F5
#define DIR 0x3F7
#define CCR 0x3F7
#define SENSE_INTERRUPT 0x08
#define CONFIGURE 0x13
#define VERSION 0x10
#define RECALIBRATE 0x07
#define SPECIFY 0x03
unsigned int FTID;
unsigned int irq_waiting;
unsigned int timer_id=0;
unsigned long long timer_time=0;
// Controller status
unsigned char motor_on[2];
unsigned char drive_avail[2];
unsigned long long motor_timeout[2];
unsigned char current_drive;
// Buffer
void *dmabuf;
void irq6()
{
irq_waiting = 1;
resume(FTID);
print("IRQ6\n");
}
void wait_timeout(int msec)
{
unsigned int ms, ls;
unsigned long long intime;
asm volatile("mov $0x32, %%eax\n"
"int $0x70" : "=a"(ms), "=b"(ls) : );
intime = (((unsigned long long)ms)<<32) | ((unsigned long long)ls);
intime += msec;
ms = (timer_time >> 32) & 0xFFFFFFFF;
ls = (timer_time) & 0xFFFFFFFF;
asm volatile("int $0x70" : : "a"(KC_TIMER_DESTROY), "b"(timer_id), "c"(ms), "d"(ls) );
timer_time = intime;
ms = (intime >> 32) & 0xFFFFFFFF;
ls = (intime) & 0xFFFFFFFF;
asm volatile("int $0x70" : "=a"(timer_id) : "a"(KC_TIMER_CREATE), "b"(FTID), "c"(ms), "d"(ls) );
}
unsigned int controller_command(unsigned char command, unsigned char *inbytes,
unsigned int inlen, unsigned char *outbytes, unsigned int outlen,
unsigned int wait_irq);
void cont_change_drive(unsigned char drive)
{
if (drive > 1)
PANIC("No such drive.");
current_drive = drive;
outb(DOR, (inb(DOR) & ~3) | drive);
}
void cont_motor_on(unsigned char drive)
{
if (drive > 1)
PANIC("No such drive.");
outb(DOR, inb(DOR) | (1 << (drive+4)));
motor_on[drive] = 1;
wait_timeout(300);
}
void update_motor_timeout(unsigned char drive)
{
unsigned int ls, ms;
unsigned long long curtime;
if (drive > 1)
PANIC("No such drive.");
asm("int $0x70" : "=a"(ms), "=b"(ls) : "a"(KC_TIMER_GET_TIME));
curtime = (((unsigned long long)ms) << 32) | ((unsigned long long)ls);
curtime += 2000;
motor_timeout[drive] = curtime;
}
void cont_motor_off(unsigned char drive)
{
if (drive > 1)
PANIC("No such drive.");
outb(DOR, inb(DOR) & ~(1 << (drive+4)));
motor_on[drive] = 0;
}
void drive_recalibrate(unsigned char drive)
{
int i;
unsigned char floppy_io[2];
if (drive > 1)
PANIC("No such drive.");
if (drive_avail[drive] == 0)
PANIC("No such drive.");
for (i=0; i<4; i++)
{
if (current_drive != drive)
{
cont_change_drive(drive);
}
if (motor_on[drive] == 0)
cont_motor_on(drive);
floppy_io[0] = drive;
if (controller_command(RECALIBRATE, floppy_io, 1, (unsigned char*)0, 0, 4000) != 0)
continue;
if (controller_command(SENSE_INTERRUPT, (unsigned char*)0, 0, floppy_io, 2, 0) != 0)
continue;
if ((floppy_io[0] & 0x20) == 0x20)
break;
}
if (i == 4)
PANIC("Couldn't recalibrate drive.");
update_motor_timeout(drive);
}
void controller_reset()
{
unsigned char floppy_io[10];
static unsigned int in_reset = 0;
unsigned int succes;
int i;
// Stop recursive reset
if (in_reset)
return;
in_reset = 1;
// Reset controller and wait for the IRQ
asm("cli");
outb(DOR, 0);
outb(DOR, 0x0C);
wait_timeout(3000);
asm("sti");
// Update status variables to reflect new situation.
motor_on[0] = motor_on[1] = 0;
motor_timeout[0] = motor_timeout[1] = 0;
current_drive = 0;
// Four sense interrupts to clear interrupt flags
for (i=0; i<4; i++)
{
if (controller_command(SENSE_INTERRUPT, (unsigned char*)0, 0, floppy_io, 2, 0) != 0 || (floppy_io[0] & 0xFC) != 0xC0)
PANIC("Failed to do sense after reset.\n");
}
// Verify version of the controller
if (controller_command(VERSION, (unsigned char*)0, 0, floppy_io, 1, 0) != 0 || floppy_io[0] != 0x90)
PANIC("Unsuported floppy controller.\n");
// And configure it for our needs.
floppy_io[0] = floppy_io[2] = 0;
floppy_io[1] = 0x47;
floppy_io[3] = VERSION;
if ((succes = controller_command(CONFIGURE, floppy_io, 3, floppy_io, 0, 0)) != 0)
{
PANIC("Unable to reconfigure controller.\n");
}
// And do a specify
floppy_io[0] = 0x80;
floppy_io[1] = 0x1E;
if ((succes = controller_command(SPECIFY, floppy_io, 2, (unsigned char*)0, 0, 0)) != 0)
{
PANIC("Unable to specify.");
}
// We're no longer in the reset procedure.
in_reset = 0;
}
unsigned int controller_command(unsigned char command, unsigned char *inbytes,
unsigned int inlen, unsigned char *outbytes, unsigned int outlen,
unsigned int wait_irq)
{
int i;
// If we are going to wait for an irq make sure flag is clear to start with
if (wait_irq)
irq_waiting = 0;
// Check state of the controller
if ((inb(MSR) & 0xd0) != 0x80)
{
controller_reset();
return 1;
}
// Output command
outb(DATA_FIFO, command);
// Output arguments
while (inlen > 0)
{
for (i=0; i<128 && (inb(MSR) & 0x80) != 0x80; i++);
if ((inb(MSR) & 0x80) != 0x80)
return 2;
if ((inb(MSR) & 0x50) != 0x10)
return 3;
outb(DATA_FIFO, *inbytes);
inbytes++;
inlen--;
}
// Verify that we don't need to do some sort of execution phase
if ((inb(MSR) & 0x20) == 0x20)
PANIC("NDMA Set, no support for execution phase (yet).");
// Wait for an irq if applicable
if (wait_irq)
{
asm("cli");
if (irq_waiting == 0)
wait_timeout(wait_irq);
if (idt_interruptable())
asm("sti");
}
// Busy loop till IO is possible again
for (i=0; i<2048 && (inb(MSR) & 0x80) != 0x80; i++);
// Verify that we actually got into that state
if ((inb(MSR) & 0x80) != 0x80)
return inb(MSR);
// Read any output bytes
while (outlen > 0)
{
for (i=0; i<128 && (inb(MSR) & 0x80) != 0x80; i++);
if ((inb(MSR) & 0x80) != 0x80)
return 5;
if ((inb(MSR) & 0x50) != 0x50)
return 6;
*outbytes = inb(DATA_FIFO);
outbytes++;
outlen--;
}
// Give it time to get out of command mode
for (i=0; i<1024 && (inb(MSR) & 0x80) != 0x80; i++);
while ((inb(MSR) & 0x10) == 0x10);
// Verify state.
if ((inb(MSR) & 0xD0) != 0x80)
return 7;
return 0;
}
void floppy_controller_init()
{
unsigned char drive_types;
// Find out which drives we have
drive_types=cmos_read_byte(0x10);
// Primary drive
if ((drive_types & 0xF0) == 0x40)
drive_avail[0] = 1;
else
drive_avail[0] = 0;
// Secondary drive
if ((drive_types & 0x0F) == 0x04)
drive_avail[1] = 1;
else
drive_avail[1] = 0;
// Reset controller
controller_reset();
// Set datarate
outb(DSR, inb(DSR) & ~0x83);
// Recalibrate available drives
if (drive_avail[0] == 1)
drive_recalibrate(0);
if (drive_avail[1] == 1)
drive_recalibrate(1);
}
void floppy_task()
{
unsigned char floppy_io[8], floppy_o[7];
unsigned char succes;
// Register IRQ6 handler
irq_set_handle(6, irq6);
// Initialize controller
floppy_controller_init();
print("Pong\n");
// Request a dma buffer
dmabuf = memory_allocate_buffer();
((unsigned char*)dmabuf)[0] = 0;
((unsigned char*)dmabuf)[1] = 0;
// Try to read a sector
print_hex(inb(0x08));
print("\n");
DMA_transfer(2, (unsigned int)dmabuf, 512, 1, 0);
print_hex(inb(0x08));
print("\n");
floppy_io[0] = 0;
floppy_io[1] = 0;
floppy_io[2] = 0;
floppy_io[3] = 1;
floppy_io[4] = 2;
floppy_io[5] = 1;
floppy_io[6] = 0x1B;
floppy_io[7] = 0xFF;
while ((succes = controller_command(0x46, floppy_io, 8, floppy_o, 7, 60000)))
{
print_num(succes);
print(" ");
print_hex(inb(0x08));
print(" fail\n");
PANIC("");
cont_motor_on(0);
}
print_hex(floppy_o[0]);
print(" ");
print_hex(floppy_o[1]);
print(" ");
print_hex(floppy_o[2]);
print("\n");
print_hex(floppy_o[3]);
print(" ");
print_hex(floppy_o[4]);
print(" ");
print_hex(floppy_o[5]);
print("\n");
print_hex(((unsigned char*)dmabuf)[0]);
print(" ");
print_hex(((unsigned char*)dmabuf)[1]);
print("\n");
while (1);
}
void floppy_init(int KPID)
{
FTID = process_spawn_task(KPID, (unsigned int)floppy_task);
}
and the DMA driver:
Code: Select all
#include <util.h>
#include <idt.h>
#define MASK_PORT_03 0x0A
#define MASK_PORT_47 0xD4
#define FLIP_FLOP_03 0x0C
#define FLIP_FLOP_47 0xD8
#define MODE_PORT_03 0x0B
#define MODE_PORT_47 0x06
unsigned short BasePorts[8] =
{
0x00, 0x02, 0x04, 0x06,
0xC0, 0xC4, 0xC8, 0xCC
};
unsigned short CountPorts[8] =
{
0x01, 0x03, 0x05, 0x07,
0xC2, 0xC6, 0xCA, 0xCE
};
unsigned short PagePorts[8] =
{
0x87, 0x83, 0x81, 0x82,
0x8F, 0x8B, 0x89, 0x8A
};
void DMA_transfer(unsigned char channel, unsigned int base, unsigned int size,
unsigned int mode, unsigned int iswrite)
{
// Verify parameters
if (channel > 7)
PANIC("Illegal ISA-DMA channel.");
if (base > 0x00FFFFFF)
PANIC("Trying ISA-DMA from high memory.");
if (size > 0x01000000)
PANIC("Trying to transfer more than 64k.");
if ((base & 0x00FF0000) != ((base + size) & 0x00FF0000))
PANIC("Trying ISA-DMA accross 64k boundary.");
if (mode > 2)
PANIC("Unsupported ISA-DMA mode.");
// CRITICAL SECTION
asm("cli");
// Mask the chosen channel
if (channel < 4)
outb(MASK_PORT_03, channel | 0x4);
else
outb(MASK_PORT_47, (channel-4) | 0x4);
// Reset flipflop
if (channel < 4)
outb(FLIP_FLOP_03, 0xFF);
else
outb(FLIP_FLOP_47, 0xFF);
// Load base address
outb(BasePorts[channel], base & 0xFF);
outb(BasePorts[channel], (base >> 8) & 0xFF);
// Reset flipflop
if (channel < 4)
outb(FLIP_FLOP_03, 0xFF);
else
outb(FLIP_FLOP_47, 0xFF);
// Load count
size--;
outb(CountPorts[channel], size & 0xFF);
outb(CountPorts[channel], (size >> 8) & 0xFF);
// Load page register
outb(PagePorts[channel], (base >> 16) & 0xFF);
// Load mode register
if (channel < 4)
outb(MODE_PORT_03, channel | (mode << 6) | (iswrite == 1 ? 4 : 8));
else
outb(MODE_PORT_47, (channel-4) | (mode << 6) | (iswrite == 1 ? 8 : 4));
// Unmask
if (channel < 4)
outb(MASK_PORT_03, channel);
else
outb(MASK_PORT_47, channel-4);
// END CRITICAL SECTION
if (idt_interruptable())
asm("sti");
}
Hopefully it will be more obvious to someone who has already done this stuff once.
Also it is unclear to me whether the example code on the dma page in the wiki is correct, if I interpret it according to the info given on the same page about the registers it masks the wrong channel during setup.
Edit: nvm the dma problem, I was wrong somewhere, in my handling of the differentiation between reads and writes to be exact.