root/usr/src/uts/sun/sys/dada/adapters/ghd/ghd.h
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _GHD_H
#define _GHD_H

#pragma ident   "%Z%%M% %I%     %E% SMI"

#ifdef  __cplusplus
extern "C" {
#endif

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/debug.h>
#include <sys/scsi/scsi.h>

#include "ghd_queue.h"          /* linked list structures */
#include "ghd_scsi.h"
#include "ghd_waitq.h"
#include "ghd_debug.h"

#ifndef TRUE
#define TRUE    1
#endif

#ifndef FALSE
#define FALSE   0
#endif

/*
 * values for cmd_state:
 */

typedef enum {
        GCMD_STATE_IDLE = 0,
        GCMD_STATE_WAITQ,
        GCMD_STATE_ACTIVE,
        GCMD_STATE_DONEQ,
        GCMD_STATE_ABORTING_CMD,
        GCMD_STATE_ABORTING_DEV,
        GCMD_STATE_RESETTING_DEV,
        GCMD_STATE_RESETTING_BUS,
        GCMD_STATE_HUNG,
        GCMD_NSTATES
} cmdstate_t;

/*
 * action codes for the HBA timeout function
 */

typedef enum {
        GACTION_EARLY_TIMEOUT = 0,      /* timed-out before started */
        GACTION_EARLY_ABORT,            /* scsi_abort() before started */
        GACTION_ABORT_CMD,              /* abort a specific request */
        GACTION_ABORT_DEV,              /* abort everything on specifici dev */
        GACTION_RESET_TARGET,           /* reset a specific dev */
        GACTION_RESET_BUS,              /* reset the whole bus */
        GACTION_INCOMPLETE              /* giving up on incomplete request */
} gact_t;


/*
 * the common portion of the Command Control Block
 */

typedef struct ghd_cmd {
        L2el_t           cmd_q;         /* link for for done/active CCB Qs */
        cmdstate_t       cmd_state;     /* request's current state */
        uint32_t         cmd_waitq_level; /* which wait Q this request is on */

        L2el_t           cmd_timer_link; /* ccb timer doubly linked list */
        clock_t          cmd_start_time; /* lbolt at start of request */
        clock_t          cmd_timeout;   /* how long to wait */

        opaque_t         cmd_private;   /* used by the HBA driver */
        void            *cmd_pktp;      /* request packet */
        gtgt_t          *cmd_gtgtp;     /* dev instance for this request */

        ddi_dma_handle_t cmd_dma_handle;
        ddi_dma_win_t    cmd_dmawin;
        ddi_dma_seg_t    cmd_dmaseg;
        int              cmd_dma_flags;
        long             cmd_totxfer;
        long             cmd_resid;
} gcmd_t;




/*
 * Initialize the gcmd_t structure
 */

#define GHD_GCMD_INIT(gcmdp, cmdp, gtgtp)       \
        (L2_INIT(&(gcmdp)->cmd_q),              \
        L2_INIT(&(gcmdp)->cmd_timer_link),      \
        (gcmdp)->cmd_private = (cmdp),          \
        (gcmdp)->cmd_gtgtp = (gtgtp)            \
)


/*
 * CMD/CCB timer config structure - one per HBA driver module
 */
typedef struct tmr_conf {
        kmutex_t        t_mutex;        /* mutex to protect t_ccc_listp */
        timeout_id_t    t_timeout_id;   /* handle for timeout() function */
        clock_t         t_ticks;        /* periodic timeout in clock ticks */
        int             t_refs;         /* reference count */
        struct cmd_ctl  *t_ccc_listp;   /* control struct list, one per HBA */
} tmr_t;
_NOTE(MUTEX_PROTECTS_DATA(tmr_t::t_mutex, tmr_t::t_ccc_listp))
_NOTE(MUTEX_PROTECTS_DATA(tmr_t::t_mutex, tmr_t::t_timeout_id))
_NOTE(MUTEX_PROTECTS_DATA(tmr_t::t_mutex, tmr_t::t_refs))



/*
 * CMD/CCB timer control structure - one per HBA instance (per board)
 */
typedef struct cmd_ctl {
        struct cmd_ctl  *ccc_nextp;     /* list of control structs */
        struct tmr_conf *ccc_tmrp;      /* back ptr to config struct */
        char            *ccc_label;     /* name of this HBA driver */
        int             ccc_chno;       /* Channle number */

        kmutex_t ccc_activel_mutex;     /* mutex to protect list ... */
        L2el_t   ccc_activel;           /* ... list of active CMD/CCBs */

        dev_info_t *ccc_hba_dip;
        ddi_iblock_cookie_t ccc_iblock;
        ddi_softintr_t  ccc_soft_id;    /* ID for timeout softintr */

        kmutex_t ccc_hba_mutex;         /* mutex for HBA soft-state */
        int      ccc_hba_pollmode;      /* FLAG_NOINTR mode active? */

        L1_t     ccc_devs;              /* unsorted list of attached devs */
        kmutex_t ccc_waitq_mutex;       /* mutex to protect device wait Qs */
        Q_t      ccc_waitq;             /* the HBA's wait queue */

        ddi_softintr_t  ccc_doneq_softid; /* ID for doneq softintr */
        kmutex_t ccc_doneq_mutex;       /* mutex to protect the doneq */
        L2el_t   ccc_doneq;             /* completed cmd_t's */

        void    *ccc_hba_handle;
        int     (*ccc_ccballoc)();      /* alloc/init gcmd and ccb */
        void    (*ccc_ccbfree)();
        void    (*ccc_sg_func)();
        int     (*ccc_hba_start)(void *handle, gcmd_t *);
        void    (*ccc_hba_complete)(void *handle, gcmd_t *, int);
        void    (*ccc_process_intr)(void *handle, void *intr_status, int chno);
        int     (*ccc_get_status)(void *handle, void *intr_status, int chno);
        int     (*ccc_timeout_func)(void *handle, gcmd_t *cmdp, gtgt_t *gtgtp,
                                    gact_t action, int calltype);
} ccc_t;
_NOTE(MUTEX_PROTECTS_DATA(cmd_ctl::ccc_activel_mutex, cmd_ctl::ccc_activel))
_NOTE(MUTEX_PROTECTS_DATA(cmd_ctl::ccc_hba_mutex, cmd_ctl::ccc_hba_dip))
_NOTE(DATA_READABLE_WITHOUT_LOCK(cmd_ctl::ccc_hba_dip))
_NOTE(MUTEX_PROTECTS_DATA(cmd_ctl::ccc_waitq_mutex, cmd_ctl::ccc_waitq))
_NOTE(MUTEX_PROTECTS_DATA(cmd_ctl::ccc_doneq_mutex, cmd_ctl::ccc_doneq))


#define GHBA_QHEAD(cccp)        ((cccp)->ccc_waitq.Q_qhead)
#define GHBA_MAXACTIVE(cccp)    ((cccp)->ccc_waitq.Q_maxactive)
#define GHBA_NACTIVE(cccp)      ((cccp)->ccc_waitq.Q_nactive)

/* Initialize the HBA's list headers */
#define CCCP_INIT(cccp) {                               \
                L1HEADER_INIT(&(cccp)->ccc_devs);       \
                L2_INIT(&(cccp)->ccc_doneq);            \
}


#define CCCP2GDEVP(cccp)                                        \
        (L1_EMPTY(&(cccp)->ccc_devs)                            \
        ? (gdev_t *)NULL                                        \
        : (gdev_t *)((cccp)->ccc_devs.l1_headp->le_datap))

/* ******************************************************************* */

#include "ghd_scsa.h"

/*
 * GHD Entry Points
 */
void     ghd_complete(ccc_t *cccp, gcmd_t *cmdp);
void     ghd_async_complete(ccc_t *cccp, gcmd_t *cmdp);
void     ghd_doneq_put(ccc_t *cccp, gcmd_t *cmdp);

int      ghd_intr(ccc_t *cccp, void *status, int chno);
int      ghd_register(char *, ccc_t *, dev_info_t *, int, void *hba_handle,
                        int (*ccc_ccballoc)(gtgt_t *, gcmd_t *, int, int,
                                            int, int),
                        void (*ccc_ccbfree)(gcmd_t *),
                        void (*ccc_sg_func)(gcmd_t *, ddi_dma_cookie_t *,
                                            int, int),
                        int  (*hba_start)(void *, gcmd_t *),
                        void (*hba_complete)(void *, gcmd_t *, int),
                        uint_t (*int_handler)(caddr_t),
                        int  (*get_status)(void *, void *, int),
                        void (*process_intr)(void *, void *, int),
                        int  (*timeout_func)(void *, gcmd_t *, gtgt_t *,
                                                gact_t, int calltype),
                        tmr_t *tmrp,
                        ddi_iblock_cookie_t iblock,
                        int chno);
