root/usr/src/uts/common/io/1394/adapters/hci1394_ixl_isr.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1999-2002 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * hci1394_ixl_isr.c
 *    Isochronous IXL Interrupt Service Routines.
 *    The interrupt handler determines which OpenHCI DMA descriptors
 *    have been executed by the hardware, tracks the path in the
 *    corresponding IXL program, issues callbacks as needed, and resets
 *    the OpenHCI DMA descriptors.
 */

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ixl1394.h>
#include <sys/1394/adapters/hci1394.h>


/* Return values for local hci1394_ixl_intr_check_done() */
#define IXL_CHECK_LOST  (-1)    /* ixl cmd intr processing lost */
#define IXL_CHECK_DONE  0       /* ixl cmd intr processing done */
#define IXL_CHECK_SKIP  1       /* ixl cmd intr processing context skipped */
#define IXL_CHECK_STOP  2       /* ixl cmd intr processing context stopped */

static boolean_t hci1394_ixl_intr_check_xfer(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp, ixl1394_command_t *ixlp,
    ixl1394_command_t **ixlnextpp, uint16_t *timestampp, int *donecodep);
static int hci1394_ixl_intr_check_done(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp);

/*
 * hci1394_ixl_interrupt
 *    main entry point (front-end) into interrupt processing.
 *    acquires mutex, checks if update in progress, sets flags accordingly,
 *    and calls to do real interrupt processing.
 */
void
hci1394_ixl_interrupt(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp, boolean_t in_stop)
{
        uint_t  status;
        int     retcode;

        status = 1;

        /* acquire the interrupt processing context mutex */
        mutex_enter(&ctxtp->intrprocmutex);

        /* set flag to indicate that interrupt processing is required */
        ctxtp->intr_flags |= HCI1394_ISO_CTXT_INTRSET;

        /* if update proc already in progress, let it handle intr processing */
        if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INUPDATE) {
                retcode = HCI1394_IXL_INTR_INUPDATE;
                status = 0;
        } else if (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
                /* else fatal error if inter processing already in progress */
                retcode = HCI1394_IXL_INTR_ININTR;
                status = 0;
        } else if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INCALL) {
                /* else fatal error if callback in progress flag is set */
                retcode = HCI1394_IXL_INTR_INCALL;
                status = 0;
        } else if (!in_stop && (ctxtp->intr_flags & HCI1394_ISO_CTXT_STOP)) {
                /* context is being stopped */
                retcode = HCI1394_IXL_INTR_STOP;
                status = 0;
        }

        /*
         * if context is available, reserve it, do interrupt processing
         * and free it
         */
        if (status) {
                ctxtp->intr_flags |= HCI1394_ISO_CTXT_ININTR;
                ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_INTRSET;
                mutex_exit(&ctxtp->intrprocmutex);

                retcode = hci1394_ixl_dma_sync(soft_statep, ctxtp);

                mutex_enter(&ctxtp->intrprocmutex);
                ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_ININTR;

                /* notify stop thread that the interrupt is finished */
                if ((ctxtp->intr_flags & HCI1394_ISO_CTXT_STOP) && !in_stop) {
                        cv_signal(&ctxtp->intr_cv);
                }
        };

        /* free the intr processing context mutex before error checks */
        mutex_exit(&ctxtp->intrprocmutex);

        /* if context stopped, invoke callback */
        if (retcode == HCI1394_IXL_INTR_DMASTOP) {
                hci1394_do_stop(soft_statep, ctxtp, B_TRUE, ID1394_DONE);
        }
        /* if error, stop and invoke callback */
        if (retcode == HCI1394_IXL_INTR_DMALOST) {
                hci1394_do_stop(soft_statep, ctxtp, B_TRUE, ID1394_FAIL);
        }
}

/*
 * hci1394_ixl_dma_sync()
 *    the heart of interrupt processing, this routine correlates where the
 *    hardware is for the specified context with the IXL program.  Invokes
 *    callbacks as needed.  Also called by "update" to make sure ixl is
 *    sync'ed up with where the hardware is.
 *    Returns one of the ixl_intr defined return codes - HCI1394_IXL_INTR...
 *    {..._DMALOST, ..._DMASTOP, ..._NOADV,... _NOERROR}
 */
