/* * Copyright (c) 1996-2000 University of Utah and the Flux Group. * All rights reserved. * * This file is part of the Flux OSKit. The OSKit is free software, also known * as "open source;" you can redistribute it and/or modify it under the terms * of the GNU General Public License (GPL), version 2, as published by the Free * Software Foundation (FSF). To explore alternate licensing terms, contact * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271. * * The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have * received a copy of the GPL along with the OSKit; see the file COPYING. If * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA. */ /* * IRQ management. * * Shared IRQs are now supported. */ #include #include #include #include #include #include #include #include #include #include #include #include /* linked list of functions for an IRQ */ struct int_handler { void (*func)(void *); void *data; struct int_handler *next; }; /* array of pointers to lists of interrupt handlers */ static struct int_handler *handlers[BASE_IRQ_COUNT]; /* flag = 1 if sharing allowed; set to 0 if not */ static char shareable[BASE_IRQ_COUNT]; /* * Interrupt handler called from base_irq_inittab. * Called with IF flag cleared and target IRQ masked. * We should return the same way. */ static unsigned int fdev_irq_handler(struct trap_state *ts) { unsigned irq = ts->err; struct int_handler *current = handlers[irq]; osenv_assert ((irq >= 0) && (irq < BASE_IRQ_COUNT)); while (current) { osenv_assert(current->func); current->func(current->data); current = current->next; } return 0; } /* * Allocate an IRQ. */ int osenv_irq_alloc(int irq, void (*handler)(void *), void *data, int flags) { struct int_handler *temp, **p; int first_time; if (irq < 0 || irq >= BASE_IRQ_COUNT) return OSKIT_EINVAL; /* * This is a blocking operation, * so to avoid races we need to do it * before we start mucking with data structures. */ temp = osenv_mem_alloc(sizeof(struct int_handler), 0, 1); if (temp == NULL) return OSKIT_ENOMEM; temp->func = handler; temp->data = data; temp->next = NULL; first_time = (handlers[irq] == NULL); /* * Make sure the entry in the base_irq_handlers table is available. */ if (first_time && base_irq_handlers[irq] != base_irq_default_handler) { osenv_mem_free(temp, 0, sizeof(struct int_handler)); return OSKIT_EBUSY; } /* * Fail if the irq is already in use * and either the new handler or the existing handler * is not shareable. */ if (!first_time && (!shareable[irq] || !(flags & OSENV_IRQ_SHAREABLE))) { osenv_mem_free(temp, 0, sizeof(struct int_handler)); return OSKIT_EBUSY; } /* * Note that we only hook in the new handler * after its structure has been fully initialized; * this way we don't have to disable interrupts, * because interrupt-level code only scans the list. */ for (p = &handlers[irq]; *p != NULL; p = &(*p)->next); *p = temp; if (first_time) { shareable[irq] = (flags & OSENV_IRQ_SHAREABLE) != 0; base_irq_handlers[irq] = fdev_irq_handler; osenv_irq_enable(irq); } return 0; } /* * Deallocate an IRQ. * Need a handle so know WHICH interrupt handler to remove. */ void osenv_irq_free(int irq, void (*handler)(void *), void *data) { struct int_handler *temp, **p; osenv_assert(irq >= 0 && irq < BASE_IRQ_COUNT); osenv_assert(base_irq_handlers[irq] == fdev_irq_handler); /* * If this is the only handler for this IRQ, * then disable the IRQ before unregistering it, * to avoid a possible infinite interrupt loop * if the interrupt comes at just the wrong time. * Not that this is ever likely to happen, * but you never know with PC hardware... */ temp = handlers[irq]; if (temp != NULL && temp->func == handler && temp->data == data && temp->next == NULL) { osenv_irq_disable(irq); handlers[irq] = NULL; base_irq_handlers[irq] = base_irq_default_handler; osenv_mem_free(temp, 0, sizeof(struct int_handler)); return; } /* * Find and unlink the handler from the list. * Interrupt handlers may safely scan the list during this time, * but we know no one else will concurrently modify it * because only process-level code modifies the list. */ p = &handlers[irq]; while (((temp = *p) != NULL) && (temp->func != handler || temp->data != data)) p = &temp->next; /* not found? */ if (temp == NULL) { osenv_log(OSENV_LOG_WARNING, "osenv_irq_free: interrupt handler not registered!\n"); return; } /* remove it! */ *p = temp->next; osenv_mem_free(temp, 0, sizeof(struct int_handler)); }