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]
#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);
Original DDI interrupt handle
Interrupt number to duplicate
Pointer to new DDI interrupt handle
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.
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.
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.
On any implementation specific failure.
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); }
ATTRIBUTE TYPE ATTRIBUTE VALUE |
Interface Stability Committed |
Writing Device Drivers