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.
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.
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.
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);
}