kern_intr.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 551 行

C
551
字号
/* * Copyright (c) 1997, Stefan Esser <se@freebsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice unmodified, this list of conditions, and the following *    disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: kern_intr.c,v 1.21 1998/11/10 09:16:29 peter Exp $ * */#include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/errno.h>#ifdef RESOURCE_CHECK#include <sys/drvresource.h>#endif /* RESOURCE_CHECK */#include <machine/ipl.h>#ifdef __i386__#include <i386/isa/icu.h>#include <i386/isa/intr_machdep.h>#endif#include <sys/interrupt.h>#include <stddef.h>#ifdef __i386__typedef struct intrec {	intrmask_t	mask;	inthand2_t	*handler;	void		*argument;	struct intrec	*next;	void		*devdata;	int		intr;	intrmask_t	*maskptr;	int		flags;} intrec;static intrec *intreclist_head[NHWI];#endif#ifndef OSKITstruct swilist {	swihand_t	*sl_handler;	struct swilist	*sl_next;};static struct swilist swilists[NSWI];#endif /* !OSKIT */#ifdef __i386__/* * The interrupt multiplexer calls each of the handlers in turn, * and applies the associated interrupt mask to "cpl", which is * defined as a ".long" in /sys/i386/isa/ipl.s */#ifndef SMPstatic __inline intrmask_tsplq(intrmask_t mask){	intrmask_t tmp = cpl;	cpl |= mask;	return (tmp);}#endif /* SMP */static voidintr_mux(void *arg){	intrec *p = arg;	while (p != NULL) {		int oldspl = splq(p->mask);		p->handler(p->argument);		splx(oldspl);		p = p->next;	}}static intrec*find_idesc(unsigned *maskptr, int irq){	intrec *p = intreclist_head[irq];	while (p && p->maskptr != maskptr)		p = p->next;	return (p);}static intrec**find_pred(intrec *idesc, int irq){	intrec **pp = &intreclist_head[irq];	intrec *p = *pp;	while (p != idesc) {		if (p == NULL)			return (NULL);		pp = &p->next;		p = *pp;	}	return (pp);}/* * Both the low level handler and the shared interrupt multiplexer * block out further interrupts as set in the handlers "mask", while * the handler is running. In fact *maskptr should be used for this * purpose, but since this requires one more pointer dereference on * each interrupt, we rather bother update "mask" whenever *maskptr * changes. The function "update_masks" should be called **after** * all manipulation of the linked list of interrupt handlers hung * off of intrdec_head[irq] is complete, since the chain of handlers * will both determine the *maskptr values and the instances of mask * that are fixed. This function should be called with the irq for * which a new handler has been add blocked, since the masks may not * yet know about the use of this irq for a device of a certain class. */static voidupdate_mux_masks(void){	int irq;	for (irq = 0; irq < ICU_LEN; irq++) {		intrec *idesc = intreclist_head[irq];		while (idesc != NULL) {			if (idesc->maskptr != NULL) {				/* our copy of *maskptr may be stale, refresh */				idesc->mask = *idesc->maskptr;			}			idesc = idesc->next;		}	}}static voidupdate_masks(intrmask_t *maskptr, int irq){	intrmask_t mask = 1 << irq;	if (maskptr == NULL)		return;	if (find_idesc(maskptr, irq) == NULL) {		/* no reference to this maskptr was found in this irq's chain */		if ((*maskptr & mask) == 0)			return;		/* the irq was included in the classes mask, remove it */		INTRUNMASK(*maskptr, mask);	} else {		/* a reference to this maskptr was found in this irq's chain */		if ((*maskptr & mask) != 0)			return;		/* put the irq into the classes mask */		INTRMASK(*maskptr, mask);	}	/* we need to update all values in the intr_mask[irq] array */	update_intr_masks();	/* update mask in chains of the interrupt multiplex handler as well */	update_mux_masks();}/* * Add interrupt handler to linked list hung off of intreclist_head[irq] * and install shared interrupt multiplex handler, if necessary */static intadd_intrdesc(intrec *idesc){	int irq = idesc->intr;	intrec *head = intreclist_head[irq];	if (head == NULL) {		/* first handler for this irq, just install it */		if (icu_setup(irq, idesc->handler, idesc->argument, 			      idesc->maskptr, idesc->flags) != 0)			return (-1);#ifndef OSKIT		update_intrname(irq, (intptr_t)idesc->devdata);#endif /* !OSKIT */		/* keep reference */		intreclist_head[irq] = idesc;	} else {		if ((idesc->flags & INTR_EXCL) != 0		    || (head->flags & INTR_EXCL) != 0) {			/*			 * can't append new handler, if either list head or			 * new handler do not allow interrupts to be shared			 */			if (bootverbose)				printf("\tdevice combination doesn't support "				       "shared irq%d\n", irq);			return (-1);		}		if (head->next == NULL) {			/*			 * second handler for this irq, replace device driver's			 * handler by shared interrupt multiplexer function			 */			icu_unset(irq, head->handler);			if (icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0) != 0)				return (-1);			if (bootverbose)				printf("\tusing shared irq%d.\n", irq);#ifndef OSKIT			update_intrname(irq, -1);#endif /* !OSKIT */		}		/* just append to the end of the chain */		while (head->next != NULL)			head = head->next;		head->next = idesc;	}	update_masks(idesc->maskptr, irq);	return (0);}/* * Add the interrupt handler descriptor data structure created by an * earlier call of create_intr() to the linked list for its irq and * adjust the interrupt masks if necessary. * * This function effectively activates the handler. */intintr_connect(intrec *idesc){	int errcode = -1;	int irq;#ifdef RESOURCE_CHECK	int resflag;#endif /* RESOURCE_CHECK */	if (idesc == NULL)		return (-1);	irq = idesc->intr;#ifdef RESOURCE_CHECK	resflag = (idesc->flags & INTR_EXCL) ? RESF_NONE : RESF_SHARED;	if (resource_claim(idesc->devdata, REST_INT, resflag, irq, irq) == 0)#endif /* RESOURCE_CHECK */	{		/* block this irq */		intrmask_t oldspl = splq(1 << irq);		/* add irq to class selected by maskptr */		errcode = add_intrdesc(idesc);		splx(oldspl);	}	if (errcode != 0 && bootverbose)		printf("\tintr_connect(irq%d) failed, result=%d\n", 		       irq, errcode);	return (errcode);}/* * Remove the interrupt handler descriptor data connected created by an * earlier call of intr_connect() from the linked list and adjust the * interrupt masks if necessary. * * This function deactivates the handler. */intintr_disconnect(intrec *idesc){	intrec **hook, *head;	int irq;	int errcode = 0;	if (idesc == NULL)		return (-1);	irq = idesc->intr;	/* find pointer that keeps the reference to this interrupt descriptor */	hook = find_pred(idesc, irq);	if (hook == NULL)		return (-1);	/* make copy of original list head, the line after may overwrite it */	head = intreclist_head[irq];	/* unlink: make predecessor point to idesc->next instead of to idesc */	*hook = idesc->next;	/* now check whether the element we removed was the list head */	if (idesc == head) {		intrmask_t oldspl = splq(1 << irq);		/* we want to remove the list head, which was known to intr_mux */		icu_unset(irq, (inthand2_t*)intr_mux);		/* check whether the new list head is the only element on list */		head = intreclist_head[irq];		if (head != NULL) {			if (head->next != NULL) {				/* install the multiplex handler with new list head as argument */				errcode = icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0);#ifndef OSKIT				if (errcode == 0)					update_intrname(irq, -1);#endif /* !OSKIT */			} else {				/* install the one remaining handler for this irq */				errcode = icu_setup(irq, head->handler,						    head->argument,						    head->maskptr, head->flags);#ifndef OSKIT				if (errcode == 0)					update_intrname(irq, (intptr_t)head->devdata);#endif /* !OSKIT */			}		}		splx(oldspl);	}	update_masks(idesc->maskptr, irq);#ifdef RESOURCE_CHECK	resource_free(idesc->devdata);#endif /* RESOURCE_CHECK */	return (0);}/* * Create an interrupt handler descriptor data structure, which later can * be activated or deactivated at will by calls of [dis]connect(intrec*). * * The dev_instance pointer is required for resource management, and will * only be passed through to resource_claim(). * * The interrupt handler takes an argument of type (void*), which is not * what is currently used for ISA devices. But since the unit number passed * to an ISA interrupt handler can be stored in a (void*) variable, this * causes no problems. Eventually all the ISA interrupt handlers should be * modified to accept the pointer to their private data, too, instead of * an integer index. * * There will be functions that derive a driver and unit name from a * dev_instance variable, and those functions will be used to maintain the * interrupt counter label array referenced by systat and vmstat to report * device interrupt rates (->update_intrlabels). */intrec *intr_create(void *dev_instance, int irq, inthand2_t handler, void *arg,	     intrmask_t *maskptr, int flags){	intrec *idesc;	if (ICU_LEN > 8 * sizeof *maskptr) {		printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",		       ICU_LEN, 8 * sizeof *maskptr);		return (NULL);	}	if ((unsigned)irq >= ICU_LEN) {		printf("create_intr: requested irq%d too high, limit is %d\n",		       irq, ICU_LEN -1);		return (NULL);	}	idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);	if (idesc) {		idesc->next     = NULL;		bzero(idesc, sizeof *idesc);		idesc->devdata  = dev_instance;		idesc->handler  = handler;		idesc->argument = arg;		idesc->maskptr  = maskptr;		idesc->intr     = irq;		idesc->flags    = flags;	}	return (idesc);}/* * Return the memory held by the interrupt handler descriptor data structure * to the system. Make sure, the handler is not actively used anymore, before. */intintr_destroy(intrec *rec){	if (intr_disconnect(rec) != 0)		return (-1);	free(rec, M_DEVBUF);	return (0);}/* * Emulate the register_intr() call previously defined as low level function. * That function (now icu_setup()) may no longer be directly called, since  * a conflict between an ISA and PCI interrupt might go by unnocticed, else. */intregister_intr(int intr, int device_id, u_int flags,	      inthand2_t handler, u_int *maskptr, int unit){	/* XXX modify to include isa_device instead of device_id */	intrec *idesc;	flags |= INTR_EXCL;	idesc = intr_create((void *)(intptr_t)device_id, intr, handler, 			    (void*)(intptr_t)unit, maskptr, flags);	return (intr_connect(idesc));}/* * Emulate the old unregister_intr() low level function.  * Make sure there is just one interrupt, that it was  * registered as non-shared, and that the handlers match. */intunregister_intr(int intr, inthand2_t handler){	intrec *p = intreclist_head[intr];	if (p != NULL && (p->flags & INTR_EXCL) != 0 && p->handler == handler)		return (intr_destroy(p));	return (EINVAL);}#endif /* __i386__ *//* *  We do the same thing, but slightly simpler. */#ifndef OSKITvoidregister_swi(intr, handler)	int intr;	swihand_t *handler;{	struct swilist *slp, *slq;	int s;	if (intr < NHWI || intr >= NHWI + NSWI)		panic("register_swi: bad intr %d", intr);	if (handler == swi_generic || handler == swi_null)		panic("register_swi: bad handler %p", (void *)handler);	slp = &swilists[intr - NHWI];	s = splhigh();	if (ihandlers[intr] == swi_null)		ihandlers[intr] = handler;	else {		if (slp->sl_next == NULL) {			slp->sl_handler = ihandlers[intr];			ihandlers[intr] = swi_generic;		}		slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT);		if (slq == NULL)			panic("register_swi: malloc failed");		slq->sl_handler = handler;		slq->sl_next = NULL;		while (slp->sl_next != NULL)			slp = slp->sl_next;		slp->sl_next = slq;	}	splx(s);}voidswi_dispatcher(intr)	int intr;{	struct swilist *slp;	slp = &swilists[intr - NHWI];	do {		(*slp->sl_handler)();		slp = slp->sl_next;	} while (slp != NULL);}voidunregister_swi(intr, handler)	int intr;	swihand_t *handler;{	struct swilist *slfoundpred, *slp, *slq;	int s;	if (intr < NHWI || intr >= NHWI + NSWI)		panic("unregister_swi: bad intr %d", intr);	if (handler == swi_generic || handler == swi_null)		panic("unregister_swi: bad handler %p", (void *)handler);	slp = &swilists[intr - NHWI];	s = splhigh();	if (ihandlers[intr] == handler)		ihandlers[intr] = swi_null;	else if (slp->sl_next != NULL) {		slfoundpred = NULL;		for (slq = slp->sl_next; slq != NULL;		    slp = slq, slq = slp->sl_next)			if (slq->sl_handler == handler)				slfoundpred = slp;		slp = &swilists[intr - NHWI];		if (slfoundpred != NULL) {			slq = slfoundpred->sl_next;			slfoundpred->sl_next = slq->sl_next;			free(slq, M_DEVBUF);		} else if (slp->sl_handler == handler) {			slq = slp->sl_next;			slp->sl_next = slq->sl_next;			slp->sl_handler = slq->sl_handler;			free(slq, M_DEVBUF);		}		if (slp->sl_next == NULL)			ihandlers[intr] = slp->sl_handler;	}	splx(s);}#endif /* !OSKIT */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?