Anish's Weblog
Anish's Weblog

20050624 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
    • ddi_intr_handle_t
  •  The 'Soft Interrupts' use this handle
    • ddi_intr_soft_handle_t

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:
  1. ddi_intr_alloc(9F)
  2. ddi_intr_add_handler(9F)
  3. 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:
  1. ddi_intr_alloc(9F)
  2. ddi_intr_get_pri(9F)
  3. ddi_intr_free(9F)
ddi_get_soft_iblock_cookie(9F) Three-step process:
  1. ddi_intr_add_softint(9F)
  2. ddi_intr_get_softint_pri(9F)
  3. 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:
Technorati Tag: Posted by anish ( Jun 24 2005, 09:00:02 AM PDT ) Permalink Comments [0]

Trackback URL: http://blogs.sun.com/anish/entry/device_driver_development_how_to
Comments:

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed

Archives
Language
Links
Referrers