DDI_INTR_DUP_HANDLER(9F)                        Kernel Functions for Drivers
NAME
       ddi_intr_dup_handler - reuse interrupt handler and arguments for MSI-
       X interrupts
SYNOPSIS
       #include <sys/types.h>
       #include <sys/conf.h>
       #include <sys/ddi.h>
       #include <sys/sunddi.h>       
int ddi_intr_dup_handler(
ddi_intr_handle_t primary, 
int vector,            
ddi_intr_handle_t *new);
INTERFACE LEVEL
       illumos DDI specific (illumos DDI).
PARAMETERS
       primary                  Original DDI interrupt handle       
vector                  Interrupt number to duplicate       
new                  Pointer to new DDI interrupt handle
DESCRIPTION
       The 
ddi_intr_dup_handler() function is a feature for MSI-X interrupts
       that allows an unallocated interrupt vector of a device to use a
       previously initialized or added primary MSI-X interrupt vector in
       order to share the same vector address, vector data, interrupt
       handler, and handler arguments. This feature allows a driver to alias
       the resources provided by the illumos Operating System to the
       unallocated interrupt vectors on an associated device.  For example,
       if 2 MSI-X interrupts were allocated to a driver and 32 interrupts
       were supported on the device, the driver could alias the 2 interrupts
       it received to the 30 remaining on the device.
       The 
ddi_intr_dup_handler() function must be called after the primary
       interrupt handle has been added to the system or enabled by       
ddi_intr_add_handler(9F) and 
ddi_intr_enable(9F) calls, respectively.
       If successful, the function returns the new interrupt handle for a
       given vector in the 
new argument passed to the function. The new
       interrupt handle must not have been previously allocated with       
ddi_intr_alloc(9F). Otherwise, the 
ddi_intr_dup_handler() call will
       fail.
       The only supported calls on 
dup-ed interrupt handles are       
ddi_intr_set_mask(9F), 
ddi_intr_clr_mask(9F),       
ddi_intr_get_pending(9F), 
ddi_intr_enable(9F), 
ddi_intr_disable(9F),
       and 
ddi_intr_free(9F).
       A call to 
ddi_intr_dup_handler() does not imply that the interrupt
       source is automatically enabled. Initially, the dup-ed handle is in
       the disabled state and must be enabled before it can be used by
       calling 
ddi_intr_enable().  Likewise, 
ddi_intr_disable() must be
       called to disable the enabled dup-ed interrupt source.
       A dup-ed interrupt is removed by calling 
ddi_intr_free() after it has
       been disabled. The 
ddi_intr_remove_handler(9F) call is not required
       for a dup-ed handle.
       Before removing the original MSI-X interrupt handler, all dup-ed
       interrupt handlers associated with this MSI-X interrupt must have
       been disabled and freed. Otherwise, calls to       
ddi_intr_remove_handler() will fail with 
DDI_FAILURE.
       See the EXAMPLES section for code that illustrates the use of the       
ddi_intr_dup_handler() function.
RETURN VALUES
       The 
ddi_intr_dup_handler() function returns:       
DDI_SUCCESS                      On success.
                      Note that the interface should be verified to ensure
                      that the return value is not equal to 
DDI_SUCCESS.
                      Incomplete checking for failure codes could result in
                      inconsistent behavior among platforms.       
DDI_EINVAL                      On encountering invalid input parameters. 
DDI_EINVAL                      is also returned if a dup is attempted from a dup-ed
                      interrupt or if the hardware device is found not to
                      support MSI-X interrupts.       
