Writing Device Drivers

High-Level Interrupt Handling Example

In the example presented in Example 7-3, the high-level mutex (xsp->high_mu) is used only to protect data shared between the high-level interrupt handler and the soft interrupt handler. This includes a queue that the high-level interrupt handler appends data to (and the low-level handler removes data from), and a flag that indicates the low-level handler is running. A separate low-level mutex (xsp->low_mu) protects the rest of the driver from the soft interrupt handler.


Example 7-3 attach(9E) Routine Handling High-Level Interrupts

static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
        struct xxstate *xsp;
        ...
        if (ddi_intr_hilevel(dip, inumber)) {
            ddi_get_iblock_cookie(dip, inumber,
                &xsp->high_iblock_cookie);
            mutex_init(&xsp->high_mu, NULL, MUTEX_DRIVER,
                (void *)xsp->high_iblock_cookie);
            if (ddi_add_intr(dip, inumber, &xsp->high_iblock_cookie,
                &xsp->high_idevice_cookie, xxhighintr, (caddr_t)xsp)
                != DDI_SUCCESS)
                goto failed;
            ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HI,
                &xsp->low_iblock_cookie)
            mutex_init(&xsp->low_mu, NULL, MUTEX_DRIVER,
                (void *)xsp->low_iblock_cookie);
            if (ddi_add_softintr(dip, DDI_SOFTINT_HI, &xsp->id,
                &xsp->low_iblock_cookie, NULL,
                xxlowintr, (caddr_t)xsp) != DDI_SUCCESS)
                goto failed;
        } else {
            add normal interrupt handler
        }
        cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);
        ...
        return (DDI_SUCCESS);
    failed:
            free allocated resources, remove interrupt handlers
        return (DDI_FAILURE);
}

The high-level interrupt routine services the device, and enqueues the data. The high-level routine triggers a software interrupt if the low-level routine is not running, as Example 7-4 demonstrates.


Example 7-4 High-level Interrupt Routine

static uint_t
xxhighintr(caddr_t arg)
{
        struct xxstate    *xsp = (struct xxstate *)arg;
            uint8_t        status;
            volatile  uint8_t  temp;
            int        need_softint;
    
     mutex_enter(&xsp->high_mu);
     /* read status */
     status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
     if (!(status & INTERRUPTING)) {
             mutex_exit(&xsp->high_mu);
              return (DDI_INTR_UNCLAIMED); /* dev not interrupting */
     }

     ddi_put8(xsp->data_access_handle,&xsp->regp->csr,
             CLEAR_INTERRUPT | ENABLE_INTERRUPTS);
                /* flush store buffers */
     temp = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
      read data from device and queue the data for the low-level interrupt handler;
     if (xsp->softint_running)
            need_softint = 0;
     else {
            xsp->softint_count++;
            need_softint = 1;
     mutex_exit(&xsp->high_mu);
     /* read-only access to xsp->id, no mutex needed */
     if (need_softint)
             ddi_trigger_softintr(xsp->id);
     return (DDI_INTR_CLAIMED);
}

The low-level interrupt routine is started by the high-level interrupt routine triggering a software interrupt. Once running, it should continue to do so until there is nothing left to process, as Example 7-5 shows.


Example 7-5 Low-level Interrupt Routine

static uint_t
xxlowintr(caddr_t arg)
{
     struct xxstate *xsp = (struct xxstate *)arg;
     ....
     mutex_enter(&xsp->low_mu);
     mutex_enter(&xsp->high_mu);
        if (xsp->softint_count > 1) {
            xsp->softint_count--;
            return (DDI_INTR_CLAIMED);
    }
       if (queue empty) {
             mutex_exit(&xsp->high_mu);
             mutex_exit(&xsp->low_mu);
             return (DDI_INTR_UNCLAIMED);
     }
     xsp->softint_running = 1;
     while (data on queue) {
              ASSERT(mutex_owned(&xsp->high_mu);
            dequeue data from high-level queue;
            mutex_exit(&xsp->high_mu);
            normal interrupt processing
            mutex_enter(&xsp->high_mu);
     }
     xsp->softint_running = 0;
    xsp->softint_count = 0;
     mutex_exit(&xsp->high_mu);
     mutex_exit(&xsp->low_mu);
   return (DDI_INTR_CLAIMED);
}