The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_intr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice unmodified, this list of conditions, and the following
   10  *    disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  *
   26  * $FreeBSD$
   27  *
   28  */
   29 
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/malloc.h>
   34 #include <sys/errno.h>
   35 #ifdef RESOURCE_CHECK
   36 #include <sys/drvresource.h>
   37 #endif /* RESOURCE_CHECK */
   38 
   39 #include <machine/ipl.h>
   40 
   41 #ifdef __i386__
   42 #include <i386/isa/icu.h>
   43 #include <i386/isa/intr_machdep.h>
   44 #endif
   45 
   46 #include <sys/interrupt.h>
   47 
   48 #include <stddef.h>
   49 
   50 #ifdef __i386__
   51 
   52 typedef struct intrec {
   53         intrmask_t      mask;
   54         inthand2_t      *handler;
   55         void            *argument;
   56         struct intrec   *next;
   57         void            *devdata;
   58         int             intr;
   59         intrmask_t      *maskptr;
   60         int             flags;
   61 } intrec;
   62 
   63 static intrec *intreclist_head[NHWI];
   64 
   65 #endif
   66 
   67 struct swilist {
   68         swihand_t       *sl_handler;
   69         struct swilist  *sl_next;
   70 };
   71 
   72 static struct swilist swilists[NSWI];
   73 
   74 #ifdef __i386__
   75 
   76 /*
   77  * The interrupt multiplexer calls each of the handlers in turn,
   78  * and applies the associated interrupt mask to "cpl", which is
   79  * defined as a ".long" in /sys/i386/isa/ipl.s
   80  */
   81 
   82 #ifndef SMP
   83 static __inline intrmask_t
   84 splq(intrmask_t mask)
   85 {
   86         intrmask_t tmp = cpl;
   87         cpl |= mask;
   88         return (tmp);
   89 }
   90 #endif /* SMP */
   91 
   92 static void
   93 intr_mux(void *arg)
   94 {
   95         intrec *p = arg;
   96 
   97         while (p != NULL) {
   98                 int oldspl = splq(p->mask);
   99                 p->handler(p->argument);
  100                 splx(oldspl);
  101                 p = p->next;
  102         }
  103 }
  104 
  105 static intrec*
  106 find_idesc(unsigned *maskptr, int irq)
  107 {
  108         intrec *p = intreclist_head[irq];
  109 
  110         while (p && p->maskptr != maskptr)
  111                 p = p->next;
  112 
  113         return (p);
  114 }
  115 
  116 static intrec**
  117 find_pred(intrec *idesc, int irq)
  118 {
  119         intrec **pp = &intreclist_head[irq];
  120         intrec *p = *pp;
  121 
  122         while (p != idesc) {
  123                 if (p == NULL)
  124                         return (NULL);
  125                 pp = &p->next;
  126                 p = *pp;
  127         }
  128         return (pp);
  129 }
  130 
  131 /*
  132  * Both the low level handler and the shared interrupt multiplexer
  133  * block out further interrupts as set in the handlers "mask", while
  134  * the handler is running. In fact *maskptr should be used for this
  135  * purpose, but since this requires one more pointer dereference on
  136  * each interrupt, we rather bother update "mask" whenever *maskptr
  137  * changes. The function "update_masks" should be called **after**
  138  * all manipulation of the linked list of interrupt handlers hung
  139  * off of intrdec_head[irq] is complete, since the chain of handlers
  140  * will both determine the *maskptr values and the instances of mask
  141  * that are fixed. This function should be called with the irq for
  142  * which a new handler has been add blocked, since the masks may not
  143  * yet know about the use of this irq for a device of a certain class.
  144  */
  145 
  146 static void
  147 update_mux_masks(void)
  148 {
  149         int irq;
  150         for (irq = 0; irq < ICU_LEN; irq++) {
  151                 intrec *idesc = intreclist_head[irq];
  152                 while (idesc != NULL) {
  153                         if (idesc->maskptr != NULL) {
  154                                 /* our copy of *maskptr may be stale, refresh */
  155                                 idesc->mask = *idesc->maskptr;
  156                         }
  157                         idesc = idesc->next;
  158                 }
  159         }
  160 }
  161 
  162 static void
  163 update_masks(intrmask_t *maskptr, int irq)
  164 {
  165         intrmask_t mask = 1 << irq;
  166 
  167         if (maskptr == NULL)
  168                 return;
  169 
  170         if (find_idesc(maskptr, irq) == NULL) {
  171                 /* no reference to this maskptr was found in this irq's chain */
  172                 if ((*maskptr & mask) == 0)
  173                         return;
  174                 /* the irq was included in the classes mask, remove it */
  175                 INTRUNMASK(*maskptr, mask);
  176         } else {
  177                 /* a reference to this maskptr was found in this irq's chain */
  178                 if ((*maskptr & mask) != 0)
  179                         return;
  180                 /* put the irq into the classes mask */
  181                 INTRMASK(*maskptr, mask);
  182         }
  183         /* we need to update all values in the intr_mask[irq] array */
  184         update_intr_masks();
  185         /* update mask in chains of the interrupt multiplex handler as well */
  186         update_mux_masks();
  187 }
  188 
  189 /*
  190  * Add interrupt handler to linked list hung off of intreclist_head[irq]
  191  * and install shared interrupt multiplex handler, if necessary
  192  */
  193 
  194 static int
  195 add_intrdesc(intrec *idesc)
  196 {
  197         int irq = idesc->intr;
  198 
  199         intrec *head = intreclist_head[irq];
  200 
  201         if (head == NULL) {
  202                 /* first handler for this irq, just install it */
  203                 if (icu_setup(irq, idesc->handler, idesc->argument, 
  204                               idesc->maskptr, idesc->flags) != 0)
  205                         return (-1);
  206 
  207                 update_intrname(irq, (intptr_t)idesc->devdata);
  208                 /* keep reference */
  209                 intreclist_head[irq] = idesc;
  210         } else {
  211                 if ((idesc->flags & INTR_EXCL) != 0
  212                     || (head->flags & INTR_EXCL) != 0) {
  213                         /*
  214                          * can't append new handler, if either list head or
  215                          * new handler do not allow interrupts to be shared
  216                          */
  217                         if (bootverbose)
  218                                 printf("\tdevice combination doesn't support "
  219                                        "shared irq%d\n", irq);
  220                         return (-1);
  221                 }
  222                 if (head->next == NULL) {
  223                         /*
  224                          * second handler for this irq, replace device driver's
  225                          * handler by shared interrupt multiplexer function
  226                          */
  227                         icu_unset(irq, head->handler);
  228                         if (icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0) != 0)
  229                                 return (-1);
  230                         if (bootverbose)
  231                                 printf("\tusing shared irq%d.\n", irq);
  232                         update_intrname(irq, -1);
  233                 }
  234                 /* just append to the end of the chain */
  235                 while (head->next != NULL)
  236                         head = head->next;
  237                 head->next = idesc;
  238         }
  239         update_masks(idesc->maskptr, irq);
  240         return (0);
  241 }
  242 
  243 /*
  244  * Add the interrupt handler descriptor data structure created by an
  245  * earlier call of create_intr() to the linked list for its irq and
  246  * adjust the interrupt masks if necessary.
  247  *
  248  * This function effectively activates the handler.
  249  */
  250 
  251 int
  252 intr_connect(intrec *idesc)
  253 {
  254         int errcode = -1;
  255         int irq;
  256 
  257 #ifdef RESOURCE_CHECK
  258         int resflag;
  259 #endif /* RESOURCE_CHECK */
  260 
  261         if (idesc == NULL)
  262                 return (-1);
  263 
  264         irq = idesc->intr;
  265 #ifdef RESOURCE_CHECK
  266         resflag = (idesc->flags & INTR_EXCL) ? RESF_NONE : RESF_SHARED;
  267         if (resource_claim(idesc->devdata, REST_INT, resflag, irq, irq) == 0)
  268 #endif /* RESOURCE_CHECK */
  269         {
  270                 /* block this irq */
  271                 intrmask_t oldspl = splq(1 << irq);
  272 
  273                 /* add irq to class selected by maskptr */
  274                 errcode = add_intrdesc(idesc);
  275                 splx(oldspl);
  276         }
  277         if (errcode != 0 && bootverbose)
  278                 printf("\tintr_connect(irq%d) failed, result=%d\n", 
  279                        irq, errcode);
  280 
  281         return (errcode);
  282 }
  283 
  284 /*
  285  * Remove the interrupt handler descriptor data connected created by an
  286  * earlier call of intr_connect() from the linked list and adjust the
  287  * interrupt masks if necessary.
  288  *
  289  * This function deactivates the handler.
  290  */
  291 
  292 int
  293 intr_disconnect(intrec *idesc)
  294 {
  295         intrec **hook, *head;
  296         int irq;
  297         int errcode = 0;
  298 
  299         if (idesc == NULL)
  300                 return (-1);
  301 
  302         irq = idesc->intr;
  303 
  304         /* find pointer that keeps the reference to this interrupt descriptor */
  305         hook = find_pred(idesc, irq);
  306         if (hook == NULL)
  307                 return (-1);
  308 
  309         /* make copy of original list head, the line after may overwrite it */
  310         head = intreclist_head[irq];
  311 
  312         /* unlink: make predecessor point to idesc->next instead of to idesc */
  313         *hook = idesc->next;
  314 
  315         /* now check whether the element we removed was the list head */
  316         if (idesc == head) {
  317                 intrmask_t oldspl = splq(1 << irq);
  318 
  319                 /* we want to remove the list head, which was known to intr_mux */
  320                 icu_unset(irq, (inthand2_t*)intr_mux);
  321 
  322                 /* check whether the new list head is the only element on list */
  323                 head = intreclist_head[irq];
  324                 if (head != NULL) {
  325                         if (head->next != NULL) {
  326                                 /* install the multiplex handler with new list head as argument */
  327                                 errcode = icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0);
  328                                 if (errcode == 0)
  329                                         update_intrname(irq, -1);
  330                         } else {
  331                                 /* install the one remaining handler for this irq */
  332                                 errcode = icu_setup(irq, head->handler,
  333                                                     head->argument,
  334                                                     head->maskptr, head->flags);
  335                                 if (errcode == 0)
  336                                         update_intrname(irq, (intptr_t)head->devdata);
  337                         }
  338                 }
  339                 splx(oldspl);
  340         }
  341         update_masks(idesc->maskptr, irq);
  342 #ifdef RESOURCE_CHECK
  343         resource_free(idesc->devdata);
  344 #endif /* RESOURCE_CHECK */
  345         return (0);
  346 }
  347 
  348 /*
  349  * Create an interrupt handler descriptor data structure, which later can
  350  * be activated or deactivated at will by calls of [dis]connect(intrec*).
  351  *
  352  * The dev_instance pointer is required for resource management, and will
  353  * only be passed through to resource_claim().
  354  *
  355  * The interrupt handler takes an argument of type (void*), which is not
  356  * what is currently used for ISA devices. But since the unit number passed
  357  * to an ISA interrupt handler can be stored in a (void*) variable, this
  358  * causes no problems. Eventually all the ISA interrupt handlers should be
  359  * modified to accept the pointer to their private data, too, instead of
  360  * an integer index.
  361  *
  362  * There will be functions that derive a driver and unit name from a
  363  * dev_instance variable, and those functions will be used to maintain the
  364  * interrupt counter label array referenced by systat and vmstat to report
  365  * device interrupt rates (->update_intrlabels).
  366  */
  367 
  368 intrec *
  369 intr_create(void *dev_instance, int irq, inthand2_t handler, void *arg,
  370              intrmask_t *maskptr, int flags)
  371 {
  372         intrec *idesc;
  373 
  374         if (ICU_LEN > 8 * sizeof *maskptr) {
  375                 printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
  376                        ICU_LEN, 8 * sizeof *maskptr);
  377                 return (NULL);
  378         }
  379         if ((unsigned)irq >= ICU_LEN) {
  380                 printf("create_intr: requested irq%d too high, limit is %d\n",
  381                        irq, ICU_LEN -1);
  382                 return (NULL);
  383         }
  384 
  385         idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
  386         if (idesc) {
  387                 idesc->next     = NULL;
  388                 bzero(idesc, sizeof *idesc);
  389 
  390                 idesc->devdata  = dev_instance;
  391                 idesc->handler  = handler;
  392                 idesc->argument = arg;
  393                 idesc->maskptr  = maskptr;
  394                 idesc->intr     = irq;
  395                 idesc->flags    = flags;
  396         }
  397         return (idesc);
  398 }
  399 
  400 /*
  401  * Return the memory held by the interrupt handler descriptor data structure
  402  * to the system. Make sure, the handler is not actively used anymore, before.
  403  */
  404 
  405 int
  406 intr_destroy(intrec *rec)
  407 {
  408         if (intr_disconnect(rec) != 0)
  409                 return (-1);
  410         free(rec, M_DEVBUF);
  411         return (0);
  412 }
  413 
  414 /*
  415  * Emulate the register_intr() call previously defined as low level function.
  416  * That function (now icu_setup()) may no longer be directly called, since 
  417  * a conflict between an ISA and PCI interrupt might go by unnocticed, else.
  418  */
  419 
  420 int
  421 register_intr(int intr, int device_id, u_int flags,
  422               inthand2_t handler, u_int *maskptr, int unit)
  423 {
  424         /* XXX modify to include isa_device instead of device_id */
  425         intrec *idesc;
  426 
  427         flags |= INTR_EXCL;
  428         idesc = intr_create((void *)(intptr_t)device_id, intr, handler, 
  429                             (void*)(intptr_t)unit, maskptr, flags);
  430         return (intr_connect(idesc));
  431 }
  432 
  433 /*
  434  * Emulate the old unregister_intr() low level function. 
  435  * Make sure there is just one interrupt, that it was 
  436  * registered as non-shared, and that the handlers match.
  437  */
  438 
  439 int
  440 unregister_intr(int intr, inthand2_t handler)
  441 {
  442         intrec *p = intreclist_head[intr];
  443 
  444         if (p != NULL && (p->flags & INTR_EXCL) != 0 && p->handler == handler)
  445                 return (intr_destroy(p));
  446         return (EINVAL);
  447 }
  448 
  449 #endif /* __i386__ */
  450 
  451 void
  452 register_swi(intr, handler)
  453         int intr;
  454         swihand_t *handler;
  455 {
  456         struct swilist *slp, *slq;
  457         int s;
  458 
  459         if (intr < NHWI || intr >= NHWI + NSWI)
  460                 panic("register_swi: bad intr %d", intr);
  461         if (handler == swi_generic || handler == swi_null)
  462                 panic("register_swi: bad handler %p", (void *)handler);
  463         slp = &swilists[intr - NHWI];
  464         s = splhigh();
  465         if (ihandlers[intr] == swi_null)
  466                 ihandlers[intr] = handler;
  467         else {
  468                 if (slp->sl_next == NULL) {
  469                         slp->sl_handler = ihandlers[intr];
  470                         ihandlers[intr] = swi_generic;
  471                 }
  472                 slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT);
  473                 if (slq == NULL)
  474                         panic("register_swi: malloc failed");
  475                 slq->sl_handler = handler;
  476                 slq->sl_next = NULL;
  477                 while (slp->sl_next != NULL)
  478                         slp = slp->sl_next;
  479                 slp->sl_next = slq;
  480         }
  481         splx(s);
  482 }
  483 
  484 void
  485 swi_dispatcher(intr)
  486         int intr;
  487 {
  488         struct swilist *slp;
  489 
  490         slp = &swilists[intr - NHWI];
  491         do {
  492                 (*slp->sl_handler)();
  493                 slp = slp->sl_next;
  494         } while (slp != NULL);
  495 }
  496 
  497 void
  498 unregister_swi(intr, handler)
  499         int intr;
  500         swihand_t *handler;
  501 {
  502         struct swilist *slfoundpred, *slp, *slq;
  503         int s;
  504 
  505         if (intr < NHWI || intr >= NHWI + NSWI)
  506                 panic("unregister_swi: bad intr %d", intr);
  507         if (handler == swi_generic || handler == swi_null)
  508                 panic("unregister_swi: bad handler %p", (void *)handler);
  509         slp = &swilists[intr - NHWI];
  510         s = splhigh();
  511         if (ihandlers[intr] == handler)
  512                 ihandlers[intr] = swi_null;
  513         else if (slp->sl_next != NULL) {
  514                 slfoundpred = NULL;
  515                 for (slq = slp->sl_next; slq != NULL;
  516                     slp = slq, slq = slp->sl_next)
  517                         if (slq->sl_handler == handler)
  518                                 slfoundpred = slp;
  519                 slp = &swilists[intr - NHWI];
  520                 if (slfoundpred != NULL) {
  521                         slq = slfoundpred->sl_next;
  522                         slfoundpred->sl_next = slq->sl_next;
  523                         free(slq, M_DEVBUF);
  524                 } else if (slp->sl_handler == handler) {
  525                         slq = slp->sl_next;
  526                         slp->sl_next = slq->sl_next;
  527                         slp->sl_handler = slq->sl_handler;
  528                         free(slq, M_DEVBUF);
  529                 }
  530                 if (slp->sl_next == NULL)
  531                         ihandlers[intr] = slp->sl_handler;
  532         }
  533         splx(s);
  534 }
  535 

Cache object: 1215ee433c02abf3fa5cdd8d24eafbe3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.