39,7 → 39,8 |
* |
* This code is designed to support: |
* - multiple devices sharing single IRQ |
* - multiple IRQs per signle device |
* - multiple IRQs per single device |
* - multiple instances of the same device |
* |
* |
* Note about architectures. |
68,8 → 69,11 |
|
#include <ddi/irq.h> |
#include <adt/hash_table.h> |
#include <mm/slab.h> |
#include <arch/types.h> |
#include <synch/spinlock.h> |
#include <console/console.h> |
#include <memstr.h> |
#include <arch.h> |
|
#define KEY_INR 0 |
76,23 → 80,33 |
#define KEY_DEVNO 1 |
|
/** |
* Spinlock protecting the hash table. |
* Spinlock protecting the kernel IRQ hash table. |
* This lock must be taken only when interrupts are disabled. |
*/ |
SPINLOCK_INITIALIZE(irq_hash_table_lock); |
static hash_table_t irq_hash_table; |
SPINLOCK_INITIALIZE(irq_kernel_hash_table_lock); |
/** The kernel IRQ hash table. */ |
static hash_table_t irq_kernel_hash_table; |
|
/** |
* Spinlock protecting the uspace IRQ hash table. |
* This lock must be taken only when interrupts are disabled. |
*/ |
SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock); |
/** The uspace IRQ hash table. */ |
hash_table_t irq_uspace_hash_table; |
|
/** |
* Hash table operations for cases when we know that |
* there will be collisions between different keys. |
*/ |
static index_t irq_ht_hash(unative_t *key); |
static bool irq_ht_compare(unative_t *key, count_t keys, link_t *item); |
static void irq_ht_remove(link_t *item); |
|
static hash_table_operations_t irq_ht_ops = { |
.hash = irq_ht_hash, |
.compare = irq_ht_compare, |
.remove_callback = NULL /* not used */ |
.remove_callback = irq_ht_remove, |
}; |
|
/** |
103,13 → 117,17 |
*/ |
static index_t irq_lin_hash(unative_t *key); |
static bool irq_lin_compare(unative_t *key, count_t keys, link_t *item); |
static void irq_lin_remove(link_t *item); |
|
static hash_table_operations_t irq_lin_ops = { |
.hash = irq_lin_hash, |
.compare = irq_lin_compare, |
.remove_callback = NULL /* not used */ |
.remove_callback = irq_lin_remove, |
}; |
|
/** Number of buckets in either of the hash tables. */ |
static count_t buckets; |
|
/** Initialize IRQ subsystem. |
* |
* @param inrs Numbers of unique IRQ numbers or INRs. |
117,6 → 135,7 |
*/ |
void irq_init(count_t inrs, count_t chains) |
{ |
buckets = chains; |
/* |
* Be smart about the choice of the hash table operations. |
* In cases in which inrs equals the requested number of |
123,10 → 142,17 |
* chains (i.e. where there is no collision between |
* different keys), we can use optimized set of operations. |
*/ |
if (inrs == chains) |
hash_table_create(&irq_hash_table, chains, 2, &irq_lin_ops); |
else |
hash_table_create(&irq_hash_table, chains, 2, &irq_ht_ops); |
if (inrs == chains) { |
hash_table_create(&irq_uspace_hash_table, chains, 2, |
&irq_lin_ops); |
hash_table_create(&irq_kernel_hash_table, chains, 2, |
&irq_lin_ops); |
} else { |
hash_table_create(&irq_uspace_hash_table, chains, 2, |
&irq_ht_ops); |
hash_table_create(&irq_kernel_hash_table, chains, 2, |
&irq_ht_ops); |
} |
} |
|
/** Initialize one IRQ structure. |
136,23 → 162,12 |
*/ |
void irq_initialize(irq_t *irq) |
{ |
memsetb(irq, sizeof(irq_t), 0); |
link_initialize(&irq->link); |
spinlock_initialize(&irq->lock, "irq.lock"); |
irq->preack = false; |
link_initialize(&irq->notif_cfg.link); |
irq->inr = -1; |
irq->devno = -1; |
irq->trigger = (irq_trigger_t) 0; |
irq->claim = NULL; |
irq->handler = NULL; |
irq->arg = NULL; |
irq->cir = NULL; |
irq->cir_arg = NULL; |
irq->notif_cfg.notify = false; |
irq->notif_cfg.answerbox = NULL; |
irq->notif_cfg.code = NULL; |
irq->notif_cfg.method = 0; |
irq->notif_cfg.counter = 0; |
link_initialize(&irq->notif_cfg.link); |
} |
|
/** Register IRQ for device. |
159,9 → 174,10 |
* |
* The irq structure must be filled with information |
* about the interrupt source and with the claim() |
* function pointer and irq_handler() function pointer. |
* function pointer and handler() function pointer. |
* |
* @param irq IRQ structure belonging to a device. |
* @param irq IRQ structure belonging to a device. |
* @return True on success, false on failure. |
*/ |
void irq_register(irq_t *irq) |
{ |
172,88 → 188,101 |
}; |
|
ipl = interrupts_disable(); |
spinlock_lock(&irq_hash_table_lock); |
hash_table_insert(&irq_hash_table, key, &irq->link); |
spinlock_unlock(&irq_hash_table_lock); |
spinlock_lock(&irq_kernel_hash_table_lock); |
spinlock_lock(&irq->lock); |
hash_table_insert(&irq_kernel_hash_table, key, &irq->link); |
spinlock_unlock(&irq->lock); |
spinlock_unlock(&irq_kernel_hash_table_lock); |
interrupts_restore(ipl); |
} |
|
/** Dispatch the IRQ. |
/** Search and lock the uspace IRQ hash table. |
* |
* We assume this function is only called from interrupt |
* context (i.e. that interrupts are disabled prior to |
* this call). |
* |
* This function attempts to lookup a fitting IRQ |
* structure. In case of success, return with interrupts |
* disabled and holding the respective structure. |
* |
* @param inr Interrupt number (aka inr or irq). |
* |
* @return IRQ structure of the respective device or NULL. |
*/ |
irq_t *irq_dispatch_and_lock(inr_t inr) |
static irq_t *irq_dispatch_and_lock_uspace(inr_t inr) |
{ |
link_t *lnk; |
unative_t key[] = { |
(unative_t) inr, |
(unative_t) -1 /* search will use claim() instead of devno */ |
(unative_t) -1 /* search will use claim() instead of devno */ |
}; |
|
spinlock_lock(&irq_hash_table_lock); |
|
lnk = hash_table_find(&irq_hash_table, key); |
spinlock_lock(&irq_uspace_hash_table_lock); |
lnk = hash_table_find(&irq_uspace_hash_table, key); |
if (lnk) { |
irq_t *irq; |
|
irq = hash_table_get_instance(lnk, irq_t, link); |
|
spinlock_unlock(&irq_hash_table_lock); |
spinlock_unlock(&irq_uspace_hash_table_lock); |
return irq; |
} |
spinlock_unlock(&irq_uspace_hash_table_lock); |
|
spinlock_unlock(&irq_hash_table_lock); |
|
return NULL; |
return NULL; |
} |
|
/** Find the IRQ structure corresponding to inr and devno. |
/** Search and lock the kernel IRQ hash table. |
* |
* This functions attempts to lookup the IRQ structure |
* corresponding to its arguments. On success, this |
* function returns with interrups disabled, holding |
* the lock of the respective IRQ structure. |
* |
* This function assumes interrupts are already disabled. |
* |
* @param inr INR being looked up. |
* @param devno Devno being looked up. |
* |
* @return Locked IRQ structure on success or NULL on failure. |
*/ |
irq_t *irq_find_and_lock(inr_t inr, devno_t devno) |
static irq_t *irq_dispatch_and_lock_kernel(inr_t inr) |
{ |
link_t *lnk; |
unative_t keys[] = { |
unative_t key[] = { |
(unative_t) inr, |
(unative_t) devno |
(unative_t) -1 /* search will use claim() instead of devno */ |
}; |
|
spinlock_lock(&irq_hash_table_lock); |
|
lnk = hash_table_find(&irq_hash_table, keys); |
spinlock_lock(&irq_kernel_hash_table_lock); |
lnk = hash_table_find(&irq_kernel_hash_table, key); |
if (lnk) { |
irq_t *irq; |
|
irq = hash_table_get_instance(lnk, irq_t, link); |
|
spinlock_unlock(&irq_hash_table_lock); |
spinlock_unlock(&irq_kernel_hash_table_lock); |
return irq; |
} |
spinlock_unlock(&irq_kernel_hash_table_lock); |
|
spinlock_unlock(&irq_hash_table_lock); |
return NULL; |
} |
|
return NULL; |
/** Dispatch the IRQ. |
* |
* We assume this function is only called from interrupt |
* context (i.e. that interrupts are disabled prior to |
* this call). |
* |
* This function attempts to lookup a fitting IRQ |
* structure. In case of success, return with interrupts |
* disabled and holding the respective structure. |
* |
* @param inr Interrupt number (aka inr or irq). |
* |
* @return IRQ structure of the respective device or NULL. |
*/ |
irq_t *irq_dispatch_and_lock(inr_t inr) |
{ |
irq_t *irq; |
|
/* |
* If the kernel console is silenced, |
* then try first the uspace handlers, |
* eventually fall back to kernel handlers. |
* |
* If the kernel console is active, |
* then do it the other way around. |
*/ |
if (silent) { |
irq = irq_dispatch_and_lock_uspace(inr); |
if (irq) |
return irq; |
return irq_dispatch_and_lock_kernel(inr); |
} |
|
irq = irq_dispatch_and_lock_kernel(inr); |
if (irq) |
return irq; |
return irq_dispatch_and_lock_uspace(inr); |
} |
|
/** Compute hash index for the key. |
272,7 → 301,7 |
index_t irq_ht_hash(unative_t key[]) |
{ |
inr_t inr = (inr_t) key[KEY_INR]; |
return inr % irq_hash_table.entries; |
return inr % buckets; |
} |
|
/** Compare hash table element with a key. |
306,7 → 335,8 |
spinlock_lock(&irq->lock); |
if (devno == -1) { |
/* Invoked by irq_dispatch_and_lock(). */ |
rv = ((irq->inr == inr) && (irq->claim() == IRQ_ACCEPT)); |
rv = ((irq->inr == inr) && |
(irq->claim(irq) == IRQ_ACCEPT)); |
} else { |
/* Invoked by irq_find_and_lock(). */ |
rv = ((irq->inr == inr) && (irq->devno == devno)); |
319,6 → 349,17 |
return rv; |
} |
|
/** Unlock IRQ structure after hash_table_remove(). |
* |
* @param lnk Link in the removed and locked IRQ structure. |
*/ |
void irq_ht_remove(link_t *lnk) |
{ |
irq_t *irq __attribute__((unused)) |
= hash_table_get_instance(lnk, irq_t, link); |
spinlock_unlock(&irq->lock); |
} |
|
/** Compute hash index for the key. |
* |
* This function computes hash index into |
365,7 → 406,7 |
spinlock_lock(&irq->lock); |
if (devno == -1) { |
/* Invoked by irq_dispatch_and_lock() */ |
rv = (irq->claim() == IRQ_ACCEPT); |
rv = (irq->claim(irq) == IRQ_ACCEPT); |
} else { |
/* Invoked by irq_find_and_lock() */ |
rv = (irq->devno == devno); |
378,5 → 419,16 |
return rv; |
} |
|
/** Unlock IRQ structure after hash_table_remove(). |
* |
* @param lnk Link in the removed and locked IRQ structure. |
*/ |
void irq_lin_remove(link_t *lnk) |
{ |
irq_t *irq __attribute__((unused)) |
= hash_table_get_instance(lnk, irq_t, link); |
spinlock_unlock(&irq->lock); |
} |
|
/** @} |
*/ |