int
hci1394_ixl_dma_sync(hci1394_state_t *soft_statep, hci1394_iso_ctxt_t *ctxtp)
{
        ixl1394_command_t *ixlp = NULL; /* current ixl command */
        ixl1394_command_t *ixlnextp;    /* next ixl command */
        uint16_t        ixlopcode;
        uint16_t        timestamp;
        int             donecode;
        boolean_t       isdone;

        void (*callback)(opaque_t, struct ixl1394_callback *);

        ASSERT(MUTEX_NOT_HELD(&ctxtp->intrprocmutex));

        /* xfer start ixl cmd where last left off */
        ixlnextp = ctxtp->ixl_execp;

        /* last completed descriptor block's timestamp  */
        timestamp = ctxtp->dma_last_time;

        /*
         * follow execution path in IXL, until find dma descriptor in IXL
         * xfer command whose status isn't set or until run out of IXL cmds
         */
        while (ixlnextp != NULL) {
                ixlp = ixlnextp;
                ixlnextp = ixlp->next_ixlp;
                ixlopcode = ixlp->ixl_opcode & ~IXL1394_OPF_UPDATE;

                /*
                 * process IXL commands: xfer start, callback, store timestamp
                 * and jump and ignore the others
                 */

                /* determine if this is an xfer start IXL command */
                if (((ixlopcode & IXL1394_OPF_ISXFER) != 0) &&
                    ((ixlopcode & IXL1394_OPTY_MASK) != 0)) {

                        /* process xfer cmd to see if HW has been here */
                        isdone = hci1394_ixl_intr_check_xfer(soft_statep, ctxtp,
                            ixlp, &ixlnextp, &timestamp, &donecode);

                        if (isdone == B_TRUE) {
                                return (donecode);
                        }

                        /* continue to process next IXL command */
                        continue;
                }

                /* else check if IXL cmd - jump, callback or store timestamp */
                switch (ixlopcode) {
                case IXL1394_OP_JUMP:
                        /*
                         * set next IXL cmd to label ptr in current IXL jump cmd
                         */
                        ixlnextp = ((ixl1394_jump_t *)ixlp)->label;
                        break;

                case IXL1394_OP_STORE_TIMESTAMP:
                        /*
                         * set last timestamp value recorded into current IXL
                         * cmd
                         */
                        ((ixl1394_store_timestamp_t *)ixlp)->timestamp =
                            timestamp;
                        break;

                case IXL1394_OP_CALLBACK:
                        /*
                         * if callback function is specified, call it with IXL
                         * cmd addr.  Make sure to grab the lock before setting
                         * the "in callback" flag in intr_flags.
                         */
                        mutex_enter(&ctxtp->intrprocmutex);
                        ctxtp->intr_flags |= HCI1394_ISO_CTXT_INCALL;
                        mutex_exit(&ctxtp->intrprocmutex);

                        callback = ((ixl1394_callback_t *)ixlp)->callback;
                        if (callback != NULL) {
                                callback(ctxtp->global_callback_arg,
                                    (ixl1394_callback_t *)ixlp);
                        }

                        /*
                         * And grab the lock again before clearing
                         * the "in callback" flag.
                         */
                        mutex_enter(&ctxtp->intrprocmutex);
                        ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_INCALL;
                        mutex_exit(&ctxtp->intrprocmutex);
                        break;
                }
        }

        /*
         * If we jumped to NULL because of an updateable JUMP, set ixl_execp
         * back to ixlp.  The destination label might get updated to a
         * non-NULL value.
         */
        if ((ixlp != NULL) && (ixlp->ixl_opcode == IXL1394_OP_JUMP_U)) {
                ctxtp->ixl_execp = ixlp;
                return (HCI1394_IXL_INTR_NOERROR);
        }

        /* save null IXL cmd and depth and last timestamp */
        ctxtp->ixl_execp = NULL;
        ctxtp->ixl_exec_depth = 0;
        ctxtp->dma_last_time = timestamp;

        ctxtp->rem_noadv_intrs = 0;


        /* return stopped status if at end of IXL cmds & context stopped */
        if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
                return (HCI1394_IXL_INTR_DMASTOP);
        }

        /* else interrupt processing is lost */
        return (HCI1394_IXL_INTR_DMALOST);
}

