'\" te
.\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
.TH DDI_INTR_DUP_HANDLER 9F "May 09, 2006"
.SH NAME
ddi_intr_dup_handler \- reuse interrupt handler and arguments for MSI-X
interrupts
.SH SYNOPSIS
.LP
.nf
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

\fBint\fR \fBddi_intr_dup_handler\fR(\fBddi_intr_handle_t\fR \fIprimary\fR, \fBint\fR \fIvector\fR,
     \fBddi_intr_handle_t *\fR\fInew\fR);
.fi

.SH INTERFACE LEVEL
.sp
.LP
Solaris DDI specific (Solaris DDI).
.SH PARAMETERS
.sp
.ne 2
.na
\fB\fIprimary\fR\fR
.ad
.RS 11n
Original DDI interrupt handle
.RE

.sp
.ne 2
.na
\fB\fIvector\fR\fR
.ad
.RS 11n
Interrupt number to duplicate
.RE

.sp
.ne 2
.na
\fB\fInew\fR\fR
.ad
.RS 11n
Pointer to new DDI interrupt handle
.RE

.SH DESCRIPTION
.sp
.LP
The \fBddi_intr_dup_handler()\fR 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 Solaris
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.
.sp
.LP
The \fBddi_intr_dup_handler()\fR function must be called after the primary
interrupt handle has been added to the system or enabled by
\fBddi_intr_add_handler\fR(9F) and \fBddi_intr_enable\fR(9F) calls,
respectively. If successful, the function returns the new interrupt handle for
a given vector in the \fInew\fR argument passed to the function. The new
interrupt handle must not have been previously allocated with
\fBddi_intr_alloc\fR(9F). Otherwise, the \fBddi_intr_dup_handler()\fR call will
fail.
.sp
.LP
The only supported calls on \fIdup-ed\fR interrupt handles are
\fBddi_intr_set_mask\fR(9F), \fBddi_intr_clr_mask\fR(9F),
\fBddi_intr_get_pending\fR(9F), \fBddi_intr_enable\fR(9F),
\fBddi_intr_disable\fR(9F), and \fBddi_intr_free\fR(9F).
.sp
.LP
A call to \fBddi_intr_dup_handler()\fR 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 \fBddi_intr_enable()\fR.
Likewise, \fBddi_intr_disable()\fR must be called to disable the enabled dup-ed
interrupt source.
.sp
.LP
A dup-ed interrupt is removed by calling \fBddi_intr_free()\fR after it has
been disabled. The \fBddi_intr_remove_handler\fR(9F) call is not required for a
dup-ed handle.
.sp
.LP
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 \fBddi_intr_remove_handler()\fR will fail with
\fBDDI_FAILURE\fR.
.sp
.LP
See the EXAMPLES section for code that illustrates the use of the
\fBddi_intr_dup_handler()\fR function.
.SH RETURN VALUES
.sp
.LP
The \fBddi_intr_dup_handler()\fR function returns:
.sp
.ne 2
.na
\fB\fBDDI_SUCCESS\fR\fR
.ad
.RS 15n
On success.
.sp
Note that the interface should be verified to ensure that the return value is
not equal to \fBDDI_SUCCESS\fR. Incomplete checking for failure codes could
result in inconsistent behavior among platforms.
.RE

.sp
.ne 2
.na
\fB\fBDDI_EINVAL\fR\fR
.ad
.RS 15n
On encountering invalid input parameters. \fBDDI_EINVAL\fR 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.
.RE

.sp
.ne 2
.na
\fB\fBDDI_FAILURE\fR\fR
.ad
.RS 15n
On any implementation specific failure.
.RE

.SH EXAMPLES
.LP
\fBExample 1 \fRUsing the \fBddi_intr_dup_handler()\fR function
.sp
.in +2
.nf
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);
}
.fi
.in -2

.SH CONTEXT
.sp
.LP
The \fBddi_intr_dup_handler()\fR function can be called from kernel
non-interrupt context.
.SH ATTRIBUTES
.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp

.sp
.TS
box;
c | c
l | l .
ATTRIBUTE TYPE	ATTRIBUTE VALUE
_
Interface Stability	Committed
.TE

.SH SEE ALSO
.sp
.LP
\fBattributes\fR(5), \fBddi_intr_add_handler\fR(9F), \fBddi_intr_alloc\fR(9F),
\fBddi_intr_clr_mask\fR(9F), \fBddi_intr_disable\fR(9F),
\fBddi_intr_enable\fR(9F), \fBddi_intr_free\fR(9F),
\fBddi_intr_get_pending\fR(9F), \fBddi_intr_get_supported_types\fR(9F),
\fBddi_intr_set_mask\fR(9F)
.sp
.LP
\fIWriting Device Drivers\fR