linuxyne wrote:What is being done in the 4th step? (I am assuming that the recaliberate was the cmd for which the 1st interrupt arrived).
1. Configure fdc for recalibration
2. wait() for interrupt #1
3. Receive interrupt #1
4. ???
5. wait() for interrupt #2
No, no. The first interrupt is after a reset, the second interrupt (that's not arriving) is after the recalibrate. Here's the code:
Code: Select all
<somewhere in FDC initialisation function>
fdc_reset();
fdc_motor_on();
fdc_do_command(FDC_CMD_RECALIBRATE, (uint8_t[]){ 0 }, NULL);
fdc_do_command(FDC_CMD_SENSE, NULL, fdc_results);
fdc_motor_off();
Code: Select all
static void fdc_reset()
{
sys_log("FDC: reset");
instance->irq = FALSE;
outb(FDC_REGISTER_DSR, 0x80);
fdc_wait_for_irq();
fdc_do_command(FDC_CMD_SPECIFY, (uint8_t[]){ (8 << 4) | 5, 15 << 1 }, NULL);
}
Code: Select all
static void fdc_motor_on()
{
outb(FDC_REGISTER_DOR, 0x18);
}
Code: Select all
static void fdc_motor_off()
{
outb(FDC_REGISTER_DOR, 0x08);
}
Code: Select all
static void fdc_do_command(fdc_command command, uint8_t* parameters, uint8_t* results)
{
uint32_t current_parameter;
uint32_t parameter_count;
uint32_t current_result;
uint32_t result_count;
uint32_t irq_expected;
if ((inb(FDC_REGISTER_MSR) & 0xC0) != 0x80)
{
fdc_reset();
fdc_do_command(command, parameters, results);
return;
}
switch (command)
{
case FDC_CMD_READ:
case FDC_CMD_WRITE:
parameter_count = 8;
result_count = 7;
irq_expected = TRUE;
break;
case FDC_CMD_SEEK:
parameter_count = 2;
result_count = 0;
irq_expected = TRUE;
break;
case FDC_CMD_RECALIBRATE:
parameter_count = 1;
result_count = 0;
irq_expected = TRUE;
break;
case FDC_CMD_SENSE:
parameter_count = 0;
result_count = 2;
irq_expected = FALSE;
break;
case FDC_CMD_CONFIGURE:
parameter_count = 3;
result_count = 0;
irq_expected = FALSE;
break;
case FDC_CMD_SPECIFY:
parameter_count = 2;
result_count = 0;
irq_expected = FALSE;
break;
case FDC_CMD_LOCK:
case FDC_CMD_UNLOCK:
parameter_count = 0;
result_count = 1;
irq_expected = FALSE;
break;
case FDC_CMD_VERSION:
parameter_count = 0;
result_count = 1;
irq_expected = FALSE;
break;
}
if (irq_expected == TRUE)
{
instance->irq = FALSE;
}
outb(FDC_REGISTER_FIFO, command);
current_parameter = 0;
while (current_parameter < parameter_count)
{
while ((inb(FDC_REGISTER_MSR) & 0xC0) != 0x80)
{
// wait
}
outb(FDC_REGISTER_FIFO, parameters[current_parameter]);
current_parameter++;
}
if (irq_expected == TRUE)
{
fdc_wait_for_irq();
}
current_result = 0;
while (current_result < result_count)
{
while ((inb(FDC_REGISTER_MSR) & 0xC0) != 0xC0)
{
// wait
}
results[current_result] = inb(FDC_REGISTER_FIFO);
current_result++;
}
}
SpyderTL wrote:Currently I'm not doing anything to avoid receiving nested interrupts. I can receive a second interrupt while the first one is still being processed. Should I try to avoid this (e.g. cli on entry to the asm wrapper and sti before iret)?
Either handle your interrupt entirely inside your interrupt handler (which prevents nested interrupts), or make your interrupt flag a counter, and separately keep track of which interrupt you are currently handling.
Specifically, for your code, you would have to set the IRQ flag back to FALSE inside your interrupt handler, before your IRET statement.
Sorry, I'm not understanding. What's happening is that drivers register an interrupt handler with the kernel for a specific irq, then when the kernel receives an interrupt it dispatches it (through a combination of an assembly wrapper and a dispatcher function as previously posted) to the interrupt handler registered for the irq received (if present). Once the driver's interrupt handler returns, the kernel sends the EOI to the PIC (if it's a hardware interrupt) and then does the iret (back in the assembly wrapper). (If there's no handler registered for an interrupt, the kernel sends EOI and returns straight away.)
As it is, it is possible for a second interrupt to come while the first interrupt handler is still executing. I thought this was normal behaviour though, for a higher-priority interrupt to interrupt a lower-priority one. However, there is no way for the interrupt handlers to become "confused" as to which interrupt is being handled, as each handler is registered for a specific irq only and also receives as a parameter the irq of the interrupt that it is handling (as technically a handler might be registered for more than one irq). The only way that there could be an issue here is if a second FDC interrupt arrived before the handler for the first one had returned, although that isn't going to happen because the rest of the FDC driver waits for the interrupt before sending the next command.
The assembly wrapper is as follows:
Code: Select all
#define create_isr_wrapper(irq)\
uint32_t irq_##irq##_handler()\
{\
uint32_t current_offset;\
asm goto("jmp %l[isr_end]":::"memory":isr_end);\
\
isr_start:\
asm volatile("pushal\npushl %1\ncall %0\nadd $4, %%esp\npopal\niret"::"r"((uint32_t) 0x01234567), "r"((uint32_t) irq):"memory");\
isr_end:\
\
current_offset = 0;\
while (*((uint32_t*) (&&isr_start + current_offset)) != 0x01234567)\
{\
current_offset++;\
}\
*((uint32_t*) (&&isr_start + current_offset)) = irq_handler;\
return (uint32_t) &&isr_start;\
}
Then I do near the top of the source file:
Code: Select all
create_isr_wrapper(0x20);
create_isr_wrapper(0x21);
create_isr_wrapper(0x22);
create_isr_wrapper(0x23);
<etc.>
And in my initialisation routine:
Code: Select all
add_idt_entry(0x20, irq_0x20_handler());
add_idt_entry(0x21, irq_0x21_handler());
add_idt_entry(0x22, irq_0x22_handler());
add_idt_entry(0x23, irq_0x23_handler());
<etc.>
As far as I can tell, this code is working fine. If I unmask the timer interrupt I get repeated timer interrupts, and if I press a key I get one interrupt from the PS/2 controller (although no more as I believe you have to retrieve the key from the PS/2 controller before it will send another key).
Although I've forgotten about some peculiarity in the x86 architecture where one or more irqs are automatically masked/disabled when an interrupt occurs, there's nothing to stop a second interrupt occurring while the first one is being processed. For that matter, I posted a somewhat interesting and detailed thread about this a while ago:
http://forum.osdev.org/viewtopic.php?f=1&t=30034.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing