
Friday June 24, 2005
Device Driver Development: How to add interrupts in a driver
Device Driver Development: How to add interrupts
New DDI interrupt interfaces
This writeup is about the new DDI interrupt interfaces introduced in OpenSolaris.
The older DDI interrupt interfaces are being replaced for the following
reasons:
- Support new interrupt types like Message Signaled interrupts -
MSIs and MSI-Xs
- Support new features like get/set priority, get/set
device
interrupt capability, get interrupt pending information, set/clear
interrupt mask etc.
- Support new Bus technologies like PCI-Express
- Have a generic framework that can support other new
(and unknown) interrupts where possible
- Support for multiple interrupts by a single device/function
- Existing interfaces are antiquated and provide limited
features.
IMPORTANT:
See Advanced
Interrupt Handlers added recently to
developer.sun.com that replaced most
of this blog.
Topics not covered there are still retained here.
New DDI Interrupt Data structures
See OpenSolaris source code
that
defines data structures to be used with the new DDI interrupt
framework:
- Interrupt handles
- Interrupt handler
- Priority for Normal and Soft interrupts
Interrupt handles
All DDI interrupt interfaces now take an interrupt handle as argument.
- The 'Normal Interrupts' shown above use this handle
- The 'Soft Interrupts' use this handle
Interrupt handler
The interrupt handler has been modified to take two arguments /* * Typedef for driver's interrupt handler */ typedef int (ddi_intr_handler_t)(void *arg1, void *arg2);
Interrupt Priority
Normal interrupt priority should be within the two defines shown below: #define DDI_INTR_PRI_MIN 1 #define DDI_INTR_PRI_MAX 12
priority is a small integer range from DDI_INTR_PRI_MIN to DDI_INTR_PRI_MAX for most drivers and represents virtual priority. It can be used directly in lock initialization calls: mutex_init, rw_init, etc.
Soft interrupt priority should be within the two defines shown below: #define DDI_INTR_SOFTPRI_MIN 1 #define DDI_INTR_SOFTPRI_MAX 9
By default most drivers could use the default Soft priority which is defined as #define DDI_INTR_SOFTPRI_DEFAULT 1
Calls to mutex_int() should use DDI_INTR_PRI() macro #define DDI_INTR_PRI(pri) (void *)((uintptr_t)(pri))
How Legacy Interrupt Functions map to new interfaces
To take advantage of the features of the enhanced DDI interrupt
framework,
developers need to use these new
interfaces and
avoid using the following
legacy interfaces, which are retained for compatibility purposes
only.
| Legacy Interrupt
Functions |
Replacements (Recommended) |
|
ddi_add_intr(9F)
|
Three-step process:
- ddi_intr_alloc(9F)
- ddi_intr_add_handler(9F)
- ddi_intr_enable(9F)
|
| ddi_add_softintr(9F) |
ddi_intr_add_softint(9F) |
| ddi_dev_nintrs(9F) |
ddi_intr_get_nintrs(9F) |
| ddi_get_iblock_cookie(9F) |
Three-step process:
- ddi_intr_alloc(9F)
- ddi_intr_get_pri(9F)
- ddi_intr_free(9F)
|
| ddi_get_soft_iblock_cookie(9F) |
Three-step process:
- ddi_intr_add_softint(9F)
- ddi_intr_get_softint_pri(9F)
- ddi_intr_remove_softint(9F)
|
| ddi_intr_hilevel(9F) |
ddi_intr_get_hilevel_pri(9F) |
| ddi_remove_intr(9F) |
ddi_intr_remove_handler(9F) |
| ddi_remove_softintr(9F) |
ddi_intr_remove_softint(9F) |
| ddi_trigger_softintr(9F) |
ddi_intr_trigger_softint(9F) |
| ddi_idevice_cookie(9S) |
Not applicable |
ddi_iblock_cookie(9S)
for mutex_init(9F) etc.
|
(void *)(uintptr_t) |
New Interrupt Interface Examples
This section provides examples for performing the following tasks:
- Changing soft interrupt priority
- Checking for pending interrupts
- Setting interrupt masks
- Clearing interrupt masks
See Advanced
Interrupt Handlers Article for examples on other interfaces.
Example: Using the ddi_inter_set_softint_pri()
Function
/* Change the soft interrupt priority to 9 */ if (ddi_intr_set_softint_pri(mydev->mydev_softint_hdl, 9) != DDI_SUCCESS) { cmn_err (CE_WARN, "ddi_intr_set_softint_pri failed"); }
Example: Using the ddi_intr_get_pending()
Function
/* Check if an interrupt is pending */ if (ddi_intr_get_pending(mydevp->htable[0], &pending) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_pending() failed"); } else if (pending) cmn_err(CE_NOTE, "ddi_intr_get_pending(): Interrupt pending");
Example: Using the ddi_intr_set_mask() Function
/* Set interrupt masking to prevent device from receiving interrupts */ if ((ddi_intr_set_mask(mydevp->htable[0]) != DDI_SUCCESS)) cmn_err(CE_WARN, "ddi_intr_set_mask() failed");
Example: Using the ddi_intr_clr_mask() Function
/* Clear interrupt masking. If successful device will start generating interrupts */ if (ddi_intr_clr_mask(mydevp->htable[0]) != DDI_SUCCESS) cmn_err(CE_WARN, "ddi_intr_clr_mask() failed");
Reference:
1. See source code bge_main.c
from OpenSolaris that has
examples on how to use the
new DDI interrupt interfaces for both Legacy
interrupts and MSI interrupts
2. See blog on MSI
and MSI-X
3. See blog on How
Interrupts work on Solaris on x86 platforms
4. New article from developer.sun.com: Advanced
Interrupt Handlers
Technorati Tag:
OpenSolaris
Technorati Tag:
Solaris
Posted by anish
( Jun 24 2005, 09:00:02 AM PDT )
Permalink
Trackback URL: http://blogs.sun.com/anish/entry/device_driver_development_how_to
|