/*
 * hci1394_ixl_intr_check_xfer()
 *    Process given IXL xfer cmd, checking status of each dma descriptor block
 *    for the command until find one whose status isn't set or until full depth
 *    reached at current IXL command or until find hardware skip has occurred.
 *
 *    Returns B_TRUE if processing should terminate (either have stopped
 *    or encountered an error), and B_FALSE if it should continue looking.
 *    If B_TRUE, donecodep contains the reason: HCI1394_IXL_INTR_DMALOST,
 *    HCI1394_IXL_INTR_DMASTOP, HCI1394_IXL_INTR_NOADV, or
 *    HCI1394_IXL_INTR_NOERROR.  NOERROR means that the current location
 *    has been determined and do not need to look further.
 */
static boolean_t
hci1394_ixl_intr_check_xfer(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp, ixl1394_command_t *ixlp,
    ixl1394_command_t **ixlnextpp, uint16_t *timestampp, int *donecodep)
{
        uint_t              dma_advances;
        int                 intrstatus;
        uint_t              skipped;
        hci1394_xfer_ctl_t  *xferctlp;
        uint16_t            ixldepth;
        uint16_t            ixlopcode;

        *donecodep = 0;
        dma_advances = 0;
        ixldepth = ctxtp->ixl_exec_depth;
        ixlopcode = ixlp->ixl_opcode & ~IXL1394_OPF_UPDATE;

        /* get control struct for this xfer start IXL command */
        xferctlp = (hci1394_xfer_ctl_t *)ixlp->compiler_privatep;

        skipped = 0;
        while ((skipped == 0) && (ixldepth < xferctlp->cnt)) {
                /*
                 * check if status is set in dma descriptor
                 * block at cur depth in cur xfer start IXL cmd
                 */
                if (hci1394_ixl_check_status(&xferctlp->dma[ixldepth],
                    ixlopcode, timestampp, B_TRUE) != 0) {

                        /* advance depth to next desc block in cur IXL cmd */
                        ixldepth++;

                        /*
                         * count dma desc blks whose status was set
                         * (i.e. advanced to next dma desc)
                         */
                        dma_advances++;
                        continue;
                }

                /* if get to here, status is not set */

                /*
                 * cur IXL cmd dma desc status not set.  save IXL cur cmd
                 * and depth and last timestamp for next time.
                 */
                ctxtp->ixl_execp = ixlp;
                ctxtp->ixl_exec_depth = ixldepth;
                ctxtp->dma_last_time = *timestampp;

                /*
                 * check if dma descriptor processing location is indeterminate
                 * (lost), context has either stopped, is done, or has skipped
                 */
                intrstatus = hci1394_ixl_intr_check_done(soft_statep, ctxtp);
                if (intrstatus == IXL_CHECK_LOST) {
                        /*
                         * location indeterminate, try once more to determine
                         * current state.  First, recheck if status has become
                         * set in cur dma descriptor block.  (don't reset status
                         * here if is set)
                         */
                        if (hci1394_ixl_check_status(&xferctlp->dma[ixldepth],
                            ixlopcode, timestampp, 1) != B_TRUE) {
                                /* resume from where we left off */
                                skipped = 0;
                                continue;
                        }

                        /*
                         * status not set, check intr processing
                         * completion status again
                         */
                        if ((intrstatus = hci1394_ixl_intr_check_done(
                                soft_statep, ctxtp)) == IXL_CHECK_LOST) {
                                /*
                                 * location still indeterminate,
                                 * processing is lost
                                 */
                                *donecodep = HCI1394_IXL_INTR_DMALOST;
                                return (B_TRUE);
                        }
                }

                /*
                 * if dma processing stopped. current location has been
                 * determined.
                 */
                if (intrstatus == IXL_CHECK_STOP) {
                        /*
                         * save timestamp, clear currently executing IXL
                         * command and depth. return stopped.
                         */
                        ctxtp->ixl_execp = NULL;
                        ctxtp->ixl_exec_depth = 0;
                        ctxtp->dma_last_time = *timestampp;
                        ctxtp->rem_noadv_intrs = 0;

                        *donecodep = HCI1394_IXL_INTR_DMASTOP;
                        return (B_TRUE);
                }

                /*
                 * dma processing done for now. current location has
                 * has been determined
                 */
                if (intrstatus == IXL_CHECK_DONE) {
                        /*
                         * if in update processing call:
                         *    clear update processing flag & return ok.
                         *    if dma advances happened, reset to max allowed.
                         *    however, if none have, don't reduce remaining
                         *    amount - that's for real interrupt call to adjust.
                         */
                        if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INUPDATE) {

                                if (dma_advances > 0) {
                                        ctxtp->rem_noadv_intrs =
                                            ctxtp->max_noadv_intrs;
                                }

                                *donecodep = HCI1394_IXL_INTR_NOERROR;
                                return (B_TRUE);
                        }

                        /*
                         * else, not in update call processing, are in normal
                         * intr call.  if no dma statuses were found set
                         * (i.e. no dma advances), reduce remaining count of
                         * interrupts allowed with no I/O completions
                         */
                        if (dma_advances == 0) {
                                ctxtp->rem_noadv_intrs--;
                        } else {
                                /*
                                 * else some dma statuses were found set.
                                 * reinit remaining count of interrupts allowed
                                 * with no I/O completions
                                 */
                                ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
                        }

                        /*
                         * if no remaining count of interrupts allowed with no
                         * I/O completions, return failure (no dma advance after
                         * max retries), else return ok
                         */
                        if (ctxtp->rem_noadv_intrs == 0) {
                                *donecodep = HCI1394_IXL_INTR_NOADV;
                                return (B_TRUE);
                        }

                        *donecodep = HCI1394_IXL_INTR_NOERROR;
                        return (B_TRUE);
                }

                /*
                 * else (intrstatus == IXL_CHECK_SKIP) indicating skip has
                 * occured, retrieve current IXL cmd, depth, and timestamp and
                 * continue interrupt processing
                 */
                skipped = 1;
                *ixlnextpp = ctxtp->ixl_execp;
                ixldepth = ctxtp->ixl_exec_depth;
                *timestampp = ctxtp->dma_last_time;

                /*
                 * also count as 1, intervening skips to next posted
                 * dma descriptor.
                 */
                dma_advances++;
        }

        /*
         * if full depth reached at current IXL cmd, set back to start for next
         * IXL xfer command that will be processed
         */
        if ((skipped == 0) && (ixldepth >= xferctlp->cnt)) {
                ctxtp->ixl_exec_depth = 0;
        }

        /*
         * make sure rem_noadv_intrs is reset to max if we advanced.
         */
        if (dma_advances > 0) {
                ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
        }

        /* continue to process next IXL command */
        return (B_FALSE);
}

/*
 * hci1394_ixl_intr_check_done()
 *    checks if context has stopped, or if able to match hardware location
 *    with an expected IXL program location.
 */
static int
hci1394_ixl_intr_check_done(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp)
{
        ixl1394_command_t   *ixlp;
        hci1394_xfer_ctl_t  *xferctlp;
        uint_t              ixldepth;
        hci1394_xfer_ctl_dma_t *dma;
        ddi_acc_handle_t    acc_hdl;
        ddi_dma_handle_t    dma_hdl;
        uint32_t            desc_status;
        hci1394_desc_t      *hcidescp;
        off_t               hcidesc_off;
        uint32_t            dma_cmd_cur_loc;
        uint32_t            dma_cmd_last_loc;
        uint32_t            dma_loc_check_enabled;
        uint32_t            dmastartp;
        uint32_t            dmaendp;

        uint_t              rem_dma_skips;
        uint16_t            skipmode;
        uint16_t            skipdepth;
        ixl1394_command_t   *skipdestp;
        ixl1394_command_t   *skipxferp;

        /*
         * start looking through the IXL list from the xfer start command where
         * we last left off (for composite opcodes, need to start from the
         * appropriate depth).
         */

        ixlp = ctxtp->ixl_execp;
        ixldepth = ctxtp->ixl_exec_depth;

        /* control struct for xfer start IXL command */
        xferctlp = (hci1394_xfer_ctl_t *)ixlp->compiler_privatep;
        dma = &xferctlp->dma[ixldepth];

        /* determine if dma location checking is enabled */
        if ((dma_loc_check_enabled =
            (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_CMDREG)) != 0) {

                /* if so, get current dma command location */
                dma_cmd_last_loc = 0xFFFFFFFF;

                while ((dma_cmd_cur_loc = HCI1394_ISOCH_CTXT_CMD_PTR(
                    soft_statep, ctxtp)) != dma_cmd_last_loc) {

                        /* retry get until location register stabilizes */
                        dma_cmd_last_loc = dma_cmd_cur_loc;
                }
        }

        /*
         * compare the (bound) address of the DMA descriptor corresponding to
         * the current xfer IXL command against the current value in the
         * DMA location register.  If exists and if matches, then
         *    if context stopped, return stopped, else return done.
         *
         * The dma start address is the first address of the descriptor block.
         * Since "Z" is a count of 16-byte descriptors in the block, calculate
         * the end address by adding Z*16 to the start addr.
         */
        dmastartp = dma->dma_bound & ~DESC_Z_MASK;
        dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);

        if (dma_loc_check_enabled &&
            ((dma_cmd_cur_loc >= dmastartp) && (dma_cmd_cur_loc < dmaendp))) {

                if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
                        return (IXL_CHECK_STOP);
                }

                return (IXL_CHECK_DONE);
        }

        /*
         * if receive mode:
         */
        if ((ixlp->ixl_opcode & IXL1394_OPF_ONXMIT) == 0)  {
                /*
                 * if context stopped, return stopped, else,
                 * if there is no current dma location reg, return done
                 * else return location indeterminate
                 */
                if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
                        return (IXL_CHECK_STOP);
                }
                if (!dma_loc_check_enabled) {
                        return (IXL_CHECK_DONE);
                }

                return (IXL_CHECK_LOST);
        }

        /*
         * else is xmit mode:
         * check status of current xfer IXL command's dma descriptor
         */
        acc_hdl  = dma->dma_buf->bi_handle;
        dma_hdl  = dma->dma_buf->bi_dma_handle;
        hcidescp = (hci1394_desc_t *)dma->dma_descp;
        hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;

        /* Sync the descriptor before we get the status */
        (void) ddi_dma_sync(dma_hdl, hcidesc_off, sizeof (hci1394_desc_t),
            DDI_DMA_SYNC_FORCPU);
        desc_status = ddi_get32(acc_hdl, &hcidescp->status);

        if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {

                /*
                 * if status is now set here, return skipped, to cause calling
                 * function to continue, even though location hasn't changed
                 */
                return (IXL_CHECK_SKIP);
        }

        /*
         * At this point, we have gotten to a DMA descriptor with an empty
         * status.  This is not enough information however to determine that
         * we've found all processed DMA descriptors because during cycle-lost
         * conditions, the HW will skip over some descriptors without writing
         * status.  So we have to look ahead until we're convinced that the HW
         * hasn't jumped ahead.
         *
         * Follow the IXL skip-to links until find one whose status is set
         * or until dma location register (if any) matches an xfer IXL
         * command's dma location or until have examined max_dma_skips
         * IXL commands.
         */
        rem_dma_skips = ctxtp->max_dma_skips;

        while (rem_dma_skips-- > 0) {

                /*
                 * get either IXL command specific or
                 * system default skipmode info
                 */
                skipdepth = 0;
                if (xferctlp->skipmodep != NULL) {
                        skipmode  = xferctlp->skipmodep->skipmode;
                        skipdestp = xferctlp->skipmodep->label;
                        skipxferp = (ixl1394_command_t *)
                            xferctlp->skipmodep->compiler_privatep;
                } else {
                        skipmode  = ctxtp->default_skipmode;
                        skipdestp = ctxtp->default_skiplabelp;
                        skipxferp = ctxtp->default_skipxferp;
                }

                switch (skipmode) {

                case IXL1394_SKIP_TO_SELF:
                        /*
                         * mode is skip to self:
                         *   if context is stopped, return stopped, else
                         *   if dma location reg not enabled, return done
                         *   else, return location indeterminate
                         */
                        if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
                            0) {
                                return (IXL_CHECK_STOP);
                        }

                        if (!dma_loc_check_enabled) {
                                return (IXL_CHECK_DONE);
                        }

                        return (IXL_CHECK_LOST);

                case IXL1394_SKIP_TO_NEXT:
                        /*
                         * mode is skip to next:
                         *    set potential skip target to current command at
                         *    next depth
                         */
                        skipdestp = ixlp;
                        skipxferp = ixlp;
                        skipdepth = ixldepth + 1;

                        /*
                         * else if at max depth at current cmd adjust to next
                         * IXL command.
                         *
                         * (NOTE: next means next IXL command along execution
                         * path,  whatever IXL command it might be.  e.g. store
                         * timestamp or callback or label or jump or send... )
                         */
                        if (skipdepth >= xferctlp->cnt) {
                                skipdepth = 0;
                                skipdestp = ixlp->next_ixlp;
                                skipxferp = xferctlp->execp;
                        }

                        /* evaluate skip to status further, below */
                        break;


                case IXL1394_SKIP_TO_LABEL:
                        /*
                         * mode is skip to label:
                         *    set skip destination depth to 0 (should be
                         *    redundant)
                         */
                        skipdepth = 0;

                        /* evaluate skip to status further, below */
                        break;

                case IXL1394_SKIP_TO_STOP:
                        /*
                         * mode is skip to stop:
                         *    set all xfer and destination skip to locations to
                         *    null
                         */
                        skipxferp = NULL;
                        skipdestp = NULL;
                        skipdepth = 0;

                        /* evaluate skip to status further, below */
                        break;

                } /* end switch */

                /*
                 * if no xfer IXL command follows at or after current skip-to
                 * location
                 */
                if (skipxferp == NULL) {
                        /*
                         *   if context is stopped, return stopped, else
                         *   if dma location reg not enabled, return done
                         *   else, return location indeterminate
                         */
                        if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
                            0) {
                                return (IXL_CHECK_STOP);
                        }

                        if (!dma_loc_check_enabled) {
                                return (IXL_CHECK_DONE);
                        }
                        return (IXL_CHECK_LOST);
                }

                /*
                 * if the skip to xfer IXL dma descriptor's status is set,
                 * then execution did skip
                 */
                xferctlp = (hci1394_xfer_ctl_t *)skipxferp->compiler_privatep;
                dma      = &xferctlp->dma[skipdepth];
                acc_hdl  = dma->dma_buf->bi_handle;
                dma_hdl  = dma->dma_buf->bi_dma_handle;
                hcidescp = (hci1394_desc_t *)dma->dma_descp;
                hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;

                /* Sync the descriptor before we get the status */
                (void) ddi_dma_sync(dma_hdl, hcidesc_off,
                    sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
                desc_status = ddi_get32(acc_hdl, &hcidescp->status);

                if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {

                        /*
                         * adjust to continue from skip to IXL command and
                         * return skipped, to have calling func continue.
                         * (Note: next IXL command may be any allowed IXL
                         * command)
                         */
                        ctxtp->ixl_execp = skipdestp;
                        ctxtp->ixl_exec_depth = skipdepth;

                        return (IXL_CHECK_SKIP);
                }

                /*
                 * if dma location command register checking is enabled,
                 * and the skip to xfer IXL dma location matches current
                 * dma location register value, execution did skip
                 */
                dmastartp = dma->dma_bound & ~DESC_Z_MASK;
                dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);

                if (dma_loc_check_enabled && ((dma_cmd_cur_loc >= dmastartp) &&
                    (dma_cmd_cur_loc < dmaendp))) {

                        /* if the context is stopped, return stopped */
                        if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
                            0) {
                                return (IXL_CHECK_STOP);
                        }
                        /*
                         * adjust to continue from skip to IXL command and
                         * return skipped, to have calling func continue
                         * (Note: next IXL command may be any allowed IXL cmd)
                         */
                        ctxtp->ixl_execp = skipdestp;
                        ctxtp->ixl_exec_depth = skipdepth;

                        return (IXL_CHECK_SKIP);
                }

                /*
                 * else, advance working current locn to skipxferp and
                 * skipdepth and continue skip evaluation loop processing
                 */
                ixlp = skipxferp;
                ixldepth = skipdepth;

        } /* end while */

        /*
         * didn't find dma status set, nor location reg match, along skip path
         *
         * if context is stopped, return stopped,
         *
         * else if no current location reg active don't change context values,
         * just return done (no skip)
         *
         * else, return location indeterminate
         */

        if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
                return (IXL_CHECK_STOP);
        }
        if (!dma_loc_check_enabled) {
                return (IXL_CHECK_DONE);
        }

        return (IXL_CHECK_LOST);
}