DDI_FAILURE                      On any implementation specific failure.
EXAMPLES
       Example 1: Using the ddi_intr_dup_handler() function
         int
         add_msix_interrupts(intr_state_t *state)
         {
           int x, y;
           /*
            * For this example, assume the device supports multiple
            * interrupt vectors, but only request to be allocated
            * 1 MSI-X to use and then dup the rest.
            */
           if (ddi_intr_get_nintrs(state->dip, DDI_INTR_TYPE_MSIX,
              &state->intr_count) != DDI_SUCCESS) {
                   cmn_err(CE_WARN, "Failed to retrieve the MSI-X interrupt count");
                   return (DDI_FAILURE);
           }
           state->intr_size = state->intr_count * sizeof (ddi_intr_handle_t);
           state->intr_htable = kmem_zalloc(state->intr_size, KM_SLEEP);
           /* Allocate one MSI-X interrupt handle */
           if (ddi_intr_alloc(state->dip, state->intr_htable,
               DDI_INTR_TYPE_MSIX, state->inum, 1, &state->actual,
               DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
                   cmn_err(CE_WARN, "Failed to allocate MSI-X interrupt");
                   kmem_free(state->intr_htable, state->intr_size);
                   return (DDI_FAILURE);
           }
           /* Get the count of how many MSI-X interrupts we dup */
           state->dup_cnt = state->intr_count - state->actual;
           if (ddi_intr_get_pri(state->intr_htable[0],
               &state->intr_pri) != DDI_SUCCESS) {
                   cmn_err(CE_WARN, "Failed to get interrupt priority");
                   goto error1;
           }
           /* Make sure the MSI-X priority is below 'high level' */
           if (state->intr_pri >= ddi_intr_get_hilevel_pri()) {
                  cmn_err(CE_WARN, "Interrupt PRI is too high");
                   goto error1;
           }
           /*
            * Add the handler for the interrupt
            */
           if (ddi_intr_add_handler(state->intr_htable[0],
               (ddi_intr_handler_t *)intr_isr, (caddr_t)state,
               NULL) != DDI_SUCCESS) {
                   cmn_err(CE_WARN, "Failed to add interrupt handler");
                   goto error1;
           }
           /* Enable the main MSI-X handle first */
           if (ddi_intr_enable(state->intr_htable[0]) != DDI_SUCCESS) {
                   cmn_err(CE_WARN, "Failed to enable interrupt");
                   goto error2;
           }
           /*
            * Create and enable dups of the original MSI-X handler, note
            * that the inum we are using starts at 0.
            */
           for (x = 1; x < state->dup_cnt; x++) {
               if (ddi_intr_dup_handler(state->intr_htable[0],
                   state->inum + x, &state->intr_htable[x]) != DDI_SUCCESS) {
                       for (y = x - 1; y > 0; y--) {
                           (void) ddi_intr_disable(state->intr_htable[y]);
                           (void) ddi_intr_free(state->intr_htable[y]);
                       }
                   goto error2;
               }
               if (ddi_intr_enable(state->intr_htable[x]) != DDI_SUCCESS) {
                   for (y = x; y > 0; y--) {
                       (void) ddi_intr_disable(state->intr_htable[y]);
                       (void) ddi_intr_free(state->intr_htable[y]);
                   }
                   goto error2;
               }
           }
           return (DDI_SUCCESS);
         error2:
             (void) ddi_intr_remove_handler(state->intr_htable[0]);
         error1:
             (void) ddi_intr_free(state->intr_htable[0]);
             kmem_free(state->intr_htable, state->intr_size);
             return (DDI_FAILURE);
         }
         void
         remove_msix_interrupts(intr_state_t *state)
         {
             int x;
             /*
              * Disable all the handles and free the dup-ed handles
              * before we can remove the main MSI-X interrupt handle.
              */
             for (x = 1; x < state->dup_cnt; x++) {
                 (void) ddi_intr_disable(state->intr_htable[x]);
                 (void) ddi_intr_free(state->intr_htable[x]);
             }
             /*
              * We can remove and free the main MSI-X handler now
              * that all the dups have been freed.
              */
             (void) ddi_intr_disable(state->intr_htable[0]);
             (void) ddi_intr_remove_handler(state->intr_htable[0]);
             (void) ddi_intr_free(state->intr_htable[0]);
             kmem_free(state->intr_htable, state->intr_size);
         }
CONTEXT
       The 
ddi_intr_dup_handler() function can be called from kernel non-
       interrupt context.
ATTRIBUTES
       See 
attributes(7) for descriptions of the following attributes:
       +--------------------+-----------------+
       |  ATTRIBUTE TYPE    | ATTRIBUTE VALUE |
       +--------------------+-----------------+
       |Interface Stability | Committed       |
       +--------------------+-----------------+
SEE ALSO
       attributes(7), 
ddi_intr_add_handler(9F), 
ddi_intr_alloc(9F),       
ddi_intr_clr_mask(9F), 
ddi_intr_disable(9F), 
ddi_intr_enable(9F),       
ddi_intr_free(9F), 
ddi_intr_get_pending(9F),       
ddi_intr_get_supported_types(9F), 
ddi_intr_set_mask(9F)       Writing Device Drivers                                 May 9, 2006        DDI_INTR_DUP_HANDLER(9F)