void     ghd_unregister(ccc_t *cccp);

int      ghd_transport(ccc_t *cccp, gcmd_t *cmdp, gtgt_t *gtgtp,
                uint32_t timeout, int polled, void *intr_status);

int      ghd_tran_abort(ccc_t *cccp, gcmd_t *cmdp, gtgt_t *gtgtp,
                                void *intr_status);
int      ghd_tran_abort_lun(ccc_t *cccp, gtgt_t *gtgtp, void *intr_status);
int      ghd_tran_reset_target(ccc_t *cccp, gtgt_t *gtgtp, void *intr_status);
int      ghd_tran_reset_bus(ccc_t *cccp, gtgt_t *gtgtp, void *intr_status);


/*
 * Allocate a gcmd_t wrapper and HBA private area
 */
gcmd_t  *ghd_gcmd_alloc(gtgt_t *gtgtp, int ccblen, int sleep);

/*
 * Free the gcmd_t wrapper and HBA private area
 */
void    ghd_gcmd_free(gcmd_t *gcmdp);


/*
 * GHD CMD/CCB timer Entry points
 */

int     ghd_timer_attach(ccc_t *cccp, tmr_t *tmrp,
                int (*timeout_func)(void *handle, gcmd_t *, gtgt_t *,
                    gact_t, int));
void    ghd_timer_detach(ccc_t *cccp);
void    ghd_timer_fini(tmr_t *tmrp);
void    ghd_timer_init(tmr_t *tmrp, clock_t ticks);
void    ghd_timer_newstate(ccc_t *cccp, gcmd_t *cmdp, gtgt_t *gtgtp,
                                gact_t action, int calltype);
void    ghd_timer_poll(ccc_t *cccp);
void    ghd_timer_start(ccc_t *cccp, gcmd_t *cmdp, uint32_t cmd_timeout);
void    ghd_timer_stop(ccc_t *cccp, gcmd_t *cmdp);


/*
 * Wait queue utility routines
 */

gtgt_t  *ghd_target_init(dev_info_t *, dev_info_t *, ccc_t *, size_t,
                                void *, uint32_t, uint32_t);
void     ghd_target_free(dev_info_t *, dev_info_t *, ccc_t *, gtgt_t *);
void     ghd_waitq_shuffle_up(ccc_t *, gdev_t *);
void     ghd_waitq_delete(ccc_t *, gcmd_t *);
int      ghd_waitq_process_and_mutex_hold(ccc_t *);
void     ghd_waitq_process_and_mutex_exit(ccc_t *);


/*
 * The values for the calltype arg for the ghd_timer_newstate() function
 */

#define GHD_NEWSTATE_TGTREQ     0
#define GHD_NEWSTATE_TIMEOUT    1

/* ******************************************************************* */

/*
 * specify GHD_INLINE to get optimized versions
 */
#define GHD_INLINE      1
#if defined(GHD_DEBUG) || defined(DEBUG) || defined(__lint)
#undef GHD_INLINE
#endif

#if defined(GHD_INLINE)
#define GHD_COMPLETE(cccp, gcmpd)       GHD_COMPLETE_INLINE(cccp, gcmdp)
#define GHD_TIMER_STOP(cccp, gcmdp)     GHD_TIMER_STOP_INLINE(cccp, gcmdp)
#define GHD_DONEQ_PUT(cccp, gcmdp)      GHD_DONEQ_PUT_INLINE(cccp, gcmdp)
#else
#define GHD_COMPLETE(cccp, gcmpd)       ghd_complete(cccp, gcmdp)
#define GHD_TIMER_STOP(cccp, gcmdp)     ghd_timer_stop(cccp, gcmdp)
#define GHD_DONEQ_PUT(cccp, gcmdp)      ghd_doneq_put(cccp, gcmdp)
#endif

