Skip to content

Commit f833abc

Browse files
committed
sys/interrupts: support shared interrupts
Some devices (e.g. PCI devices) share a common IRQ line. Allow installing more than one IRQ handler per vector. When a vector has more than one installed handler, interrupt_handle() tries to execute each and everyone of them until one returns the newly defined INTERRUPT_HANDLED constant. Update the now unneeded guard inside pci_device_register_interrupt_handler() and interrupts_has_been_installed(). Closes #58
1 parent bb5b916 commit f833abc

11 files changed

Lines changed: 134 additions & 66 deletions

File tree

include/kernel/devices/pci.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ error_t pci_device_register(struct pci_device *);
8484
* @param interrupt_handler The interrupt handler function
8585
* @param data The data passed to the interrupt handler
8686
*/
87-
error_t pci_device_register_interrupt_handler(struct pci_device *,
87+
error_t pci_device_install_interrupt_handler(struct pci_device *,
8888
interrupt_handler_func_t, void *data);
8989

9090
/** Enable/Disable a device's response to I/O space accesses */

include/kernel/interrupts.h

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@
2828
#include <kernel/error.h>
2929
#include <kernel/types.h>
3030

31+
#include <libalgo/linked_list.h>
32+
33+
/**
34+
* Values returned by an interrupt handler.
35+
*/
36+
typedef enum interrupt_return {
37+
INTERRUPT_HANDLED, /*!< Interrupt was handled by the handler. */
38+
INTERRUPT_IGNORED, /*!< Interrupt was for another handler. */
39+
} interrupt_return_t;
40+
3141
/**
3242
* @brief Frame passed onto the interrupt handlers when triggering an interrupt
3343
* @note This is a only a forward declaration. The actual definition
@@ -36,13 +46,18 @@
3646
typedef struct interrupt_frame interrupt_frame;
3747

3848
/** Function pointer to an interrupt handler */
39-
typedef u32 (*interrupt_handler_func_t)(void *);
49+
typedef interrupt_return_t (*interrupt_handler_func_t)(void *);
50+
51+
struct interrupt_handler {
52+
interrupt_handler_func_t handler;
53+
void *data;
54+
node_t this; /* used by interrupt_vector->handlers */
55+
};
4056

4157
/** A single hardware IRQ vector. */
4258
struct interrupt_vector {
43-
interrupt_handler_func_t handler;
44-
void *data;
45-
const char *name;
59+
const char *name;
60+
llist_t handlers;
4661
};
4762

4863
struct interrupt_chip {
@@ -56,24 +71,32 @@ struct interrupt_chip {
5671
* @param handler The handler function called when the interrupt occurs
5772
* @param data Data passed to the interrupt handler
5873
*/
59-
error_t interrupts_set_handler(unsigned int irq, interrupt_handler_func_t, void *);
74+
error_t
75+
interrupts_install_handler(unsigned int irq, interrupt_handler_func_t, void *);
76+
77+
/** Install a pre-configured interrupt handler.
78+
*
79+
* This function must be used only when configuring an interrupt handler
80+
* at before the virtual memory subsystem is initialized (INIT_BOOTSTRAP).
81+
*
82+
* The interrupt_handler strcuture is initialized and provided by the caller.
83+
*/
84+
error_t
85+
interrupts_install_static_handler(unsigned int nr, struct interrupt_handler *);
6086

6187
/** Retreive the current handler for a given IRQ
6288
*
6389
* @param irq The interrupt number
6490
* @param[out] pdata If not NULL, the handler's associated data is stored inside
6591
* this pointer (optional)
6692
*
93+
* @note In the case of shared interrupts this function always returns the first
94+
* installed handler.
95+
*
6796
* @return The current handler function fo the IRQ
6897
*/
6998
interrupt_handler_func_t interrupts_get_handler(unsigned int irq, void **);
7099

71-
/** Return wether a custom interrupt has been installed for the given vector */
72-
static inline bool interrupts_has_been_installed(unsigned int irq)
73-
{
74-
return interrupts_get_handler(irq, NULL) != NULL;
75-
}
76-
77100
error_t interrupt_handle(unsigned int nr);
78101
const char *interrupt_name(unsigned int nr);
79102

@@ -85,7 +108,7 @@ const char *interrupt_name(unsigned int nr);
85108
* You must always use this function when defining an interrupt handler.
86109
*/
87110
#define INTERRUPT_HANDLER_FUNCTION(_interrupt) \
88-
u32 INTERRUPT_HANDLER(_interrupt)(void *data)
111+
interrupt_return_t INTERRUPT_HANDLER(_interrupt)(void *data)
89112

90113
/** @brief Disable interrupts on the current CPU. */
91114
static inline void interrupts_disable(void)

kernel/arch/i686/devices/pic.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ void pic_reset()
5757
log_info("Setting up custom IRQs handlers");
5858
pic_enable_irq(IRQ_CASCADE); /* Allow IRQs on slave to be triggered */
5959
pic_enable_irq(IRQ_KEYBOARD);
60-
interrupts_set_handler(PIC_MASTER_VECTOR + IRQ_KEYBOARD,
61-
INTERRUPT_HANDLER(irq_keyboard), NULL);
60+
interrupts_install_handler(PIC_MASTER_VECTOR + IRQ_KEYBOARD,
61+
INTERRUPT_HANDLER(irq_keyboard), NULL);
6262
}
6363

6464
void pic_eoi(pic_irq irq)
@@ -109,5 +109,5 @@ static INTERRUPT_HANDLER_FUNCTION(irq_keyboard)
109109

110110
pic_eoi(IRQ_KEYBOARD);
111111

112-
return E_SUCCESS;
112+
return INTERRUPT_HANDLED;
113113
}

kernel/arch/i686/interrupts.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,8 @@ error_t arch_interrupts_init(struct interrupt_chip *root_chip)
144144
* every the custom interrupt vector.
145145
*/
146146
for (int i = 0; i < IDT_LENGTH; ++i) {
147-
idt_interrupt_vectors[i].data = NULL;
148-
idt_interrupt_vectors[i].handler = NULL;
149147
idt_interrupt_vectors[i].name = idt_interrupt_names[i];
148+
INIT_LLIST(idt_interrupt_vectors[i].handlers);
150149
configure_idt_entry(&idt[i], INTERRUPT_GATE_32B,
151150
interrupt_handler_stubs[i]);
152151
}

kernel/arch/i686/mmu.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,15 @@
8080
#include <stddef.h>
8181
#include <string.h>
8282

83+
/*
84+
* Page fault handler must be statically defined since it is registered
85+
* before the virtual memory API has been initialized.
86+
*/
8387
static INTERRUPT_HANDLER_FUNCTION(page_fault);
88+
static struct interrupt_handler page_fault_interrupt_handler = {
89+
.handler = INTERRUPT_HANDLER(page_fault),
90+
.data = NULL,
91+
};
8492

8593
/** Number of entries inside the page directory */
8694
#define MMU_PDE_COUNT (1024)
@@ -701,7 +709,8 @@ bool mmu_init(void)
701709
return false;
702710
}
703711