/*
 * hci1394_isoch_cycle_inconsistent()
 *    Called during interrupt notification to indicate that the cycle time
 *    has changed unexpectedly.  We need to take this opportunity to
 *    update our tracking of each running transmit context's execution.
 *    cycle_inconsistent only affects transmit, so recv contexts are left alone.
 */
void
hci1394_isoch_cycle_inconsistent(hci1394_state_t *soft_statep)
{
        int i, cnt_thresh;
        boolean_t note;
        hrtime_t current_time, last_time, delta, delta_thresh;
        hci1394_iso_ctxt_t *ctxtp;      /* current context */

        ASSERT(soft_statep);

        hci1394_ohci_intr_clear(soft_statep->ohci, OHCI_INTR_CYC_INCONSISTENT);

        /* grab the mutex before checking each context's INUSE and RUNNING */
        mutex_enter(&soft_statep->isoch->ctxt_list_mutex);

        /* check for transmit contexts which are inuse and running */
        for (i = 0; i < soft_statep->isoch->ctxt_xmit_count; i++) {
                ctxtp = &soft_statep->isoch->ctxt_xmit[i];

                if ((ctxtp->ctxt_flags &
                    (HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {

                        mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
                        hci1394_ixl_interrupt(soft_statep, ctxtp, B_FALSE);
                        mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
                }
        }

        /*
         * get the current time and calculate the delta between now and
         * when the last interrupt was processed.  (NOTE: if the time
         * returned by gethrtime() rolls-over while we are counting these
         * interrupts, we will incorrectly restart the counting process.
         * However, because the probability of this happening is small and
         * not catching the roll-over will AT MOST double the time it takes
         * us to discover and correct from this condition, we can safely
         * ignore it.)
         */
        current_time = gethrtime();
        last_time = soft_statep->isoch->cycle_incon_thresh.last_intr_time;
        delta = current_time - last_time;

        /*
         * compare the calculated delta to the delta T threshold.  If it
         * is less than the threshold, then increment the counter.  If it
         * is not then reset the counter.
         */
        delta_thresh = soft_statep->isoch->cycle_incon_thresh.delta_t_thresh;
        if (delta < delta_thresh)
                soft_statep->isoch->cycle_incon_thresh.delta_t_counter++;
        else
                soft_statep->isoch->cycle_incon_thresh.delta_t_counter = 0;

        /*
         * compare the counter to the counter threshold.  If it is greater,
         * then disable the cycle inconsistent interrupt.
         */
        cnt_thresh = soft_statep->isoch->cycle_incon_thresh.counter_thresh;
        note = B_FALSE;
        if (soft_statep->isoch->cycle_incon_thresh.delta_t_counter >
            cnt_thresh) {
                hci1394_ohci_intr_disable(soft_statep->ohci,
                    OHCI_INTR_CYC_INCONSISTENT);
                note = B_TRUE;
        }

        /* save away the current time into the last_intr_time field */
        soft_statep->isoch->cycle_incon_thresh.last_intr_time = current_time;

        mutex_exit(&soft_statep->isoch->ctxt_list_mutex);

        if (note == B_TRUE) {
                cmn_err(CE_NOTE, "!hci1394(%d): cycle_inconsistent interrupt "
                    "disabled until next bus reset",
                    soft_statep->drvinfo.di_instance);
        }
}


/*
 * hci1394_isoch_cycle_lost()
 *    Interrupt indicates an expected cycle_start packet (and therefore our
 *    opportunity to transmit) did not show up.  Update our tracking of each
 *    running transmit context.
 */
void
hci1394_isoch_cycle_lost(hci1394_state_t *soft_statep)
{
        int i, cnt_thresh;
        boolean_t note;
        hrtime_t current_time, last_time, delta, delta_thresh;
        hci1394_iso_ctxt_t *ctxtp;      /* current context */

        ASSERT(soft_statep);

        hci1394_ohci_intr_clear(soft_statep->ohci, OHCI_INTR_CYC_LOST);

        /* grab the mutex before checking each context's INUSE and RUNNING */
        mutex_enter(&soft_statep->isoch->ctxt_list_mutex);

        /* check for transmit contexts which are inuse and running */
        for (i = 0; i < soft_statep->isoch->ctxt_xmit_count; i++) {
                ctxtp = &soft_statep->isoch->ctxt_xmit[i];

                if ((ctxtp->ctxt_flags &
                    (HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {

                        mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
                        hci1394_ixl_interrupt(soft_statep, ctxtp, B_FALSE);
                        mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
                }
        }

        /*
         * get the current time and calculate the delta between now and
         * when the last interrupt was processed.  (NOTE: if the time
         * returned by gethrtime() rolls-over while we are counting these
         * interrupts, we will incorrectly restart the counting process.
         * However, because the probability of this happening is small and
         * not catching the roll-over will AT MOST double the time it takes
         * us to discover and correct from this condition, we can safely
         * ignore it.)
         */
        current_time = gethrtime();
        last_time = soft_statep->isoch->cycle_lost_thresh.last_intr_time;
        delta = current_time - last_time;

        /*
         * compare the calculated delta to the delta T threshold.  If it
         * is less than the threshold, then increment the counter.  If it
         * is not then reset the counter.
         */
        delta_thresh = soft_statep->isoch->cycle_lost_thresh.delta_t_thresh;
        if (delta < delta_thresh)
                soft_statep->isoch->cycle_lost_thresh.delta_t_counter++;
        else
                soft_statep->isoch->cycle_lost_thresh.delta_t_counter = 0;

        /*
         * compare the counter to the counter threshold.  If it is greater,
         * then disable the cycle lost interrupt.
         */
        cnt_thresh = soft_statep->isoch->cycle_lost_thresh.counter_thresh;
        note = B_FALSE;
        if (soft_statep->isoch->cycle_lost_thresh.delta_t_counter >
            cnt_thresh) {
                hci1394_ohci_intr_disable(soft_statep->ohci,
                    OHCI_INTR_CYC_LOST);
                note = B_TRUE;
        }

        /* save away the current time into the last_intr_time field */
        soft_statep->isoch->cycle_lost_thresh.last_intr_time = current_time;

        mutex_exit(&soft_statep->isoch->ctxt_list_mutex);

        if (note == B_TRUE) {
                cmn_err(CE_NOTE, "!hci1394(%d): cycle_lost interrupt "
                    "disabled until next bus reset",
                    soft_statep->drvinfo.di_instance);
        }
}