/*
 * request is complete, stop the request timer and add to doneq
 */
#define GHD_COMPLETE_INLINE(cccp, gcmdp)                \
{                                               \
        ghd_waitq_delete(cccp, gcmdp);          \
        (gcmdp)->cmd_state = GCMD_STATE_DONEQ;  \
        GHD_TIMER_STOP((cccp), (gcmdp));        \
        GHD_DONEQ_PUT((cccp), (gcmdp));         \
}

#define GHD_TIMER_STOP_INLINE(cccp, gcmdp)              \
{                                                       \
        mutex_enter(&(cccp)->ccc_activel_mutex);        \
        L2_delete(&(gcmdp)->cmd_timer_link);            \
        mutex_exit(&(cccp)->ccc_activel_mutex);         \
}

/*
 * mark the request done and append it to the doneq
 */
#define GHD_DONEQ_PUT_INLINE(cccp, gcmdp)                       \
{                                                               \
                                                                \
        mutex_enter(&(cccp)->ccc_doneq_mutex);                  \
        (gcmdp)->cmd_state = GCMD_STATE_DONEQ;                  \
        L2_add(&(cccp)->ccc_doneq, &(gcmdp)->cmd_q, (gcmdp));   \
        if (!(cccp)->ccc_hba_pollmode)                          \
                ddi_trigger_softintr((cccp)->ccc_doneq_softid); \
        mutex_exit(&(cccp)->ccc_doneq_mutex);                   \
}


/* ******************************************************************* */

/*
 * These are shortcut macros for linkages setup by GHD
 */

/*
 * (gcmd_t *) to (struct scsi_pkt *)
 */
#define GCMDP2PKTP(gcmdp)       ((gcmdp)->cmd_pktp)

/*
 * (gcmd_t *) to (gtgt_t *)
 */
#define GCMDP2GTGTP(gcmdp)      ((gcmdp)->cmd_gtgtp)

/*
 * (struct scsi_pkt *) to (gcmd_t *)
 */
#define PKTP2GCMDP(pktp)        ((gcmd_t *)(pktp)->pkt_ha_private)


/* These are shortcut macros for linkages setup by SCSA */

/*
 * (struct scsi_address *) to (scsi_hba_tran *)
 */
#define ADDR2TRAN(ap)           ((ap)->a_hba_tran)

/*
 * (struct scsi_device *) to (scsi_address *)
 */
#define SDEV2ADDR(sdp)          (&(sdp)->sd_address)

/*
 * (struct scsi_device *) to (scsi_hba_tran *)
 */
#define SDEV2TRAN(sdp)          ADDR2TRAN(SDEV2ADDR(sdp))

/*
 * (struct scsi_pkt *) to (scsi_hba_tran *)
 */
#define PKTP2TRAN(pktp)         ADDR2TRAN(&(pktp)->pkt_address)

/*
 * (scsi_hba_tran_t *) to (per-target-soft-state *)
 */
#define TRAN2GTGTP(tranp)       ((gtgt_t *)((tranp)->tran_tgt_private))

/*
 * (struct scsi_device *) to (per-target-soft-state *)
 */
#define SDEV2GTGTP(sd)          TRAN2GTGTP(SDEV2TRAN(sd))

/*
 * (struct scsi_pkt *) to (per-target-soft-state *)
 */
#define PKTP2GTGTP(pktp)        TRAN2GTGTP(PKTP2TRAN(pktp))


/*
 * (scsi_hba_tran_t *) to (per-HBA-soft-state *)
 */
#define TRAN2HBA(tranp)         ((tranp)->tran_hba_private)


/*
 * (struct scsi_device *) to (per-HBA-soft-state *)
 */
#define SDEV2HBA(sd)            TRAN2HBA(SDEV2TRAN(sd))

/*
 * (struct scsi_address *) to (per-target-soft-state *)
 */
#define ADDR2GTGTP(ap)          TRAN2GTGTP(ADDR2TRAN(ap))

/* ******************************************************************* */


#ifdef  __cplusplus
}
#endif

#endif /* _GHD_H */