704-
interrupts_set_handler(PAGE_FAULT, INTERRUPT_HANDLER(page_fault), NULL);
712+
interrupts_install_static_handler(PAGE_FAULT,
713+
&page_fault_interrupt_handler);
705714

706715
/* Initialize caching structures. */
707716
mmu_init_pat();
@@ -783,7 +792,7 @@ static INTERRUPT_HANDLER_FUNCTION(page_fault)
783792
}
784793

785794
if (!address_space_fault(as, faulty_address, is_cow))
786-
return E_SUCCESS;
795+
return INTERRUPT_HANDLED;
787796
}
788797

789798
page_fault_panic:

kernel/arch/i686/timer.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ error_t arch_timer_start(u32 frequency)
3939
return err;
4040
}
4141

42-
interrupts_set_handler(PIC_MASTER_VECTOR + IRQ_TIMER,
43-
INTERRUPT_HANDLER(irq_timer), NULL);
42+
interrupts_install_handler(PIC_MASTER_VECTOR + IRQ_TIMER,
43+
INTERRUPT_HANDLER(irq_timer), NULL);
4444
pic_enable_irq(IRQ_TIMER);
4545

4646
return E_SUCCESS;
@@ -56,7 +56,7 @@ static INTERRUPT_HANDLER_FUNCTION(irq_timer)
5656
pic_eoi(IRQ_TIMER);
5757

5858
if (!scheduler_initialized)
59-
return E_SUCCESS;
59+
return INTERRUPT_HANDLED;
6060

6161
// TODO: Use a separate and more modern timer for scheduler (LAPIC, HPET)
6262
//
@@ -75,5 +75,5 @@ static INTERRUPT_HANDLER_FUNCTION(irq_timer)
7575
if (current->running.preempt <= timer_ticks_counter)
7676
schedule();
7777

78-
return E_SUCCESS;
78+
return INTERRUPT_HANDLED;
7979
}

kernel/devices/pci.c

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,18 @@ static void pci_device_setup_bars(struct pci_device *device)
208208
}
209209
}
210210

211-
static u32 __pci_device_handle_interrupt(void *device)
211+
static interrupt_return_t __pci_device_handle_interrupt(void *device)
212212
{
213213
struct pci_device *pdev = device;
214-
error_t ret;
214+
interrupt_return_t ret;
215215

216-
if (!pdev->interrupt_handler)
217-
return E_SUCCESS;
216+
/*
217+
* NOTE: What if the interrupt line is shared with a non-pci interrupt?
218+
* BIOS should configure the IRQ lines properly for this to never
219+
* happen I guess...
220+
*/
221+
if (!pdev || !pdev->interrupt_handler)
222+
return INTERRUPT_IGNORED;
218223

219224
interrupts_disable();
220225

@@ -226,9 +231,12 @@ static u32 __pci_device_handle_interrupt(void *device)
226231
return ret;
227232
}
228233

229-
error_t pci_device_register_interrupt_handler(struct pci_device *pdev,
230-
interrupt_handler_func_t handler,
231-
void *data)
234+
/*
235+
*
236+
*/
237+
error_t pci_device_install_interrupt_handler(struct pci_device *pdev,
238+
interrupt_handler_func_t handler,
239+
void *data)
232240
{
233241
uint8_t interrupt;
234242

@@ -238,18 +246,11 @@ error_t pci_device_register_interrupt_handler(struct pci_device *pdev,
238246
if (!pdev->interrupt_line)
239247
return E_SUCCESS;
240248

241-
/* TODO: Implement MSI (+ remove dependency on arch-specifi IRQ) */
242-
if (interrupts_has_been_installed(interrupt)) {
243-
log_warn("another interrupt has already been installed on the "
244-
"interrupt line (" FMT8 ")",
245-
interrupt);
246-
return E_BUSY;
247-
}
248-
249249
pdev->interrupt_data = data;
250250
pdev->interrupt_handler = handler;
251251

252-
interrupts_set_handler(interrupt, __pci_device_handle_interrupt, pdev);
252+
/* TODO: Implement MSI + remove dependency on arch-specifi PIC IRQ. */
253+
interrupts_install_handler(interrupt, __pci_device_handle_interrupt, pdev);
253254
pic_enable_irq(pdev->interrupt_line);
254255

255256
pci_device_enable_interrupts(pdev, true);

kernel/devices/rtl8139.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,19 +242,22 @@ static error_t rtl8139_receive_packet(struct rtl8139 *rtl8139)
242242
return ret;
243243
}
244244

245-
static u32 rtl8139_interrupt_handler(void *data)
245+
static interrupt_return_t rtl8139_interrupt_handler(void *data)
246246
{
247247
struct rtl8139 *rtl8139 = data;
248248
uint16_t isr = rtl8139_readw(rtl8139, INTERRUPT_STATUS);
249249

250-
if (isr & INT_RX_OK) {
250+
isr &= ~RTL8139_SUPPORTED_INTERRUPTS;
251+
if (isr == 0)
252+
return INTERRUPT_IGNORED; /* not for us. */
253+
254+
if (isr & INT_RX_OK)
251255
rtl8139_receive_packet(rtl8139);
252-
}
253256

254257
/* clear interrupt source bit by writing one to the ISR (6.7) */
255258
rtl8139_writew(rtl8139, INTERRUPT_STATUS, isr);
256259

257-
return E_SUCCESS;
260+
return INTERRUPT_HANDLED;
258261
}
259262

260263
static error_t rtl8139_enable_capability(struct ethernet_device *device,
@@ -377,7 +380,7 @@ static error_t rtl8139_probe(struct device *dev)
377380
mmu_find_physical((vaddr_t)tx));
378381
}
379382

380-
ret = pci_device_register_interrupt_handler(pdev, rtl8139_interrupt_handler,
383+
ret = pci_device_install_interrupt_handler(pdev, rtl8139_interrupt_handler,
381384
rtl8139);
382385
if (ret)
383386
goto probe_failed;

kernel/misc/uacpi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ uacpi_kernel_install_interrupt_handler(uacpi_u32 irq,
356356
handle->handler = interrupts_get_handler(irq, &handle->data);
357357
*out_irq_handle = handle;
358358

359-
err = interrupts_set_handler(irq, handler, ctx);
359+
err = interrupts_install_handler(irq, handler, ctx);
360360
if (err) {
361361
kfree(handle);
362362
*out_irq_handle = NULL;
@@ -373,7 +373,7 @@ uacpi_kernel_uninstall_interrupt_handler(uacpi_interrupt_handler handler,
373373
UNUSED(handler);
374374

375375
uacpi_irq_handle *irq = irq_handle;
376-
interrupts_set_handler(irq->irq, irq->handler, irq->data);
376+
interrupts_install_handler(irq->irq, irq->handler, irq->data);
377377

378378
return UACPI_STATUS_OK;
379379
}

0 commit comments

Comments
 (0)