Index: head/sys/cam/scsi/scsi_low.c =================================================================== --- head/sys/cam/scsi/scsi_low.c (revision 116350) +++ head/sys/cam/scsi/scsi_low.c (revision 116351) @@ -1,4932 +1,4913 @@ /* $NecBSD: scsi_low.c,v 1.24.10.8 2001/06/26 07:39:44 honda Exp $ */ /* $NetBSD$ */ #include __FBSDID("$FreeBSD$"); #define SCSI_LOW_STATICS #define SCSI_LOW_DEBUG #define SCSI_LOW_NEGOTIATE_BEFORE_SENSE #define SCSI_LOW_START_UP_CHECK /* #define SCSI_LOW_INFO_DETAIL */ /* #define SCSI_LOW_QCLEAR_AFTER_CA */ /* #define SCSI_LOW_FLAGS_QUIRKS_OK */ #ifdef __NetBSD__ #define SCSI_LOW_TARGET_OPEN #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define SCSI_LOW_FLAGS_QUIRKS_OK #endif /* __FreeBSD__ */ /* * [NetBSD for NEC PC-98 series] * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001 * NetBSD/pc98 porting staff. All rights reserved. * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001 * Naofumi HONDA. All rights reserved. * * [Ported for FreeBSD CAM] * Copyright (c) 2000, 2001 * MITSUNAGA Noriaki, NOKUBI Hirotaka and TAKAHASHI Yoshihiro. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * When our host is reselected, * nexus establish processes are little complicated. * Normal steps are followings: * 1) Our host selected by target => target nexus (slp->sl_Tnexus) * 2) Identify msgin => lun nexus (slp->sl_Lnexus) * 3) Qtag msg => ccb nexus (slp->sl_Qnexus) */ #include "opt_ddb.h" #include #include #include #ifdef __FreeBSD__ #if __FreeBSD_version >= 500001 #include #else #include #endif #endif /* __FreeBSD__ */ #include #include #include #include #ifdef __NetBSD__ #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #include #include #include #include #include #include #include #include #include #endif /* __FreeBSD__ */ /************************************************************** * Constants **************************************************************/ #define SCSI_LOW_POLL_HZ 1000 /* functions return values */ #define SCSI_LOW_START_NO_QTAG 0 #define SCSI_LOW_START_QTAG 1 #define SCSI_LOW_DONE_COMPLETE 0 #define SCSI_LOW_DONE_RETRY 1 /* internal disk flags */ #define SCSI_LOW_DISK_DISC 0x00000001 #define SCSI_LOW_DISK_QTAG 0x00000002 #define SCSI_LOW_DISK_LINK 0x00000004 #define SCSI_LOW_DISK_PARITY 0x00000008 #define SCSI_LOW_DISK_SYNC 0x00010000 #define SCSI_LOW_DISK_WIDE_16 0x00020000 #define SCSI_LOW_DISK_WIDE_32 0x00040000 #define SCSI_LOW_DISK_WIDE (SCSI_LOW_DISK_WIDE_16 | SCSI_LOW_DISK_WIDE_32) #define SCSI_LOW_DISK_LFLAGS 0x0000ffff #define SCSI_LOW_DISK_TFLAGS 0xffff0000 /************************************************************** * Declarations **************************************************************/ /* static */ void scsi_low_info(struct scsi_low_softc *, struct targ_info *, u_char *); static void scsi_low_engage(void *); static struct slccb *scsi_low_establish_ccb(struct targ_info *, struct lun_info *, scsi_low_tag_t); static int scsi_low_done(struct scsi_low_softc *, struct slccb *); static int scsi_low_setup_done(struct scsi_low_softc *, struct slccb *); static void scsi_low_bus_release(struct scsi_low_softc *, struct targ_info *); static void scsi_low_twiddle_wait(void); static struct lun_info *scsi_low_alloc_li(struct targ_info *, int, int); static struct targ_info *scsi_low_alloc_ti(struct scsi_low_softc *, int); static void scsi_low_calcf_lun(struct lun_info *); static void scsi_low_calcf_target(struct targ_info *); static void scsi_low_calcf_show(struct lun_info *); static void scsi_low_reset_nexus(struct scsi_low_softc *, int); static void scsi_low_reset_nexus_target(struct scsi_low_softc *, struct targ_info *, int); static void scsi_low_reset_nexus_lun(struct scsi_low_softc *, struct lun_info *, int); static int scsi_low_init(struct scsi_low_softc *, u_int); static void scsi_low_start(struct scsi_low_softc *); static void scsi_low_free_ti(struct scsi_low_softc *); static int scsi_low_alloc_qtag(struct slccb *); static int scsi_low_dealloc_qtag(struct slccb *); static int scsi_low_enqueue(struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *, u_int, u_int); static int scsi_low_message_enqueue(struct scsi_low_softc *, struct targ_info *, struct lun_info *, u_int); static void scsi_low_unit_ready_cmd(struct slccb *); static void scsi_low_timeout(void *); static int scsi_low_timeout_check(struct scsi_low_softc *); #ifdef SCSI_LOW_START_UP_CHECK static int scsi_low_start_up(struct scsi_low_softc *); #endif /* SCSI_LOW_START_UP_CHECK */ static int scsi_low_abort_ccb(struct scsi_low_softc *, struct slccb *); static struct slccb *scsi_low_revoke_ccb(struct scsi_low_softc *, struct slccb *, int); int scsi_low_version_major = 2; int scsi_low_version_minor = 17; static struct scsi_low_softc_tab sl_tab = LIST_HEAD_INITIALIZER(sl_tab); /************************************************************** * Debug, Run test and Statics **************************************************************/ #ifdef SCSI_LOW_INFO_DETAIL #define SCSI_LOW_INFO(slp, ti, s) scsi_low_info((slp), (ti), (s)) #else /* !SCSI_LOW_INFO_DETAIL */ #define SCSI_LOW_INFO(slp, ti, s) printf("%s: %s\n", (slp)->sl_xname, (s)) #endif /* !SCSI_LOW_INFO_DETAIL */ #ifdef SCSI_LOW_STATICS static struct scsi_low_statics { int nexus_win; int nexus_fail; int nexus_disconnected; int nexus_reselected; int nexus_conflict; } scsi_low_statics; #endif /* SCSI_LOW_STATICS */ #ifdef SCSI_LOW_DEBUG #define SCSI_LOW_DEBUG_DONE 0x00001 #define SCSI_LOW_DEBUG_DISC 0x00002 #define SCSI_LOW_DEBUG_SENSE 0x00004 #define SCSI_LOW_DEBUG_CALCF 0x00008 #define SCSI_LOW_DEBUG_ACTION 0x10000 int scsi_low_debug = 0; #define SCSI_LOW_MAX_ATTEN_CHECK 32 #define SCSI_LOW_ATTEN_CHECK 0x0001 #define SCSI_LOW_CMDLNK_CHECK 0x0002 #define SCSI_LOW_ABORT_CHECK 0x0004 #define SCSI_LOW_NEXUS_CHECK 0x0008 int scsi_low_test = 0; int scsi_low_test_id = 0; static void scsi_low_test_abort(struct scsi_low_softc *, struct targ_info *, struct lun_info *); static void scsi_low_test_cmdlnk(struct scsi_low_softc *, struct slccb *); static void scsi_low_test_atten(struct scsi_low_softc *, struct targ_info *, u_int); #define SCSI_LOW_DEBUG_TEST_GO(fl, id) \ ((scsi_low_test & (fl)) != 0 && (scsi_low_test_id & (1 << (id))) == 0) #define SCSI_LOW_DEBUG_GO(fl, id) \ ((scsi_low_debug & (fl)) != 0 && (scsi_low_test_id & (1 << (id))) == 0) #endif /* SCSI_LOW_DEBUG */ /************************************************************** * CCB **************************************************************/ GENERIC_CCB_STATIC_ALLOC(scsi_low, slccb) GENERIC_CCB(scsi_low, slccb, ccb_chain) /************************************************************** * Inline functions **************************************************************/ #define SCSI_LOW_INLINE static __inline SCSI_LOW_INLINE void scsi_low_activate_qtag(struct slccb *); SCSI_LOW_INLINE void scsi_low_deactivate_qtag(struct slccb *); SCSI_LOW_INLINE void scsi_low_ccb_message_assert(struct slccb *, u_int); SCSI_LOW_INLINE void scsi_low_ccb_message_exec(struct scsi_low_softc *, struct slccb *); SCSI_LOW_INLINE void scsi_low_ccb_message_retry(struct slccb *); SCSI_LOW_INLINE void scsi_low_ccb_message_clear(struct slccb *); SCSI_LOW_INLINE void scsi_low_init_msgsys(struct scsi_low_softc *, struct targ_info *); SCSI_LOW_INLINE void scsi_low_activate_qtag(cb) struct slccb *cb; { struct lun_info *li = cb->li; if (cb->ccb_tag != SCSI_LOW_UNKTAG) return; li->li_nqio ++; cb->ccb_tag = cb->ccb_otag; } SCSI_LOW_INLINE void scsi_low_deactivate_qtag(cb) struct slccb *cb; { struct lun_info *li = cb->li; if (cb->ccb_tag == SCSI_LOW_UNKTAG) return; li->li_nqio --; cb->ccb_tag = SCSI_LOW_UNKTAG; } SCSI_LOW_INLINE void scsi_low_ccb_message_exec(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { scsi_low_assert_msg(slp, cb->ti, cb->ccb_msgoutflag, 0); cb->ccb_msgoutflag = 0; } SCSI_LOW_INLINE void scsi_low_ccb_message_assert(cb, msg) struct slccb *cb; u_int msg; { cb->ccb_msgoutflag = cb->ccb_omsgoutflag = msg; } SCSI_LOW_INLINE void scsi_low_ccb_message_retry(cb) struct slccb *cb; { cb->ccb_msgoutflag = cb->ccb_omsgoutflag; } SCSI_LOW_INLINE void scsi_low_ccb_message_clear(cb) struct slccb *cb; { cb->ccb_msgoutflag = 0; } SCSI_LOW_INLINE void scsi_low_init_msgsys(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { ti->ti_msginptr = 0; ti->ti_emsgflags = ti->ti_msgflags = ti->ti_omsgflags = 0; SCSI_LOW_DEASSERT_ATN(slp); SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_NULL); } /*============================================================= * START OF OS switch (All OS depend fucntions should be here) =============================================================*/ /* common os depend utitlities */ #define SCSI_LOW_CMD_RESIDUAL_CHK 0x0001 #define SCSI_LOW_CMD_ORDERED_QTAG 0x0002 #define SCSI_LOW_CMD_ABORT_WARNING 0x0004 static u_int8_t scsi_low_cmd_flags[256] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /*0*/ 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 0, 0, 0, 0, 0, /*1*/ 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, /*2*/ 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 0, 0, 0, 5, 5, /*3*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, }; struct scsi_low_error_code { int error_bits; int error_code; }; static struct slccb *scsi_low_find_ccb(struct scsi_low_softc *, u_int, u_int, void *); static int scsi_low_translate_error_code(struct slccb *, struct scsi_low_error_code *); static struct slccb * scsi_low_find_ccb(slp, target, lun, osdep) struct scsi_low_softc *slp; u_int target, lun; void *osdep; { struct targ_info *ti; struct lun_info *li; struct slccb *cb; ti = slp->sl_ti[target]; li = scsi_low_alloc_li(ti, lun, 0); if (li == NULL) return NULL; if ((cb = slp->sl_Qnexus) != NULL && cb->osdep == osdep) return cb; for (cb = TAILQ_FIRST(&slp->sl_start); cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) { if (cb->osdep == osdep) return cb; } for (cb = TAILQ_FIRST(&li->li_discq); cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) { if (cb->osdep == osdep) return cb; } return NULL; } static int scsi_low_translate_error_code(cb, tp) struct slccb *cb; struct scsi_low_error_code *tp; { if (cb->ccb_error == 0) return tp->error_code; for (tp ++; (cb->ccb_error & tp->error_bits) == 0; tp ++) ; return tp->error_code; } #ifdef SCSI_LOW_INTERFACE_XS /************************************************************** * SCSI INTERFACE (XS) **************************************************************/ #define SCSI_LOW_MINPHYS 0x10000 #define SCSI_LOW_MALLOC(size) malloc((size), M_DEVBUF, M_NOWAIT) #define SCSI_LOW_FREE(pt) free((pt), M_DEVBUF) #define SCSI_LOW_ALLOC_CCB(flags) scsi_low_get_ccb((flags)) #define SCSI_LOW_XS_POLL_HZ 1000 static int scsi_low_poll_xs(struct scsi_low_softc *, struct slccb *); static void scsi_low_scsi_minphys_xs(struct buf *); #ifdef SCSI_LOW_TARGET_OPEN static int scsi_low_target_open(struct scsipi_link *, struct cfdata *); #endif /* SCSI_LOW_TARGET_OPEN */ static int scsi_low_scsi_cmd_xs(struct scsipi_xfer *); static int scsi_low_enable_xs(void *, int); static int scsi_low_ioctl_xs(struct scsipi_link *, u_long, caddr_t, int, struct proc *); static int scsi_low_attach_xs(struct scsi_low_softc *); static int scsi_low_world_start_xs(struct scsi_low_softc *); static int scsi_low_dettach_xs(struct scsi_low_softc *); static int scsi_low_ccb_setup_xs(struct scsi_low_softc *, struct slccb *); static int scsi_low_done_xs(struct scsi_low_softc *, struct slccb *); static void scsi_low_timeout_xs(struct scsi_low_softc *, int, int); static u_int scsi_low_translate_quirks_xs(u_int); static void scsi_low_setup_quirks_xs(struct targ_info *, struct lun_info *, u_int); struct scsi_low_osdep_funcs scsi_low_osdep_funcs_xs = { scsi_low_attach_xs, scsi_low_world_start_xs, scsi_low_dettach_xs, scsi_low_ccb_setup_xs, scsi_low_done_xs, scsi_low_timeout_xs }; struct scsipi_device scsi_low_dev = { NULL, /* Use default error handler */ NULL, /* have a queue, served by this */ NULL, /* have no async handler */ NULL, /* Use default 'done' routine */ }; struct scsi_low_error_code scsi_low_error_code_xs[] = { {0, XS_NOERROR}, {SENSEIO, XS_SENSE}, {BUSYERR, XS_BUSY }, {SELTIMEOUTIO, XS_SELTIMEOUT}, {TIMEOUTIO, XS_TIMEOUT}, {-1, XS_DRIVER_STUFFUP} }; static int scsi_low_ioctl_xs(link, cmd, addr, flag, p) struct scsipi_link *link; u_long cmd; caddr_t addr; int flag; struct proc *p; { struct scsi_low_softc *slp; int s, error = ENOTTY; slp = (struct scsi_low_softc *) link->adapter_softc; if ((slp->sl_flags & HW_INACTIVE) != 0) return ENXIO; if (cmd == SCBUSIORESET) { s = SCSI_LOW_SPLSCSI(); scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, NULL); splx(s); error = 0; } else if (slp->sl_funcs->scsi_low_ioctl != 0) { error = (*slp->sl_funcs->scsi_low_ioctl) (slp, cmd, addr, flag, p); } return error; } static int scsi_low_enable_xs(arg, enable) void *arg; int enable; { struct scsi_low_softc *slp = arg; if (enable != 0) { if ((slp->sl_flags & HW_INACTIVE) != 0) return ENXIO; } else { if ((slp->sl_flags & HW_INACTIVE) != 0 || (slp->sl_flags & HW_POWERCTRL) == 0) return 0; slp->sl_flags |= HW_POWDOWN; if (slp->sl_funcs->scsi_low_power != NULL) { (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_POWDOWN); } } return 0; } static void scsi_low_scsi_minphys_xs(bp) struct buf *bp; { if (bp->b_bcount > SCSI_LOW_MINPHYS) bp->b_bcount = SCSI_LOW_MINPHYS; minphys(bp); } static int scsi_low_poll_xs(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct scsipi_xfer *xs = cb->osdep; int tcount; cb->ccb_flags |= CCB_NOSDONE; tcount = 0; while (slp->sl_nio > 0) { SCSI_LOW_DELAY((1000 * 1000) / SCSI_LOW_XS_POLL_HZ); (*slp->sl_funcs->scsi_low_poll) (slp); if ((slp->sl_flags & (HW_INACTIVE | HW_INITIALIZING)) != 0) { cb->ccb_flags |= CCB_NORETRY; cb->ccb_error |= FATALIO; (void) scsi_low_revoke_ccb(slp, cb, 1); printf("%s: hardware inactive in poll mode\n", slp->sl_xname); } if ((xs->flags & ITSDONE) != 0) break; if (tcount ++ < SCSI_LOW_XS_POLL_HZ / SCSI_LOW_TIMEOUT_HZ) continue; tcount = 0; scsi_low_timeout_check(slp); } xs->flags |= ITSDONE; scsipi_done(xs); return COMPLETE; } static int scsi_low_scsi_cmd_xs(xs) struct scsipi_xfer *xs; { struct scsipi_link *splp = xs->sc_link; struct scsi_low_softc *slp = splp->adapter_softc; struct targ_info *ti; struct lun_info *li; struct slccb *cb; int s, targ, lun, flags, rv; if ((cb = SCSI_LOW_ALLOC_CCB(xs->flags & SCSI_NOSLEEP)) == NULL) return TRY_AGAIN_LATER; targ = splp->scsipi_scsi.target, lun = splp->scsipi_scsi.lun; ti = slp->sl_ti[targ]; cb->osdep = xs; cb->bp = xs->bp; if ((xs->flags & SCSI_POLL) == 0) flags = CCB_AUTOSENSE; else flags = CCB_AUTOSENSE | CCB_POLLED; s = SCSI_LOW_SPLSCSI(); li = scsi_low_alloc_li(ti, lun, 1); if ((u_int) splp->quirks != li->li_sloi.sloi_quirks) { scsi_low_setup_quirks_xs(ti, li, (u_int) splp->quirks); } if ((xs->flags & SCSI_RESET) != 0) { flags |= CCB_NORETRY | CCB_URGENT; scsi_low_enqueue(slp, ti, li, cb, flags, SCSI_LOW_MSG_RESET); } else { if (ti->ti_setup_msg != 0) { scsi_low_message_enqueue(slp, ti, li, flags); } flags |= CCB_SCSIIO; scsi_low_enqueue(slp, ti, li, cb, flags, 0); } #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_ABORT_CHECK, ti->ti_id) != 0) { scsi_low_test_abort(slp, ti, li); } #endif /* SCSI_LOW_DEBUG */ if ((cb->ccb_flags & CCB_POLLED) != 0) { rv = scsi_low_poll_xs(slp, cb); } else { rv = SUCCESSFULLY_QUEUED; } splx(s); return rv; } static int scsi_low_attach_xs(slp) struct scsi_low_softc *slp; { struct scsipi_adapter *sap; struct scsipi_link *splp; strncpy(slp->sl_xname, slp->sl_dev.dv_xname, 16); sap = SCSI_LOW_MALLOC(sizeof(*sap)); if (sap == NULL) return ENOMEM; splp = SCSI_LOW_MALLOC(sizeof(*splp)); if (splp == NULL) return ENOMEM; SCSI_LOW_BZERO(sap, sizeof(*sap)); SCSI_LOW_BZERO(splp, sizeof(*splp)); sap->scsipi_cmd = scsi_low_scsi_cmd_xs; sap->scsipi_minphys = scsi_low_scsi_minphys_xs; sap->scsipi_enable = scsi_low_enable_xs; sap->scsipi_ioctl = scsi_low_ioctl_xs; #ifdef SCSI_LOW_TARGET_OPEN sap->open_target_lu = scsi_low_target_open; #endif /* SCSI_LOW_TARGET_OPEN */ splp->adapter_softc = slp; splp->scsipi_scsi.adapter_target = slp->sl_hostid; splp->scsipi_scsi.max_target = slp->sl_ntargs - 1; splp->scsipi_scsi.max_lun = slp->sl_nluns - 1; splp->scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE; splp->openings = slp->sl_openings; splp->type = BUS_SCSI; splp->adapter_softc = slp; splp->adapter = sap; splp->device = &scsi_low_dev; slp->sl_si.si_splp = splp; slp->sl_show_result = SHOW_ALL_NEG; return 0; } static int scsi_low_world_start_xs(slp) struct scsi_low_softc *slp; { return 0; } static int scsi_low_dettach_xs(slp) struct scsi_low_softc *slp; { /* * scsipi does not have dettach bus fucntion. * scsipi_dettach_scsibus(slp->sl_si.si_splp); */ return 0; } static int scsi_low_ccb_setup_xs(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct scsipi_xfer *xs = (struct scsipi_xfer *) cb->osdep; if ((cb->ccb_flags & CCB_SCSIIO) != 0) { cb->ccb_scp.scp_cmd = (u_int8_t *) xs->cmd; cb->ccb_scp.scp_cmdlen = xs->cmdlen; cb->ccb_scp.scp_data = xs->data; cb->ccb_scp.scp_datalen = xs->datalen; cb->ccb_scp.scp_direction = (xs->flags & SCSI_DATA_OUT) ? SCSI_LOW_WRITE : SCSI_LOW_READ; cb->ccb_tcmax = xs->timeout / 1000; } else { scsi_low_unit_ready_cmd(cb); } return SCSI_LOW_START_QTAG; } static int scsi_low_done_xs(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct scsipi_xfer *xs; xs = (struct scsipi_xfer *) cb->osdep; if (cb->ccb_error == 0) { xs->error = XS_NOERROR; xs->resid = 0; } else { if (cb->ccb_rcnt >= slp->sl_max_retry) cb->ccb_error |= ABORTIO; if ((cb->ccb_flags & CCB_NORETRY) == 0 && (cb->ccb_error & ABORTIO) == 0) return EJUSTRETURN; if ((cb->ccb_error & SENSEIO) != 0) { xs->sense.scsi_sense = cb->ccb_sense; } xs->error = scsi_low_translate_error_code(cb, &scsi_low_error_code_xs[0]); #ifdef SCSI_LOW_DIAGNOSTIC if ((cb->ccb_flags & CCB_SILENT) == 0 && cb->ccb_scp.scp_cmdlen > 0 && (scsi_low_cmd_flags[cb->ccb_scp.scp_cmd[0]] & SCSI_LOW_CMD_ABORT_WARNING) != 0) { printf("%s: WARNING: scsi_low IO abort\n", slp->sl_xname); scsi_low_print(slp, NULL); } #endif /* SCSI_LOW_DIAGNOSTIC */ } if (cb->ccb_scp.scp_status == ST_UNKNOWN) xs->status = 0; /* XXX */ else xs->status = cb->ccb_scp.scp_status; xs->flags |= ITSDONE; if ((cb->ccb_flags & CCB_NOSDONE) == 0) scsipi_done(xs); return 0; } static void scsi_low_timeout_xs(slp, ch, action) struct scsi_low_softc *slp; int ch; int action; { switch (ch) { case SCSI_LOW_TIMEOUT_CH_IO: switch (action) { case SCSI_LOW_TIMEOUT_START: timeout(scsi_low_timeout, slp, hz / SCSI_LOW_TIMEOUT_HZ); break; case SCSI_LOW_TIMEOUT_STOP: untimeout(scsi_low_timeout, slp); break; } break; case SCSI_LOW_TIMEOUT_CH_ENGAGE: switch (action) { case SCSI_LOW_TIMEOUT_START: timeout(scsi_low_engage, slp, 1); break; case SCSI_LOW_TIMEOUT_STOP: untimeout(scsi_low_engage, slp); break; } break; case SCSI_LOW_TIMEOUT_CH_RECOVER: break; } } u_int scsi_low_translate_quirks_xs(quirks) u_int quirks; { u_int flags; flags = SCSI_LOW_DISK_LFLAGS | SCSI_LOW_DISK_TFLAGS; #ifdef SDEV_NODISC if (quirks & SDEV_NODISC) flags &= ~SCSI_LOW_DISK_DISC; #endif /* SDEV_NODISC */ #ifdef SDEV_NOPARITY if (quirks & SDEV_NOPARITY) flags &= ~SCSI_LOW_DISK_PARITY; #endif /* SDEV_NOPARITY */ #ifdef SDEV_NOCMDLNK if (quirks & SDEV_NOCMDLNK) flags &= ~SCSI_LOW_DISK_LINK; #endif /* SDEV_NOCMDLNK */ #ifdef SDEV_NOTAG if (quirks & SDEV_NOTAG) flags &= ~SCSI_LOW_DISK_QTAG; #endif /* SDEV_NOTAG */ #ifdef SDEV_NOSYNC if (quirks & SDEV_NOSYNC) flags &= ~SCSI_LOW_DISK_SYNC; #endif /* SDEV_NOSYNC */ return flags; } static void scsi_low_setup_quirks_xs(ti, li, flags) struct targ_info *ti; struct lun_info *li; u_int flags; { u_int quirks; li->li_sloi.sloi_quirks = flags; quirks = scsi_low_translate_quirks_xs(flags); ti->ti_quirks = quirks & SCSI_LOW_DISK_TFLAGS; li->li_quirks = quirks & SCSI_LOW_DISK_LFLAGS; ti->ti_flags_valid |= SCSI_LOW_TARG_FLAGS_QUIRKS_VALID; li->li_flags_valid |= SCSI_LOW_LUN_FLAGS_QUIRKS_VALID; scsi_low_calcf_target(ti); scsi_low_calcf_lun(li); scsi_low_calcf_show(li); } #ifdef SCSI_LOW_TARGET_OPEN static int scsi_low_target_open(link, cf) struct scsipi_link *link; struct cfdata *cf; { u_int target = link->scsipi_scsi.target; u_int lun = link->scsipi_scsi.lun; struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; slp = (struct scsi_low_softc *) link->adapter_softc; ti = slp->sl_ti[target]; li = scsi_low_alloc_li(ti, lun, 0); if (li == NULL) return 0; li->li_cfgflags = cf->cf_flags; scsi_low_setup_quirks_xs(ti, li, (u_int) link->quirks); return 0; } #endif /* SCSI_LOW_TARGET_OPEN */ #endif /* SCSI_LOW_INTERFACE_XS */ #ifdef SCSI_LOW_INTERFACE_CAM /************************************************************** * SCSI INTERFACE (CAM) **************************************************************/ #define SCSI_LOW_MALLOC(size) malloc((size), M_DEVBUF, M_NOWAIT) #define SCSI_LOW_FREE(pt) free((pt), M_DEVBUF) #define SCSI_LOW_ALLOC_CCB(flags) scsi_low_get_ccb() static void scsi_low_poll_cam(struct cam_sim *); static void scsi_low_cam_rescan_callback(struct cam_periph *, union ccb *); static void scsi_low_rescan_bus_cam(struct scsi_low_softc *); void scsi_low_scsi_action_cam(struct cam_sim *, union ccb *); static int scsi_low_attach_cam(struct scsi_low_softc *); static int scsi_low_world_start_cam(struct scsi_low_softc *); static int scsi_low_dettach_cam(struct scsi_low_softc *); static int scsi_low_ccb_setup_cam(struct scsi_low_softc *, struct slccb *); static int scsi_low_done_cam(struct scsi_low_softc *, struct slccb *); static void scsi_low_timeout_cam(struct scsi_low_softc *, int, int); struct scsi_low_osdep_funcs scsi_low_osdep_funcs_cam = { scsi_low_attach_cam, scsi_low_world_start_cam, scsi_low_dettach_cam, scsi_low_ccb_setup_cam, scsi_low_done_cam, scsi_low_timeout_cam }; struct scsi_low_error_code scsi_low_error_code_cam[] = { {0, CAM_REQ_CMP}, {SENSEIO, CAM_AUTOSNS_VALID | CAM_REQ_CMP_ERR}, {SENSEERR, CAM_AUTOSENSE_FAIL}, {UACAERR, CAM_SCSI_STATUS_ERROR}, {BUSYERR | STATERR, CAM_SCSI_STATUS_ERROR}, {SELTIMEOUTIO, CAM_SEL_TIMEOUT}, {TIMEOUTIO, CAM_CMD_TIMEOUT}, {PDMAERR, CAM_DATA_RUN_ERR}, {PARITYERR, CAM_UNCOR_PARITY}, {UBFERR, CAM_UNEXP_BUSFREE}, {ABORTIO, CAM_REQ_ABORTED}, {-1, CAM_UNREC_HBA_ERROR} }; #define SIM2SLP(sim) ((struct scsi_low_softc *) cam_sim_softc((sim))) /* XXX: * Please check a polling hz, currently we assume scsi_low_poll() is * called each 1 ms. */ #define SCSI_LOW_CAM_POLL_HZ 1000 /* OK ? */ static void scsi_low_poll_cam(sim) struct cam_sim *sim; { struct scsi_low_softc *slp = SIM2SLP(sim); (*slp->sl_funcs->scsi_low_poll) (slp); if (slp->sl_si.si_poll_count ++ >= SCSI_LOW_CAM_POLL_HZ / SCSI_LOW_TIMEOUT_HZ) { slp->sl_si.si_poll_count = 0; scsi_low_timeout_check(slp); } } static void scsi_low_cam_rescan_callback(periph, ccb) struct cam_periph *periph; union ccb *ccb; { xpt_free_path(ccb->ccb_h.path); free(ccb, M_DEVBUF); } static void scsi_low_rescan_bus_cam(slp) struct scsi_low_softc *slp; { struct cam_path *path; union ccb *ccb = malloc(sizeof(union ccb), M_DEVBUF, M_WAITOK); cam_status status; bzero(ccb, sizeof(union ccb)); status = xpt_create_path(&path, xpt_periph, cam_sim_path(slp->sl_si.sim), -1, 0); if (status != CAM_REQ_CMP) return; xpt_setup_ccb(&ccb->ccb_h, path, 5); ccb->ccb_h.func_code = XPT_SCAN_BUS; ccb->ccb_h.cbfcnp = scsi_low_cam_rescan_callback; ccb->crcn.flags = CAM_FLAG_NONE; xpt_action(ccb); } void scsi_low_scsi_action_cam(sim, ccb) struct cam_sim *sim; union ccb *ccb; { struct scsi_low_softc *slp = SIM2SLP(sim); struct targ_info *ti; struct lun_info *li; struct slccb *cb; u_int lun, flags, msg, target; int s, rv; target = (u_int) (ccb->ccb_h.target_id); lun = (u_int) ccb->ccb_h.target_lun; #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_ACTION, target) != 0) { printf("%s: cam_action: func code 0x%x target: %d, lun: %d\n", slp->sl_xname, ccb->ccb_h.func_code, target, lun); } #endif /* SCSI_LOW_DEBUG */ switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ #ifdef SCSI_LOW_DIAGNOSTIC if (target == CAM_TARGET_WILDCARD || lun == CAM_LUN_WILDCARD) { printf("%s: invalid target/lun\n", slp->sl_xname); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } #endif /* SCSI_LOW_DIAGNOSTIC */ if (((cb = SCSI_LOW_ALLOC_CCB(1)) == NULL)) { ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(ccb); return; } ti = slp->sl_ti[target]; cb->osdep = ccb; cb->bp = NULL; if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) flags = CCB_AUTOSENSE | CCB_SCSIIO; else flags = CCB_SCSIIO; s = SCSI_LOW_SPLSCSI(); li = scsi_low_alloc_li(ti, lun, 1); if (ti->ti_setup_msg != 0) { scsi_low_message_enqueue(slp, ti, li, CCB_AUTOSENSE); } scsi_low_enqueue(slp, ti, li, cb, flags, 0); #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_ABORT_CHECK, target) != 0) { scsi_low_test_abort(slp, ti, li); } #endif /* SCSI_LOW_DEBUG */ splx(s); break; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_ABORT: /* Abort the specified CCB */ #ifdef SCSI_LOW_DIAGNOSTIC if (target == CAM_TARGET_WILDCARD || lun == CAM_LUN_WILDCARD) { printf("%s: invalid target/lun\n", slp->sl_xname); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } #endif /* SCSI_LOW_DIAGNOSTIC */ s = SCSI_LOW_SPLSCSI(); cb = scsi_low_find_ccb(slp, target, lun, ccb->cab.abort_ccb); rv = scsi_low_abort_ccb(slp, cb); splx(s); if (rv == 0) ccb->ccb_h.status = CAM_REQ_CMP; else ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; u_int val; #ifdef SCSI_LOW_DIAGNOSTIC if (target == CAM_TARGET_WILDCARD) { printf("%s: invalid target\n", slp->sl_xname); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } #endif /* SCSI_LOW_DIAGNOSTIC */ cts = &ccb->cts; ti = slp->sl_ti[target]; if (lun == CAM_LUN_WILDCARD) lun = 0; s = SCSI_LOW_SPLSCSI(); if ((cts->valid & (CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID)) != 0) { if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { val = cts->bus_width; if (val < ti->ti_width) ti->ti_width = val; } if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { val = cts->sync_period; if (val == 0 || val > ti->ti_maxsynch.period) ti->ti_maxsynch.period = val; } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { val = cts->sync_offset; if (val < ti->ti_maxsynch.offset) ti->ti_maxsynch.offset = val; } ti->ti_flags_valid |= SCSI_LOW_TARG_FLAGS_QUIRKS_VALID; scsi_low_calcf_target(ti); } if ((cts->valid & (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID)) != 0) { li = scsi_low_alloc_li(ti, lun, 1); if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) li->li_quirks |= SCSI_LOW_DISK_DISC; else li->li_quirks &= ~SCSI_LOW_DISK_DISC; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) li->li_quirks |= SCSI_LOW_DISK_QTAG; else li->li_quirks &= ~SCSI_LOW_DISK_QTAG; } li->li_flags_valid |= SCSI_LOW_LUN_FLAGS_QUIRKS_VALID; scsi_low_calcf_target(ti); scsi_low_calcf_lun(li); if ((slp->sl_show_result & SHOW_CALCF_RES) != 0) scsi_low_calcf_show(li); } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; u_int diskflags; cts = &ccb->cts; #ifdef SCSI_LOW_DIAGNOSTIC if (target == CAM_TARGET_WILDCARD) { printf("%s: invalid target\n", slp->sl_xname); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } #endif /* SCSI_LOW_DIAGNOSTIC */ ti = slp->sl_ti[target]; if (lun == CAM_LUN_WILDCARD) lun = 0; s = SCSI_LOW_SPLSCSI(); li = scsi_low_alloc_li(ti, lun, 1); #ifdef CAM_NEW_TRAN_CODE if (li != NULL && cts->type == CTS_TYPE_CURRENT_SETTINGS) { struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; #ifdef SCSI_LOW_DIAGNOSTIC if (li->li_flags_valid != SCSI_LOW_LUN_FLAGS_ALL_VALID) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; printf("%s: invalid GET_TRANS_CURRENT_SETTINGS call\n", slp->sl_xname); goto settings_out; } #endif /* SCSI_LOW_DIAGNOSTIC */ cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; diskflags = li->li_diskflags & li->li_cfgflags; if (diskflags & SCSI_LOW_DISK_DISC) spi->flags |= CTS_SPI_FLAGS_DISC_ENB; if (diskflags & SCSI_LOW_DISK_QTAG) scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; spi->sync_period = ti->ti_maxsynch.period; spi->valid |= CTS_SPI_VALID_SYNC_RATE; spi->sync_offset = ti->ti_maxsynch.offset; spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->valid |= CTS_SPI_VALID_BUS_WIDTH; spi->bus_width = ti->ti_width; if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else scsi->valid = 0; } else ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; #else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { #ifdef SCSI_LOW_DIAGNOSTIC if ((li->li_flags_valid & SCSI_LOW_LUN_FLAGS_DISK_VALID) == 0) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; printf("%s: invalid GET_TRANS_USER_SETTINGS call\n", slp->sl_xname); goto settings_out; } #endif /* SCSI_LOW_DIAGNOSTIC */ diskflags = li->li_diskflags & li->li_cfgflags; if ((diskflags & SCSI_LOW_DISK_DISC) != 0) cts->flags |= CCB_TRANS_DISC_ENB; else cts->flags &= ~CCB_TRANS_DISC_ENB; if ((diskflags & SCSI_LOW_DISK_QTAG) != 0) cts->flags |= CCB_TRANS_TAG_ENB; else cts->flags &= ~CCB_TRANS_TAG_ENB; } else if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { #ifdef SCSI_LOW_DIAGNOSTIC if (li->li_flags_valid != SCSI_LOW_LUN_FLAGS_ALL_VALID) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; printf("%s: invalid GET_TRANS_CURRENT_SETTINGS call\n", slp->sl_xname); goto settings_out; } #endif /* SCSI_LOW_DIAGNOSTIC */ if ((li->li_flags & SCSI_LOW_DISC) != 0) cts->flags |= CCB_TRANS_DISC_ENB; else cts->flags &= ~CCB_TRANS_DISC_ENB; if ((li->li_flags & SCSI_LOW_QTAG) != 0) cts->flags |= CCB_TRANS_TAG_ENB; else cts->flags &= ~CCB_TRANS_TAG_ENB; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; goto settings_out; } cts->sync_period = ti->ti_maxsynch.period; cts->sync_offset = ti->ti_maxsynch.offset; cts->bus_width = ti->ti_width; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; #endif settings_out: splx(s); xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { /* not yet HN2 */ - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended; - - extended = 1; - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ s = SCSI_LOW_SPLSCSI(); scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, NULL); splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ #ifdef SCSI_LOW_DIAGNOSTIC if (target == CAM_TARGET_WILDCARD) { printf("%s: invalid target\n", slp->sl_xname); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } #endif /* SCSI_LOW_DIAGNOSTIC */ msg = SCSI_LOW_MSG_RESET; if (((cb = SCSI_LOW_ALLOC_CCB(1)) == NULL)) { ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(ccb); return; } ti = slp->sl_ti[target]; if (lun == CAM_LUN_WILDCARD) lun = 0; cb->osdep = ccb; cb->bp = NULL; if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) flags = CCB_AUTOSENSE | CCB_NORETRY | CCB_URGENT; else flags = CCB_NORETRY | CCB_URGENT; s = SCSI_LOW_SPLSCSI(); li = scsi_low_alloc_li(ti, lun, 1); scsi_low_enqueue(slp, ti, li, cb, flags, msg); splx(s); break; case XPT_PATH_INQ: { /* Path routing inquiry */ struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = scsi_low_version_major; cpi->hba_inquiry = PI_TAG_ABLE | PI_LINKED_CDB; ti = slp->sl_ti[slp->sl_hostid]; /* host id */ if (ti->ti_width > SCSI_LOW_BUS_WIDTH_8) cpi->hba_inquiry |= PI_WIDE_16; if (ti->ti_width > SCSI_LOW_BUS_WIDTH_16) cpi->hba_inquiry |= PI_WIDE_32; if (ti->ti_maxsynch.offset > 0) cpi->hba_inquiry |= PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = slp->sl_ntargs - 1; cpi->max_lun = slp->sl_nluns - 1; cpi->initiator_id = slp->sl_hostid; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; #ifdef CAM_NEW_TRAN_CODE cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; #endif strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "SCSI_LOW", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: printf("scsi_low: non support func_code = %d ", ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static int scsi_low_attach_cam(slp) struct scsi_low_softc *slp; { struct cam_devq *devq; int tagged_openings; sprintf(slp->sl_xname, "%s%d", DEVPORT_DEVNAME(slp->sl_dev), DEVPORT_DEVUNIT(slp->sl_dev)); devq = cam_simq_alloc(SCSI_LOW_NCCB); if (devq == NULL) return (ENOMEM); /* * ask the adapter what subunits are present */ tagged_openings = min(slp->sl_openings, SCSI_LOW_MAXNEXUS); slp->sl_si.sim = cam_sim_alloc(scsi_low_scsi_action_cam, scsi_low_poll_cam, DEVPORT_DEVNAME(slp->sl_dev), slp, DEVPORT_DEVUNIT(slp->sl_dev), slp->sl_openings, tagged_openings, devq); if (slp->sl_si.sim == NULL) { cam_simq_free(devq); return ENODEV; } if (xpt_bus_register(slp->sl_si.sim, 0) != CAM_SUCCESS) { free(slp->sl_si.sim, M_DEVBUF); return ENODEV; } if (xpt_create_path(&slp->sl_si.path, /*periph*/NULL, cam_sim_path(slp->sl_si.sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(slp->sl_si.sim)); cam_sim_free(slp->sl_si.sim, /*free_simq*/TRUE); free(slp->sl_si.sim, M_DEVBUF); return ENODEV; } slp->sl_show_result = SHOW_CALCF_RES; /* OK ? */ return 0; } static int scsi_low_world_start_cam(slp) struct scsi_low_softc *slp; { if (!cold) scsi_low_rescan_bus_cam(slp); return 0; } static int scsi_low_dettach_cam(slp) struct scsi_low_softc *slp; { xpt_async(AC_LOST_DEVICE, slp->sl_si.path, NULL); xpt_free_path(slp->sl_si.path); xpt_bus_deregister(cam_sim_path(slp->sl_si.sim)); cam_sim_free(slp->sl_si.sim, /* free_devq */ TRUE); return 0; } static int scsi_low_ccb_setup_cam(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { union ccb *ccb = (union ccb *) cb->osdep; if ((cb->ccb_flags & CCB_SCSIIO) != 0) { cb->ccb_scp.scp_cmd = ccb->csio.cdb_io.cdb_bytes; cb->ccb_scp.scp_cmdlen = (int) ccb->csio.cdb_len; cb->ccb_scp.scp_data = ccb->csio.data_ptr; cb->ccb_scp.scp_datalen = (int) ccb->csio.dxfer_len; if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) cb->ccb_scp.scp_direction = SCSI_LOW_WRITE; else /* if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) */ cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = ccb->ccb_h.timeout / 1000; } else { scsi_low_unit_ready_cmd(cb); } return SCSI_LOW_START_QTAG; } static int scsi_low_done_cam(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { union ccb *ccb; ccb = (union ccb *) cb->osdep; if (cb->ccb_error == 0) { ccb->ccb_h.status = CAM_REQ_CMP; ccb->csio.resid = 0; } else { if (cb->ccb_rcnt >= slp->sl_max_retry) cb->ccb_error |= ABORTIO; if ((cb->ccb_flags & CCB_NORETRY) == 0 && (cb->ccb_error & ABORTIO) == 0) return EJUSTRETURN; if ((cb->ccb_error & SENSEIO) != 0) { memcpy(&ccb->csio.sense_data, &cb->ccb_sense, sizeof(ccb->csio.sense_data)); } ccb->ccb_h.status = scsi_low_translate_error_code(cb, &scsi_low_error_code_cam[0]); #ifdef SCSI_LOW_DIAGNOSTIC if ((cb->ccb_flags & CCB_SILENT) == 0 && cb->ccb_scp.scp_cmdlen > 0 && (scsi_low_cmd_flags[cb->ccb_scp.scp_cmd[0]] & SCSI_LOW_CMD_ABORT_WARNING) != 0) { printf("%s: WARNING: scsi_low IO abort\n", slp->sl_xname); scsi_low_print(slp, NULL); } #endif /* SCSI_LOW_DIAGNOSTIC */ } if ((ccb->ccb_h.status & CAM_STATUS_MASK) == 0) ccb->ccb_h.status |= CAM_REQ_CMP_ERR; if (cb->ccb_scp.scp_status == ST_UNKNOWN) ccb->csio.scsi_status = 0; /* XXX */ else ccb->csio.scsi_status = cb->ccb_scp.scp_status; if ((cb->ccb_flags & CCB_NOSDONE) == 0) xpt_done(ccb); return 0; } static void scsi_low_timeout_cam(slp, ch, action) struct scsi_low_softc *slp; int ch; int action; { switch (ch) { case SCSI_LOW_TIMEOUT_CH_IO: switch (action) { case SCSI_LOW_TIMEOUT_START: slp->sl_si.timeout_ch = timeout(scsi_low_timeout, slp, hz / SCSI_LOW_TIMEOUT_HZ); break; case SCSI_LOW_TIMEOUT_STOP: untimeout(scsi_low_timeout, slp, slp->sl_si.timeout_ch); break; } break; case SCSI_LOW_TIMEOUT_CH_ENGAGE: switch (action) { case SCSI_LOW_TIMEOUT_START: slp->sl_si.engage_ch = timeout(scsi_low_engage, slp, 1); break; case SCSI_LOW_TIMEOUT_STOP: untimeout(scsi_low_engage, slp, slp->sl_si.engage_ch); break; } break; case SCSI_LOW_TIMEOUT_CH_RECOVER: break; } } #endif /* SCSI_LOW_INTERFACE_CAM */ /*============================================================= * END OF OS switch (All OS depend fucntions should be above) =============================================================*/ /************************************************************** * scsi low deactivate and activate **************************************************************/ int scsi_low_is_busy(slp) struct scsi_low_softc *slp; { if (slp->sl_nio > 0) return EBUSY; return 0; } int scsi_low_deactivate(slp) struct scsi_low_softc *slp; { int s; s = SCSI_LOW_SPLSCSI(); slp->sl_flags |= HW_INACTIVE; (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_IO, SCSI_LOW_TIMEOUT_STOP); (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_ENGAGE, SCSI_LOW_TIMEOUT_STOP); splx(s); return 0; } int scsi_low_activate(slp) struct scsi_low_softc *slp; { int error, s; s = SCSI_LOW_SPLSCSI(); slp->sl_flags &= ~HW_INACTIVE; if ((error = scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, NULL)) != 0) { slp->sl_flags |= HW_INACTIVE; splx(s); return error; } slp->sl_timeout_count = 0; (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_IO, SCSI_LOW_TIMEOUT_START); splx(s); return 0; } /************************************************************** * scsi low log **************************************************************/ #ifdef SCSI_LOW_DIAGNOSTIC static void scsi_low_msg_log_init(struct scsi_low_msg_log *); static void scsi_low_msg_log_write(struct scsi_low_msg_log *, u_int8_t *, int); static void scsi_low_msg_log_show(struct scsi_low_msg_log *, char *, int); static void scsi_low_msg_log_init(slmlp) struct scsi_low_msg_log *slmlp; { slmlp->slml_ptr = 0; } static void scsi_low_msg_log_write(slmlp, datap, len) struct scsi_low_msg_log *slmlp; u_int8_t *datap; int len; { int ptr, ind; if (slmlp->slml_ptr >= SCSI_LOW_MSG_LOG_DATALEN) return; ptr = slmlp->slml_ptr ++; for (ind = 0; ind < sizeof(slmlp->slml_msg[0]) && ind < len; ind ++) slmlp->slml_msg[ptr].msg[ind] = datap[ind]; for ( ; ind < sizeof(slmlp->slml_msg[0]); ind ++) slmlp->slml_msg[ptr].msg[ind] = 0; } static void scsi_low_msg_log_show(slmlp, s, len) struct scsi_low_msg_log *slmlp; char *s; int len; { int ptr, ind; printf("%s: (%d) ", s, slmlp->slml_ptr); for (ptr = 0; ptr < slmlp->slml_ptr; ptr ++) { for (ind = 0; ind < len && ind < sizeof(slmlp->slml_msg[0]); ind ++) { printf("[%x]", (u_int) slmlp->slml_msg[ptr].msg[ind]); } printf(">"); } printf("\n"); } #endif /* SCSI_LOW_DIAGNOSTIC */ /************************************************************** * power control **************************************************************/ static void scsi_low_engage(arg) void *arg; { struct scsi_low_softc *slp = arg; int s = SCSI_LOW_SPLSCSI(); switch (slp->sl_rstep) { case 0: slp->sl_rstep ++; (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_ENGAGE); (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_ENGAGE, SCSI_LOW_TIMEOUT_START); break; case 1: slp->sl_rstep ++; slp->sl_flags &= ~HW_RESUME; scsi_low_start(slp); break; case 2: break; } splx(s); } static int scsi_low_init(slp, flags) struct scsi_low_softc *slp; u_int flags; { int rv = 0; slp->sl_flags |= HW_INITIALIZING; /* clear power control timeout */ if ((slp->sl_flags & HW_POWERCTRL) != 0) { (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_ENGAGE, SCSI_LOW_TIMEOUT_STOP); slp->sl_flags &= ~(HW_POWDOWN | HW_RESUME); slp->sl_active = 1; slp->sl_powc = SCSI_LOW_POWDOWN_TC; } /* reset current nexus */ scsi_low_reset_nexus(slp, flags); if ((slp->sl_flags & HW_INACTIVE) != 0) { rv = EBUSY; goto out; } if (flags != SCSI_LOW_RESTART_SOFT) { rv = ((*slp->sl_funcs->scsi_low_init) (slp, flags)); } out: slp->sl_flags &= ~HW_INITIALIZING; return rv; } /************************************************************** * allocate lun_info **************************************************************/ static struct lun_info * scsi_low_alloc_li(ti, lun, alloc) struct targ_info *ti; int lun; int alloc; { struct scsi_low_softc *slp = ti->ti_sc; struct lun_info *li; li = LIST_FIRST(&ti->ti_litab); if (li != NULL) { if (li->li_lun == lun) return li; while ((li = LIST_NEXT(li, lun_chain)) != NULL) { if (li->li_lun == lun) { LIST_REMOVE(li, lun_chain); LIST_INSERT_HEAD(&ti->ti_litab, li, lun_chain); return li; } } } if (alloc == 0) return li; li = SCSI_LOW_MALLOC(ti->ti_lunsize); if (li == NULL) panic("no lun info mem"); SCSI_LOW_BZERO(li, ti->ti_lunsize); li->li_lun = lun; li->li_ti = ti; li->li_cfgflags = SCSI_LOW_SYNC | SCSI_LOW_LINK | SCSI_LOW_DISC | SCSI_LOW_QTAG; li->li_quirks = li->li_diskflags = SCSI_LOW_DISK_LFLAGS; li->li_flags_valid = SCSI_LOW_LUN_FLAGS_USER_VALID; #ifdef SCSI_LOW_FLAGS_QUIRKS_OK li->li_flags_valid |= SCSI_LOW_LUN_FLAGS_QUIRKS_VALID; #endif /* SCSI_LOW_FLAGS_QUIRKS_OK */ li->li_qtagbits = (u_int) -1; TAILQ_INIT(&li->li_discq); LIST_INSERT_HEAD(&ti->ti_litab, li, lun_chain); /* host specific structure initialization per lun */ if (slp->sl_funcs->scsi_low_lun_init != NULL) (*slp->sl_funcs->scsi_low_lun_init) (slp, ti, li, SCSI_LOW_INFO_ALLOC); scsi_low_calcf_lun(li); return li; } /************************************************************** * allocate targ_info **************************************************************/ static struct targ_info * scsi_low_alloc_ti(slp, targ) struct scsi_low_softc *slp; int targ; { struct targ_info *ti; if (TAILQ_FIRST(&slp->sl_titab) == NULL) TAILQ_INIT(&slp->sl_titab); ti = SCSI_LOW_MALLOC(slp->sl_targsize); if (ti == NULL) panic("%s short of memory", slp->sl_xname); SCSI_LOW_BZERO(ti, slp->sl_targsize); ti->ti_id = targ; ti->ti_sc = slp; slp->sl_ti[targ] = ti; TAILQ_INSERT_TAIL(&slp->sl_titab, ti, ti_chain); LIST_INIT(&ti->ti_litab); ti->ti_quirks = ti->ti_diskflags = SCSI_LOW_DISK_TFLAGS; ti->ti_owidth = SCSI_LOW_BUS_WIDTH_8; ti->ti_flags_valid = SCSI_LOW_TARG_FLAGS_USER_VALID; #ifdef SCSI_LOW_FLAGS_QUIRKS_OK ti->ti_flags_valid |= SCSI_LOW_TARG_FLAGS_QUIRKS_VALID; #endif /* SCSI_LOW_FLAGS_QUIRKS_OK */ if (slp->sl_funcs->scsi_low_targ_init != NULL) { (*slp->sl_funcs->scsi_low_targ_init) (slp, ti, SCSI_LOW_INFO_ALLOC); } scsi_low_calcf_target(ti); return ti; } static void scsi_low_free_ti(slp) struct scsi_low_softc *slp; { struct targ_info *ti, *tib; struct lun_info *li, *nli; for (ti = TAILQ_FIRST(&slp->sl_titab); ti; ti = tib) { for (li = LIST_FIRST(&ti->ti_litab); li != NULL; li = nli) { if (slp->sl_funcs->scsi_low_lun_init != NULL) { (*slp->sl_funcs->scsi_low_lun_init) (slp, ti, li, SCSI_LOW_INFO_DEALLOC); } nli = LIST_NEXT(li, lun_chain); SCSI_LOW_FREE(li); } if (slp->sl_funcs->scsi_low_targ_init != NULL) { (*slp->sl_funcs->scsi_low_targ_init) (slp, ti, SCSI_LOW_INFO_DEALLOC); } tib = TAILQ_NEXT(ti, ti_chain); SCSI_LOW_FREE(ti); } } /************************************************************** * timeout **************************************************************/ void scsi_low_bus_idle(slp) struct scsi_low_softc *slp; { slp->sl_retry_sel = 0; if (slp->sl_Tnexus == NULL) scsi_low_start(slp); } static void scsi_low_timeout(arg) void *arg; { struct scsi_low_softc *slp = arg; int s; s = SCSI_LOW_SPLSCSI(); (void) scsi_low_timeout_check(slp); (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_IO, SCSI_LOW_TIMEOUT_START); splx(s); } static int scsi_low_timeout_check(slp) struct scsi_low_softc *slp; { struct targ_info *ti; struct lun_info *li; struct slccb *cb = NULL; /* XXX */ /* selection restart */ if (slp->sl_retry_sel != 0) { slp->sl_retry_sel = 0; if (slp->sl_Tnexus != NULL) goto step1; cb = TAILQ_FIRST(&slp->sl_start); if (cb == NULL) goto step1; if (cb->ccb_selrcnt >= SCSI_LOW_MAX_SELECTION_RETRY) { cb->ccb_flags |= CCB_NORETRY; cb->ccb_error |= SELTIMEOUTIO; if (scsi_low_revoke_ccb(slp, cb, 1) != NULL) panic("%s: ccb not finished", slp->sl_xname); } if (slp->sl_Tnexus == NULL) scsi_low_start(slp); } /* call hardware timeout */ step1: if (slp->sl_funcs->scsi_low_timeout != NULL) { (*slp->sl_funcs->scsi_low_timeout) (slp); } if (slp->sl_timeout_count ++ < SCSI_LOW_TIMEOUT_CHECK_INTERVAL * SCSI_LOW_TIMEOUT_HZ) return 0; slp->sl_timeout_count = 0; if (slp->sl_nio > 0) { if ((cb = slp->sl_Qnexus) != NULL) { cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; if (cb->ccb_tc < 0) goto bus_reset; } else if (slp->sl_disc == 0) { if ((cb = TAILQ_FIRST(&slp->sl_start)) == NULL) return 0; cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; if (cb->ccb_tc < 0) goto bus_reset; } else for (ti = TAILQ_FIRST(&slp->sl_titab); ti != NULL; ti = TAILQ_NEXT(ti, ti_chain)) { if (ti->ti_disc == 0) continue; for (li = LIST_FIRST(&ti->ti_litab); li != NULL; li = LIST_NEXT(li, lun_chain)) { for (cb = TAILQ_FIRST(&li->li_discq); cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) { cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; if (cb->ccb_tc < 0) goto bus_reset; } } } } else if ((slp->sl_flags & HW_POWERCTRL) != 0) { if ((slp->sl_flags & (HW_POWDOWN | HW_RESUME)) != 0) return 0; if (slp->sl_active != 0) { slp->sl_powc = SCSI_LOW_POWDOWN_TC; slp->sl_active = 0; return 0; } slp->sl_powc --; if (slp->sl_powc < 0) { slp->sl_powc = SCSI_LOW_POWDOWN_TC; slp->sl_flags |= HW_POWDOWN; (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_POWDOWN); } } return 0; bus_reset: cb->ccb_error |= TIMEOUTIO; printf("%s: slccb (0x%lx) timeout!\n", slp->sl_xname, (u_long) cb); scsi_low_info(slp, NULL, "scsi bus hangup. try to recover."); scsi_low_init(slp, SCSI_LOW_RESTART_HARD); scsi_low_start(slp); return ERESTART; } static int scsi_low_abort_ccb(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct targ_info *ti; struct lun_info *li; u_int msg; if (cb == NULL) return EINVAL; if ((cb->ccb_omsgoutflag & (SCSI_LOW_MSG_ABORT | SCSI_LOW_MSG_ABORT_QTAG)) != 0) return EBUSY; ti = cb->ti; li = cb->li; if (cb->ccb_tag == SCSI_LOW_UNKTAG) msg = SCSI_LOW_MSG_ABORT; else msg = SCSI_LOW_MSG_ABORT_QTAG; cb->ccb_error |= ABORTIO; cb->ccb_flags |= CCB_NORETRY; scsi_low_ccb_message_assert(cb, msg); if (cb == slp->sl_Qnexus) { scsi_low_assert_msg(slp, ti, msg, 1); } else if ((cb->ccb_flags & CCB_DISCQ) != 0) { if (scsi_low_revoke_ccb(slp, cb, 0) == NULL) panic("%s: revoked ccb done", slp->sl_xname); cb->ccb_flags |= CCB_STARTQ; TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); if (slp->sl_Tnexus == NULL) scsi_low_start(slp); } else { if (scsi_low_revoke_ccb(slp, cb, 1) != NULL) panic("%s: revoked ccb retried", slp->sl_xname); } return 0; } /************************************************************** * Generic SCSI INTERFACE **************************************************************/ int scsi_low_attach(slp, openings, ntargs, nluns, targsize, lunsize) struct scsi_low_softc *slp; int openings, ntargs, nluns, targsize, lunsize; { struct targ_info *ti; struct lun_info *li; int s, i, nccb, rv; #ifdef SCSI_LOW_INTERFACE_XS slp->sl_osdep_fp = &scsi_low_osdep_funcs_xs; #endif /* SCSI_LOW_INTERFACE_XS */ #ifdef SCSI_LOW_INTERFACE_CAM slp->sl_osdep_fp = &scsi_low_osdep_funcs_cam; #endif /* SCSI_LOW_INTERFACE_CAM */ if (slp->sl_osdep_fp == NULL) panic("scsi_low: interface not spcified"); if (ntargs > SCSI_LOW_NTARGETS) { printf("scsi_low: %d targets are too large\n", ntargs); printf("change kernel options SCSI_LOW_NTARGETS"); return EINVAL; } if (openings <= 0) slp->sl_openings = (SCSI_LOW_NCCB / ntargs); else slp->sl_openings = openings; slp->sl_ntargs = ntargs; slp->sl_nluns = nluns; slp->sl_max_retry = SCSI_LOW_MAX_RETRY; if (lunsize < sizeof(struct lun_info)) lunsize = sizeof(struct lun_info); if (targsize < sizeof(struct targ_info)) targsize = sizeof(struct targ_info); slp->sl_targsize = targsize; for (i = 0; i < ntargs; i ++) { ti = scsi_low_alloc_ti(slp, i); ti->ti_lunsize = lunsize; li = scsi_low_alloc_li(ti, 0, 1); } /* initialize queue */ nccb = openings * ntargs; if (nccb >= SCSI_LOW_NCCB || nccb <= 0) nccb = SCSI_LOW_NCCB; scsi_low_init_ccbque(nccb); TAILQ_INIT(&slp->sl_start); /* call os depend attach */ s = SCSI_LOW_SPLSCSI(); rv = (*slp->sl_osdep_fp->scsi_low_osdep_attach) (slp); if (rv != 0) { splx(s); printf("%s: scsi_low_attach: osdep attach failed\n", slp->sl_xname); return EINVAL; } /* check hardware */ SCSI_LOW_DELAY(1000); /* wait for 1ms */ if (scsi_low_init(slp, SCSI_LOW_RESTART_HARD) != 0) { splx(s); printf("%s: scsi_low_attach: initialization failed\n", slp->sl_xname); return EINVAL; } /* start watch dog */ slp->sl_timeout_count = 0; (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_IO, SCSI_LOW_TIMEOUT_START); LIST_INSERT_HEAD(&sl_tab, slp, sl_chain); /* fake call */ scsi_low_abort_ccb(slp, scsi_low_find_ccb(slp, 0, 0, NULL)); #ifdef SCSI_LOW_START_UP_CHECK /* probing devices */ scsi_low_start_up(slp); #endif /* SCSI_LOW_START_UP_CHECK */ /* call os depend attach done*/ (*slp->sl_osdep_fp->scsi_low_osdep_world_start) (slp); splx(s); return 0; } int scsi_low_dettach(slp) struct scsi_low_softc *slp; { int s, rv; s = SCSI_LOW_SPLSCSI(); if (scsi_low_is_busy(slp) != 0) { splx(s); return EBUSY; } scsi_low_deactivate(slp); rv = (*slp->sl_osdep_fp->scsi_low_osdep_dettach) (slp); if (rv != 0) { splx(s); return EBUSY; } scsi_low_free_ti(slp); LIST_REMOVE(slp, sl_chain); splx(s); return 0; } /************************************************************** * Generic enqueue **************************************************************/ static int scsi_low_enqueue(slp, ti, li, cb, flags, msg) struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; struct slccb *cb; u_int flags, msg; { cb->ti = ti; cb->li = li; scsi_low_ccb_message_assert(cb, msg); cb->ccb_otag = cb->ccb_tag = SCSI_LOW_UNKTAG; scsi_low_alloc_qtag(cb); cb->ccb_flags = flags | CCB_STARTQ; cb->ccb_tc = cb->ccb_tcmax = SCSI_LOW_MIN_TOUT; cb->ccb_error |= PENDINGIO; if ((flags & CCB_URGENT) != 0) { TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); } else { TAILQ_INSERT_TAIL(&slp->sl_start, cb, ccb_chain); } slp->sl_nio ++; if (slp->sl_Tnexus == NULL) scsi_low_start(slp); return 0; } static int scsi_low_message_enqueue(slp, ti, li, flags) struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; u_int flags; { struct slccb *cb; u_int tmsgflags; tmsgflags = ti->ti_setup_msg; ti->ti_setup_msg = 0; flags |= CCB_NORETRY; if ((cb = SCSI_LOW_ALLOC_CCB(1)) == NULL) return ENOMEM; cb->osdep = NULL; cb->bp = NULL; scsi_low_enqueue(slp, ti, li, cb, flags, tmsgflags); return 0; } /************************************************************** * Generic Start & Done **************************************************************/ #define SLSC_MODE_SENSE_SHORT 0x1a static u_int8_t ss_cmd[6] = {START_STOP, 0, 0, 0, SSS_START, 0}; static u_int8_t sms_cmd[6] = {SLSC_MODE_SENSE_SHORT, 0x08, 0x0a, 0, sizeof(struct scsi_low_mode_sense_data), 0}; static u_int8_t inq_cmd[6] = {INQUIRY, 0, 0, 0, sizeof(struct scsi_low_inq_data), 0}; static u_int8_t unit_ready_cmd[6]; static int scsi_low_setup_start(struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *); static int scsi_low_sense_abort_start(struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *); static int scsi_low_resume(struct scsi_low_softc *); static void scsi_low_unit_ready_cmd(cb) struct slccb *cb; { cb->ccb_scp.scp_cmd = unit_ready_cmd; cb->ccb_scp.scp_cmdlen = sizeof(unit_ready_cmd); cb->ccb_scp.scp_datalen = 0; cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = 15; } static int scsi_low_sense_abort_start(slp, ti, li, cb) struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; struct slccb *cb; { cb->ccb_scp.scp_cmdlen = 6; SCSI_LOW_BZERO(cb->ccb_scsi_cmd, cb->ccb_scp.scp_cmdlen); cb->ccb_scsi_cmd[0] = REQUEST_SENSE; cb->ccb_scsi_cmd[4] = sizeof(cb->ccb_sense); cb->ccb_scp.scp_cmd = cb->ccb_scsi_cmd; cb->ccb_scp.scp_data = (u_int8_t *) &cb->ccb_sense; cb->ccb_scp.scp_datalen = sizeof(cb->ccb_sense); cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = 15; scsi_low_ccb_message_clear(cb); if ((cb->ccb_flags & CCB_CLEARQ) != 0) { scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); } else { SCSI_LOW_BZERO(&cb->ccb_sense, sizeof(cb->ccb_sense)); #ifdef SCSI_LOW_NEGOTIATE_BEFORE_SENSE scsi_low_assert_msg(slp, ti, ti->ti_setup_msg_done, 0); #endif /* SCSI_LOW_NEGOTIATE_BEFORE_SENSE */ } return SCSI_LOW_START_NO_QTAG; } static int scsi_low_setup_start(slp, ti, li, cb) struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; struct slccb *cb; { switch(li->li_state) { case SCSI_LOW_LUN_SLEEP: scsi_low_unit_ready_cmd(cb); break; case SCSI_LOW_LUN_START: cb->ccb_scp.scp_cmd = ss_cmd; cb->ccb_scp.scp_cmdlen = sizeof(ss_cmd); cb->ccb_scp.scp_datalen = 0; cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = 30; break; case SCSI_LOW_LUN_INQ: cb->ccb_scp.scp_cmd = inq_cmd; cb->ccb_scp.scp_cmdlen = sizeof(inq_cmd); cb->ccb_scp.scp_data = (u_int8_t *)&li->li_inq; cb->ccb_scp.scp_datalen = sizeof(li->li_inq); cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = 15; break; case SCSI_LOW_LUN_MODEQ: cb->ccb_scp.scp_cmd = sms_cmd; cb->ccb_scp.scp_cmdlen = sizeof(sms_cmd); cb->ccb_scp.scp_data = (u_int8_t *)&li->li_sms; cb->ccb_scp.scp_datalen = sizeof(li->li_sms); cb->ccb_scp.scp_direction = SCSI_LOW_READ; cb->ccb_tcmax = 15; return SCSI_LOW_START_QTAG; default: panic("%s: no setup phase", slp->sl_xname); } return SCSI_LOW_START_NO_QTAG; } static int scsi_low_resume(slp) struct scsi_low_softc *slp; { if (slp->sl_flags & HW_RESUME) return EJUSTRETURN; slp->sl_flags &= ~HW_POWDOWN; if (slp->sl_funcs->scsi_low_power != NULL) { slp->sl_flags |= HW_RESUME; slp->sl_rstep = 0; (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_ENGAGE); (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, SCSI_LOW_TIMEOUT_CH_ENGAGE, SCSI_LOW_TIMEOUT_START); return EJUSTRETURN; } return 0; } static void scsi_low_start(slp) struct scsi_low_softc *slp; { struct targ_info *ti; struct lun_info *li; struct slccb *cb; int rv; /* check hardware exists or under initializations ? */ if ((slp->sl_flags & (HW_INACTIVE | HW_INITIALIZING)) != 0) return; /* check hardware power up ? */ if ((slp->sl_flags & HW_POWERCTRL) != 0) { slp->sl_active ++; if (slp->sl_flags & (HW_POWDOWN | HW_RESUME)) { if (scsi_low_resume(slp) == EJUSTRETURN) return; } } /* setup nexus */ #ifdef SCSI_LOW_DIAGNOSTIC if (slp->sl_Tnexus || slp->sl_Lnexus || slp->sl_Qnexus) { scsi_low_info(slp, NULL, "NEXUS INCOSISTENT"); panic("%s: inconsistent", slp->sl_xname); } #endif /* SCSI_LOW_DIAGNOSTIC */ for (cb = TAILQ_FIRST(&slp->sl_start); cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) { li = cb->li; if (li->li_disc == 0) { goto scsi_low_cmd_start; } else if (li->li_nqio > 0) { if (li->li_nqio < li->li_maxnqio || (cb->ccb_flags & (CCB_SENSE | CCB_CLEARQ)) != 0) goto scsi_low_cmd_start; } } return; scsi_low_cmd_start: cb->ccb_flags &= ~CCB_STARTQ; TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); ti = cb->ti; /* clear all error flag bits (for restart) */ cb->ccb_error = 0; cb->ccb_datalen = -1; cb->ccb_scp.scp_status = ST_UNKNOWN; /* setup nexus pointer */ slp->sl_Qnexus = cb; slp->sl_Lnexus = li; slp->sl_Tnexus = ti; /* initialize msgsys */ scsi_low_init_msgsys(slp, ti); /* exec cmd */ if ((cb->ccb_flags & (CCB_SENSE | CCB_CLEARQ)) != 0) { /* CA state or forced abort */ rv = scsi_low_sense_abort_start(slp, ti, li, cb); } else if (li->li_state >= SCSI_LOW_LUN_OK) { cb->ccb_flags &= ~CCB_INTERNAL; rv = (*slp->sl_osdep_fp->scsi_low_osdep_ccb_setup) (slp, cb); if (cb->ccb_msgoutflag != 0) { scsi_low_ccb_message_exec(slp, cb); } } else { cb->ccb_flags |= CCB_INTERNAL; rv = scsi_low_setup_start(slp, ti, li, cb); } /* allocate qtag */ #define SCSI_LOW_QTAG_OK (SCSI_LOW_QTAG | SCSI_LOW_DISC) if (rv == SCSI_LOW_START_QTAG && (li->li_flags & SCSI_LOW_QTAG_OK) == SCSI_LOW_QTAG_OK && li->li_maxnqio > 0) { u_int qmsg; scsi_low_activate_qtag(cb); if ((scsi_low_cmd_flags[cb->ccb_scp.scp_cmd[0]] & SCSI_LOW_CMD_ORDERED_QTAG) != 0) qmsg = SCSI_LOW_MSG_ORDERED_QTAG; else if ((cb->ccb_flags & CCB_URGENT) != 0) qmsg = SCSI_LOW_MSG_HEAD_QTAG; else qmsg = SCSI_LOW_MSG_SIMPLE_QTAG; scsi_low_assert_msg(slp, ti, qmsg, 0); } /* timeout */ if (cb->ccb_tcmax < SCSI_LOW_MIN_TOUT) cb->ccb_tcmax = SCSI_LOW_MIN_TOUT; cb->ccb_tc = cb->ccb_tcmax; /* setup saved scsi data pointer */ cb->ccb_sscp = cb->ccb_scp; /* setup current scsi pointer */ slp->sl_scp = cb->ccb_sscp; slp->sl_error = cb->ccb_error; /* assert always an identify msg */ scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_IDENTIFY, 0); /* debug section */ #ifdef SCSI_LOW_DIAGNOSTIC scsi_low_msg_log_init(&ti->ti_log_msgin); scsi_low_msg_log_init(&ti->ti_log_msgout); #endif /* SCSI_LOW_DIAGNOSTIC */ /* selection start */ slp->sl_selid = cb; rv = ((*slp->sl_funcs->scsi_low_start_bus) (slp, cb)); if (rv == SCSI_LOW_START_OK) { #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_win ++; #endif /* SCSI_LOW_STATICS */ return; } scsi_low_arbit_fail(slp, cb); #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_fail ++; #endif /* SCSI_LOW_STATICS */ } void scsi_low_arbit_fail(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct targ_info *ti = cb->ti; scsi_low_deactivate_qtag(cb); scsi_low_ccb_message_retry(cb); cb->ccb_flags |= CCB_STARTQ; TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); scsi_low_bus_release(slp, ti); cb->ccb_selrcnt ++; if (slp->sl_disc == 0) { #ifdef SCSI_LOW_DIAGNOSTIC printf("%s: try selection again\n", slp->sl_xname); #endif /* SCSI_LOW_DIAGNOSTIC */ slp->sl_retry_sel = 1; } } static void scsi_low_bus_release(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { if (ti->ti_disc > 0) { SCSI_LOW_SETUP_PHASE(ti, PH_DISC); } else { SCSI_LOW_SETUP_PHASE(ti, PH_NULL); } /* clear all nexus pointer */ slp->sl_Qnexus = NULL; slp->sl_Lnexus = NULL; slp->sl_Tnexus = NULL; /* clear selection assert */ slp->sl_selid = NULL; /* clear nexus data */ slp->sl_scp.scp_direction = SCSI_LOW_RWUNK; /* clear phase change counter */ slp->sl_ph_count = 0; } static int scsi_low_setup_done(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { struct targ_info *ti; struct lun_info *li; ti = cb->ti; li = cb->li; if (cb->ccb_rcnt >= slp->sl_max_retry) { cb->ccb_error |= ABORTIO; return SCSI_LOW_DONE_COMPLETE; } /* XXX: special huck for selection timeout */ if (li->li_state == SCSI_LOW_LUN_SLEEP && (cb->ccb_error & SELTIMEOUTIO) != 0) { cb->ccb_error |= ABORTIO; return SCSI_LOW_DONE_COMPLETE; } switch(li->li_state) { case SCSI_LOW_LUN_INQ: if (cb->ccb_error != 0) { li->li_diskflags &= ~(SCSI_LOW_DISK_LINK | SCSI_LOW_DISK_QTAG); if (li->li_lun > 0) goto resume; ti->ti_diskflags &= ~(SCSI_LOW_DISK_SYNC | SCSI_LOW_DISK_WIDE); } else if ((li->li_inq.sd_version & 7) >= 2 || (li->li_inq.sd_len >= 4)) { if ((li->li_inq.sd_support & 0x2) == 0) li->li_diskflags &= ~SCSI_LOW_DISK_QTAG; if ((li->li_inq.sd_support & 0x8) == 0) li->li_diskflags &= ~SCSI_LOW_DISK_LINK; if (li->li_lun > 0) goto resume; if ((li->li_inq.sd_support & 0x10) == 0) ti->ti_diskflags &= ~SCSI_LOW_DISK_SYNC; if ((li->li_inq.sd_support & 0x20) == 0) ti->ti_diskflags &= ~SCSI_LOW_DISK_WIDE_16; if ((li->li_inq.sd_support & 0x40) == 0) ti->ti_diskflags &= ~SCSI_LOW_DISK_WIDE_32; } else { li->li_diskflags &= ~(SCSI_LOW_DISK_QTAG | SCSI_LOW_DISK_LINK); if (li->li_lun > 0) goto resume; ti->ti_diskflags &= ~SCSI_LOW_DISK_WIDE; } ti->ti_flags_valid |= SCSI_LOW_TARG_FLAGS_DISK_VALID; resume: scsi_low_calcf_target(ti); scsi_low_calcf_lun(li); break; case SCSI_LOW_LUN_MODEQ: if (cb->ccb_error != 0) { if (cb->ccb_error & SENSEIO) { #ifdef SCSI_LOW_DEBUG if (scsi_low_debug & SCSI_LOW_DEBUG_SENSE) { printf("SENSE: [%x][%x][%x][%x][%x]\n", (u_int) cb->ccb_sense.error_code, (u_int) cb->ccb_sense.segment, (u_int) cb->ccb_sense.flags, (u_int) cb->ccb_sense.add_sense_code, (u_int) cb->ccb_sense.add_sense_code_qual); } #endif /* SCSI_LOW_DEBUG */ } else { li->li_diskflags &= ~SCSI_LOW_DISK_QTAG; } } else if ((li->li_sms.sms_cmp.cmp_page & 0x3f) == 0x0a) { if (li->li_sms.sms_cmp.cmp_qc & 0x02) li->li_qflags |= SCSI_LOW_QFLAG_CA_QCLEAR; else li->li_qflags &= ~SCSI_LOW_QFLAG_CA_QCLEAR; if ((li->li_sms.sms_cmp.cmp_qc & 0x01) != 0) li->li_diskflags &= ~SCSI_LOW_DISK_QTAG; } li->li_flags_valid |= SCSI_LOW_LUN_FLAGS_DISK_VALID; scsi_low_calcf_lun(li); break; default: break; } li->li_state ++; if (li->li_state == SCSI_LOW_LUN_OK) { scsi_low_calcf_target(ti); scsi_low_calcf_lun(li); if (li->li_flags_valid == SCSI_LOW_LUN_FLAGS_ALL_VALID && (slp->sl_show_result & SHOW_CALCF_RES) != 0) { scsi_low_calcf_show(li); } } cb->ccb_rcnt --; return SCSI_LOW_DONE_RETRY; } static int scsi_low_done(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { int rv; if (cb->ccb_error == 0) { if ((cb->ccb_flags & (CCB_SENSE | CCB_CLEARQ)) != 0) { #ifdef SCSI_LOW_QCLEAR_AFTER_CA /* XXX: * SCSI-2 draft suggests * page 0x0a QErr bit determins if * the target aborts or continues * the queueing io's after CA state resolved. * However many targets seem not to support * the page 0x0a. Thus we should manually clear the * queuing io's after CA state. */ if ((cb->ccb_flags & CCB_CLEARQ) == 0) { cb->ccb_rcnt --; cb->ccb_flags |= CCB_CLEARQ; goto retry; } #endif /* SCSI_LOW_QCLEAR_AFTER_CA */ if ((cb->ccb_flags & CCB_SENSE) != 0) cb->ccb_error |= (SENSEIO | ABORTIO); cb->ccb_flags &= ~(CCB_SENSE | CCB_CLEARQ); } else switch (cb->ccb_sscp.scp_status) { case ST_GOOD: case ST_MET: case ST_INTERGOOD: case ST_INTERMET: if (cb->ccb_datalen == 0 || cb->ccb_scp.scp_datalen == 0) break; if (cb->ccb_scp.scp_cmdlen > 0 && (scsi_low_cmd_flags[cb->ccb_scp.scp_cmd[0]] & SCSI_LOW_CMD_RESIDUAL_CHK) == 0) break; cb->ccb_error |= PDMAERR; break; case ST_BUSY: case ST_QUEFULL: cb->ccb_error |= (BUSYERR | STATERR); break; case ST_CONFLICT: cb->ccb_error |= (STATERR | ABORTIO); break; case ST_CHKCOND: case ST_CMDTERM: if (cb->ccb_flags & (CCB_AUTOSENSE | CCB_INTERNAL)) { cb->ccb_rcnt --; cb->ccb_flags |= CCB_SENSE; goto retry; } cb->ccb_error |= (UACAERR | STATERR | ABORTIO); break; case ST_UNKNOWN: default: cb->ccb_error |= FATALIO; break; } } else { if (cb->ccb_flags & CCB_SENSE) { cb->ccb_error |= (SENSEERR | ABORTIO); } cb->ccb_flags &= ~(CCB_CLEARQ | CCB_SENSE); } /* internal ccb */ if ((cb->ccb_flags & CCB_INTERNAL) != 0) { if (scsi_low_setup_done(slp, cb) == SCSI_LOW_DONE_RETRY) goto retry; } /* check a ccb msgout flag */ if (cb->ccb_omsgoutflag != 0) { #define SCSI_LOW_MSG_ABORT_OK (SCSI_LOW_MSG_ABORT | \ SCSI_LOW_MSG_ABORT_QTAG | \ SCSI_LOW_MSG_CLEAR_QTAG | \ SCSI_LOW_MSG_TERMIO) if ((cb->ccb_omsgoutflag & SCSI_LOW_MSG_ABORT_OK) != 0) { cb->ccb_error |= ABORTIO; } } /* call OS depend done */ if (cb->osdep != NULL) { rv = (*slp->sl_osdep_fp->scsi_low_osdep_done) (slp, cb); if (rv == EJUSTRETURN) goto retry; } else if (cb->ccb_error != 0) { if (cb->ccb_rcnt >= slp->sl_max_retry) cb->ccb_error |= ABORTIO; if ((cb->ccb_flags & CCB_NORETRY) == 0 && (cb->ccb_error & ABORTIO) == 0) goto retry; } /* free our target */ #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_DONE, cb->ti->ti_id) != 0) { printf(">> SCSI_LOW_DONE_COMPLETE ===============\n"); scsi_low_print(slp, NULL); } #endif /* SCSI_LOW_DEBUG */ scsi_low_deactivate_qtag(cb); scsi_low_dealloc_qtag(cb); scsi_low_free_ccb(cb); slp->sl_nio --; return SCSI_LOW_DONE_COMPLETE; retry: #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_DONE, cb->ti->ti_id) != 0) { printf("** SCSI_LOW_DONE_RETRY ===============\n"); scsi_low_print(slp, NULL); } #endif /* SCSI_LOW_DEBUG */ cb->ccb_rcnt ++; scsi_low_deactivate_qtag(cb); scsi_low_ccb_message_retry(cb); return SCSI_LOW_DONE_RETRY; } /************************************************************** * Reset **************************************************************/ static void scsi_low_reset_nexus_target(slp, ti, fdone) struct scsi_low_softc *slp; struct targ_info *ti; int fdone; { struct lun_info *li; for (li = LIST_FIRST(&ti->ti_litab); li != NULL; li = LIST_NEXT(li, lun_chain)) { scsi_low_reset_nexus_lun(slp, li, fdone); li->li_state = SCSI_LOW_LUN_SLEEP; li->li_maxnqio = 0; } ti->ti_disc = 0; ti->ti_setup_msg = 0; ti->ti_setup_msg_done = 0; ti->ti_osynch.offset = ti->ti_osynch.period = 0; ti->ti_owidth = SCSI_LOW_BUS_WIDTH_8; ti->ti_diskflags = SCSI_LOW_DISK_TFLAGS; ti->ti_flags_valid &= ~SCSI_LOW_TARG_FLAGS_DISK_VALID; if (slp->sl_funcs->scsi_low_targ_init != NULL) { ((*slp->sl_funcs->scsi_low_targ_init) (slp, ti, SCSI_LOW_INFO_REVOKE)); } scsi_low_calcf_target(ti); for (li = LIST_FIRST(&ti->ti_litab); li != NULL; li = LIST_NEXT(li, lun_chain)) { li->li_flags = 0; li->li_diskflags = SCSI_LOW_DISK_LFLAGS; li->li_flags_valid &= ~SCSI_LOW_LUN_FLAGS_DISK_VALID; if (slp->sl_funcs->scsi_low_lun_init != NULL) { ((*slp->sl_funcs->scsi_low_lun_init) (slp, ti, li, SCSI_LOW_INFO_REVOKE)); } scsi_low_calcf_lun(li); } } static void scsi_low_reset_nexus(slp, fdone) struct scsi_low_softc *slp; int fdone; { struct targ_info *ti; struct slccb *cb, *topcb; if ((cb = slp->sl_Qnexus) != NULL) { topcb = scsi_low_revoke_ccb(slp, cb, fdone); } else { topcb = NULL; } for (ti = TAILQ_FIRST(&slp->sl_titab); ti != NULL; ti = TAILQ_NEXT(ti, ti_chain)) { scsi_low_reset_nexus_target(slp, ti, fdone); scsi_low_bus_release(slp, ti); scsi_low_init_msgsys(slp, ti); } if (topcb != NULL) { topcb->ccb_flags |= CCB_STARTQ; TAILQ_INSERT_HEAD(&slp->sl_start, topcb, ccb_chain); } slp->sl_disc = 0; slp->sl_retry_sel = 0; slp->sl_flags &= ~HW_PDMASTART; } /* misc */ static int tw_pos; static char tw_chars[] = "|/-\\"; #define TWIDDLEWAIT 10000 static void scsi_low_twiddle_wait(void) { cnputc('\b'); cnputc(tw_chars[tw_pos++]); tw_pos %= (sizeof(tw_chars) - 1); SCSI_LOW_DELAY(TWIDDLEWAIT); } void scsi_low_bus_reset(slp) struct scsi_low_softc *slp; { int i; (*slp->sl_funcs->scsi_low_bus_reset) (slp); printf("%s: try to reset scsi bus ", slp->sl_xname); for (i = 0; i <= SCSI2_RESET_DELAY / TWIDDLEWAIT ; i++) scsi_low_twiddle_wait(); cnputc('\b'); printf("\n"); } int scsi_low_restart(slp, flags, s) struct scsi_low_softc *slp; int flags; u_char *s; { int error; if (s != NULL) printf("%s: scsi bus restart. reason: %s\n", slp->sl_xname, s); if ((error = scsi_low_init(slp, flags)) != 0) return error; scsi_low_start(slp); return 0; } /************************************************************** * disconnect and reselect **************************************************************/ #define MSGCMD_LUN(msg) (msg & 0x07) static struct slccb * scsi_low_establish_ccb(ti, li, tag) struct targ_info *ti; struct lun_info *li; scsi_low_tag_t tag; { struct scsi_low_softc *slp = ti->ti_sc; struct slccb *cb; if (li == NULL) return NULL; cb = TAILQ_FIRST(&li->li_discq); for ( ; cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) if (cb->ccb_tag == tag) goto found; return cb; /* * establish our ccb nexus */ found: #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_NEXUS_CHECK, ti->ti_id) != 0) { printf("%s: nexus(0x%lx) abort check start\n", slp->sl_xname, (u_long) cb); cb->ccb_flags |= (CCB_NORETRY | CCB_SILENT); scsi_low_revoke_ccb(slp, cb, 1); return NULL; } if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_ATTEN_CHECK, ti->ti_id) != 0) { if (cb->ccb_omsgoutflag == 0) scsi_low_ccb_message_assert(cb, SCSI_LOW_MSG_NOOP); } #endif /* SCSI_LOW_DEBUG */ TAILQ_REMOVE(&li->li_discq, cb, ccb_chain); cb->ccb_flags &= ~CCB_DISCQ; slp->sl_Qnexus = cb; slp->sl_scp = cb->ccb_sscp; slp->sl_error |= cb->ccb_error; slp->sl_disc --; ti->ti_disc --; li->li_disc --; /* inform "ccb nexus established" to the host driver */ (*slp->sl_funcs->scsi_low_establish_ccb_nexus) (slp); /* check msg */ if (cb->ccb_msgoutflag != 0) { scsi_low_ccb_message_exec(slp, cb); } return cb; } struct targ_info * scsi_low_reselected(slp, targ) struct scsi_low_softc *slp; u_int targ; { struct targ_info *ti; struct slccb *cb; u_char *s; /* * Check select vs reselected collision. */ if ((cb = slp->sl_selid) != NULL) { scsi_low_arbit_fail(slp, cb); #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_conflict ++; #endif /* SCSI_LOW_STATICS */ } /* * Check if no current active nexus. */ if (slp->sl_Tnexus != NULL) { s = "host busy"; goto world_restart; } /* * Check a valid target id asserted ? */ if (targ >= slp->sl_ntargs || targ == slp->sl_hostid) { s = "scsi id illegal"; goto world_restart; } /* * Check the target scsi status. */ ti = slp->sl_ti[targ]; if (ti->ti_phase != PH_DISC && ti->ti_phase != PH_NULL) { s = "phase mismatch"; goto world_restart; } /* * Setup init msgsys */ slp->sl_error = 0; scsi_low_init_msgsys(slp, ti); /* * Establish our target nexus */ SCSI_LOW_SETUP_PHASE(ti, PH_RESEL); slp->sl_Tnexus = ti; #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_reselected ++; #endif /* SCSI_LOW_STATICS */ return ti; world_restart: printf("%s: reselect(%x:unknown) %s\n", slp->sl_xname, targ, s); scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "reselect: scsi world confused"); return NULL; } /************************************************************** * cmd out pointer setup **************************************************************/ int scsi_low_cmd(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { struct slccb *cb = slp->sl_Qnexus; slp->sl_ph_count ++; if (cb == NULL) { /* * no ccb, abort! */ slp->sl_scp.scp_cmd = (u_int8_t *) &unit_ready_cmd; slp->sl_scp.scp_cmdlen = sizeof(unit_ready_cmd); slp->sl_scp.scp_datalen = 0; slp->sl_scp.scp_direction = SCSI_LOW_READ; slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); SCSI_LOW_INFO(slp, ti, "CMDOUT: ccb nexus not found"); return EINVAL; } else { #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_CMDLNK_CHECK, ti->ti_id)) { scsi_low_test_cmdlnk(slp, cb); } #endif /* SCSI_LOW_DEBUG */ } return 0; } /************************************************************** * data out pointer setup **************************************************************/ int scsi_low_data(slp, ti, bp, direction) struct scsi_low_softc *slp; struct targ_info *ti; struct buf **bp; int direction; { struct slccb *cb = slp->sl_Qnexus; if (cb != NULL && direction == cb->ccb_sscp.scp_direction) { *bp = cb->bp; return 0; } slp->sl_error |= (FATALIO | PDMAERR); slp->sl_scp.scp_datalen = 0; slp->sl_scp.scp_direction = direction; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); if (ti->ti_ophase != ti->ti_phase) { char *s; if (cb == NULL) s = "DATA PHASE: ccb nexus not found"; else s = "DATA PHASE: xfer direction mismatch"; SCSI_LOW_INFO(slp, ti, s); } *bp = NULL; return EINVAL; } /************************************************************** * MSG_SYS **************************************************************/ #define MSGINPTR_CLR(ti) {(ti)->ti_msginptr = 0; (ti)->ti_msginlen = 0;} #define MSGIN_PERIOD(ti) ((ti)->ti_msgin[3]) #define MSGIN_OFFSET(ti) ((ti)->ti_msgin[4]) #define MSGIN_WIDTHP(ti) ((ti)->ti_msgin[3]) #define MSGIN_DATA_LAST 0x30 static int scsi_low_errfunc_synch(struct scsi_low_softc *, u_int); static int scsi_low_errfunc_wide(struct scsi_low_softc *, u_int); static int scsi_low_errfunc_identify(struct scsi_low_softc *, u_int); static int scsi_low_errfunc_qtag(struct scsi_low_softc *, u_int); static int scsi_low_msgfunc_synch(struct scsi_low_softc *); static int scsi_low_msgfunc_wide(struct scsi_low_softc *); static int scsi_low_msgfunc_identify(struct scsi_low_softc *); static int scsi_low_msgfunc_abort(struct scsi_low_softc *); static int scsi_low_msgfunc_qabort(struct scsi_low_softc *); static int scsi_low_msgfunc_qtag(struct scsi_low_softc *); static int scsi_low_msgfunc_reset(struct scsi_low_softc *); struct scsi_low_msgout_data { u_int md_flags; u_int8_t md_msg; int (*md_msgfunc)(struct scsi_low_softc *); int (*md_errfunc)(struct scsi_low_softc *, u_int); #define MSG_RELEASE_ATN 0x0001 u_int md_condition; }; struct scsi_low_msgout_data scsi_low_msgout_data[] = { /* 0 */ {SCSI_LOW_MSG_RESET, MSG_RESET, scsi_low_msgfunc_reset, NULL, MSG_RELEASE_ATN}, /* 1 */ {SCSI_LOW_MSG_REJECT, MSG_REJECT, NULL, NULL, MSG_RELEASE_ATN}, /* 2 */ {SCSI_LOW_MSG_PARITY, MSG_PARITY, NULL, NULL, MSG_RELEASE_ATN}, /* 3 */ {SCSI_LOW_MSG_ERROR, MSG_I_ERROR, NULL, NULL, MSG_RELEASE_ATN}, /* 4 */ {SCSI_LOW_MSG_IDENTIFY, MSG_IDENTIFY, scsi_low_msgfunc_identify, scsi_low_errfunc_identify, 0}, /* 5 */ {SCSI_LOW_MSG_ABORT, MSG_ABORT, scsi_low_msgfunc_abort, NULL, MSG_RELEASE_ATN}, /* 6 */ {SCSI_LOW_MSG_TERMIO, MSG_TERM_IO, NULL, NULL, MSG_RELEASE_ATN}, /* 7 */ {SCSI_LOW_MSG_SIMPLE_QTAG, MSG_SIMPLE_QTAG, scsi_low_msgfunc_qtag, scsi_low_errfunc_qtag, 0}, /* 8 */ {SCSI_LOW_MSG_ORDERED_QTAG, MSG_ORDERED_QTAG, scsi_low_msgfunc_qtag, scsi_low_errfunc_qtag, 0}, /* 9 */{SCSI_LOW_MSG_HEAD_QTAG, MSG_HEAD_QTAG, scsi_low_msgfunc_qtag, scsi_low_errfunc_qtag, 0}, /* 10 */ {SCSI_LOW_MSG_ABORT_QTAG, MSG_ABORT_QTAG, scsi_low_msgfunc_qabort, NULL, MSG_RELEASE_ATN}, /* 11 */ {SCSI_LOW_MSG_CLEAR_QTAG, MSG_CLEAR_QTAG, scsi_low_msgfunc_abort, NULL, MSG_RELEASE_ATN}, /* 12 */{SCSI_LOW_MSG_WIDE, MSG_EXTEND, scsi_low_msgfunc_wide, scsi_low_errfunc_wide, MSG_RELEASE_ATN}, /* 13 */{SCSI_LOW_MSG_SYNCH, MSG_EXTEND, scsi_low_msgfunc_synch, scsi_low_errfunc_synch, MSG_RELEASE_ATN}, /* 14 */{SCSI_LOW_MSG_NOOP, MSG_NOOP, NULL, NULL, MSG_RELEASE_ATN}, /* 15 */{SCSI_LOW_MSG_ALL, 0}, }; static int scsi_low_msginfunc_ext(struct scsi_low_softc *); static int scsi_low_synch(struct scsi_low_softc *); static int scsi_low_wide(struct scsi_low_softc *); static int scsi_low_msginfunc_msg_reject(struct scsi_low_softc *); static int scsi_low_msginfunc_rejop(struct scsi_low_softc *); static int scsi_low_msginfunc_rp(struct scsi_low_softc *); static int scsi_low_msginfunc_sdp(struct scsi_low_softc *); static int scsi_low_msginfunc_disc(struct scsi_low_softc *); static int scsi_low_msginfunc_cc(struct scsi_low_softc *); static int scsi_low_msginfunc_lcc(struct scsi_low_softc *); static int scsi_low_msginfunc_parity(struct scsi_low_softc *); static int scsi_low_msginfunc_noop(struct scsi_low_softc *); static int scsi_low_msginfunc_simple_qtag(struct scsi_low_softc *); static int scsi_low_msginfunc_i_wide_residue(struct scsi_low_softc *); struct scsi_low_msgin_data { u_int md_len; int (*md_msgfunc)(struct scsi_low_softc *); }; struct scsi_low_msgin_data scsi_low_msgin_data[] = { /* 0 */ {1, scsi_low_msginfunc_cc}, /* 1 */ {2, scsi_low_msginfunc_ext}, /* 2 */ {1, scsi_low_msginfunc_sdp}, /* 3 */ {1, scsi_low_msginfunc_rp}, /* 4 */ {1, scsi_low_msginfunc_disc}, /* 5 */ {1, scsi_low_msginfunc_rejop}, /* 6 */ {1, scsi_low_msginfunc_rejop}, /* 7 */ {1, scsi_low_msginfunc_msg_reject}, /* 8 */ {1, scsi_low_msginfunc_noop}, /* 9 */ {1, scsi_low_msginfunc_parity}, /* a */ {1, scsi_low_msginfunc_lcc}, /* b */ {1, scsi_low_msginfunc_lcc}, /* c */ {1, scsi_low_msginfunc_rejop}, /* d */ {2, scsi_low_msginfunc_rejop}, /* e */ {1, scsi_low_msginfunc_rejop}, /* f */ {1, scsi_low_msginfunc_rejop}, /* 0x10 */ {1, scsi_low_msginfunc_rejop}, /* 0x11 */ {1, scsi_low_msginfunc_rejop}, /* 0x12 */ {1, scsi_low_msginfunc_rejop}, /* 0x13 */ {1, scsi_low_msginfunc_rejop}, /* 0x14 */ {1, scsi_low_msginfunc_rejop}, /* 0x15 */ {1, scsi_low_msginfunc_rejop}, /* 0x16 */ {1, scsi_low_msginfunc_rejop}, /* 0x17 */ {1, scsi_low_msginfunc_rejop}, /* 0x18 */ {1, scsi_low_msginfunc_rejop}, /* 0x19 */ {1, scsi_low_msginfunc_rejop}, /* 0x1a */ {1, scsi_low_msginfunc_rejop}, /* 0x1b */ {1, scsi_low_msginfunc_rejop}, /* 0x1c */ {1, scsi_low_msginfunc_rejop}, /* 0x1d */ {1, scsi_low_msginfunc_rejop}, /* 0x1e */ {1, scsi_low_msginfunc_rejop}, /* 0x1f */ {1, scsi_low_msginfunc_rejop}, /* 0x20 */ {2, scsi_low_msginfunc_simple_qtag}, /* 0x21 */ {2, scsi_low_msginfunc_rejop}, /* 0x22 */ {2, scsi_low_msginfunc_rejop}, /* 0x23 */ {2, scsi_low_msginfunc_i_wide_residue}, /* 0x24 */ {2, scsi_low_msginfunc_rejop}, /* 0x25 */ {2, scsi_low_msginfunc_rejop}, /* 0x26 */ {2, scsi_low_msginfunc_rejop}, /* 0x27 */ {2, scsi_low_msginfunc_rejop}, /* 0x28 */ {2, scsi_low_msginfunc_rejop}, /* 0x29 */ {2, scsi_low_msginfunc_rejop}, /* 0x2a */ {2, scsi_low_msginfunc_rejop}, /* 0x2b */ {2, scsi_low_msginfunc_rejop}, /* 0x2c */ {2, scsi_low_msginfunc_rejop}, /* 0x2d */ {2, scsi_low_msginfunc_rejop}, /* 0x2e */ {2, scsi_low_msginfunc_rejop}, /* 0x2f */ {2, scsi_low_msginfunc_rejop}, /* 0x30 */ {1, scsi_low_msginfunc_rejop} /* default rej op */ }; /************************************************************** * msgout **************************************************************/ static int scsi_low_msgfunc_synch(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; int ptr = ti->ti_msgoutlen; ti->ti_msgoutstr[ptr + 1] = MSG_EXTEND_SYNCHLEN; ti->ti_msgoutstr[ptr + 2] = MSG_EXTEND_SYNCHCODE; ti->ti_msgoutstr[ptr + 3] = ti->ti_maxsynch.period; ti->ti_msgoutstr[ptr + 4] = ti->ti_maxsynch.offset; return MSG_EXTEND_SYNCHLEN + 2; } static int scsi_low_msgfunc_wide(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; int ptr = ti->ti_msgoutlen; ti->ti_msgoutstr[ptr + 1] = MSG_EXTEND_WIDELEN; ti->ti_msgoutstr[ptr + 2] = MSG_EXTEND_WIDECODE; ti->ti_msgoutstr[ptr + 3] = ti->ti_width; return MSG_EXTEND_WIDELEN + 2; } static int scsi_low_msgfunc_identify(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; struct lun_info *li = slp->sl_Lnexus; struct slccb *cb = slp->sl_Qnexus; int ptr = ti->ti_msgoutlen; u_int8_t msg; msg = MSG_IDENTIFY; if (cb == NULL) { slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); SCSI_LOW_INFO(slp, ti, "MSGOUT: nexus unknown"); } else { if (scsi_low_is_disconnect_ok(cb) != 0) msg |= (MSG_IDENTIFY_DISCPRIV | li->li_lun); else msg |= li->li_lun; if (ti->ti_phase == PH_MSGOUT) { (*slp->sl_funcs->scsi_low_establish_lun_nexus) (slp); if (cb->ccb_tag == SCSI_LOW_UNKTAG) { (*slp->sl_funcs->scsi_low_establish_ccb_nexus) (slp); } } } ti->ti_msgoutstr[ptr + 0] = msg; return 1; } static int scsi_low_msgfunc_abort(slp) struct scsi_low_softc *slp; { SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_ABORT); return 1; } static int scsi_low_msgfunc_qabort(slp) struct scsi_low_softc *slp; { SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_TERM); return 1; } static int scsi_low_msgfunc_reset(slp) struct scsi_low_softc *slp; { SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_RESET); return 1; } static int scsi_low_msgfunc_qtag(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; struct slccb *cb = slp->sl_Qnexus; int ptr = ti->ti_msgoutlen; if (cb == NULL || cb->ccb_tag == SCSI_LOW_UNKTAG) { ti->ti_msgoutstr[ptr + 0] = MSG_NOOP; return 1; } else { ti->ti_msgoutstr[ptr + 1] = (u_int8_t) cb->ccb_tag; if (ti->ti_phase == PH_MSGOUT) { (*slp->sl_funcs->scsi_low_establish_ccb_nexus) (slp); } } return 2; } /* * The following functions are called when targets give unexpected * responces in msgin (after msgout). */ static int scsi_low_errfunc_identify(slp, msgflags) struct scsi_low_softc *slp; u_int msgflags; { if (slp->sl_Lnexus != NULL) { slp->sl_Lnexus->li_cfgflags &= ~SCSI_LOW_DISC; scsi_low_calcf_lun(slp->sl_Lnexus); } return 0; } static int scsi_low_errfunc_synch(slp, msgflags) struct scsi_low_softc *slp; u_int msgflags; { struct targ_info *ti = slp->sl_Tnexus; MSGIN_PERIOD(ti) = 0; MSGIN_OFFSET(ti) = 0; scsi_low_synch(slp); return 0; } static int scsi_low_errfunc_wide(slp, msgflags) struct scsi_low_softc *slp; u_int msgflags; { struct targ_info *ti = slp->sl_Tnexus; MSGIN_WIDTHP(ti) = 0; scsi_low_wide(slp); return 0; } static int scsi_low_errfunc_qtag(slp, msgflags) struct scsi_low_softc *slp; u_int msgflags; { if ((msgflags & SCSI_LOW_MSG_REJECT) != 0) { if (slp->sl_Qnexus != NULL) { scsi_low_deactivate_qtag(slp->sl_Qnexus); } if (slp->sl_Lnexus != NULL) { slp->sl_Lnexus->li_cfgflags &= ~SCSI_LOW_QTAG; scsi_low_calcf_lun(slp->sl_Lnexus); } printf("%s: scsi_low: qtag msg rejected\n", slp->sl_xname); } return 0; } int scsi_low_msgout(slp, ti, fl) struct scsi_low_softc *slp; struct targ_info *ti; u_int fl; { struct scsi_low_msgout_data *mdp; int len = 0; #ifdef SCSI_LOW_DIAGNOSTIC if (ti != slp->sl_Tnexus) { scsi_low_print(slp, NULL); panic("scsi_low_msgout: Target nexus inconsistent"); } #endif /* SCSI_LOW_DIAGNOSTIC */ slp->sl_ph_count ++; if (slp->sl_ph_count > SCSI_LOW_MAX_PHCHANGES) { printf("%s: too many phase changes\n", slp->sl_xname); slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); } /* STEP I. * Scsi phase changes. * Previously msgs asserted are accepted by our target or * processed by scsi_low_msgin. * Thus clear all saved informations. */ if ((fl & SCSI_LOW_MSGOUT_INIT) != 0) { ti->ti_omsgflags = 0; ti->ti_emsgflags = 0; } else if (slp->sl_atten == 0) { /* STEP II. * We did not assert attention, however still our target required * msgs. Resend previous msgs. */ ti->ti_msgflags |= ti->ti_omsgflags; ti->ti_omsgflags = 0; #ifdef SCSI_LOW_DIAGNOSTIC printf("%s: scsi_low_msgout: retry msgout\n", slp->sl_xname); #endif /* SCSI_LOW_DIAGNOSTIC */ } /* STEP III. * We have no msgs. send MSG_NOOP (OK?) */ if (scsi_low_is_msgout_continue(ti, 0) == 0) scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_NOOP, 0); /* STEP IV. * Process all msgs */ ti->ti_msgoutlen = 0; slp->sl_clear_atten = 0; mdp = &scsi_low_msgout_data[0]; for ( ; mdp->md_flags != SCSI_LOW_MSG_ALL; mdp ++) { if ((ti->ti_msgflags & mdp->md_flags) != 0) { ti->ti_omsgflags |= mdp->md_flags; ti->ti_msgflags &= ~mdp->md_flags; ti->ti_emsgflags = mdp->md_flags; ti->ti_msgoutstr[ti->ti_msgoutlen] = mdp->md_msg; if (mdp->md_msgfunc != NULL) len = (*mdp->md_msgfunc) (slp); else len = 1; #ifdef SCSI_LOW_DIAGNOSTIC scsi_low_msg_log_write(&ti->ti_log_msgout, &ti->ti_msgoutstr[ti->ti_msgoutlen], len); #endif /* SCSI_LOW_DIAGNOSTIC */ ti->ti_msgoutlen += len; if ((mdp->md_condition & MSG_RELEASE_ATN) != 0) { slp->sl_clear_atten = 1; break; } if ((fl & SCSI_LOW_MSGOUT_UNIFY) == 0 || ti->ti_msgflags == 0) break; if (ti->ti_msgoutlen >= SCSI_LOW_MAX_MSGLEN - 5) break; } } if (scsi_low_is_msgout_continue(ti, 0) == 0) slp->sl_clear_atten = 1; return ti->ti_msgoutlen; } /************************************************************** * msgin **************************************************************/ static int scsi_low_msginfunc_noop(slp) struct scsi_low_softc *slp; { return 0; } static int scsi_low_msginfunc_rejop(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; u_int8_t msg = ti->ti_msgin[0]; printf("%s: MSGIN: msg 0x%x rejected\n", slp->sl_xname, (u_int) msg); scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); return 0; } static int scsi_low_msginfunc_cc(slp) struct scsi_low_softc *slp; { struct lun_info *li; SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_CMDC); /* validate status */ if (slp->sl_Qnexus == NULL) return ENOENT; slp->sl_Qnexus->ccb_sscp.scp_status = slp->sl_scp.scp_status; li = slp->sl_Lnexus; switch (slp->sl_scp.scp_status) { case ST_GOOD: li->li_maxnqio = li->li_maxnexus; break; case ST_CHKCOND: li->li_maxnqio = 0; if (li->li_qflags & SCSI_LOW_QFLAG_CA_QCLEAR) scsi_low_reset_nexus_lun(slp, li, 0); break; case ST_BUSY: li->li_maxnqio = 0; break; case ST_QUEFULL: if (li->li_maxnexus >= li->li_nqio) li->li_maxnexus = li->li_nqio - 1; li->li_maxnqio = li->li_maxnexus; break; case ST_INTERGOOD: case ST_INTERMET: slp->sl_error |= MSGERR; break; default: break; } return 0; } static int scsi_low_msginfunc_lcc(slp) struct scsi_low_softc *slp; { struct targ_info *ti; struct lun_info *li; struct slccb *ncb, *cb; ti = slp->sl_Tnexus; li = slp->sl_Lnexus; if ((cb = slp->sl_Qnexus) == NULL) goto bad; cb->ccb_sscp.scp_status = slp->sl_scp.scp_status; switch (slp->sl_scp.scp_status) { case ST_INTERGOOD: case ST_INTERMET: li->li_maxnqio = li->li_maxnexus; break; default: slp->sl_error |= MSGERR; break; } if ((li->li_flags & SCSI_LOW_LINK) == 0) goto bad; cb->ccb_error |= slp->sl_error; if (cb->ccb_error != 0) goto bad; for (ncb = TAILQ_FIRST(&slp->sl_start); ncb != NULL; ncb = TAILQ_NEXT(ncb, ccb_chain)) { if (ncb->li == li) goto cmd_link_start; } bad: SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_LCTERM); scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); return EIO; cmd_link_start: ncb->ccb_flags &= ~CCB_STARTQ; TAILQ_REMOVE(&slp->sl_start, ncb, ccb_chain); scsi_low_dealloc_qtag(ncb); ncb->ccb_tag = cb->ccb_tag; ncb->ccb_otag = cb->ccb_otag; cb->ccb_tag = SCSI_LOW_UNKTAG; cb->ccb_otag = SCSI_LOW_UNKTAG; if (scsi_low_done(slp, cb) == SCSI_LOW_DONE_RETRY) panic("%s: linked ccb retried", slp->sl_xname); slp->sl_Qnexus = ncb; slp->sl_ph_count = 0; ncb->ccb_error = 0; ncb->ccb_datalen = -1; ncb->ccb_scp.scp_status = ST_UNKNOWN; ncb->ccb_flags &= ~CCB_INTERNAL; scsi_low_init_msgsys(slp, ti); (*slp->sl_osdep_fp->scsi_low_osdep_ccb_setup) (slp, ncb); if (ncb->ccb_tcmax < SCSI_LOW_MIN_TOUT) ncb->ccb_tcmax = SCSI_LOW_MIN_TOUT; ncb->ccb_tc = ncb->ccb_tcmax; /* setup saved scsi data pointer */ ncb->ccb_sscp = ncb->ccb_scp; slp->sl_scp = ncb->ccb_sscp; slp->sl_error = ncb->ccb_error; #ifdef SCSI_LOW_DIAGNOSTIC scsi_low_msg_log_init(&ti->ti_log_msgin); scsi_low_msg_log_init(&ti->ti_log_msgout); #endif /* SCSI_LOW_DIAGNOSTIC */ return EJUSTRETURN; } static int scsi_low_msginfunc_disc(slp) struct scsi_low_softc *slp; { SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_DISC); return 0; } static int scsi_low_msginfunc_sdp(slp) struct scsi_low_softc *slp; { struct slccb *cb = slp->sl_Qnexus; if (cb != NULL) { cb->ccb_sscp.scp_datalen = slp->sl_scp.scp_datalen; cb->ccb_sscp.scp_data = slp->sl_scp.scp_data; } else scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_REJECT, 0); return 0; } static int scsi_low_msginfunc_rp(slp) struct scsi_low_softc *slp; { if (slp->sl_Qnexus != NULL) slp->sl_scp = slp->sl_Qnexus->ccb_sscp; else scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_REJECT, 0); return 0; } static int scsi_low_synch(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; u_int period = 0, offset = 0, speed; u_char *s; int error; if ((MSGIN_PERIOD(ti) >= ti->ti_maxsynch.period && MSGIN_OFFSET(ti) <= ti->ti_maxsynch.offset) || MSGIN_OFFSET(ti) == 0) { if ((offset = MSGIN_OFFSET(ti)) != 0) period = MSGIN_PERIOD(ti); s = offset ? "synchronous" : "async"; } else { /* XXX: * Target seems to be brain damaged. * Force async transfer. */ ti->ti_maxsynch.period = 0; ti->ti_maxsynch.offset = 0; printf("%s: target brain damaged. async transfer\n", slp->sl_xname); return EINVAL; } ti->ti_maxsynch.period = period; ti->ti_maxsynch.offset = offset; error = (*slp->sl_funcs->scsi_low_msg) (slp, ti, SCSI_LOW_MSG_SYNCH); if (error != 0) { /* XXX: * Current period and offset are not acceptable * for our adapter. * The adapter changes max synch and max offset. */ printf("%s: synch neg failed. retry synch msg neg ...\n", slp->sl_xname); return error; } ti->ti_osynch = ti->ti_maxsynch; if (offset > 0) { ti->ti_setup_msg_done |= SCSI_LOW_MSG_SYNCH; } /* inform data */ if ((slp->sl_show_result & SHOW_SYNCH_NEG) != 0) { #ifdef SCSI_LOW_NEGOTIATE_BEFORE_SENSE struct slccb *cb = slp->sl_Qnexus; if (cb != NULL && (cb->ccb_flags & CCB_SENSE) != 0) return 0; #endif /* SCSI_LOW_NEGOTIATE_BEFORE_SENSE */ printf("%s(%d:*): <%s> offset %d period %dns ", slp->sl_xname, ti->ti_id, s, offset, period * 4); if (period != 0) { speed = 1000 * 10 / (period * 4); printf("%d.%d M/s", speed / 10, speed % 10); } printf("\n"); } return 0; } static int scsi_low_wide(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; int error; ti->ti_width = MSGIN_WIDTHP(ti); error = (*slp->sl_funcs->scsi_low_msg) (slp, ti, SCSI_LOW_MSG_WIDE); if (error != 0) { /* XXX: * Current width is not acceptable for our adapter. * The adapter changes max width. */ printf("%s: wide neg failed. retry wide msg neg ...\n", slp->sl_xname); return error; } ti->ti_owidth = ti->ti_width; if (ti->ti_width > SCSI_LOW_BUS_WIDTH_8) { ti->ti_setup_msg_done |= (SCSI_LOW_MSG_SYNCH | SCSI_LOW_MSG_WIDE); } /* inform data */ if ((slp->sl_show_result & SHOW_WIDE_NEG) != 0) { #ifdef SCSI_LOW_NEGOTIATE_BEFORE_SENSE struct slccb *cb = slp->sl_Qnexus; if (cb != NULL && (cb->ccb_flags & CCB_SENSE) != 0) return 0; #endif /* SCSI_LOW_NEGOTIATE_BEFORE_SENSE */ printf("%s(%d:*): transfer width %d bits\n", slp->sl_xname, ti->ti_id, 1 << (3 + ti->ti_width)); } return 0; } static int scsi_low_msginfunc_simple_qtag(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; scsi_low_tag_t etag = (scsi_low_tag_t) ti->ti_msgin[1]; if (slp->sl_Qnexus != NULL) { if (slp->sl_Qnexus->ccb_tag != etag) { slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); SCSI_LOW_INFO(slp, ti, "MSGIN: qtag mismatch"); } } else if (scsi_low_establish_ccb(ti, slp->sl_Lnexus, etag) == NULL) { #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_NEXUS_CHECK, ti->ti_id)) return 0; #endif /* SCSI_LOW_DEBUG */ slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT_QTAG, 0); SCSI_LOW_INFO(slp, ti, "MSGIN: taged ccb not found"); } return 0; } static int scsi_low_msginfunc_i_wide_residue(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; struct slccb *cb = slp->sl_Qnexus; int res = (int) ti->ti_msgin[1]; if (cb == NULL || res <= 0 || (ti->ti_width == SCSI_LOW_BUS_WIDTH_16 && res > 1) || (ti->ti_width == SCSI_LOW_BUS_WIDTH_32 && res > 3)) return EINVAL; if (slp->sl_scp.scp_datalen + res > cb->ccb_scp.scp_datalen) return EINVAL; slp->sl_scp.scp_datalen += res; slp->sl_scp.scp_data -= res; scsi_low_data_finish(slp); return 0; } static int scsi_low_msginfunc_ext(slp) struct scsi_low_softc *slp; { struct slccb *cb = slp->sl_Qnexus; struct lun_info *li = slp->sl_Lnexus; struct targ_info *ti = slp->sl_Tnexus; int count, retry; u_int32_t *ptr; if (ti->ti_msginptr == 2) { ti->ti_msginlen = ti->ti_msgin[1] + 2; return 0; } switch (MKMSG_EXTEND(ti->ti_msgin[1], ti->ti_msgin[2])) { case MKMSG_EXTEND(MSG_EXTEND_MDPLEN, MSG_EXTEND_MDPCODE): if (cb == NULL) break; ptr = (u_int32_t *)(&ti->ti_msgin[3]); count = (int) htonl((long) (*ptr)); if(slp->sl_scp.scp_datalen - count < 0 || slp->sl_scp.scp_datalen - count > cb->ccb_scp.scp_datalen) break; slp->sl_scp.scp_datalen -= count; slp->sl_scp.scp_data += count; return 0; case MKMSG_EXTEND(MSG_EXTEND_SYNCHLEN, MSG_EXTEND_SYNCHCODE): if (li == NULL) break; retry = scsi_low_synch(slp); if (retry != 0 || (ti->ti_emsgflags & SCSI_LOW_MSG_SYNCH) == 0) scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_SYNCH, 0); #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_ATTEN_CHECK, ti->ti_id)) { scsi_low_test_atten(slp, ti, SCSI_LOW_MSG_SYNCH); } #endif /* SCSI_LOW_DEBUG */ return 0; case MKMSG_EXTEND(MSG_EXTEND_WIDELEN, MSG_EXTEND_WIDECODE): if (li == NULL) break; retry = scsi_low_wide(slp); if (retry != 0 || (ti->ti_emsgflags & SCSI_LOW_MSG_WIDE) == 0) scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_WIDE, 0); return 0; default: break; } scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); return EINVAL; } static int scsi_low_msginfunc_parity(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; /* only I -> T, invalid! */ scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); return 0; } static int scsi_low_msginfunc_msg_reject(slp) struct scsi_low_softc *slp; { struct targ_info *ti = slp->sl_Tnexus; struct scsi_low_msgout_data *mdp; u_int msgflags; if (ti->ti_emsgflags != 0) { printf("%s: msg flags [0x%x] rejected\n", slp->sl_xname, ti->ti_emsgflags); msgflags = SCSI_LOW_MSG_REJECT; mdp = &scsi_low_msgout_data[0]; for ( ; mdp->md_flags != SCSI_LOW_MSG_ALL; mdp ++) { if ((ti->ti_emsgflags & mdp->md_flags) != 0) { ti->ti_emsgflags &= ~mdp->md_flags; if (mdp->md_errfunc != NULL) (*mdp->md_errfunc) (slp, msgflags); break; } } return 0; } else { SCSI_LOW_INFO(slp, ti, "MSGIN: rejected msg not found"); slp->sl_error |= MSGERR; } return EINVAL; } int scsi_low_msgin(slp, ti, c) struct scsi_low_softc *slp; struct targ_info *ti; u_int c; { struct scsi_low_msgin_data *sdp; struct lun_info *li; u_int8_t msg; #ifdef SCSI_LOW_DIAGNOSTIC if (ti != slp->sl_Tnexus) { scsi_low_print(slp, NULL); panic("scsi_low_msgin: Target nexus inconsistent"); } #endif /* SCSI_LOW_DIAGNOSTIC */ /* * Phase changes, clear the pointer. */ if (ti->ti_ophase != ti->ti_phase) { MSGINPTR_CLR(ti); ti->ti_msgin_parity_error = 0; slp->sl_ph_count ++; if (slp->sl_ph_count > SCSI_LOW_MAX_PHCHANGES) { printf("%s: too many phase changes\n", slp->sl_xname); slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); } } /* * Store a current messages byte into buffer and * wait for the completion of the current msg. */ ti->ti_msgin[ti->ti_msginptr ++] = (u_int8_t) c; if (ti->ti_msginptr >= SCSI_LOW_MAX_MSGLEN) { ti->ti_msginptr = SCSI_LOW_MAX_MSGLEN - 1; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); } /* * Check parity errors. */ if ((c & SCSI_LOW_DATA_PE) != 0) { ti->ti_msgin_parity_error ++; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_PARITY, 0); goto out; } if (ti->ti_msgin_parity_error != 0) goto out; /* * Calculate messages length. */ msg = ti->ti_msgin[0]; if (msg < MSGIN_DATA_LAST) sdp = &scsi_low_msgin_data[msg]; else sdp = &scsi_low_msgin_data[MSGIN_DATA_LAST]; if (ti->ti_msginlen == 0) { ti->ti_msginlen = sdp->md_len; } /* * Check comletion. */ if (ti->ti_msginptr < ti->ti_msginlen) return EJUSTRETURN; /* * Do process. */ if ((msg & MSG_IDENTIFY) == 0) { if (((*sdp->md_msgfunc) (slp)) == EJUSTRETURN) return EJUSTRETURN; } else { li = slp->sl_Lnexus; if (li == NULL) { li = scsi_low_alloc_li(ti, MSGCMD_LUN(msg), 0); if (li == NULL) goto badlun; slp->sl_Lnexus = li; (*slp->sl_funcs->scsi_low_establish_lun_nexus) (slp); } else { if (MSGCMD_LUN(msg) != li->li_lun) goto badlun; } if (slp->sl_Qnexus == NULL && li->li_nqio == 0) { if (!scsi_low_establish_ccb(ti, li, SCSI_LOW_UNKTAG)) { #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_NEXUS_CHECK, ti->ti_id) != 0) { goto out; } #endif /* SCSI_LOW_DEBUG */ goto badlun; } } } goto out; /* * Msg process completed, reset msgin pointer and assert ATN if desired. */ badlun: slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); SCSI_LOW_INFO(slp, ti, "MSGIN: identify wrong"); out: if (ti->ti_msginptr < ti->ti_msginlen) return EJUSTRETURN; #ifdef SCSI_LOW_DIAGNOSTIC scsi_low_msg_log_write(&ti->ti_log_msgin, &ti->ti_msgin[0], ti->ti_msginlen); #endif /* SCSI_LOW_DIAGNOSTIC */ MSGINPTR_CLR(ti); return 0; } /********************************************************** * disconnect **********************************************************/ int scsi_low_disconnected(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { struct slccb *cb = slp->sl_Qnexus; /* check phase completion */ switch (slp->sl_msgphase) { case MSGPH_RESET: scsi_low_statusin(slp, slp->sl_Tnexus, ST_GOOD); scsi_low_msginfunc_cc(slp); scsi_low_reset_nexus_target(slp, slp->sl_Tnexus, 0); goto io_resume; case MSGPH_ABORT: scsi_low_statusin(slp, slp->sl_Tnexus, ST_GOOD); scsi_low_msginfunc_cc(slp); scsi_low_reset_nexus_lun(slp, slp->sl_Lnexus, 0); goto io_resume; case MSGPH_TERM: scsi_low_statusin(slp, slp->sl_Tnexus, ST_GOOD); scsi_low_msginfunc_cc(slp); goto io_resume; case MSGPH_DISC: if (cb != NULL) { struct lun_info *li; li = cb->li; TAILQ_INSERT_TAIL(&li->li_discq, cb, ccb_chain); cb->ccb_flags |= CCB_DISCQ; cb->ccb_error |= slp->sl_error; li->li_disc ++; ti->ti_disc ++; slp->sl_disc ++; } #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_disconnected ++; #endif /* SCSI_LOW_STATICS */ #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_DISC, ti->ti_id) != 0) { printf("## SCSI_LOW_DISCONNECTED ===============\n"); scsi_low_print(slp, NULL); } #endif /* SCSI_LOW_DEBUG */ break; case MSGPH_NULL: slp->sl_error |= FATALIO; if (ti->ti_phase == PH_SELSTART) slp->sl_error |= SELTIMEOUTIO; else slp->sl_error |= UBFERR; /* fall through */ case MSGPH_LCTERM: case MSGPH_CMDC: io_resume: if (cb == NULL) break; #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_TEST_GO(SCSI_LOW_ATTEN_CHECK, ti->ti_id)) { if (cb->ccb_omsgoutflag == SCSI_LOW_MSG_NOOP && (cb->ccb_msgoutflag != 0 || (ti->ti_msgflags & SCSI_LOW_MSG_NOOP))) { scsi_low_info(slp, ti, "ATTEN CHECK FAILED"); } } #endif /* SCSI_LOW_DEBUG */ cb->ccb_error |= slp->sl_error; if (scsi_low_done(slp, cb) == SCSI_LOW_DONE_RETRY) { cb->ccb_flags |= CCB_STARTQ; TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); } break; } scsi_low_bus_release(slp, ti); scsi_low_start(slp); return 1; } /********************************************************** * TAG operations **********************************************************/ static int scsi_low_alloc_qtag(cb) struct slccb *cb; { struct lun_info *li = cb->li; scsi_low_tag_t etag; if (cb->ccb_otag != SCSI_LOW_UNKTAG) return 0; #ifndef SCSI_LOW_ALT_QTAG_ALLOCATE etag = ffs(li->li_qtagbits); if (etag == 0) return ENOSPC; li->li_qtagbits &= ~(1 << (etag - 1)); cb->ccb_otag = etag; return 0; #else /* SCSI_LOW_ALT_QTAG_ALLOCATE */ for (etag = li->li_qd ; li->li_qd < SCSI_LOW_MAXNEXUS; li->li_qd ++) if (li->li_qtagarray[li->li_qd] == 0) goto found; for (li->li_qd = 0; li->li_qd < etag; li->li_qd ++) if (li->li_qtagarray[li->li_qd] == 0) goto found; return ENOSPC; found: li->li_qtagarray[li->li_qd] ++; cb->ccb_otag = (li->li_qd ++); return 0; #endif /* SCSI_LOW_ALT_QTAG_ALLOCATE */ } static int scsi_low_dealloc_qtag(cb) struct slccb *cb; { struct lun_info *li = cb->li; scsi_low_tag_t etag; if (cb->ccb_otag == SCSI_LOW_UNKTAG) return 0; #ifndef SCSI_LOW_ALT_QTAG_ALLOCATE etag = cb->ccb_otag - 1; #ifdef SCSI_LOW_DIAGNOSTIC if (etag >= sizeof(li->li_qtagbits) * NBBY) panic("scsi_low_dealloc_tag: illegal tag"); #endif /* SCSI_LOW_DIAGNOSTIC */ li->li_qtagbits |= (1 << etag); #else /* SCSI_LOW_ALT_QTAG_ALLOCATE */ etag = cb->ccb_otag; #ifdef SCSI_LOW_DIAGNOSTIC if (etag >= SCSI_LOW_MAXNEXUS) panic("scsi_low_dealloc_tag: illegal tag"); #endif /* SCSI_LOW_DIAGNOSTIC */ li->li_qtagarray[etag] --; #endif /* SCSI_LOW_ALT_QTAG_ALLOCATE */ cb->ccb_otag = SCSI_LOW_UNKTAG; return 0; } static struct slccb * scsi_low_revoke_ccb(slp, cb, fdone) struct scsi_low_softc *slp; struct slccb *cb; int fdone; { struct targ_info *ti = cb->ti; struct lun_info *li = cb->li; #ifdef SCSI_LOW_DIAGNOSTIC if ((cb->ccb_flags & (CCB_STARTQ | CCB_DISCQ)) == (CCB_STARTQ | CCB_DISCQ)) { panic("%s: ccb in both queue", slp->sl_xname); } #endif /* SCSI_LOW_DIAGNOSTIC */ if ((cb->ccb_flags & CCB_STARTQ) != 0) { TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); } if ((cb->ccb_flags & CCB_DISCQ) != 0) { TAILQ_REMOVE(&li->li_discq, cb, ccb_chain); li->li_disc --; ti->ti_disc --; slp->sl_disc --; } cb->ccb_flags &= ~(CCB_STARTQ | CCB_DISCQ | CCB_SENSE | CCB_CLEARQ | CCB_INTERNAL); if (fdone != 0 && (cb->ccb_rcnt ++ >= slp->sl_max_retry || (cb->ccb_flags & CCB_NORETRY) != 0)) { cb->ccb_error |= FATALIO; cb->ccb_flags &= ~CCB_AUTOSENSE; if (scsi_low_done(slp, cb) != SCSI_LOW_DONE_COMPLETE) panic("%s: done ccb retried", slp->sl_xname); return NULL; } else { cb->ccb_error |= PENDINGIO; scsi_low_deactivate_qtag(cb); scsi_low_ccb_message_retry(cb); cb->ccb_tc = cb->ccb_tcmax = SCSI_LOW_MIN_TOUT; return cb; } } static void scsi_low_reset_nexus_lun(slp, li, fdone) struct scsi_low_softc *slp; struct lun_info *li; int fdone; { struct slccb *cb, *ncb, *ecb; if (li == NULL) return; ecb = NULL; for (cb = TAILQ_FIRST(&li->li_discq); cb != NULL; cb = ncb) { ncb = TAILQ_NEXT(cb, ccb_chain); cb = scsi_low_revoke_ccb(slp, cb, fdone); if (cb != NULL) { /* * presumely keep ordering of io */ cb->ccb_flags |= CCB_STARTQ; if (ecb == NULL) { TAILQ_INSERT_HEAD(&slp->sl_start,\ cb, ccb_chain); } else { TAILQ_INSERT_AFTER(&slp->sl_start,\ ecb, cb, ccb_chain); } ecb = cb; } } } /************************************************************** * Qurik setup **************************************************************/ static void scsi_low_calcf_lun(li) struct lun_info *li; { struct targ_info *ti = li->li_ti; struct scsi_low_softc *slp = ti->ti_sc; u_int cfgflags, diskflags; if (li->li_flags_valid == SCSI_LOW_LUN_FLAGS_ALL_VALID) cfgflags = li->li_cfgflags; else cfgflags = 0; diskflags = li->li_diskflags & li->li_quirks; /* disconnect */ li->li_flags &= ~SCSI_LOW_DISC; if ((slp->sl_cfgflags & CFG_NODISC) == 0 && (diskflags & SCSI_LOW_DISK_DISC) != 0 && (cfgflags & SCSI_LOW_DISC) != 0) li->li_flags |= SCSI_LOW_DISC; /* parity */ li->li_flags |= SCSI_LOW_NOPARITY; if ((slp->sl_cfgflags & CFG_NOPARITY) == 0 && (diskflags & SCSI_LOW_DISK_PARITY) != 0 && (cfgflags & SCSI_LOW_NOPARITY) == 0) li->li_flags &= ~SCSI_LOW_NOPARITY; /* qtag */ if ((slp->sl_cfgflags & CFG_NOQTAG) == 0 && (cfgflags & SCSI_LOW_QTAG) != 0 && (diskflags & SCSI_LOW_DISK_QTAG) != 0) { li->li_flags |= SCSI_LOW_QTAG; li->li_maxnexus = SCSI_LOW_MAXNEXUS; li->li_maxnqio = li->li_maxnexus; } else { li->li_flags &= ~SCSI_LOW_QTAG; li->li_maxnexus = 0; li->li_maxnqio = li->li_maxnexus; } /* cmd link */ li->li_flags &= ~SCSI_LOW_LINK; if ((cfgflags & SCSI_LOW_LINK) != 0 && (diskflags & SCSI_LOW_DISK_LINK) != 0) li->li_flags |= SCSI_LOW_LINK; /* compatible flags */ li->li_flags &= ~SCSI_LOW_SYNC; if (ti->ti_maxsynch.offset > 0) li->li_flags |= SCSI_LOW_SYNC; #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_CALCF, ti->ti_id) != 0) { scsi_low_calcf_show(li); } #endif /* SCSI_LOW_DEBUG */ } static void scsi_low_calcf_target(ti) struct targ_info *ti; { struct scsi_low_softc *slp = ti->ti_sc; u_int offset, period, diskflags; diskflags = ti->ti_diskflags & ti->ti_quirks; /* synch */ if ((slp->sl_cfgflags & CFG_ASYNC) == 0 && (diskflags & SCSI_LOW_DISK_SYNC) != 0) { offset = ti->ti_maxsynch.offset; period = ti->ti_maxsynch.period; if (offset == 0 || period == 0) offset = period = 0; } else { offset = period = 0; } ti->ti_maxsynch.offset = offset; ti->ti_maxsynch.period = period; /* wide */ if ((diskflags & SCSI_LOW_DISK_WIDE_32) == 0 && ti->ti_width > SCSI_LOW_BUS_WIDTH_16) ti->ti_width = SCSI_LOW_BUS_WIDTH_16; if ((diskflags & SCSI_LOW_DISK_WIDE_16) == 0 && ti->ti_width > SCSI_LOW_BUS_WIDTH_8) ti->ti_width = SCSI_LOW_BUS_WIDTH_8; if (ti->ti_flags_valid == SCSI_LOW_TARG_FLAGS_ALL_VALID) { if (ti->ti_maxsynch.offset != ti->ti_osynch.offset || ti->ti_maxsynch.period != ti->ti_osynch.period) ti->ti_setup_msg |= SCSI_LOW_MSG_SYNCH; if (ti->ti_width != ti->ti_owidth) ti->ti_setup_msg |= (SCSI_LOW_MSG_WIDE | SCSI_LOW_MSG_SYNCH); ti->ti_osynch = ti->ti_maxsynch; ti->ti_owidth = ti->ti_width; } #ifdef SCSI_LOW_DEBUG if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_CALCF, ti->ti_id) != 0) { printf("%s(%d:*): max period(%dns) offset(%d) width(%d)\n", slp->sl_xname, ti->ti_id, ti->ti_maxsynch.period * 4, ti->ti_maxsynch.offset, ti->ti_width); } #endif /* SCSI_LOW_DEBUG */ } static void scsi_low_calcf_show(li) struct lun_info *li; { struct targ_info *ti = li->li_ti; struct scsi_low_softc *slp = ti->ti_sc; printf("%s(%d:%d): period(%d ns) offset(%d) width(%d) flags 0x%b\n", slp->sl_xname, ti->ti_id, li->li_lun, ti->ti_maxsynch.period * 4, ti->ti_maxsynch.offset, ti->ti_width, li->li_flags, SCSI_LOW_BITS); } #ifdef SCSI_LOW_START_UP_CHECK /************************************************************** * scsi world start up **************************************************************/ static int scsi_low_poll(struct scsi_low_softc *, struct slccb *); static int scsi_low_start_up(slp) struct scsi_low_softc *slp; { struct targ_info *ti; struct lun_info *li; struct slccb *cb; int target, lun; printf("%s: scsi_low: probing all devices ....\n", slp->sl_xname); for (target = 0; target < slp->sl_ntargs; target ++) { if (target == slp->sl_hostid) { if ((slp->sl_show_result & SHOW_PROBE_RES) != 0) { printf("%s: scsi_low: target %d (host card)\n", slp->sl_xname, target); } continue; } if ((slp->sl_show_result & SHOW_PROBE_RES) != 0) { printf("%s: scsi_low: target %d lun ", slp->sl_xname, target); } ti = slp->sl_ti[target]; for (lun = 0; lun < slp->sl_nluns; lun ++) { if ((cb = SCSI_LOW_ALLOC_CCB(1)) == NULL) break; cb->osdep = NULL; cb->bp = NULL; li = scsi_low_alloc_li(ti, lun, 1); scsi_low_enqueue(slp, ti, li, cb, CCB_AUTOSENSE | CCB_POLLED, 0); scsi_low_poll(slp, cb); if (li->li_state != SCSI_LOW_LUN_OK) break; if ((slp->sl_show_result & SHOW_PROBE_RES) != 0) { printf("%d ", lun); } } if ((slp->sl_show_result & SHOW_PROBE_RES) != 0) { printf("\n"); } } return 0; } static int scsi_low_poll(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { int tcount; tcount = 0; while (slp->sl_nio > 0) { SCSI_LOW_DELAY((1000 * 1000) / SCSI_LOW_POLL_HZ); (*slp->sl_funcs->scsi_low_poll) (slp); if (tcount ++ < SCSI_LOW_POLL_HZ / SCSI_LOW_TIMEOUT_HZ) continue; tcount = 0; scsi_low_timeout_check(slp); } return 0; } #endif /* SCSI_LOW_START_UP_CHECK */ /********************************************************** * DEBUG SECTION **********************************************************/ #ifdef SCSI_LOW_DEBUG static void scsi_low_test_abort(slp, ti, li) struct scsi_low_softc *slp; struct targ_info *ti; struct lun_info *li; { struct slccb *acb; if (li->li_disc > 1) { acb = TAILQ_FIRST(&li->li_discq); if (scsi_low_abort_ccb(slp, acb) == 0) { printf("%s: aborting ccb(0x%lx) start\n", slp->sl_xname, (u_long) acb); } } } static void scsi_low_test_atten(slp, ti, msg) struct scsi_low_softc *slp; struct targ_info *ti; u_int msg; { if (slp->sl_ph_count < SCSI_LOW_MAX_ATTEN_CHECK) scsi_low_assert_msg(slp, ti, msg, 0); else printf("%s: atten check OK\n", slp->sl_xname); } static void scsi_low_test_cmdlnk(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { #define SCSI_LOW_CMDLNK_NOK (CCB_INTERNAL | CCB_SENSE | CCB_CLEARQ) if ((cb->ccb_flags & SCSI_LOW_CMDLNK_NOK) != 0) return; memcpy(cb->ccb_scsi_cmd, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen); cb->ccb_scsi_cmd[slp->sl_scp.scp_cmdlen - 1] |= 1; slp->sl_scp.scp_cmd = cb->ccb_scsi_cmd; } #endif /* SCSI_LOW_DEBUG */ /* static */ void scsi_low_info(slp, ti, s) struct scsi_low_softc *slp; struct targ_info *ti; u_char *s; { if (slp == NULL) slp = LIST_FIRST(&sl_tab); if (s == NULL) s = "no message"; printf(">>>>> SCSI_LOW_INFO(0x%lx): %s\n", (u_long) slp->sl_Tnexus, s); if (ti == NULL) { for (ti = TAILQ_FIRST(&slp->sl_titab); ti != NULL; ti = TAILQ_NEXT(ti, ti_chain)) { scsi_low_print(slp, ti); } } else { scsi_low_print(slp, ti); } } static u_char *phase[] = { "FREE", "ARBSTART", "SELSTART", "SELECTED", "CMDOUT", "DATA", "MSGIN", "MSGOUT", "STATIN", "DISC", "RESEL" }; void scsi_low_print(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { struct lun_info *li; struct slccb *cb; struct sc_p *sp; if (ti == NULL || ti == slp->sl_Tnexus) { ti = slp->sl_Tnexus; li = slp->sl_Lnexus; cb = slp->sl_Qnexus; } else { li = LIST_FIRST(&ti->ti_litab); cb = TAILQ_FIRST(&li->li_discq); } sp = &slp->sl_scp; printf("%s: === NEXUS T(0x%lx) L(0x%lx) Q(0x%lx) NIO(%d) ===\n", slp->sl_xname, (u_long) ti, (u_long) li, (u_long) cb, slp->sl_nio); /* target stat */ if (ti != NULL) { u_int flags = 0, maxnqio = 0, nqio = 0; int lun = -1; if (li != NULL) { lun = li->li_lun; flags = li->li_flags; maxnqio = li->li_maxnqio; nqio = li->li_nqio; } printf("%s(%d:%d) ph<%s> => ph<%s> DISC(%d) QIO(%d:%d)\n", slp->sl_xname, ti->ti_id, lun, phase[(int) ti->ti_ophase], phase[(int) ti->ti_phase], ti->ti_disc, nqio, maxnqio); if (cb != NULL) { printf("CCB: cmd[0] 0x%x clen 0x%x dlen 0x%x<0x%x stat 0x%x err %b\n", (u_int) cb->ccb_scp.scp_cmd[0], cb->ccb_scp.scp_cmdlen, cb->ccb_datalen, cb->ccb_scp.scp_datalen, (u_int) cb->ccb_sscp.scp_status, cb->ccb_error, SCSI_LOW_ERRORBITS); } printf("MSGIN: ptr(%x) [%x][%x][%x][%x][%x] attention: %d\n", (u_int) (ti->ti_msginptr), (u_int) (ti->ti_msgin[0]), (u_int) (ti->ti_msgin[1]), (u_int) (ti->ti_msgin[2]), (u_int) (ti->ti_msgin[3]), (u_int) (ti->ti_msgin[4]), slp->sl_atten); printf("MSGOUT: msgflags 0x%x [%x][%x][%x][%x][%x] msgoutlen %d C_FLAGS: %b\n", (u_int) ti->ti_msgflags, (u_int) (ti->ti_msgoutstr[0]), (u_int) (ti->ti_msgoutstr[1]), (u_int) (ti->ti_msgoutstr[2]), (u_int) (ti->ti_msgoutstr[3]), (u_int) (ti->ti_msgoutstr[4]), ti->ti_msgoutlen, flags, SCSI_LOW_BITS); #ifdef SCSI_LOW_DIAGNOSTIC scsi_low_msg_log_show(&ti->ti_log_msgin, "MIN LOG ", 2); scsi_low_msg_log_show(&ti->ti_log_msgout, "MOUT LOG", 2); #endif /* SCSI_LOW_DIAGNOSTIC */ } printf("SCB: daddr 0x%lx dlen 0x%x stat 0x%x err %b\n", (u_long) sp->scp_data, sp->scp_datalen, (u_int) sp->scp_status, slp->sl_error, SCSI_LOW_ERRORBITS); } Index: head/sys/dev/advansys/advansys.c =================================================================== --- head/sys/dev/advansys/advansys.c (revision 116350) +++ head/sys/dev/advansys/advansys.c (revision 116351) @@ -1,1440 +1,1424 @@ /* * Generic driver for the Advanced Systems Inc. SCSI controllers * Product specific probe and attach routines can be found in: * * i386/isa/adv_isa.c ABP5140, ABP542, ABP5150, ABP842, ABP852 * i386/eisa/adv_eisa.c ABP742, ABP752 * pci/adv_pci.c ABP920, ABP930, ABP930U, ABP930UA, ABP940, ABP940U, * ABP940UA, ABP950, ABP960, ABP960U, ABP960UA, * ABP970, ABP970U * * Copyright (c) 1996-2000 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Ported from: * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * * Copyright (c) 1995-1997 Advanced System Products, Inc. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that redistributions of source * code retain the above copyright notice and this comment without * modification. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void adv_action(struct cam_sim *sim, union ccb *ccb); static void adv_execute_ccb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error); static void adv_poll(struct cam_sim *sim); static void adv_run_doneq(struct adv_softc *adv); static struct adv_ccb_info * adv_alloc_ccb_info(struct adv_softc *adv); static void adv_destroy_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo); static __inline struct adv_ccb_info * adv_get_ccb_info(struct adv_softc *adv); static __inline void adv_free_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo); static __inline void adv_set_state(struct adv_softc *adv, adv_state state); static __inline void adv_clear_state(struct adv_softc *adv, union ccb* ccb); static void adv_clear_state_really(struct adv_softc *adv, union ccb* ccb); static __inline struct adv_ccb_info * adv_get_ccb_info(struct adv_softc *adv) { struct adv_ccb_info *cinfo; int opri; opri = splcam(); if ((cinfo = SLIST_FIRST(&adv->free_ccb_infos)) != NULL) { SLIST_REMOVE_HEAD(&adv->free_ccb_infos, links); } else { cinfo = adv_alloc_ccb_info(adv); } splx(opri); return (cinfo); } static __inline void adv_free_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo) { int opri; opri = splcam(); cinfo->state = ACCB_FREE; SLIST_INSERT_HEAD(&adv->free_ccb_infos, cinfo, links); splx(opri); } static __inline void adv_set_state(struct adv_softc *adv, adv_state state) { if (adv->state == 0) xpt_freeze_simq(adv->sim, /*count*/1); adv->state |= state; } static __inline void adv_clear_state(struct adv_softc *adv, union ccb* ccb) { if (adv->state != 0) adv_clear_state_really(adv, ccb); } static void adv_clear_state_really(struct adv_softc *adv, union ccb* ccb) { if ((adv->state & ADV_BUSDMA_BLOCK_CLEARED) != 0) adv->state &= ~(ADV_BUSDMA_BLOCK_CLEARED|ADV_BUSDMA_BLOCK); if ((adv->state & ADV_RESOURCE_SHORTAGE) != 0) { int openings; openings = adv->max_openings - adv->cur_active - ADV_MIN_FREE_Q; if (openings >= adv->openings_needed) { adv->state &= ~ADV_RESOURCE_SHORTAGE; adv->openings_needed = 0; } } if ((adv->state & ADV_IN_TIMEOUT) != 0) { struct adv_ccb_info *cinfo; cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr; if ((cinfo->state & ACCB_RECOVERY_CCB) != 0) { struct ccb_hdr *ccb_h; /* * We now traverse our list of pending CCBs * and reinstate their timeouts. */ ccb_h = LIST_FIRST(&adv->pending_ccbs); while (ccb_h != NULL) { ccb_h->timeout_ch = timeout(adv_timeout, (caddr_t)ccb_h, (ccb_h->timeout * hz) / 1000); ccb_h = LIST_NEXT(ccb_h, sim_links.le); } adv->state &= ~ADV_IN_TIMEOUT; printf("%s: No longer in timeout\n", adv_name(adv)); } } if (adv->state == 0) ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } void adv_map(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t* physaddr; physaddr = (bus_addr_t*)arg; *physaddr = segs->ds_addr; } char * adv_name(struct adv_softc *adv) { static char name[10]; snprintf(name, sizeof(name), "adv%d", adv->unit); return (name); } static void adv_action(struct cam_sim *sim, union ccb *ccb) { struct adv_softc *adv; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("adv_action\n")); adv = (struct adv_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct ccb_hdr *ccb_h; struct ccb_scsiio *csio; struct adv_ccb_info *cinfo; ccb_h = &ccb->ccb_h; csio = &ccb->csio; cinfo = adv_get_ccb_info(adv); if (cinfo == NULL) panic("XXX Handle CCB info error!!!"); ccb_h->ccb_cinfo_ptr = cinfo; cinfo->ccb = ccb; /* Only use S/G if there is a transfer */ if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer */ if ((ccb_h->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(adv->buffer_dmat, cinfo->dmamap, csio->data_ptr, csio->dxfer_len, adv_execute_ccb, csio, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ adv_set_state(adv, ADV_BUSDMA_BLOCK); } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; adv_execute_ccb(csio, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccb_h->flags & CAM_DATA_PHYS) != 0) panic("adv_setup_data - Physical " "segment pointers unsupported"); if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0) panic("adv_setup_data - Virtual " "segment addresses unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; adv_execute_ccb(ccb, segs, csio->sglist_cnt, 0); } } else { adv_execute_ccb(ccb, NULL, 0, 0); } break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; target_bit_vector targ_mask; struct adv_transinfo *tconf; u_int update_type; int s; cts = &ccb->cts; targ_mask = ADV_TID_TO_TARGET_MASK(cts->ccb_h.target_id); update_type = 0; /* * The user must specify which type of settings he wishes * to change. */ if (((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) && ((cts->flags & CCB_TRANS_USER_SETTINGS) == 0)) { tconf = &adv->tinfo[cts->ccb_h.target_id].current; update_type |= ADV_TRANS_GOAL; } else if (((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) && ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) == 0)) { tconf = &adv->tinfo[cts->ccb_h.target_id].user; update_type |= ADV_TRANS_USER; } else { ccb->ccb_h.status = CAM_REQ_INVALID; break; } s = splcam(); if ((update_type & ADV_TRANS_GOAL) != 0) { if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) adv->disc_enable |= targ_mask; else adv->disc_enable &= ~targ_mask; adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable); } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) adv->cmd_qng_enabled |= targ_mask; else adv->cmd_qng_enabled &= ~targ_mask; } } if ((update_type & ADV_TRANS_USER) != 0) { if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) adv->user_disc_enable |= targ_mask; else adv->user_disc_enable &= ~targ_mask; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) adv->user_cmd_qng_enabled |= targ_mask; else adv->user_cmd_qng_enabled &= ~targ_mask; } } /* * If the user specifies either the sync rate, or offset, * but not both, the unspecified parameter defaults to its * current value in transfer negotiations. */ if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { /* * If the user provided a sync rate but no offset, * use the current offset. */ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) cts->sync_offset = tconf->offset; /* * If the user provided an offset but no sync rate, * use the current sync rate. */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) cts->sync_period = tconf->period; adv_period_offset_to_sdtr(adv, &cts->sync_period, &cts->sync_offset, cts->ccb_h.target_id); adv_set_syncrate(adv, /*struct cam_path */NULL, cts->ccb_h.target_id, cts->sync_period, cts->sync_offset, update_type); } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; struct adv_transinfo *tconf; target_bit_vector target_mask; int s; cts = &ccb->cts; target_mask = ADV_TID_TO_TARGET_MASK(cts->ccb_h.target_id); cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); s = splcam(); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { tconf = &adv->tinfo[cts->ccb_h.target_id].current; if ((adv->disc_enable & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adv->cmd_qng_enabled & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } else { tconf = &adv->tinfo[cts->ccb_h.target_id].user; if ((adv->user_disc_enable & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adv->user_cmd_qng_enabled & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } cts->sync_period = tconf->period; cts->sync_offset = tconf->offset; splx(s); cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; int extended; - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); extended = (adv->control & ADV_CNTL_BIOS_GT_1GB) != 0; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, extended); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int s; s = splcam(); adv_stop_execution(adv); adv_reset_bus(adv, /*initiate_reset*/TRUE); adv_start_execution(adv); splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = 7; cpi->initiator_id = adv->scsi_id; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Advansys", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /* * Currently, the output of bus_dmammap_load suits our needs just * fine, but should it change, we'd need to do something here. */ #define adv_fixup_dmasegs(adv, dm_segs) (struct adv_sg_entry *)(dm_segs) static void adv_execute_ccb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error) { struct ccb_scsiio *csio; struct ccb_hdr *ccb_h; struct cam_sim *sim; struct adv_softc *adv; struct adv_ccb_info *cinfo; struct adv_scsi_q scsiq; struct adv_sg_head sghead; int s; csio = (struct ccb_scsiio *)arg; ccb_h = &csio->ccb_h; sim = xpt_path_sim(ccb_h->path); adv = (struct adv_softc *)cam_sim_softc(sim); cinfo = (struct adv_ccb_info *)csio->ccb_h.ccb_cinfo_ptr; /* * Setup our done routine to release the simq on * the next ccb that completes. */ if ((adv->state & ADV_BUSDMA_BLOCK) != 0) adv->state |= ADV_BUSDMA_BLOCK_CLEARED; if ((ccb_h->flags & CAM_CDB_POINTER) != 0) { if ((ccb_h->flags & CAM_CDB_PHYS) == 0) { /* XXX Need phystovirt!!!! */ /* How about pmap_kenter??? */ scsiq.cdbptr = csio->cdb_io.cdb_ptr; } else { scsiq.cdbptr = csio->cdb_io.cdb_ptr; } } else { scsiq.cdbptr = csio->cdb_io.cdb_bytes; } /* * Build up the request */ scsiq.q1.status = 0; scsiq.q1.q_no = 0; scsiq.q1.cntl = 0; scsiq.q1.sg_queue_cnt = 0; scsiq.q1.target_id = ADV_TID_TO_TARGET_MASK(ccb_h->target_id); scsiq.q1.target_lun = ccb_h->target_lun; scsiq.q1.sense_len = csio->sense_len; scsiq.q1.extra_bytes = 0; scsiq.q2.ccb_index = cinfo - adv->ccb_infos; scsiq.q2.target_ix = ADV_TIDLUN_TO_IX(ccb_h->target_id, ccb_h->target_lun); scsiq.q2.flag = 0; scsiq.q2.cdb_len = csio->cdb_len; if ((ccb_h->flags & CAM_TAG_ACTION_VALID) != 0) scsiq.q2.tag_code = csio->tag_action; else scsiq.q2.tag_code = 0; scsiq.q2.vm_id = 0; if (nsegments != 0) { bus_dmasync_op_t op; scsiq.q1.data_addr = dm_segs->ds_addr; scsiq.q1.data_cnt = dm_segs->ds_len; if (nsegments > 1) { scsiq.q1.cntl |= QC_SG_HEAD; sghead.entry_cnt = sghead.entry_to_copy = nsegments; sghead.res = 0; sghead.sg_list = adv_fixup_dmasegs(adv, dm_segs); scsiq.sg_head = &sghead; } else { scsiq.sg_head = NULL; } if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(adv->buffer_dmat, cinfo->dmamap, op); } else { scsiq.q1.data_addr = 0; scsiq.q1.data_cnt = 0; scsiq.sg_head = NULL; } s = splcam(); /* * Last time we need to check if this SCB needs to * be aborted. */ if (ccb_h->status != CAM_REQ_INPROG) { if (nsegments != 0) bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap); adv_clear_state(adv, (union ccb *)csio); adv_free_ccb_info(adv, cinfo); xpt_done((union ccb *)csio); splx(s); return; } if (adv_execute_scsi_queue(adv, &scsiq, csio->dxfer_len) != 0) { /* Temporary resource shortage */ adv_set_state(adv, ADV_RESOURCE_SHORTAGE); if (nsegments != 0) bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap); csio->ccb_h.status = CAM_REQUEUE_REQ; adv_clear_state(adv, (union ccb *)csio); adv_free_ccb_info(adv, cinfo); xpt_done((union ccb *)csio); splx(s); return; } cinfo->state |= ACCB_ACTIVE; ccb_h->status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&adv->pending_ccbs, ccb_h, sim_links.le); /* Schedule our timeout */ ccb_h->timeout_ch = timeout(adv_timeout, csio, (ccb_h->timeout * hz)/1000); splx(s); } static struct adv_ccb_info * adv_alloc_ccb_info(struct adv_softc *adv) { int error; struct adv_ccb_info *cinfo; cinfo = &adv->ccb_infos[adv->ccb_infos_allocated]; cinfo->state = ACCB_FREE; error = bus_dmamap_create(adv->buffer_dmat, /*flags*/0, &cinfo->dmamap); if (error != 0) { printf("%s: Unable to allocate CCB info " "dmamap - error %d\n", adv_name(adv), error); return (NULL); } adv->ccb_infos_allocated++; return (cinfo); } static void adv_destroy_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo) { bus_dmamap_destroy(adv->buffer_dmat, cinfo->dmamap); } void adv_timeout(void *arg) { int s; union ccb *ccb; struct adv_softc *adv; struct adv_ccb_info *cinfo; ccb = (union ccb *)arg; adv = (struct adv_softc *)xpt_path_sim(ccb->ccb_h.path)->softc; cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr; xpt_print_path(ccb->ccb_h.path); printf("Timed out\n"); s = splcam(); /* Have we been taken care of already?? */ if (cinfo == NULL || cinfo->state == ACCB_FREE) { splx(s); return; } adv_stop_execution(adv); if ((cinfo->state & ACCB_ABORT_QUEUED) == 0) { struct ccb_hdr *ccb_h; /* * In order to simplify the recovery process, we ask the XPT * layer to halt the queue of new transactions and we traverse * the list of pending CCBs and remove their timeouts. This * means that the driver attempts to clear only one error * condition at a time. In general, timeouts that occur * close together are related anyway, so there is no benefit * in attempting to handle errors in parrallel. Timeouts will * be reinstated when the recovery process ends. */ adv_set_state(adv, ADV_IN_TIMEOUT); /* This CCB is the CCB representing our recovery actions */ cinfo->state |= ACCB_RECOVERY_CCB|ACCB_ABORT_QUEUED; ccb_h = LIST_FIRST(&adv->pending_ccbs); while (ccb_h != NULL) { untimeout(adv_timeout, ccb_h, ccb_h->timeout_ch); ccb_h = LIST_NEXT(ccb_h, sim_links.le); } /* XXX Should send a BDR */ /* Attempt an abort as our first tact */ xpt_print_path(ccb->ccb_h.path); printf("Attempting abort\n"); adv_abort_ccb(adv, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb, CAM_CMD_TIMEOUT, /*queued_only*/FALSE); ccb->ccb_h.timeout_ch = timeout(adv_timeout, ccb, 2 * hz); } else { /* Our attempt to perform an abort failed, go for a reset */ xpt_print_path(ccb->ccb_h.path); printf("Resetting bus\n"); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT; adv_reset_bus(adv, /*initiate_reset*/TRUE); } adv_start_execution(adv); splx(s); } struct adv_softc * adv_alloc(device_t dev, bus_space_tag_t tag, bus_space_handle_t bsh) { struct adv_softc *adv = device_get_softc(dev); /* * Allocate a storage area for us */ LIST_INIT(&adv->pending_ccbs); SLIST_INIT(&adv->free_ccb_infos); adv->dev = dev; adv->unit = device_get_unit(dev); adv->tag = tag; adv->bsh = bsh; return(adv); } void adv_free(struct adv_softc *adv) { switch (adv->init_level) { case 6: { struct adv_ccb_info *cinfo; while ((cinfo = SLIST_FIRST(&adv->free_ccb_infos)) != NULL) { SLIST_REMOVE_HEAD(&adv->free_ccb_infos, links); adv_destroy_ccb_info(adv, cinfo); } bus_dmamap_unload(adv->sense_dmat, adv->sense_dmamap); } case 5: bus_dmamem_free(adv->sense_dmat, adv->sense_buffers, adv->sense_dmamap); case 4: bus_dma_tag_destroy(adv->sense_dmat); case 3: bus_dma_tag_destroy(adv->buffer_dmat); case 2: bus_dma_tag_destroy(adv->parent_dmat); case 1: if (adv->ccb_infos != NULL) free(adv->ccb_infos, M_DEVBUF); case 0: break; } } int adv_init(struct adv_softc *adv) { struct adv_eeprom_config eeprom_config; int checksum, i; int max_sync; u_int16_t config_lsw; u_int16_t config_msw; adv_lib_init(adv); /* * Stop script execution. */ adv_write_lram_16(adv, ADV_HALTCODE_W, 0x00FE); adv_stop_execution(adv); if (adv_stop_chip(adv) == 0 || adv_is_chip_halted(adv) == 0) { printf("adv%d: Unable to halt adapter. Initialization" "failed\n", adv->unit); return (1); } ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR); if (ADV_INW(adv, ADV_REG_PROG_COUNTER) != ADV_MCODE_START_ADDR) { printf("adv%d: Unable to set program counter. Initialization" "failed\n", adv->unit); return (1); } config_msw = ADV_INW(adv, ADV_CONFIG_MSW); config_lsw = ADV_INW(adv, ADV_CONFIG_LSW); if ((config_msw & ADV_CFG_MSW_CLR_MASK) != 0) { config_msw &= ~ADV_CFG_MSW_CLR_MASK; /* * XXX The Linux code flags this as an error, * but what should we report to the user??? * It seems that clearing the config register * makes this error recoverable. */ ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw); } /* Suck in the configuration from the EEProm */ checksum = adv_get_eeprom_config(adv, &eeprom_config); if (ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_AUTO_CONFIG) { /* * XXX The Linux code sets a warning level for this * condition, yet nothing of meaning is printed to * the user. What does this mean??? */ if (adv->chip_version == 3) { if (eeprom_config.cfg_lsw != config_lsw) eeprom_config.cfg_lsw = config_lsw; if (eeprom_config.cfg_msw != config_msw) { eeprom_config.cfg_msw = config_msw; } } } if (checksum == eeprom_config.chksum) { /* Range/Sanity checking */ if (eeprom_config.max_total_qng < ADV_MIN_TOTAL_QNG) { eeprom_config.max_total_qng = ADV_MIN_TOTAL_QNG; } if (eeprom_config.max_total_qng > ADV_MAX_TOTAL_QNG) { eeprom_config.max_total_qng = ADV_MAX_TOTAL_QNG; } if (eeprom_config.max_tag_qng > eeprom_config.max_total_qng) { eeprom_config.max_tag_qng = eeprom_config.max_total_qng; } if (eeprom_config.max_tag_qng < ADV_MIN_TAG_Q_PER_DVC) { eeprom_config.max_tag_qng = ADV_MIN_TAG_Q_PER_DVC; } adv->max_openings = eeprom_config.max_total_qng; adv->user_disc_enable = eeprom_config.disc_enable; adv->user_cmd_qng_enabled = eeprom_config.use_cmd_qng; adv->isa_dma_speed = EEPROM_DMA_SPEED(eeprom_config); adv->scsi_id = EEPROM_SCSIID(eeprom_config) & ADV_MAX_TID; EEPROM_SET_SCSIID(eeprom_config, adv->scsi_id); adv->control = eeprom_config.cntl; for (i = 0; i <= ADV_MAX_TID; i++) { u_int8_t sync_data; if ((eeprom_config.init_sdtr & (0x1 << i)) == 0) sync_data = 0; else sync_data = eeprom_config.sdtr_data[i]; adv_sdtr_to_period_offset(adv, sync_data, &adv->tinfo[i].user.period, &adv->tinfo[i].user.offset, i); } config_lsw = eeprom_config.cfg_lsw; eeprom_config.cfg_msw = config_msw; } else { u_int8_t sync_data; printf("adv%d: Warning EEPROM Checksum mismatch. " "Using default device parameters\n", adv->unit); /* Set reasonable defaults since we can't read the EEPROM */ adv->isa_dma_speed = /*ADV_DEF_ISA_DMA_SPEED*/1; adv->max_openings = ADV_DEF_MAX_TOTAL_QNG; adv->disc_enable = TARGET_BIT_VECTOR_SET; adv->user_disc_enable = TARGET_BIT_VECTOR_SET; adv->cmd_qng_enabled = TARGET_BIT_VECTOR_SET; adv->user_cmd_qng_enabled = TARGET_BIT_VECTOR_SET; adv->scsi_id = 7; adv->control = 0xFFFF; if (adv->chip_version == ADV_CHIP_VER_PCI_ULTRA_3050) /* Default to no Ultra to support the 3030 */ adv->control &= ~ADV_CNTL_SDTR_ENABLE_ULTRA; sync_data = ADV_DEF_SDTR_OFFSET | (ADV_DEF_SDTR_INDEX << 4); for (i = 0; i <= ADV_MAX_TID; i++) { adv_sdtr_to_period_offset(adv, sync_data, &adv->tinfo[i].user.period, &adv->tinfo[i].user.offset, i); } config_lsw |= ADV_CFG_LSW_SCSI_PARITY_ON; } config_msw &= ~ADV_CFG_MSW_CLR_MASK; config_lsw |= ADV_CFG_LSW_HOST_INT_ON; if ((adv->type & (ADV_PCI|ADV_ULTRA)) == (ADV_PCI|ADV_ULTRA) && (adv->control & ADV_CNTL_SDTR_ENABLE_ULTRA) == 0) /* 25ns or 10MHz */ max_sync = 25; else /* Unlimited */ max_sync = 0; for (i = 0; i <= ADV_MAX_TID; i++) { if (adv->tinfo[i].user.period < max_sync) adv->tinfo[i].user.period = max_sync; } if (adv_test_external_lram(adv) == 0) { if ((adv->type & (ADV_PCI|ADV_ULTRA)) == (ADV_PCI|ADV_ULTRA)) { eeprom_config.max_total_qng = ADV_MAX_PCI_ULTRA_INRAM_TOTAL_QNG; eeprom_config.max_tag_qng = ADV_MAX_PCI_ULTRA_INRAM_TAG_QNG; } else { eeprom_config.cfg_msw |= 0x0800; config_msw |= 0x0800; eeprom_config.max_total_qng = ADV_MAX_PCI_INRAM_TOTAL_QNG; eeprom_config.max_tag_qng = ADV_MAX_INRAM_TAG_QNG; } adv->max_openings = eeprom_config.max_total_qng; } ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw); ADV_OUTW(adv, ADV_CONFIG_LSW, config_lsw); #if 0 /* * Don't write the eeprom data back for now. * I'd rather not mess up the user's card. We also don't * fully sanitize the eeprom settings above for the write-back * to be 100% correct. */ if (adv_set_eeprom_config(adv, &eeprom_config) != 0) printf("%s: WARNING! Failure writing to EEPROM.\n", adv_name(adv)); #endif adv_set_chip_scsiid(adv, adv->scsi_id); if (adv_init_lram_and_mcode(adv)) return (1); adv->disc_enable = adv->user_disc_enable; adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable); for (i = 0; i <= ADV_MAX_TID; i++) { /* * Start off in async mode. */ adv_set_syncrate(adv, /*struct cam_path */NULL, i, /*period*/0, /*offset*/0, ADV_TRANS_CUR); /* * Enable the use of tagged commands on all targets. * This allows the kernel driver to make up it's own mind * as it sees fit to tag queue instead of having the * firmware try and second guess the tag_code settins. */ adv_write_lram_8(adv, ADVV_MAX_DVC_QNG_BEG + i, adv->max_openings); } adv_write_lram_8(adv, ADVV_USE_TAGGED_QNG_B, TARGET_BIT_VECTOR_SET); adv_write_lram_8(adv, ADVV_CAN_TAGGED_QNG_B, TARGET_BIT_VECTOR_SET); printf("adv%d: AdvanSys %s Host Adapter, SCSI ID %d, queue depth %d\n", adv->unit, (adv->type & ADV_ULTRA) && (max_sync == 0) ? "Ultra SCSI" : "SCSI", adv->scsi_id, adv->max_openings); return (0); } void adv_intr(void *arg) { struct adv_softc *adv; u_int16_t chipstat; u_int16_t saved_ram_addr; u_int8_t ctrl_reg; u_int8_t saved_ctrl_reg; u_int8_t host_flag; adv = (struct adv_softc *)arg; chipstat = ADV_INW(adv, ADV_CHIP_STATUS); /* Is it for us? */ if ((chipstat & (ADV_CSW_INT_PENDING|ADV_CSW_SCSI_RESET_LATCH)) == 0) return; ctrl_reg = ADV_INB(adv, ADV_CHIP_CTRL); saved_ctrl_reg = ctrl_reg & (~(ADV_CC_SCSI_RESET | ADV_CC_CHIP_RESET | ADV_CC_SINGLE_STEP | ADV_CC_DIAG | ADV_CC_TEST)); if ((chipstat & (ADV_CSW_SCSI_RESET_LATCH|ADV_CSW_SCSI_RESET_ACTIVE))) { printf("Detected Bus Reset\n"); adv_reset_bus(adv, /*initiate_reset*/FALSE); return; } if ((chipstat & ADV_CSW_INT_PENDING) != 0) { saved_ram_addr = ADV_INW(adv, ADV_LRAM_ADDR); host_flag = adv_read_lram_8(adv, ADVV_HOST_FLAG_B); adv_write_lram_8(adv, ADVV_HOST_FLAG_B, host_flag | ADV_HOST_FLAG_IN_ISR); adv_ack_interrupt(adv); if ((chipstat & ADV_CSW_HALTED) != 0 && (ctrl_reg & ADV_CC_SINGLE_STEP) != 0) { adv_isr_chip_halted(adv); saved_ctrl_reg &= ~ADV_CC_HALT; } else { adv_run_doneq(adv); } ADV_OUTW(adv, ADV_LRAM_ADDR, saved_ram_addr); #ifdef DIAGNOSTIC if (ADV_INW(adv, ADV_LRAM_ADDR) != saved_ram_addr) panic("adv_intr: Unable to set LRAM addr"); #endif adv_write_lram_8(adv, ADVV_HOST_FLAG_B, host_flag); } ADV_OUTB(adv, ADV_CHIP_CTRL, saved_ctrl_reg); } static void adv_run_doneq(struct adv_softc *adv) { struct adv_q_done_info scsiq; u_int doneq_head; u_int done_qno; doneq_head = adv_read_lram_16(adv, ADVV_DONE_Q_TAIL_W) & 0xFF; done_qno = adv_read_lram_8(adv, ADV_QNO_TO_QADDR(doneq_head) + ADV_SCSIQ_B_FWD); while (done_qno != ADV_QLINK_END) { union ccb* ccb; struct adv_ccb_info *cinfo; u_int done_qaddr; u_int sg_queue_cnt; int aborted; done_qaddr = ADV_QNO_TO_QADDR(done_qno); /* Pull status from this request */ sg_queue_cnt = adv_copy_lram_doneq(adv, done_qaddr, &scsiq, adv->max_dma_count); /* Mark it as free */ adv_write_lram_8(adv, done_qaddr + ADV_SCSIQ_B_STATUS, scsiq.q_status & ~(QS_READY|QS_ABORTED)); /* Process request based on retrieved info */ if ((scsiq.cntl & QC_SG_HEAD) != 0) { u_int i; /* * S/G based request. Free all of the queue * structures that contained S/G information. */ for (i = 0; i < sg_queue_cnt; i++) { done_qno = adv_read_lram_8(adv, done_qaddr + ADV_SCSIQ_B_FWD); #ifdef DIAGNOSTIC if (done_qno == ADV_QLINK_END) { panic("adv_qdone: Corrupted SG " "list encountered"); } #endif done_qaddr = ADV_QNO_TO_QADDR(done_qno); /* Mark SG queue as free */ adv_write_lram_8(adv, done_qaddr + ADV_SCSIQ_B_STATUS, QS_FREE); } } else sg_queue_cnt = 0; #ifdef DIAGNOSTIC if (adv->cur_active < (sg_queue_cnt + 1)) panic("adv_qdone: Attempting to free more " "queues than are active"); #endif adv->cur_active -= sg_queue_cnt + 1; aborted = (scsiq.q_status & QS_ABORTED) != 0; if ((scsiq.q_status != QS_DONE) && (scsiq.q_status & QS_ABORTED) == 0) panic("adv_qdone: completed scsiq with unknown status"); scsiq.remain_bytes += scsiq.extra_bytes; if ((scsiq.d3.done_stat == QD_WITH_ERROR) && (scsiq.d3.host_stat == QHSTA_M_DATA_OVER_RUN)) { if ((scsiq.cntl & (QC_DATA_IN|QC_DATA_OUT)) == 0) { scsiq.d3.done_stat = QD_NO_ERROR; scsiq.d3.host_stat = QHSTA_NO_ERROR; } } cinfo = &adv->ccb_infos[scsiq.d2.ccb_index]; ccb = cinfo->ccb; ccb->csio.resid = scsiq.remain_bytes; adv_done(adv, ccb, scsiq.d3.done_stat, scsiq.d3.host_stat, scsiq.d3.scsi_stat, scsiq.q_no); doneq_head = done_qno; done_qno = adv_read_lram_8(adv, done_qaddr + ADV_SCSIQ_B_FWD); } adv_write_lram_16(adv, ADVV_DONE_Q_TAIL_W, doneq_head); } void adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat, u_int host_stat, u_int scsi_status, u_int q_no) { struct adv_ccb_info *cinfo; cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr; LIST_REMOVE(&ccb->ccb_h, sim_links.le); untimeout(adv_timeout, ccb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(adv->buffer_dmat, cinfo->dmamap, op); bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap); } switch (done_stat) { case QD_NO_ERROR: if (host_stat == QHSTA_NO_ERROR) { ccb->ccb_h.status = CAM_REQ_CMP; break; } xpt_print_path(ccb->ccb_h.path); printf("adv_done - queue done without error, " "but host status non-zero(%x)\n", host_stat); /*FALLTHROUGH*/ case QD_WITH_ERROR: switch (host_stat) { case QHSTA_M_TARGET_STATUS_BUSY: case QHSTA_M_BAD_QUEUE_FULL_OR_BUSY: /* * Assume that if we were a tagged transaction * the target reported queue full. Otherwise, * report busy. The firmware really should just * pass the original status back up to us even * if it thinks the target was in error for * returning this status as no other transactions * from this initiator are in effect, but this * ignores multi-initiator setups and there is * evidence that the firmware gets its per-device * transaction counts screwed up occassionally. */ ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0 && host_stat != QHSTA_M_TARGET_STATUS_BUSY) scsi_status = SCSI_STATUS_QUEUE_FULL; else scsi_status = SCSI_STATUS_BUSY; adv_abort_ccb(adv, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, /*ccb*/NULL, CAM_REQUEUE_REQ, /*queued_only*/TRUE); /*FALLTHROUGH*/ case QHSTA_M_NO_AUTO_REQ_SENSE: case QHSTA_NO_ERROR: ccb->csio.scsi_status = scsi_status; switch (scsi_status) { case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_CMD_TERMINATED: ccb->ccb_h.status |= CAM_AUTOSNS_VALID; /* Structure copy */ ccb->csio.sense_data = adv->sense_buffers[q_no - 1]; /* FALLTHROUGH */ case SCSI_STATUS_BUSY: case SCSI_STATUS_RESERV_CONFLICT: case SCSI_STATUS_QUEUE_FULL: case SCSI_STATUS_COND_MET: case SCSI_STATUS_INTERMED: case SCSI_STATUS_INTERMED_COND_MET: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; break; case SCSI_STATUS_OK: ccb->ccb_h.status |= CAM_REQ_CMP; break; } break; case QHSTA_M_SEL_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case QHSTA_M_DATA_OVER_RUN: ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; case QHSTA_M_UNEXPECTED_BUS_FREE: ccb->ccb_h.status = CAM_UNEXP_BUSFREE; break; case QHSTA_M_BAD_BUS_PHASE_SEQ: ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_M_BAD_CMPL_STATUS_IN: /* No command complete after a status message */ ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT: case QHSTA_M_WTM_TIMEOUT: case QHSTA_M_HUNG_REQ_SCSI_BUS_RESET: /* The SCSI bus hung in a phase */ ccb->ccb_h.status = CAM_SEQUENCE_FAIL; adv_reset_bus(adv, /*initiate_reset*/TRUE); break; case QHSTA_M_AUTO_REQ_SENSE_FAIL: ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; break; case QHSTA_D_QDONE_SG_LIST_CORRUPTED: case QHSTA_D_ASC_DVC_ERROR_CODE_SET: case QHSTA_D_HOST_ABORT_FAILED: case QHSTA_D_EXE_SCSI_Q_FAILED: case QHSTA_D_ASPI_NO_BUF_POOL: case QHSTA_M_BAD_TAG_CODE: case QHSTA_D_LRAM_CMP_ERROR: case QHSTA_M_MICRO_CODE_ERROR_HALT: default: panic("%s: Unhandled Host status error %x", adv_name(adv), host_stat); /* NOTREACHED */ } break; case QD_ABORTED_BY_HOST: /* Don't clobber any, more explicit, error codes we've set */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) ccb->ccb_h.status = CAM_REQ_ABORTED; break; default: xpt_print_path(ccb->ccb_h.path); printf("adv_done - queue done with unknown status %x:%x\n", done_stat, host_stat); ccb->ccb_h.status = CAM_REQ_CMP_ERR; break; } adv_clear_state(adv, ccb); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && (ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } adv_free_ccb_info(adv, cinfo); /* * Null this out so that we catch driver bugs that cause a * ccb to be completed twice. */ ccb->ccb_h.ccb_cinfo_ptr = NULL; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; xpt_done(ccb); } /* * Function to poll for command completion when * interrupts are disabled (crash dumps) */ static void adv_poll(struct cam_sim *sim) { adv_intr(cam_sim_softc(sim)); } /* * Attach all the sub-devices we can find */ int adv_attach(adv) struct adv_softc *adv; { struct ccb_setasync csa; struct cam_devq *devq; int max_sg; /* * Allocate an array of ccb mapping structures. We put the * index of the ccb_info structure into the queue representing * a transaction and use it for mapping the queue to the * upper level SCSI transaction it represents. */ adv->ccb_infos = malloc(sizeof(*adv->ccb_infos) * adv->max_openings, M_DEVBUF, M_NOWAIT); if (adv->ccb_infos == NULL) return (ENOMEM); adv->init_level++; /* * Create our DMA tags. These tags define the kinds of device * accessible memory allocations and memory mappings we will * need to perform during normal operation. * * Unless we need to further restrict the allocation, we rely * on the restrictions of the parent dmat, hence the common * use of MAXADDR and MAXSIZE. * * The ASC boards use chains of "queues" (the transactional * resources on the board) to represent long S/G lists. * The first queue represents the command and holds a * single address and data pair. The queues that follow * can each hold ADV_SG_LIST_PER_Q entries. Given the * total number of queues, we can express the largest * transaction we can map. We reserve a few queues for * error recovery. Take those into account as well. * * There is a way to take an interrupt to download the * next batch of S/G entries if there are more than 255 * of them (the counter in the queue structure is a u_int8_t). * We don't use this feature, so limit the S/G list size * accordingly. */ max_sg = (adv->max_openings - ADV_MIN_FREE_Q - 1) * ADV_SG_LIST_PER_Q; if (max_sg > 255) max_sg = 255; /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create( /* parent */ adv->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ MAXPHYS, /* nsegments */ max_sg, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ BUS_DMA_ALLOCNOW, &adv->buffer_dmat) != 0) { return (ENXIO); } adv->init_level++; /* DMA tag for our sense buffers */ if (bus_dma_tag_create( /* parent */ adv->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ sizeof(struct scsi_sense_data) * adv->max_openings, /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &adv->sense_dmat) != 0) { return (ENXIO); } adv->init_level++; /* Allocation for our sense buffers */ if (bus_dmamem_alloc(adv->sense_dmat, (void **)&adv->sense_buffers, BUS_DMA_NOWAIT, &adv->sense_dmamap) != 0) { return (ENOMEM); } adv->init_level++; /* And permanently map them */ bus_dmamap_load(adv->sense_dmat, adv->sense_dmamap, adv->sense_buffers, sizeof(struct scsi_sense_data)*adv->max_openings, adv_map, &adv->sense_physbase, /*flags*/0); adv->init_level++; /* * Fire up the chip */ if (adv_start_chip(adv) != 1) { printf("adv%d: Unable to start on board processor. Aborting.\n", adv->unit); return (ENXIO); } /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(adv->max_openings); if (devq == NULL) return (ENOMEM); /* * Construct our SIM entry. */ adv->sim = cam_sim_alloc(adv_action, adv_poll, "adv", adv, adv->unit, 1, adv->max_openings, devq); if (adv->sim == NULL) return (ENOMEM); /* * Register the bus. * * XXX Twin Channel EISA Cards??? */ if (xpt_bus_register(adv->sim, 0) != CAM_SUCCESS) { cam_sim_free(adv->sim, /*free devq*/TRUE); return (ENXIO); } if (xpt_create_path(&adv->path, /*periph*/NULL, cam_sim_path(adv->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(adv->sim)); cam_sim_free(adv->sim, /*free devq*/TRUE); return (ENXIO); } xpt_setup_ccb(&csa.ccb_h, adv->path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_FOUND_DEVICE|AC_LOST_DEVICE; csa.callback = advasync; csa.callback_arg = adv; xpt_action((union ccb *)&csa); return (0); } Index: head/sys/dev/advansys/adwcam.c =================================================================== --- head/sys/dev/advansys/adwcam.c (revision 116350) +++ head/sys/dev/advansys/adwcam.c (revision 116351) @@ -1,1559 +1,1540 @@ /* * CAM SCSI interface for the the Advanced Systems Inc. * Second Generation SCSI controllers. * * Product specific probe and attach routines can be found in: * * adw_pci.c ABP[3]940UW, ABP950UW, ABP3940U2W * * Copyright (c) 1998, 1999, 2000 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Ported from: * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * * Copyright (c) 1995-1998 Advanced System Products, Inc. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that redistributions of source * code retain the above copyright notice and this comment without * modification. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Definitions for our use of the SIM private CCB area */ #define ccb_acb_ptr spriv_ptr0 #define ccb_adw_ptr spriv_ptr1 u_long adw_unit; static __inline cam_status adwccbstatus(union ccb*); static __inline struct acb* adwgetacb(struct adw_softc *adw); static __inline void adwfreeacb(struct adw_softc *adw, struct acb *acb); static void adwmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error); static struct sg_map_node* adwallocsgmap(struct adw_softc *adw); static int adwallocacbs(struct adw_softc *adw); static void adwexecuteacb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error); static void adw_action(struct cam_sim *sim, union ccb *ccb); static void adw_poll(struct cam_sim *sim); static void adw_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void adwprocesserror(struct adw_softc *adw, struct acb *acb); static void adwtimeout(void *arg); static void adw_handle_device_reset(struct adw_softc *adw, u_int target); static void adw_handle_bus_reset(struct adw_softc *adw, int initiated); static __inline cam_status adwccbstatus(union ccb* ccb) { return (ccb->ccb_h.status & CAM_STATUS_MASK); } static __inline struct acb* adwgetacb(struct adw_softc *adw) { struct acb* acb; int s; s = splcam(); if ((acb = SLIST_FIRST(&adw->free_acb_list)) != NULL) { SLIST_REMOVE_HEAD(&adw->free_acb_list, links); } else if (adw->num_acbs < adw->max_acbs) { adwallocacbs(adw); acb = SLIST_FIRST(&adw->free_acb_list); if (acb == NULL) printf("%s: Can't malloc ACB\n", adw_name(adw)); else { SLIST_REMOVE_HEAD(&adw->free_acb_list, links); } } splx(s); return (acb); } static __inline void adwfreeacb(struct adw_softc *adw, struct acb *acb) { int s; s = splcam(); if ((acb->state & ACB_ACTIVE) != 0) LIST_REMOVE(&acb->ccb->ccb_h, sim_links.le); if ((acb->state & ACB_RELEASE_SIMQ) != 0) acb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; else if ((adw->state & ADW_RESOURCE_SHORTAGE) != 0 && (acb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { acb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; adw->state &= ~ADW_RESOURCE_SHORTAGE; } acb->state = ACB_FREE; SLIST_INSERT_HEAD(&adw->free_acb_list, acb, links); splx(s); } static void adwmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *busaddrp; busaddrp = (bus_addr_t *)arg; *busaddrp = segs->ds_addr; } static struct sg_map_node * adwallocsgmap(struct adw_softc *adw) { struct sg_map_node *sg_map; sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT); if (sg_map == NULL) return (NULL); /* Allocate S/G space for the next batch of ACBS */ if (bus_dmamem_alloc(adw->sg_dmat, (void **)&sg_map->sg_vaddr, BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) { free(sg_map, M_DEVBUF); return (NULL); } SLIST_INSERT_HEAD(&adw->sg_maps, sg_map, links); bus_dmamap_load(adw->sg_dmat, sg_map->sg_dmamap, sg_map->sg_vaddr, PAGE_SIZE, adwmapmem, &sg_map->sg_physaddr, /*flags*/0); bzero(sg_map->sg_vaddr, PAGE_SIZE); return (sg_map); } /* * Allocate another chunk of CCB's. Return count of entries added. * Assumed to be called at splcam(). */ static int adwallocacbs(struct adw_softc *adw) { struct acb *next_acb; struct sg_map_node *sg_map; bus_addr_t busaddr; struct adw_sg_block *blocks; int newcount; int i; next_acb = &adw->acbs[adw->num_acbs]; sg_map = adwallocsgmap(adw); if (sg_map == NULL) return (0); blocks = sg_map->sg_vaddr; busaddr = sg_map->sg_physaddr; newcount = (PAGE_SIZE / (ADW_SG_BLOCKCNT * sizeof(*blocks))); for (i = 0; adw->num_acbs < adw->max_acbs && i < newcount; i++) { int error; error = bus_dmamap_create(adw->buffer_dmat, /*flags*/0, &next_acb->dmamap); if (error != 0) break; next_acb->queue.scsi_req_baddr = acbvtob(adw, next_acb); next_acb->queue.scsi_req_bo = acbvtobo(adw, next_acb); next_acb->queue.sense_baddr = acbvtob(adw, next_acb) + offsetof(struct acb, sense_data); next_acb->sg_blocks = blocks; next_acb->sg_busaddr = busaddr; next_acb->state = ACB_FREE; SLIST_INSERT_HEAD(&adw->free_acb_list, next_acb, links); blocks += ADW_SG_BLOCKCNT; busaddr += ADW_SG_BLOCKCNT * sizeof(*blocks); next_acb++; adw->num_acbs++; } return (i); } static void adwexecuteacb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct acb *acb; union ccb *ccb; struct adw_softc *adw; int s; acb = (struct acb *)arg; ccb = acb->ccb; adw = (struct adw_softc *)ccb->ccb_h.ccb_adw_ptr; if (error != 0) { if (error != EFBIG) printf("%s: Unexepected error 0x%x returned from " "bus_dmamap_load\n", adw_name(adw), error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; } adwfreeacb(adw, acb); xpt_done(ccb); return; } if (nseg != 0) { bus_dmasync_op_t op; acb->queue.data_addr = dm_segs[0].ds_addr; acb->queue.data_cnt = ccb->csio.dxfer_len; if (nseg > 1) { struct adw_sg_block *sg_block; struct adw_sg_elm *sg; bus_addr_t sg_busaddr; u_int sg_index; bus_dma_segment_t *end_seg; end_seg = dm_segs + nseg; sg_busaddr = acb->sg_busaddr; sg_index = 0; /* Copy the segments into our SG list */ for (sg_block = acb->sg_blocks;; sg_block++) { u_int i; sg = sg_block->sg_list; for (i = 0; i < ADW_NO_OF_SG_PER_BLOCK; i++) { if (dm_segs >= end_seg) break; sg->sg_addr = dm_segs->ds_addr; sg->sg_count = dm_segs->ds_len; sg++; dm_segs++; } sg_block->sg_cnt = i; sg_index += i; if (dm_segs == end_seg) { sg_block->sg_busaddr_next = 0; break; } else { sg_busaddr += sizeof(struct adw_sg_block); sg_block->sg_busaddr_next = sg_busaddr; } } acb->queue.sg_real_addr = acb->sg_busaddr; } else { acb->queue.sg_real_addr = 0; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(adw->buffer_dmat, acb->dmamap, op); } else { acb->queue.data_addr = 0; acb->queue.data_cnt = 0; acb->queue.sg_real_addr = 0; } s = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(adw->buffer_dmat, acb->dmamap); adwfreeacb(adw, acb); xpt_done(ccb); splx(s); return; } acb->state |= ACB_ACTIVE; ccb->ccb_h.status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&adw->pending_ccbs, &ccb->ccb_h, sim_links.le); ccb->ccb_h.timeout_ch = timeout(adwtimeout, (caddr_t)acb, (ccb->ccb_h.timeout * hz) / 1000); adw_send_acb(adw, acb, acbvtob(adw, acb)); splx(s); } static void adw_action(struct cam_sim *sim, union ccb *ccb) { struct adw_softc *adw; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("adw_action\n")); adw = (struct adw_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct ccb_scsiio *csio; struct ccb_hdr *ccbh; struct acb *acb; csio = &ccb->csio; ccbh = &ccb->ccb_h; /* Max supported CDB length is 12 bytes */ if (csio->cdb_len > 12) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } if ((acb = adwgetacb(adw)) == NULL) { int s; s = splcam(); adw->state |= ADW_RESOURCE_SHORTAGE; splx(s); xpt_freeze_simq(sim, /*count*/1); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } /* Link acb and ccb so we can find one from the other */ acb->ccb = ccb; ccb->ccb_h.ccb_acb_ptr = acb; ccb->ccb_h.ccb_adw_ptr = adw; acb->queue.cntl = 0; acb->queue.target_cmd = 0; acb->queue.target_id = ccb->ccb_h.target_id; acb->queue.target_lun = ccb->ccb_h.target_lun; acb->queue.mflag = 0; acb->queue.sense_len = MIN(csio->sense_len, sizeof(acb->sense_data)); acb->queue.cdb_len = csio->cdb_len; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { switch (csio->tag_action) { case MSG_SIMPLE_Q_TAG: acb->queue.scsi_cntl = ADW_QSC_SIMPLE_Q_TAG; break; case MSG_HEAD_OF_Q_TAG: acb->queue.scsi_cntl = ADW_QSC_HEAD_OF_Q_TAG; break; case MSG_ORDERED_Q_TAG: acb->queue.scsi_cntl = ADW_QSC_ORDERED_Q_TAG; break; default: acb->queue.scsi_cntl = ADW_QSC_NO_TAGMSG; break; } } else acb->queue.scsi_cntl = ADW_QSC_NO_TAGMSG; if ((ccb->ccb_h.flags & CAM_DIS_DISCONNECT) != 0) acb->queue.scsi_cntl |= ADW_QSC_NO_DISC; acb->queue.done_status = 0; acb->queue.scsi_status = 0; acb->queue.host_status = 0; acb->queue.sg_wk_ix = 0; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(csio->cdb_io.cdb_ptr, acb->queue.cdb, csio->cdb_len); } else { /* I guess I could map it in... */ ccb->ccb_h.status = CAM_REQ_INVALID; adwfreeacb(adw, acb); xpt_done(ccb); return; } } else { bcopy(csio->cdb_io.cdb_bytes, acb->queue.cdb, csio->cdb_len); } /* * If we have any data to send with this command, * map it into bus space. */ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccbh->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer. */ if ((ccbh->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(adw->buffer_dmat, acb->dmamap, csio->data_ptr, csio->dxfer_len, adwexecuteacb, acb, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(sim, 1); acb->state |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; adwexecuteacb(acb, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccbh->flags & CAM_DATA_PHYS) != 0) panic("adw_action - Physical " "segment pointers " "unsupported"); if ((ccbh->flags&CAM_SG_LIST_PHYS)==0) panic("adw_action - Virtual " "segment addresses " "unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; adwexecuteacb(acb, segs, csio->sglist_cnt, (csio->sglist_cnt < ADW_SGSIZE) ? 0 : EFBIG); } } else { adwexecuteacb(acb, NULL, 0, 0); } break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ { adw_idle_cmd_status_t status; status = adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET, ccb->ccb_h.target_id); if (status == ADW_IDLE_CMD_SUCCESS) { ccb->ccb_h.status = CAM_REQ_CMP; if (bootverbose) { xpt_print_path(ccb->ccb_h.path); printf("BDR Delivered\n"); } } else ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); break; } case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; u_int target_mask; int s; cts = &ccb->cts; target_mask = 0x01 << ccb->ccb_h.target_id; s = splcam(); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { u_int sdtrdone; sdtrdone = adw_lram_read_16(adw, ADW_MC_SDTR_DONE); if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { u_int discenb; discenb = adw_lram_read_16(adw, ADW_MC_DISC_ENABLE); if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) discenb |= target_mask; else discenb &= ~target_mask; adw_lram_write_16(adw, ADW_MC_DISC_ENABLE, discenb); } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) adw->tagenb |= target_mask; else adw->tagenb &= ~target_mask; } if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { u_int wdtrenb_orig; u_int wdtrenb; u_int wdtrdone; wdtrenb_orig = adw_lram_read_16(adw, ADW_MC_WDTR_ABLE); wdtrenb = wdtrenb_orig; wdtrdone = adw_lram_read_16(adw, ADW_MC_WDTR_DONE); switch (cts->bus_width) { case MSG_EXT_WDTR_BUS_32_BIT: case MSG_EXT_WDTR_BUS_16_BIT: wdtrenb |= target_mask; break; case MSG_EXT_WDTR_BUS_8_BIT: default: wdtrenb &= ~target_mask; break; } if (wdtrenb != wdtrenb_orig) { adw_lram_write_16(adw, ADW_MC_WDTR_ABLE, wdtrenb); wdtrdone &= ~target_mask; adw_lram_write_16(adw, ADW_MC_WDTR_DONE, wdtrdone); /* Wide negotiation forces async */ sdtrdone &= ~target_mask; adw_lram_write_16(adw, ADW_MC_SDTR_DONE, sdtrdone); } } if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { u_int sdtr_orig; u_int sdtr; u_int sdtrable_orig; u_int sdtrable; sdtr = adw_get_chip_sdtr(adw, ccb->ccb_h.target_id); sdtr_orig = sdtr; sdtrable = adw_lram_read_16(adw, ADW_MC_SDTR_ABLE); sdtrable_orig = sdtrable; if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { sdtr = adw_find_sdtr(adw, cts->sync_period); } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { if (cts->sync_offset == 0) sdtr = ADW_MC_SDTR_ASYNC; } if (sdtr == ADW_MC_SDTR_ASYNC) sdtrable &= ~target_mask; else sdtrable |= target_mask; if (sdtr != sdtr_orig || sdtrable != sdtrable_orig) { adw_set_chip_sdtr(adw, ccb->ccb_h.target_id, sdtr); sdtrdone &= ~target_mask; adw_lram_write_16(adw, ADW_MC_SDTR_ABLE, sdtrable); adw_lram_write_16(adw, ADW_MC_SDTR_DONE, sdtrdone); } } } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; u_int target_mask; cts = &ccb->cts; target_mask = 0x01 << ccb->ccb_h.target_id; if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { u_int mc_sdtr; cts->flags = 0; if ((adw->user_discenb & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adw->user_tagenb & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; if ((adw->user_wdtr & target_mask) != 0) cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; else cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; mc_sdtr = adw_get_user_sdtr(adw, ccb->ccb_h.target_id); cts->sync_period = adw_find_period(adw, mc_sdtr); if (cts->sync_period != 0) cts->sync_offset = 15; /* XXX ??? */ else cts->sync_offset = 0; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; } else { u_int targ_tinfo; cts->flags = 0; if ((adw_lram_read_16(adw, ADW_MC_DISC_ENABLE) & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adw->tagenb & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; targ_tinfo = adw_lram_read_16(adw, ADW_MC_DEVICE_HSHK_CFG_TABLE + (2 * ccb->ccb_h.target_id)); if ((targ_tinfo & ADW_HSHK_CFG_WIDE_XFR) != 0) cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; else cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; cts->sync_period = adw_hshk_cfg_period_factor(targ_tinfo); cts->sync_offset = targ_tinfo & ADW_HSHK_CFG_OFFSET; if (cts->sync_period == 0) cts->sync_offset = 0; if (cts->sync_offset == 0) cts->sync_period = 0; } cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended; - /* * XXX Use Adaptec translation until I find out how to * get this information from the card. */ - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - extended = 1; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int failure; failure = adw_reset_bus(adw); if (failure != 0) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; } else { if (bootverbose) { xpt_print_path(adw->path); printf("Bus Reset Delivered\n"); } ccb->ccb_h.status = CAM_REQ_CMP; } xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_WIDE_16|PI_SDTR_ABLE|PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = ADW_MAX_TID; cpi->max_lun = ADW_MAX_LUN; cpi->initiator_id = adw->initiator_id; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "AdvanSys", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void adw_poll(struct cam_sim *sim) { adw_intr(cam_sim_softc(sim)); } static void adw_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { } struct adw_softc * adw_alloc(device_t dev, struct resource *regs, int regs_type, int regs_id) { struct adw_softc *adw; int i; /* * Allocate a storage area for us */ adw = malloc(sizeof(struct adw_softc), M_DEVBUF, M_NOWAIT | M_ZERO); if (adw == NULL) { printf("adw%d: cannot malloc!\n", device_get_unit(dev)); return NULL; } LIST_INIT(&adw->pending_ccbs); SLIST_INIT(&adw->sg_maps); adw->device = dev; adw->unit = device_get_unit(dev); adw->regs_res_type = regs_type; adw->regs_res_id = regs_id; adw->regs = regs; adw->tag = rman_get_bustag(regs); adw->bsh = rman_get_bushandle(regs); i = adw->unit / 10; adw->name = malloc(sizeof("adw") + i + 1, M_DEVBUF, M_NOWAIT); if (adw->name == NULL) { printf("adw%d: cannot malloc name!\n", adw->unit); free(adw, M_DEVBUF); return NULL; } sprintf(adw->name, "adw%d", adw->unit); return(adw); } void adw_free(struct adw_softc *adw) { switch (adw->init_level) { case 9: { struct sg_map_node *sg_map; while ((sg_map = SLIST_FIRST(&adw->sg_maps)) != NULL) { SLIST_REMOVE_HEAD(&adw->sg_maps, links); bus_dmamap_unload(adw->sg_dmat, sg_map->sg_dmamap); bus_dmamem_free(adw->sg_dmat, sg_map->sg_vaddr, sg_map->sg_dmamap); free(sg_map, M_DEVBUF); } bus_dma_tag_destroy(adw->sg_dmat); } case 8: bus_dmamap_unload(adw->acb_dmat, adw->acb_dmamap); case 7: bus_dmamem_free(adw->acb_dmat, adw->acbs, adw->acb_dmamap); bus_dmamap_destroy(adw->acb_dmat, adw->acb_dmamap); case 6: bus_dma_tag_destroy(adw->acb_dmat); case 5: bus_dmamap_unload(adw->carrier_dmat, adw->carrier_dmamap); case 4: bus_dmamem_free(adw->carrier_dmat, adw->carriers, adw->carrier_dmamap); bus_dmamap_destroy(adw->carrier_dmat, adw->carrier_dmamap); case 3: bus_dma_tag_destroy(adw->carrier_dmat); case 2: bus_dma_tag_destroy(adw->buffer_dmat); case 1: bus_dma_tag_destroy(adw->parent_dmat); case 0: break; } free(adw->name, M_DEVBUF); free(adw, M_DEVBUF); } int adw_init(struct adw_softc *adw) { struct adw_eeprom eep_config; u_int tid; u_int i; u_int16_t checksum; u_int16_t scsicfg1; checksum = adw_eeprom_read(adw, &eep_config); bcopy(eep_config.serial_number, adw->serial_number, sizeof(adw->serial_number)); if (checksum != eep_config.checksum) { u_int16_t serial_number[3]; adw->flags |= ADW_EEPROM_FAILED; printf("%s: EEPROM checksum failed. Restoring Defaults\n", adw_name(adw)); /* * Restore the default EEPROM settings. * Assume the 6 byte board serial number that was read * from EEPROM is correct even if the EEPROM checksum * failed. */ bcopy(adw->default_eeprom, &eep_config, sizeof(eep_config)); bcopy(adw->serial_number, eep_config.serial_number, sizeof(serial_number)); adw_eeprom_write(adw, &eep_config); } /* Pull eeprom information into our softc. */ adw->bios_ctrl = eep_config.bios_ctrl; adw->user_wdtr = eep_config.wdtr_able; for (tid = 0; tid < ADW_MAX_TID; tid++) { u_int mc_sdtr; u_int16_t tid_mask; tid_mask = 0x1 << tid; if ((adw->features & ADW_ULTRA) != 0) { /* * Ultra chips store sdtr and ultraenb * bits in their seeprom, so we must * construct valid mc_sdtr entries for * indirectly. */ if (eep_config.sync1.sync_enable & tid_mask) { if (eep_config.sync2.ultra_enable & tid_mask) mc_sdtr = ADW_MC_SDTR_20; else mc_sdtr = ADW_MC_SDTR_10; } else mc_sdtr = ADW_MC_SDTR_ASYNC; } else { switch (ADW_TARGET_GROUP(tid)) { case 3: mc_sdtr = eep_config.sync4.sdtr4; break; case 2: mc_sdtr = eep_config.sync3.sdtr3; break; case 1: mc_sdtr = eep_config.sync2.sdtr2; break; default: /* Shut up compiler */ case 0: mc_sdtr = eep_config.sync1.sdtr1; break; } mc_sdtr >>= ADW_TARGET_GROUP_SHIFT(tid); mc_sdtr &= 0xFF; } adw_set_user_sdtr(adw, tid, mc_sdtr); } adw->user_tagenb = eep_config.tagqng_able; adw->user_discenb = eep_config.disc_enable; adw->max_acbs = eep_config.max_host_qng; adw->initiator_id = (eep_config.adapter_scsi_id & ADW_MAX_TID); /* * Sanity check the number of host openings. */ if (adw->max_acbs > ADW_DEF_MAX_HOST_QNG) adw->max_acbs = ADW_DEF_MAX_HOST_QNG; else if (adw->max_acbs < ADW_DEF_MIN_HOST_QNG) { /* If the value is zero, assume it is uninitialized. */ if (adw->max_acbs == 0) adw->max_acbs = ADW_DEF_MAX_HOST_QNG; else adw->max_acbs = ADW_DEF_MIN_HOST_QNG; } scsicfg1 = 0; if ((adw->features & ADW_ULTRA2) != 0) { switch (eep_config.termination_lvd) { default: printf("%s: Invalid EEPROM LVD Termination Settings.\n", adw_name(adw)); printf("%s: Reverting to Automatic LVD Termination\n", adw_name(adw)); /* FALLTHROUGH */ case ADW_EEPROM_TERM_AUTO: break; case ADW_EEPROM_TERM_BOTH_ON: scsicfg1 |= ADW2_SCSI_CFG1_TERM_LVD_LO; /* FALLTHROUGH */ case ADW_EEPROM_TERM_HIGH_ON: scsicfg1 |= ADW2_SCSI_CFG1_TERM_LVD_HI; /* FALLTHROUGH */ case ADW_EEPROM_TERM_OFF: scsicfg1 |= ADW2_SCSI_CFG1_DIS_TERM_DRV; break; } } switch (eep_config.termination_se) { default: printf("%s: Invalid SE EEPROM Termination Settings.\n", adw_name(adw)); printf("%s: Reverting to Automatic SE Termination\n", adw_name(adw)); /* FALLTHROUGH */ case ADW_EEPROM_TERM_AUTO: break; case ADW_EEPROM_TERM_BOTH_ON: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_L; /* FALLTHROUGH */ case ADW_EEPROM_TERM_HIGH_ON: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_H; /* FALLTHROUGH */ case ADW_EEPROM_TERM_OFF: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_MANUAL; break; } printf("%s: SCSI ID %d, ", adw_name(adw), adw->initiator_id); /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create( /* parent */ adw->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ MAXBSIZE, /* nsegments */ ADW_SGSIZE, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ BUS_DMA_ALLOCNOW, &adw->buffer_dmat) != 0) { return (ENOMEM); } adw->init_level++; /* DMA tag for our ccb carrier structures */ if (bus_dma_tag_create( /* parent */ adw->parent_dmat, /* alignment */ 0x10, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ (adw->max_acbs + ADW_NUM_CARRIER_QUEUES + 1) * sizeof(struct adw_carrier), /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &adw->carrier_dmat) != 0) { return (ENOMEM); } adw->init_level++; /* Allocation for our ccb carrier structures */ if (bus_dmamem_alloc(adw->carrier_dmat, (void **)&adw->carriers, BUS_DMA_NOWAIT, &adw->carrier_dmamap) != 0) { return (ENOMEM); } adw->init_level++; /* And permanently map them */ bus_dmamap_load(adw->carrier_dmat, adw->carrier_dmamap, adw->carriers, (adw->max_acbs + ADW_NUM_CARRIER_QUEUES + 1) * sizeof(struct adw_carrier), adwmapmem, &adw->carrier_busbase, /*flags*/0); /* Clear them out. */ bzero(adw->carriers, (adw->max_acbs + ADW_NUM_CARRIER_QUEUES + 1) * sizeof(struct adw_carrier)); /* Setup our free carrier list */ adw->free_carriers = adw->carriers; for (i = 0; i < adw->max_acbs + ADW_NUM_CARRIER_QUEUES; i++) { adw->carriers[i].carr_offset = carriervtobo(adw, &adw->carriers[i]); adw->carriers[i].carr_ba = carriervtob(adw, &adw->carriers[i]); adw->carriers[i].areq_ba = 0; adw->carriers[i].next_ba = carriervtobo(adw, &adw->carriers[i+1]); } /* Terminal carrier. Never leaves the freelist */ adw->carriers[i].carr_offset = carriervtobo(adw, &adw->carriers[i]); adw->carriers[i].carr_ba = carriervtob(adw, &adw->carriers[i]); adw->carriers[i].areq_ba = 0; adw->carriers[i].next_ba = ~0; adw->init_level++; /* DMA tag for our acb structures */ if (bus_dma_tag_create( /* parent */ adw->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ adw->max_acbs * sizeof(struct acb), /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &adw->acb_dmat) != 0) { return (ENOMEM); } adw->init_level++; /* Allocation for our ccbs */ if (bus_dmamem_alloc(adw->acb_dmat, (void **)&adw->acbs, BUS_DMA_NOWAIT, &adw->acb_dmamap) != 0) return (ENOMEM); adw->init_level++; /* And permanently map them */ bus_dmamap_load(adw->acb_dmat, adw->acb_dmamap, adw->acbs, adw->max_acbs * sizeof(struct acb), adwmapmem, &adw->acb_busbase, /*flags*/0); /* Clear them out. */ bzero(adw->acbs, adw->max_acbs * sizeof(struct acb)); /* DMA tag for our S/G structures. We allocate in page sized chunks */ if (bus_dma_tag_create( /* parent */ adw->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ PAGE_SIZE, /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &adw->sg_dmat) != 0) { return (ENOMEM); } adw->init_level++; /* Allocate our first batch of ccbs */ if (adwallocacbs(adw) == 0) return (ENOMEM); if (adw_init_chip(adw, scsicfg1) != 0) return (ENXIO); printf("Queue Depth %d\n", adw->max_acbs); return (0); } /* * Attach all the sub-devices we can find */ int adw_attach(struct adw_softc *adw) { struct ccb_setasync csa; struct cam_devq *devq; int s; int error; error = 0; s = splcam(); /* Hook up our interrupt handler */ if ((error = bus_setup_intr(adw->device, adw->irq, INTR_TYPE_CAM | INTR_ENTROPY, adw_intr, adw, &adw->ih)) != 0) { device_printf(adw->device, "bus_setup_intr() failed: %d\n", error); goto fail; } /* Start the Risc processor now that we are fully configured. */ adw_outw(adw, ADW_RISC_CSR, ADW_RISC_CSR_RUN); /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(adw->max_acbs); if (devq == NULL) return (ENOMEM); /* * Construct our SIM entry. */ adw->sim = cam_sim_alloc(adw_action, adw_poll, "adw", adw, adw->unit, 1, adw->max_acbs, devq); if (adw->sim == NULL) { error = ENOMEM; goto fail; } /* * Register the bus. */ if (xpt_bus_register(adw->sim, 0) != CAM_SUCCESS) { cam_sim_free(adw->sim, /*free devq*/TRUE); error = ENOMEM; goto fail; } if (xpt_create_path(&adw->path, /*periph*/NULL, cam_sim_path(adw->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) == CAM_REQ_CMP) { xpt_setup_ccb(&csa.ccb_h, adw->path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = adw_async; csa.callback_arg = adw; xpt_action((union ccb *)&csa); } fail: splx(s); return (error); } void adw_intr(void *arg) { struct adw_softc *adw; u_int int_stat; adw = (struct adw_softc *)arg; if ((adw_inw(adw, ADW_CTRL_REG) & ADW_CTRL_REG_HOST_INTR) == 0) return; /* Reading the register clears the interrupt. */ int_stat = adw_inb(adw, ADW_INTR_STATUS_REG); if ((int_stat & ADW_INTR_STATUS_INTRB) != 0) { u_int intrb_code; /* Async Microcode Event */ intrb_code = adw_lram_read_8(adw, ADW_MC_INTRB_CODE); switch (intrb_code) { case ADW_ASYNC_CARRIER_READY_FAILURE: /* * The RISC missed our update of * the commandq. */ if (LIST_FIRST(&adw->pending_ccbs) != NULL) adw_tickle_risc(adw, ADW_TICKLE_A); break; case ADW_ASYNC_SCSI_BUS_RESET_DET: /* * The firmware detected a SCSI Bus reset. */ printf("Someone Reset the Bus\n"); adw_handle_bus_reset(adw, /*initiated*/FALSE); break; case ADW_ASYNC_RDMA_FAILURE: /* * Handle RDMA failure by resetting the * SCSI Bus and chip. */ #if XXX AdvResetChipAndSB(adv_dvc_varp); #endif break; case ADW_ASYNC_HOST_SCSI_BUS_RESET: /* * Host generated SCSI bus reset occurred. */ adw_handle_bus_reset(adw, /*initiated*/TRUE); break; default: printf("adw_intr: unknown async code 0x%x\n", intrb_code); break; } } /* * Run down the RequestQ. */ while ((adw->responseq->next_ba & ADW_RQ_DONE) != 0) { struct adw_carrier *free_carrier; struct acb *acb; union ccb *ccb; #if 0 printf("0x%x, 0x%x, 0x%x, 0x%x\n", adw->responseq->carr_offset, adw->responseq->carr_ba, adw->responseq->areq_ba, adw->responseq->next_ba); #endif /* * The firmware copies the adw_scsi_req_q.acb_baddr * field into the areq_ba field of the carrier. */ acb = acbbotov(adw, adw->responseq->areq_ba); /* * The least significant four bits of the next_ba * field are used as flags. Mask them out and then * advance through the list. */ free_carrier = adw->responseq; adw->responseq = carrierbotov(adw, free_carrier->next_ba & ADW_NEXT_BA_MASK); free_carrier->next_ba = adw->free_carriers->carr_offset; adw->free_carriers = free_carrier; /* Process CCB */ ccb = acb->ccb; untimeout(adwtimeout, acb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(adw->buffer_dmat, acb->dmamap, op); bus_dmamap_unload(adw->buffer_dmat, acb->dmamap); ccb->csio.resid = acb->queue.data_cnt; } else ccb->csio.resid = 0; /* Common Cases inline... */ if (acb->queue.host_status == QHSTA_NO_ERROR && (acb->queue.done_status == QD_NO_ERROR || acb->queue.done_status == QD_WITH_ERROR)) { ccb->csio.scsi_status = acb->queue.scsi_status; ccb->ccb_h.status = 0; switch (ccb->csio.scsi_status) { case SCSI_STATUS_OK: ccb->ccb_h.status |= CAM_REQ_CMP; break; case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_CMD_TERMINATED: bcopy(&acb->sense_data, &ccb->csio.sense_data, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.sense_resid = acb->queue.sense_len; /* FALLTHROUGH */ default: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); break; } adwfreeacb(adw, acb); xpt_done(ccb); } else { adwprocesserror(adw, acb); } } } static void adwprocesserror(struct adw_softc *adw, struct acb *acb) { union ccb *ccb; ccb = acb->ccb; if (acb->queue.done_status == QD_ABORTED_BY_HOST) { ccb->ccb_h.status = CAM_REQ_ABORTED; } else { switch (acb->queue.host_status) { case QHSTA_M_SEL_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case QHSTA_M_SXFR_OFF_UFLW: case QHSTA_M_SXFR_OFF_OFLW: case QHSTA_M_DATA_OVER_RUN: ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; case QHSTA_M_SXFR_DESELECTED: case QHSTA_M_UNEXPECTED_BUS_FREE: ccb->ccb_h.status = CAM_UNEXP_BUSFREE; break; case QHSTA_M_SCSI_BUS_RESET: case QHSTA_M_SCSI_BUS_RESET_UNSOL: ccb->ccb_h.status = CAM_SCSI_BUS_RESET; break; case QHSTA_M_BUS_DEVICE_RESET: ccb->ccb_h.status = CAM_BDR_SENT; break; case QHSTA_M_QUEUE_ABORTED: /* BDR or Bus Reset */ printf("Saw Queue Aborted\n"); ccb->ccb_h.status = adw->last_reset; break; case QHSTA_M_SXFR_SDMA_ERR: case QHSTA_M_SXFR_SXFR_PERR: case QHSTA_M_RDMA_PERR: ccb->ccb_h.status = CAM_UNCOR_PARITY; break; case QHSTA_M_WTM_TIMEOUT: case QHSTA_M_SXFR_WD_TMO: { /* The SCSI bus hung in a phase */ xpt_print_path(adw->path); printf("Watch Dog timer expired. Reseting bus\n"); adw_reset_bus(adw); break; } case QHSTA_M_SXFR_XFR_PH_ERR: ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_M_SXFR_UNKNOWN_ERROR: break; case QHSTA_M_BAD_CMPL_STATUS_IN: /* No command complete after a status message */ ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_M_AUTO_REQ_SENSE_FAIL: ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; break; case QHSTA_M_INVALID_DEVICE: ccb->ccb_h.status = CAM_PATH_INVALID; break; case QHSTA_M_NO_AUTO_REQ_SENSE: /* * User didn't request sense, but we got a * check condition. */ ccb->csio.scsi_status = acb->queue.scsi_status; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; default: panic("%s: Unhandled Host status error %x", adw_name(adw), acb->queue.host_status); /* NOTREACHED */ } } if ((acb->state & ACB_RECOVERY_ACB) != 0) { if (ccb->ccb_h.status == CAM_SCSI_BUS_RESET || ccb->ccb_h.status == CAM_BDR_SENT) ccb->ccb_h.status = CAM_CMD_TIMEOUT; } if (ccb->ccb_h.status != CAM_REQ_CMP) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } adwfreeacb(adw, acb); xpt_done(ccb); } static void adwtimeout(void *arg) { struct acb *acb; union ccb *ccb; struct adw_softc *adw; adw_idle_cmd_status_t status; int target_id; int s; acb = (struct acb *)arg; ccb = acb->ccb; adw = (struct adw_softc *)ccb->ccb_h.ccb_adw_ptr; xpt_print_path(ccb->ccb_h.path); printf("ACB %p - timed out\n", (void *)acb); s = splcam(); if ((acb->state & ACB_ACTIVE) == 0) { xpt_print_path(ccb->ccb_h.path); printf("ACB %p - timed out CCB already completed\n", (void *)acb); splx(s); return; } acb->state |= ACB_RECOVERY_ACB; target_id = ccb->ccb_h.target_id; /* Attempt a BDR first */ status = adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET, ccb->ccb_h.target_id); splx(s); if (status == ADW_IDLE_CMD_SUCCESS) { printf("%s: BDR Delivered. No longer in timeout\n", adw_name(adw)); adw_handle_device_reset(adw, target_id); } else { adw_reset_bus(adw); xpt_print_path(adw->path); printf("Bus Reset Delivered. No longer in timeout\n"); } } static void adw_handle_device_reset(struct adw_softc *adw, u_int target) { struct cam_path *path; cam_status error; error = xpt_create_path(&path, /*periph*/NULL, cam_sim_path(adw->sim), target, CAM_LUN_WILDCARD); if (error == CAM_REQ_CMP) { xpt_async(AC_SENT_BDR, path, NULL); xpt_free_path(path); } adw->last_reset = CAM_BDR_SENT; } static void adw_handle_bus_reset(struct adw_softc *adw, int initiated) { if (initiated) { /* * The microcode currently sets the SCSI Bus Reset signal * while handling the AscSendIdleCmd() IDLE_CMD_SCSI_RESET * command above. But the SCSI Bus Reset Hold Time in the * microcode is not deterministic (it may in fact be for less * than the SCSI Spec. minimum of 25 us). Therefore on return * the Adv Library sets the SCSI Bus Reset signal for * ADW_SCSI_RESET_HOLD_TIME_US, which is defined to be greater * than 25 us. */ u_int scsi_ctrl; scsi_ctrl = adw_inw(adw, ADW_SCSI_CTRL) & ~ADW_SCSI_CTRL_RSTOUT; adw_outw(adw, ADW_SCSI_CTRL, scsi_ctrl | ADW_SCSI_CTRL_RSTOUT); DELAY(ADW_SCSI_RESET_HOLD_TIME_US); adw_outw(adw, ADW_SCSI_CTRL, scsi_ctrl); /* * We will perform the async notification when the * SCSI Reset interrupt occurs. */ } else xpt_async(AC_BUS_RESET, adw->path, NULL); adw->last_reset = CAM_SCSI_BUS_RESET; } Index: head/sys/dev/ahb/ahb.c =================================================================== --- head/sys/dev/ahb/ahb.c (revision 116350) +++ head/sys/dev/ahb/ahb.c (revision 116351) @@ -1,1364 +1,1347 @@ /* * CAM SCSI device driver for the Adaptec 174X SCSI Host adapter * * Copyright (c) 1998 Justin T. Gibbs * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ccb_ecb_ptr spriv_ptr0 #define ccb_ahb_ptr spriv_ptr1 #define ahb_inb(ahb, port) \ bus_space_read_1((ahb)->tag, (ahb)->bsh, port) #define ahb_inl(ahb, port) \ bus_space_read_4((ahb)->tag, (ahb)->bsh, port) #define ahb_outb(ahb, port, value) \ bus_space_write_1((ahb)->tag, (ahb)->bsh, port, value) #define ahb_outl(ahb, port, value) \ bus_space_write_4((ahb)->tag, (ahb)->bsh, port, value) static const char *ahbmatch(eisa_id_t type); static struct ahb_softc *ahballoc(u_long unit, struct resource *res); static void ahbfree(struct ahb_softc *ahb); static int ahbreset(struct ahb_softc *ahb); static void ahbmapecbs(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int ahbxptattach(struct ahb_softc *ahb); static void ahbhandleimmed(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat); static void ahbcalcresid(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb); static __inline void ahbdone(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat); static void ahbintr(void *arg); static bus_dmamap_callback_t ahbexecuteecb; static void ahbaction(struct cam_sim *sim, union ccb *ccb); static void ahbpoll(struct cam_sim *sim); /* Our timeout handler */ static timeout_t ahbtimeout; static __inline struct ecb* ahbecbget(struct ahb_softc *ahb); static __inline void ahbecbfree(struct ahb_softc* ahb, struct ecb* ecb); static __inline u_int32_t ahbecbvtop(struct ahb_softc *ahb, struct ecb *ecb); static __inline struct ecb* ahbecbptov(struct ahb_softc *ahb, u_int32_t ecb_addr); static __inline u_int32_t ahbstatuspaddr(u_int32_t ecb_paddr); static __inline u_int32_t ahbsensepaddr(u_int32_t ecb_paddr); static __inline u_int32_t ahbsgpaddr(u_int32_t ecb_paddr); static __inline void ahbqueuembox(struct ahb_softc *ahb, u_int32_t mboxval, u_int attn_code); static __inline struct ecb* ahbecbget(struct ahb_softc *ahb) { struct ecb* ecb; int s; s = splcam(); if ((ecb = SLIST_FIRST(&ahb->free_ecbs)) != NULL) SLIST_REMOVE_HEAD(&ahb->free_ecbs, links); splx(s); return (ecb); } static __inline void ahbecbfree(struct ahb_softc* ahb, struct ecb* ecb) { int s; s = splcam(); ecb->state = ECB_FREE; SLIST_INSERT_HEAD(&ahb->free_ecbs, ecb, links); splx(s); } static __inline u_int32_t ahbecbvtop(struct ahb_softc *ahb, struct ecb *ecb) { return (ahb->ecb_physbase + (u_int32_t)((caddr_t)ecb - (caddr_t)ahb->ecb_array)); } static __inline struct ecb* ahbecbptov(struct ahb_softc *ahb, u_int32_t ecb_addr) { return (ahb->ecb_array + ((struct ecb*)ecb_addr - (struct ecb*)ahb->ecb_physbase)); } static __inline u_int32_t ahbstatuspaddr(u_int32_t ecb_paddr) { return (ecb_paddr + offsetof(struct ecb, status)); } static __inline u_int32_t ahbsensepaddr(u_int32_t ecb_paddr) { return (ecb_paddr + offsetof(struct ecb, sense)); } static __inline u_int32_t ahbsgpaddr(u_int32_t ecb_paddr) { return (ecb_paddr + offsetof(struct ecb, sg_list)); } static __inline void ahbqueuembox(struct ahb_softc *ahb, u_int32_t mboxval, u_int attn_code) { u_int loopmax = 300; while (--loopmax) { u_int status; status = ahb_inb(ahb, HOSTSTAT); if ((status & (HOSTSTAT_MBOX_EMPTY|HOSTSTAT_BUSY)) == HOSTSTAT_MBOX_EMPTY) break; DELAY(20); } if (loopmax == 0) panic("ahb%ld: adapter not taking commands\n", ahb->unit); ahb_outl(ahb, MBOXOUT0, mboxval); ahb_outb(ahb, ATTN, attn_code); } static const char * ahbmatch(eisa_id_t type) { switch(type & 0xfffffe00) { case EISA_DEVICE_ID_ADAPTEC_1740: return ("Adaptec 174x SCSI host adapter"); break; default: break; } return (NULL); } static int ahbprobe(device_t dev) { const char *desc; u_int32_t iobase; u_int32_t irq; u_int8_t intdef; int shared; desc = ahbmatch(eisa_get_id(dev)); if (!desc) return (ENXIO); device_set_desc(dev, desc); iobase = (eisa_get_slot(dev) * EISA_SLOT_SIZE) + AHB_EISA_SLOT_OFFSET; eisa_add_iospace(dev, iobase, AHB_EISA_IOSIZE, RESVADDR_NONE); intdef = inb(INTDEF + iobase); switch (intdef & 0x7) { case INT9: irq = 9; break; case INT10: irq = 10; break; case INT11: irq = 11; break; case INT12: irq = 12; break; case INT14: irq = 14; break; case INT15: irq = 15; break; default: printf("Adaptec 174X at slot %d: illegal " "irq setting %d\n", eisa_get_slot(dev), (intdef & 0x7)); irq = 0; break; } if (irq == 0) return ENXIO; shared = (inb(INTDEF + iobase) & INTLEVEL) ? EISA_TRIGGER_LEVEL : EISA_TRIGGER_EDGE; eisa_add_intr(dev, irq, shared); return 0; } static int ahbattach(device_t dev) { /* * find unit and check we have that many defined */ struct ahb_softc *ahb; struct ecb* next_ecb; struct resource *io = 0; struct resource *irq = 0; int rid; void *ih; rid = 0; io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (!io) { device_printf(dev, "No I/O space?!\n"); return ENOMEM; } if ((ahb = ahballoc(device_get_unit(dev), io)) == NULL) { goto error_exit2; } if (ahbreset(ahb) != 0) goto error_exit; rid = 0; irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE); if (!irq) { device_printf(dev, "Can't allocate interrupt\n"); goto error_exit; } /* * Create our DMA tags. These tags define the kinds of device * accessible memory allocations and memory mappings we will * need to perform during normal operation. */ /* DMA tag for mapping buffers into device visible space. */ /* XXX Should be a child of the EISA bus dma tag */ if (bus_dma_tag_create( /* parent */ NULL, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ MAXBSIZE, /* nsegments */ AHB_NSEG, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ BUS_DMA_ALLOCNOW, &ahb->buffer_dmat) != 0) goto error_exit; ahb->init_level++; /* DMA tag for our ccb structures and ha inquiry data */ if (bus_dma_tag_create( /* parent */ NULL, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ (AHB_NECB * sizeof(struct ecb)) + sizeof(*ahb->ha_inq_data), /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &ahb->ecb_dmat) != 0) goto error_exit; ahb->init_level++; /* Allocation for our ccbs */ if (bus_dmamem_alloc(ahb->ecb_dmat, (void **)&ahb->ecb_array, BUS_DMA_NOWAIT, &ahb->ecb_dmamap) != 0) goto error_exit; ahb->ha_inq_data = (struct ha_inquiry_data *)&ahb->ecb_array[AHB_NECB]; ahb->init_level++; /* And permanently map them */ bus_dmamap_load(ahb->ecb_dmat, ahb->ecb_dmamap, ahb->ecb_array, AHB_NSEG * sizeof(struct ecb), ahbmapecbs, ahb, /*flags*/0); ahb->init_level++; /* Allocate the buffer dmamaps for each of our ECBs */ bzero(ahb->ecb_array, (AHB_NECB * sizeof(struct ecb)) + sizeof(*ahb->ha_inq_data)); next_ecb = ahb->ecb_array; while (ahb->num_ecbs < AHB_NECB) { u_int32_t ecb_paddr; if (bus_dmamap_create(ahb->buffer_dmat, /*flags*/0, &next_ecb->dmamap)) break; ecb_paddr = ahbecbvtop(ahb, next_ecb); next_ecb->hecb.status_ptr = ahbstatuspaddr(ecb_paddr); next_ecb->hecb.sense_ptr = ahbsensepaddr(ecb_paddr); ahb->num_ecbs++; ahbecbfree(ahb, next_ecb); next_ecb++; } if (ahb->num_ecbs == 0) goto error_exit; ahb->init_level++; /* * Now that we know we own the resources we need, register * our bus with the XPT. */ if (ahbxptattach(ahb)) goto error_exit; /* Enable our interrupt */ bus_setup_intr(dev, irq, INTR_TYPE_CAM|INTR_ENTROPY, ahbintr, ahb, &ih); return (0); error_exit: /* * The board's IRQ line will not be left enabled * if we can't intialize correctly, so its safe * to release the irq. */ ahbfree(ahb); error_exit2: if (io) bus_release_resource(dev, SYS_RES_IOPORT, 0, io); if (irq) bus_release_resource(dev, SYS_RES_IRQ, 0, irq); return (-1); } static struct ahb_softc * ahballoc(u_long unit, struct resource *res) { struct ahb_softc *ahb; /* * Allocate a storage area for us */ ahb = malloc(sizeof(struct ahb_softc), M_DEVBUF, M_NOWAIT | M_ZERO); if (!ahb) { printf("ahb%ld: cannot malloc!\n", unit); return (NULL); } SLIST_INIT(&ahb->free_ecbs); LIST_INIT(&ahb->pending_ccbs); ahb->unit = unit; ahb->tag = rman_get_bustag(res); ahb->bsh = rman_get_bushandle(res); ahb->disc_permitted = ~0; ahb->tags_permitted = ~0; return (ahb); } static void ahbfree(struct ahb_softc *ahb) { switch (ahb->init_level) { default: case 4: bus_dmamap_unload(ahb->ecb_dmat, ahb->ecb_dmamap); case 3: bus_dmamem_free(ahb->ecb_dmat, ahb->ecb_array, ahb->ecb_dmamap); bus_dmamap_destroy(ahb->ecb_dmat, ahb->ecb_dmamap); case 2: bus_dma_tag_destroy(ahb->ecb_dmat); case 1: bus_dma_tag_destroy(ahb->buffer_dmat); case 0: break; } free(ahb, M_DEVBUF); } /* * reset board, If it doesn't respond, return failure */ static int ahbreset(struct ahb_softc *ahb) { int wait = 1000; /* 1 sec enough? */ int test; if ((ahb_inb(ahb, PORTADDR) & PORTADDR_ENHANCED) == 0) { printf("ahb_reset: Controller not in enhanced mode\n"); return (-1); } ahb_outb(ahb, CONTROL, CNTRL_HARD_RST); DELAY(1000); ahb_outb(ahb, CONTROL, 0); while (--wait) { DELAY(1000); if ((ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_BUSY) == 0) break; } if (wait == 0) { printf("ahbreset: No answer from aha1742 board\n"); return (-1); } if ((test = ahb_inb(ahb, MBOXIN0)) != 0) { printf("ahb_reset: self test failed, val = 0x%x\n", test); return (-1); } while (ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_INTPEND) { ahb_outb(ahb, CONTROL, CNTRL_CLRINT); DELAY(10000); } return (0); } static void ahbmapecbs(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct ahb_softc* ahb; ahb = (struct ahb_softc*)arg; ahb->ecb_physbase = segs->ds_addr; /* * Space for adapter inquiry information is on the * tail of the ecb array. */ ahb->ha_inq_physbase = ahbecbvtop(ahb, &ahb->ecb_array[AHB_NECB]); } static int ahbxptattach(struct ahb_softc *ahb) { struct cam_devq *devq; struct ecb *ecb; u_int i; /* Remeber who are we on the scsi bus */ ahb->scsi_id = ahb_inb(ahb, SCSIDEF) & HSCSIID; /* Use extended translation?? */ ahb->extended_trans = ahb_inb(ahb, RESV1) & EXTENDED_TRANS; /* Fetch adapter inquiry data */ ecb = ahbecbget(ahb); /* Always succeeds - no outstanding commands */ ecb->hecb.opcode = ECBOP_READ_HA_INQDATA; ecb->hecb.flag_word1 = FW1_SUPPRESS_URUN_ERR|FW1_ERR_STATUS_BLK_ONLY; ecb->hecb.data_ptr = ahb->ha_inq_physbase; ecb->hecb.data_len = sizeof(struct ha_inquiry_data); ecb->hecb.sense_ptr = 0; ecb->state = ECB_ACTIVE; /* Tell the adapter about this command */ ahbqueuembox(ahb, ahbecbvtop(ahb, ecb), ATTN_STARTECB|ahb->scsi_id); /* Poll for interrupt completion */ for (i = 1000; ecb->state != ECB_FREE && i != 0; i--) { ahbintr(ahb); DELAY(1000); } ahb->num_ecbs = MIN(ahb->num_ecbs, ahb->ha_inq_data->scsi_data.reserved[1]); printf("ahb%ld: %.8s %s SCSI Adapter, FW Rev. %.4s, ID=%d, %d ECBs\n", ahb->unit, ahb->ha_inq_data->scsi_data.product, (ahb->ha_inq_data->scsi_data.flags & 0x4) ? "Differential" : "Single Ended", ahb->ha_inq_data->scsi_data.revision, ahb->scsi_id, ahb->num_ecbs); /* Restore sense paddr for future CCB clients */ ecb->hecb.sense_ptr = ahbsensepaddr(ahbecbvtop(ahb, ecb)); ahbecbfree(ahb, ecb); /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(ahb->num_ecbs); if (devq == NULL) return (ENOMEM); /* * Construct our SIM entry */ ahb->sim = cam_sim_alloc(ahbaction, ahbpoll, "ahb", ahb, ahb->unit, 2, ahb->num_ecbs, devq); if (ahb->sim == NULL) { cam_simq_free(devq); return (ENOMEM); } if (xpt_bus_register(ahb->sim, 0) != CAM_SUCCESS) { cam_sim_free(ahb->sim, /*free_devq*/TRUE); return (ENXIO); } if (xpt_create_path(&ahb->path, /*periph*/NULL, cam_sim_path(ahb->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(ahb->sim)); cam_sim_free(ahb->sim, /*free_devq*/TRUE); return (ENXIO); } /* * Allow the board to generate interrupts. */ ahb_outb(ahb, INTDEF, ahb_inb(ahb, INTDEF) | INTEN); return (0); } static void ahbhandleimmed(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat) { struct ccb_hdr *ccb_h; u_int target_id; if (ahb->immed_cmd == 0) { printf("ahb%ld: Immediate Command complete with no " " pending command\n", ahb->unit); return; } target_id = intstat & INTSTAT_TARGET_MASK; ccb_h = LIST_FIRST(&ahb->pending_ccbs); while (ccb_h != NULL) { struct ecb *pending_ecb; union ccb *ccb; pending_ecb = (struct ecb *)ccb_h->ccb_ecb_ptr; ccb = pending_ecb->ccb; ccb_h = LIST_NEXT(ccb_h, sim_links.le); if (ccb->ccb_h.target_id == target_id || target_id == ahb->scsi_id) { untimeout(ahbtimeout, pending_ecb, ccb->ccb_h.timeout_ch); LIST_REMOVE(&ccb->ccb_h, sim_links.le); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) bus_dmamap_unload(ahb->buffer_dmat, pending_ecb->dmamap); if (pending_ecb == ahb->immed_ecb) ccb->ccb_h.status = CAM_CMD_TIMEOUT|CAM_RELEASE_SIMQ; else if (target_id == ahb->scsi_id) ccb->ccb_h.status = CAM_SCSI_BUS_RESET; else ccb->ccb_h.status = CAM_BDR_SENT; ahbecbfree(ahb, pending_ecb); xpt_done(ccb); } else if (ahb->immed_ecb != NULL) { /* Re-instate timeout */ ccb->ccb_h.timeout_ch = timeout(ahbtimeout, (caddr_t)pending_ecb, (ccb->ccb_h.timeout * hz) / 1000); } } if (ahb->immed_ecb != NULL) { ahb->immed_ecb = NULL; printf("ahb%ld: No longer in timeout\n", ahb->unit); } else if (target_id == ahb->scsi_id) printf("ahb%ld: SCSI Bus Reset Delivered\n", ahb->unit); else printf("ahb%ld: Bus Device Reset Delibered to target %d\n", ahb->unit, target_id); ahb->immed_cmd = 0; } static void ahbcalcresid(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb) { if (ecb->status.data_overrun != 0) { /* * Overrun Condition. The hardware doesn't * provide a meaningful byte count in this case * (the residual is always 0). Tell the XPT * layer about the error. */ ccb->ccb_h.status = CAM_DATA_RUN_ERR; } else { ccb->csio.resid = ecb->status.resid_count; if ((ecb->hecb.flag_word1 & FW1_SG_ECB) != 0) { /* * For S/G transfers, the adapter provides a pointer * to the address in the last S/G element used and a * residual for that element. So, we need to sum up * the elements that follow it in order to get a real * residual number. If we have an overrun, the residual * reported will be 0 and we already know that all S/G * segments have been exhausted, so we can skip this * step. */ ahb_sg_t *sg; int num_sg; num_sg = ecb->hecb.data_len / sizeof(ahb_sg_t); /* Find the S/G the adapter was working on */ for (sg = ecb->sg_list; num_sg != 0 && sg->addr != ecb->status.resid_addr; num_sg--, sg++) ; /* Skip it */ num_sg--; sg++; /* Sum the rest */ for (; num_sg != 0; num_sg--, sg++) ccb->csio.resid += sg->len; } /* Underruns are not errors */ ccb->ccb_h.status = CAM_REQ_CMP; } } static void ahbprocesserror(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb) { struct hardware_ecb *hecb; struct ecb_status *status; hecb = &ecb->hecb; status = &ecb->status; switch (status->ha_status) { case HS_OK: ccb->csio.scsi_status = status->scsi_status; if (status->scsi_status != 0) { ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; if (status->sense_stored) { ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.sense_resid = ccb->csio.sense_len - status->sense_len; bcopy(&ecb->sense, &ccb->csio.sense_data, status->sense_len); } } break; case HS_TARGET_NOT_ASSIGNED: ccb->ccb_h.status = CAM_PATH_INVALID; break; case HS_SEL_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case HS_DATA_RUN_ERR: ahbcalcresid(ahb, ecb, ccb); break; case HS_UNEXPECTED_BUSFREE: ccb->ccb_h.status = CAM_UNEXP_BUSFREE; break; case HS_INVALID_PHASE: ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case HS_REQUEST_SENSE_FAILED: ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; break; case HS_TAG_MSG_REJECTED: { struct ccb_trans_settings neg; xpt_print_path(ccb->ccb_h.path); printf("refuses tagged commands. Performing " "non-tagged I/O\n"); neg.flags = 0; neg.valid = CCB_TRANS_TQ_VALID; xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, /*priority*/1); xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); ahb->tags_permitted &= ~(0x01 << ccb->ccb_h.target_id); ccb->ccb_h.status = CAM_MSG_REJECT_REC; break; } case HS_FIRMWARE_LOAD_REQ: case HS_HARDWARE_ERR: /* * Tell the system that the Adapter * is no longer functional. */ ccb->ccb_h.status = CAM_NO_HBA; break; case HS_CMD_ABORTED_HOST: case HS_CMD_ABORTED_ADAPTER: case HS_ATN_TARGET_FAILED: case HS_SCSI_RESET_ADAPTER: case HS_SCSI_RESET_INCOMING: ccb->ccb_h.status = CAM_SCSI_BUS_RESET; break; case HS_INVALID_ECB_PARAM: printf("ahb%ld: opcode 0x%02x, flag_word1 0x%02x, flag_word2 0x%02x\n", ahb->unit, hecb->opcode, hecb->flag_word1, hecb->flag_word2); ccb->ccb_h.status = CAM_SCSI_BUS_RESET; break; case HS_DUP_TCB_RECEIVED: case HS_INVALID_OPCODE: case HS_INVALID_CMD_LINK: case HS_PROGRAM_CKSUM_ERROR: panic("ahb%ld: Can't happen host status %x occurred", ahb->unit, status->ha_status); break; } if (ccb->ccb_h.status != CAM_REQ_CMP) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } } static void ahbdone(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat) { struct ecb *ecb; union ccb *ccb; ecb = ahbecbptov(ahb, mbox); if ((ecb->state & ECB_ACTIVE) == 0) panic("ecb not active"); ccb = ecb->ccb; if (ccb != NULL) { untimeout(ahbtimeout, ecb, ccb->ccb_h.timeout_ch); LIST_REMOVE(&ccb->ccb_h, sim_links.le); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(ahb->buffer_dmat, ecb->dmamap, op); bus_dmamap_unload(ahb->buffer_dmat, ecb->dmamap); } if ((intstat & INTSTAT_MASK) == INTSTAT_ECB_OK) { ccb->ccb_h.status = CAM_REQ_CMP; ccb->csio.resid = 0; } else { ahbprocesserror(ahb, ecb, ccb); } ahbecbfree(ahb, ecb); xpt_done(ccb); } else { /* Non CCB Command */ if ((intstat & INTSTAT_MASK) != INTSTAT_ECB_OK) { printf("ahb%ld: Command 0%x Failed %x:%x:%x\n", ahb->unit, ecb->hecb.opcode, *((u_int16_t*)&ecb->status), ecb->status.ha_status, ecb->status.resid_count); } /* Client owns this ECB and will release it. */ } } /* * Catch an interrupt from the adaptor */ static void ahbintr(void *arg) { struct ahb_softc *ahb; u_int intstat; u_int32_t mbox; ahb = (struct ahb_softc *)arg; while (ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_INTPEND) { /* * Fetch information about this interrupt. */ intstat = ahb_inb(ahb, INTSTAT); mbox = ahb_inl(ahb, MBOXIN0); /* * Reset interrupt latch. */ ahb_outb(ahb, CONTROL, CNTRL_CLRINT); /* * Process the completed operation */ switch (intstat & INTSTAT_MASK) { case INTSTAT_ECB_OK: case INTSTAT_ECB_CMPWRETRY: case INTSTAT_ECB_CMPWERR: ahbdone(ahb, mbox, intstat); break; case INTSTAT_AEN_OCCURED: if ((intstat & INTSTAT_TARGET_MASK) == ahb->scsi_id) { /* Bus Reset */ xpt_print_path(ahb->path); switch (mbox) { case HS_SCSI_RESET_ADAPTER: printf("Host Adapter Initiated " "Bus Reset occurred\n"); break; case HS_SCSI_RESET_INCOMING: printf("Bus Reset Initiated " "by another device occurred\n"); break; } /* Notify the XPT */ xpt_async(AC_BUS_RESET, ahb->path, NULL); break; } printf("Unsupported initiator selection AEN occured\n"); break; case INTSTAT_IMMED_OK: case INTSTAT_IMMED_ERR: ahbhandleimmed(ahb, mbox, intstat); break; case INTSTAT_HW_ERR: panic("Unrecoverable hardware Error Occurred\n"); } } } static void ahbexecuteecb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct ecb *ecb; union ccb *ccb; struct ahb_softc *ahb; u_int32_t ecb_paddr; int s; ecb = (struct ecb *)arg; ccb = ecb->ccb; ahb = (struct ahb_softc *)ccb->ccb_h.ccb_ahb_ptr; if (error != 0) { if (error != EFBIG) printf("ahb%ld: Unexepected error 0x%x returned from " "bus_dmamap_load\n", ahb->unit, error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; } ahbecbfree(ahb, ecb); xpt_done(ccb); return; } ecb_paddr = ahbecbvtop(ahb, ecb); if (nseg != 0) { ahb_sg_t *sg; bus_dma_segment_t *end_seg; bus_dmasync_op_t op; end_seg = dm_segs + nseg; /* Copy the segments into our SG list */ sg = ecb->sg_list; while (dm_segs < end_seg) { sg->addr = dm_segs->ds_addr; sg->len = dm_segs->ds_len; sg++; dm_segs++; } if (nseg > 1) { ecb->hecb.flag_word1 |= FW1_SG_ECB; ecb->hecb.data_ptr = ahbsgpaddr(ecb_paddr); ecb->hecb.data_len = sizeof(ahb_sg_t) * nseg; } else { ecb->hecb.data_ptr = ecb->sg_list->addr; ecb->hecb.data_len = ecb->sg_list->len; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { /* ecb->hecb.flag_word2 |= FW2_DATA_DIR_IN; */ op = BUS_DMASYNC_PREREAD; } else { op = BUS_DMASYNC_PREWRITE; } /* ecb->hecb.flag_word2 |= FW2_CHECK_DATA_DIR; */ bus_dmamap_sync(ahb->buffer_dmat, ecb->dmamap, op); } else { ecb->hecb.data_ptr = 0; ecb->hecb.data_len = 0; } s = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(ahb->buffer_dmat, ecb->dmamap); ahbecbfree(ahb, ecb); xpt_done(ccb); splx(s); return; } ecb->state = ECB_ACTIVE; ccb->ccb_h.status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&ahb->pending_ccbs, &ccb->ccb_h, sim_links.le); /* Tell the adapter about this command */ ahbqueuembox(ahb, ecb_paddr, ATTN_STARTECB|ccb->ccb_h.target_id); ccb->ccb_h.timeout_ch = timeout(ahbtimeout, (caddr_t)ecb, (ccb->ccb_h.timeout * hz) / 1000); splx(s); } static void ahbaction(struct cam_sim *sim, union ccb *ccb) { struct ahb_softc *ahb; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahbaction\n")); ahb = (struct ahb_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct ecb *ecb; struct hardware_ecb *hecb; /* * get an ecb to use. */ if ((ecb = ahbecbget(ahb)) == NULL) { /* Should never occur */ panic("Failed to get an ecb"); } /* * So we can find the ECB when an abort is requested */ ecb->ccb = ccb; ccb->ccb_h.ccb_ecb_ptr = ecb; ccb->ccb_h.ccb_ahb_ptr = ahb; /* * Put all the arguments for the xfer in the ecb */ hecb = &ecb->hecb; hecb->opcode = ECBOP_INITIATOR_SCSI_CMD; hecb->flag_word1 = FW1_AUTO_REQUEST_SENSE | FW1_ERR_STATUS_BLK_ONLY; hecb->flag_word2 = ccb->ccb_h.target_lun | FW2_NO_RETRY_ON_BUSY; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { hecb->flag_word2 |= FW2_TAG_ENB | ((ccb->csio.tag_action & 0x3) << FW2_TAG_TYPE_SHIFT); } if ((ccb->ccb_h.flags & CAM_DIS_DISCONNECT) != 0) hecb->flag_word2 |= FW2_DISABLE_DISC; hecb->sense_len = ccb->csio.sense_len; hecb->cdb_len = ccb->csio.cdb_len; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(ccb->csio.cdb_io.cdb_ptr, hecb->cdb, hecb->cdb_len); } else { /* I guess I could map it in... */ ccb->ccb_h.status = CAM_REQ_INVALID; ahbecbfree(ahb, ecb); xpt_done(ccb); return; } } else { bcopy(ccb->csio.cdb_io.cdb_bytes, hecb->cdb, hecb->cdb_len); } /* * If we have any data to send with this command, * map it into bus space. */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer. */ if ((ccb->ccb_h.flags & CAM_DATA_PHYS)==0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load( ahb->buffer_dmat, ecb->dmamap, ccb->csio.data_ptr, ccb->csio.dxfer_len, ahbexecuteecb, ecb, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(ahb->sim, 1); ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)ccb->csio.data_ptr; seg.ds_len = ccb->csio.dxfer_len; ahbexecuteecb(ecb, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccb->ccb_h.flags & CAM_DATA_PHYS) != 0) panic("ahbaction - Physical segment " "pointers unsupported"); if ((ccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) panic("btaction - Virtual segment " "addresses unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *) ccb->csio.data_ptr; ahbexecuteecb(ecb, segs, ccb->csio.sglist_cnt, 0); } } else { ahbexecuteecb(ecb, NULL, 0, 0); } break; } case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; u_int target_mask; cts = &ccb->cts; target_mask = 0x01 << ccb->ccb_h.target_id; if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { cts->flags = 0; if ((ahb->disc_permitted & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((ahb->tags_permitted & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; cts->sync_period = 25; /* 10MHz */ if (cts->sync_period != 0) cts->sync_offset = 15; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; } xpt_done(ccb); break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ { int i; int s; s = splcam(); ahb->immed_cmd = IMMED_RESET; ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ccb->ccb_h.target_id); /* Poll for interrupt completion */ for (i = 1000; ahb->immed_cmd != 0 && i != 0; i--) { DELAY(1000); ahbintr(cam_sim_softc(sim)); } splx(s); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb > 1024 && (ahb->extended_trans != 0)) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, ahb->extended_trans); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int i; ahb->immed_cmd = IMMED_RESET; ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ahb->scsi_id); /* Poll for interrupt completion */ for (i = 1000; ahb->immed_cmd != 0 && i != 0; i--) DELAY(1000); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = 7; cpi->initiator_id = ahb->scsi_id; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } #if 0 /* Need these??? */ case XPT_IMMED_NOTIFY: /* Notify Host Target driver of event */ case XPT_NOTIFY_ACK: /* Acknowledgement of event */ #endif default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void ahbpoll(struct cam_sim *sim) { ahbintr(cam_sim_softc(sim)); } static void ahbtimeout(void *arg) { struct ecb *ecb; union ccb *ccb; struct ahb_softc *ahb; int s; ecb = (struct ecb *)arg; ccb = ecb->ccb; ahb = (struct ahb_softc *)ccb->ccb_h.ccb_ahb_ptr; xpt_print_path(ccb->ccb_h.path); printf("ECB %p - timed out\n", (void *)ecb); s = splcam(); if ((ecb->state & ECB_ACTIVE) == 0) { xpt_print_path(ccb->ccb_h.path); printf("ECB %p - timed out ECB already completed\n", (void *)ecb); splx(s); return; } /* * In order to simplify the recovery process, we ask the XPT * layer to halt the queue of new transactions and we traverse * the list of pending CCBs and remove their timeouts. This * means that the driver attempts to clear only one error * condition at a time. In general, timeouts that occur * close together are related anyway, so there is no benefit * in attempting to handle errors in parrallel. Timeouts will * be reinstated when the recovery process ends. */ if ((ecb->state & ECB_DEVICE_RESET) == 0) { struct ccb_hdr *ccb_h; if ((ecb->state & ECB_RELEASE_SIMQ) == 0) { xpt_freeze_simq(ahb->sim, /*count*/1); ecb->state |= ECB_RELEASE_SIMQ; } ccb_h = LIST_FIRST(&ahb->pending_ccbs); while (ccb_h != NULL) { struct ecb *pending_ecb; pending_ecb = (struct ecb *)ccb_h->ccb_ecb_ptr; untimeout(ahbtimeout, pending_ecb, ccb_h->timeout_ch); ccb_h = LIST_NEXT(ccb_h, sim_links.le); } /* Store for our interrupt handler */ ahb->immed_ecb = ecb; /* * Send a Bus Device Reset message: * The target that is holding up the bus may not * be the same as the one that triggered this timeout * (different commands have different timeout lengths), * but we have no way of determining this from our * timeout handler. Our strategy here is to queue a * BDR message to the target of the timed out command. * If this fails, we'll get another timeout 2 seconds * later which will attempt a bus reset. */ xpt_print_path(ccb->ccb_h.path); printf("Queuing BDR\n"); ecb->state |= ECB_DEVICE_RESET; ccb->ccb_h.timeout_ch = timeout(ahbtimeout, (caddr_t)ecb, 2 * hz); ahb->immed_cmd = IMMED_RESET; ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ccb->ccb_h.target_id); } else if ((ecb->state & ECB_SCSIBUS_RESET) != 0) { /* * Try a SCSI bus reset. We do this only if we * have already attempted to clear the condition with a BDR. */ xpt_print_path(ccb->ccb_h.path); printf("Attempting SCSI Bus reset\n"); ecb->state |= ECB_SCSIBUS_RESET; ccb->ccb_h.timeout_ch = timeout(ahbtimeout, (caddr_t)ecb, 2 * hz); ahb->immed_cmd = IMMED_RESET; ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ahb->scsi_id); } else { /* Bring out the hammer... */ ahbreset(ahb); /* Simulate the reset complete interrupt */ ahbhandleimmed(ahb, 0, ahb->scsi_id|INTSTAT_IMMED_OK); } splx(s); } static device_method_t ahb_eisa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ahbprobe), DEVMETHOD(device_attach, ahbattach), { 0, 0 } }; static driver_t ahb_eisa_driver = { "ahb", ahb_eisa_methods, 1, /* unused */ }; static devclass_t ahb_devclass; DRIVER_MODULE(ahb, eisa, ahb_eisa_driver, ahb_devclass, 0, 0); Index: head/sys/dev/aic/aic.c =================================================================== --- head/sys/dev/aic/aic.c (revision 116350) +++ head/sys/dev/aic/aic.c (revision 116351) @@ -1,1581 +1,1563 @@ /*- * Copyright (c) 1999 Luoqi Chen. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void aic_action(struct cam_sim *sim, union ccb *ccb); static void aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error); static void aic_start(struct aic_softc *aic); static void aic_select(struct aic_softc *aic); static void aic_selected(struct aic_softc *aic); static void aic_reselected(struct aic_softc *aic); static void aic_reconnect(struct aic_softc *aic, int tag); static void aic_cmd(struct aic_softc *aic); static void aic_msgin(struct aic_softc *aic); static void aic_handle_msgin(struct aic_softc *aic); static void aic_msgout(struct aic_softc *aic); static void aic_datain(struct aic_softc *aic); static void aic_dataout(struct aic_softc *aic); static void aic_done(struct aic_softc *aic, struct aic_scb *scb); static void aic_poll(struct cam_sim *sim); static void aic_timeout(void *arg); static void aic_scsi_reset(struct aic_softc *aic); static void aic_chip_reset(struct aic_softc *aic); static void aic_reset(struct aic_softc *aic, int initiate_reset); devclass_t aic_devclass; static struct aic_scb *free_scbs; static struct aic_scb * aic_get_scb(struct aic_softc *aic) { struct aic_scb *scb; int s = splcam(); if ((scb = free_scbs) != NULL) free_scbs = (struct aic_scb *)free_scbs->ccb; splx(s); return (scb); } static void aic_free_scb(struct aic_softc *aic, struct aic_scb *scb) { int s = splcam(); if ((aic->flags & AIC_RESOURCE_SHORTAGE) != 0 && (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; aic->flags &= ~AIC_RESOURCE_SHORTAGE; } scb->flags = 0; scb->ccb = (union ccb *)free_scbs; free_scbs = scb; splx(s); } static void aic_action(struct cam_sim *sim, union ccb *ccb) { struct aic_softc *aic; int s; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_action\n")); aic = (struct aic_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ { struct aic_scb *scb; if ((scb = aic_get_scb(aic)) == NULL) { s = splcam(); aic->flags |= AIC_RESOURCE_SHORTAGE; splx(s); xpt_freeze_simq(aic->sim, /*count*/1); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } scb->ccb = ccb; ccb->ccb_h.ccb_scb_ptr = scb; ccb->ccb_h.ccb_aic_ptr = aic; scb->target = ccb->ccb_h.target_id; scb->lun = ccb->ccb_h.target_lun; if (ccb->ccb_h.func_code == XPT_SCSI_IO) { scb->cmd_len = ccb->csio.cdb_len; if (ccb->ccb_h.flags & CAM_CDB_POINTER) { if (ccb->ccb_h.flags & CAM_CDB_PHYS) { ccb->ccb_h.status = CAM_REQ_INVALID; aic_free_scb(aic, scb); xpt_done(ccb); return; } scb->cmd_ptr = ccb->csio.cdb_io.cdb_ptr; } else { scb->cmd_ptr = ccb->csio.cdb_io.cdb_bytes; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) || (ccb->ccb_h.flags & CAM_DATA_PHYS)) { ccb->ccb_h.status = CAM_REQ_INVALID; aic_free_scb(aic, scb); xpt_done(ccb); return; } scb->data_ptr = ccb->csio.data_ptr; scb->data_len = ccb->csio.dxfer_len; } else { scb->data_ptr = NULL; scb->data_len = 0; } aic_execute_scb(scb, NULL, 0, 0); } else { scb->flags |= SCB_DEVICE_RESET; aic_execute_scb(scb, NULL, 0, 0); } break; } case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; struct aic_tinfo *ti; cts = &ccb->cts; ti = &aic->tinfo[ccb->ccb_h.target_id]; s = splcam(); if ((cts->valid & CCB_TRANS_DISC_VALID) != 0 && (aic->flags & AIC_DISC_ENABLE) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) ti->flags |= TINFO_DISC_ENB; else ti->flags &= ~TINFO_DISC_ENB; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) ti->flags |= TINFO_TAG_ENB; else ti->flags &= ~TINFO_TAG_ENB; } if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { ti->goal.period = cts->sync_period; if (ti->goal.period > aic->min_period) { ti->goal.period = 0; ti->goal.offset = 0; } else if (ti->goal.period < aic->max_period) ti->goal.period = aic->max_period; } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { ti->goal.offset = cts->sync_offset; if (ti->goal.offset == 0) ti->goal.period = 0; else if (ti->goal.offset > AIC_SYNC_OFFSET) ti->goal.offset = AIC_SYNC_OFFSET; } if ((ti->goal.period != ti->current.period) || (ti->goal.offset != ti->current.offset)) ti->flags |= TINFO_SDTR_NEGO; splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; struct aic_tinfo *ti; cts = &ccb->cts; ti = &aic->tinfo[ccb->ccb_h.target_id]; s = splcam(); cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if ((ti->flags & TINFO_DISC_ENB) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((ti->flags & TINFO_TAG_ENB) != 0) cts->flags |= CCB_TRANS_TAG_ENB; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { cts->sync_period = ti->current.period; cts->sync_offset = ti->current.offset; } else { cts->sync_period = ti->user.period; cts->sync_offset = ti->user.offset; } cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; splx(s); cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended = 0; - - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb >= 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ aic_reset(aic, /*initiate_reset*/TRUE); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = 7; cpi->initiator_id = aic->initiator; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct aic_scb *scb = (struct aic_scb *)arg; union ccb *ccb = scb->ccb; struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr; int s; s = splcam(); if (ccb->ccb_h.status != CAM_REQ_INPROG) { splx(s); aic_free_scb(aic, scb); xpt_done(ccb); return; } scb->flags |= SCB_ACTIVE; ccb->ccb_h.status |= CAM_SIM_QUEUED; TAILQ_INSERT_TAIL(&aic->pending_ccbs, &ccb->ccb_h, sim_links.tqe); ccb->ccb_h.timeout_ch = timeout(aic_timeout, (caddr_t)scb, (ccb->ccb_h.timeout * hz) / 1000); aic_start(aic); splx(s); } /* * Start another command if the controller is not busy. */ static void aic_start(struct aic_softc *aic) { struct ccb_hdr *ccb_h; struct aic_tinfo *ti; if (aic->state != AIC_IDLE) return; TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) { ti = &aic->tinfo[ccb_h->target_id]; if ((ti->lubusy & (1 << ccb_h->target_lun)) == 0) { TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe); aic->nexus = (struct aic_scb *)ccb_h->ccb_scb_ptr; aic_select(aic); return; } } CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_start: idle\n")); aic_outb(aic, SIMODE0, ENSELDI); aic_outb(aic, SIMODE1, ENSCSIRST); aic_outb(aic, SCSISEQ, ENRESELI); } /* * Start a selection. */ static void aic_select(struct aic_softc *aic) { struct aic_scb *scb = aic->nexus; CAM_DEBUG(scb->ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_select - ccb %p\n", scb->ccb)); aic->state = AIC_SELECTING; aic_outb(aic, DMACNTRL1, 0); aic_outb(aic, SCSIID, aic->initiator << OID_S | scb->target); aic_outb(aic, SXFRCTL1, STIMO_256ms | ENSTIMER | (aic->flags & AIC_PARITY_ENABLE ? ENSPCHK : 0)); aic_outb(aic, SIMODE0, ENSELDI|ENSELDO); aic_outb(aic, SIMODE1, ENSCSIRST|ENSELTIMO); aic_outb(aic, SCSISEQ, ENRESELI|ENSELO|ENAUTOATNO); } /* * We have successfully selected a target, prepare for the information * transfer phases. */ static void aic_selected(struct aic_softc *aic) { struct aic_scb *scb = aic->nexus; union ccb *ccb = scb->ccb; struct aic_tinfo *ti = &aic->tinfo[scb->target]; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_selected - ccb %p\n", ccb)); aic->state = AIC_HASNEXUS; if (scb->flags & SCB_DEVICE_RESET) { aic->msg_buf[0] = MSG_BUS_DEV_RESET; aic->msg_len = 1; aic->msg_outq = AIC_MSG_MSGBUF; } else { aic->msg_outq = AIC_MSG_IDENTIFY; if ((ti->flags & TINFO_TAG_ENB) != 0 && (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) aic->msg_outq |= AIC_MSG_TAG_Q; else ti->lubusy |= 1 << scb->lun; if ((ti->flags & TINFO_SDTR_NEGO) != 0) aic->msg_outq |= AIC_MSG_SDTR; } aic_outb(aic, CLRSINT0, CLRSELDO); aic_outb(aic, CLRSINT1, CLRBUSFREE); aic_outb(aic, SCSISEQ, ENAUTOATNP); aic_outb(aic, SIMODE0, 0); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); aic_outb(aic, SCSIRATE, ti->scsirate); } /* * We are re-selected by a target, save the target id and wait for the * target to further identify itself. */ static void aic_reselected(struct aic_softc *aic) { u_int8_t selid; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n")); /* * If we have started a selection, it must have lost out in * the arbitration, put the command back to the pending queue. */ if (aic->nexus) { TAILQ_INSERT_HEAD(&aic->pending_ccbs, &aic->nexus->ccb->ccb_h, sim_links.tqe); aic->nexus = NULL; } selid = aic_inb(aic, SELID) & ~(1 << aic->initiator); if (selid & (selid - 1)) { /* this should never have happened */ printf("aic_reselected: invalid selid %x\n", selid); aic_reset(aic, /*initiate_reset*/TRUE); return; } aic->state = AIC_RESELECTED; aic->target = ffs(selid) - 1; aic->lun = -1; aic_outb(aic, CLRSINT0, CLRSELDI); aic_outb(aic, CLRSINT1, CLRBUSFREE); aic_outb(aic, SIMODE0, 0); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); aic_outb(aic, SCSISEQ, ENAUTOATNP); aic_outb(aic, SCSIRATE, aic->tinfo[aic->target].scsirate); } /* * Raise ATNO to signal the target that we have a message for it. */ static __inline void aic_sched_msgout(struct aic_softc *aic, u_int8_t msg) { if (msg) { aic->msg_buf[0] = msg; aic->msg_len = 1; } aic->msg_outq |= AIC_MSG_MSGBUF; aic_outb(aic, SCSISIGO, aic_inb(aic, SCSISIGI) | ATNO); } /* * Wait for SPIORDY (SCSI PIO ready) flag, or a phase change. */ static __inline int aic_spiordy(struct aic_softc *aic) { while (!(aic_inb(aic, DMASTAT) & INTSTAT) && !(aic_inb(aic, SSTAT0) & SPIORDY)) ; return !(aic_inb(aic, DMASTAT) & INTSTAT); } /* * Reestablish a disconnected nexus. */ static void aic_reconnect(struct aic_softc *aic, int tag) { struct aic_scb *scb; struct ccb_hdr *ccb_h; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reconnect\n")); /* Find the nexus */ TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) { scb = (struct aic_scb *)ccb_h->ccb_scb_ptr; if (scb->target == aic->target && scb->lun == aic->lun && (tag == -1 || scb->tag == tag)) break; } /* ABORT if nothing is found */ if (!ccb_h) { if (tag == -1) aic_sched_msgout(aic, MSG_ABORT); else aic_sched_msgout(aic, MSG_ABORT_TAG); xpt_async(AC_UNSOL_RESEL, aic->path, NULL); return; } /* Reestablish the nexus */ TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe); aic->nexus = scb; scb->flags &= ~SCB_DISCONNECTED; aic->state = AIC_HASNEXUS; } /* * Read messages. */ static void aic_msgin(struct aic_softc *aic) { int msglen; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgin\n")); aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE); aic_outb(aic, SXFRCTL0, CHEN|SPIOEN); aic->flags &= ~AIC_DROP_MSGIN; aic->msg_len = 0; do { /* * If a parity error is detected, drop the remaining * bytes and inform the target so it could resend * the messages. */ if (aic_inb(aic, SSTAT1) & SCSIPERR) { aic_outb(aic, CLRSINT1, CLRSCSIPERR); aic->flags |= AIC_DROP_MSGIN; aic_sched_msgout(aic, MSG_PARITY_ERROR); } if ((aic->flags & AIC_DROP_MSGIN)) { aic_inb(aic, SCSIDAT); continue; } /* read the message byte without ACKing on it */ aic->msg_buf[aic->msg_len++] = aic_inb(aic, SCSIBUS); if (aic->msg_buf[0] == MSG_EXTENDED) { if (aic->msg_len < 2) { (void) aic_inb(aic, SCSIDAT); continue; } switch (aic->msg_buf[2]) { case MSG_EXT_SDTR: msglen = MSG_EXT_SDTR_LEN; break; case MSG_EXT_WDTR: msglen = MSG_EXT_WDTR_LEN; break; default: msglen = 0; break; } if (aic->msg_buf[1] != msglen) { aic->flags |= AIC_DROP_MSGIN; aic_sched_msgout(aic, MSG_MESSAGE_REJECT); } msglen += 2; } else if (aic->msg_buf[0] >= 0x20 && aic->msg_buf[0] <= 0x2f) msglen = 2; else msglen = 1; /* * If we have a complete message, handle it before the final * ACK (in case we decide to reject the message). */ if (aic->msg_len == msglen) { aic_handle_msgin(aic); aic->msg_len = 0; } /* ACK on the message byte */ (void) aic_inb(aic, SCSIDAT); } while (aic_spiordy(aic)); aic_outb(aic, SXFRCTL0, CHEN); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); } /* * Handle a message. */ static void aic_handle_msgin(struct aic_softc *aic) { struct aic_scb *scb; struct ccb_hdr *ccb_h; struct aic_tinfo *ti; struct ccb_trans_settings neg; if (aic->state == AIC_RESELECTED) { if (!MSG_ISIDENTIFY(aic->msg_buf[0])) { aic_sched_msgout(aic, MSG_MESSAGE_REJECT); return; } aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK; if (aic->tinfo[aic->target].lubusy & (1 << aic->lun)) aic_reconnect(aic, -1); else aic->state = AIC_RECONNECTING; return; } if (aic->state == AIC_RECONNECTING) { if (aic->msg_buf[0] != MSG_SIMPLE_Q_TAG) { aic_sched_msgout(aic, MSG_MESSAGE_REJECT); return; } aic_reconnect(aic, aic->msg_buf[1]); return; } switch (aic->msg_buf[0]) { case MSG_CMDCOMPLETE: { struct ccb_scsiio *csio; scb = aic->nexus; ccb_h = &scb->ccb->ccb_h; csio = &scb->ccb->csio; if ((scb->flags & SCB_SENSE) != 0) { /* auto REQUEST SENSE command */ scb->flags &= ~SCB_SENSE; csio->sense_resid = scb->data_len; if (scb->status == SCSI_STATUS_OK) { ccb_h->status |= CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID; /*scsi_sense_print(csio);*/ } else { ccb_h->status |= CAM_AUTOSENSE_FAIL; printf("ccb %p sense failed %x\n", ccb_h, scb->status); } } else { csio->scsi_status = scb->status; csio->resid = scb->data_len; if (scb->status == SCSI_STATUS_OK) { /* everything goes well */ ccb_h->status |= CAM_REQ_CMP; } else if ((ccb_h->flags & CAM_DIS_AUTOSENSE) == 0 && (csio->scsi_status == SCSI_STATUS_CHECK_COND || csio->scsi_status == SCSI_STATUS_CMD_TERMINATED)) { /* try to retrieve sense information */ scb->flags |= SCB_SENSE; aic->flags |= AIC_BUSFREE_OK; return; } else ccb_h->status |= CAM_SCSI_STATUS_ERROR; } aic_done(aic, scb); aic->flags |= AIC_BUSFREE_OK; break; } case MSG_EXTENDED: switch (aic->msg_buf[2]) { case MSG_EXT_SDTR: scb = aic->nexus; ti = &aic->tinfo[scb->target]; if (ti->flags & TINFO_SDTR_SENT) { ti->current.period = aic->msg_buf[3]; ti->current.offset = aic->msg_buf[4]; } else { ti->current.period = aic->msg_buf[3] = max(ti->goal.period, aic->msg_buf[3]); ti->current.offset = aic->msg_buf[4] = min(ti->goal.offset, aic->msg_buf[4]); /* * The target initiated the negotiation, * send back a response. */ aic_sched_msgout(aic, 0); } ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO); ti->scsirate = ti->current.offset ? ti->current.offset | ((ti->current.period * 4 + 49) / 50 - 2) << 4 : 0; aic_outb(aic, SCSIRATE, ti->scsirate); neg.sync_period = ti->goal.period = ti->current.period; neg.sync_offset = ti->goal.offset = ti->current.offset; neg.valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; ccb_h = &scb->ccb->ccb_h; xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1); xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg); break; case MSG_EXT_WDTR: default: aic_sched_msgout(aic, MSG_MESSAGE_REJECT); break; } break; case MSG_DISCONNECT: scb = aic->nexus; ccb_h = &scb->ccb->ccb_h; TAILQ_INSERT_TAIL(&aic->nexus_ccbs, ccb_h, sim_links.tqe); scb->flags |= SCB_DISCONNECTED; aic->flags |= AIC_BUSFREE_OK; aic->nexus = NULL; CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE, ("disconnected\n")); break; case MSG_MESSAGE_REJECT: switch (aic->msg_outq & -aic->msg_outq) { case AIC_MSG_TAG_Q: scb = aic->nexus; ti = &aic->tinfo[scb->target]; ti->flags &= ~TINFO_TAG_ENB; ti->lubusy |= 1 << scb->lun; break; case AIC_MSG_SDTR: scb = aic->nexus; ti = &aic->tinfo[scb->target]; ti->current.period = ti->goal.period = 0; ti->current.offset = ti->goal.offset = 0; ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO); ti->scsirate = 0; aic_outb(aic, SCSIRATE, ti->scsirate); neg.sync_period = ti->current.period; neg.sync_offset = ti->current.offset; neg.valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; ccb_h = &scb->ccb->ccb_h; xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1); xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg); break; default: break; } break; case MSG_SAVEDATAPOINTER: break; case MSG_RESTOREPOINTERS: break; case MSG_NOOP: break; default: aic_sched_msgout(aic, MSG_MESSAGE_REJECT); break; } } /* * Send messages. */ static void aic_msgout(struct aic_softc *aic) { struct aic_scb *scb; union ccb *ccb; struct aic_tinfo *ti; int msgidx = 0; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgout\n")); aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE); aic_outb(aic, SXFRCTL0, CHEN|SPIOEN); /* * If the previous phase is also the message out phase, * we need to retransmit all the messages, probably * because the target has detected a parity error during * the past transmission. */ if (aic->prev_phase == PH_MSGOUT) aic->msg_outq = aic->msg_sent; do { int q = aic->msg_outq; if (msgidx > 0 && msgidx == aic->msg_len) { /* complete message sent, start the next one */ q &= -q; aic->msg_sent |= q; aic->msg_outq ^= q; q = aic->msg_outq; msgidx = 0; } if (msgidx == 0) { /* setup the message */ switch (q & -q) { case AIC_MSG_IDENTIFY: scb = aic->nexus; ccb = scb->ccb; ti = &aic->tinfo[scb->target]; aic->msg_buf[0] = MSG_IDENTIFY(scb->lun, (ti->flags & TINFO_DISC_ENB) && !(ccb->ccb_h.flags & CAM_DIS_DISCONNECT)); aic->msg_len = 1; break; case AIC_MSG_TAG_Q: scb = aic->nexus; ccb = scb->ccb; aic->msg_buf[0] = ccb->csio.tag_action; aic->msg_buf[1] = scb->tag; aic->msg_len = 2; break; case AIC_MSG_SDTR: scb = aic->nexus; ti = &aic->tinfo[scb->target]; aic->msg_buf[0] = MSG_EXTENDED; aic->msg_buf[1] = MSG_EXT_SDTR_LEN; aic->msg_buf[2] = MSG_EXT_SDTR; aic->msg_buf[3] = ti->goal.period; aic->msg_buf[4] = ti->goal.offset; aic->msg_len = MSG_EXT_SDTR_LEN + 2; ti->flags |= TINFO_SDTR_SENT; break; case AIC_MSG_MSGBUF: /* a single message already in the buffer */ if (aic->msg_buf[0] == MSG_BUS_DEV_RESET || aic->msg_buf[0] == MSG_ABORT || aic->msg_buf[0] == MSG_ABORT_TAG) aic->flags |= AIC_BUSFREE_OK; break; } } /* * If this is the last message byte of all messages, * clear ATNO to signal transmission complete. */ if ((q & (q - 1)) == 0 && msgidx == aic->msg_len - 1) aic_outb(aic, CLRSINT1, CLRATNO); /* transmit the message byte */ aic_outb(aic, SCSIDAT, aic->msg_buf[msgidx++]); } while (aic_spiordy(aic)); aic_outb(aic, SXFRCTL0, CHEN); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); } /* * Read data bytes. */ static void aic_datain(struct aic_softc *aic) { struct aic_scb *scb = aic->nexus; u_int8_t dmastat, dmacntrl0; int n; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_datain\n")); aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE); aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN); dmacntrl0 = ENDMA; if (aic->flags & AIC_DWIO_ENABLE) dmacntrl0 |= DWORDPIO; aic_outb(aic, DMACNTRL0, dmacntrl0); while (scb->data_len > 0) { for (;;) { /* wait for the fifo to fill up or a phase change */ dmastat = aic_inb(aic, DMASTAT); if (dmastat & (INTSTAT|DFIFOFULL)) break; } if (dmastat & DFIFOFULL) { n = FIFOSIZE; } else { /* * No more data, wait for the remaining bytes in * the scsi fifo to be transfer to the host fifo. */ while (!(aic_inb(aic, SSTAT2) & SEMPTY)) ; n = aic_inb(aic, FIFOSTAT); } n = imin(scb->data_len, n); if (aic->flags & AIC_DWIO_ENABLE) { if (n >= 12) { aic_insl(aic, DMADATALONG, scb->data_ptr, n>>2); scb->data_ptr += n & ~3; scb->data_len -= n & ~3; n &= 3; } } else { if (n >= 8) { aic_insw(aic, DMADATA, scb->data_ptr, n >> 1); scb->data_ptr += n & ~1; scb->data_len -= n & ~1; n &= 1; } } if (n) { aic_outb(aic, DMACNTRL0, ENDMA|B8MODE); aic_insb(aic, DMADATA, scb->data_ptr, n); scb->data_ptr += n; scb->data_len -= n; aic_outb(aic, DMACNTRL0, dmacntrl0); } if (dmastat & INTSTAT) break; } aic_outb(aic, SXFRCTL0, CHEN); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); } /* * Send data bytes. */ static void aic_dataout(struct aic_softc *aic) { struct aic_scb *scb = aic->nexus; u_int8_t dmastat, dmacntrl0, sstat2; int n; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_dataout\n")); aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE); aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN); dmacntrl0 = ENDMA|WRITE; if (aic->flags & AIC_DWIO_ENABLE) dmacntrl0 |= DWORDPIO; aic_outb(aic, DMACNTRL0, dmacntrl0); while (scb->data_len > 0) { for (;;) { /* wait for the fifo to clear up or a phase change */ dmastat = aic_inb(aic, DMASTAT); if (dmastat & (INTSTAT|DFIFOEMP)) break; } if (dmastat & INTSTAT) break; n = imin(scb->data_len, FIFOSIZE); if (aic->flags & AIC_DWIO_ENABLE) { if (n >= 12) { aic_outsl(aic, DMADATALONG, scb->data_ptr,n>>2); scb->data_ptr += n & ~3; scb->data_len -= n & ~3; n &= 3; } } else { if (n >= 8) { aic_outsw(aic, DMADATA, scb->data_ptr, n >> 1); scb->data_ptr += n & ~1; scb->data_len -= n & ~1; n &= 1; } } if (n) { aic_outb(aic, DMACNTRL0, ENDMA|WRITE|B8MODE); aic_outsb(aic, DMADATA, scb->data_ptr, n); scb->data_ptr += n; scb->data_len -= n; aic_outb(aic, DMACNTRL0, dmacntrl0); } } for (;;) { /* wait until all bytes in the fifos are transmitted */ dmastat = aic_inb(aic, DMASTAT); sstat2 = aic_inb(aic, SSTAT2); if ((dmastat & DFIFOEMP) && (sstat2 & SEMPTY)) break; if (dmastat & INTSTAT) { /* adjust for untransmitted bytes */ n = aic_inb(aic, FIFOSTAT) + (sstat2 & 0xf); scb->data_ptr -= n; scb->data_len += n; /* clear the fifo */ aic_outb(aic, SXFRCTL0, CHEN|CLRCH); aic_outb(aic, DMACNTRL0, RSTFIFO); break; } } aic_outb(aic, SXFRCTL0, CHEN); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); } /* * Send the scsi command. */ static void aic_cmd(struct aic_softc *aic) { struct aic_scb *scb = aic->nexus; struct scsi_request_sense sense_cmd; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_cmd\n")); if (scb->flags & SCB_SENSE) { /* autosense request */ sense_cmd.opcode = REQUEST_SENSE; sense_cmd.byte2 = scb->lun << 5; sense_cmd.length = scb->ccb->csio.sense_len; sense_cmd.control = 0; sense_cmd.unused[0] = 0; sense_cmd.unused[1] = 0; scb->cmd_ptr = (u_int8_t *)&sense_cmd; scb->cmd_len = sizeof(sense_cmd); scb->data_ptr = (u_int8_t *)&scb->ccb->csio.sense_data; scb->data_len = scb->ccb->csio.sense_len; } aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE); aic_outb(aic, DMACNTRL0, ENDMA|WRITE); aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN); aic_outsw(aic, DMADATA, (u_int16_t *)scb->cmd_ptr, scb->cmd_len >> 1); while ((aic_inb(aic, SSTAT2) & SEMPTY) == 0 && (aic_inb(aic, DMASTAT) & INTSTAT) == 0) ; aic_outb(aic, SXFRCTL0, CHEN); aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT); } /* * Finish off a command. The caller is responsible to remove the ccb * from any queue. */ static void aic_done(struct aic_softc *aic, struct aic_scb *scb) { union ccb *ccb = scb->ccb; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_done - ccb %p status %x resid %d\n", ccb, ccb->ccb_h.status, ccb->csio.resid)); untimeout(aic_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch); if ((scb->flags & SCB_DEVICE_RESET) != 0 && ccb->ccb_h.func_code != XPT_RESET_DEV) { struct cam_path *path; struct ccb_hdr *ccb_h; cam_status error; error = xpt_create_path(&path, /*periph*/NULL, cam_sim_path(aic->sim), scb->target, CAM_LUN_WILDCARD); if (error == CAM_REQ_CMP) { xpt_async(AC_SENT_BDR, path, NULL); xpt_free_path(path); } ccb_h = TAILQ_FIRST(&aic->pending_ccbs); while (ccb_h != NULL) { struct aic_scb *pending_scb; pending_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr; if (ccb_h->target_id == scb->target) { ccb_h->status |= CAM_BDR_SENT; ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe); TAILQ_REMOVE(&aic->pending_ccbs, &pending_scb->ccb->ccb_h, sim_links.tqe); aic_done(aic, pending_scb); } else { ccb_h->timeout_ch = timeout(aic_timeout, (caddr_t)pending_scb, (ccb_h->timeout * hz) / 1000); ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe); } } ccb_h = TAILQ_FIRST(&aic->nexus_ccbs); while (ccb_h != NULL) { struct aic_scb *nexus_scb; nexus_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr; if (ccb_h->target_id == scb->target) { ccb_h->status |= CAM_BDR_SENT; ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe); TAILQ_REMOVE(&aic->nexus_ccbs, &nexus_scb->ccb->ccb_h, sim_links.tqe); aic_done(aic, nexus_scb); } else { ccb_h->timeout_ch = timeout(aic_timeout, (caddr_t)nexus_scb, (ccb_h->timeout * hz) / 1000); ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe); } } } if (aic->nexus == scb || scb->flags & SCB_DISCONNECTED) aic->tinfo[scb->target].lubusy &= ~(1 << scb->lun); if (aic->nexus == scb) { aic->nexus = NULL; } aic_free_scb(aic, scb); xpt_done(ccb); } static void aic_poll(struct cam_sim *sim) { aic_intr(cam_sim_softc(sim)); } static void aic_timeout(void *arg) { struct aic_scb *scb = (struct aic_scb *)arg; union ccb *ccb = scb->ccb; struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr; int s; xpt_print_path(ccb->ccb_h.path); printf("ccb %p - timed out", ccb); if (aic->nexus && aic->nexus != scb) printf(", nexus %p", aic->nexus->ccb); printf(", phase 0x%x, state %d\n", aic_inb(aic, SCSISIGI), aic->state); s = splcam(); if ((scb->flags & SCB_ACTIVE) == 0) { splx(s); xpt_print_path(ccb->ccb_h.path); printf("ccb %p - timed out already completed\n", ccb); return; } if ((scb->flags & SCB_DEVICE_RESET) == 0 && aic->nexus == scb) { struct ccb_hdr *ccb_h = &scb->ccb->ccb_h; if ((ccb_h->status & CAM_RELEASE_SIMQ) == 0) { xpt_freeze_simq(aic->sim, /*count*/1); ccb_h->status |= CAM_RELEASE_SIMQ; } TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) { untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr, ccb_h->timeout_ch); } TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) { untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr, ccb_h->timeout_ch); } scb->flags |= SCB_DEVICE_RESET; ccb->ccb_h.timeout_ch = timeout(aic_timeout, (caddr_t)scb, 5 * hz); aic_sched_msgout(aic, MSG_BUS_DEV_RESET); } else { if (aic->nexus == scb) { ccb->ccb_h.status |= CAM_CMD_TIMEOUT; aic_done(aic, scb); } aic_reset(aic, /*initiate_reset*/TRUE); } splx(s); } void aic_intr(void *arg) { struct aic_softc *aic = (struct aic_softc *)arg; u_int8_t sstat0, sstat1; union ccb *ccb; struct aic_scb *scb; if (!(aic_inb(aic, DMASTAT) & INTSTAT)) return; aic_outb(aic, DMACNTRL0, 0); sstat0 = aic_inb(aic, SSTAT0); sstat1 = aic_inb(aic, SSTAT1); if ((sstat1 & SCSIRSTI) != 0) { /* a device-initiated bus reset */ aic_outb(aic, CLRSINT1, CLRSCSIRSTI); aic_reset(aic, /*initiate_reset*/FALSE); return; } if ((sstat1 & SCSIPERR) != 0) { aic_outb(aic, CLRSINT1, CLRSCSIPERR); aic_sched_msgout(aic, MSG_PARITY_ERROR); aic_outb(aic, DMACNTRL0, INTEN); return; } if (aic_inb(aic, SSTAT4)) { aic_outb(aic, CLRSERR, CLRSYNCERR|CLRFWERR|CLRFRERR); aic_reset(aic, /*initiate_reset*/TRUE); return; } if (aic->state <= AIC_SELECTING) { if ((sstat0 & SELDI) != 0) { aic_reselected(aic); aic_outb(aic, DMACNTRL0, INTEN); return; } if ((sstat0 & SELDO) != 0) { aic_selected(aic); aic_outb(aic, DMACNTRL0, INTEN); return; } if ((sstat1 & SELTO) != 0) { scb = aic->nexus; ccb = scb->ccb; ccb->ccb_h.status = CAM_SEL_TIMEOUT; aic_done(aic, scb); while ((sstat1 & BUSFREE) == 0) sstat1 = aic_inb(aic, SSTAT1); aic->flags |= AIC_BUSFREE_OK; } } if ((sstat1 & BUSFREE) != 0) { aic_outb(aic, SCSISEQ, 0); aic_outb(aic, CLRSINT0, sstat0); aic_outb(aic, CLRSINT1, sstat1); if ((scb = aic->nexus)) { if ((aic->flags & AIC_BUSFREE_OK) == 0) { ccb = scb->ccb; ccb->ccb_h.status = CAM_UNEXP_BUSFREE; aic_done(aic, scb); } else if (scb->flags & SCB_DEVICE_RESET) { ccb = scb->ccb; if (ccb->ccb_h.func_code == XPT_RESET_DEV) { xpt_async(AC_SENT_BDR, ccb->ccb_h.path, NULL); ccb->ccb_h.status |= CAM_REQ_CMP; } else ccb->ccb_h.status |= CAM_CMD_TIMEOUT; aic_done(aic, scb); } else if (scb->flags & SCB_SENSE) { /* autosense request */ aic->flags &= ~AIC_BUSFREE_OK; aic->tinfo[scb->target].lubusy &= ~(1 << scb->lun); aic_select(aic); aic_outb(aic, DMACNTRL0, INTEN); return; } } aic->flags &= ~AIC_BUSFREE_OK; aic->state = AIC_IDLE; aic_start(aic); aic_outb(aic, DMACNTRL0, INTEN); return; } if ((sstat1 & REQINIT) != 0) { u_int8_t phase = aic_inb(aic, SCSISIGI) & PH_MASK; aic_outb(aic, SCSISIGO, phase); aic_outb(aic, CLRSINT1, CLRPHASECHG); switch (phase) { case PH_MSGOUT: aic_msgout(aic); break; case PH_MSGIN: aic_msgin(aic); break; case PH_STAT: scb = aic->nexus; ccb = scb->ccb; aic_outb(aic, DMACNTRL0, 0); aic_outb(aic, SXFRCTL0, CHEN|SPIOEN); scb->status = aic_inb(aic, SCSIDAT); aic_outb(aic, SXFRCTL0, CHEN); break; case PH_CMD: aic_cmd(aic); break; case PH_DATAIN: aic_datain(aic); break; case PH_DATAOUT: aic_dataout(aic); break; } aic->prev_phase = phase; aic_outb(aic, DMACNTRL0, INTEN); return; } printf("aic_intr: unexpected intr sstat0 %x sstat1 %x\n", sstat0, sstat1); aic_outb(aic, DMACNTRL0, INTEN); } /* * Reset ourselves. */ static void aic_chip_reset(struct aic_softc *aic) { /* * Doc. recommends to clear these two registers before * operations commence */ aic_outb(aic, SCSITEST, 0); aic_outb(aic, TEST, 0); /* Reset SCSI-FIFO and abort any transfers */ aic_outb(aic, SXFRCTL0, CHEN|CLRCH|CLRSTCNT); /* Reset HOST-FIFO */ aic_outb(aic, DMACNTRL0, RSTFIFO); aic_outb(aic, DMACNTRL1, 0); /* Disable all selection features */ aic_outb(aic, SCSISEQ, 0); aic_outb(aic, SXFRCTL1, 0); /* Disable interrupts */ aic_outb(aic, SIMODE0, 0); aic_outb(aic, SIMODE1, 0); /* Clear interrupts */ aic_outb(aic, CLRSINT0, 0x7f); aic_outb(aic, CLRSINT1, 0xef); /* Disable synchronous transfers */ aic_outb(aic, SCSIRATE, 0); /* Haven't seen ant errors (yet) */ aic_outb(aic, CLRSERR, 0x07); /* Set our SCSI-ID */ aic_outb(aic, SCSIID, aic->initiator << OID_S); aic_outb(aic, BRSTCNTRL, EISA_BRST_TIM); } /* * Reset the SCSI bus */ static void aic_scsi_reset(struct aic_softc *aic) { aic_outb(aic, SCSISEQ, SCSIRSTO); DELAY(500); aic_outb(aic, SCSISEQ, 0); DELAY(50); } /* * Reset. Abort all pending commands. */ static void aic_reset(struct aic_softc *aic, int initiate_reset) { struct ccb_hdr *ccb_h; CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reset\n")); if (initiate_reset) aic_scsi_reset(aic); aic_chip_reset(aic); xpt_async(AC_BUS_RESET, aic->path, NULL); while ((ccb_h = TAILQ_FIRST(&aic->pending_ccbs)) != NULL) { TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe); ccb_h->status |= CAM_SCSI_BUS_RESET; aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr); } while ((ccb_h = TAILQ_FIRST(&aic->nexus_ccbs)) != NULL) { TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe); ccb_h->status |= CAM_SCSI_BUS_RESET; aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr); } if (aic->nexus) { ccb_h = &aic->nexus->ccb->ccb_h; ccb_h->status |= CAM_SCSI_BUS_RESET; aic_done(aic, aic->nexus); } aic->state = AIC_IDLE; aic_outb(aic, DMACNTRL0, INTEN); } static char *aic_chip_names[] = { "AIC6260", "AIC6360", "AIC6370", "GM82C700", }; static struct { int type; char *idstring; } aic_chip_ids[] = { { AIC6360, IDSTRING_AIC6360 }, { AIC6370, IDSTRING_AIC6370 }, { GM82C700, IDSTRING_GM82C700 }, }; static void aic_init(struct aic_softc *aic) { struct aic_scb *scb; struct aic_tinfo *ti; u_int8_t porta, portb; char chip_id[33]; int i; TAILQ_INIT(&aic->pending_ccbs); TAILQ_INIT(&aic->nexus_ccbs); aic->nexus = NULL; aic->state = AIC_IDLE; aic->prev_phase = -1; aic->flags = 0; aic_chip_reset(aic); aic_scsi_reset(aic); /* determine the chip type from its ID string */ aic->chip_type = AIC6260; aic_insb(aic, ID, chip_id, sizeof(chip_id) - 1); chip_id[sizeof(chip_id) - 1] = '\0'; for (i = 0; i < sizeof(aic_chip_ids) / sizeof(aic_chip_ids[0]); i++) { if (!strcmp(chip_id, aic_chip_ids[i].idstring)) { aic->chip_type = aic_chip_ids[i].type; break; } } porta = aic_inb(aic, PORTA); portb = aic_inb(aic, PORTB); aic->initiator = PORTA_ID(porta); if (PORTA_PARITY(porta)) aic->flags |= AIC_PARITY_ENABLE; if (PORTB_DISC(portb)) aic->flags |= AIC_DISC_ENABLE; if (PORTB_DMA(portb)) aic->flags |= AIC_DMA_ENABLE; /* * We can do fast SCSI (10MHz clock rate) if bit 4 of portb * is set and we've got a 6360. The 6260 can only do standard * 5MHz SCSI. */ if (aic->chip_type > AIC6260 || aic_inb(aic, REV)) { if (PORTB_FSYNC(portb)) aic->flags |= AIC_FAST_ENABLE; aic->flags |= AIC_DWIO_ENABLE; } if (aic->flags & AIC_FAST_ENABLE) aic->max_period = AIC_FAST_SYNC_PERIOD; else aic->max_period = AIC_SYNC_PERIOD; aic->min_period = AIC_MIN_SYNC_PERIOD; free_scbs = NULL; for (i = 255; i >= 0; i--) { scb = &aic->scbs[i]; scb->tag = i; aic_free_scb(aic, scb); } for (i = 0; i < 8; i++) { if (i == aic->initiator) continue; ti = &aic->tinfo[i]; bzero(ti, sizeof(*ti)); ti->flags = TINFO_TAG_ENB; if (aic->flags & AIC_DISC_ENABLE) ti->flags |= TINFO_DISC_ENB; ti->user.period = aic->max_period; ti->user.offset = AIC_SYNC_OFFSET; ti->scsirate = 0; } aic_outb(aic, DMACNTRL0, INTEN); } int aic_probe(struct aic_softc *aic) { int i; /* Remove aic6360 from possible powerdown mode */ aic_outb(aic, DMACNTRL0, 0); #define STSIZE 16 aic_outb(aic, DMACNTRL1, 0); /* Reset stack pointer */ for (i = 0; i < STSIZE; i++) aic_outb(aic, STACK, i); /* See if we can pull out the same sequence */ aic_outb(aic, DMACNTRL1, 0); for (i = 0; i < STSIZE && aic_inb(aic, STACK) == i; i++) ; if (i != STSIZE) return (ENXIO); #undef STSIZE return (0); } int aic_attach(struct aic_softc *aic) { struct cam_devq *devq; /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(256); if (devq == NULL) return (ENOMEM); /* * Construct our SIM entry */ aic->sim = cam_sim_alloc(aic_action, aic_poll, "aic", aic, aic->unit, 2, 256, devq); if (aic->sim == NULL) { cam_simq_free(devq); return (ENOMEM); } if (xpt_bus_register(aic->sim, 0) != CAM_SUCCESS) { cam_sim_free(aic->sim, /*free_devq*/TRUE); return (ENXIO); } if (xpt_create_path(&aic->path, /*periph*/NULL, cam_sim_path(aic->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(aic->sim)); cam_sim_free(aic->sim, /*free_devq*/TRUE); return (ENXIO); } aic_init(aic); printf("aic%d: %s", aic->unit, aic_chip_names[aic->chip_type]); if (aic->flags & AIC_DMA_ENABLE) printf(", dma"); if (aic->flags & AIC_DISC_ENABLE) printf(", disconnection"); if (aic->flags & AIC_PARITY_ENABLE) printf(", parity check"); if (aic->flags & AIC_FAST_ENABLE) printf(", fast SCSI"); printf("\n"); return (0); } int aic_detach(struct aic_softc *aic) { xpt_async(AC_LOST_DEVICE, aic->path, NULL); xpt_free_path(aic->path); xpt_bus_deregister(cam_sim_path(aic->sim)); cam_sim_free(aic->sim, /*free_devq*/TRUE); return (0); } Index: head/sys/dev/aic7xxx/aic79xx_osm.c =================================================================== --- head/sys/dev/aic7xxx/aic79xx_osm.c (revision 116350) +++ head/sys/dev/aic7xxx/aic79xx_osm.c (revision 116351) @@ -1,2016 +1,2000 @@ /* * Bus independent FreeBSD shim for the aic7xxx based adaptec SCSI controllers * * Copyright (c) 1994-2002 Justin T. Gibbs. * Copyright (c) 2001-2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/aic79xx_osm.c#27 $ * * $FreeBSD$ */ #include #include #include "opt_ddb.h" #ifdef DDB #include #endif #ifndef AHD_TMODE_ENABLE #define AHD_TMODE_ENABLE 0 #endif #define ccb_scb_ptr spriv_ptr0 #if UNUSED static void ahd_dump_targcmd(struct target_cmd *cmd); #endif static int ahd_modevent(module_t mod, int type, void *data); static void ahd_action(struct cam_sim *sim, union ccb *ccb); static void ahd_set_tran_settings(struct ahd_softc *ahd, int our_id, char channel, struct ccb_trans_settings *cts); static void ahd_get_tran_settings(struct ahd_softc *ahd, int our_id, char channel, struct ccb_trans_settings *cts); static void ahd_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg); static void ahd_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error); static void ahd_poll(struct cam_sim *sim); static void ahd_setup_data(struct ahd_softc *ahd, struct cam_sim *sim, struct ccb_scsiio *csio, struct scb *scb); static void ahd_abort_ccb(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb); static int ahd_create_path(struct ahd_softc *ahd, char channel, u_int target, u_int lun, struct cam_path **path); #if NOT_YET static void ahd_set_recoveryscb(struct ahd_softc *ahd, struct scb *scb); #endif static int ahd_create_path(struct ahd_softc *ahd, char channel, u_int target, u_int lun, struct cam_path **path) { path_id_t path_id; if (channel == 'B') path_id = cam_sim_path(ahd->platform_data->sim_b); else path_id = cam_sim_path(ahd->platform_data->sim); return (xpt_create_path(path, /*periph*/NULL, path_id, target, lun)); } int ahd_map_int(struct ahd_softc *ahd) { int error; /* Hook up our interrupt handler */ error = bus_setup_intr(ahd->dev_softc, ahd->platform_data->irq, INTR_TYPE_CAM, ahd_platform_intr, ahd, &ahd->platform_data->ih); if (error != 0) device_printf(ahd->dev_softc, "bus_setup_intr() failed: %d\n", error); return (error); } /* * Attach all the sub-devices we can find */ int ahd_attach(struct ahd_softc *ahd) { char ahd_info[256]; struct ccb_setasync csa; struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; long s; int count; count = 0; sim = NULL; ahd_controller_info(ahd, ahd_info); printf("%s\n", ahd_info); ahd_lock(ahd, &s); /* * Create the device queue for our SIM(s). */ devq = cam_simq_alloc(AHD_MAX_QUEUE); if (devq == NULL) goto fail; /* * Construct our SIM entry */ sim = cam_sim_alloc(ahd_action, ahd_poll, "ahd", ahd, device_get_unit(ahd->dev_softc), 1, /*XXX*/256, devq); if (sim == NULL) { cam_simq_free(devq); goto fail; } if (xpt_bus_register(sim, /*bus_id*/0) != CAM_SUCCESS) { cam_sim_free(sim, /*free_devq*/TRUE); sim = NULL; goto fail; } if (xpt_create_path(&path, /*periph*/NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, /*free_devq*/TRUE); sim = NULL; goto fail; } xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = ahd_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); count++; fail: ahd->platform_data->sim = sim; ahd->platform_data->path = path; if (count != 0) { /* We have to wait until after any system dumps... */ ahd->platform_data->eh = EVENTHANDLER_REGISTER(shutdown_final, ahd_shutdown, ahd, SHUTDOWN_PRI_DEFAULT); ahd_intr_enable(ahd, TRUE); } ahd_unlock(ahd, &s); return (count); } /* * Catch an interrupt from the adapter */ void ahd_platform_intr(void *arg) { struct ahd_softc *ahd; ahd = (struct ahd_softc *)arg; ahd_intr(ahd); } /* * We have an scb which has been processed by the * adaptor, now we look to see how the operation * went. */ void ahd_done(struct ahd_softc *ahd, struct scb *scb) { union ccb *ccb; CAM_DEBUG(scb->io_ctx->ccb_h.path, CAM_DEBUG_TRACE, ("ahd_done - scb %d\n", SCB_GET_TAG(scb))); ccb = scb->io_ctx; LIST_REMOVE(scb, pending_links); untimeout(ahd_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(ahd->buffer_dmat, scb->dmamap, op); bus_dmamap_unload(ahd->buffer_dmat, scb->dmamap); } #ifdef AHD_TARGET_MODE if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct cam_path *ccb_path; /* * If we have finally disconnected, clean up our * pending device state. * XXX - There may be error states that cause where * we will remain connected. */ ccb_path = ccb->ccb_h.path; if (ahd->pending_device != NULL && xpt_path_comp(ahd->pending_device->path, ccb_path) == 0) { if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { ahd->pending_device = NULL; } else { xpt_print_path(ccb->ccb_h.path); printf("Still disconnected\n"); ahd_freeze_ccb(ccb); } } if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) ccb->ccb_h.status |= CAM_REQ_CMP; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ahd_free_scb(ahd, scb); xpt_done(ccb); return; } #endif /* * If the recovery SCB completes, we have to be * out of our timeout. */ if ((scb->flags & SCB_RECOVERY_SCB) != 0) { struct scb *list_scb; /* * We were able to complete the command successfully, * so reinstate the timeouts for all other pending * commands. */ LIST_FOREACH(list_scb, &ahd->pending_scbs, pending_links) { union ccb *ccb; uint64_t time; ccb = list_scb->io_ctx; if (ccb->ccb_h.timeout == CAM_TIME_INFINITY) continue; time = ccb->ccb_h.timeout; time *= hz; time /= 1000; ccb->ccb_h.timeout_ch = timeout(ahd_timeout, list_scb, time); } if (ahd_get_transaction_status(scb) == CAM_BDR_SENT || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED) ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT); ahd_print_path(ahd, scb); printf("no longer in timeout, status = %x\n", ccb->ccb_h.status); } /* Don't clobber any existing error state */ if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; } else if ((scb->flags & SCB_SENSE) != 0) { /* * We performed autosense retrieval. * * Zero any sense not transferred by the * device. The SCSI spec mandates that any * untransfered data should be assumed to be * zero. Complete the 'bounce' of sense information * through buffers accessible via bus-space by * copying it into the clients csio. */ memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); memcpy(&ccb->csio.sense_data, ahd_get_sense_buf(ahd, scb), /* XXX What size do we want to use??? */ sizeof(ccb->csio.sense_data) - ccb->csio.sense_resid); scb->io_ctx->ccb_h.status |= CAM_AUTOSNS_VALID; } else if ((scb->flags & SCB_PKT_SENSE) != 0) { struct scsi_status_iu_header *siu; u_int sense_len; int i; /* * Copy only the sense data into the provided buffer. */ siu = (struct scsi_status_iu_header *)scb->sense_data; sense_len = MIN(scsi_4btoul(siu->sense_length), sizeof(ccb->csio.sense_data)); memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); memcpy(&ccb->csio.sense_data, ahd_get_sense_buf(ahd, scb) + SIU_SENSE_OFFSET(siu), sense_len); printf("Copied %d bytes of sense data offset %d:", sense_len, SIU_SENSE_OFFSET(siu)); for (i = 0; i < sense_len; i++) printf(" 0x%x", ((uint8_t *)&ccb->csio.sense_data)[i]); printf("\n"); scb->io_ctx->ccb_h.status |= CAM_AUTOSNS_VALID; } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ahd_free_scb(ahd, scb); xpt_done(ccb); } static void ahd_action(struct cam_sim *sim, union ccb *ccb) { struct ahd_softc *ahd; #ifdef AHD_TARGET_MODE struct ahd_tmode_lstate *lstate; #endif u_int target_id; u_int our_id; long s; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahd_action\n")); ahd = (struct ahd_softc *)cam_sim_softc(sim); target_id = ccb->ccb_h.target_id; our_id = SIM_SCSI_ID(ahd, sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ #ifdef AHD_TARGET_MODE case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO:/* Continue Host Target I/O Connection*/ { struct ahd_tmode_tstate *tstate; cam_status status; status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { /* Response from the black hole device */ tstate = NULL; lstate = ahd->black_hole; } else { ccb->ccb_h.status = status; xpt_done(ccb); break; } } if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { ahd_lock(ahd, &s); SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h, sim_links.sle); ccb->ccb_h.status = CAM_REQ_INPROG; if ((ahd->flags & AHD_TQINFIFO_BLOCKED) != 0) ahd_run_tqinfifo(ahd, /*paused*/FALSE); ahd_unlock(ahd, &s); break; } /* * The target_id represents the target we attempt to * select. In target mode, this is the initiator of * the original command. */ our_id = target_id; target_id = ccb->csio.init_id; /* FALLTHROUGH */ } #endif case XPT_SCSI_IO: /* Execute the requested I/O operation */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ { struct scb *scb; struct hardware_scb *hscb; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; u_int col_idx; if ((ahd->flags & AHD_INITIATORROLE) == 0 && (ccb->ccb_h.func_code == XPT_SCSI_IO || ccb->ccb_h.func_code == XPT_RESET_DEV)) { ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); return; } /* * get an scb to use. */ ahd_lock(ahd, &s); tinfo = ahd_fetch_transinfo(ahd, 'A', our_id, target_id, &tstate); if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) == 0 || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0 || ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { col_idx = AHD_NEVER_COL_IDX; } else { col_idx = AHD_BUILD_COL_IDX(target_id, ccb->ccb_h.target_lun); } if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) { xpt_freeze_simq(sim, /*count*/1); ahd->flags |= AHD_RESOURCE_SHORTAGE; ahd_unlock(ahd, &s); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } ahd_unlock(ahd, &s); hscb = scb->hscb; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("start scb(%p)\n", scb)); scb->io_ctx = ccb; /* * So we can find the SCB when an abort is requested */ ccb->ccb_h.ccb_scb_ptr = scb; /* * Put all the arguments for the xfer in the scb */ hscb->control = 0; hscb->scsiid = BUILD_SCSIID(ahd, sim, target_id, our_id); hscb->lun = ccb->ccb_h.target_lun; if (ccb->ccb_h.func_code == XPT_RESET_DEV) { hscb->cdb_len = 0; scb->flags |= SCB_DEVICE_RESET; hscb->control |= MK_MESSAGE; hscb->task_management = SIU_TASKMGMT_LUN_RESET; ahd_execute_scb(scb, NULL, 0, 0); } else { #ifdef AHD_TARGET_MODE if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct target_data *tdata; tdata = &hscb->shared_data.tdata; if (ahd->pending_device == lstate) scb->flags |= SCB_TARGET_IMMEDIATE; hscb->control |= TARGET_SCB; tdata->target_phases = 0; if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { tdata->target_phases |= SPHASE_PENDING; tdata->scsi_status = ccb->csio.scsi_status; } if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) tdata->target_phases |= NO_DISCONNECT; tdata->initiator_tag = ahd_htole16(ccb->csio.tag_id); } #endif hscb->task_management = 0; if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) hscb->control |= ccb->csio.tag_action; ahd_setup_data(ahd, sim, &ccb->csio, scb); } break; } #ifdef AHD_TARGET_MODE case XPT_NOTIFY_ACK: case XPT_IMMED_NOTIFY: { struct ahd_tmode_tstate *tstate; struct ahd_tmode_lstate *lstate; cam_status status; status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { ccb->ccb_h.status = status; xpt_done(ccb); break; } SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h, sim_links.sle); ccb->ccb_h.status = CAM_REQ_INPROG; ahd_send_lstate_events(ahd, lstate); break; } case XPT_EN_LUN: /* Enable LUN as a target */ ahd_handle_en_lun(ahd, sim, ccb); xpt_done(ccb); break; #endif case XPT_ABORT: /* Abort the specified CCB */ { ahd_abort_ccb(ahd, sim, ccb); break; } case XPT_SET_TRAN_SETTINGS: { ahd_lock(ahd, &s); ahd_set_tran_settings(ahd, SIM_SCSI_ID(ahd, sim), SIM_CHANNEL(ahd, sim), &ccb->cts); ahd_unlock(ahd, &s); xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { ahd_lock(ahd, &s); ahd_get_tran_settings(ahd, SIM_SCSI_ID(ahd, sim), SIM_CHANNEL(ahd, sim), &ccb->cts); ahd_unlock(ahd, &s); xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - uint32_t size_mb; - uint32_t secs_per_cylinder; int extended; - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); extended = ahd->flags & AHD_EXTENDED_TRANS_A; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, extended); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int found; ahd_lock(ahd, &s); found = ahd_reset_channel(ahd, SIM_CHANNEL(ahd, sim), /*initiate reset*/TRUE); ahd_unlock(ahd, &s); if (bootverbose) { xpt_print_path(SIM_PATH(ahd, sim)); printf("SCSI bus reset delivered. " "%d SCBs aborted.\n", found); } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; if ((ahd->features & AHD_WIDE) != 0) cpi->hba_inquiry |= PI_WIDE_16; if ((ahd->features & AHD_TARGETMODE) != 0) { cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; } else { cpi->target_sprt = 0; } cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = (ahd->features & AHD_WIDE) ? 15 : 7; cpi->max_lun = AHD_NUM_LUNS - 1; cpi->initiator_id = ahd->our_id; if ((ahd->flags & AHD_RESET_BUS_A) == 0) { cpi->hba_misc |= PIM_NOBUSRESET; } cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); #ifdef AHD_NEW_TRAN_SETTINGS cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_ST; cpi->transport_version = 4; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_DT_ST; #endif cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); break; } } static void ahd_set_tran_settings(struct ahd_softc *ahd, int our_id, char channel, struct ccb_trans_settings *cts) { #ifdef AHD_NEW_TRAN_SETTINGS struct ahd_devinfo devinfo; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; uint16_t *discenable; uint16_t *tagenable; u_int update_type; scsi = &cts->proto_specific.scsi; spi = &cts->xport_specific.spi; ahd_compile_devinfo(&devinfo, SIM_SCSI_ID(ahd, sim), cts->ccb_h.target_id, cts->ccb_h.target_lun, SIM_CHANNEL(ahd, sim), ROLE_UNKNOWN); tinfo = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); update_type = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { update_type |= AHD_TRANS_GOAL; discenable = &tstate->discenable; tagenable = &tstate->tagenable; tinfo->curr.protocol_version = cts->protocol_version; tinfo->curr.transport_version = cts->transport_version; tinfo->goal.protocol_version = cts->protocol_version; tinfo->goal.transport_version = cts->transport_version; } else if (cts->type == CTS_TYPE_USER_SETTINGS) { update_type |= AHD_TRANS_USER; discenable = &ahd->user_discenable; tagenable = &ahd->user_tagenable; tinfo->user.protocol_version = cts->protocol_version; tinfo->user.transport_version = cts->transport_version; } else { cts->ccb_h.status = CAM_REQ_INVALID; return; } if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) *discenable |= devinfo.target_mask; else *discenable &= ~devinfo.target_mask; } if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) *tagenable |= devinfo.target_mask; else *tagenable &= ~devinfo.target_mask; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { ahd_validate_width(ahd, /*tinfo limit*/NULL, &spi->bus_width, ROLE_UNKNOWN); ahd_set_width(ahd, &devinfo, spi->bus_width, update_type, /*paused*/FALSE); } if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) { if (update_type == AHD_TRANS_USER) spi->ppr_options = tinfo->user.ppr_options; else spi->ppr_options = tinfo->goal.ppr_options; } if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) { if (update_type == AHD_TRANS_USER) spi->sync_offset = tinfo->user.offset; else spi->sync_offset = tinfo->goal.offset; } if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { if (update_type == AHD_TRANS_USER) spi->sync_period = tinfo->user.period; else spi->sync_period = tinfo->goal.period; } if (((spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0) || ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0)) { u_int maxsync; maxsync = AHD_SYNCRATE_MAX; if (spi->bus_width != MSG_EXT_WDTR_BUS_16_BIT) spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ; if ((*discenable & devinfo.target_mask) == 0) spi->ppr_options &= ~MSG_EXT_PPR_IU_REQ; ahd_find_syncrate(ahd, &spi->sync_period, &spi->ppr_options, maxsync); ahd_validate_offset(ahd, /*tinfo limit*/NULL, spi->sync_period, &spi->sync_offset, spi->bus_width, ROLE_UNKNOWN); /* We use a period of 0 to represent async */ if (spi->sync_offset == 0) { spi->sync_period = 0; spi->ppr_options = 0; } ahd_set_syncrate(ahd, &devinfo, spi->sync_period, spi->sync_offset, spi->ppr_options, update_type, /*paused*/FALSE); } cts->ccb_h.status = CAM_REQ_CMP; #else struct ahd_devinfo devinfo; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; uint16_t *discenable; uint16_t *tagenable; u_int update_type; ahd_compile_devinfo(&devinfo, SIM_SCSI_ID(ahd, sim), cts->ccb_h.target_id, cts->ccb_h.target_lun, SIM_CHANNEL(ahd, sim), ROLE_UNKNOWN); tinfo = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { update_type |= AHD_TRANS_GOAL; discenable = &tstate->discenable; tagenable = &tstate->tagenable; } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { update_type |= AHD_TRANS_USER; discenable = &ahd->user_discenable; tagenable = &ahd->user_tagenable; } else { cts->ccb_h.status = CAM_REQ_INVALID; return; } if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) *discenable |= devinfo.target_mask; else *discenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) *tagenable |= devinfo.target_mask; else *tagenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { ahd_validate_width(ahd, /*tinfo limit*/NULL, &cts->bus_width, ROLE_UNKNOWN); ahd_set_width(ahd, &devinfo, cts->bus_width, update_type, /*paused*/FALSE); } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { if (update_type == AHD_TRANS_USER) cts->sync_offset = tinfo->user.offset; else cts->sync_offset = tinfo->goal.offset; } if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { if (update_type == AHD_TRANS_USER) cts->sync_period = tinfo->user.period; else cts->sync_period = tinfo->goal.period; } if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) || ((cts->valid & CCB_TRANS_TQ_VALID) != 0) || ((cts->valid & CCB_TRANS_DISC_VALID) != 0)) { u_int ppr_options; u_int maxsync; maxsync = AHD_SYNCRATE_MAX; ppr_options = 0; if (cts->sync_period <= AHD_SYNCRATE_DT && cts->bus_width == MSG_EXT_WDTR_BUS_16_BIT) { ppr_options = tinfo->user.ppr_options | MSG_EXT_PPR_DT_REQ; } if ((*tagenable & devinfo.target_mask) == 0 || (*discenable & devinfo.target_mask) == 0) ppr_options &= ~MSG_EXT_PPR_IU_REQ; ahd_find_syncrate(ahd, &cts->sync_period, &ppr_options, maxsync); ahd_validate_offset(ahd, /*tinfo limit*/NULL, cts->sync_period, &cts->sync_offset, MSG_EXT_WDTR_BUS_8_BIT, ROLE_UNKNOWN); /* We use a period of 0 to represent async */ if (cts->sync_offset == 0) { cts->sync_period = 0; ppr_options = 0; } if (ppr_options != 0 && tinfo->user.transport_version >= 3) { tinfo->goal.transport_version = tinfo->user.transport_version; tinfo->curr.transport_version = tinfo->user.transport_version; } ahd_set_syncrate(ahd, &devinfo, cts->sync_period, cts->sync_offset, ppr_options, update_type, /*paused*/FALSE); } cts->ccb_h.status = CAM_REQ_CMP; #endif } static void ahd_get_tran_settings(struct ahd_softc *ahd, int our_id, char channel, struct ccb_trans_settings *cts) { #ifdef AHD_NEW_TRAN_SETTINGS struct ahd_devinfo devinfo; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; struct ahd_initiator_tinfo *targ_info; struct ahd_tmode_tstate *tstate; struct ahd_transinfo *tinfo; scsi = &cts->proto_specific.scsi; spi = &cts->xport_specific.spi; ahd_compile_devinfo(&devinfo, our_id, cts->ccb_h.target_id, cts->ccb_h.target_lun, channel, ROLE_UNKNOWN); targ_info = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); if (cts->type == CTS_TYPE_CURRENT_SETTINGS) tinfo = &targ_info->curr; else tinfo = &targ_info->user; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (cts->type == CTS_TYPE_USER_SETTINGS) { if ((ahd->user_discenable & devinfo.target_mask) != 0) spi->flags |= CTS_SPI_FLAGS_DISC_ENB; if ((ahd->user_tagenable & devinfo.target_mask) != 0) scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } else { if ((tstate->discenable & devinfo.target_mask) != 0) spi->flags |= CTS_SPI_FLAGS_DISC_ENB; if ((tstate->tagenable & devinfo.target_mask) != 0) scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } cts->protocol_version = tinfo->protocol_version; cts->transport_version = tinfo->transport_version; spi->sync_period = tinfo->period; spi->sync_offset = tinfo->offset; spi->bus_width = tinfo->width; spi->ppr_options = tinfo->ppr_options; cts->protocol = PROTO_SCSI; cts->transport = XPORT_SPI; spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_PPR_OPTIONS; if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else { scsi->valid = 0; } cts->ccb_h.status = CAM_REQ_CMP; #else struct ahd_devinfo devinfo; struct ahd_initiator_tinfo *targ_info; struct ahd_tmode_tstate *tstate; struct ahd_transinfo *tinfo; ahd_compile_devinfo(&devinfo, our_id, cts->ccb_h.target_id, cts->ccb_h.target_lun, channel, ROLE_UNKNOWN); targ_info = ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) tinfo = &targ_info->curr; else tinfo = &targ_info->user; cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) == 0) { if ((ahd->user_discenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((ahd->user_tagenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } else { if ((tstate->discenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((tstate->tagenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; cts->bus_width = tinfo->width; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID; if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) cts->valid |= CCB_TRANS_DISC_VALID|CCB_TRANS_TQ_VALID; cts->ccb_h.status = CAM_REQ_CMP; #endif } static void ahd_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) { struct ahd_softc *ahd; struct cam_sim *sim; sim = (struct cam_sim *)callback_arg; ahd = (struct ahd_softc *)cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: { struct ahd_devinfo devinfo; long s; ahd_compile_devinfo(&devinfo, SIM_SCSI_ID(ahd, sim), xpt_path_target_id(path), xpt_path_lun_id(path), SIM_CHANNEL(ahd, sim), ROLE_UNKNOWN); /* * Revert to async/narrow transfers * for the next device. */ ahd_lock(ahd, &s); ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, AHD_TRANS_GOAL|AHD_TRANS_CUR, /*paused*/FALSE); ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0, /*ppr_options*/0, AHD_TRANS_GOAL|AHD_TRANS_CUR, /*paused*/FALSE); ahd_unlock(ahd, &s); break; } default: break; } } static void ahd_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error) { struct scb *scb; union ccb *ccb; struct ahd_softc *ahd; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; u_int mask; u_long s; scb = (struct scb *)arg; ccb = scb->io_ctx; ahd = scb->ahd_softc; if (error != 0) { if (error == EFBIG) ahd_set_transaction_status(scb, CAM_REQ_TOO_BIG); else ahd_set_transaction_status(scb, CAM_REQ_CMP_ERR); if (nsegments != 0) bus_dmamap_unload(ahd->buffer_dmat, scb->dmamap); ahd_lock(ahd, &s); ahd_free_scb(ahd, scb); ahd_unlock(ahd, &s); xpt_done(ccb); return; } scb->sg_count = 0; if (nsegments != 0) { void *sg; bus_dmasync_op_t op; u_int i; /* Copy the segments into our SG list */ for (i = nsegments, sg = scb->sg_list; i > 0; i--) { sg = ahd_sg_setup(ahd, scb, sg, dm_segs->ds_addr, dm_segs->ds_len, /*last*/i == 1); dm_segs++; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(ahd->buffer_dmat, scb->dmamap, op); if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct target_data *tdata; tdata = &scb->hscb->shared_data.tdata; tdata->target_phases |= DPHASE_PENDING; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) tdata->data_phase = P_DATAOUT; else tdata->data_phase = P_DATAIN; } } ahd_lock(ahd, &s); /* * Last time we need to check if this SCB needs to * be aborted. */ if (ahd_get_transaction_status(scb) != CAM_REQ_INPROG) { if (nsegments != 0) bus_dmamap_unload(ahd->buffer_dmat, scb->dmamap); ahd_free_scb(ahd, scb); ahd_unlock(ahd, &s); xpt_done(ccb); return; } tinfo = ahd_fetch_transinfo(ahd, SCSIID_CHANNEL(ahd, scb->hscb->scsiid), SCSIID_OUR_ID(scb->hscb->scsiid), SCSIID_TARGET(ahd, scb->hscb->scsiid), &tstate); mask = SCB_GET_TARGET_MASK(ahd, scb); if ((tstate->discenable & mask) != 0 && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) scb->hscb->control |= DISCENB; if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { scb->flags |= SCB_PACKETIZED; if (scb->hscb->task_management != 0) scb->hscb->control &= ~MK_MESSAGE; } if ((ccb->ccb_h.flags & CAM_NEGOTIATE) != 0 && (tinfo->goal.width != 0 || tinfo->goal.period != 0 || tinfo->goal.ppr_options != 0)) { scb->flags |= SCB_NEGOTIATE; scb->hscb->control |= MK_MESSAGE; } else if ((tstate->auto_negotiate & mask) != 0) { scb->flags |= SCB_AUTO_NEGOTIATE; scb->hscb->control |= MK_MESSAGE; } LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); ccb->ccb_h.status |= CAM_SIM_QUEUED; if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { uint64_t time; if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) ccb->ccb_h.timeout = 5 * 1000; time = ccb->ccb_h.timeout; time *= hz; time /= 1000; ccb->ccb_h.timeout_ch = timeout(ahd_timeout, (caddr_t)scb, time); } if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) { /* Define a mapping from our tag to the SCB. */ ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb; ahd_pause(ahd); ahd_set_scbptr(ahd, SCB_GET_TAG(scb)); ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG); ahd_unpause(ahd); } else { ahd_queue_scb(ahd, scb); } ahd_unlock(ahd, &s); } static void ahd_poll(struct cam_sim *sim) { ahd_intr(cam_sim_softc(sim)); } static void ahd_setup_data(struct ahd_softc *ahd, struct cam_sim *sim, struct ccb_scsiio *csio, struct scb *scb) { struct hardware_scb *hscb; struct ccb_hdr *ccb_h; hscb = scb->hscb; ccb_h = &csio->ccb_h; csio->resid = 0; csio->sense_resid = 0; if (ccb_h->func_code == XPT_SCSI_IO) { hscb->cdb_len = csio->cdb_len; if ((ccb_h->flags & CAM_CDB_POINTER) != 0) { if (hscb->cdb_len > MAX_CDB_LEN && (ccb_h->flags & CAM_CDB_PHYS) == 0) { u_long s; /* * Should CAM start to support CDB sizes * greater than 16 bytes, we could use * the sense buffer to store the CDB. */ ahd_set_transaction_status(scb, CAM_REQ_INVALID); ahd_lock(ahd, &s); ahd_free_scb(ahd, scb); ahd_unlock(ahd, &s); xpt_done((union ccb *)csio); return; } if ((ccb_h->flags & CAM_CDB_PHYS) != 0) { hscb->shared_data.idata.cdb_from_host.cdbptr = ahd_htole64((uintptr_t)csio->cdb_io.cdb_ptr); hscb->shared_data.idata.cdb_from_host.cdblen = csio->cdb_len; hscb->cdb_len |= SCB_CDB_LEN_PTR; } else { memcpy(hscb->shared_data.idata.cdb, csio->cdb_io.cdb_ptr, hscb->cdb_len); } } else { if (hscb->cdb_len > MAX_CDB_LEN) { u_long s; ahd_set_transaction_status(scb, CAM_REQ_INVALID); ahd_lock(ahd, &s); ahd_free_scb(ahd, scb); ahd_unlock(ahd, &s); xpt_done((union ccb *)csio); return; } memcpy(hscb->shared_data.idata.cdb, csio->cdb_io.cdb_bytes, hscb->cdb_len); } } /* Only use S/G if there is a transfer */ if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) { /* We've been given a pointer to a single buffer */ if ((ccb_h->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(ahd->buffer_dmat, scb->dmamap, csio->data_ptr, csio->dxfer_len, ahd_execute_scb, scb, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(sim, /*count*/1); scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ if (csio->dxfer_len > AHD_MAXTRANSFER_SIZE) panic("ahd_setup_data - Transfer size " "larger than can device max"); seg.ds_addr = (bus_addr_t)(vm_offset_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; ahd_execute_scb(scb, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccb_h->flags & CAM_DATA_PHYS) != 0) panic("ahd_setup_data - Physical segment " "pointers unsupported"); if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0) panic("ahd_setup_data - Virtual segment " "addresses unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; ahd_execute_scb(scb, segs, csio->sglist_cnt, 0); } } else { ahd_execute_scb(scb, NULL, 0, 0); } } #if NOT_YET static void ahd_set_recoveryscb(struct ahd_softc *ahd, struct scb *scb) { if ((scb->flags & SCB_RECOVERY_SCB) == 0) { struct scb *list_scb; scb->flags |= SCB_RECOVERY_SCB; /* * Take all queued, but not sent SCBs out of the equation. * Also ensure that no new CCBs are queued to us while we * try to fix this problem. */ if ((scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { xpt_freeze_simq(SCB_GET_SIM(ahd, scb), /*count*/1); scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; } /* * Go through all of our pending SCBs and remove * any scheduled timeouts for them. We will reschedule * them after we've successfully fixed this problem. */ LIST_FOREACH(list_scb, &ahd->pending_scbs, pending_links) { union ccb *ccb; ccb = list_scb->io_ctx; untimeout(ahd_timeout, list_scb, ccb->ccb_h.timeout_ch); } } } #endif void ahd_timeout(void *arg) { struct scb *scb; struct ahd_softc *ahd; ahd_mode_state saved_modes; long s; int target; int lun; char channel; #if NOT_YET int i; int found; u_int last_phase; #endif scb = (struct scb *)arg; ahd = (struct ahd_softc *)scb->ahd_softc; ahd_lock(ahd, &s); ahd_pause_and_flushwork(ahd); saved_modes = ahd_save_modes(ahd); #if 0 ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); ahd_outb(ahd, SCSISIGO, ACKO); printf("set ACK\n"); ahd_outb(ahd, SCSISIGO, 0); printf("clearing Ack\n"); ahd_restore_modes(ahd, saved_modes); #endif if ((scb->flags & SCB_ACTIVE) == 0) { /* Previous timeout took care of me already */ printf("%s: Timedout SCB already complete. " "Interrupts may not be functioning.\n", ahd_name(ahd)); ahd_unpause(ahd); ahd_unlock(ahd, &s); return; } target = SCB_GET_TARGET(ahd, scb); channel = SCB_GET_CHANNEL(ahd, scb); lun = SCB_GET_LUN(scb); ahd_print_path(ahd, scb); printf("SCB 0x%x - timed out\n", SCB_GET_TAG(scb)); ahd_dump_card_state(ahd); ahd_reset_channel(ahd, SIM_CHANNEL(ahd, sim), /*initiate reset*/TRUE); ahd_unlock(ahd, &s); return; #if NOT_YET last_phase = ahd_inb(ahd, LASTPHASE); if (scb->sg_count > 0) { for (i = 0; i < scb->sg_count; i++) { printf("sg[%d] - Addr 0x%x : Length %d\n", i, ((struct ahd_dma_seg *)scb->sg_list)[i].addr, ((struct ahd_dma_seg *)scb->sg_list)[i].len & AHD_SG_LEN_MASK); } } if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) { /* * Been down this road before. * Do a full bus reset. */ bus_reset: ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT); found = ahd_reset_channel(ahd, channel, /*Initiate Reset*/TRUE); printf("%s: Issued Channel %c Bus Reset. " "%d SCBs aborted\n", ahd_name(ahd), channel, found); } else { /* * If we are a target, transition to bus free and report * the timeout. * * The target/initiator that is holding up the bus may not * be the same as the one that triggered this timeout * (different commands have different timeout lengths). * If the bus is idle and we are actiing as the initiator * for this request, queue a BDR message to the timed out * target. Otherwise, if the timed out transaction is * active: * Initiator transaction: * Stuff the message buffer with a BDR message and assert * ATN in the hopes that the target will let go of the bus * and go to the mesgout phase. If this fails, we'll * get another timeout 2 seconds later which will attempt * a bus reset. * * Target transaction: * Transition to BUS FREE and report the error. * It's good to be the target! */ u_int active_scb_index; u_int saved_scbptr; saved_scbptr = ahd_get_scbptr(ahd); active_scb_index = saved_scbptr; if (last_phase != P_BUSFREE && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0 && (active_scb_index < ahd->scb_data.numscbs)) { struct scb *active_scb; /* * If the active SCB is not us, assume that * the active SCB has a longer timeout than * the timedout SCB, and wait for the active * SCB to timeout. */ active_scb = ahd_lookup_scb(ahd, active_scb_index); if (active_scb != scb) { struct ccb_hdr *ccbh; uint64_t newtimeout; ahd_print_path(ahd, scb); printf("Other SCB Timeout%s", (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0 ? " again\n" : "\n"); scb->flags |= SCB_OTHERTCL_TIMEOUT; newtimeout = MAX(active_scb->io_ctx->ccb_h.timeout, scb->io_ctx->ccb_h.timeout); newtimeout *= hz; newtimeout /= 1000; ccbh = &scb->io_ctx->ccb_h; scb->io_ctx->ccb_h.timeout_ch = timeout(ahd_timeout, scb, newtimeout); ahd_unpause(ahd); ahd_unlock(ahd, &s); return; } /* It's us */ if ((scb->hscb->control & TARGET_SCB) != 0) { /* * Send back any queued up transactions * and properly record the error condition. */ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), SCB_GET_CHANNEL(ahd, scb), SCB_GET_LUN(scb), SCB_GET_TAG(scb), ROLE_TARGET, CAM_CMD_TIMEOUT); /* Will clear us from the bus */ ahd_restart(ahd); ahd_unlock(ahd, &s); return; } ahd_set_recoveryscb(ahd, active_scb); ahd_outb(ahd, MSG_OUT, HOST_MSG); ahd_outb(ahd, SCSISIGO, last_phase|ATNO); ahd_print_path(ahd, active_scb); printf("BDR message in message buffer\n"); active_scb->flags |= SCB_DEVICE_RESET; active_scb->io_ctx->ccb_h.timeout_ch = timeout(ahd_timeout, (caddr_t)active_scb, 2 * hz); ahd_unpause(ahd); } else { int disconnected; /* XXX Shouldn't panic. Just punt instead? */ if ((scb->hscb->control & TARGET_SCB) != 0) panic("Timed-out target SCB but bus idle"); if (last_phase != P_BUSFREE && (ahd_inb(ahd, SSTAT0) & TARGET) != 0) { /* XXX What happened to the SCB? */ /* Hung target selection. Goto busfree */ printf("%s: Hung target selection\n", ahd_name(ahd)); ahd_restart(ahd); ahd_unlock(ahd, &s); return; } if (ahd_search_qinfifo(ahd, target, channel, lun, SCB_GET_TAG(scb), ROLE_INITIATOR, /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; } else { disconnected = TRUE; } if (disconnected) { ahd_set_recoveryscb(ahd, scb); /* * Actually re-queue this SCB in an attempt * to select the device before it reconnects. * In either case (selection or reselection), * we will now issue a target reset to the * timed-out device. * * Set the MK_MESSAGE control bit indicating * that we desire to send a message. We * also set the disconnected flag since * in the paging case there is no guarantee * that our SCB control byte matches the * version on the card. We don't want the * sequencer to abort the command thinking * an unsolicited reselection occurred. */ scb->hscb->control |= MK_MESSAGE|DISCONNECTED; scb->flags |= SCB_DEVICE_RESET; /* * The sequencer will never re-reference the * in-core SCB. To make sure we are notified * during reslection, set the MK_MESSAGE flag * in the card's copy of the SCB. */ ahd_set_scbptr(ahd, SCB_GET_TAG(scb)); ahd_outb(ahd, SCB_CONTROL, ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); /* * Clear out any entries in the QINFIFO first * so we are the next SCB for this target * to run. */ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb), channel, SCB_GET_LUN(scb), SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); ahd_print_path(ahd, scb); printf("Queuing a BDR SCB\n"); ahd_qinfifo_requeue_tail(ahd, scb); ahd_set_scbptr(ahd, saved_scbptr); scb->io_ctx->ccb_h.timeout_ch = timeout(ahd_timeout, (caddr_t)scb, 2 * hz); ahd_unpause(ahd); } else { /* Go "immediatly" to the bus reset */ /* This shouldn't happen */ ahd_set_recoveryscb(ahd, scb); ahd_print_path(ahd, scb); printf("SCB %d: Immediate reset. " "Flags = 0x%x\n", SCB_GET_TAG(scb), scb->flags); goto bus_reset; } } } ahd_unlock(ahd, &s); #endif } static void ahd_abort_ccb(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb) { union ccb *abort_ccb; abort_ccb = ccb->cab.abort_ccb; switch (abort_ccb->ccb_h.func_code) { #ifdef AHD_TARGET_MODE case XPT_ACCEPT_TARGET_IO: case XPT_IMMED_NOTIFY: case XPT_CONT_TARGET_IO: { struct ahd_tmode_tstate *tstate; struct ahd_tmode_lstate *lstate; struct ccb_hdr_slist *list; cam_status status; status = ahd_find_tmode_devs(ahd, sim, abort_ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { ccb->ccb_h.status = status; break; } if (abort_ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) list = &lstate->accept_tios; else if (abort_ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) list = &lstate->immed_notifies; else list = NULL; if (list != NULL) { struct ccb_hdr *curelm; int found; curelm = SLIST_FIRST(list); found = 0; if (curelm == &abort_ccb->ccb_h) { found = 1; SLIST_REMOVE_HEAD(list, sim_links.sle); } else { while(curelm != NULL) { struct ccb_hdr *nextelm; nextelm = SLIST_NEXT(curelm, sim_links.sle); if (nextelm == &abort_ccb->ccb_h) { found = 1; SLIST_NEXT(curelm, sim_links.sle) = SLIST_NEXT(nextelm, sim_links.sle); break; } curelm = nextelm; } } if (found) { abort_ccb->ccb_h.status = CAM_REQ_ABORTED; xpt_done(abort_ccb); ccb->ccb_h.status = CAM_REQ_CMP; } else { xpt_print_path(abort_ccb->ccb_h.path); printf("Not found\n"); ccb->ccb_h.status = CAM_PATH_INVALID; } break; } /* FALLTHROUGH */ } #endif case XPT_SCSI_IO: /* XXX Fully implement the hard ones */ ccb->ccb_h.status = CAM_UA_ABORT; break; default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } void ahd_send_async(struct ahd_softc *ahd, char channel, u_int target, u_int lun, ac_code code, void *opt_arg) { struct ccb_trans_settings cts; struct cam_path *path; void *arg; int error; arg = NULL; error = ahd_create_path(ahd, channel, target, lun, &path); if (error != CAM_REQ_CMP) return; switch (code) { case AC_TRANSFER_NEG: { #ifdef AHD_NEW_TRAN_SETTINGS struct ccb_trans_settings_scsi *scsi; cts.type = CTS_TYPE_CURRENT_SETTINGS; scsi = &cts.proto_specific.scsi; #else cts.flags = CCB_TRANS_CURRENT_SETTINGS; #endif cts.ccb_h.path = path; cts.ccb_h.target_id = target; cts.ccb_h.target_lun = lun; ahd_get_tran_settings(ahd, ahd->our_id, channel, &cts); arg = &cts; #ifdef AHD_NEW_TRAN_SETTINGS scsi->valid &= ~CTS_SCSI_VALID_TQ; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; #else cts.valid &= ~CCB_TRANS_TQ_VALID; cts.flags &= ~CCB_TRANS_TAG_ENB; #endif if (opt_arg == NULL) break; if (*((ahd_queue_alg *)opt_arg) == AHD_QUEUE_TAGGED) #ifdef AHD_NEW_TRAN_SETTINGS scsi->flags |= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; #else cts.flags |= CCB_TRANS_TAG_ENB; cts.valid |= CCB_TRANS_TQ_VALID; #endif break; } case AC_SENT_BDR: case AC_BUS_RESET: break; default: panic("ahd_send_async: Unexpected async event"); } xpt_async(code, path, arg); xpt_free_path(path); } void ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, int enable) { } int ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg) { ahd->platform_data = malloc(sizeof(struct ahd_platform_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ahd->platform_data == NULL) return (ENOMEM); return (0); } void ahd_platform_free(struct ahd_softc *ahd) { struct ahd_platform_data *pdata; pdata = ahd->platform_data; if (pdata != NULL) { if (pdata->regs[0] != NULL) bus_release_resource(ahd->dev_softc, pdata->regs_res_type[0], pdata->regs_res_id[0], pdata->regs[0]); if (pdata->regs[1] != NULL) bus_release_resource(ahd->dev_softc, pdata->regs_res_type[1], pdata->regs_res_id[1], pdata->regs[1]); if (pdata->irq != NULL) bus_release_resource(ahd->dev_softc, pdata->irq_res_type, 0, pdata->irq); if (pdata->sim_b != NULL) { xpt_async(AC_LOST_DEVICE, pdata->path_b, NULL); xpt_free_path(pdata->path_b); xpt_bus_deregister(cam_sim_path(pdata->sim_b)); cam_sim_free(pdata->sim_b, /*free_devq*/TRUE); } if (pdata->sim != NULL) { xpt_async(AC_LOST_DEVICE, pdata->path, NULL); xpt_free_path(pdata->path); xpt_bus_deregister(cam_sim_path(pdata->sim)); cam_sim_free(pdata->sim, /*free_devq*/TRUE); } if (pdata->eh != NULL) EVENTHANDLER_DEREGISTER(shutdown_final, pdata->eh); free(ahd->platform_data, M_DEVBUF); } } int ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd) { /* We don't sort softcs under FreeBSD so report equal always */ return (0); } int ahd_detach(device_t dev) { struct ahd_softc *ahd; u_long l; u_long s; ahd_list_lock(&l); device_printf(dev, "detaching device\n"); ahd = device_get_softc(dev); ahd = ahd_find_softc(ahd); if (ahd == NULL) { device_printf(dev, "aic7xxx already detached\n"); ahd_list_unlock(&l); return (ENOENT); } ahd_lock(ahd, &s); ahd_intr_enable(ahd, FALSE); bus_teardown_intr(dev, ahd->platform_data->irq, ahd->platform_data->ih); ahd_unlock(ahd, &s); ahd_free(ahd); ahd_list_unlock(&l); return (0); } #if UNUSED static void ahd_dump_targcmd(struct target_cmd *cmd) { uint8_t *byte; uint8_t *last_byte; int i; byte = &cmd->initiator_channel; /* Debugging info for received commands */ last_byte = &cmd[1].initiator_channel; i = 0; while (byte < last_byte) { if (i == 0) printf("\t"); printf("%#x", *byte++); i++; if (i == 8) { printf("\n"); i = 0; } else { printf(", "); } } } #endif static int ahd_modevent(module_t mod, int type, void *data) { /* XXX Deal with busy status on unload. */ return 0; } static moduledata_t ahd_mod = { "ahd", ahd_modevent, NULL }; /********************************** DDB Hooks *********************************/ #ifdef DDB static struct ahd_softc *ahd_ddb_softc; static int ahd_ddb_paused; static int ahd_ddb_paused_on_entry; DB_COMMAND(ahd_set_unit, ahd_ddb_set_unit) { struct ahd_softc *list_ahd; ahd_ddb_softc = NULL; TAILQ_FOREACH(list_ahd, &ahd_tailq, links) { if (list_ahd->unit == addr) ahd_ddb_softc = list_ahd; } if (ahd_ddb_softc == NULL) db_error("No matching softc found!\n"); } DB_COMMAND(ahd_pause, ahd_ddb_pause) { if (ahd_ddb_softc == NULL) { db_error("Must set unit with ahd_set_unit first!\n"); return; } if (ahd_ddb_paused == 0) { ahd_ddb_paused++; if (ahd_is_paused(ahd_ddb_softc)) { ahd_ddb_paused_on_entry++; return; } ahd_pause(ahd_ddb_softc); } } DB_COMMAND(ahd_unpause, ahd_ddb_unpause) { if (ahd_ddb_softc == NULL) { db_error("Must set unit with ahd_set_unit first!\n"); return; } if (ahd_ddb_paused != 0) { ahd_ddb_paused = 0; if (ahd_ddb_paused_on_entry) return; ahd_unpause(ahd_ddb_softc); } else if (ahd_ddb_paused_on_entry != 0) { /* Two unpauses to clear a paused on entry. */ ahd_ddb_paused_on_entry = 0; ahd_unpause(ahd_ddb_softc); } } DB_COMMAND(ahd_in, ahd_ddb_in) { int c; int size; if (ahd_ddb_softc == NULL) { db_error("Must set unit with ahd_set_unit first!\n"); return; } if (have_addr == 0) return; size = 1; while ((c = *modif++) != '\0') { switch (c) { case 'b': size = 1; break; case 'w': size = 2; break; case 'l': size = 4; break; } } if (count <= 0) count = 1; while (--count >= 0) { db_printf("%04lx (M)%x: \t", (u_long)addr, ahd_inb(ahd_ddb_softc, MODE_PTR)); switch (size) { case 1: db_printf("%02x\n", ahd_inb(ahd_ddb_softc, addr)); break; case 2: db_printf("%04x\n", ahd_inw(ahd_ddb_softc, addr)); break; case 4: db_printf("%08x\n", ahd_inl(ahd_ddb_softc, addr)); break; } } } DB_SET(ahd_out, ahd_ddb_out, db_cmd_set, CS_MORE, NULL) { db_expr_t old_value; db_expr_t new_value; int size; if (ahd_ddb_softc == NULL) { db_error("Must set unit with ahd_set_unit first!\n"); return; } switch (modif[0]) { case '\0': case 'b': size = 1; break; case 'h': size = 2; break; case 'l': size = 4; break; default: db_error("Unknown size\n"); return; } while (db_expression(&new_value)) { switch (size) { default: case 1: old_value = ahd_inb(ahd_ddb_softc, addr); ahd_outb(ahd_ddb_softc, addr, new_value); break; case 2: old_value = ahd_inw(ahd_ddb_softc, addr); ahd_outw(ahd_ddb_softc, addr, new_value); break; case 4: old_value = ahd_inl(ahd_ddb_softc, addr); ahd_outl(ahd_ddb_softc, addr, new_value); break; } db_printf("%04lx (M)%x: \t0x%lx\t=\t0x%lx", (u_long)addr, ahd_inb(ahd_ddb_softc, MODE_PTR), (u_long)old_value, (u_long)new_value); addr += size; } db_skip_to_eol(); } #endif DECLARE_MODULE(ahd, ahd_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_DEPEND(ahd, cam, 1, 1, 1); MODULE_VERSION(ahd, 1); Index: head/sys/dev/aic7xxx/aic7xxx_osm.c =================================================================== --- head/sys/dev/aic7xxx/aic7xxx_osm.c (revision 116350) +++ head/sys/dev/aic7xxx/aic7xxx_osm.c (revision 116351) @@ -1,1973 +1,1957 @@ /* * Bus independent FreeBSD shim for the aic7xxx based adaptec SCSI controllers * * Copyright (c) 1994-2001 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/aic7xxx_osm.c#13 $ * * $FreeBSD$ */ #include #include #ifndef AHC_TMODE_ENABLE #define AHC_TMODE_ENABLE 0 #endif #define ccb_scb_ptr spriv_ptr0 devclass_t ahc_devclass; #if UNUSED static void ahc_dump_targcmd(struct target_cmd *cmd); #endif static int ahc_modevent(module_t mod, int type, void *data); static void ahc_action(struct cam_sim *sim, union ccb *ccb); static void ahc_get_tran_settings(struct ahc_softc *ahc, int our_id, char channel, struct ccb_trans_settings *cts); static void ahc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg); static void ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error); static void ahc_poll(struct cam_sim *sim); static void ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, struct ccb_scsiio *csio, struct scb *scb); static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb); static int ahc_create_path(struct ahc_softc *ahc, char channel, u_int target, u_int lun, struct cam_path **path); static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb); static int ahc_create_path(struct ahc_softc *ahc, char channel, u_int target, u_int lun, struct cam_path **path) { path_id_t path_id; if (channel == 'B') path_id = cam_sim_path(ahc->platform_data->sim_b); else path_id = cam_sim_path(ahc->platform_data->sim); return (xpt_create_path(path, /*periph*/NULL, path_id, target, lun)); } int ahc_map_int(struct ahc_softc *ahc) { int error; /* Hook up our interrupt handler */ error = bus_setup_intr(ahc->dev_softc, ahc->platform_data->irq, INTR_TYPE_CAM, ahc_platform_intr, ahc, &ahc->platform_data->ih); if (error != 0) device_printf(ahc->dev_softc, "bus_setup_intr() failed: %d\n", error); return (error); } /* * Attach all the sub-devices we can find */ int ahc_attach(struct ahc_softc *ahc) { char ahc_info[256]; struct ccb_setasync csa; struct cam_devq *devq; int bus_id; int bus_id2; struct cam_sim *sim; struct cam_sim *sim2; struct cam_path *path; struct cam_path *path2; long s; int count; count = 0; sim = NULL; sim2 = NULL; ahc_controller_info(ahc, ahc_info); printf("%s\n", ahc_info); ahc_lock(ahc, &s); /* * Attach secondary channel first if the user has * declared it the primary channel. */ if ((ahc->features & AHC_TWIN) != 0 && (ahc->flags & AHC_PRIMARY_CHANNEL) != 0) { bus_id = 1; bus_id2 = 0; } else { bus_id = 0; bus_id2 = 1; } /* * Create the device queue for our SIM(s). */ devq = cam_simq_alloc(AHC_MAX_QUEUE); if (devq == NULL) goto fail; /* * Construct our first channel SIM entry */ sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), 1, AHC_MAX_QUEUE, devq); if (sim == NULL) { cam_simq_free(devq); goto fail; } if (xpt_bus_register(sim, bus_id) != CAM_SUCCESS) { cam_sim_free(sim, /*free_devq*/TRUE); sim = NULL; goto fail; } if (xpt_create_path(&path, /*periph*/NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, /*free_devq*/TRUE); sim = NULL; goto fail; } xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = ahc_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); count++; if (ahc->features & AHC_TWIN) { sim2 = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, device_get_unit(ahc->dev_softc), 1, AHC_MAX_QUEUE, devq); if (sim2 == NULL) { printf("ahc_attach: Unable to attach second " "bus due to resource shortage"); goto fail; } if (xpt_bus_register(sim2, bus_id2) != CAM_SUCCESS) { printf("ahc_attach: Unable to attach second " "bus due to resource shortage"); /* * We do not want to destroy the device queue * because the first bus is using it. */ cam_sim_free(sim2, /*free_devq*/FALSE); goto fail; } if (xpt_create_path(&path2, /*periph*/NULL, cam_sim_path(sim2), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim2)); cam_sim_free(sim2, /*free_devq*/FALSE); sim2 = NULL; goto fail; } xpt_setup_ccb(&csa.ccb_h, path2, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = ahc_async; csa.callback_arg = sim2; xpt_action((union ccb *)&csa); count++; } fail: if ((ahc->features & AHC_TWIN) != 0 && (ahc->flags & AHC_PRIMARY_CHANNEL) != 0) { ahc->platform_data->sim_b = sim; ahc->platform_data->path_b = path; ahc->platform_data->sim = sim2; ahc->platform_data->path = path2; } else { ahc->platform_data->sim = sim; ahc->platform_data->path = path; ahc->platform_data->sim_b = sim2; ahc->platform_data->path_b = path2; } if (count != 0) { /* We have to wait until after any system dumps... */ ahc->platform_data->eh = EVENTHANDLER_REGISTER(shutdown_final, ahc_shutdown, ahc, SHUTDOWN_PRI_DEFAULT); ahc_intr_enable(ahc, TRUE); } ahc_unlock(ahc, &s); return (count); } /* * Catch an interrupt from the adapter */ void ahc_platform_intr(void *arg) { struct ahc_softc *ahc; ahc = (struct ahc_softc *)arg; ahc_intr(ahc); } /* * We have an scb which has been processed by the * adaptor, now we look to see how the operation * went. */ void ahc_done(struct ahc_softc *ahc, struct scb *scb) { union ccb *ccb; CAM_DEBUG(scb->io_ctx->ccb_h.path, CAM_DEBUG_TRACE, ("ahc_done - scb %d\n", scb->hscb->tag)); ccb = scb->io_ctx; LIST_REMOVE(scb, pending_links); if ((scb->flags & SCB_UNTAGGEDQ) != 0) { struct scb_tailq *untagged_q; int target_offset; target_offset = SCB_GET_TARGET_OFFSET(ahc, scb); untagged_q = &ahc->untagged_queues[target_offset]; TAILQ_REMOVE(untagged_q, scb, links.tqe); scb->flags &= ~SCB_UNTAGGEDQ; ahc_run_untagged_queue(ahc, untagged_q); } untimeout(ahc_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(ahc->buffer_dmat, scb->dmamap, op); bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); } if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct cam_path *ccb_path; /* * If we have finally disconnected, clean up our * pending device state. * XXX - There may be error states that cause where * we will remain connected. */ ccb_path = ccb->ccb_h.path; if (ahc->pending_device != NULL && xpt_path_comp(ahc->pending_device->path, ccb_path) == 0) { if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { ahc->pending_device = NULL; } else { if (bootverbose) { xpt_print_path(ccb->ccb_h.path); printf("Still connected\n"); } ahc_freeze_ccb(ccb); } } if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) ccb->ccb_h.status |= CAM_REQ_CMP; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ahc_free_scb(ahc, scb); xpt_done(ccb); return; } /* * If the recovery SCB completes, we have to be * out of our timeout. */ if ((scb->flags & SCB_RECOVERY_SCB) != 0) { struct scb *list_scb; /* * We were able to complete the command successfully, * so reinstate the timeouts for all other pending * commands. */ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) { union ccb *ccb; uint64_t time; ccb = list_scb->io_ctx; if (ccb->ccb_h.timeout == CAM_TIME_INFINITY) continue; time = ccb->ccb_h.timeout; time *= hz; time /= 1000; ccb->ccb_h.timeout_ch = timeout(ahc_timeout, list_scb, time); } if (ahc_get_transaction_status(scb) == CAM_BDR_SENT || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED) ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT); ahc_print_path(ahc, scb); printf("no longer in timeout, status = %x\n", ccb->ccb_h.status); } /* Don't clobber any existing error state */ if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; } else if ((scb->flags & SCB_SENSE) != 0) { /* * We performed autosense retrieval. * * Zero any sense not transferred by the * device. The SCSI spec mandates that any * untransfered data should be assumed to be * zero. Complete the 'bounce' of sense information * through buffers accessible via bus-space by * copying it into the clients csio. */ memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); memcpy(&ccb->csio.sense_data, ahc_get_sense_buf(ahc, scb), (ahc_le32toh(scb->sg_list->len) & AHC_SG_LEN_MASK) - ccb->csio.sense_resid); scb->io_ctx->ccb_h.status |= CAM_AUTOSNS_VALID; } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ahc_free_scb(ahc, scb); xpt_done(ccb); } static void ahc_action(struct cam_sim *sim, union ccb *ccb) { struct ahc_softc *ahc; struct ahc_tmode_lstate *lstate; u_int target_id; u_int our_id; long s; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahc_action\n")); ahc = (struct ahc_softc *)cam_sim_softc(sim); target_id = ccb->ccb_h.target_id; our_id = SIM_SCSI_ID(ahc, sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO:/* Continue Host Target I/O Connection*/ { struct ahc_tmode_tstate *tstate; cam_status status; status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { /* Response from the black hole device */ tstate = NULL; lstate = ahc->black_hole; } else { ccb->ccb_h.status = status; xpt_done(ccb); break; } } if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { ahc_lock(ahc, &s); SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h, sim_links.sle); ccb->ccb_h.status = CAM_REQ_INPROG; if ((ahc->flags & AHC_TQINFIFO_BLOCKED) != 0) ahc_run_tqinfifo(ahc, /*paused*/FALSE); ahc_unlock(ahc, &s); break; } /* * The target_id represents the target we attempt to * select. In target mode, this is the initiator of * the original command. */ our_id = target_id; target_id = ccb->csio.init_id; /* FALLTHROUGH */ } case XPT_SCSI_IO: /* Execute the requested I/O operation */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ { struct scb *scb; struct hardware_scb *hscb; if ((ahc->flags & AHC_INITIATORROLE) == 0 && (ccb->ccb_h.func_code == XPT_SCSI_IO || ccb->ccb_h.func_code == XPT_RESET_DEV)) { ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); return; } /* * get an scb to use. */ ahc_lock(ahc, &s); if ((scb = ahc_get_scb(ahc)) == NULL) { xpt_freeze_simq(sim, /*count*/1); ahc->flags |= AHC_RESOURCE_SHORTAGE; ahc_unlock(ahc, &s); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } ahc_unlock(ahc, &s); hscb = scb->hscb; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("start scb(%p)\n", scb)); scb->io_ctx = ccb; /* * So we can find the SCB when an abort is requested */ ccb->ccb_h.ccb_scb_ptr = scb; /* * Put all the arguments for the xfer in the scb */ hscb->control = 0; hscb->scsiid = BUILD_SCSIID(ahc, sim, target_id, our_id); hscb->lun = ccb->ccb_h.target_lun; if (ccb->ccb_h.func_code == XPT_RESET_DEV) { hscb->cdb_len = 0; scb->flags |= SCB_DEVICE_RESET; hscb->control |= MK_MESSAGE; ahc_execute_scb(scb, NULL, 0, 0); } else { if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct target_data *tdata; tdata = &hscb->shared_data.tdata; if (ahc->pending_device == lstate) scb->flags |= SCB_TARGET_IMMEDIATE; hscb->control |= TARGET_SCB; scb->flags |= SCB_TARGET_SCB; tdata->target_phases = 0; if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { tdata->target_phases |= SPHASE_PENDING; tdata->scsi_status = ccb->csio.scsi_status; } if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) tdata->target_phases |= NO_DISCONNECT; tdata->initiator_tag = ccb->csio.tag_id; } if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) hscb->control |= ccb->csio.tag_action; ahc_setup_data(ahc, sim, &ccb->csio, scb); } break; } case XPT_NOTIFY_ACK: case XPT_IMMED_NOTIFY: { struct ahc_tmode_tstate *tstate; struct ahc_tmode_lstate *lstate; cam_status status; status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { ccb->ccb_h.status = status; xpt_done(ccb); break; } SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h, sim_links.sle); ccb->ccb_h.status = CAM_REQ_INPROG; ahc_send_lstate_events(ahc, lstate); break; } case XPT_EN_LUN: /* Enable LUN as a target */ ahc_handle_en_lun(ahc, sim, ccb); xpt_done(ccb); break; case XPT_ABORT: /* Abort the specified CCB */ { ahc_abort_ccb(ahc, sim, ccb); break; } case XPT_SET_TRAN_SETTINGS: { #ifdef AHC_NEW_TRAN_SETTINGS struct ahc_devinfo devinfo; struct ccb_trans_settings *cts; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; struct ahc_initiator_tinfo *tinfo; struct ahc_tmode_tstate *tstate; uint16_t *discenable; uint16_t *tagenable; u_int update_type; cts = &ccb->cts; scsi = &cts->proto_specific.scsi; spi = &cts->xport_specific.spi; ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), cts->ccb_h.target_id, cts->ccb_h.target_lun, SIM_CHANNEL(ahc, sim), ROLE_UNKNOWN); tinfo = ahc_fetch_transinfo(ahc, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); update_type = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { update_type |= AHC_TRANS_GOAL; discenable = &tstate->discenable; tagenable = &tstate->tagenable; tinfo->curr.protocol_version = cts->protocol_version; tinfo->curr.transport_version = cts->transport_version; tinfo->goal.protocol_version = cts->protocol_version; tinfo->goal.transport_version = cts->transport_version; } else if (cts->type == CTS_TYPE_USER_SETTINGS) { update_type |= AHC_TRANS_USER; discenable = &ahc->user_discenable; tagenable = &ahc->user_tagenable; tinfo->user.protocol_version = cts->protocol_version; tinfo->user.transport_version = cts->transport_version; } else { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } ahc_lock(ahc, &s); if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) *discenable |= devinfo.target_mask; else *discenable &= ~devinfo.target_mask; } if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) *tagenable |= devinfo.target_mask; else *tagenable &= ~devinfo.target_mask; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { ahc_validate_width(ahc, /*tinfo limit*/NULL, &spi->bus_width, ROLE_UNKNOWN); ahc_set_width(ahc, &devinfo, spi->bus_width, update_type, /*paused*/FALSE); } if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) { if (update_type == AHC_TRANS_USER) spi->ppr_options = tinfo->user.ppr_options; else spi->ppr_options = tinfo->goal.ppr_options; } if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) { if (update_type == AHC_TRANS_USER) spi->sync_offset = tinfo->user.offset; else spi->sync_offset = tinfo->goal.offset; } if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { if (update_type == AHC_TRANS_USER) spi->sync_period = tinfo->user.period; else spi->sync_period = tinfo->goal.period; } if (((spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0) || ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0)) { struct ahc_syncrate *syncrate; u_int maxsync; if ((ahc->features & AHC_ULTRA2) != 0) maxsync = AHC_SYNCRATE_DT; else if ((ahc->features & AHC_ULTRA) != 0) maxsync = AHC_SYNCRATE_ULTRA; else maxsync = AHC_SYNCRATE_FAST; if (spi->bus_width != MSG_EXT_WDTR_BUS_16_BIT) spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ; syncrate = ahc_find_syncrate(ahc, &spi->sync_period, &spi->ppr_options, maxsync); ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate, &spi->sync_offset, spi->bus_width, ROLE_UNKNOWN); /* We use a period of 0 to represent async */ if (spi->sync_offset == 0) { spi->sync_period = 0; spi->ppr_options = 0; } ahc_set_syncrate(ahc, &devinfo, syncrate, spi->sync_period, spi->sync_offset, spi->ppr_options, update_type, /*paused*/FALSE); } ahc_unlock(ahc, &s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); #else struct ahc_devinfo devinfo; struct ccb_trans_settings *cts; struct ahc_initiator_tinfo *tinfo; struct ahc_tmode_tstate *tstate; uint16_t *discenable; uint16_t *tagenable; u_int update_type; long s; cts = &ccb->cts; ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), cts->ccb_h.target_id, cts->ccb_h.target_lun, SIM_CHANNEL(ahc, sim), ROLE_UNKNOWN); tinfo = ahc_fetch_transinfo(ahc, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { update_type |= AHC_TRANS_GOAL; discenable = &tstate->discenable; tagenable = &tstate->tagenable; } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { update_type |= AHC_TRANS_USER; discenable = &ahc->user_discenable; tagenable = &ahc->user_tagenable; } else { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } ahc_lock(ahc, &s); if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) *discenable |= devinfo.target_mask; else *discenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) *tagenable |= devinfo.target_mask; else *tagenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { ahc_validate_width(ahc, /*tinfo limit*/NULL, &cts->bus_width, ROLE_UNKNOWN); ahc_set_width(ahc, &devinfo, cts->bus_width, update_type, /*paused*/FALSE); } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { if (update_type == AHC_TRANS_USER) cts->sync_offset = tinfo->user.offset; else cts->sync_offset = tinfo->goal.offset; } if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { if (update_type == AHC_TRANS_USER) cts->sync_period = tinfo->user.period; else cts->sync_period = tinfo->goal.period; } if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { struct ahc_syncrate *syncrate; u_int ppr_options; u_int maxsync; if ((ahc->features & AHC_ULTRA2) != 0) maxsync = AHC_SYNCRATE_DT; else if ((ahc->features & AHC_ULTRA) != 0) maxsync = AHC_SYNCRATE_ULTRA; else maxsync = AHC_SYNCRATE_FAST; ppr_options = 0; if (cts->sync_period <= 9 && cts->bus_width == MSG_EXT_WDTR_BUS_16_BIT) ppr_options = MSG_EXT_PPR_DT_REQ; syncrate = ahc_find_syncrate(ahc, &cts->sync_period, &ppr_options, maxsync); ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate, &cts->sync_offset, MSG_EXT_WDTR_BUS_8_BIT, ROLE_UNKNOWN); /* We use a period of 0 to represent async */ if (cts->sync_offset == 0) { cts->sync_period = 0; ppr_options = 0; } if (ppr_options == MSG_EXT_PPR_DT_REQ && tinfo->user.transport_version >= 3) { tinfo->goal.transport_version = tinfo->user.transport_version; tinfo->curr.transport_version = tinfo->user.transport_version; } ahc_set_syncrate(ahc, &devinfo, syncrate, cts->sync_period, cts->sync_offset, ppr_options, update_type, /*paused*/FALSE); } ahc_unlock(ahc, &s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); #endif break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { ahc_lock(ahc, &s); ahc_get_tran_settings(ahc, SIM_SCSI_ID(ahc, sim), SIM_CHANNEL(ahc, sim), &ccb->cts); ahc_unlock(ahc, &s); xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - uint32_t size_mb; - uint32_t secs_per_cylinder; int extended; - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); extended = SIM_IS_SCSIBUS_B(ahc, sim) ? ahc->flags & AHC_EXTENDED_TRANS_B : ahc->flags & AHC_EXTENDED_TRANS_A; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, extended); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int found; ahc_lock(ahc, &s); found = ahc_reset_channel(ahc, SIM_CHANNEL(ahc, sim), /*initiate reset*/TRUE); ahc_unlock(ahc, &s); if (bootverbose) { xpt_print_path(SIM_PATH(ahc, sim)); printf("SCSI bus reset delivered. " "%d SCBs aborted.\n", found); } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; if ((ahc->features & AHC_WIDE) != 0) cpi->hba_inquiry |= PI_WIDE_16; if ((ahc->features & AHC_TARGETMODE) != 0) { cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; } else { cpi->target_sprt = 0; } cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = (ahc->features & AHC_WIDE) ? 15 : 7; cpi->max_lun = AHC_NUM_LUNS - 1; if (SIM_IS_SCSIBUS_B(ahc, sim)) { cpi->initiator_id = ahc->our_id_b; if ((ahc->flags & AHC_RESET_BUS_B) == 0) cpi->hba_misc |= PIM_NOBUSRESET; } else { cpi->initiator_id = ahc->our_id; if ((ahc->flags & AHC_RESET_BUS_A) == 0) cpi->hba_misc |= PIM_NOBUSRESET; } cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); #ifdef AHC_NEW_TRAN_SETTINGS cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_ST; if ((ahc->features & AHC_DT) != 0) { cpi->transport_version = 3; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_DT_ST; } #endif cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(ccb); break; } } static void ahc_get_tran_settings(struct ahc_softc *ahc, int our_id, char channel, struct ccb_trans_settings *cts) { #ifdef AHC_NEW_TRAN_SETTINGS struct ahc_devinfo devinfo; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; struct ahc_initiator_tinfo *targ_info; struct ahc_tmode_tstate *tstate; struct ahc_transinfo *tinfo; scsi = &cts->proto_specific.scsi; spi = &cts->xport_specific.spi; ahc_compile_devinfo(&devinfo, our_id, cts->ccb_h.target_id, cts->ccb_h.target_lun, channel, ROLE_UNKNOWN); targ_info = ahc_fetch_transinfo(ahc, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); if (cts->type == CTS_TYPE_CURRENT_SETTINGS) tinfo = &targ_info->curr; else tinfo = &targ_info->user; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (cts->type == CTS_TYPE_USER_SETTINGS) { if ((ahc->user_discenable & devinfo.target_mask) != 0) spi->flags |= CTS_SPI_FLAGS_DISC_ENB; if ((ahc->user_tagenable & devinfo.target_mask) != 0) scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } else { if ((tstate->discenable & devinfo.target_mask) != 0) spi->flags |= CTS_SPI_FLAGS_DISC_ENB; if ((tstate->tagenable & devinfo.target_mask) != 0) scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } cts->protocol_version = tinfo->protocol_version; cts->transport_version = tinfo->transport_version; spi->sync_period = tinfo->period; spi->sync_offset = tinfo->offset; spi->bus_width = tinfo->width; spi->ppr_options = tinfo->ppr_options; cts->protocol = PROTO_SCSI; cts->transport = XPORT_SPI; spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_PPR_OPTIONS; if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else { scsi->valid = 0; } cts->ccb_h.status = CAM_REQ_CMP; #else struct ahc_devinfo devinfo; struct ahc_initiator_tinfo *targ_info; struct ahc_tmode_tstate *tstate; struct ahc_transinfo *tinfo; ahc_compile_devinfo(&devinfo, our_id, cts->ccb_h.target_id, cts->ccb_h.target_lun, channel, ROLE_UNKNOWN); targ_info = ahc_fetch_transinfo(ahc, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) tinfo = &targ_info->curr; else tinfo = &targ_info->user; cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) == 0) { if ((ahc->user_discenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((ahc->user_tagenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } else { if ((tstate->discenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((tstate->tagenable & devinfo.target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; cts->bus_width = tinfo->width; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID; if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) cts->valid |= CCB_TRANS_DISC_VALID|CCB_TRANS_TQ_VALID; cts->ccb_h.status = CAM_REQ_CMP; #endif } static void ahc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) { struct ahc_softc *ahc; struct cam_sim *sim; sim = (struct cam_sim *)callback_arg; ahc = (struct ahc_softc *)cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: { struct ahc_devinfo devinfo; long s; ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), xpt_path_target_id(path), xpt_path_lun_id(path), SIM_CHANNEL(ahc, sim), ROLE_UNKNOWN); /* * Revert to async/narrow transfers * for the next device. */ ahc_lock(ahc, &s); ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, AHC_TRANS_GOAL|AHC_TRANS_CUR, /*paused*/FALSE); ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL, /*period*/0, /*offset*/0, /*ppr_options*/0, AHC_TRANS_GOAL|AHC_TRANS_CUR, /*paused*/FALSE); ahc_unlock(ahc, &s); break; } default: break; } } static void ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error) { struct scb *scb; union ccb *ccb; struct ahc_softc *ahc; struct ahc_initiator_tinfo *tinfo; struct ahc_tmode_tstate *tstate; u_int mask; long s; scb = (struct scb *)arg; ccb = scb->io_ctx; ahc = scb->ahc_softc; if (error != 0) { if (error == EFBIG) ahc_set_transaction_status(scb, CAM_REQ_TOO_BIG); else ahc_set_transaction_status(scb, CAM_REQ_CMP_ERR); if (nsegments != 0) bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); ahc_unlock(ahc, &s); xpt_done(ccb); return; } if (nsegments != 0) { struct ahc_dma_seg *sg; bus_dma_segment_t *end_seg; bus_dmasync_op_t op; end_seg = dm_segs + nsegments; /* Copy the segments into our SG list */ sg = scb->sg_list; while (dm_segs < end_seg) { uint32_t len; sg->addr = ahc_htole32(dm_segs->ds_addr); len = dm_segs->ds_len | ((dm_segs->ds_addr >> 8) & 0x7F000000); sg->len = ahc_htole32(len); sg++; dm_segs++; } /* * Note where to find the SG entries in bus space. * We also set the full residual flag which the * sequencer will clear as soon as a data transfer * occurs. */ scb->hscb->sgptr = ahc_htole32(scb->sg_list_phys|SG_FULL_RESID); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(ahc->buffer_dmat, scb->dmamap, op); if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { struct target_data *tdata; tdata = &scb->hscb->shared_data.tdata; tdata->target_phases |= DPHASE_PENDING; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) tdata->data_phase = P_DATAOUT; else tdata->data_phase = P_DATAIN; /* * If the transfer is of an odd length and in the * "in" direction (scsi->HostBus), then it may * trigger a bug in the 'WideODD' feature of * non-Ultra2 chips. Force the total data-length * to be even by adding an extra, 1 byte, SG, * element. We do this even if we are not currently * negotiated wide as negotiation could occur before * this command is executed. */ if ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0 && (ccb->csio.dxfer_len & 0x1) != 0 && (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { nsegments++; if (nsegments > AHC_NSEG) { ahc_set_transaction_status(scb, CAM_REQ_TOO_BIG); bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); ahc_unlock(ahc, &s); xpt_done(ccb); return; } sg->addr = ahc_htole32(ahc->dma_bug_buf); sg->len = ahc_htole32(1); sg++; } } sg--; sg->len |= ahc_htole32(AHC_DMA_LAST_SEG); /* Copy the first SG into the "current" data pointer area */ scb->hscb->dataptr = scb->sg_list->addr; scb->hscb->datacnt = scb->sg_list->len; } else { scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL); scb->hscb->dataptr = 0; scb->hscb->datacnt = 0; } scb->sg_count = nsegments; ahc_lock(ahc, &s); /* * Last time we need to check if this SCB needs to * be aborted. */ if (ahc_get_transaction_status(scb) != CAM_REQ_INPROG) { if (nsegments != 0) bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); ahc_free_scb(ahc, scb); ahc_unlock(ahc, &s); xpt_done(ccb); return; } tinfo = ahc_fetch_transinfo(ahc, SCSIID_CHANNEL(ahc, scb->hscb->scsiid), SCSIID_OUR_ID(scb->hscb->scsiid), SCSIID_TARGET(ahc, scb->hscb->scsiid), &tstate); mask = SCB_GET_TARGET_MASK(ahc, scb); scb->hscb->scsirate = tinfo->scsirate; scb->hscb->scsioffset = tinfo->curr.offset; if ((tstate->ultraenb & mask) != 0) scb->hscb->control |= ULTRAENB; if ((tstate->discenable & mask) != 0 && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) scb->hscb->control |= DISCENB; if ((ccb->ccb_h.flags & CAM_NEGOTIATE) != 0 && (tinfo->goal.width != 0 || tinfo->goal.offset != 0 || tinfo->goal.ppr_options != 0)) { scb->flags |= SCB_NEGOTIATE; scb->hscb->control |= MK_MESSAGE; } else if ((tstate->auto_negotiate & mask) != 0) { scb->flags |= SCB_AUTO_NEGOTIATE; scb->hscb->control |= MK_MESSAGE; } LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links); ccb->ccb_h.status |= CAM_SIM_QUEUED; if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { uint64_t time; if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) ccb->ccb_h.timeout = 5 * 1000; time = ccb->ccb_h.timeout; time *= hz; time /= 1000; ccb->ccb_h.timeout_ch = timeout(ahc_timeout, (caddr_t)scb, time); } /* * We only allow one untagged transaction * per target in the initiator role unless * we are storing a full busy target *lun* * table in SCB space. */ if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0 && (ahc->flags & AHC_SCB_BTT) == 0) { struct scb_tailq *untagged_q; int target_offset; target_offset = SCB_GET_TARGET_OFFSET(ahc, scb); untagged_q = &(ahc->untagged_queues[target_offset]); TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe); scb->flags |= SCB_UNTAGGEDQ; if (TAILQ_FIRST(untagged_q) != scb) { ahc_unlock(ahc, &s); return; } } scb->flags |= SCB_ACTIVE; if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) { /* Define a mapping from our tag to the SCB. */ ahc->scb_data->scbindex[scb->hscb->tag] = scb; ahc_pause(ahc); if ((ahc->flags & AHC_PAGESCBS) == 0) ahc_outb(ahc, SCBPTR, scb->hscb->tag); ahc_outb(ahc, TARG_IMMEDIATE_SCB, scb->hscb->tag); ahc_unpause(ahc); } else { ahc_queue_scb(ahc, scb); } ahc_unlock(ahc, &s); } static void ahc_poll(struct cam_sim *sim) { struct ahc_softc *ahc; ahc = (struct ahc_softc *)cam_sim_softc(sim); ahc_intr(ahc); } static void ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, struct ccb_scsiio *csio, struct scb *scb) { struct hardware_scb *hscb; struct ccb_hdr *ccb_h; hscb = scb->hscb; ccb_h = &csio->ccb_h; csio->resid = 0; csio->sense_resid = 0; if (ccb_h->func_code == XPT_SCSI_IO) { hscb->cdb_len = csio->cdb_len; if ((ccb_h->flags & CAM_CDB_POINTER) != 0) { if (hscb->cdb_len > sizeof(hscb->cdb32) || (ccb_h->flags & CAM_CDB_PHYS) != 0) { u_long s; ahc_set_transaction_status(scb, CAM_REQ_INVALID); ahc_lock(ahc, &s); ahc_free_scb(ahc, scb); ahc_unlock(ahc, &s); xpt_done((union ccb *)csio); return; } if (hscb->cdb_len > 12) { memcpy(hscb->cdb32, csio->cdb_io.cdb_ptr, hscb->cdb_len); scb->flags |= SCB_CDB32_PTR; } else { memcpy(hscb->shared_data.cdb, csio->cdb_io.cdb_ptr, hscb->cdb_len); } } else { if (hscb->cdb_len > 12) { memcpy(hscb->cdb32, csio->cdb_io.cdb_bytes, hscb->cdb_len); scb->flags |= SCB_CDB32_PTR; } else { memcpy(hscb->shared_data.cdb, csio->cdb_io.cdb_bytes, hscb->cdb_len); } } } /* Only use S/G if there is a transfer */ if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) { /* We've been given a pointer to a single buffer */ if ((ccb_h->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(ahc->buffer_dmat, scb->dmamap, csio->data_ptr, csio->dxfer_len, ahc_execute_scb, scb, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(sim, /*count*/1); scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ if (csio->dxfer_len > AHC_MAXTRANSFER_SIZE) panic("ahc_setup_data - Transfer size " "larger than can device max"); seg.ds_addr = (bus_addr_t)(vm_offset_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; ahc_execute_scb(scb, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccb_h->flags & CAM_DATA_PHYS) != 0) panic("ahc_setup_data - Physical segment " "pointers unsupported"); if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0) panic("ahc_setup_data - Virtual segment " "addresses unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; ahc_execute_scb(scb, segs, csio->sglist_cnt, 0); } } else { ahc_execute_scb(scb, NULL, 0, 0); } } static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb) { if ((scb->flags & SCB_RECOVERY_SCB) == 0) { struct scb *list_scb; scb->flags |= SCB_RECOVERY_SCB; /* * Take all queued, but not sent SCBs out of the equation. * Also ensure that no new CCBs are queued to us while we * try to fix this problem. */ if ((scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { xpt_freeze_simq(SCB_GET_SIM(ahc, scb), /*count*/1); scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; } /* * Go through all of our pending SCBs and remove * any scheduled timeouts for them. We will reschedule * them after we've successfully fixed this problem. */ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) { union ccb *ccb; ccb = list_scb->io_ctx; untimeout(ahc_timeout, list_scb, ccb->ccb_h.timeout_ch); } } } void ahc_timeout(void *arg) { struct scb *scb; struct ahc_softc *ahc; long s; int found; u_int last_phase; int target; int lun; int i; char channel; scb = (struct scb *)arg; ahc = (struct ahc_softc *)scb->ahc_softc; ahc_lock(ahc, &s); ahc_pause_and_flushwork(ahc); if ((scb->flags & SCB_ACTIVE) == 0) { /* Previous timeout took care of me already */ printf("%s: Timedout SCB already complete. " "Interrupts may not be functioning.\n", ahc_name(ahc)); ahc_unpause(ahc); ahc_unlock(ahc, &s); return; } target = SCB_GET_TARGET(ahc, scb); channel = SCB_GET_CHANNEL(ahc, scb); lun = SCB_GET_LUN(scb); ahc_print_path(ahc, scb); printf("SCB 0x%x - timed out\n", scb->hscb->tag); ahc_dump_card_state(ahc); last_phase = ahc_inb(ahc, LASTPHASE); if (scb->sg_count > 0) { for (i = 0; i < scb->sg_count; i++) { printf("sg[%d] - Addr 0x%x : Length %d\n", i, scb->sg_list[i].addr, scb->sg_list[i].len & AHC_SG_LEN_MASK); } } if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) { /* * Been down this road before. * Do a full bus reset. */ bus_reset: ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT); found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE); printf("%s: Issued Channel %c Bus Reset. " "%d SCBs aborted\n", ahc_name(ahc), channel, found); } else { /* * If we are a target, transition to bus free and report * the timeout. * * The target/initiator that is holding up the bus may not * be the same as the one that triggered this timeout * (different commands have different timeout lengths). * If the bus is idle and we are actiing as the initiator * for this request, queue a BDR message to the timed out * target. Otherwise, if the timed out transaction is * active: * Initiator transaction: * Stuff the message buffer with a BDR message and assert * ATN in the hopes that the target will let go of the bus * and go to the mesgout phase. If this fails, we'll * get another timeout 2 seconds later which will attempt * a bus reset. * * Target transaction: * Transition to BUS FREE and report the error. * It's good to be the target! */ u_int active_scb_index; u_int saved_scbptr; saved_scbptr = ahc_inb(ahc, SCBPTR); active_scb_index = ahc_inb(ahc, SCB_TAG); if ((ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0 && (active_scb_index < ahc->scb_data->numscbs)) { struct scb *active_scb; /* * If the active SCB is not us, assume that * the active SCB has a longer timeout than * the timedout SCB, and wait for the active * SCB to timeout. */ active_scb = ahc_lookup_scb(ahc, active_scb_index); if (active_scb != scb) { struct ccb_hdr *ccbh; uint64_t newtimeout; ahc_print_path(ahc, scb); printf("Other SCB Timeout%s", (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0 ? " again\n" : "\n"); scb->flags |= SCB_OTHERTCL_TIMEOUT; newtimeout = MAX(active_scb->io_ctx->ccb_h.timeout, scb->io_ctx->ccb_h.timeout); newtimeout *= hz; newtimeout /= 1000; ccbh = &scb->io_ctx->ccb_h; scb->io_ctx->ccb_h.timeout_ch = timeout(ahc_timeout, scb, newtimeout); ahc_unpause(ahc); ahc_unlock(ahc, &s); return; } /* It's us */ if ((scb->flags & SCB_TARGET_SCB) != 0) { /* * Send back any queued up transactions * and properly record the error condition. */ ahc_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb), SCB_GET_CHANNEL(ahc, scb), SCB_GET_LUN(scb), scb->hscb->tag, ROLE_TARGET, CAM_CMD_TIMEOUT); /* Will clear us from the bus */ ahc_restart(ahc); ahc_unlock(ahc, &s); return; } ahc_set_recoveryscb(ahc, active_scb); ahc_outb(ahc, MSG_OUT, HOST_MSG); ahc_outb(ahc, SCSISIGO, last_phase|ATNO); ahc_print_path(ahc, active_scb); printf("BDR message in message buffer\n"); active_scb->flags |= SCB_DEVICE_RESET; active_scb->io_ctx->ccb_h.timeout_ch = timeout(ahc_timeout, (caddr_t)active_scb, 2 * hz); ahc_unpause(ahc); } else { int disconnected; /* XXX Shouldn't panic. Just punt instead? */ if ((scb->flags & SCB_TARGET_SCB) != 0) panic("Timed-out target SCB but bus idle"); if (last_phase != P_BUSFREE && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) { /* XXX What happened to the SCB? */ /* Hung target selection. Goto busfree */ printf("%s: Hung target selection\n", ahc_name(ahc)); ahc_restart(ahc); ahc_unlock(ahc, &s); return; } if (ahc_search_qinfifo(ahc, target, channel, lun, scb->hscb->tag, ROLE_INITIATOR, /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; } else { disconnected = TRUE; } if (disconnected) { ahc_set_recoveryscb(ahc, scb); /* * Actually re-queue this SCB in an attempt * to select the device before it reconnects. * In either case (selection or reselection), * we will now issue a target reset to the * timed-out device. * * Set the MK_MESSAGE control bit indicating * that we desire to send a message. We * also set the disconnected flag since * in the paging case there is no guarantee * that our SCB control byte matches the * version on the card. We don't want the * sequencer to abort the command thinking * an unsolicited reselection occurred. */ scb->hscb->control |= MK_MESSAGE|DISCONNECTED; scb->flags |= SCB_DEVICE_RESET; /* * Remove any cached copy of this SCB in the * disconnected list in preparation for the * queuing of our abort SCB. We use the * same element in the SCB, SCB_NEXT, for * both the qinfifo and the disconnected list. */ ahc_search_disc_list(ahc, target, channel, lun, scb->hscb->tag, /*stop_on_first*/TRUE, /*remove*/TRUE, /*save_state*/FALSE); /* * In the non-paging case, the sequencer will * never re-reference the in-core SCB. * To make sure we are notified during * reslection, set the MK_MESSAGE flag in * the card's copy of the SCB. */ if ((ahc->flags & AHC_PAGESCBS) == 0) { ahc_outb(ahc, SCBPTR, scb->hscb->tag); ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) | MK_MESSAGE); } /* * Clear out any entries in the QINFIFO first * so we are the next SCB for this target * to run. */ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb), channel, SCB_GET_LUN(scb), SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); ahc_print_path(ahc, scb); printf("Queuing a BDR SCB\n"); ahc_qinfifo_requeue_tail(ahc, scb); ahc_outb(ahc, SCBPTR, saved_scbptr); scb->io_ctx->ccb_h.timeout_ch = timeout(ahc_timeout, (caddr_t)scb, 2 * hz); ahc_unpause(ahc); } else { /* Go "immediatly" to the bus reset */ /* This shouldn't happen */ ahc_set_recoveryscb(ahc, scb); ahc_print_path(ahc, scb); printf("SCB %d: Immediate reset. " "Flags = 0x%x\n", scb->hscb->tag, scb->flags); goto bus_reset; } } } ahc_unlock(ahc, &s); } static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) { union ccb *abort_ccb; abort_ccb = ccb->cab.abort_ccb; switch (abort_ccb->ccb_h.func_code) { case XPT_ACCEPT_TARGET_IO: case XPT_IMMED_NOTIFY: case XPT_CONT_TARGET_IO: { struct ahc_tmode_tstate *tstate; struct ahc_tmode_lstate *lstate; struct ccb_hdr_slist *list; cam_status status; status = ahc_find_tmode_devs(ahc, sim, abort_ccb, &tstate, &lstate, TRUE); if (status != CAM_REQ_CMP) { ccb->ccb_h.status = status; break; } if (abort_ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) list = &lstate->accept_tios; else if (abort_ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) list = &lstate->immed_notifies; else list = NULL; if (list != NULL) { struct ccb_hdr *curelm; int found; curelm = SLIST_FIRST(list); found = 0; if (curelm == &abort_ccb->ccb_h) { found = 1; SLIST_REMOVE_HEAD(list, sim_links.sle); } else { while(curelm != NULL) { struct ccb_hdr *nextelm; nextelm = SLIST_NEXT(curelm, sim_links.sle); if (nextelm == &abort_ccb->ccb_h) { found = 1; SLIST_NEXT(curelm, sim_links.sle) = SLIST_NEXT(nextelm, sim_links.sle); break; } curelm = nextelm; } } if (found) { abort_ccb->ccb_h.status = CAM_REQ_ABORTED; xpt_done(abort_ccb); ccb->ccb_h.status = CAM_REQ_CMP; } else { xpt_print_path(abort_ccb->ccb_h.path); printf("Not found\n"); ccb->ccb_h.status = CAM_PATH_INVALID; } break; } /* FALLTHROUGH */ } case XPT_SCSI_IO: /* XXX Fully implement the hard ones */ ccb->ccb_h.status = CAM_UA_ABORT; break; default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } void ahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun, ac_code code, void *opt_arg) { struct ccb_trans_settings cts; struct cam_path *path; void *arg; int error; arg = NULL; error = ahc_create_path(ahc, channel, target, lun, &path); if (error != CAM_REQ_CMP) return; switch (code) { case AC_TRANSFER_NEG: { #ifdef AHC_NEW_TRAN_SETTINGS struct ccb_trans_settings_scsi *scsi; cts.type = CTS_TYPE_CURRENT_SETTINGS; scsi = &cts.proto_specific.scsi; #else cts.flags = CCB_TRANS_CURRENT_SETTINGS; #endif cts.ccb_h.path = path; cts.ccb_h.target_id = target; cts.ccb_h.target_lun = lun; ahc_get_tran_settings(ahc, channel == 'A' ? ahc->our_id : ahc->our_id_b, channel, &cts); arg = &cts; #ifdef AHC_NEW_TRAN_SETTINGS scsi->valid &= ~CTS_SCSI_VALID_TQ; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; #else cts.valid &= ~CCB_TRANS_TQ_VALID; cts.flags &= ~CCB_TRANS_TAG_ENB; #endif if (opt_arg == NULL) break; if (*((ahc_queue_alg *)opt_arg) == AHC_QUEUE_TAGGED) #ifdef AHC_NEW_TRAN_SETTINGS scsi->flags |= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; #else cts.flags |= CCB_TRANS_TAG_ENB; cts.valid |= CCB_TRANS_TQ_VALID; #endif break; } case AC_SENT_BDR: case AC_BUS_RESET: break; default: panic("ahc_send_async: Unexpected async event"); } xpt_async(code, path, arg); xpt_free_path(path); } void ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, int enable) { } int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg) { ahc->platform_data = malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ahc->platform_data == NULL) return (ENOMEM); return (0); } void ahc_platform_free(struct ahc_softc *ahc) { struct ahc_platform_data *pdata; pdata = ahc->platform_data; if (pdata != NULL) { if (pdata->regs != NULL) bus_release_resource(ahc->dev_softc, pdata->regs_res_type, pdata->regs_res_id, pdata->regs); if (pdata->irq != NULL) bus_release_resource(ahc->dev_softc, pdata->irq_res_type, 0, pdata->irq); if (pdata->sim_b != NULL) { xpt_async(AC_LOST_DEVICE, pdata->path_b, NULL); xpt_free_path(pdata->path_b); xpt_bus_deregister(cam_sim_path(pdata->sim_b)); cam_sim_free(pdata->sim_b, /*free_devq*/TRUE); } if (pdata->sim != NULL) { xpt_async(AC_LOST_DEVICE, pdata->path, NULL); xpt_free_path(pdata->path); xpt_bus_deregister(cam_sim_path(pdata->sim)); cam_sim_free(pdata->sim, /*free_devq*/TRUE); } if (pdata->eh != NULL) EVENTHANDLER_DEREGISTER(shutdown_final, pdata->eh); free(ahc->platform_data, M_DEVBUF); } } int ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc) { /* We don't sort softcs under FreeBSD so report equal always */ return (0); } int ahc_detach(device_t dev) { struct ahc_softc *ahc; u_long l; u_long s; ahc_list_lock(&l); device_printf(dev, "detaching device\n"); ahc = device_get_softc(dev); ahc = ahc_find_softc(ahc); if (ahc == NULL) { device_printf(dev, "aic7xxx already detached\n"); ahc_list_unlock(&l); return (ENOENT); } ahc_lock(ahc, &s); ahc_intr_enable(ahc, FALSE); bus_teardown_intr(dev, ahc->platform_data->irq, ahc->platform_data->ih); ahc_unlock(ahc, &s); ahc_free(ahc); ahc_list_unlock(&l); return (0); } #if UNUSED static void ahc_dump_targcmd(struct target_cmd *cmd) { uint8_t *byte; uint8_t *last_byte; int i; byte = &cmd->initiator_channel; /* Debugging info for received commands */ last_byte = &cmd[1].initiator_channel; i = 0; while (byte < last_byte) { if (i == 0) printf("\t"); printf("%#x", *byte++); i++; if (i == 8) { printf("\n"); i = 0; } else { printf(", "); } } } #endif static int ahc_modevent(module_t mod, int type, void *data) { /* XXX Deal with busy status on unload. */ return 0; } static moduledata_t ahc_mod = { "ahc", ahc_modevent, NULL }; DECLARE_MODULE(ahc, ahc_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_DEPEND(ahc, cam, 1, 1, 1); MODULE_VERSION(ahc, 1); Index: head/sys/dev/amd/amd.c =================================================================== --- head/sys/dev/amd/amd.c (revision 116350) +++ head/sys/dev/amd/amd.c (revision 116351) @@ -1,2508 +1,2493 @@ /* ********************************************************************* * FILE NAME : amd.c * BY : C.L. Huang (ching@tekram.com.tw) * Erich Chen (erich@tekram.com.tw) * Description: Device Driver for the amd53c974 PCI Bus Master * SCSI Host adapter found on cards such as * the Tekram DC-390(T). * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************* * $FreeBSD$ */ /* ********************************************************************* * HISTORY: * * REV# DATE NAME DESCRIPTION * 1.00 07/02/96 CLH First release for RELEASE-2.1.0 * 1.01 08/20/96 CLH Update for RELEASE-2.1.5 * 1.02 11/06/96 CLH Fixed more than 1 LUN scanning * 1.03 12/20/96 CLH Modify to support 2.2-ALPHA * 1.04 12/26/97 CLH Modify to support RELEASE-2.2.5 * 1.05 01/01/99 ERICH CHEN Modify to support RELEASE-3.0.x (CAM) ********************************************************************* */ /* #define AMD_DEBUG0 */ /* #define AMD_DEBUG_SCSI_PHASE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_DEVICE_ID_AMD53C974 0x20201022ul #define PCI_BASE_ADDR0 0x10 typedef u_int (phase_handler_t)(struct amd_softc *, struct amd_srb *, u_int); typedef phase_handler_t *phase_handler_func_t; static void amd_intr(void *vamd); static int amdstart(struct amd_softc *amd, struct amd_srb * pSRB); static phase_handler_t amd_NopPhase; static phase_handler_t amd_DataOutPhase0; static phase_handler_t amd_DataInPhase0; #define amd_CommandPhase0 amd_NopPhase static phase_handler_t amd_StatusPhase0; static phase_handler_t amd_MsgOutPhase0; static phase_handler_t amd_MsgInPhase0; static phase_handler_t amd_DataOutPhase1; static phase_handler_t amd_DataInPhase1; static phase_handler_t amd_CommandPhase1; static phase_handler_t amd_StatusPhase1; static phase_handler_t amd_MsgOutPhase1; static phase_handler_t amd_MsgInPhase1; static void amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb); static int amdparsemsg(struct amd_softc *amd); static int amdhandlemsgreject(struct amd_softc *amd); static void amdconstructsdtr(struct amd_softc *amd, u_int period, u_int offset); static u_int amdfindclockrate(struct amd_softc *amd, u_int *period); static int amdsentmsg(struct amd_softc *amd, u_int msgtype, int full); static void DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int dir); static void amd_Disconnect(struct amd_softc *amd); static void amd_Reselect(struct amd_softc *amd); static void SRBdone(struct amd_softc *amd, struct amd_srb *pSRB); static void amd_ScsiRstDetect(struct amd_softc *amd); static void amd_ResetSCSIBus(struct amd_softc *amd); static void RequestSense(struct amd_softc *amd, struct amd_srb *pSRB); static void amd_InvalidCmd(struct amd_softc *amd); static void amd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error); #if 0 static void amd_timeout(void *arg1); static void amd_reset(struct amd_softc *amd); #endif static u_int8_t * phystovirt(struct amd_srb *pSRB, u_int32_t xferCnt); void amd_linkSRB(struct amd_softc *amd); static int amd_init(device_t); static void amd_load_defaults(struct amd_softc *amd); static void amd_load_eeprom_or_defaults(struct amd_softc *amd); static int amd_EEpromInDO(struct amd_softc *amd); static u_int16_t EEpromGetData1(struct amd_softc *amd); static void amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval); static void amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry); static void amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd); static void amd_ReadEEprom(struct amd_softc *amd); static int amd_probe(device_t); static int amd_attach(device_t); static void amdcompletematch(struct amd_softc *amd, target_id_t target, lun_id_t lun, u_int tag, struct srb_queue *queue, cam_status status); static void amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, u_int period, u_int offset, u_int type); static void amdsettags(struct amd_softc *amd, u_int target, int tagenb); static __inline void amd_clear_msg_state(struct amd_softc *amd); static __inline void amd_clear_msg_state(struct amd_softc *amd) { amd->msgout_len = 0; amd->msgout_index = 0; amd->msgin_index = 0; } static __inline uint32_t amd_get_sense_bufaddr(struct amd_softc *amd, struct amd_srb *pSRB) { int offset; offset = pSRB->TagNumber; return (amd->sense_busaddr + (offset * sizeof(struct scsi_sense_data))); } static __inline struct scsi_sense_data * amd_get_sense_buf(struct amd_softc *amd, struct amd_srb *pSRB) { int offset; offset = pSRB->TagNumber; return (&amd->sense_buffers[offset]); } static __inline uint32_t amd_get_sense_bufsize(struct amd_softc *amd, struct amd_srb *pSRB) { return (sizeof(struct scsi_sense_data)); } /* CAM SIM entry points */ #define ccb_srb_ptr spriv_ptr0 #define ccb_amd_ptr spriv_ptr1 static void amd_action(struct cam_sim *sim, union ccb *ccb); static void amd_poll(struct cam_sim *sim); /* * State engine function tables indexed by SCSI phase number */ phase_handler_func_t amd_SCSI_phase0[] = { amd_DataOutPhase0, amd_DataInPhase0, amd_CommandPhase0, amd_StatusPhase0, amd_NopPhase, amd_NopPhase, amd_MsgOutPhase0, amd_MsgInPhase0 }; phase_handler_func_t amd_SCSI_phase1[] = { amd_DataOutPhase1, amd_DataInPhase1, amd_CommandPhase1, amd_StatusPhase1, amd_NopPhase, amd_NopPhase, amd_MsgOutPhase1, amd_MsgInPhase1 }; /* * EEProm/BIOS negotiation periods */ u_int8_t eeprom_period[] = { 25, /* 10.0MHz */ 32, /* 8.0MHz */ 38, /* 6.6MHz */ 44, /* 5.7MHz */ 50, /* 5.0MHz */ 63, /* 4.0MHz */ 83, /* 3.0MHz */ 125 /* 2.0MHz */ }; /* * chip clock setting to SCSI specified sync parameter table. */ u_int8_t tinfo_sync_period[] = { 25, /* 10.0 */ 32, /* 8.0 */ 38, /* 6.6 */ 44, /* 5.7 */ 50, /* 5.0 */ 57, /* 4.4 */ 63, /* 4.0 */ 70, /* 3.6 */ 76, /* 3.3 */ 83 /* 3.0 */ }; static __inline struct amd_srb * amdgetsrb(struct amd_softc * amd) { int intflag; struct amd_srb * pSRB; intflag = splcam(); pSRB = TAILQ_FIRST(&amd->free_srbs); if (pSRB) TAILQ_REMOVE(&amd->free_srbs, pSRB, links); splx(intflag); return (pSRB); } static void amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb) { struct scsi_request_sense sense_cmd; u_int8_t *cdb; u_int cdb_len; if (srb->SRBFlag & AUTO_REQSENSE) { sense_cmd.opcode = REQUEST_SENSE; sense_cmd.byte2 = srb->pccb->ccb_h.target_lun << 5; sense_cmd.unused[0] = 0; sense_cmd.unused[1] = 0; sense_cmd.length = sizeof(struct scsi_sense_data); sense_cmd.control = 0; cdb = &sense_cmd.opcode; cdb_len = sizeof(sense_cmd); } else { cdb = &srb->CmdBlock[0]; cdb_len = srb->ScsiCmdLen; } amd_write8_multi(amd, SCSIFIFOREG, cdb, cdb_len); } /* * Attempt to start a waiting transaction. Interrupts must be disabled * upon entry to this function. */ static void amdrunwaiting(struct amd_softc *amd) { struct amd_srb *srb; if (amd->last_phase != SCSI_BUS_FREE) return; srb = TAILQ_FIRST(&amd->waiting_srbs); if (srb == NULL) return; if (amdstart(amd, srb) == 0) { TAILQ_REMOVE(&amd->waiting_srbs, srb, links); TAILQ_INSERT_HEAD(&amd->running_srbs, srb, links); } } static void amdexecutesrb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct amd_srb *srb; union ccb *ccb; struct amd_softc *amd; int s; srb = (struct amd_srb *)arg; ccb = srb->pccb; amd = (struct amd_softc *)ccb->ccb_h.ccb_amd_ptr; if (error != 0) { if (error != EFBIG) printf("amd%d: Unexepected error 0x%x returned from " "bus_dmamap_load\n", amd->unit, error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; } TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); xpt_done(ccb); return; } if (nseg != 0) { struct amd_sg *sg; bus_dma_segment_t *end_seg; bus_dmasync_op_t op; end_seg = dm_segs + nseg; /* Copy the segments into our SG list */ srb->pSGlist = &srb->SGsegment[0]; sg = srb->pSGlist; while (dm_segs < end_seg) { sg->SGXLen = dm_segs->ds_len; sg->SGXPtr = dm_segs->ds_addr; sg++; dm_segs++; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(amd->buffer_dmat, srb->dmamap, op); } srb->SGcount = nseg; srb->SGIndex = 0; srb->AdaptStatus = 0; srb->TargetStatus = 0; srb->MsgCnt = 0; srb->SRBStatus = 0; srb->SRBFlag = 0; srb->SRBState = 0; srb->TotalXferredLen = 0; srb->SGPhysAddr = 0; srb->SGToBeXferLen = 0; srb->EndMessage = 0; s = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(amd->buffer_dmat, srb->dmamap); TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); xpt_done(ccb); splx(s); return; } ccb->ccb_h.status |= CAM_SIM_QUEUED; #if 0 /* XXX Need a timeout handler */ ccb->ccb_h.timeout_ch = timeout(amdtimeout, (caddr_t)srb, (ccb->ccb_h.timeout * hz) / 1000); #endif TAILQ_INSERT_TAIL(&amd->waiting_srbs, srb, links); amdrunwaiting(amd); splx(s); } static void amd_action(struct cam_sim * psim, union ccb * pccb) { struct amd_softc * amd; u_int target_id; CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("amd_action\n")); amd = (struct amd_softc *) cam_sim_softc(psim); target_id = pccb->ccb_h.target_id; switch (pccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct amd_srb * pSRB; struct ccb_scsiio *pcsio; pcsio = &pccb->csio; /* * Assign an SRB and connect it with this ccb. */ pSRB = amdgetsrb(amd); if (!pSRB) { /* Freeze SIMQ */ pccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(pccb); return; } pSRB->pccb = pccb; pccb->ccb_h.ccb_srb_ptr = pSRB; pccb->ccb_h.ccb_amd_ptr = amd; pSRB->ScsiCmdLen = pcsio->cdb_len; bcopy(pcsio->cdb_io.cdb_bytes, pSRB->CmdBlock, pcsio->cdb_len); if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer. */ if ((pccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(amd->buffer_dmat, pSRB->dmamap, pcsio->data_ptr, pcsio->dxfer_len, amdexecutesrb, pSRB, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain * ordering, freeze the * controller queue * until our mapping is * returned. */ xpt_freeze_simq(amd->psim, 1); pccb->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)pcsio->data_ptr; seg.ds_len = pcsio->dxfer_len; amdexecutesrb(pSRB, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); pccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(pccb); return; } /* Just use the segments provided */ segs = (struct bus_dma_segment *)pcsio->data_ptr; amdexecutesrb(pSRB, segs, pcsio->sglist_cnt, 0); } } else amdexecutesrb(pSRB, NULL, 0, 0); break; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &pccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = amd->max_lun; /* 7 or 0 */ cpi->initiator_id = amd->AdaptSCSIID; cpi->bus_id = cam_sim_bus(psim); strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "TRM-AMD", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(psim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_ABORT: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; case XPT_RESET_BUS: { int i; amd_ResetSCSIBus(amd); amd->ACBFlag = 0; for (i = 0; i < 500; i++) { DELAY(1000); /* Wait until our interrupt * handler sees it */ } pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_RESET_DEV: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; case XPT_TERM_IO: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); /* XXX: intentional fall-through ?? */ case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; struct amd_target_info *targ_info; struct amd_transinfo *tinfo; int intflag; cts = &pccb->cts; intflag = splcam(); targ_info = &amd->tinfo[target_id]; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { /* current transfer settings */ if (targ_info->disc_tag & AMD_CUR_DISCENB) { cts->flags = CCB_TRANS_DISC_ENB; } else { cts->flags = 0; /* no tag & disconnect */ } if (targ_info->disc_tag & AMD_CUR_TAGENB) { cts->flags |= CCB_TRANS_TAG_ENB; } tinfo = &targ_info->current; } else { /* default(user) transfer settings */ if (targ_info->disc_tag & AMD_USR_DISCENB) { cts->flags = CCB_TRANS_DISC_ENB; } else { cts->flags = 0; } if (targ_info->disc_tag & AMD_USR_TAGENB) { cts->flags |= CCB_TRANS_TAG_ENB; } tinfo = &targ_info->user; } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; splx(intflag); cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; struct amd_target_info *targ_info; u_int update_type; int intflag; int last_entry; cts = &pccb->cts; update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { update_type |= AMD_TRANS_GOAL; } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { update_type |= AMD_TRANS_USER; } if (update_type == 0 || update_type == (AMD_TRANS_USER|AMD_TRANS_GOAL)) { cts->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); } intflag = splcam(); targ_info = &amd->tinfo[target_id]; if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if (update_type & AMD_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { targ_info->disc_tag |= AMD_CUR_DISCENB; } else { targ_info->disc_tag &= ~AMD_CUR_DISCENB; } } if (update_type & AMD_TRANS_USER) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { targ_info->disc_tag |= AMD_USR_DISCENB; } else { targ_info->disc_tag &= ~AMD_USR_DISCENB; } } } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if (update_type & AMD_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { targ_info->disc_tag |= AMD_CUR_TAGENB; } else { targ_info->disc_tag &= ~AMD_CUR_TAGENB; } } if (update_type & AMD_TRANS_USER) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { targ_info->disc_tag |= AMD_USR_TAGENB; } else { targ_info->disc_tag &= ~AMD_USR_TAGENB; } } } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { if (update_type & AMD_TRANS_GOAL) cts->sync_offset = targ_info->goal.offset; else cts->sync_offset = targ_info->user.offset; } if (cts->sync_offset > AMD_MAX_SYNC_OFFSET) cts->sync_offset = AMD_MAX_SYNC_OFFSET; if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { if (update_type & AMD_TRANS_GOAL) cts->sync_period = targ_info->goal.period; else cts->sync_period = targ_info->user.period; } last_entry = sizeof(tinfo_sync_period) - 1; if ((cts->sync_period != 0) && (cts->sync_period < tinfo_sync_period[0])) cts->sync_period = tinfo_sync_period[0]; if (cts->sync_period > tinfo_sync_period[last_entry]) cts->sync_period = 0; if (cts->sync_offset == 0) cts->sync_period = 0; if ((update_type & AMD_TRANS_USER) != 0) { targ_info->user.period = cts->sync_period; targ_info->user.offset = cts->sync_offset; } if ((update_type & AMD_TRANS_GOAL) != 0) { targ_info->goal.period = cts->sync_period; targ_info->goal.offset = cts->sync_offset; } splx(intflag); pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; int extended; - ccg = &pccb->ccg; - size_mb = ccg->volume_size/((1024L * 1024L)/ccg->block_size); extended = (amd->eepromBuf[EE_MODE2] & GREATER_1G) != 0; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - pccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&pccb->ccg, extended); xpt_done(pccb); break; } default: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; } } static void amd_poll(struct cam_sim * psim) { amd_intr(cam_sim_softc(psim)); } static u_int8_t * phystovirt(struct amd_srb * pSRB, u_int32_t xferCnt) { intptr_t dataPtr; struct ccb_scsiio *pcsio; u_int8_t i; struct amd_sg * pseg; dataPtr = 0; pcsio = &pSRB->pccb->csio; dataPtr = (intptr_t) pcsio->data_ptr; pseg = pSRB->SGsegment; for (i = 0; i < pSRB->SGIndex; i++) { dataPtr += (int) pseg->SGXLen; pseg++; } dataPtr += (int) xferCnt; return ((u_int8_t *) dataPtr); } static void amd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *baddr; baddr = (bus_addr_t *)arg; *baddr = segs->ds_addr; } static void ResetDevParam(struct amd_softc * amd) { u_int target; for (target = 0; target <= amd->max_id; target++) { if (amd->AdaptSCSIID != target) { amdsetsync(amd, target, /*clockrate*/0, /*period*/0, /*offset*/0, AMD_TRANS_CUR); } } } static void amdcompletematch(struct amd_softc *amd, target_id_t target, lun_id_t lun, u_int tag, struct srb_queue *queue, cam_status status) { struct amd_srb *srb; struct amd_srb *next_srb; for (srb = TAILQ_FIRST(queue); srb != NULL; srb = next_srb) { union ccb *ccb; next_srb = TAILQ_NEXT(srb, links); if (srb->pccb->ccb_h.target_id != target && target != CAM_TARGET_WILDCARD) continue; if (srb->pccb->ccb_h.target_lun != lun && lun != CAM_LUN_WILDCARD) continue; if (srb->TagNumber != tag && tag != AMD_TAG_WILDCARD) continue; ccb = srb->pccb; TAILQ_REMOVE(queue, srb, links); TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0 && (status & CAM_DEV_QFRZN) != 0) xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = status; xpt_done(ccb); } } static void amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, u_int period, u_int offset, u_int type) { struct amd_target_info *tinfo; u_int old_period; u_int old_offset; tinfo = &amd->tinfo[target]; old_period = tinfo->current.period; old_offset = tinfo->current.offset; if ((type & AMD_TRANS_CUR) != 0 && (old_period != period || old_offset != offset)) { struct cam_path *path; tinfo->current.period = period; tinfo->current.offset = offset; tinfo->sync_period_reg = clockrate; tinfo->sync_offset_reg = offset; tinfo->CtrlR3 &= ~FAST_SCSI; tinfo->CtrlR4 &= ~EATER_25NS; if (clockrate > 7) tinfo->CtrlR4 |= EATER_25NS; else tinfo->CtrlR3 |= FAST_SCSI; if ((type & AMD_TRANS_ACTIVE) == AMD_TRANS_ACTIVE) { amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); amd_write8(amd, CNTLREG3, tinfo->CtrlR3); amd_write8(amd, CNTLREG4, tinfo->CtrlR4); } /* If possible, update the XPT's notion of our transfer rate */ if (xpt_create_path(&path, /*periph*/NULL, cam_sim_path(amd->psim), target, CAM_LUN_WILDCARD) == CAM_REQ_CMP) { struct ccb_trans_settings neg; xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); neg.sync_period = period; neg.sync_offset = offset; neg.valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; xpt_async(AC_TRANSFER_NEG, path, &neg); xpt_free_path(path); } } if ((type & AMD_TRANS_GOAL) != 0) { tinfo->goal.period = period; tinfo->goal.offset = offset; } if ((type & AMD_TRANS_USER) != 0) { tinfo->user.period = period; tinfo->user.offset = offset; } } static void amdsettags(struct amd_softc *amd, u_int target, int tagenb) { panic("Implement me!\n"); } #if 0 /* ********************************************************************** * Function : amd_reset (struct amd_softc * amd) * Purpose : perform a hard reset on the SCSI bus( and AMD chip). * Inputs : cmd - command which caused the SCSI RESET ********************************************************************** */ static void amd_reset(struct amd_softc * amd) { int intflag; u_int8_t bval; u_int16_t i; #ifdef AMD_DEBUG0 printf("DC390: RESET"); #endif intflag = splcam(); bval = amd_read8(amd, CNTLREG1); bval |= DIS_INT_ON_SCSI_RST; amd_write8(amd, CNTLREG1, bval); /* disable interrupt */ amd_ResetSCSIBus(amd); for (i = 0; i < 500; i++) { DELAY(1000); } bval = amd_read8(amd, CNTLREG1); bval &= ~DIS_INT_ON_SCSI_RST; amd_write8(amd, CNTLREG1, bval); /* re-enable interrupt */ amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); ResetDevParam(amd); amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, AMD_TAG_WILDCARD, &amd->running_srbs, CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, AMD_TAG_WILDCARD, &amd->waiting_srbs, CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); amd->active_srb = NULL; amd->ACBFlag = 0; splx(intflag); return; } void amd_timeout(void *arg1) { struct amd_srb * pSRB; pSRB = (struct amd_srb *) arg1; } #endif static int amdstart(struct amd_softc *amd, struct amd_srb *pSRB) { union ccb *pccb; struct ccb_scsiio *pcsio; struct amd_target_info *targ_info; u_int identify_msg; u_int command; u_int target; u_int lun; pccb = pSRB->pccb; pcsio = &pccb->csio; target = pccb->ccb_h.target_id; lun = pccb->ccb_h.target_lun; targ_info = &amd->tinfo[target]; amd_clear_msg_state(amd); amd_write8(amd, SCSIDESTIDREG, target); amd_write8(amd, SYNCPERIOREG, targ_info->sync_period_reg); amd_write8(amd, SYNCOFFREG, targ_info->sync_offset_reg); amd_write8(amd, CNTLREG1, targ_info->CtrlR1); amd_write8(amd, CNTLREG3, targ_info->CtrlR3); amd_write8(amd, CNTLREG4, targ_info->CtrlR4); amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); identify_msg = MSG_IDENTIFYFLAG | lun; if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 && (pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0 && (pSRB->CmdBlock[0] != REQUEST_SENSE) && (pSRB->SRBFlag & AUTO_REQSENSE) == 0) identify_msg |= MSG_IDENTIFY_DISCFLAG; amd_write8(amd, SCSIFIFOREG, identify_msg); if ((targ_info->disc_tag & AMD_CUR_TAGENB) == 0 || (identify_msg & MSG_IDENTIFY_DISCFLAG) == 0) pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; if (targ_info->current.period != targ_info->goal.period || targ_info->current.offset != targ_info->goal.offset) { command = SEL_W_ATN_STOP; amdconstructsdtr(amd, targ_info->goal.period, targ_info->goal.offset); } else if ((pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { command = SEL_W_ATN2; pSRB->SRBState = SRB_START; amd_write8(amd, SCSIFIFOREG, pcsio->tag_action); amd_write8(amd, SCSIFIFOREG, pSRB->TagNumber); } else { command = SEL_W_ATN; pSRB->SRBState = SRB_START; } if (command != SEL_W_ATN_STOP) amdsetupcommand(amd, pSRB); if (amd_read8(amd, SCSISTATREG) & INTERRUPT) { pSRB->SRBState = SRB_READY; return (1); } else { amd->last_phase = SCSI_ARBITRATING; amd_write8(amd, SCSICMDREG, command); amd->active_srb = pSRB; amd->cur_target = target; amd->cur_lun = lun; return (0); } } /* * Catch an interrupt from the adapter. * Process pending device interrupts. */ static void amd_intr(void *arg) { struct amd_softc *amd; struct amd_srb *pSRB; u_int internstat = 0; u_int scsistat; u_int intstat; amd = (struct amd_softc *)arg; if (amd == NULL) { #ifdef AMD_DEBUG0 printf("amd_intr: amd NULL return......"); #endif return; } scsistat = amd_read8(amd, SCSISTATREG); if (!(scsistat & INTERRUPT)) { #ifdef AMD_DEBUG0 printf("amd_intr: scsistat = NULL ,return......"); #endif return; } #ifdef AMD_DEBUG_SCSI_PHASE printf("scsistat=%2x,", scsistat); #endif internstat = amd_read8(amd, INTERNSTATREG); intstat = amd_read8(amd, INTSTATREG); #ifdef AMD_DEBUG_SCSI_PHASE printf("intstat=%2x,", intstat); #endif if (intstat & DISCONNECTED) { amd_Disconnect(amd); return; } if (intstat & RESELECTED) { amd_Reselect(amd); return; } if (intstat & INVALID_CMD) { amd_InvalidCmd(amd); return; } if (intstat & SCSI_RESET_) { amd_ScsiRstDetect(amd); return; } if (intstat & (SUCCESSFUL_OP + SERVICE_REQUEST)) { pSRB = amd->active_srb; /* * Run our state engine. First perform * post processing for the last phase we * were in, followed by any processing * required to handle the current phase. */ scsistat = amd_SCSI_phase0[amd->last_phase](amd, pSRB, scsistat); amd->last_phase = scsistat & SCSI_PHASE_MASK; (void)amd_SCSI_phase1[amd->last_phase](amd, pSRB, scsistat); } } static u_int amd_DataOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { struct amd_sg *psgl; u_int32_t ResidCnt, xferCnt; if (!(pSRB->SRBState & SRB_XFERPAD)) { if (scsistat & PARITY_ERR) { pSRB->SRBStatus |= PARITY_ERROR; } if (scsistat & COUNT_2_ZERO) { while ((amd_read8(amd, DMA_Status)&DMA_XFER_DONE) == 0) ; pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; if (pSRB->SGIndex < pSRB->SGcount) { pSRB->pSGlist++; psgl = pSRB->pSGlist; pSRB->SGPhysAddr = psgl->SGXPtr; pSRB->SGToBeXferLen = psgl->SGXLen; } else { pSRB->SGToBeXferLen = 0; } } else { ResidCnt = amd_read8(amd, CURRENTFIFOREG) & 0x1f; ResidCnt += amd_read8(amd, CTCREG_LOW) | (amd_read8(amd, CTCREG_MID) << 8) | (amd_read8(amd, CURTXTCNTREG) << 16); xferCnt = pSRB->SGToBeXferLen - ResidCnt; pSRB->SGPhysAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; } } amd_write8(amd, DMA_Cmd, WRITE_DIRECTION | DMA_IDLE_CMD); return (scsistat); } static u_int amd_DataInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { u_int8_t bval; u_int16_t i, residual; struct amd_sg *psgl; u_int32_t ResidCnt, xferCnt; u_int8_t * ptr; if (!(pSRB->SRBState & SRB_XFERPAD)) { if (scsistat & PARITY_ERR) { pSRB->SRBStatus |= PARITY_ERROR; } if (scsistat & COUNT_2_ZERO) { while (1) { bval = amd_read8(amd, DMA_Status); if ((bval & DMA_XFER_DONE) != 0) break; } amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; if (pSRB->SGIndex < pSRB->SGcount) { pSRB->pSGlist++; psgl = pSRB->pSGlist; pSRB->SGPhysAddr = psgl->SGXPtr; pSRB->SGToBeXferLen = psgl->SGXLen; } else { pSRB->SGToBeXferLen = 0; } } else { /* phase changed */ residual = 0; bval = amd_read8(amd, CURRENTFIFOREG); while (bval & 0x1f) { if ((bval & 0x1f) == 1) { for (i = 0; i < 0x100; i++) { bval = amd_read8(amd, CURRENTFIFOREG); if (!(bval & 0x1f)) { goto din_1; } else if (i == 0x0ff) { residual = 1; goto din_1; } } } else { bval = amd_read8(amd, CURRENTFIFOREG); } } din_1: amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_BLAST_CMD); for (i = 0; i < 0x8000; i++) { if ((amd_read8(amd, DMA_Status)&BLAST_COMPLETE)) break; } amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); ResidCnt = amd_read8(amd, CTCREG_LOW) | (amd_read8(amd, CTCREG_MID) << 8) | (amd_read8(amd, CURTXTCNTREG) << 16); xferCnt = pSRB->SGToBeXferLen - ResidCnt; pSRB->SGPhysAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; if (residual) { /* get residual byte */ bval = amd_read8(amd, SCSIFIFOREG); ptr = phystovirt(pSRB, xferCnt); *ptr = bval; pSRB->SGPhysAddr++; pSRB->TotalXferredLen++; pSRB->SGToBeXferLen--; } } } return (scsistat); } static u_int amd_StatusPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { pSRB->TargetStatus = amd_read8(amd, SCSIFIFOREG); /* get message */ pSRB->EndMessage = amd_read8(amd, SCSIFIFOREG); pSRB->SRBState = SRB_COMPLETED; amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); return (SCSI_NOP0); } static u_int amd_MsgOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { scsistat = SCSI_NOP0; } return (scsistat); } static u_int amd_MsgInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { int done; amd->msgin_buf[amd->msgin_index] = amd_read8(amd, SCSIFIFOREG); done = amdparsemsg(amd); if (done) amd->msgin_index = 0; else amd->msgin_index++; return (SCSI_NOP0); } static int amdparsemsg(struct amd_softc *amd) { int reject; int done; int response; done = FALSE; response = FALSE; reject = FALSE; /* * Parse as much of the message as is availible, * rejecting it if we don't support it. When * the entire message is availible and has been * handled, return TRUE indicating that we have * parsed an entire message. */ switch (amd->msgin_buf[0]) { case MSG_DISCONNECT: amd->active_srb->SRBState = SRB_DISCONNECT; amd->disc_count[amd->cur_target][amd->cur_lun]++; done = TRUE; break; case MSG_SIMPLE_Q_TAG: { struct amd_srb *disc_srb; if (amd->msgin_index < 1) break; disc_srb = &amd->SRB_array[amd->msgin_buf[1]]; if (amd->active_srb != NULL || disc_srb->SRBState != SRB_DISCONNECT || disc_srb->pccb->ccb_h.target_id != amd->cur_target || disc_srb->pccb->ccb_h.target_lun != amd->cur_lun) { printf("amd%d: Unexpected tagged reselection " "for target %d, Issuing Abort\n", amd->unit, amd->cur_target); amd->msgout_buf[0] = MSG_ABORT; amd->msgout_len = 1; response = TRUE; break; } amd->active_srb = disc_srb; amd->disc_count[amd->cur_target][amd->cur_lun]--; done = TRUE; break; } case MSG_MESSAGE_REJECT: response = amdhandlemsgreject(amd); if (response == FALSE) amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); /* FALLTHROUGH */ case MSG_NOOP: done = TRUE; break; case MSG_EXTENDED: { u_int clockrate; u_int period; u_int offset; u_int saved_offset; /* Wait for enough of the message to begin validation */ if (amd->msgin_index < 1) break; if (amd->msgin_buf[1] != MSG_EXT_SDTR_LEN) { reject = TRUE; break; } /* Wait for opcode */ if (amd->msgin_index < 2) break; if (amd->msgin_buf[2] != MSG_EXT_SDTR) { reject = TRUE; break; } /* * Wait until we have both args before validating * and acting on this message. * * Add one to MSG_EXT_SDTR_LEN to account for * the extended message preamble. */ if (amd->msgin_index < (MSG_EXT_SDTR_LEN + 1)) break; period = amd->msgin_buf[3]; saved_offset = offset = amd->msgin_buf[4]; clockrate = amdfindclockrate(amd, &period); if (offset > AMD_MAX_SYNC_OFFSET) offset = AMD_MAX_SYNC_OFFSET; if (period == 0 || offset == 0) { offset = 0; period = 0; clockrate = 0; } amdsetsync(amd, amd->cur_target, clockrate, period, offset, AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); /* * See if we initiated Sync Negotiation * and didn't have to fall down to async * transfers. */ if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/TRUE)) { /* We started it */ if (saved_offset != offset) { /* Went too low - force async */ reject = TRUE; } } else { /* * Send our own SDTR in reply */ if (bootverbose) printf("Sending SDTR!\n"); amd->msgout_index = 0; amd->msgout_len = 0; amdconstructsdtr(amd, period, offset); amd->msgout_index = 0; response = TRUE; } done = TRUE; break; } case MSG_SAVEDATAPOINTER: case MSG_RESTOREPOINTERS: /* XXX Implement!!! */ done = TRUE; break; default: reject = TRUE; break; } if (reject) { amd->msgout_index = 0; amd->msgout_len = 1; amd->msgout_buf[0] = MSG_MESSAGE_REJECT; done = TRUE; response = TRUE; } if (response) amd_write8(amd, SCSICMDREG, SET_ATN_CMD); if (done && !response) /* Clear the outgoing message buffer */ amd->msgout_len = 0; /* Drop Ack */ amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); return (done); } static u_int amdfindclockrate(struct amd_softc *amd, u_int *period) { u_int i; u_int clockrate; for (i = 0; i < sizeof(tinfo_sync_period); i++) { u_int8_t *table_entry; table_entry = &tinfo_sync_period[i]; if (*period <= *table_entry) { /* * When responding to a target that requests * sync, the requested rate may fall between * two rates that we can output, but still be * a rate that we can receive. Because of this, * we want to respond to the target with * the same rate that it sent to us even * if the period we use to send data to it * is lower. Only lower the response period * if we must. */ if (i == 0) { *period = *table_entry; } break; } } if (i == sizeof(tinfo_sync_period)) { /* Too slow for us. Use asnyc transfers. */ *period = 0; clockrate = 0; } else clockrate = i + 4; return (clockrate); } /* * See if we sent a particular extended message to the target. * If "full" is true, the target saw the full message. * If "full" is false, the target saw at least the first * byte of the message. */ static int amdsentmsg(struct amd_softc *amd, u_int msgtype, int full) { int found; int index; found = FALSE; index = 0; while (index < amd->msgout_len) { if ((amd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 || amd->msgout_buf[index] == MSG_MESSAGE_REJECT) index++; else if (amd->msgout_buf[index] >= MSG_SIMPLE_Q_TAG && amd->msgout_buf[index] < MSG_IGN_WIDE_RESIDUE) { /* Skip tag type and tag id */ index += 2; } else if (amd->msgout_buf[index] == MSG_EXTENDED) { /* Found a candidate */ if (amd->msgout_buf[index+2] == msgtype) { u_int end_index; end_index = index + 1 + amd->msgout_buf[index + 1]; if (full) { if (amd->msgout_index > end_index) found = TRUE; } else if (amd->msgout_index > index) found = TRUE; } break; } else { panic("amdsentmsg: Inconsistent msg buffer"); } } return (found); } static void amdconstructsdtr(struct amd_softc *amd, u_int period, u_int offset) { amd->msgout_buf[amd->msgout_index++] = MSG_EXTENDED; amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR_LEN; amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR; amd->msgout_buf[amd->msgout_index++] = period; amd->msgout_buf[amd->msgout_index++] = offset; amd->msgout_len += 5; } static int amdhandlemsgreject(struct amd_softc *amd) { /* * If we had an outstanding SDTR for this * target, this is a signal that the target * is refusing negotiation. Also watch out * for rejected tag messages. */ struct amd_srb *srb; struct amd_target_info *targ_info; int response = FALSE; srb = amd->active_srb; targ_info = &amd->tinfo[amd->cur_target]; if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/FALSE)) { /* note asynch xfers and clear flag */ amdsetsync(amd, amd->cur_target, /*clockrate*/0, /*period*/0, /*offset*/0, AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); printf("amd%d:%d: refuses synchronous negotiation. " "Using asynchronous transfers\n", amd->unit, amd->cur_target); } else if ((srb != NULL) && (srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { struct ccb_trans_settings neg; printf("amd%d:%d: refuses tagged commands. Performing " "non-tagged I/O\n", amd->unit, amd->cur_target); amdsettags(amd, amd->cur_target, FALSE); neg.flags = 0; neg.valid = CCB_TRANS_TQ_VALID; xpt_setup_ccb(&neg.ccb_h, srb->pccb->ccb_h.path, /*priority*/1); xpt_async(AC_TRANSFER_NEG, srb->pccb->ccb_h.path, &neg); /* * Resend the identify for this CCB as the target * may believe that the selection is invalid otherwise. */ if (amd->msgout_len != 0) bcopy(&amd->msgout_buf[0], &amd->msgout_buf[1], amd->msgout_len); amd->msgout_buf[0] = MSG_IDENTIFYFLAG | srb->pccb->ccb_h.target_lun; amd->msgout_len++; if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 && (srb->pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) amd->msgout_buf[0] |= MSG_IDENTIFY_DISCFLAG; srb->pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; /* * Requeue all tagged commands for this target * currently in our posession so they can be * converted to untagged commands. */ amdcompletematch(amd, amd->cur_target, amd->cur_lun, AMD_TAG_WILDCARD, &amd->waiting_srbs, CAM_DEV_QFRZN|CAM_REQUEUE_REQ); } else { /* * Otherwise, we ignore it. */ printf("amd%d:%d: Message reject received -- ignored\n", amd->unit, amd->cur_target); } return (response); } #if 0 if (!(pSRB->SRBState & SRB_MSGIN_MULTI)) { if (bval == MSG_DISCONNECT) { pSRB->SRBState = SRB_DISCONNECT; } else if (bval == MSG_SAVEDATAPOINTER) { goto min6; } else if ((bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_Q_TAG) && (bval <= MSG_ORDERED_Q_TAG))) { pSRB->SRBState |= SRB_MSGIN_MULTI; pSRB->MsgInBuf[0] = bval; pSRB->MsgCnt = 1; pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; } else if (bval == MSG_MESSAGE_REJECT) { amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); if (pSRB->SRBState & DO_SYNC_NEGO) { goto set_async; } } else if (bval == MSG_RESTOREPOINTERS) { goto min6; } else { goto min6; } } else { /* minx: */ *pSRB->pMsgPtr = bval; pSRB->MsgCnt++; pSRB->pMsgPtr++; if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_Q_TAG) && (pSRB->MsgInBuf[0] <= MSG_ORDERED_Q_TAG)) { if (pSRB->MsgCnt == 2) { pSRB->SRBState = 0; pSRB = &amd->SRB_array[pSRB->MsgInBuf[1]]; if (pSRB->SRBState & SRB_DISCONNECT) == 0) { pSRB = amd->pTmpSRB; pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; EnableMsgOut2(amd, pSRB); } else { if (pDCB->DCBFlag & ABORT_DEV_) { pSRB->SRBState = SRB_ABORT_SENT; EnableMsgOut1(amd, pSRB); } pDCB->pActiveSRB = pSRB; pSRB->SRBState = SRB_DATA_XFER; } } } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5)) { pSRB->SRBState &= ~(SRB_MSGIN_MULTI + DO_SYNC_NEGO); if ((pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1)) { /* reject_msg: */ pSRB->MsgCnt = 1; pSRB->MsgInBuf[0] = MSG_MESSAGE_REJECT; amd_write8(amd, SCSICMDREG, SET_ATN_CMD); } else if (!(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4])) { set_async: /* set async */ pDCB = pSRB->pSRBDCB; /* disable sync & sync nego */ pDCB->SyncMode &= ~(SYNC_ENABLE|SYNC_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; pDCB->tinfo.goal.period = 0; pDCB->tinfo.goal.offset = 0; pDCB->tinfo.current.period = 0; pDCB->tinfo.current.offset = 0; pDCB->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT; pDCB->CtrlR3 = FAST_CLK; /* non_fast */ pDCB->CtrlR4 &= 0x3f; pDCB->CtrlR4 |= EATER_25NS; goto re_prog; } else {/* set sync */ pDCB = pSRB->pSRBDCB; /* enable sync & sync nego */ pDCB->SyncMode |= SYNC_ENABLE|SYNC_NEGO_DONE; /* set sync offset */ pDCB->SyncOffset &= 0x0f0; pDCB->SyncOffset |= pSRB->MsgInBuf[4]; /* set sync period */ pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3]; wval = (u_int16_t) pSRB->MsgInBuf[3]; wval = wval << 2; wval--; wval1 = wval / 25; if ((wval1 * 25) != wval) { wval1++; } bval = FAST_CLK|FAST_SCSI; pDCB->CtrlR4 &= 0x3f; if (wval1 >= 8) { /* Fast SCSI */ wval1--; bval = FAST_CLK; pDCB->CtrlR4 |= EATER_25NS; } pDCB->CtrlR3 = bval; pDCB->SyncPeriod = (u_int8_t) wval1; pDCB->tinfo.goal.period = tinfo_sync_period[pDCB->SyncPeriod - 4]; pDCB->tinfo.goal.offset = pDCB->SyncOffset; pDCB->tinfo.current.period = tinfo_sync_period[pDCB->SyncPeriod - 4];; pDCB->tinfo.current.offset = pDCB->SyncOffset; /* * program SCSI control register */ re_prog: amd_write8(amd, SYNCPERIOREG, pDCB->SyncPeriod); amd_write8(amd, SYNCOFFREG, pDCB->SyncOffset); amd_write8(amd, CNTLREG3, pDCB->CtrlR3); amd_write8(amd, CNTLREG4, pDCB->CtrlR4); } } } min6: amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); return (SCSI_NOP0); } #endif static u_int amd_DataOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { DataIO_Comm(amd, pSRB, WRITE_DIRECTION); return (scsistat); } static u_int amd_DataInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { DataIO_Comm(amd, pSRB, READ_DIRECTION); return (scsistat); } static void DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int ioDir) { struct amd_sg * psgl; u_int32_t lval; if (pSRB->SGIndex < pSRB->SGcount) { amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir);/* |EN_DMA_INT */ if (!pSRB->SGToBeXferLen) { psgl = pSRB->pSGlist; pSRB->SGPhysAddr = psgl->SGXPtr; pSRB->SGToBeXferLen = psgl->SGXLen; } lval = pSRB->SGToBeXferLen; amd_write8(amd, CTCREG_LOW, lval); amd_write8(amd, CTCREG_MID, lval >> 8); amd_write8(amd, CURTXTCNTREG, lval >> 16); amd_write32(amd, DMA_XferCnt, pSRB->SGToBeXferLen); amd_write32(amd, DMA_XferAddr, pSRB->SGPhysAddr); pSRB->SRBState = SRB_DATA_XFER; amd_write8(amd, SCSICMDREG, DMA_COMMAND|INFO_XFER_CMD); amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir); /* |EN_DMA_INT */ amd_write8(amd, DMA_Cmd, DMA_START_CMD|ioDir);/* |EN_DMA_INT */ } else { /* xfer pad */ if (pSRB->SGcount) { pSRB->AdaptStatus = H_OVER_UNDER_RUN; pSRB->SRBStatus |= OVER_RUN; } amd_write8(amd, CTCREG_LOW, 0); amd_write8(amd, CTCREG_MID, 0); amd_write8(amd, CURTXTCNTREG, 0); pSRB->SRBState |= SRB_XFERPAD; amd_write8(amd, SCSICMDREG, DMA_COMMAND|XFER_PAD_BYTE); } } static u_int amd_CommandPhase1(struct amd_softc *amd, struct amd_srb *srb, u_int scsistat) { amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); amdsetupcommand(amd, srb); srb->SRBState = SRB_COMMAND; amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); return (scsistat); } static u_int amd_StatusPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); pSRB->SRBState = SRB_STATUS; amd_write8(amd, SCSICMDREG, INITIATOR_CMD_CMPLTE); return (scsistat); } static u_int amd_MsgOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); if (amd->msgout_len == 0) { amd->msgout_buf[0] = MSG_NOOP; amd->msgout_len = 1; } amd_write8_multi(amd, SCSIFIFOREG, amd->msgout_buf, amd->msgout_len); amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); return (scsistat); } static u_int amd_MsgInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); return (scsistat); } static u_int amd_NopPhase(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) { return (scsistat); } static void amd_Disconnect(struct amd_softc * amd) { struct amd_srb *srb; int target; int lun; srb = amd->active_srb; amd->active_srb = NULL; amd->last_phase = SCSI_BUS_FREE; amd_write8(amd, SCSICMDREG, EN_SEL_RESEL); target = amd->cur_target; lun = amd->cur_lun; if (srb == NULL) { /* Invalid reselection */ amdrunwaiting(amd); } else if (srb->SRBState & SRB_ABORT_SENT) { /* Clean up and done this srb */ #if 0 while (( = TAILQ_FIRST(&amd->running_srbs)) != NULL) { /* XXX What about "done'ing" these srbs??? */ if (pSRB->pSRBDCB == pDCB) { TAILQ_REMOVE(&amd->running_srbs, pSRB, links); TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); } } amdrunwaiting(amd); #endif } else { if ((srb->SRBState & (SRB_START | SRB_MSGOUT)) || !(srb->SRBState & (SRB_DISCONNECT | SRB_COMPLETED))) { srb->TargetStatus = AMD_SCSI_STAT_SEL_TIMEOUT; goto disc1; } else if (srb->SRBState & SRB_DISCONNECT) { if (!(srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID)) amd->untagged_srbs[target][lun] = srb; amdrunwaiting(amd); } else if (srb->SRBState & SRB_COMPLETED) { disc1: srb->SRBState = SRB_FREE; SRBdone(amd, srb); } } return; } static void amd_Reselect(struct amd_softc *amd) { struct amd_target_info *tinfo; u_int16_t disc_count; amd_clear_msg_state(amd); if (amd->active_srb != NULL) { /* Requeue the SRB for our attempted Selection */ TAILQ_REMOVE(&amd->running_srbs, amd->active_srb, links); TAILQ_INSERT_HEAD(&amd->waiting_srbs, amd->active_srb, links); amd->active_srb = NULL; } /* get ID */ amd->cur_target = amd_read8(amd, SCSIFIFOREG); amd->cur_target ^= amd->HostID_Bit; amd->cur_target = ffs(amd->cur_target) - 1; amd->cur_lun = amd_read8(amd, SCSIFIFOREG) & 7; tinfo = &amd->tinfo[amd->cur_target]; amd->active_srb = amd->untagged_srbs[amd->cur_target][amd->cur_lun]; disc_count = amd->disc_count[amd->cur_target][amd->cur_lun]; if (disc_count == 0) { printf("amd%d: Unexpected reselection for target %d, " "Issuing Abort\n", amd->unit, amd->cur_target); amd->msgout_buf[0] = MSG_ABORT; amd->msgout_len = 1; amd_write8(amd, SCSICMDREG, SET_ATN_CMD); } if (amd->active_srb != NULL) { amd->disc_count[amd->cur_target][amd->cur_lun]--; amd->untagged_srbs[amd->cur_target][amd->cur_lun] = NULL; } amd_write8(amd, SCSIDESTIDREG, amd->cur_target); amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); amd_write8(amd, CNTLREG1, tinfo->CtrlR1); amd_write8(amd, CNTLREG3, tinfo->CtrlR3); amd_write8(amd, CNTLREG4, tinfo->CtrlR4); amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD);/* drop /ACK */ amd->last_phase = SCSI_NOP0; } static void SRBdone(struct amd_softc *amd, struct amd_srb *pSRB) { u_int8_t bval, i, status; union ccb *pccb; struct ccb_scsiio *pcsio; int intflag; struct amd_sg *ptr2; u_int32_t swlval; pccb = pSRB->pccb; pcsio = &pccb->csio; CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("SRBdone - TagNumber %d\n", pSRB->TagNumber)); if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(amd->buffer_dmat, pSRB->dmamap, op); bus_dmamap_unload(amd->buffer_dmat, pSRB->dmamap); } status = pSRB->TargetStatus; pccb->ccb_h.status = CAM_REQ_CMP; pccb->ccb_h.status = CAM_REQ_CMP; if (pSRB->SRBFlag & AUTO_REQSENSE) { pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = SCSI_STATUS_CHECK_COND; if (status == SCSI_STATUS_CHECK_COND) { pccb->ccb_h.status = CAM_AUTOSENSE_FAIL; goto ckc_e; } *((u_int32_t *)&(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; pcsio->sense_resid = pcsio->sense_len - pSRB->TotalXferredLen; pSRB->TotalXferredLen = pSRB->Segment1[1]; if (pSRB->TotalXferredLen) { /* ???? */ pcsio->resid = pcsio->dxfer_len - pSRB->TotalXferredLen; /* The resid field contains valid data */ /* Flush resid bytes on complete */ } else { pcsio->scsi_status = SCSI_STATUS_CHECK_COND; } bzero(&pcsio->sense_data, pcsio->sense_len); bcopy(amd_get_sense_buf(amd, pSRB), &pcsio->sense_data, pcsio->sense_len); pccb->ccb_h.status = CAM_AUTOSNS_VALID; goto ckc_e; } if (status) { if (status == SCSI_STATUS_CHECK_COND) { if ((pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen)) { bval = pSRB->SGcount; swlval = pSRB->SGToBeXferLen; ptr2 = pSRB->pSGlist; ptr2++; for (i = pSRB->SGIndex + 1; i < bval; i++) { swlval += ptr2->SGXLen; ptr2++; } /* ??????? */ pcsio->resid = (u_int32_t) swlval; #ifdef AMD_DEBUG0 printf("XferredLen=%8x,NotYetXferLen=%8x,", pSRB->TotalXferredLen, swlval); #endif } if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { #ifdef AMD_DEBUG0 printf("RequestSense..................\n"); #endif RequestSense(amd, pSRB); return; } pcsio->scsi_status = SCSI_STATUS_CHECK_COND; pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; goto ckc_e; } else if (status == SCSI_STATUS_QUEUE_FULL) { pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pcsio->scsi_status = SCSI_STATUS_QUEUE_FULL; pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; goto ckc_e; } else if (status == AMD_SCSI_STAT_SEL_TIMEOUT) { pSRB->AdaptStatus = H_SEL_TIMEOUT; pSRB->TargetStatus = 0; pcsio->scsi_status = AMD_SCSI_STAT_SEL_TIMEOUT; pccb->ccb_h.status = CAM_SEL_TIMEOUT; } else if (status == SCSI_STATUS_BUSY) { #ifdef AMD_DEBUG0 printf("DC390: target busy at %s %d\n", __FILE__, __LINE__); #endif pcsio->scsi_status = SCSI_STATUS_BUSY; pccb->ccb_h.status = CAM_SCSI_BUSY; } else if (status == SCSI_STATUS_RESERV_CONFLICT) { #ifdef AMD_DEBUG0 printf("DC390: target reserved at %s %d\n", __FILE__, __LINE__); #endif pcsio->scsi_status = SCSI_STATUS_RESERV_CONFLICT; pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; /* XXX */ } else { pSRB->AdaptStatus = 0; #ifdef AMD_DEBUG0 printf("DC390: driver stuffup at %s %d\n", __FILE__, __LINE__); #endif pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; } } else { status = pSRB->AdaptStatus; if (status & H_OVER_UNDER_RUN) { pSRB->TargetStatus = 0; pccb->ccb_h.status = CAM_DATA_RUN_ERR; } else if (pSRB->SRBStatus & PARITY_ERROR) { #ifdef AMD_DEBUG0 printf("DC390: driver stuffup %s %d\n", __FILE__, __LINE__); #endif /* Driver failed to perform operation */ pccb->ccb_h.status = CAM_UNCOR_PARITY; } else { /* No error */ pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pcsio->resid = 0; /* there is no error, (sense is invalid) */ } } ckc_e: intflag = splcam(); if ((pccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { /* CAM request not yet complete =>device_Q frozen */ xpt_freeze_devq(pccb->ccb_h.path, 1); pccb->ccb_h.status |= CAM_DEV_QFRZN; } TAILQ_REMOVE(&amd->running_srbs, pSRB, links); TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); amdrunwaiting(amd); splx(intflag); xpt_done(pccb); } static void amd_ResetSCSIBus(struct amd_softc * amd) { int intflag; intflag = splcam(); amd->ACBFlag |= RESET_DEV; amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); amd_write8(amd, SCSICMDREG, RST_SCSI_BUS_CMD); splx(intflag); return; } static void amd_ScsiRstDetect(struct amd_softc * amd) { int intflag; u_int32_t wlval; #ifdef AMD_DEBUG0 printf("amd_ScsiRstDetect \n"); #endif wlval = 1000; while (--wlval) { /* delay 1 sec */ DELAY(1000); } intflag = splcam(); amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); if (amd->ACBFlag & RESET_DEV) { amd->ACBFlag |= RESET_DONE; } else { amd->ACBFlag |= RESET_DETECT; ResetDevParam(amd); amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, AMD_TAG_WILDCARD, &amd->running_srbs, CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, AMD_TAG_WILDCARD, &amd->waiting_srbs, CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); amd->active_srb = NULL; amd->ACBFlag = 0; amdrunwaiting(amd); } splx(intflag); return; } static void RequestSense(struct amd_softc *amd, struct amd_srb *pSRB) { union ccb *pccb; struct ccb_scsiio *pcsio; pccb = pSRB->pccb; pcsio = &pccb->csio; pSRB->SRBFlag |= AUTO_REQSENSE; pSRB->Segment0[0] = *((u_int32_t *) & (pSRB->CmdBlock[0])); pSRB->Segment0[1] = *((u_int32_t *) & (pSRB->CmdBlock[4])); pSRB->Segment1[0] = (pSRB->ScsiCmdLen << 8) + pSRB->SGcount; pSRB->Segment1[1] = pSRB->TotalXferredLen; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pSRB->Segmentx.SGXPtr = amd_get_sense_bufaddr(amd, pSRB); pSRB->Segmentx.SGXLen = amd_get_sense_bufsize(amd, pSRB); pSRB->pSGlist = &pSRB->Segmentx; pSRB->SGcount = 1; pSRB->SGIndex = 0; pSRB->CmdBlock[0] = REQUEST_SENSE; pSRB->CmdBlock[1] = pSRB->pccb->ccb_h.target_lun << 5; pSRB->CmdBlock[2] = 0; pSRB->CmdBlock[3] = 0; pSRB->CmdBlock[4] = pcsio->sense_len; pSRB->CmdBlock[5] = 0; pSRB->ScsiCmdLen = 6; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; if (amdstart(amd, pSRB) != 0) { TAILQ_REMOVE(&amd->running_srbs, pSRB, links); TAILQ_INSERT_HEAD(&amd->waiting_srbs, pSRB, links); } } static void amd_InvalidCmd(struct amd_softc * amd) { struct amd_srb *srb; srb = amd->active_srb; if (srb->SRBState & (SRB_START|SRB_MSGOUT)) amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); } void amd_linkSRB(struct amd_softc *amd) { u_int16_t count, i; struct amd_srb *psrb; count = amd->SRBCount; for (i = 0; i < count; i++) { psrb = (struct amd_srb *)&amd->SRB_array[i]; psrb->TagNumber = i; TAILQ_INSERT_TAIL(&amd->free_srbs, psrb, links); } } static void amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval) { if (mode == ENABLE_CE) { *regval = 0xc0; } else { *regval = 0x80; } pci_write_config(amd->dev, *regval, 0, /*bytes*/1); if (mode == DISABLE_CE) { pci_write_config(amd->dev, *regval, 0, /*bytes*/1); } DELAY(160); } static void amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry) { u_int bval; bval = 0; if (Carry) { bval = 0x40; *regval = 0x80; pci_write_config(amd->dev, *regval, bval, /*bytes*/1); } DELAY(160); bval |= 0x80; pci_write_config(amd->dev, *regval, bval, /*bytes*/1); DELAY(160); pci_write_config(amd->dev, *regval, 0, /*bytes*/1); DELAY(160); } static int amd_EEpromInDO(struct amd_softc *amd) { pci_write_config(amd->dev, 0x80, 0x80, /*bytes*/1); DELAY(160); pci_write_config(amd->dev, 0x80, 0x40, /*bytes*/1); DELAY(160); if (pci_read_config(amd->dev, 0, /*bytes*/1) == 0x22) return (1); return (0); } static u_int16_t EEpromGetData1(struct amd_softc *amd) { u_int i; u_int carryFlag; u_int16_t wval; wval = 0; for (i = 0; i < 16; i++) { wval <<= 1; carryFlag = amd_EEpromInDO(amd); wval |= carryFlag; } return (wval); } static void amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd) { u_int i, j; int carryFlag; carryFlag = 1; j = 0x80; for (i = 0; i < 9; i++) { amd_EEpromOutDI(amd, regval, carryFlag); carryFlag = (EEpromCmd & j) ? 1 : 0; j >>= 1; } } static void amd_ReadEEprom(struct amd_softc *amd) { int regval; u_int i; u_int16_t *ptr; u_int8_t cmd; ptr = (u_int16_t *)&amd->eepromBuf[0]; cmd = EEPROM_READ; for (i = 0; i < 0x40; i++) { amd_EnDisableCE(amd, ENABLE_CE, ®val); amd_Prepare(amd, ®val, cmd); *ptr = EEpromGetData1(amd); ptr++; cmd++; amd_EnDisableCE(amd, DISABLE_CE, ®val); } } static void amd_load_defaults(struct amd_softc *amd) { int target; bzero(&amd->eepromBuf, sizeof amd->eepromBuf); for (target = 0; target < MAX_SCSI_ID; target++) amd->eepromBuf[target << 2] = (TAG_QUEUING|EN_DISCONNECT|SYNC_NEGO|PARITY_CHK); amd->eepromBuf[EE_ADAPT_SCSI_ID] = 7; amd->eepromBuf[EE_MODE2] = ACTIVE_NEGATION|LUN_CHECK|GREATER_1G; amd->eepromBuf[EE_TAG_CMD_NUM] = 4; } static void amd_load_eeprom_or_defaults(struct amd_softc *amd) { u_int16_t wval, *ptr; u_int8_t i; amd_ReadEEprom(amd); wval = 0; ptr = (u_int16_t *) & amd->eepromBuf[0]; for (i = 0; i < EE_DATA_SIZE; i += 2, ptr++) wval += *ptr; if (wval != EE_CHECKSUM) { if (bootverbose) printf("amd%d: SEEPROM data unavailable. " "Using default device parameters.\n", amd->unit); amd_load_defaults(amd); } } /* ********************************************************************** * Function : static int amd_init (struct Scsi_Host *host) * Purpose : initialize the internal structures for a given SCSI host * Inputs : host - pointer to this host adapter's structure/ ********************************************************************** */ static int amd_init(device_t dev) { struct amd_softc *amd = device_get_softc(dev); struct resource *iores; int i, rid; u_int bval; rid = PCI_BASE_ADDR0; iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (iores == NULL) { if (bootverbose) printf("amd_init: bus_alloc_resource failure!\n"); return ENXIO; } amd->tag = rman_get_bustag(iores); amd->bsh = rman_get_bushandle(iores); /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MAXBSIZE, /*nsegments*/AMD_NSEG, /*maxsegsz*/AMD_MAXTRANSFER_SIZE, /*flags*/BUS_DMA_ALLOCNOW, &amd->buffer_dmat) != 0) { if (bootverbose) printf("amd_init: bus_dma_tag_create failure!\n"); return ENXIO; } /* Create, allocate, and map DMA buffers for autosense data */ if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, sizeof(struct scsi_sense_data) * MAX_SRB_CNT, /*nsegments*/1, /*maxsegsz*/AMD_MAXTRANSFER_SIZE, /*flags*/0, &amd->sense_dmat) != 0) { if (bootverbose) device_printf(dev, "cannot create sense buffer dmat\n"); return (ENXIO); } if (bus_dmamem_alloc(amd->sense_dmat, (void **)&amd->sense_buffers, BUS_DMA_NOWAIT, &amd->sense_dmamap) != 0) return (ENOMEM); bus_dmamap_load(amd->sense_dmat, amd->sense_dmamap, amd->sense_buffers, sizeof(struct scsi_sense_data) * MAX_SRB_CNT, amd_dmamap_cb, &amd->sense_busaddr, /*flags*/0); TAILQ_INIT(&amd->free_srbs); TAILQ_INIT(&amd->running_srbs); TAILQ_INIT(&amd->waiting_srbs); amd->last_phase = SCSI_BUS_FREE; amd->dev = dev; amd->unit = device_get_unit(dev); amd->SRBCount = MAX_SRB_CNT; amd->status = 0; amd_load_eeprom_or_defaults(amd); amd->max_id = 7; if (amd->eepromBuf[EE_MODE2] & LUN_CHECK) { amd->max_lun = 7; } else { amd->max_lun = 0; } amd->AdaptSCSIID = amd->eepromBuf[EE_ADAPT_SCSI_ID]; amd->HostID_Bit = (1 << amd->AdaptSCSIID); amd->AdaptSCSILUN = 0; /* (eepromBuf[EE_TAG_CMD_NUM]) << 2; */ amd->ACBFlag = 0; amd->Gmode2 = amd->eepromBuf[EE_MODE2]; amd_linkSRB(amd); for (i = 0; i <= amd->max_id; i++) { if (amd->AdaptSCSIID != i) { struct amd_target_info *tinfo; PEEprom prom; tinfo = &amd->tinfo[i]; prom = (PEEprom)&amd->eepromBuf[i << 2]; if ((prom->EE_MODE1 & EN_DISCONNECT) != 0) { tinfo->disc_tag |= AMD_USR_DISCENB; if ((prom->EE_MODE1 & TAG_QUEUING) != 0) tinfo->disc_tag |= AMD_USR_TAGENB; } if ((prom->EE_MODE1 & SYNC_NEGO) != 0) { tinfo->user.period = eeprom_period[prom->EE_SPEED]; tinfo->user.offset = AMD_MAX_SYNC_OFFSET; } tinfo->CtrlR1 = amd->AdaptSCSIID; if ((prom->EE_MODE1 & PARITY_CHK) != 0) tinfo->CtrlR1 |= PARITY_ERR_REPO; tinfo->CtrlR3 = FAST_CLK; tinfo->CtrlR4 = EATER_25NS; if ((amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) != 0) tinfo->CtrlR4 |= NEGATE_REQACKDATA; } } amd_write8(amd, SCSITIMEOUTREG, 153); /* 250ms selection timeout */ /* Conversion factor = 0 , 40MHz clock */ amd_write8(amd, CLKFACTREG, CLK_FREQ_40MHZ); /* NOP cmd - clear command register */ amd_write8(amd, SCSICMDREG, NOP_CMD); amd_write8(amd, CNTLREG2, EN_FEATURE|EN_SCSI2_CMD); amd_write8(amd, CNTLREG3, FAST_CLK); bval = EATER_25NS; if (amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) { bval |= NEGATE_REQACKDATA; } amd_write8(amd, CNTLREG4, bval); /* Disable SCSI bus reset interrupt */ amd_write8(amd, CNTLREG1, DIS_INT_ON_SCSI_RST); return 0; } /* * attach and init a host adapter */ static int amd_attach(device_t dev) { struct cam_devq *devq; /* Device Queue to use for this SIM */ u_int8_t intstat; struct amd_softc *amd = device_get_softc(dev); int unit = device_get_unit(dev); int rid; void *ih; struct resource *irqres; if (amd_init(dev)) { if (bootverbose) printf("amd_attach: amd_init failure!\n"); return ENXIO; } /* Reset Pending INT */ intstat = amd_read8(amd, INTSTATREG); /* After setting up the adapter, map our interrupt */ rid = 0; irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (irqres == NULL || bus_setup_intr(dev, irqres, INTR_TYPE_CAM | INTR_ENTROPY, amd_intr, amd, &ih)) { if (bootverbose) printf("amd%d: unable to register interrupt handler!\n", unit); return ENXIO; } /* * Now let the CAM generic SCSI layer find the SCSI devices on * the bus * start queue to reset to the idle loop. * * Create device queue of SIM(s) * (MAX_START_JOB - 1) : * max_sim_transactions */ devq = cam_simq_alloc(MAX_START_JOB); if (devq == NULL) { if (bootverbose) printf("amd_attach: cam_simq_alloc failure!\n"); return ENXIO; } amd->psim = cam_sim_alloc(amd_action, amd_poll, "amd", amd, amd->unit, 1, MAX_TAGS_CMD_QUEUE, devq); if (amd->psim == NULL) { cam_simq_free(devq); if (bootverbose) printf("amd_attach: cam_sim_alloc failure!\n"); return ENXIO; } if (xpt_bus_register(amd->psim, 0) != CAM_SUCCESS) { cam_sim_free(amd->psim, /*free_devq*/TRUE); if (bootverbose) printf("amd_attach: xpt_bus_register failure!\n"); return ENXIO; } if (xpt_create_path(&amd->ppath, /* periph */ NULL, cam_sim_path(amd->psim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(amd->psim)); cam_sim_free(amd->psim, /* free_simq */ TRUE); if (bootverbose) printf("amd_attach: xpt_create_path failure!\n"); return ENXIO; } return 0; } static int amd_probe(device_t dev) { if (pci_get_devid(dev) == PCI_DEVICE_ID_AMD53C974) { device_set_desc(dev, "Tekram DC390(T)/AMD53c974 SCSI Host Adapter"); return 0; } return ENXIO; } static device_method_t amd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, amd_probe), DEVMETHOD(device_attach, amd_attach), { 0, 0 } }; static driver_t amd_driver = { "amd", amd_methods, sizeof(struct amd_softc) }; static devclass_t amd_devclass; DRIVER_MODULE(amd, pci, amd_driver, amd_devclass, 0, 0); Index: head/sys/dev/amr/amr_cam.c =================================================================== --- head/sys/dev/amr/amr_cam.c (revision 116350) +++ head/sys/dev/amr/amr_cam.c (revision 116351) @@ -1,629 +1,614 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 2002 Eric Moore * Copyright (c) 2002 LSI Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The party using or redistributing the source code and binary forms * agrees to the disclaimer below and the terms and conditions set forth * herein. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void amr_cam_action(struct cam_sim *sim, union ccb *ccb); static void amr_cam_poll(struct cam_sim *sim); static void amr_cam_complete(struct amr_command *ac); static void amr_cam_complete_extcdb(struct amr_command *ac); /******************************************************************************** * Enqueue/dequeue functions */ static __inline void amr_enqueue_ccb(struct amr_softc *sc, union ccb *ccb) { int s; s = splbio(); TAILQ_INSERT_TAIL(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe); splx(s); } static __inline void amr_requeue_ccb(struct amr_softc *sc, union ccb *ccb) { int s; s = splbio(); TAILQ_INSERT_HEAD(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe); splx(s); } static __inline union ccb * amr_dequeue_ccb(struct amr_softc *sc) { union ccb *ccb; int s; s = splbio(); if ((ccb = (union ccb *)TAILQ_FIRST(&sc->amr_cam_ccbq)) != NULL) TAILQ_REMOVE(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe); splx(s); return(ccb); } /******************************************************************************** * Attach our 'real' SCSI channels to CAM */ int amr_cam_attach(struct amr_softc *sc) { struct cam_devq *devq; int chn; /* initialise the ccb queue */ TAILQ_INIT(&sc->amr_cam_ccbq); /* * Allocate a devq for all our channels combined. This should * allow for the maximum number of SCSI commands we will accept * at one time. */ if ((devq = cam_simq_alloc(AMR_MAX_SCSI_CMDS)) == NULL) return(ENOMEM); /* * Iterate over our channels, registering them with CAM */ for (chn = 0; chn < sc->amr_maxchan; chn++) { /* allocate a sim */ if ((sc->amr_cam_sim[chn] = cam_sim_alloc(amr_cam_action, amr_cam_poll, "amr", sc, device_get_unit(sc->amr_dev), 1, AMR_MAX_SCSI_CMDS, devq)) == NULL) { cam_simq_free(devq); device_printf(sc->amr_dev, "CAM SIM attach failed\n"); return(ENOMEM); } /* register the bus ID so we can get it later */ if (xpt_bus_register(sc->amr_cam_sim[chn], chn)) { device_printf(sc->amr_dev, "CAM XPT bus registration failed\n"); return(ENXIO); } } /* * XXX we should scan the config and work out which devices are actually * protected. */ return(0); } /******************************************************************************** * Disconnect ourselves from CAM */ void amr_cam_detach(struct amr_softc *sc) { int chn, first; for (chn = 0, first = 1; chn < sc->amr_maxchan; chn++) { /* * If a sim was allocated for this channel, free it */ if (sc->amr_cam_sim[chn] != NULL) { xpt_bus_deregister(cam_sim_path(sc->amr_cam_sim[chn])); cam_sim_free(sc->amr_cam_sim[chn], first ? TRUE : FALSE); first = 0; } } } /******************************************************************************** ******************************************************************************** CAM passthrough interface ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Handle a request for action from CAM */ static void amr_cam_action(struct cam_sim *sim, union ccb *ccb) { struct amr_softc *sc = cam_sim_softc(sim); switch(ccb->ccb_h.func_code) { /* * Perform SCSI I/O to a physical device. */ case XPT_SCSI_IO: { struct ccb_hdr *ccbh = &ccb->ccb_h; struct ccb_scsiio *csio = &ccb->csio; /* Validate the CCB */ ccbh->status = CAM_REQ_INPROG; /* check the CDB length */ if (csio->cdb_len > AMR_MAX_EXTCDB_LEN) ccbh->status = CAM_REQ_CMP_ERR; if ((csio->cdb_len > AMR_MAX_CDB_LEN) && (sc->support_ext_cdb == 0 )) ccbh->status = CAM_REQ_CMP_ERR; /* check that the CDB pointer is not to a physical address */ if ((ccbh->flags & CAM_CDB_POINTER) && (ccbh->flags & CAM_CDB_PHYS)) ccbh->status = CAM_REQ_CMP_ERR; /* if there is data transfer, it must be to/from a virtual address */ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if (ccbh->flags & CAM_DATA_PHYS) /* we can't map it */ ccbh->status = CAM_REQ_CMP_ERR; if (ccbh->flags & CAM_SCATTER_VALID) /* we want to do the s/g setup */ ccbh->status = CAM_REQ_CMP_ERR; } /* * If the command is to a LUN other than 0, fail it. * This is probably incorrect, but during testing the firmware did not * seem to respect the LUN field, and thus devices appear echoed. */ if (csio->ccb_h.target_lun != 0) ccbh->status = CAM_REQ_CMP_ERR; /* if we're happy with the request, queue it for attention */ if (ccbh->status == CAM_REQ_INPROG) { /* save the channel number in the ccb */ csio->ccb_h.sim_priv.entries[0].field = cam_sim_bus(sim); amr_enqueue_ccb(sc, ccb); amr_startio(sc); return; } break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg = &ccb->ccg; - u_int32_t size_in_mb; - u_int32_t secs_per_cylinder; - - size_in_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); - - if (size_in_mb > 1024) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); break; } /* * Return path stats. Some of these should probably be * amended. */ case XPT_PATH_INQ: { struct ccb_pathinq *cpi = & ccb->cpi; debug(3, "XPT_PATH_INQ"); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = AMR_MAX_TARGETS; cpi->max_lun = 0 /* AMR_MAX_LUNS*/; cpi->initiator_id = 7; /* XXX variable? */ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "LSI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 132 * 1024; /* XXX get from controller? */ cpi->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: { struct ccb_pathinq *cpi = & ccb->cpi; debug(1, "XPT_RESET_BUS"); cpi->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_DEV: { debug(1, "XPT_RESET_DEV"); ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; debug(3, "XPT_GET_TRAN_SETTINGS"); cts = &(ccb->cts); if ((cts->flags & CCB_TRANS_USER_SETTINGS) == 0) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; } cts->flags = CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB; cts->bus_width = MSG_EXT_WDTR_BUS_32_BIT; cts->sync_period = 6; /* 40MHz how wide is this bus? */ cts->sync_offset = 31; /* How to extract this from board? */ cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_SET_TRAN_SETTINGS: debug(3, "XPT_SET_TRAN_SETTINGS"); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; /* * Reject anything else as unsupported. */ default: /* we can't do this */ ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } /******************************************************************************** * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI command. */ int amr_cam_command(struct amr_softc *sc, struct amr_command **acp) { struct amr_command *ac; struct amr_passthrough *ap; struct amr_ext_passthrough *aep; struct ccb_scsiio *csio; int bus, target, error; error = 0; ac = NULL; ap = NULL; aep = NULL; /* check to see if there is a ccb for us to work with */ if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL) goto out; /* get bus/target, XXX validate against protected devices? */ bus = csio->ccb_h.sim_priv.entries[0].field; target = csio->ccb_h.target_id; /* * Build a passthrough command. */ /* construct passthrough */ if (sc->support_ext_cdb ) { if ((aep = malloc(sizeof(*aep), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; goto out; } aep->ap_timeout = 2; aep->ap_ars = 1; aep->ap_request_sense_length = 14; aep->ap_islogical = 0; aep->ap_channel = bus; aep->ap_scsi_id = target; aep->ap_logical_drive_no = csio->ccb_h.target_lun; aep->ap_cdb_length = csio->cdb_len; aep->ap_data_transfer_length = csio->dxfer_len; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, aep->ap_cdb, csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, aep->ap_cdb, csio->cdb_len); } /* we leave the data s/g list and s/g count to the map routine later */ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep->ap_cdb[0], aep->ap_cdb_length, csio->dxfer_len, aep->ap_channel, aep->ap_scsi_id, aep->ap_logical_drive_no); } else { if ((ap = malloc(sizeof(*ap), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; goto out; } ap->ap_timeout = 0; ap->ap_ars = 1; ap->ap_request_sense_length = 14; ap->ap_islogical = 0; ap->ap_channel = bus; ap->ap_scsi_id = target; ap->ap_logical_drive_no = csio->ccb_h.target_lun; ap->ap_cdb_length = csio->cdb_len; ap->ap_data_transfer_length = csio->dxfer_len; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb, csio->cdb_len); } /* we leave the data s/g list and s/g count to the map routine later */ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0], ap->ap_cdb_length, csio->dxfer_len, ap->ap_channel, ap->ap_scsi_id, ap->ap_logical_drive_no); } /* construct command */ if ((ac = amr_alloccmd(sc)) == NULL) { error = ENOMEM; goto out; } ac->ac_flags |= AMR_CMD_DATAOUT; ac->ac_ccb_data = csio->data_ptr; ac->ac_ccb_length = csio->dxfer_len; if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) ac->ac_flags |= AMR_CMD_CCB_DATAIN; if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) ac->ac_flags |= AMR_CMD_CCB_DATAOUT; ac->ac_private = csio; if ( sc->support_ext_cdb ) { ac->ac_data = aep; ac->ac_length = sizeof(*aep); ac->ac_complete = amr_cam_complete_extcdb; ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS; } else { ac->ac_data = ap; ac->ac_length = sizeof(*ap); ac->ac_complete = amr_cam_complete; ac->ac_mailbox.mb_command = AMR_CMD_PASS; } out: if (error != 0) { if (ac != NULL) amr_releasecmd(ac); if (ap != NULL) free(ap, M_DEVBUF); if (aep != NULL) free(aep, M_DEVBUF); if (csio != NULL) /* put it back and try again later */ amr_requeue_ccb(sc, (union ccb *)csio); } *acp = ac; return(error); } /******************************************************************************** * Check for interrupt status */ static void amr_cam_poll(struct cam_sim *sim) { amr_done(cam_sim_softc(sim)); } /******************************************************************************** * Handle completion of a command submitted via CAM. */ static void amr_cam_complete(struct amr_command *ac) { struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data; struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private; struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr; /* XXX note that we're ignoring ac->ac_status - good idea? */ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, ap->ap_scsi_status); /* * Hide disks from CAM so that they're not picked up and treated as 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not managed", we * could add handling for that to allow disks to be selectively visible. */ if ((ap->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) { bzero(csio->data_ptr, csio->dxfer_len); if (ap->ap_scsi_status == 0xf0) { csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; } else { csio->ccb_h.status = CAM_DEV_NOT_THERE; } } else { /* handle passthrough SCSI status */ switch(ap->ap_scsi_status) { case 0: /* completed OK */ csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: csio->ccb_h.status = CAM_REQ_CMP_ERR; break; } } free(ap, M_DEVBUF); if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); xpt_done((union ccb *)csio); amr_releasecmd(ac); } /******************************************************************************** * Handle completion of a command submitted via CAM. * Completion for extended cdb */ static void amr_cam_complete_extcdb(struct amr_command *ac) { struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data; struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private; struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr; /* XXX note that we're ignoring ac->ac_status - good idea? */ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, aep->ap_scsi_status); /* * Hide disks from CAM so that they're not picked up and treated as 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not managed", we * could add handling for that to allow disks to be selectively visible. */ if ((aep->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) { bzero(csio->data_ptr, csio->dxfer_len); if (aep->ap_scsi_status == 0xf0) { csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; } else { csio->ccb_h.status = CAM_DEV_NOT_THERE; } } else { /* handle passthrough SCSI status */ switch(aep->ap_scsi_status) { case 0: /* completed OK */ csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: csio->ccb_h.status = CAM_REQ_CMP_ERR; break; } } free(aep, M_DEVBUF); if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); xpt_done((union ccb *)csio); amr_releasecmd(ac); } Index: head/sys/dev/ata/atapi-cam.c =================================================================== --- head/sys/dev/ata/atapi-cam.c (revision 116350) +++ head/sys/dev/ata/atapi-cam.c (revision 116351) @@ -1,700 +1,682 @@ /*- * Copyright (c) 2001,2002 Thomas Quinot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* hardware command descriptor block */ struct atapi_hcb { struct atapi_xpt_softc *softc; int unit; int bus; int target; int lun; union ccb *ccb; u_int8_t cmd[CAM_MAX_CDBLEN]; int flags; #define DOING_AUTOSENSE 1 char *dxfer_alloc; TAILQ_ENTRY(atapi_hcb) chain; }; /* private data associated with an ATA bus */ struct atapi_xpt_softc { struct ata_channel *ata_ch; struct cam_path *path; struct cam_sim *sim; int flags; #define BUS_REGISTERED 0x01 #define RESOURCE_SHORTAGE 0x02 TAILQ_HEAD(,atapi_hcb) pending_hcbs; LIST_ENTRY(atapi_xpt_softc) chain; }; enum reinit_reason { BOOT_ATTACH, ATTACH, RESET }; static LIST_HEAD(,atapi_xpt_softc) all_buses = LIST_HEAD_INITIALIZER(all_buses); /* CAM XPT methods */ static void atapi_action(struct cam_sim *, union ccb *); static void atapi_poll(struct cam_sim *); static void atapi_async(void *, u_int32_t, struct cam_path *, void *); static void atapi_async1(void *, u_int32_t, struct cam_path *, void *); static int atapi_cb(struct atapi_request *); /* internal functions */ static void reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason); static void setup_dev(struct atapi_xpt_softc *, struct ata_device *); static void setup_async_cb(struct atapi_xpt_softc *, uint32_t); static void cam_rescan_callback(struct cam_periph *, union ccb *); static void cam_rescan(struct cam_sim *); static void free_hcb_and_ccb_done(struct atapi_hcb *, u_int32_t); static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc *, int, int, union ccb *); static void free_hcb(struct atapi_hcb *hcb); static void free_softc(struct atapi_xpt_softc *scp); static struct atapi_xpt_softc *get_softc(struct ata_channel *ata_ch); static struct ata_device *get_ata_device(struct atapi_xpt_softc *scp, int id); static MALLOC_DEFINE(M_ATACAM, "ATA CAM transport", "ATA driver CAM-XPT layer"); void atapi_cam_attach_bus(struct ata_channel *ata_ch) { struct atapi_xpt_softc *scp = NULL; struct cam_devq *devq = NULL; struct cam_sim *sim = NULL; struct cam_path *path = NULL; int unit; LIST_FOREACH(scp, &all_buses, chain) { if (scp->ata_ch == ata_ch) return; } if ((scp = malloc(sizeof(struct atapi_xpt_softc), M_ATACAM, M_NOWAIT | M_ZERO)) == NULL) goto error; scp->ata_ch = ata_ch; TAILQ_INIT(&scp->pending_hcbs); LIST_INSERT_HEAD(&all_buses, scp, chain); unit = device_get_unit(ata_ch->dev); if ((devq = cam_simq_alloc(16)) == NULL) goto error; if ((sim = cam_sim_alloc(atapi_action, atapi_poll, "ata", (void *)scp, unit, 1, 1, devq)) == NULL) { cam_simq_free(devq); goto error; } scp->sim = sim; if (xpt_bus_register(sim, 0) != CAM_SUCCESS) { goto error; } scp->flags |= BUS_REGISTERED; if (xpt_create_path(&path, /*periph*/ NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { goto error; } scp->path = path; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Registered SIM for ata%d\n", unit)); setup_async_cb(scp, AC_LOST_DEVICE); reinit_bus(scp, cold ? BOOT_ATTACH : ATTACH); return; error: free_softc(scp); } void atapi_cam_detach_bus(struct ata_channel *ata_ch) { struct atapi_xpt_softc *scp = get_softc(ata_ch); free_softc(scp); } void atapi_cam_reinit_bus(struct ata_channel *ata_ch) { struct atapi_xpt_softc *scp = get_softc(ata_ch); /* * scp might be null if the bus is being reinitialised during * the boot-up sequence, before the ATAPI bus is registered. */ if (scp != NULL) reinit_bus(scp, RESET); } static void reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason) { if (scp->ata_ch->devices & ATA_ATAPI_MASTER) setup_dev(scp, &scp->ata_ch->device[MASTER]); if (scp->ata_ch->devices & ATA_ATAPI_SLAVE) setup_dev(scp, &scp->ata_ch->device[SLAVE]); switch (reason) { case BOOT_ATTACH: break; case RESET: xpt_async(AC_BUS_RESET, scp->path, NULL); /*FALLTHROUGH*/ case ATTACH: cam_rescan(scp->sim); break; } } static void setup_dev(struct atapi_xpt_softc *scp, struct ata_device *atp) { if (atp->driver == NULL) { ata_set_name(atp, "atapicam", 2 * device_get_unit(atp->channel->dev) + (atp->unit == ATA_MASTER) ? 0 : 1); atp->driver = (void *)scp; } } static void setup_async_cb(struct atapi_xpt_softc *scp, uint32_t events) { struct ccb_setasync csa; xpt_setup_ccb(&csa.ccb_h, scp->path, /*priority*/ 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = events; csa.callback = &atapi_async; csa.callback_arg = scp->sim; xpt_action((union ccb *) &csa); } static void atapi_action(struct cam_sim *sim, union ccb *ccb) { struct atapi_xpt_softc *softc = (struct atapi_xpt_softc*)cam_sim_softc(sim); struct ccb_hdr *ccb_h = &ccb->ccb_h; struct atapi_hcb *hcb = NULL; int unit = cam_sim_unit(sim); int bus = cam_sim_bus(sim); int len, s; char *buf; switch (ccb_h->func_code) { case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags)); cpi->max_target = 1; cpi->max_lun = 0; cpi->async_flags = 0; cpi->hpath_id = 0; cpi->initiator_id = 7; strncpy(cpi->sim_vid, "FreeBSD", sizeof(cpi->sim_vid)); strncpy(cpi->hba_vid, "ATAPI", sizeof(cpi->hba_vid)); strncpy(cpi->dev_name, cam_sim_name(sim), sizeof cpi->dev_name); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); if (softc->ata_ch && ccb_h->target_id >= 0) { switch (softc->ata_ch->device[ccb_h->target_id].mode) { case ATA_PIO1: cpi->base_transfer_speed = 5200; break; case ATA_PIO2: cpi->base_transfer_speed = 7000; break; case ATA_PIO3: cpi->base_transfer_speed = 11000; break; case ATA_PIO4: case ATA_DMA: case ATA_WDMA2: cpi->base_transfer_speed = 16000; break; case ATA_UDMA2: cpi->base_transfer_speed = 33000; break; case ATA_UDMA4: cpi->base_transfer_speed = 66000; break; case ATA_UDMA5: cpi->base_transfer_speed = 100000; break; case ATA_UDMA6: cpi->base_transfer_speed = 133000; break; default: cpi->base_transfer_speed = 3300; } } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_RESET_DEV: { int tid = ccb_h->target_id; struct ata_device *dev = get_ata_device(softc, tid); CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("dev reset\n")); atapi_reinit(dev); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_RESET_BUS: CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("bus reset\n")); ata_reinit(softc->ata_ch); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; case XPT_SET_TRAN_SETTINGS: /* ignore these, we're not doing SCSI here */ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("SET_TRAN_SETTINGS not supported\n")); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); return; case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; /* * XXX The default CAM transport code is very scsi specific and * doesn't understand IDE speeds very well. Be silent about it * here and let it default to what is set in XPT_PATH_INQ */ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("GET_TRAN_SETTINGS\n")); cts->valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID); cts->flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - unsigned int size_mb; - unsigned int secs_per_cylinder; - int extended; - CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("CALC_GEOMETRY\n")); - ccg = &ccb->ccg; - size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); - extended = 1; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); return; } case XPT_SCSI_IO: { struct ccb_scsiio *csio = &ccb->csio; int tid = ccb_h->target_id, lid = ccb_h->target_lun; struct ata_device *dev = get_ata_device(softc, tid); CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("XPT_SCSI_IO\n")); /* check that this request was not aborted already */ if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { printf("XPT_SCSI_IO received but already in progress?\n"); xpt_done(ccb); return; } if (dev == NULL) { CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("SCSI IO received for invalid device\n")); ccb_h->status = CAM_TID_INVALID; xpt_done(ccb); return; } if (lid > 0) { CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("SCSI IO received for invalid lun %d\n", lid)); ccb_h->status = CAM_LUN_INVALID; xpt_done(ccb); return; } if ((ccb_h->flags & CAM_SCATTER_VALID)) { /* scatter-gather not supported */ xpt_print_path(ccb_h->path); printf("ATAPI-CAM does not support scatter-gather yet!\n"); break; } if ((hcb = allocate_hcb(softc, unit, bus, ccb)) == NULL) goto action_oom; ccb_h->status |= CAM_SIM_QUEUED; bcopy((ccb_h->flags & CAM_CDB_POINTER) ? csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes, hcb->cmd, csio->cdb_len); #ifdef CAMDEBUG if (CAM_DEBUGGED(ccb_h->path, CAM_DEBUG_CDB)) { char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; printf("atapi_action: hcb@%p: %s\n", hcb, scsi_cdb_string(hcb->cmd, cdb_str, sizeof(cdb_str))); } #endif len = csio->dxfer_len; buf = csio->data_ptr; /* some SCSI commands require special processing */ switch (hcb->cmd[0]) { case INQUIRY: { /* * many ATAPI devices seem to report more than * SHORT_INQUIRY_LENGTH bytes of available INQUIRY * information, but respond with some incorrect condition * when actually asked for it, so we are going to pretend * that only SHORT_INQUIRY_LENGTH are expected, anyway. */ struct scsi_inquiry *inq = (struct scsi_inquiry *) &hcb->cmd[0]; if (inq->byte2 == 0 && inq->page_code == 0 && inq->length > SHORT_INQUIRY_LENGTH) { bzero(buf, len); len = inq->length = SHORT_INQUIRY_LENGTH; } break; } case READ_6: /* FALLTHROUGH */ case WRITE_6: CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("Translating %s into _10 equivalent\n", (hcb->cmd[0] == READ_6) ? "READ_6" : "WRITE_6")); hcb->cmd[0] |= 0x20; hcb->cmd[9] = hcb->cmd[5]; hcb->cmd[8] = hcb->cmd[4]; hcb->cmd[7] = 0; hcb->cmd[6] = 0; hcb->cmd[5] = hcb->cmd[3]; hcb->cmd[4] = hcb->cmd[2]; hcb->cmd[3] = hcb->cmd[1] & 0x1f; hcb->cmd[2] = 0; hcb->cmd[1] = 0; break; } if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN && (len & 1)) { /* ATA always transfers an even number of bytes */ if (!(buf = hcb->dxfer_alloc = malloc(++len, M_ATACAM, M_NOWAIT | M_ZERO))) goto action_oom; } s = splbio(); TAILQ_INSERT_TAIL(&softc->pending_hcbs, hcb, chain); splx(s); if (atapi_queue_cmd(dev, hcb->cmd, buf, len, (((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) ? ATPR_F_READ : 0) | ATPR_F_QUIET, ccb_h->timeout, atapi_cb, (void *)hcb) == 0) return; break; } default: CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("unsupported function code 0x%02x\n", ccb_h->func_code)); ccb_h->status = CAM_REQ_INVALID; xpt_done(ccb); return; } action_oom: if (hcb != NULL) free_hcb(hcb); xpt_print_path(ccb_h->path); printf("out of memory, freezing queue.\n"); softc->flags |= RESOURCE_SHORTAGE; xpt_freeze_simq(sim, /*count*/ 1); ccb_h->status = CAM_REQUEUE_REQ; xpt_done(ccb); } static void atapi_poll(struct cam_sim *sim) { /* do nothing - we do not actually service any interrupts */ printf("atapi_poll called!\n"); } static int atapi_cb(struct atapi_request *req) { struct atapi_hcb *hcb = (struct atapi_hcb *) req->driver; struct ccb_scsiio *csio = &hcb->ccb->csio; int hcb_status = req->result; int s = splbio(); #ifdef CAMDEBUG if (CAM_DEBUGGED(csio->ccb_h.path, CAM_DEBUG_CDB)) { printf("atapi_cb: hcb@%p status = %02x: (sk = %02x%s%s%s)\n", hcb, hcb_status, hcb_status >> 4, (hcb_status & 4) ? " ABRT" : "", (hcb_status & 2) ? " EOM" : "", (hcb_status & 1) ? " ILI" : ""); printf(" %s: cmd %02x - sk=%02x asc=%02x ascq=%02x\n", req->device->name, req->ccb[0], req->sense.sense_key, req->sense.asc, req->sense.ascq); } #endif if (hcb_status != 0) { csio->scsi_status = SCSI_STATUS_CHECK_COND; if ((csio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { csio->ccb_h.status |= CAM_AUTOSNS_VALID; bcopy((void *)&req->sense, (void *)&csio->sense_data, sizeof(struct atapi_reqsense)); } free_hcb_and_ccb_done(hcb, CAM_SCSI_STATUS_ERROR); } else { if (((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) && hcb->dxfer_alloc != NULL) bcopy(hcb->dxfer_alloc, csio->data_ptr, csio->dxfer_len); csio->scsi_status = SCSI_STATUS_OK; free_hcb_and_ccb_done(hcb, CAM_REQ_CMP); } splx(s); return 0; } static void free_hcb_and_ccb_done(struct atapi_hcb *hcb, u_int32_t status) { struct atapi_xpt_softc *softc = hcb->softc; union ccb *ccb = hcb->ccb; if (hcb != NULL) { /* we're about to free a hcb, so the shortage has ended */ if (softc->flags & RESOURCE_SHORTAGE) { softc->flags &= ~RESOURCE_SHORTAGE; status |= CAM_RELEASE_SIMQ; } free_hcb(hcb); } ccb->ccb_h.status = status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED)); xpt_done(ccb); } static void atapi_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { int s = splbio(); atapi_async1(callback_arg, code, path, arg); splx(s); } static void atapi_async1(void *callback_arg, u_int32_t code, struct cam_path* path, void *arg) { struct atapi_xpt_softc *softc; struct cam_sim *sim; int targ; sim = (struct cam_sim *) callback_arg; softc = (struct atapi_xpt_softc *) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: targ = xpt_path_target_id(path); xpt_print_path(path); if (targ == -1) printf("Lost host adapter\n"); else printf("Lost target %d???\n", targ); break; default: break; } } static void cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) { if (ccb->ccb_h.status != CAM_REQ_CMP) { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescan failed, 0x%04x\n", ccb->ccb_h.status)); } else { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescan succeeded\n")); } xpt_free_path(ccb->ccb_h.path); free(ccb, M_ATACAM); } static void cam_rescan(struct cam_sim *sim) { struct cam_path *path; union ccb *ccb = malloc(sizeof(union ccb), M_ATACAM, M_WAITOK | M_ZERO); if (xpt_create_path(&path, xpt_periph, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) return; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescanning ATAPI bus.\n")); xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); ccb->ccb_h.func_code = XPT_SCAN_BUS; ccb->ccb_h.cbfcnp = cam_rescan_callback; ccb->crcn.flags = CAM_FLAG_NONE; xpt_action(ccb); /* scan is in progress now */ } static struct atapi_hcb * allocate_hcb(struct atapi_xpt_softc *softc, int unit, int bus, union ccb *ccb) { struct atapi_hcb *hcb = (struct atapi_hcb *) malloc(sizeof(struct atapi_hcb), M_ATACAM, M_NOWAIT | M_ZERO); if (hcb != NULL) { hcb->softc = softc; hcb->unit = unit; hcb->bus = bus; hcb->ccb = ccb; } return hcb; } static void free_hcb(struct atapi_hcb *hcb) { TAILQ_REMOVE(&hcb->softc->pending_hcbs, hcb, chain); if (hcb->dxfer_alloc != NULL) free(hcb->dxfer_alloc, M_ATACAM); free(hcb, M_ATACAM); } static void free_softc(struct atapi_xpt_softc *scp) { struct atapi_hcb *hcb; if (scp != NULL) { TAILQ_FOREACH(hcb, &scp->pending_hcbs, chain) { free_hcb_and_ccb_done(hcb, CAM_UNREC_HBA_ERROR); } if (scp->path != NULL) { setup_async_cb(scp, 0); xpt_free_path(scp->path); } if ((scp->flags & BUS_REGISTERED) != 0) { if (xpt_bus_deregister(cam_sim_path(scp->sim)) == CAM_REQ_CMP) scp->flags &= ~BUS_REGISTERED; } if (scp->sim != NULL) { if ((scp->flags & BUS_REGISTERED) == 0) cam_sim_free(scp->sim, /*free_devq*/TRUE); else printf("Can't free %s SIM (still registered)\n", cam_sim_name(scp->sim)); } LIST_REMOVE(scp, chain); free(scp, M_ATACAM); } } static struct atapi_xpt_softc * get_softc(struct ata_channel *ata_ch) { struct atapi_xpt_softc *scp; LIST_FOREACH(scp, &all_buses, chain) { if (scp->ata_ch == ata_ch) return scp; } return NULL; } static struct ata_device * get_ata_device(struct atapi_xpt_softc *scp, int id) { int role = ATA_ATAPI_MASTER; switch (id) { case 1: role = ATA_ATAPI_SLAVE; /* FALLTHROUGH */ case 0: if (scp->ata_ch->devices & role) return &scp->ata_ch->device[id]; /* FALLTHROUGH */ default: return NULL; } } Index: head/sys/dev/dpt/dpt_scsi.c =================================================================== --- head/sys/dev/dpt/dpt_scsi.c (revision 116350) +++ head/sys/dev/dpt/dpt_scsi.c (revision 116351) @@ -1,2724 +1,2705 @@ /* * Copyright (c) 1997 by Simon Shapiro * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * dpt_scsi.c: SCSI dependant code for the DPT driver * * credits: Assisted by Mike Neuffer in the early low level DPT code * Thanx to Mark Salyzyn of DPT for his assistance. * Special thanx to Justin Gibbs for invaluable help in * making this driver look and work like a FreeBSD component. * Last but not least, many thanx to UCB and the FreeBSD * team for creating and maintaining such a wonderful O/S. * * TODO: * Add ISA probe code. * * Add driver-level RAID-0. This will allow interoperability with * NiceTry, M$-Doze, Win-Dog, Slowlaris, etc., in recognizing RAID * arrays that span controllers (Wow!). */ #ident "$FreeBSD$" #define _DPT_C_ #include "opt_dpt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* dpt_isa.c, dpt_eisa.c, and dpt_pci.c need this in a central place */ int dpt_controllers_present; devclass_t dpt_devclass; #define microtime_now dpt_time_now() #define dpt_inl(dpt, port) \ bus_space_read_4((dpt)->tag, (dpt)->bsh, port) #define dpt_inb(dpt, port) \ bus_space_read_1((dpt)->tag, (dpt)->bsh, port) #define dpt_outl(dpt, port, value) \ bus_space_write_4((dpt)->tag, (dpt)->bsh, port, value) #define dpt_outb(dpt, port, value) \ bus_space_write_1((dpt)->tag, (dpt)->bsh, port, value) /* * These will have to be setup by parameters passed at boot/load time. For * perfromance reasons, we make them constants for the time being. */ #define dpt_min_segs DPT_MAX_SEGS #define dpt_max_segs DPT_MAX_SEGS /* Definitions for our use of the SIM private CCB area */ #define ccb_dccb_ptr spriv_ptr0 #define ccb_dpt_ptr spriv_ptr1 /* ================= Private Inline Function declarations ===================*/ static __inline int dpt_just_reset(dpt_softc_t * dpt); static __inline int dpt_raid_busy(dpt_softc_t * dpt); static __inline int dpt_pio_wait (u_int32_t, u_int, u_int, u_int); static __inline int dpt_wait(dpt_softc_t *dpt, u_int bits, u_int state); static __inline struct dpt_ccb* dptgetccb(struct dpt_softc *dpt); static __inline void dptfreeccb(struct dpt_softc *dpt, struct dpt_ccb *dccb); static __inline bus_addr_t dptccbvtop(struct dpt_softc *dpt, struct dpt_ccb *dccb); static __inline int dpt_send_immediate(dpt_softc_t *dpt, eata_ccb_t *cmd_block, u_int32_t cmd_busaddr, u_int retries, u_int ifc, u_int code, u_int code2); /* ==================== Private Function declarations =======================*/ static void dptmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error); static struct sg_map_node* dptallocsgmap(struct dpt_softc *dpt); static int dptallocccbs(dpt_softc_t *dpt); static int dpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr, u_int size, u_int page, u_int target, int extent); static void dpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr, u_int8_t *buff); static void dpt_poll(struct cam_sim *sim); static void dptexecuteccb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error); static void dpt_action(struct cam_sim *sim, union ccb *ccb); static int dpt_send_eata_command(dpt_softc_t *dpt, eata_ccb_t *cmd, u_int32_t cmd_busaddr, u_int command, u_int retries, u_int ifc, u_int code, u_int code2); static void dptprocesserror(dpt_softc_t *dpt, dpt_ccb_t *dccb, union ccb *ccb, u_int hba_stat, u_int scsi_stat, u_int32_t resid); static void dpttimeout(void *arg); static void dptshutdown(void *arg, int howto); /* ================= Private Inline Function definitions ====================*/ static __inline int dpt_just_reset(dpt_softc_t * dpt) { if ((dpt_inb(dpt, 2) == 'D') && (dpt_inb(dpt, 3) == 'P') && (dpt_inb(dpt, 4) == 'T') && (dpt_inb(dpt, 5) == 'H')) return (1); else return (0); } static __inline int dpt_raid_busy(dpt_softc_t * dpt) { if ((dpt_inb(dpt, 0) == 'D') && (dpt_inb(dpt, 1) == 'P') && (dpt_inb(dpt, 2) == 'T')) return (1); else return (0); } static __inline int dpt_pio_wait (u_int32_t base, u_int reg, u_int bits, u_int state) { int i; u_int c; for (i = 0; i < 20000; i++) { /* wait 20ms for not busy */ c = inb(base + reg) & bits; if (!(c == state)) return (0); else DELAY(50); } return (-1); } static __inline int dpt_wait(dpt_softc_t *dpt, u_int bits, u_int state) { int i; u_int c; for (i = 0; i < 20000; i++) { /* wait 20ms for not busy */ c = dpt_inb(dpt, HA_RSTATUS) & bits; if (c == state) return (0); else DELAY(50); } return (-1); } static __inline struct dpt_ccb* dptgetccb(struct dpt_softc *dpt) { struct dpt_ccb* dccb; int s; s = splcam(); if ((dccb = SLIST_FIRST(&dpt->free_dccb_list)) != NULL) { SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links); dpt->free_dccbs--; } else if (dpt->total_dccbs < dpt->max_dccbs) { dptallocccbs(dpt); dccb = SLIST_FIRST(&dpt->free_dccb_list); if (dccb == NULL) printf("dpt%d: Can't malloc DCCB\n", dpt->unit); else { SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links); dpt->free_dccbs--; } } splx(s); return (dccb); } static __inline void dptfreeccb(struct dpt_softc *dpt, struct dpt_ccb *dccb) { int s; s = splcam(); if ((dccb->state & DCCB_ACTIVE) != 0) LIST_REMOVE(&dccb->ccb->ccb_h, sim_links.le); if ((dccb->state & DCCB_RELEASE_SIMQ) != 0) dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; else if (dpt->resource_shortage != 0 && (dccb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; dpt->resource_shortage = FALSE; } dccb->state = DCCB_FREE; SLIST_INSERT_HEAD(&dpt->free_dccb_list, dccb, links); ++dpt->free_dccbs; splx(s); } static __inline bus_addr_t dptccbvtop(struct dpt_softc *dpt, struct dpt_ccb *dccb) { return (dpt->dpt_ccb_busbase + (u_int32_t)((caddr_t)dccb - (caddr_t)dpt->dpt_dccbs)); } static __inline struct dpt_ccb * dptccbptov(struct dpt_softc *dpt, bus_addr_t busaddr) { return (dpt->dpt_dccbs + ((struct dpt_ccb *)busaddr - (struct dpt_ccb *)dpt->dpt_ccb_busbase)); } /* * Send a command for immediate execution by the DPT * See above function for IMPORTANT notes. */ static __inline int dpt_send_immediate(dpt_softc_t *dpt, eata_ccb_t *cmd_block, u_int32_t cmd_busaddr, u_int retries, u_int ifc, u_int code, u_int code2) { return (dpt_send_eata_command(dpt, cmd_block, cmd_busaddr, EATA_CMD_IMMEDIATE, retries, ifc, code, code2)); } /* ===================== Private Function definitions =======================*/ static void dptmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *busaddrp; busaddrp = (bus_addr_t *)arg; *busaddrp = segs->ds_addr; } static struct sg_map_node * dptallocsgmap(struct dpt_softc *dpt) { struct sg_map_node *sg_map; sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT); if (sg_map == NULL) return (NULL); /* Allocate S/G space for the next batch of CCBS */ if (bus_dmamem_alloc(dpt->sg_dmat, (void **)&sg_map->sg_vaddr, BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) { free(sg_map, M_DEVBUF); return (NULL); } (void)bus_dmamap_load(dpt->sg_dmat, sg_map->sg_dmamap, sg_map->sg_vaddr, PAGE_SIZE, dptmapmem, &sg_map->sg_physaddr, /*flags*/0); SLIST_INSERT_HEAD(&dpt->sg_maps, sg_map, links); return (sg_map); } /* * Allocate another chunk of CCB's. Return count of entries added. * Assumed to be called at splcam(). */ static int dptallocccbs(dpt_softc_t *dpt) { struct dpt_ccb *next_ccb; struct sg_map_node *sg_map; bus_addr_t physaddr; dpt_sg_t *segs; int newcount; int i; next_ccb = &dpt->dpt_dccbs[dpt->total_dccbs]; if (next_ccb == dpt->dpt_dccbs) { /* * First time through. Re-use the S/G * space we allocated for initialization * CCBS. */ sg_map = SLIST_FIRST(&dpt->sg_maps); } else { sg_map = dptallocsgmap(dpt); } if (sg_map == NULL) return (0); segs = sg_map->sg_vaddr; physaddr = sg_map->sg_physaddr; newcount = (PAGE_SIZE / (dpt->sgsize * sizeof(dpt_sg_t))); for (i = 0; dpt->total_dccbs < dpt->max_dccbs && i < newcount; i++) { int error; error = bus_dmamap_create(dpt->buffer_dmat, /*flags*/0, &next_ccb->dmamap); if (error != 0) break; next_ccb->sg_list = segs; next_ccb->sg_busaddr = htonl(physaddr); next_ccb->eata_ccb.cp_dataDMA = htonl(physaddr); next_ccb->eata_ccb.cp_statDMA = htonl(dpt->sp_physaddr); next_ccb->eata_ccb.cp_reqDMA = htonl(dptccbvtop(dpt, next_ccb) + offsetof(struct dpt_ccb, sense_data)); next_ccb->eata_ccb.cp_busaddr = dpt->dpt_ccb_busend; next_ccb->state = DCCB_FREE; next_ccb->tag = dpt->total_dccbs; SLIST_INSERT_HEAD(&dpt->free_dccb_list, next_ccb, links); segs += dpt->sgsize; physaddr += (dpt->sgsize * sizeof(dpt_sg_t)); dpt->dpt_ccb_busend += sizeof(*next_ccb); next_ccb++; dpt->total_dccbs++; } return (i); } dpt_conf_t * dpt_pio_get_conf (u_int32_t base) { static dpt_conf_t * conf; u_int16_t * p; int i; /* * Allocate a dpt_conf_t */ if (!conf) { conf = (dpt_conf_t *)malloc(sizeof(dpt_conf_t), M_DEVBUF, M_NOWAIT); } /* * If we didn't get one then we probably won't ever get one. */ if (!conf) { printf("dpt: unable to allocate dpt_conf_t\n"); return (NULL); } /* * If we have one, clean it up. */ bzero(conf, sizeof(dpt_conf_t)); /* * Reset the controller. */ outb((base + HA_WCOMMAND), EATA_CMD_RESET); /* * Wait for the controller to become ready. * For some reason there can be -no- delays after calling reset * before we wait on ready status. */ if (dpt_pio_wait(base, HA_RSTATUS, HA_SBUSY, 0)) { printf("dpt: timeout waiting for controller to become ready\n"); return (NULL); } if (dpt_pio_wait(base, HA_RAUXSTAT, HA_ABUSY, 0)) { printf("dpt: timetout waiting for adapter ready.\n"); return (NULL); } /* * Send the PIO_READ_CONFIG command. */ outb((base + HA_WCOMMAND), EATA_CMD_PIO_READ_CONFIG); /* * Read the data into the struct. */ p = (u_int16_t *)conf; for (i = 0; i < (sizeof(dpt_conf_t) / 2); i++) { if (dpt_pio_wait(base, HA_RSTATUS, HA_SDRQ, 0)) { if (bootverbose) printf("dpt: timeout in data read.\n"); return (NULL); } (*p) = inw(base + HA_RDATA); p++; } if (inb(base + HA_RSTATUS) & HA_SERROR) { if (bootverbose) printf("dpt: error reading configuration data.\n"); return (NULL); } #define BE_EATA_SIGNATURE 0x45415441 #define LE_EATA_SIGNATURE 0x41544145 /* * Test to see if we have a valid card. */ if ((conf->signature == BE_EATA_SIGNATURE) || (conf->signature == LE_EATA_SIGNATURE)) { while (inb(base + HA_RSTATUS) & HA_SDRQ) { inw(base + HA_RDATA); } return (conf); } return (NULL); } /* * Read a configuration page into the supplied dpt_cont_t buffer. */ static int dpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr, u_int size, u_int page, u_int target, int extent) { eata_ccb_t *cp; u_int8_t status; int ndx; int ospl; int result; cp = &dccb->eata_ccb; bzero((void *)(uintptr_t)(volatile void *)dpt->sp, sizeof(*dpt->sp)); cp->Interpret = 1; cp->DataIn = 1; cp->Auto_Req_Sen = 1; cp->reqlen = sizeof(struct scsi_sense_data); cp->cp_id = target; cp->cp_LUN = 0; /* In the EATA packet */ cp->cp_lun = 0; /* In the SCSI command */ cp->cp_scsi_cmd = INQUIRY; cp->cp_len = size; cp->cp_extent = extent; cp->cp_page = page; cp->cp_channel = 0; /* DNC, Interpret mode is set */ cp->cp_identify = 1; cp->cp_datalen = htonl(size); ospl = splcam(); /* * This could be a simple for loop, but we suspected the compiler To * have optimized it a bit too much. Wait for the controller to * become ready */ while (((status = dpt_inb(dpt, HA_RSTATUS)) != (HA_SREADY | HA_SSC) && (status != (HA_SREADY | HA_SSC | HA_SERROR)) && (status != (HA_SDRDY | HA_SERROR | HA_SDRQ))) || (dpt_wait(dpt, HA_SBUSY, 0))) { /* * RAID Drives still Spinning up? (This should only occur if * the DPT controller is in a NON PC (PCI?) platform). */ if (dpt_raid_busy(dpt)) { printf("dpt%d WARNING: Get_conf() RSUS failed.\n", dpt->unit); splx(ospl); return (0); } } DptStat_Reset_BUSY(dpt->sp); /* * XXXX We might want to do something more clever than aborting at * this point, like resetting (rebooting) the controller and trying * again. */ if ((result = dpt_send_eata_command(dpt, cp, dccb_busaddr, EATA_CMD_DMA_SEND_CP, 10000, 0, 0, 0)) != 0) { printf("dpt%d WARNING: Get_conf() failed (%d) to send " "EATA_CMD_DMA_READ_CONFIG\n", dpt->unit, result); splx(ospl); return (0); } /* Wait for two seconds for a response. This can be slow */ for (ndx = 0; (ndx < 20000) && !((status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ); ndx++) { DELAY(50); } /* Grab the status and clear interrupts */ status = dpt_inb(dpt, HA_RSTATUS); splx(ospl); /* * Check the status carefully. Return only if the * command was successful. */ if (((status & HA_SERROR) == 0) && (dpt->sp->hba_stat == 0) && (dpt->sp->scsi_stat == 0) && (dpt->sp->residue_len == 0)) return (0); if (dpt->sp->scsi_stat == SCSI_STATUS_CHECK_COND) return (0); return (1); } /* Detect Cache parameters and size */ static void dpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr, u_int8_t *buff) { eata_ccb_t *cp; u_int8_t *param; int bytes; int result; int ospl; int ndx; u_int8_t status; /* * Default setting, for best perfromance.. * This is what virtually all cards default to.. */ dpt->cache_type = DPT_CACHE_WRITEBACK; dpt->cache_size = 0; cp = &dccb->eata_ccb; bzero((void *)(uintptr_t)(volatile void *)dpt->sp, sizeof(dpt->sp)); bzero(buff, 512); /* Setup the command structure */ cp->Interpret = 1; cp->DataIn = 1; cp->Auto_Req_Sen = 1; cp->reqlen = sizeof(struct scsi_sense_data); cp->cp_id = 0; /* who cares? The HBA will interpret.. */ cp->cp_LUN = 0; /* In the EATA packet */ cp->cp_lun = 0; /* In the SCSI command */ cp->cp_channel = 0; cp->cp_scsi_cmd = EATA_CMD_DMA_SEND_CP; cp->cp_len = 56; cp->cp_extent = 0; cp->cp_page = 0; cp->cp_identify = 1; cp->cp_dispri = 1; /* * Build the EATA Command Packet structure * for a Log Sense Command. */ cp->cp_cdb[0] = 0x4d; cp->cp_cdb[1] = 0x0; cp->cp_cdb[2] = 0x40 | 0x33; cp->cp_cdb[7] = 1; cp->cp_datalen = htonl(512); ospl = splcam(); result = dpt_send_eata_command(dpt, cp, dccb_busaddr, EATA_CMD_DMA_SEND_CP, 10000, 0, 0, 0); if (result != 0) { printf("dpt%d WARNING: detect_cache() failed (%d) to send " "EATA_CMD_DMA_SEND_CP\n", dpt->unit, result); splx(ospl); return; } /* Wait for two seconds for a response. This can be slow... */ for (ndx = 0; (ndx < 20000) && !((status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ); ndx++) { DELAY(50); } /* Grab the status and clear interrupts */ status = dpt_inb(dpt, HA_RSTATUS); splx(ospl); /* * Sanity check */ if (buff[0] != 0x33) { return; } bytes = DPT_HCP_LENGTH(buff); param = DPT_HCP_FIRST(buff); if (DPT_HCP_CODE(param) != 1) { /* * DPT Log Page layout error */ printf("dpt%d: NOTICE: Log Page (1) layout error\n", dpt->unit); return; } if (!(param[4] & 0x4)) { dpt->cache_type = DPT_NO_CACHE; return; } while (DPT_HCP_CODE(param) != 6) { param = DPT_HCP_NEXT(param); if ((param < buff) || (param >= &buff[bytes])) { return; } } if (param[4] & 0x2) { /* * Cache disabled */ dpt->cache_type = DPT_NO_CACHE; return; } if (param[4] & 0x4) { dpt->cache_type = DPT_CACHE_WRITETHROUGH; } /* XXX This isn't correct. This log parameter only has two bytes.... */ #if 0 dpt->cache_size = param[5] | (param[6] << 8) | (param[7] << 16) | (param[8] << 24); #endif } static void dpt_poll(struct cam_sim *sim) { dpt_intr(cam_sim_softc(sim)); } static void dptexecuteccb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct dpt_ccb *dccb; union ccb *ccb; struct dpt_softc *dpt; int s; dccb = (struct dpt_ccb *)arg; ccb = dccb->ccb; dpt = (struct dpt_softc *)ccb->ccb_h.ccb_dpt_ptr; if (error != 0) { if (error != EFBIG) printf("dpt%d: Unexepected error 0x%x returned from " "bus_dmamap_load\n", dpt->unit, error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; } dptfreeccb(dpt, dccb); xpt_done(ccb); return; } if (nseg != 0) { dpt_sg_t *sg; bus_dma_segment_t *end_seg; bus_dmasync_op_t op; end_seg = dm_segs + nseg; /* Copy the segments into our SG list */ sg = dccb->sg_list; while (dm_segs < end_seg) { sg->seg_len = htonl(dm_segs->ds_len); sg->seg_addr = htonl(dm_segs->ds_addr); sg++; dm_segs++; } if (nseg > 1) { dccb->eata_ccb.scatter = 1; dccb->eata_ccb.cp_dataDMA = dccb->sg_busaddr; dccb->eata_ccb.cp_datalen = htonl(nseg * sizeof(dpt_sg_t)); } else { dccb->eata_ccb.cp_dataDMA = dccb->sg_list[0].seg_addr; dccb->eata_ccb.cp_datalen = dccb->sg_list[0].seg_len; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(dpt->buffer_dmat, dccb->dmamap, op); } else { dccb->eata_ccb.cp_dataDMA = 0; dccb->eata_ccb.cp_datalen = 0; } s = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap); dptfreeccb(dpt, dccb); xpt_done(ccb); splx(s); return; } dccb->state |= DCCB_ACTIVE; ccb->ccb_h.status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&dpt->pending_ccb_list, &ccb->ccb_h, sim_links.le); ccb->ccb_h.timeout_ch = timeout(dpttimeout, (caddr_t)dccb, (ccb->ccb_h.timeout * hz) / 1000); if (dpt_send_eata_command(dpt, &dccb->eata_ccb, dccb->eata_ccb.cp_busaddr, EATA_CMD_DMA_SEND_CP, 0, 0, 0, 0) != 0) { ccb->ccb_h.status = CAM_NO_HBA; /* HBA dead or just busy?? */ if (nseg != 0) bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap); dptfreeccb(dpt, dccb); xpt_done(ccb); } splx(s); } static void dpt_action(struct cam_sim *sim, union ccb *ccb) { struct dpt_softc *dpt; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("dpt_action\n")); dpt = (struct dpt_softc *)cam_sim_softc(sim); if ((dpt->state & DPT_HA_SHUTDOWN_ACTIVE) != 0) { xpt_print_path(ccb->ccb_h.path); printf("controller is shutdown. Aborting CCB.\n"); ccb->ccb_h.status = CAM_NO_HBA; xpt_done(ccb); return; } switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct ccb_scsiio *csio; struct ccb_hdr *ccbh; struct dpt_ccb *dccb; struct eata_ccb *eccb; csio = &ccb->csio; ccbh = &ccb->ccb_h; /* Max CDB length is 12 bytes */ if (csio->cdb_len > 12) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } if ((dccb = dptgetccb(dpt)) == NULL) { int s; s = splcam(); dpt->resource_shortage = 1; splx(s); xpt_freeze_simq(sim, /*count*/1); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } eccb = &dccb->eata_ccb; /* Link dccb and ccb so we can find one from the other */ dccb->ccb = ccb; ccb->ccb_h.ccb_dccb_ptr = dccb; ccb->ccb_h.ccb_dpt_ptr = dpt; /* * Explicitly set all flags so that the compiler can * be smart about setting them. */ eccb->SCSI_Reset = 0; eccb->HBA_Init = 0; eccb->Auto_Req_Sen = (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) ? 0 : 1; eccb->scatter = 0; eccb->Quick = 0; eccb->Interpret = ccb->ccb_h.target_id == dpt->hostid[cam_sim_bus(sim)] ? 1 : 0; eccb->DataOut = (ccb->ccb_h.flags & CAM_DIR_OUT) ? 1 : 0; eccb->DataIn = (ccb->ccb_h.flags & CAM_DIR_IN) ? 1 : 0; eccb->reqlen = csio->sense_len; eccb->cp_id = ccb->ccb_h.target_id; eccb->cp_channel = cam_sim_bus(sim); eccb->cp_LUN = ccb->ccb_h.target_lun; eccb->cp_luntar = 0; eccb->cp_dispri = (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) ? 0 : 1; eccb->cp_identify = 1; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0 && csio->tag_action != CAM_TAG_ACTION_NONE) { eccb->cp_msg[0] = csio->tag_action; eccb->cp_msg[1] = dccb->tag; } else { eccb->cp_msg[0] = 0; eccb->cp_msg[1] = 0; } eccb->cp_msg[2] = 0; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(csio->cdb_io.cdb_ptr, eccb->cp_cdb, csio->cdb_len); } else { /* I guess I could map it in... */ ccb->ccb_h.status = CAM_REQ_INVALID; dptfreeccb(dpt, dccb); xpt_done(ccb); return; } } else { bcopy(csio->cdb_io.cdb_bytes, eccb->cp_cdb, csio->cdb_len); } /* * If we have any data to send with this command, * map it into bus space. */ /* Only use S/G if there is a transfer */ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccbh->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer. */ if ((ccbh->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(dpt->buffer_dmat, dccb->dmamap, csio->data_ptr, csio->dxfer_len, dptexecuteccb, dccb, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(sim, 1); dccb->state |= CAM_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; dptexecuteccb(dccb, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccbh->flags & CAM_DATA_PHYS) != 0) panic("dpt_action - Physical " "segment pointers " "unsupported"); if ((ccbh->flags&CAM_SG_LIST_PHYS)==0) panic("dpt_action - Virtual " "segment addresses " "unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; dptexecuteccb(dccb, segs, csio->sglist_cnt, 0); } } else { /* * XXX JGibbs. * Does it want them both on or both off? * CAM_DIR_NONE is both on, so this code can * be removed if this is also what the DPT * exptects. */ eccb->DataOut = 0; eccb->DataIn = 0; dptexecuteccb(dccb, NULL, 0, 0); } break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; u_int target_mask; cts = &ccb->cts; target_mask = 0x01 << ccb->ccb_h.target_id; if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { cts->flags = CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB; cts->bus_width = (dpt->max_id > 7) ? MSG_EXT_WDTR_BUS_8_BIT : MSG_EXT_WDTR_BUS_16_BIT; cts->sync_period = 25; /* 10MHz */ if (cts->sync_period != 0) cts->sync_offset = 15; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; } xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended; - /* * XXX Use Adaptec translation until I find out how to * get this information from the card. */ - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - extended = 1; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; if (dpt->max_id > 7) cpi->hba_inquiry |= PI_WIDE_16; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = dpt->max_id; cpi->max_lun = dpt->max_lun; cpi->initiator_id = dpt->hostid[cam_sim_bus(sim)]; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "DPT", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /* * This routine will try to send an EATA command to the DPT HBA. * It will, by default, try 20,000 times, waiting 50us between tries. * It returns 0 on success and 1 on failure. * It is assumed to be called at splcam(). */ static int dpt_send_eata_command(dpt_softc_t *dpt, eata_ccb_t *cmd_block, u_int32_t cmd_busaddr, u_int command, u_int retries, u_int ifc, u_int code, u_int code2) { u_int loop; if (!retries) retries = 20000; /* * I hate this polling nonsense. Wish there was a way to tell the DPT * to go get commands at its own pace, or to interrupt when ready. * In the mean time we will measure how many itterations it really * takes. */ for (loop = 0; loop < retries; loop++) { if ((dpt_inb(dpt, HA_RAUXSTAT) & HA_ABUSY) == 0) break; else DELAY(50); } if (loop < retries) { #ifdef DPT_MEASURE_PERFORMANCE if (loop > dpt->performance.max_eata_tries) dpt->performance.max_eata_tries = loop; if (loop < dpt->performance.min_eata_tries) dpt->performance.min_eata_tries = loop; #endif } else { #ifdef DPT_MEASURE_PERFORMANCE ++dpt->performance.command_too_busy; #endif return (1); } /* The controller is alive, advance the wedge timer */ #ifdef DPT_RESET_HBA dpt->last_contact = microtime_now; #endif if (cmd_block == NULL) cmd_busaddr = 0; #if (BYTE_ORDER == BIG_ENDIAN) else { cmd_busaddr = ((cmd_busaddr >> 24) & 0xFF) | ((cmd_busaddr >> 16) & 0xFF) | ((cmd_busaddr >> 8) & 0xFF) | (cmd_busaddr & 0xFF); } #endif /* And now the address */ dpt_outl(dpt, HA_WDMAADDR, cmd_busaddr); if (command == EATA_CMD_IMMEDIATE) { if (cmd_block == NULL) { dpt_outb(dpt, HA_WCODE2, code2); dpt_outb(dpt, HA_WCODE, code); } dpt_outb(dpt, HA_WIFC, ifc); } dpt_outb(dpt, HA_WCOMMAND, command); return (0); } /* ==================== Exported Function definitions =======================*/ void dpt_alloc(device_t dev) { dpt_softc_t *dpt = device_get_softc(dev); int i; dpt->tag = rman_get_bustag(dpt->io_res); dpt->bsh = rman_get_bushandle(dpt->io_res) + dpt->io_offset; dpt->unit = device_get_unit(dev); SLIST_INIT(&dpt->free_dccb_list); LIST_INIT(&dpt->pending_ccb_list); for (i = 0; i < MAX_CHANNELS; i++) dpt->resetlevel[i] = DPT_HA_OK; #ifdef DPT_MEASURE_PERFORMANCE dpt_reset_performance(dpt); #endif /* DPT_MEASURE_PERFORMANCE */ return; } void dpt_free(struct dpt_softc *dpt) { switch (dpt->init_level) { default: case 5: bus_dmamap_unload(dpt->dccb_dmat, dpt->dccb_dmamap); case 4: bus_dmamem_free(dpt->dccb_dmat, dpt->dpt_dccbs, dpt->dccb_dmamap); bus_dmamap_destroy(dpt->dccb_dmat, dpt->dccb_dmamap); case 3: bus_dma_tag_destroy(dpt->dccb_dmat); case 2: bus_dma_tag_destroy(dpt->buffer_dmat); case 1: { struct sg_map_node *sg_map; while ((sg_map = SLIST_FIRST(&dpt->sg_maps)) != NULL) { SLIST_REMOVE_HEAD(&dpt->sg_maps, links); bus_dmamap_unload(dpt->sg_dmat, sg_map->sg_dmamap); bus_dmamem_free(dpt->sg_dmat, sg_map->sg_vaddr, sg_map->sg_dmamap); free(sg_map, M_DEVBUF); } bus_dma_tag_destroy(dpt->sg_dmat); } case 0: break; } } int dpt_alloc_resources (device_t dev) { dpt_softc_t * dpt; int error; dpt = device_get_softc(dev); dpt->io_res = bus_alloc_resource(dev, dpt->io_type, &dpt->io_rid, 0, ~0, 1, RF_ACTIVE); if (dpt->io_res == NULL) { device_printf(dev, "No I/O space?!\n"); error = ENOMEM; goto bad; } dpt->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &dpt->irq_rid, 0, ~0, 1, RF_ACTIVE); if (dpt->irq_res == NULL) { device_printf(dev, "No IRQ!\n"); error = ENOMEM; goto bad; } return (0); bad: return(error); } void dpt_release_resources (device_t dev) { struct dpt_softc * dpt; dpt = device_get_softc(dev); if (dpt->ih) bus_teardown_intr(dev, dpt->irq_res, dpt->ih); if (dpt->io_res) bus_release_resource(dev, dpt->io_type, dpt->io_rid, dpt->io_res); if (dpt->irq_res) bus_release_resource(dev, SYS_RES_IRQ, dpt->irq_rid, dpt->irq_res); if (dpt->drq_res) bus_release_resource(dev, SYS_RES_DRQ, dpt->drq_rid, dpt->drq_res); return; } static u_int8_t string_sizes[] = { sizeof(((dpt_inq_t*)NULL)->vendor), sizeof(((dpt_inq_t*)NULL)->modelNum), sizeof(((dpt_inq_t*)NULL)->firmware), sizeof(((dpt_inq_t*)NULL)->protocol), }; int dpt_init(struct dpt_softc *dpt) { dpt_conf_t conf; struct sg_map_node *sg_map; dpt_ccb_t *dccb; u_int8_t *strp; int index; int i; int retval; dpt->init_level = 0; SLIST_INIT(&dpt->sg_maps); #ifdef DPT_RESET_BOARD printf("dpt%d: resetting HBA\n", dpt->unit); dpt_outb(dpt, HA_WCOMMAND, EATA_CMD_RESET); DELAY(750000); /* XXX Shouldn't we poll a status register or something??? */ #endif /* DMA tag for our S/G structures. We allocate in page sized chunks */ if (bus_dma_tag_create( /* parent */ dpt->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ PAGE_SIZE, /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &dpt->sg_dmat) != 0) { goto error_exit; } dpt->init_level++; /* * We allocate our DPT ccbs as a contiguous array of bus dma'able * memory. To get the allocation size, we need to know how many * ccbs the card supports. This requires a ccb. We solve this * chicken and egg problem by allocating some re-usable S/G space * up front, and treating it as our status packet, CCB, and target * memory space for these commands. */ sg_map = dptallocsgmap(dpt); if (sg_map == NULL) goto error_exit; dpt->sp = (volatile dpt_sp_t *)sg_map->sg_vaddr; dccb = (struct dpt_ccb *)(uintptr_t)(volatile void *)&dpt->sp[1]; bzero(dccb, sizeof(*dccb)); dpt->sp_physaddr = sg_map->sg_physaddr; dccb->eata_ccb.cp_dataDMA = htonl(sg_map->sg_physaddr + sizeof(dpt_sp_t) + sizeof(*dccb)); dccb->eata_ccb.cp_busaddr = ~0; dccb->eata_ccb.cp_statDMA = htonl(dpt->sp_physaddr); dccb->eata_ccb.cp_reqDMA = htonl(dpt->sp_physaddr + sizeof(*dccb) + offsetof(struct dpt_ccb, sense_data)); /* Okay. Fetch our config */ bzero(&dccb[1], sizeof(conf)); /* data area */ retval = dpt_get_conf(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t), sizeof(conf), 0xc1, 7, 1); if (retval != 0) { printf("dpt%d: Failed to get board configuration\n", dpt->unit); return (retval); } bcopy(&dccb[1], &conf, sizeof(conf)); bzero(&dccb[1], sizeof(dpt->board_data)); retval = dpt_get_conf(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t), sizeof(dpt->board_data), 0, conf.scsi_id0, 0); if (retval != 0) { printf("dpt%d: Failed to get inquiry information\n", dpt->unit); return (retval); } bcopy(&dccb[1], &dpt->board_data, sizeof(dpt->board_data)); dpt_detect_cache(dpt, dccb, sg_map->sg_physaddr + sizeof(dpt_sp_t), (u_int8_t *)&dccb[1]); switch (ntohl(conf.splen)) { case DPT_EATA_REVA: dpt->EATA_revision = 'a'; break; case DPT_EATA_REVB: dpt->EATA_revision = 'b'; break; case DPT_EATA_REVC: dpt->EATA_revision = 'c'; break; case DPT_EATA_REVZ: dpt->EATA_revision = 'z'; break; default: dpt->EATA_revision = '?'; } dpt->max_id = conf.MAX_ID; dpt->max_lun = conf.MAX_LUN; dpt->irq = conf.IRQ; dpt->dma_channel = (8 - conf.DMA_channel) & 7; dpt->channels = conf.MAX_CHAN + 1; dpt->state |= DPT_HA_OK; if (conf.SECOND) dpt->primary = FALSE; else dpt->primary = TRUE; dpt->more_support = conf.MORE_support; if (strncmp(dpt->board_data.firmware, "07G0", 4) >= 0) dpt->immediate_support = 1; else dpt->immediate_support = 0; dpt->broken_INQUIRY = FALSE; dpt->cplen = ntohl(conf.cplen); dpt->cppadlen = ntohs(conf.cppadlen); dpt->max_dccbs = ntohs(conf.queuesiz); if (dpt->max_dccbs > 256) { printf("dpt%d: Max CCBs reduced from %d to " "256 due to tag algorithm\n", dpt->unit, dpt->max_dccbs); dpt->max_dccbs = 256; } dpt->hostid[0] = conf.scsi_id0; dpt->hostid[1] = conf.scsi_id1; dpt->hostid[2] = conf.scsi_id2; if (conf.SG_64K) dpt->sgsize = 8192; else dpt->sgsize = ntohs(conf.SGsiz); /* We can only get 64k buffers, so don't bother to waste space. */ if (dpt->sgsize < 17 || dpt->sgsize > 32) dpt->sgsize = 32; if (dpt->sgsize > dpt_max_segs) dpt->sgsize = dpt_max_segs; /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create( /* parent */ dpt->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ MAXBSIZE, /* nsegments */ dpt->sgsize, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ BUS_DMA_ALLOCNOW, &dpt->buffer_dmat) != 0) { printf("dpt: bus_dma_tag_create(...,dpt->buffer_dmat) failed\n"); goto error_exit; } dpt->init_level++; /* DMA tag for our ccb structures and interrupt status packet */ if (bus_dma_tag_create( /* parent */ dpt->parent_dmat, /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ (dpt->max_dccbs * sizeof(struct dpt_ccb)) + sizeof(dpt_sp_t), /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, &dpt->dccb_dmat) != 0) { printf("dpt: bus_dma_tag_create(...,dpt->dccb_dmat) failed\n"); goto error_exit; } dpt->init_level++; /* Allocation for our ccbs and interrupt status packet */ if (bus_dmamem_alloc(dpt->dccb_dmat, (void **)&dpt->dpt_dccbs, BUS_DMA_NOWAIT, &dpt->dccb_dmamap) != 0) { printf("dpt: bus_dmamem_alloc(dpt->dccb_dmat,...) failed\n"); goto error_exit; } dpt->init_level++; /* And permanently map them */ bus_dmamap_load(dpt->dccb_dmat, dpt->dccb_dmamap, dpt->dpt_dccbs, (dpt->max_dccbs * sizeof(struct dpt_ccb)) + sizeof(dpt_sp_t), dptmapmem, &dpt->dpt_ccb_busbase, /*flags*/0); /* Clear them out. */ bzero(dpt->dpt_dccbs, (dpt->max_dccbs * sizeof(struct dpt_ccb)) + sizeof(dpt_sp_t)); dpt->dpt_ccb_busend = dpt->dpt_ccb_busbase; dpt->sp = (dpt_sp_t*)&dpt->dpt_dccbs[dpt->max_dccbs]; dpt->sp_physaddr = dpt->dpt_ccb_busbase + (dpt->max_dccbs * sizeof(dpt_ccb_t)); dpt->init_level++; /* Allocate our first batch of ccbs */ if (dptallocccbs(dpt) == 0) { printf("dpt: dptallocccbs(dpt) == 0\n"); return (2); } /* Prepare for Target Mode */ dpt->target_mode_enabled = 1; /* Nuke excess spaces from inquiry information */ strp = dpt->board_data.vendor; for (i = 0; i < sizeof(string_sizes); i++) { index = string_sizes[i] - 1; while (index && (strp[index] == ' ')) strp[index--] = '\0'; strp += string_sizes[i]; } printf("dpt%d: %.8s %.16s FW Rev. %.4s, ", dpt->unit, dpt->board_data.vendor, dpt->board_data.modelNum, dpt->board_data.firmware); printf("%d channel%s, ", dpt->channels, dpt->channels > 1 ? "s" : ""); if (dpt->cache_type != DPT_NO_CACHE && dpt->cache_size != 0) { printf("%s Cache, ", dpt->cache_type == DPT_CACHE_WRITETHROUGH ? "Write-Through" : "Write-Back"); } printf("%d CCBs\n", dpt->max_dccbs); return (0); error_exit: return (1); } int dpt_attach(dpt_softc_t *dpt) { struct cam_devq *devq; int i; /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(dpt->max_dccbs); if (devq == NULL) return (0); for (i = 0; i < dpt->channels; i++) { /* * Construct our SIM entry */ dpt->sims[i] = cam_sim_alloc(dpt_action, dpt_poll, "dpt", dpt, dpt->unit, /*untagged*/2, /*tagged*/dpt->max_dccbs, devq); if (xpt_bus_register(dpt->sims[i], i) != CAM_SUCCESS) { cam_sim_free(dpt->sims[i], /*free_devq*/i == 0); break; } if (xpt_create_path(&dpt->paths[i], /*periph*/NULL, cam_sim_path(dpt->sims[i]), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(dpt->sims[i])); cam_sim_free(dpt->sims[i], /*free_devq*/i == 0); break; } } if (i > 0) EVENTHANDLER_REGISTER(shutdown_final, dptshutdown, dpt, SHUTDOWN_PRI_DEFAULT); return (i); } int dpt_detach (device_t dev) { struct dpt_softc * dpt; int i; dpt = device_get_softc(dev); for (i = 0; i < dpt->channels; i++) { #if 0 xpt_async(AC_LOST_DEVICE, dpt->paths[i], NULL); #endif xpt_free_path(dpt->paths[i]); xpt_bus_deregister(cam_sim_path(dpt->sims[i])); cam_sim_free(dpt->sims[i], /*free_devq*/TRUE); } dptshutdown((void *)dpt, SHUTDOWN_PRI_DEFAULT); dpt_release_resources(dev); dpt_free(dpt); return (0); } /* * This is the interrupt handler for the DPT driver. */ void dpt_intr(void *arg) { dpt_softc_t *dpt; dpt_ccb_t *dccb; union ccb *ccb; u_int status; u_int aux_status; u_int hba_stat; u_int scsi_stat; u_int32_t residue_len; /* Number of bytes not transferred */ dpt = (dpt_softc_t *)arg; /* First order of business is to check if this interrupt is for us */ while (((aux_status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ) != 0) { /* * What we want to do now, is to capture the status, all of it, * move it where it belongs, wake up whoever sleeps waiting to * process this result, and get out of here. */ if (dpt->sp->ccb_busaddr < dpt->dpt_ccb_busbase || dpt->sp->ccb_busaddr >= dpt->dpt_ccb_busend) { printf("Encountered bogus status packet\n"); status = dpt_inb(dpt, HA_RSTATUS); return; } dccb = dptccbptov(dpt, dpt->sp->ccb_busaddr); dpt->sp->ccb_busaddr = ~0; /* Ignore status packets with EOC not set */ if (dpt->sp->EOC == 0) { printf("dpt%d ERROR: Request %d received with " "clear EOC.\n Marking as LOST.\n", dpt->unit, dccb->transaction_id); #ifdef DPT_HANDLE_TIMEOUTS dccb->state |= DPT_CCB_STATE_MARKED_LOST; #endif /* This CLEARS the interrupt! */ status = dpt_inb(dpt, HA_RSTATUS); continue; } dpt->sp->EOC = 0; /* * Double buffer the status information so the hardware can * work on updating the status packet while we decifer the * one we were just interrupted for. * According to Mark Salyzyn, we only need few pieces of it. */ hba_stat = dpt->sp->hba_stat; scsi_stat = dpt->sp->scsi_stat; residue_len = dpt->sp->residue_len; /* Clear interrupts, check for error */ if ((status = dpt_inb(dpt, HA_RSTATUS)) & HA_SERROR) { /* * Error Condition. Check for magic cookie. Exit * this test on earliest sign of non-reset condition */ /* Check that this is not a board reset interrupt */ if (dpt_just_reset(dpt)) { printf("dpt%d: HBA rebooted.\n" " All transactions should be " "resubmitted\n", dpt->unit); printf("dpt%d: >>---->> This is incomplete, " "fix me.... <<----<<", dpt->unit); panic("DPT Rebooted"); } } /* Process CCB */ ccb = dccb->ccb; untimeout(dpttimeout, dccb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(dpt->buffer_dmat, dccb->dmamap, op); bus_dmamap_unload(dpt->buffer_dmat, dccb->dmamap); } /* Common Case inline... */ if (hba_stat == HA_NO_ERROR) { ccb->csio.scsi_status = scsi_stat; ccb->ccb_h.status = 0; switch (scsi_stat) { case SCSI_STATUS_OK: ccb->ccb_h.status |= CAM_REQ_CMP; break; case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_CMD_TERMINATED: bcopy(&dccb->sense_data, &ccb->csio.sense_data, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; /* FALLTHROUGH */ default: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; /* XXX Freeze DevQ */ break; } ccb->csio.resid = residue_len; dptfreeccb(dpt, dccb); xpt_done(ccb); } else { dptprocesserror(dpt, dccb, ccb, hba_stat, scsi_stat, residue_len); } } } static void dptprocesserror(dpt_softc_t *dpt, dpt_ccb_t *dccb, union ccb *ccb, u_int hba_stat, u_int scsi_stat, u_int32_t resid) { ccb->csio.resid = resid; switch (hba_stat) { case HA_ERR_SEL_TO: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case HA_ERR_CMD_TO: ccb->ccb_h.status = CAM_CMD_TIMEOUT; break; case HA_SCSIBUS_RESET: case HA_HBA_POWER_UP: /* Similar effect to a bus reset??? */ ccb->ccb_h.status = CAM_SCSI_BUS_RESET; break; case HA_CP_ABORTED: case HA_CP_RESET: /* XXX ??? */ case HA_CP_ABORT_NA: /* XXX ??? */ case HA_CP_RESET_NA: /* XXX ??? */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) ccb->ccb_h.status = CAM_REQ_ABORTED; break; case HA_PCI_PARITY: case HA_PCI_MABORT: case HA_PCI_TABORT: case HA_PCI_STABORT: case HA_BUS_PARITY: case HA_PARITY_ERR: case HA_ECC_ERR: ccb->ccb_h.status = CAM_UNCOR_PARITY; break; case HA_UNX_MSGRJCT: ccb->ccb_h.status = CAM_MSG_REJECT_REC; break; case HA_UNX_BUSPHASE: ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case HA_UNX_BUS_FREE: ccb->ccb_h.status = CAM_UNEXP_BUSFREE; break; case HA_SCSI_HUNG: case HA_RESET_STUCK: /* * Dead??? Can the controller get unstuck * from these conditions */ ccb->ccb_h.status = CAM_NO_HBA; break; case HA_RSENSE_FAIL: ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; break; default: printf("dpt%d: Undocumented Error %x\n", dpt->unit, hba_stat); printf("Please mail this message to shimon@simon-shapiro.org\n"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; break; } dptfreeccb(dpt, dccb); xpt_done(ccb); } static void dpttimeout(void *arg) { struct dpt_ccb *dccb; union ccb *ccb; struct dpt_softc *dpt; int s; dccb = (struct dpt_ccb *)arg; ccb = dccb->ccb; dpt = (struct dpt_softc *)ccb->ccb_h.ccb_dpt_ptr; xpt_print_path(ccb->ccb_h.path); printf("CCB %p - timed out\n", (void *)dccb); s = splcam(); /* * Try to clear any pending jobs. FreeBSD will loose interrupts, * leaving the controller suspended, and commands timed-out. * By calling the interrupt handler, any command thus stuck will be * completed. */ dpt_intr(dpt); if ((dccb->state & DCCB_ACTIVE) == 0) { xpt_print_path(ccb->ccb_h.path); printf("CCB %p - timed out CCB already completed\n", (void *)dccb); splx(s); return; } /* Abort this particular command. Leave all others running */ dpt_send_immediate(dpt, &dccb->eata_ccb, dccb->eata_ccb.cp_busaddr, /*retries*/20000, EATA_SPECIFIC_ABORT, 0, 0); ccb->ccb_h.status = CAM_CMD_TIMEOUT; splx(s); } /* * Shutdown the controller and ensure that the cache is completely flushed. * Called from the shutdown_final event after all disk access has completed. */ static void dptshutdown(void *arg, int howto) { dpt_softc_t *dpt; dpt = (dpt_softc_t *)arg; printf("dpt%d: Shutting down (mode %x) HBA. Please wait...\n", dpt->unit, howto); /* * What we do for a shutdown, is give the DPT early power loss warning */ dpt_send_immediate(dpt, NULL, 0, EATA_POWER_OFF_WARN, 0, 0, 0); DELAY(1000 * 1000 * 5); printf("dpt%d: Controller was warned of shutdown and is now " "disabled\n", dpt->unit); } /*============================================================================*/ #if 0 #ifdef DPT_RESET_HBA /* ** Function name : dpt_reset_hba ** ** Description : Reset the HBA and properly discard all pending work ** Input : Softc ** Output : Nothing */ static void dpt_reset_hba(dpt_softc_t *dpt) { eata_ccb_t *ccb; int ospl; dpt_ccb_t dccb, *dccbp; int result; struct scsi_xfer *xs; /* Prepare a control block. The SCSI command part is immaterial */ dccb.xs = NULL; dccb.flags = 0; dccb.state = DPT_CCB_STATE_NEW; dccb.std_callback = NULL; dccb.wrbuff_callback = NULL; ccb = &dccb.eata_ccb; ccb->CP_OpCode = EATA_CMD_RESET; ccb->SCSI_Reset = 0; ccb->HBA_Init = 1; ccb->Auto_Req_Sen = 1; ccb->cp_id = 0; /* Should be ignored */ ccb->DataIn = 1; ccb->DataOut = 0; ccb->Interpret = 1; ccb->reqlen = htonl(sizeof(struct scsi_sense_data)); ccb->cp_statDMA = htonl(vtophys(&ccb->cp_statDMA)); ccb->cp_reqDMA = htonl(vtophys(&ccb->cp_reqDMA)); ccb->cp_viraddr = (u_int32_t) & ccb; ccb->cp_msg[0] = HA_IDENTIFY_MSG | HA_DISCO_RECO; ccb->cp_scsi_cmd = 0; /* Should be ignored */ /* Lock up the submitted queue. We are very persistant here */ ospl = splcam(); while (dpt->queue_status & DPT_SUBMITTED_QUEUE_ACTIVE) { DELAY(100); } dpt->queue_status |= DPT_SUBMITTED_QUEUE_ACTIVE; splx(ospl); /* Send the RESET message */ if ((result = dpt_send_eata_command(dpt, &dccb.eata_ccb, EATA_CMD_RESET, 0, 0, 0, 0)) != 0) { printf("dpt%d: Failed to send the RESET message.\n" " Trying cold boot (ouch!)\n", dpt->unit); if ((result = dpt_send_eata_command(dpt, &dccb.eata_ccb, EATA_COLD_BOOT, 0, 0, 0, 0)) != 0) { panic("dpt%d: Faild to cold boot the HBA\n", dpt->unit); } #ifdef DPT_MEASURE_PERFORMANCE dpt->performance.cold_boots++; #endif /* DPT_MEASURE_PERFORMANCE */ } #ifdef DPT_MEASURE_PERFORMANCE dpt->performance.warm_starts++; #endif /* DPT_MEASURE_PERFORMANCE */ printf("dpt%d: Aborting pending requests. O/S should re-submit\n", dpt->unit); while ((dccbp = TAILQ_FIRST(&dpt->completed_ccbs)) != NULL) { struct scsi_xfer *xs = dccbp->xs; /* Not all transactions have xs structs */ if (xs != NULL) { /* Tell the kernel proper this did not complete well */ xs->error |= XS_SELTIMEOUT; xs->flags |= SCSI_ITSDONE; scsi_done(xs); } dpt_Qremove_submitted(dpt, dccbp); /* Remember, Callbacks are NOT in the standard queue */ if (dccbp->std_callback != NULL) { (dccbp->std_callback)(dpt, dccbp->eata_ccb.cp_channel, dccbp); } else { ospl = splcam(); dpt_Qpush_free(dpt, dccbp); splx(ospl); } } printf("dpt%d: reset done aborting all pending commands\n", dpt->unit); dpt->queue_status &= ~DPT_SUBMITTED_QUEUE_ACTIVE; } #endif /* DPT_RESET_HBA */ /* * Build a Command Block for target mode READ/WRITE BUFFER, * with the ``sync'' bit ON. * * Although the length and offset are 24 bit fields in the command, they cannot * exceed 8192 bytes, so we take them as short integers andcheck their range. * If they are sensless, we round them to zero offset, maximum length and * complain. */ static void dpt_target_ccb(dpt_softc_t * dpt, int bus, u_int8_t target, u_int8_t lun, dpt_ccb_t * ccb, int mode, u_int8_t command, u_int16_t length, u_int16_t offset) { eata_ccb_t *cp; int ospl; if ((length + offset) > DPT_MAX_TARGET_MODE_BUFFER_SIZE) { printf("dpt%d: Length of %d, and offset of %d are wrong\n", dpt->unit, length, offset); length = DPT_MAX_TARGET_MODE_BUFFER_SIZE; offset = 0; } ccb->xs = NULL; ccb->flags = 0; ccb->state = DPT_CCB_STATE_NEW; ccb->std_callback = (ccb_callback) dpt_target_done; ccb->wrbuff_callback = NULL; cp = &ccb->eata_ccb; cp->CP_OpCode = EATA_CMD_DMA_SEND_CP; cp->SCSI_Reset = 0; cp->HBA_Init = 0; cp->Auto_Req_Sen = 1; cp->cp_id = target; cp->DataIn = 1; cp->DataOut = 0; cp->Interpret = 0; cp->reqlen = htonl(sizeof(struct scsi_sense_data)); cp->cp_statDMA = htonl(vtophys(&cp->cp_statDMA)); cp->cp_reqDMA = htonl(vtophys(&cp->cp_reqDMA)); cp->cp_viraddr = (u_int32_t) & ccb; cp->cp_msg[0] = HA_IDENTIFY_MSG | HA_DISCO_RECO; cp->cp_scsi_cmd = command; cp->cp_cdb[1] = (u_int8_t) (mode & SCSI_TM_MODE_MASK); cp->cp_lun = lun; /* Order is important here! */ cp->cp_cdb[2] = 0x00; /* Buffer Id, only 1 :-( */ cp->cp_cdb[3] = (length >> 16) & 0xFF; /* Buffer offset MSB */ cp->cp_cdb[4] = (length >> 8) & 0xFF; cp->cp_cdb[5] = length & 0xFF; cp->cp_cdb[6] = (length >> 16) & 0xFF; /* Length MSB */ cp->cp_cdb[7] = (length >> 8) & 0xFF; cp->cp_cdb[8] = length & 0xFF; /* Length LSB */ cp->cp_cdb[9] = 0; /* No sync, no match bits */ /* * This could be optimized to live in dpt_register_buffer. * We keep it here, just in case the kernel decides to reallocate pages */ if (dpt_scatter_gather(dpt, ccb, DPT_RW_BUFFER_SIZE, dpt->rw_buffer[bus][target][lun])) { printf("dpt%d: Failed to setup Scatter/Gather for " "Target-Mode buffer\n", dpt->unit); } } /* Setup a target mode READ command */ static void dpt_set_target(int redo, dpt_softc_t * dpt, u_int8_t bus, u_int8_t target, u_int8_t lun, int mode, u_int16_t length, u_int16_t offset, dpt_ccb_t * ccb) { int ospl; if (dpt->target_mode_enabled) { ospl = splcam(); if (!redo) dpt_target_ccb(dpt, bus, target, lun, ccb, mode, SCSI_TM_READ_BUFFER, length, offset); ccb->transaction_id = ++dpt->commands_processed; #ifdef DPT_MEASURE_PERFORMANCE dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd]++; ccb->command_started = microtime_now; #endif dpt_Qadd_waiting(dpt, ccb); dpt_sched_queue(dpt); splx(ospl); } else { printf("dpt%d: Target Mode Request, but Target Mode is OFF\n", dpt->unit); } } /* * Schedule a buffer to be sent to another target. * The work will be scheduled and the callback provided will be called when * the work is actually done. * * Please NOTE: ``Anyone'' can send a buffer, but only registered clients * get notified of receipt of buffers. */ int dpt_send_buffer(int unit, u_int8_t channel, u_int8_t target, u_int8_t lun, u_int8_t mode, u_int16_t length, u_int16_t offset, void *data, buff_wr_done callback) { dpt_softc_t *dpt; dpt_ccb_t *ccb = NULL; int ospl; /* This is an external call. Be a bit paranoid */ for (dpt = TAILQ_FIRST(&dpt_softc_list); dpt != NULL; dpt = TAILQ_NEXT(dpt, links)) { if (dpt->unit == unit) goto valid_unit; } return (INVALID_UNIT); valid_unit: if (dpt->target_mode_enabled) { if ((channel >= dpt->channels) || (target > dpt->max_id) || (lun > dpt->max_lun)) { return (INVALID_SENDER); } if ((dpt->rw_buffer[channel][target][lun] == NULL) || (dpt->buffer_receiver[channel][target][lun] == NULL)) return (NOT_REGISTERED); ospl = splsoftcam(); /* Process the free list */ if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) { printf("dpt%d ERROR: Cannot allocate any more free CCB's.\n" " Please try later\n", dpt->unit); splx(ospl); return (NO_RESOURCES); } /* Now grab the newest CCB */ if ((ccb = dpt_Qpop_free(dpt)) == NULL) { splx(ospl); panic("dpt%d: Got a NULL CCB from pop_free()\n", dpt->unit); } splx(ospl); bcopy(dpt->rw_buffer[channel][target][lun] + offset, data, length); dpt_target_ccb(dpt, channel, target, lun, ccb, mode, SCSI_TM_WRITE_BUFFER, length, offset); ccb->std_callback = (ccb_callback) callback; /* Potential trouble */ ospl = splcam(); ccb->transaction_id = ++dpt->commands_processed; #ifdef DPT_MEASURE_PERFORMANCE dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd]++; ccb->command_started = microtime_now; #endif dpt_Qadd_waiting(dpt, ccb); dpt_sched_queue(dpt); splx(ospl); return (0); } return (DRIVER_DOWN); } static void dpt_target_done(dpt_softc_t * dpt, int bus, dpt_ccb_t * ccb) { int ospl; eata_ccb_t *cp; cp = &ccb->eata_ccb; /* * Remove the CCB from the waiting queue. * We do NOT put it back on the free, etc., queues as it is a special * ccb, owned by the dpt_softc of this unit. */ ospl = splsoftcam(); dpt_Qremove_completed(dpt, ccb); splx(ospl); #define br_channel (ccb->eata_ccb.cp_channel) #define br_target (ccb->eata_ccb.cp_id) #define br_lun (ccb->eata_ccb.cp_LUN) #define br_index [br_channel][br_target][br_lun] #define read_buffer_callback (dpt->buffer_receiver br_index ) #define read_buffer (dpt->rw_buffer[br_channel][br_target][br_lun]) #define cb(offset) (ccb->eata_ccb.cp_cdb[offset]) #define br_offset ((cb(3) << 16) | (cb(4) << 8) | cb(5)) #define br_length ((cb(6) << 16) | (cb(7) << 8) | cb(8)) /* Different reasons for being here, you know... */ switch (ccb->eata_ccb.cp_scsi_cmd) { case SCSI_TM_READ_BUFFER: if (read_buffer_callback != NULL) { /* This is a buffer generated by a kernel process */ read_buffer_callback(dpt->unit, br_channel, br_target, br_lun, read_buffer, br_offset, br_length); } else { /* * This is a buffer waited for by a user (sleeping) * command */ wakeup(ccb); } /* We ALWAYS re-issue the same command; args are don't-care */ dpt_set_target(1, 0, 0, 0, 0, 0, 0, 0, 0); break; case SCSI_TM_WRITE_BUFFER: (ccb->wrbuff_callback) (dpt->unit, br_channel, br_target, br_offset, br_length, br_lun, ccb->status_packet.hba_stat); break; default: printf("dpt%d: %s is an unsupported command for target mode\n", dpt->unit, scsi_cmd_name(ccb->eata_ccb.cp_scsi_cmd)); } ospl = splsoftcam(); dpt->target_ccb[br_channel][br_target][br_lun] = NULL; dpt_Qpush_free(dpt, ccb); splx(ospl); } /* * Use this function to register a client for a buffer read target operation. * The function you register will be called every time a buffer is received * by the target mode code. */ dpt_rb_t dpt_register_buffer(int unit, u_int8_t channel, u_int8_t target, u_int8_t lun, u_int8_t mode, u_int16_t length, u_int16_t offset, dpt_rec_buff callback, dpt_rb_op_t op) { dpt_softc_t *dpt; dpt_ccb_t *ccb = NULL; int ospl; for (dpt = TAILQ_FIRST(&dpt_softc_list); dpt != NULL; dpt = TAILQ_NEXT(dpt, links)) { if (dpt->unit == unit) goto valid_unit; } return (INVALID_UNIT); valid_unit: if (dpt->state & DPT_HA_SHUTDOWN_ACTIVE) return (DRIVER_DOWN); if ((channel > (dpt->channels - 1)) || (target > (dpt->max_id - 1)) || (lun > (dpt->max_lun - 1))) return (INVALID_SENDER); if (dpt->buffer_receiver[channel][target][lun] == NULL) { if (op == REGISTER_BUFFER) { /* Assign the requested callback */ dpt->buffer_receiver[channel][target][lun] = callback; /* Get a CCB */ ospl = splsoftcam(); /* Process the free list */ if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) { printf("dpt%d ERROR: Cannot allocate any more free CCB's.\n" " Please try later\n", dpt->unit); splx(ospl); return (NO_RESOURCES); } /* Now grab the newest CCB */ if ((ccb = dpt_Qpop_free(dpt)) == NULL) { splx(ospl); panic("dpt%d: Got a NULL CCB from pop_free()\n", dpt->unit); } splx(ospl); /* Clean up the leftover of the previous tenant */ ccb->status = DPT_CCB_STATE_NEW; dpt->target_ccb[channel][target][lun] = ccb; dpt->rw_buffer[channel][target][lun] = malloc(DPT_RW_BUFFER_SIZE, M_DEVBUF, M_NOWAIT); if (dpt->rw_buffer[channel][target][lun] == NULL) { printf("dpt%d: Failed to allocate " "Target-Mode buffer\n", dpt->unit); ospl = splsoftcam(); dpt_Qpush_free(dpt, ccb); splx(ospl); return (NO_RESOURCES); } dpt_set_target(0, dpt, channel, target, lun, mode, length, offset, ccb); return (SUCCESSFULLY_REGISTERED); } else return (NOT_REGISTERED); } else { if (op == REGISTER_BUFFER) { if (dpt->buffer_receiver[channel][target][lun] == callback) return (ALREADY_REGISTERED); else return (REGISTERED_TO_ANOTHER); } else { if (dpt->buffer_receiver[channel][target][lun] == callback) { dpt->buffer_receiver[channel][target][lun] = NULL; ospl = splsoftcam(); dpt_Qpush_free(dpt, ccb); splx(ospl); free(dpt->rw_buffer[channel][target][lun], M_DEVBUF); return (SUCCESSFULLY_REGISTERED); } else return (INVALID_CALLBACK); } } } /* Return the state of the blinking DPT LED's */ u_int8_t dpt_blinking_led(dpt_softc_t * dpt) { int ndx; int ospl; u_int32_t state; u_int32_t previous; u_int8_t result; ospl = splcam(); result = 0; for (ndx = 0, state = 0, previous = 0; (ndx < 10) && (state != previous); ndx++) { previous = state; state = dpt_inl(dpt, 1); } if ((state == previous) && (state == DPT_BLINK_INDICATOR)) result = dpt_inb(dpt, 5); splx(ospl); return (result); } /* * Execute a command which did not come from the kernel's SCSI layer. * The only way to map user commands to bus and target is to comply with the * standard DPT wire-down scheme: */ int dpt_user_cmd(dpt_softc_t * dpt, eata_pt_t * user_cmd, caddr_t cmdarg, int minor_no) { dpt_ccb_t *ccb; void *data; int channel, target, lun; int huh; int result; int ospl; int submitted; data = NULL; channel = minor2hba(minor_no); target = minor2target(minor_no); lun = minor2lun(minor_no); if ((channel > (dpt->channels - 1)) || (target > dpt->max_id) || (lun > dpt->max_lun)) return (ENXIO); if (target == dpt->sc_scsi_link[channel].adapter_targ) { /* This one is for the controller itself */ if ((user_cmd->eataID[0] != 'E') || (user_cmd->eataID[1] != 'A') || (user_cmd->eataID[2] != 'T') || (user_cmd->eataID[3] != 'A')) { return (ENXIO); } } /* Get a DPT CCB, so we can prepare a command */ ospl = splsoftcam(); /* Process the free list */ if ((TAILQ_EMPTY(&dpt->free_ccbs)) && dpt_alloc_freelist(dpt)) { printf("dpt%d ERROR: Cannot allocate any more free CCB's.\n" " Please try later\n", dpt->unit); splx(ospl); return (EFAULT); } /* Now grab the newest CCB */ if ((ccb = dpt_Qpop_free(dpt)) == NULL) { splx(ospl); panic("dpt%d: Got a NULL CCB from pop_free()\n", dpt->unit); } else { splx(ospl); /* Clean up the leftover of the previous tenant */ ccb->status = DPT_CCB_STATE_NEW; } bcopy((caddr_t) & user_cmd->command_packet, (caddr_t) & ccb->eata_ccb, sizeof(eata_ccb_t)); /* We do not want to do user specified scatter/gather. Why?? */ if (ccb->eata_ccb.scatter == 1) return (EINVAL); ccb->eata_ccb.Auto_Req_Sen = 1; ccb->eata_ccb.reqlen = htonl(sizeof(struct scsi_sense_data)); ccb->eata_ccb.cp_datalen = htonl(sizeof(ccb->eata_ccb.cp_datalen)); ccb->eata_ccb.cp_dataDMA = htonl(vtophys(ccb->eata_ccb.cp_dataDMA)); ccb->eata_ccb.cp_statDMA = htonl(vtophys(&ccb->eata_ccb.cp_statDMA)); ccb->eata_ccb.cp_reqDMA = htonl(vtophys(&ccb->eata_ccb.cp_reqDMA)); ccb->eata_ccb.cp_viraddr = (u_int32_t) & ccb; if (ccb->eata_ccb.DataIn || ccb->eata_ccb.DataOut) { /* Data I/O is involved in this command. Alocate buffer */ if (ccb->eata_ccb.cp_datalen > PAGE_SIZE) { data = contigmalloc(ccb->eata_ccb.cp_datalen, M_TEMP, M_WAITOK, 0, ~0, ccb->eata_ccb.cp_datalen, 0x10000); } else { data = malloc(ccb->eata_ccb.cp_datalen, M_TEMP, M_WAITOK); } if (data == NULL) { printf("dpt%d: Cannot allocate %d bytes " "for EATA command\n", dpt->unit, ccb->eata_ccb.cp_datalen); return (EFAULT); } #define usr_cmd_DMA (caddr_t)user_cmd->command_packet.cp_dataDMA if (ccb->eata_ccb.DataIn == 1) { if (copyin(usr_cmd_DMA, data, ccb->eata_ccb.cp_datalen) == -1) return (EFAULT); } } else { /* No data I/O involved here. Make sure the DPT knows that */ ccb->eata_ccb.cp_datalen = 0; data = NULL; } if (ccb->eata_ccb.FWNEST == 1) ccb->eata_ccb.FWNEST = 0; if (ccb->eata_ccb.cp_datalen != 0) { if (dpt_scatter_gather(dpt, ccb, ccb->eata_ccb.cp_datalen, data) != 0) { if (data != NULL) free(data, M_TEMP); return (EFAULT); } } /** * We are required to quiet a SCSI bus. * since we do not queue comands on a bus basis, * we wait for ALL commands on a controller to complete. * In the mean time, sched_queue() will not schedule new commands. */ if ((ccb->eata_ccb.cp_cdb[0] == MULTIFUNCTION_CMD) && (ccb->eata_ccb.cp_cdb[2] == BUS_QUIET)) { /* We wait for ALL traffic for this HBa to subside */ ospl = splsoftcam(); dpt->state |= DPT_HA_QUIET; splx(ospl); while ((submitted = dpt->submitted_ccbs_count) != 0) { huh = tsleep((void *) dpt, PCATCH | PRIBIO, "dptqt", 100 * hz); switch (huh) { case 0: /* Wakeup call received */ break; case EWOULDBLOCK: /* Timer Expired */ break; default: /* anything else */ break; } } } /* Resume normal operation */ if ((ccb->eata_ccb.cp_cdb[0] == MULTIFUNCTION_CMD) && (ccb->eata_ccb.cp_cdb[2] == BUS_UNQUIET)) { ospl = splsoftcam(); dpt->state &= ~DPT_HA_QUIET; splx(ospl); } /** * Schedule the command and submit it. * We bypass dpt_sched_queue, as it will block on DPT_HA_QUIET */ ccb->xs = NULL; ccb->flags = 0; ccb->eata_ccb.Auto_Req_Sen = 1; /* We always want this feature */ ccb->transaction_id = ++dpt->commands_processed; ccb->std_callback = (ccb_callback) dpt_user_cmd_done; ccb->result = (u_int32_t) & cmdarg; ccb->data = data; #ifdef DPT_MEASURE_PERFORMANCE ++dpt->performance.command_count[ccb->eata_ccb.cp_scsi_cmd]; ccb->command_started = microtime_now; #endif ospl = splcam(); dpt_Qadd_waiting(dpt, ccb); splx(ospl); dpt_sched_queue(dpt); /* Wait for the command to complete */ (void) tsleep((void *) ccb, PCATCH | PRIBIO, "dptucw", 100 * hz); /* Free allocated memory */ if (data != NULL) free(data, M_TEMP); return (0); } static void dpt_user_cmd_done(dpt_softc_t * dpt, int bus, dpt_ccb_t * ccb) { int ospl = splsoftcam(); u_int32_t result; caddr_t cmd_arg; /** * If Auto Request Sense is on, copyout the sense struct */ #define usr_pckt_DMA (caddr_t)(intptr_t)ntohl(ccb->eata_ccb.cp_reqDMA) #define usr_pckt_len ntohl(ccb->eata_ccb.cp_datalen) if (ccb->eata_ccb.Auto_Req_Sen == 1) { if (copyout((caddr_t) & ccb->sense_data, usr_pckt_DMA, sizeof(struct scsi_sense_data))) { ccb->result = EFAULT; dpt_Qpush_free(dpt, ccb); splx(ospl); wakeup(ccb); return; } } /* If DataIn is on, copyout the data */ if ((ccb->eata_ccb.DataIn == 1) && (ccb->status_packet.hba_stat == HA_NO_ERROR)) { if (copyout(ccb->data, usr_pckt_DMA, usr_pckt_len)) { dpt_Qpush_free(dpt, ccb); ccb->result = EFAULT; splx(ospl); wakeup(ccb); return; } } /* Copyout the status */ result = ccb->status_packet.hba_stat; cmd_arg = (caddr_t) ccb->result; if (copyout((caddr_t) & result, cmd_arg, sizeof(result))) { dpt_Qpush_free(dpt, ccb); ccb->result = EFAULT; splx(ospl); wakeup(ccb); return; } /* Put the CCB back in the freelist */ ccb->state |= DPT_CCB_STATE_COMPLETED; dpt_Qpush_free(dpt, ccb); /* Free allocated memory */ splx(ospl); return; } #ifdef DPT_HANDLE_TIMEOUTS /** * This function walks down the SUBMITTED queue. * Every request that is too old gets aborted and marked. * Since the DPT will complete (interrupt) immediately (what does that mean?), * We just walk the list, aborting old commands and marking them as such. * The dpt_complete function will get rid of the that were interrupted in the * normal manner. * * This function needs to run at splcam(), as it interacts with the submitted * queue, as well as the completed and free queues. Just like dpt_intr() does. * To run it at any ISPL other than that of dpt_intr(), will mean that dpt_intr * willbe able to pre-empt it, grab a transaction in progress (towards * destruction) and operate on it. The state of this transaction will be not * very clear. * The only other option, is to lock it only as long as necessary but have * dpt_intr() spin-wait on it. In a UP environment this makes no sense and in * a SMP environment, the advantage is dubvious for a function that runs once * every ten seconds for few microseconds and, on systems with healthy * hardware, does not do anything anyway. */ static void dpt_handle_timeouts(dpt_softc_t * dpt) { dpt_ccb_t *ccb; int ospl; ospl = splcam(); if (dpt->state & DPT_HA_TIMEOUTS_ACTIVE) { printf("dpt%d WARNING: Timeout Handling Collision\n", dpt->unit); splx(ospl); return; } dpt->state |= DPT_HA_TIMEOUTS_ACTIVE; /* Loop through the entire submitted queue, looking for lost souls */ for (ccb = TAILQ_FIRST(&dpt->submitted_ccbs); ccb != NULL; ccb = TAILQ_NEXT(ccb, links)) { struct scsi_xfer *xs; u_int32_t age, max_age; xs = ccb->xs; age = dpt_time_delta(ccb->command_started, microtime_now); #define TenSec 10000000 if (xs == NULL) { /* Local, non-kernel call */ max_age = TenSec; } else { max_age = (((xs->timeout * (dpt->submitted_ccbs_count + DPT_TIMEOUT_FACTOR)) > TenSec) ? (xs->timeout * (dpt->submitted_ccbs_count + DPT_TIMEOUT_FACTOR)) : TenSec); } /* * If a transaction is marked lost and is TWICE as old as we * care, then, and only then do we destroy it! */ if (ccb->state & DPT_CCB_STATE_MARKED_LOST) { /* Remember who is next */ if (age > (max_age * 2)) { dpt_Qremove_submitted(dpt, ccb); ccb->state &= ~DPT_CCB_STATE_MARKED_LOST; ccb->state |= DPT_CCB_STATE_ABORTED; #define cmd_name scsi_cmd_name(ccb->eata_ccb.cp_scsi_cmd) if (ccb->retries++ > DPT_RETRIES) { printf("dpt%d ERROR: Destroying stale " "%d (%s)\n" " on " "c%db%dt%du%d (%d/%d)\n", dpt->unit, ccb->transaction_id, cmd_name, dpt->unit, ccb->eata_ccb.cp_channel, ccb->eata_ccb.cp_id, ccb->eata_ccb.cp_LUN, age, ccb->retries); #define send_ccb &ccb->eata_ccb #define ESA EATA_SPECIFIC_ABORT (void) dpt_send_immediate(dpt, send_ccb, ESA, 0, 0); dpt_Qpush_free(dpt, ccb); /* The SCSI layer should re-try */ xs->error |= XS_TIMEOUT; xs->flags |= SCSI_ITSDONE; scsi_done(xs); } else { printf("dpt%d ERROR: Stale %d (%s) on " "c%db%dt%du%d (%d)\n" " gets another " "chance(%d/%d)\n", dpt->unit, ccb->transaction_id, cmd_name, dpt->unit, ccb->eata_ccb.cp_channel, ccb->eata_ccb.cp_id, ccb->eata_ccb.cp_LUN, age, ccb->retries, DPT_RETRIES); dpt_Qpush_waiting(dpt, ccb); dpt_sched_queue(dpt); } } } else { /* * This is a transaction that is not to be destroyed * (yet) But it is too old for our liking. We wait as * long as the upper layer thinks. Not really, we * multiply that by the number of commands in the * submitted queue + 1. */ if (!(ccb->state & DPT_CCB_STATE_MARKED_LOST) && (age != ~0) && (age > max_age)) { printf("dpt%d ERROR: Marking %d (%s) on " "c%db%dt%du%d \n" " as late after %dusec\n", dpt->unit, ccb->transaction_id, cmd_name, dpt->unit, ccb->eata_ccb.cp_channel, ccb->eata_ccb.cp_id, ccb->eata_ccb.cp_LUN, age); ccb->state |= DPT_CCB_STATE_MARKED_LOST; } } } dpt->state &= ~DPT_HA_TIMEOUTS_ACTIVE; splx(ospl); } static void dpt_timeout(void *arg) { dpt_softc_t *dpt = (dpt_softc_t *) arg; if (!(dpt->state & DPT_HA_TIMEOUTS_ACTIVE)) dpt_handle_timeouts(dpt); timeout(dpt_timeout, (caddr_t) dpt, hz * 10); } #endif /* DPT_HANDLE_TIMEOUTS */ #endif Index: head/sys/dev/firewire/sbp.c =================================================================== --- head/sys/dev/firewire/sbp.c (revision 116350) +++ head/sys/dev/firewire/sbp.c (revision 116351) @@ -1,2677 +1,2662 @@ /* * Copyright (c) 2003 Hidetosh Shimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetosh Shimokawa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the acknowledgement as bellow: * * This product includes software developed by K. Kobayashi and H. Shimokawa * * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #if __FreeBSD_version < 500106 #include /* for struct devstat */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ccb_sdev_ptr spriv_ptr0 #define ccb_sbp_ptr spriv_ptr1 #define SBP_NUM_TARGETS 8 /* MAX 64 */ #define SBP_NUM_LUNS 8 /* limited by CAM_SCSI2_MAXLUN in cam_xpt.c */ #define SBP_DMA_SIZE PAGE_SIZE #define SBP_LOGIN_SIZE sizeof(struct sbp_login_res) #define SBP_QUEUE_LEN ((SBP_DMA_SIZE - SBP_LOGIN_SIZE) / sizeof(struct sbp_ocb)) #define SBP_NUM_OCB (SBP_QUEUE_LEN * SBP_NUM_TARGETS) #define SBP_INITIATOR 7 #define LOGIN_DELAY 2 /* * STATUS FIFO addressing * bit * ----------------------- * 0- 1( 2): 0 (alingment) * 2- 7( 6): target * 8-15( 8): lun * 16-23( 8): unit * 24-31( 8): reserved * 32-47(16): SBP_BIND_HI * 48-64(16): bus_id, node_id */ #define SBP_BIND_HI 0x1 #define SBP_DEV2ADDR(u, t, l) \ ((((u) & 0xff) << 16) | (((l) & 0xff) << 8) | (((t) & 0x3f) << 2)) #define SBP_ADDR2TRG(a) (((a) >> 2) & 0x3f) #define SBP_ADDR2LUN(a) (((a) >> 8) & 0xff) #define ORB_NOTIFY (1 << 31) #define ORB_FMT_STD (0 << 29) #define ORB_FMT_VED (2 << 29) #define ORB_FMT_NOP (3 << 29) #define ORB_FMT_MSK (3 << 29) #define ORB_EXV (1 << 28) /* */ #define ORB_CMD_IN (1 << 27) /* */ #define ORB_CMD_SPD(x) ((x) << 24) #define ORB_CMD_MAXP(x) ((x) << 20) #define ORB_RCN_TMO(x) ((x) << 20) #define ORB_CMD_PTBL (1 << 19) #define ORB_CMD_PSZ(x) ((x) << 16) #define ORB_FUN_LGI (0 << 16) #define ORB_FUN_QLG (1 << 16) #define ORB_FUN_RCN (3 << 16) #define ORB_FUN_LGO (7 << 16) #define ORB_FUN_ATA (0xb << 16) #define ORB_FUN_ATS (0xc << 16) #define ORB_FUN_LUR (0xe << 16) #define ORB_FUN_RST (0xf << 16) #define ORB_FUN_MSK (0xf << 16) #define ORB_FUN_RUNQUEUE 0xffff static char *orb_fun_name[] = { /* 0 */ "LOGIN", /* 1 */ "QUERY LOGINS", /* 2 */ "Reserved", /* 3 */ "RECONNECT", /* 4 */ "SET PASSWORD", /* 5 */ "Reserved", /* 6 */ "Reserved", /* 7 */ "LOGOUT", /* 8 */ "Reserved", /* 9 */ "Reserved", /* A */ "Reserved", /* B */ "ABORT TASK", /* C */ "ABORT TASK SET", /* D */ "Reserved", /* E */ "LOGICAL UNIT RESET", /* F */ "TARGET RESET" }; #define ORB_RES_CMPL 0 #define ORB_RES_FAIL 1 #define ORB_RES_ILLE 2 #define ORB_RES_VEND 3 static int debug = 0; static int auto_login = 1; static int max_speed = 2; static int sbp_cold = 1; SYSCTL_DECL(_hw_firewire); SYSCTL_NODE(_hw_firewire, OID_AUTO, sbp, CTLFLAG_RD, 0, "SBP-II Subsystem"); SYSCTL_INT(_debug, OID_AUTO, sbp_debug, CTLFLAG_RW, &debug, 0, "SBP debug flag"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, auto_login, CTLFLAG_RW, &auto_login, 0, "SBP perform login automatically"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, max_speed, CTLFLAG_RW, &max_speed, 0, "SBP transfer max speed"); #define SBP_DEBUG(x) if (debug > x) { #define END_DEBUG } #define NEED_RESPONSE 0 struct ind_ptr { u_int32_t hi,lo; }; #define SBP_SEG_MAX rounddown(0xffff, PAGE_SIZE) #ifdef __sparc64__ /* iommu */ #define SBP_IND_MAX howmany(MAXPHYS, SBP_SEG_MAX) #else #define SBP_IND_MAX howmany(MAXPHYS, PAGE_SIZE) #endif struct sbp_ocb { STAILQ_ENTRY(sbp_ocb) ocb; union ccb *ccb; bus_addr_t bus_addr; volatile u_int32_t orb[8]; #define IND_PTR_OFFSET (8*sizeof(u_int32_t)) volatile struct ind_ptr ind_ptr[SBP_IND_MAX]; struct sbp_dev *sdev; int flags; /* XXX should be removed */ bus_dmamap_t dmamap; }; #define OCB_ACT_MGM 0 #define OCB_ACT_CMD 1 #define OCB_MATCH(o,s) ((o)->bus_addr == ntohl((s)->orb_lo)) #define SBP_RECV_LEN (16 + 32) /* header + payload */ struct sbp_login_res{ u_int16_t len; u_int16_t id; u_int16_t res0; u_int16_t cmd_hi; u_int32_t cmd_lo; u_int16_t res1; u_int16_t recon_hold; }; struct sbp_status{ #if BYTE_ORDER == BIG_ENDIAN u_int8_t src:2, resp:2, dead:1, len:3; #else u_int8_t len:3, dead:1, resp:2, src:2; #endif u_int8_t status; u_int16_t orb_hi; u_int32_t orb_lo; u_int32_t data[6]; }; struct sbp_cmd_status{ #define SBP_SFMT_CURR 0 #define SBP_SFMT_DEFER 1 #if BYTE_ORDER == BIG_ENDIAN u_int8_t sfmt:2, status:6; u_int8_t valid:1, mark:1, eom:1, ill_len:1, s_key:4; #else u_int8_t status:6, sfmt:2; u_int8_t s_key:4, ill_len:1, eom:1, mark:1, valid:1; #endif u_int8_t s_code; u_int8_t s_qlfr; u_int32_t info; u_int32_t cdb; #if BYTE_ORDER == BIG_ENDIAN u_int32_t s_keydep:24, fru:8; #else u_int32_t fru:8, s_keydep:24; #endif u_int32_t vend[2]; }; struct sbp_dev{ #define SBP_DEV_RESET 0 /* accept login */ #define SBP_DEV_LOGIN 1 /* to login */ #if 0 #define SBP_DEV_RECONN 2 /* to reconnect */ #endif #define SBP_DEV_TOATTACH 3 /* to attach */ #define SBP_DEV_PROBE 4 /* scan lun */ #define SBP_DEV_ATTACHED 5 /* in operation */ #define SBP_DEV_DEAD 6 /* unavailable unit */ #define SBP_DEV_RETRY 7 /* unavailable unit */ u_int8_t status:4, timeout:4; u_int8_t type; u_int16_t lun_id; int freeze; struct cam_path *path; struct sbp_target *target; struct fwdma_alloc dma; struct sbp_login_res *login; struct callout login_callout; struct sbp_ocb *ocb; STAILQ_HEAD(, sbp_ocb) ocbs; STAILQ_HEAD(, sbp_ocb) free_ocbs; char vendor[32]; char product[32]; char revision[10]; }; struct sbp_target { int target_id; int num_lun; struct sbp_dev *luns; struct sbp_softc *sbp; struct fw_device *fwdev; u_int32_t mgm_hi, mgm_lo; struct sbp_ocb *mgm_ocb_cur; STAILQ_HEAD(, sbp_ocb) mgm_ocb_queue; struct callout mgm_ocb_timeout; #define SCAN_DELAY 2 struct callout scan_callout; STAILQ_HEAD(, fw_xfer) xferlist; int n_xfer; }; struct sbp_softc { struct firewire_dev_comm fd; struct cam_sim *sim; struct cam_path *path; struct sbp_target targets[SBP_NUM_TARGETS]; struct fw_bind fwb; bus_dma_tag_t dmat; #define SBP_RESOURCE_SHORTAGE 0x10 unsigned char flags; }; static void sbp_post_explore __P((void *)); static void sbp_recv __P((struct fw_xfer *)); static void sbp_mgm_callback __P((struct fw_xfer *)); static void sbp_cmd_callback __P((struct fw_xfer *)); static void sbp_orb_pointer __P((struct sbp_dev *, struct sbp_ocb *)); static void sbp_execute_ocb __P((void *, bus_dma_segment_t *, int, int)); static void sbp_free_ocb __P((struct sbp_dev *, struct sbp_ocb *)); static void sbp_abort_ocb __P((struct sbp_ocb *, int)); static void sbp_abort_all_ocbs __P((struct sbp_dev *, int)); static struct fw_xfer * sbp_write_cmd __P((struct sbp_dev *, int, int)); static struct sbp_ocb * sbp_get_ocb __P((struct sbp_dev *)); static struct sbp_ocb * sbp_enqueue_ocb __P((struct sbp_dev *, struct sbp_ocb *)); static struct sbp_ocb * sbp_dequeue_ocb __P((struct sbp_dev *, struct sbp_status *)); static void sbp_cam_detach_target __P((struct sbp_target *)); static void sbp_mgm_timeout __P((void *arg)); static void sbp_timeout __P((void *arg)); static void sbp_mgm_orb __P((struct sbp_dev *, int, struct sbp_ocb *)); #define sbp_login(sdev) \ callout_reset(&(sdev)->login_callout, LOGIN_DELAY * hz, \ sbp_login_callout, (void *)(sdev)); MALLOC_DEFINE(M_SBP, "sbp", "SBP-II/FireWire"); /* cam related functions */ static void sbp_action(struct cam_sim *sim, union ccb *ccb); static void sbp_poll(struct cam_sim *sim); static void sbp_cam_scan_lun(struct cam_periph *, union ccb *); static void sbp_cam_scan_target(void *arg); static char *orb_status0[] = { /* 0 */ "No additional information to report", /* 1 */ "Request type not supported", /* 2 */ "Speed not supported", /* 3 */ "Page size not supported", /* 4 */ "Access denied", /* 5 */ "Logical unit not supported", /* 6 */ "Maximum payload too small", /* 7 */ "Reserved for future standardization", /* 8 */ "Resources unavailable", /* 9 */ "Function rejected", /* A */ "Login ID not recognized", /* B */ "Dummy ORB completed", /* C */ "Request aborted", /* FF */ "Unspecified error" #define MAX_ORB_STATUS0 0xd }; static char *orb_status1_object[] = { /* 0 */ "Operation request block (ORB)", /* 1 */ "Data buffer", /* 2 */ "Page table", /* 3 */ "Unable to specify" }; static char *orb_status1_serial_bus_error[] = { /* 0 */ "Missing acknowledge", /* 1 */ "Reserved; not to be used", /* 2 */ "Time-out error", /* 3 */ "Reserved; not to be used", /* 4 */ "Busy retry limit exceeded(X)", /* 5 */ "Busy retry limit exceeded(A)", /* 6 */ "Busy retry limit exceeded(B)", /* 7 */ "Reserved for future standardization", /* 8 */ "Reserved for future standardization", /* 9 */ "Reserved for future standardization", /* A */ "Reserved for future standardization", /* B */ "Tardy retry limit exceeded", /* C */ "Conflict error", /* D */ "Data error", /* E */ "Type error", /* F */ "Address error" }; static void sbp_identify(driver_t *driver, device_t parent) { device_t child; SBP_DEBUG(0) printf("sbp_identify\n"); END_DEBUG child = BUS_ADD_CHILD(parent, 0, "sbp", device_get_unit(parent)); } /* * sbp_probe() */ static int sbp_probe(device_t dev) { device_t pa; SBP_DEBUG(0) printf("sbp_probe\n"); END_DEBUG pa = device_get_parent(dev); if(device_get_unit(dev) != device_get_unit(pa)){ return(ENXIO); } device_set_desc(dev, "SBP2/SCSI over firewire"); if (bootverbose) debug = bootverbose; return (0); } static void sbp_show_sdev_info(struct sbp_dev *sdev, int new) { struct fw_device *fwdev; printf("%s:%d:%d ", device_get_nameunit(sdev->target->sbp->fd.dev), sdev->target->target_id, sdev->lun_id ); if (new == 2) { return; } fwdev = sdev->target->fwdev; printf("ordered:%d type:%d EUI:%08x%08x node:%d " "speed:%d maxrec:%d", (sdev->type & 0x40) >> 6, (sdev->type & 0x1f), fwdev->eui.hi, fwdev->eui.lo, fwdev->dst, fwdev->speed, fwdev->maxrec ); if (new) printf(" new!\n"); else printf("\n"); sbp_show_sdev_info(sdev, 2); printf("'%s' '%s' '%s'\n", sdev->vendor, sdev->product, sdev->revision); } static struct { int bus; int target; struct fw_eui64 eui; } wired[] = { /* Bus Target EUI64 */ #if 0 {0, 2, {0x00018ea0, 0x01fd0154}}, /* Logitec HDD */ {0, 0, {0x00018ea6, 0x00100682}}, /* Logitec DVD */ {0, 1, {0x00d03200, 0xa412006a}}, /* Yano HDD */ #endif {-1, -1, {0,0}} }; static int sbp_new_target(struct sbp_softc *sbp, struct fw_device *fwdev) { int bus, i, target=-1; char w[SBP_NUM_TARGETS]; bzero(w, sizeof(w)); bus = device_get_unit(sbp->fd.dev); /* XXX wired-down configuration should be gotten from tunable or device hint */ for (i = 0; wired[i].bus >= 0; i ++) { if (wired[i].bus == bus) { w[wired[i].target] = 1; if (wired[i].eui.hi == fwdev->eui.hi && wired[i].eui.lo == fwdev->eui.lo) target = wired[i].target; } } if (target >= 0) { if(target < SBP_NUM_TARGETS && sbp->targets[target].fwdev == NULL) return(target); device_printf(sbp->fd.dev, "target %d is not free for %08x:%08x\n", target, fwdev->eui.hi, fwdev->eui.lo); target = -1; } /* non-wired target */ for (i = 0; i < SBP_NUM_TARGETS; i ++) if (sbp->targets[i].fwdev == NULL && w[i] == 0) { target = i; break; } return target; } static struct sbp_target * sbp_alloc_target(struct sbp_softc *sbp, struct fw_device *fwdev) { int i, maxlun, lun; struct sbp_target *target; struct sbp_dev *sdev; struct crom_context cc; struct csrreg *reg; SBP_DEBUG(1) printf("sbp_alloc_target\n"); END_DEBUG i = sbp_new_target(sbp, fwdev); if (i < 0) { device_printf(sbp->fd.dev, "increase SBP_NUM_TARGETS!\n"); return NULL; } /* new target */ target = &sbp->targets[i]; target->sbp = sbp; target->fwdev = fwdev; target->target_id = i; /* XXX we may want to reload mgm port after each bus reset */ /* XXX there might be multiple management agents */ crom_init_context(&cc, target->fwdev->csrrom); reg = crom_search_key(&cc, CROM_MGM); if (reg == NULL || reg->val == 0) { printf("NULL management address\n"); target->fwdev = NULL; return NULL; } target->mgm_hi = 0xffff; target->mgm_lo = 0xf0000000 | (reg->val << 2); target->mgm_ocb_cur = NULL; SBP_DEBUG(1) printf("target:%d mgm_port: %x\n", i, target->mgm_lo); END_DEBUG STAILQ_INIT(&target->xferlist); target->n_xfer = 0; STAILQ_INIT(&target->mgm_ocb_queue); CALLOUT_INIT(&target->mgm_ocb_timeout); CALLOUT_INIT(&target->scan_callout); /* XXX num_lun may be changed. realloc luns? */ crom_init_context(&cc, target->fwdev->csrrom); /* XXX shoud parse appropriate unit directories only */ maxlun = -1; while (cc.depth >= 0) { reg = crom_search_key(&cc, CROM_LUN); if (reg == NULL) break; lun = reg->val & 0xffff; SBP_DEBUG(0) printf("target %d lun %d found\n", target->target_id, lun); END_DEBUG if (maxlun < lun) maxlun = lun; crom_next(&cc); } if (maxlun < 0) printf("no lun found!\n"); if (maxlun >= SBP_NUM_LUNS) maxlun = SBP_NUM_LUNS; target->num_lun = maxlun + 1; target->luns = (struct sbp_dev *) malloc( sizeof(struct sbp_dev) * target->num_lun, M_SBP, M_NOWAIT | M_ZERO); for (i = 0; i < target->num_lun; i++) { sdev = &target->luns[i]; sdev->lun_id = i; sdev->target = target; STAILQ_INIT(&sdev->ocbs); CALLOUT_INIT(&sdev->login_callout); sdev->status = SBP_DEV_DEAD; } crom_init_context(&cc, target->fwdev->csrrom); while (cc.depth >= 0) { reg = crom_search_key(&cc, CROM_LUN); if (reg == NULL) break; lun = reg->val & 0xffff; if (lun >= SBP_NUM_LUNS) { printf("too large lun %d\n", lun); continue; } sdev = &target->luns[lun]; sdev->status = SBP_DEV_RESET; sdev->type = (reg->val & 0xf0000) >> 16; fwdma_malloc(sbp->fd.fc, /* alignment */ sizeof(u_int32_t), SBP_DMA_SIZE, &sdev->dma, BUS_DMA_NOWAIT); if (sdev->dma.v_addr == NULL) { printf("%s: dma space allocation failed\n", __FUNCTION__); return (NULL); } sdev->login = (struct sbp_login_res *) sdev->dma.v_addr; sdev->ocb = (struct sbp_ocb *) ((char *)sdev->dma.v_addr + SBP_LOGIN_SIZE); bzero((char *)sdev->ocb, sizeof (struct sbp_ocb) * SBP_QUEUE_LEN); STAILQ_INIT(&sdev->free_ocbs); for (i = 0; i < SBP_QUEUE_LEN; i++) { struct sbp_ocb *ocb; ocb = &sdev->ocb[i]; ocb->bus_addr = sdev->dma.bus_addr + SBP_LOGIN_SIZE + sizeof(struct sbp_ocb) * i + offsetof(struct sbp_ocb, orb[0]); if (bus_dmamap_create(sbp->dmat, 0, &ocb->dmamap)) { printf("sbp_attach: cannot create dmamap\n"); return (NULL); } sbp_free_ocb(sdev, ocb); } crom_next(&cc); } return target; } static void sbp_probe_lun(struct sbp_dev *sdev) { struct fw_device *fwdev; struct crom_context c, *cc = &c; struct csrreg *reg; bzero(sdev->vendor, sizeof(sdev->vendor)); bzero(sdev->product, sizeof(sdev->product)); fwdev = sdev->target->fwdev; crom_init_context(cc, fwdev->csrrom); /* get vendor string */ crom_search_key(cc, CSRKEY_VENDOR); crom_next(cc); crom_parse_text(cc, sdev->vendor, sizeof(sdev->vendor)); /* skip to the unit directory for SBP-2 */ while ((reg = crom_search_key(cc, CSRKEY_VER)) != NULL) { if (reg->val == CSRVAL_T10SBP2) break; crom_next(cc); } /* get firmware revision */ reg = crom_search_key(cc, CSRKEY_FIRM_VER); if (reg != NULL) snprintf(sdev->revision, sizeof(sdev->revision), "%06x", reg->val); /* get product string */ crom_search_key(cc, CSRKEY_MODEL); crom_next(cc); crom_parse_text(cc, sdev->product, sizeof(sdev->product)); } static void sbp_login_callout(void *arg) { struct sbp_dev *sdev = (struct sbp_dev *)arg; sbp_mgm_orb(sdev, ORB_FUN_LGI, NULL); } #define SBP_FWDEV_ALIVE(fwdev) (((fwdev)->status == FWDEVATTACHED) \ && crom_has_specver((fwdev)->csrrom, CSRVAL_ANSIT10, CSRVAL_T10SBP2)) static void sbp_probe_target(void *arg) { struct sbp_target *target = (struct sbp_target *)arg; struct sbp_softc *sbp; struct sbp_dev *sdev; struct firewire_comm *fc; int i, alive; alive = SBP_FWDEV_ALIVE(target->fwdev); SBP_DEBUG(1) printf("sbp_probe_target %d\n", target->target_id); if (!alive) printf("not alive\n"); END_DEBUG sbp = target->sbp; fc = target->sbp->fd.fc; /* XXX untimeout mgm_ocb and dequeue */ for (i=0; i < target->num_lun; i++) { sdev = &target->luns[i]; if (alive && (sdev->status != SBP_DEV_DEAD)) { if (sdev->path != NULL) { xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; } sbp_probe_lun(sdev); SBP_DEBUG(0) sbp_show_sdev_info(sdev, (sdev->status == SBP_DEV_RESET)); END_DEBUG sbp_abort_all_ocbs(sdev, CAM_SCSI_BUS_RESET); switch (sdev->status) { case SBP_DEV_RESET: /* new or revived target */ if (auto_login) sbp_login(sdev); break; case SBP_DEV_TOATTACH: case SBP_DEV_PROBE: case SBP_DEV_ATTACHED: case SBP_DEV_RETRY: default: sbp_mgm_orb(sdev, ORB_FUN_RCN, NULL); break; } } else { switch (sdev->status) { case SBP_DEV_ATTACHED: SBP_DEBUG(0) /* the device has gone */ sbp_show_sdev_info(sdev, 2); printf("lost target\n"); END_DEBUG if (sdev->path) { xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; } sdev->status = SBP_DEV_RETRY; sbp_abort_all_ocbs(sdev, CAM_SCSI_BUS_RESET); break; case SBP_DEV_PROBE: case SBP_DEV_TOATTACH: sdev->status = SBP_DEV_RESET; break; case SBP_DEV_RETRY: case SBP_DEV_RESET: case SBP_DEV_DEAD: break; } } } } static void sbp_post_busreset(void *arg) { struct sbp_softc *sbp; sbp = (struct sbp_softc *)arg; SBP_DEBUG(0) printf("sbp_post_busreset\n"); END_DEBUG } static void sbp_post_explore(void *arg) { struct sbp_softc *sbp = (struct sbp_softc *)arg; struct sbp_target *target; struct fw_device *fwdev; int i, alive; SBP_DEBUG(0) printf("sbp_post_explore (sbp_cold=%d)\n", sbp_cold); END_DEBUG #if 0 /* * XXX don't let CAM the bus rest. CAM tries to do something with * freezed (DEV_RETRY) devices */ xpt_async(AC_BUS_RESET, sbp->path, /*arg*/ NULL); #endif if (sbp_cold > 0) sbp_cold --; /* Gabage Collection */ for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ target = &sbp->targets[i]; STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) if (target->fwdev == NULL || target->fwdev == fwdev) break; if(fwdev == NULL){ /* device has removed in lower driver */ sbp_cam_detach_target(target); if (target->luns != NULL) free(target->luns, M_SBP); target->num_lun = 0;; target->luns = NULL; target->fwdev = NULL; } } /* traverse device list */ STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) { SBP_DEBUG(0) printf("sbp_post_explore: EUI:%08x%08x ", fwdev->eui.hi, fwdev->eui.lo); if (fwdev->status != FWDEVATTACHED) printf("not attached, state=%d.\n", fwdev->status); else printf("attached\n"); END_DEBUG alive = SBP_FWDEV_ALIVE(fwdev); for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ target = &sbp->targets[i]; if(target->fwdev == fwdev ) { /* known target */ break; } } if(i == SBP_NUM_TARGETS){ if (alive) { /* new target */ target = sbp_alloc_target(sbp, fwdev); if (target == NULL) continue; } else { continue; } } sbp_probe_target((void *)target); } } #if NEED_RESPONSE static void sbp_loginres_callback(struct fw_xfer *xfer){ int s; struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_loginres_callback\n"); END_DEBUG /* recycle */ s = splfw(); STAILQ_INSERT_TAIL(&sdev->target->sbp->fwb.xferlist, xfer, link); splx(s); return; } #endif static __inline void sbp_xfer_free(struct fw_xfer *xfer) { struct sbp_dev *sdev; int s; sdev = (struct sbp_dev *)xfer->sc; fw_xfer_unload(xfer); s = splfw(); STAILQ_INSERT_TAIL(&sdev->target->xferlist, xfer, link); splx(s); } static void sbp_reset_start_callback(struct fw_xfer *xfer) { struct sbp_dev *tsdev, *sdev = (struct sbp_dev *)xfer->sc; struct sbp_target *target = sdev->target; int i; if (xfer->resp != 0) { sbp_show_sdev_info(sdev, 2); printf("sbp_reset_start failed: resp=%d\n", xfer->resp); } for (i = 0; i < target->num_lun; i++) { tsdev = &target->luns[i]; if (tsdev->status == SBP_DEV_LOGIN) sbp_login(sdev); } } static void sbp_reset_start(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_reset_start\n"); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); xfer->act.hand = sbp_reset_start_callback; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqq.dest_hi = 0xffff; fp->mode.wreqq.dest_lo = 0xf0000000 | RESET_START; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_mgm_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; int resp; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_mgm_callback\n"); END_DEBUG resp = xfer->resp; sbp_xfer_free(xfer); #if 0 if (resp != 0) { sbp_show_sdev_info(sdev, 2); printf("management ORB failed(%d) ... RESET_START\n", resp); sbp_reset_start(sdev); } #endif return; } static void sbp_cmd_callback(struct fw_xfer *xfer) { SBP_DEBUG(2) struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; sbp_show_sdev_info(sdev, 2); printf("sbp_cmd_callback\n"); END_DEBUG sbp_xfer_free(xfer); return; } static struct sbp_dev * sbp_next_dev(struct sbp_target *target, int lun) { struct sbp_dev *sdev; int i; for (i = lun, sdev = &target->luns[lun]; i < target->num_lun; i++, sdev++) { if (sdev->status == SBP_DEV_PROBE) break; } if (i >= target->num_lun) return(NULL); return(sdev); } #define SCAN_PRI 1 static void sbp_cam_scan_lun(struct cam_periph *periph, union ccb *ccb) { struct sbp_target *target; struct sbp_dev *sdev; sdev = (struct sbp_dev *) ccb->ccb_h.ccb_sdev_ptr; target = sdev->target; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_cam_scan_lun\n"); END_DEBUG if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { sdev->status = SBP_DEV_ATTACHED; } else { sbp_show_sdev_info(sdev, 2); printf("scan failed\n"); } sdev = sbp_next_dev(target, sdev->lun_id + 1); if (sdev == NULL) { free(ccb, M_SBP); return; } /* reuse ccb */ xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); ccb->ccb_h.ccb_sdev_ptr = sdev; xpt_action(ccb); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 1; } static void sbp_cam_scan_target(void *arg) { struct sbp_target *target = (struct sbp_target *)arg; struct sbp_dev *sdev; union ccb *ccb; sdev = sbp_next_dev(target, 0); if (sdev == NULL) { printf("sbp_cam_scan_target: nothing to do for target%d\n", target->target_id); return; } SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_cam_scan_target\n"); END_DEBUG ccb = malloc(sizeof(union ccb), M_SBP, M_NOWAIT | M_ZERO); if (ccb == NULL) { printf("sbp_cam_scan_target: malloc failed\n"); return; } xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); ccb->ccb_h.func_code = XPT_SCAN_LUN; ccb->ccb_h.cbfcnp = sbp_cam_scan_lun; ccb->ccb_h.flags |= CAM_DEV_QFREEZE; ccb->crcn.flags = CAM_FLAG_NONE; ccb->ccb_h.ccb_sdev_ptr = sdev; /* The scan is in progress now. */ xpt_action(ccb); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 1; } static __inline void sbp_scan_dev(struct sbp_dev *sdev) { sdev->status = SBP_DEV_PROBE; callout_reset(&sdev->target->scan_callout, SCAN_DELAY * hz, sbp_cam_scan_target, (void *)sdev->target); } static void sbp_do_attach(struct fw_xfer *xfer) { struct sbp_dev *sdev; struct sbp_target *target; struct sbp_softc *sbp; sdev = (struct sbp_dev *)xfer->sc; target = sdev->target; sbp = target->sbp; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_do_attach\n"); END_DEBUG sbp_xfer_free(xfer); if (sdev->path == NULL) xpt_create_path(&sdev->path, xpt_periph, cam_sim_path(target->sbp->sim), target->target_id, sdev->lun_id); /* * Let CAM scan the bus if we are in the boot process. * XXX xpt_scan_bus cannot detect LUN larger than 0 * if LUN 0 doesn't exists. */ if (sbp_cold > 0) { sdev->status = SBP_DEV_ATTACHED; return; } sbp_scan_dev(sdev); return; } static void sbp_agent_reset_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_cmd_callback\n"); END_DEBUG if (xfer->resp != 0) { sbp_show_sdev_info(sdev, 2); printf("sbp_cmd_callback resp=%d\n", xfer->resp); } sbp_xfer_free(xfer); if (sdev->path) { xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 0; } } static void sbp_agent_reset(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_agent_reset\n"); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x04); if (xfer == NULL) return; if (sdev->status == SBP_DEV_ATTACHED || sdev->status == SBP_DEV_PROBE) xfer->act.hand = sbp_agent_reset_callback; else xfer->act.hand = sbp_do_attach; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); sbp_abort_all_ocbs(sdev, CAM_BDR_SENT); } static void sbp_busy_timeout_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_busy_timeout_callback\n"); END_DEBUG sbp_xfer_free(xfer); sbp_agent_reset(sdev); } static void sbp_busy_timeout(struct sbp_dev *sdev) { struct fw_pkt *fp; struct fw_xfer *xfer; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("sbp_busy_timeout\n"); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); xfer->act.hand = sbp_busy_timeout_callback; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqq.dest_hi = 0xffff; fp->mode.wreqq.dest_lo = 0xf0000000 | BUSY_TIMEOUT; fp->mode.wreqq.data = htonl((1 << (13+12)) | 0xf); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_orb_pointer(struct sbp_dev *sdev, struct sbp_ocb *ocb) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(2) sbp_show_sdev_info(sdev, 2); printf("sbp_orb_pointer\n"); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0x08); if (xfer == NULL) return; xfer->act.hand = sbp_cmd_callback; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqb.len = 8; fp->mode.wreqb.extcode = 0; fp->mode.wreqb.payload[0] = htonl(((sdev->target->sbp->fd.fc->nodeid | FWLOCALBUS )<< 16)); fp->mode.wreqb.payload[1] = htonl(ocb->bus_addr); if(fw_asyreq(xfer->fc, -1, xfer) != 0){ sbp_xfer_free(xfer); ocb->ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ocb->ccb); } } #if 0 static void sbp_doorbell(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_doorbell\n"); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x10); if (xfer == NULL) return; xfer->act.hand = sbp_cmd_callback; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); } #endif static struct fw_xfer * sbp_write_cmd(struct sbp_dev *sdev, int tcode, int offset) { struct fw_xfer *xfer; struct fw_pkt *fp; struct sbp_target *target; int s, new = 0; target = sdev->target; s = splfw(); xfer = STAILQ_FIRST(&target->xferlist); if (xfer == NULL) { if (target->n_xfer > 5 /* XXX */) { printf("sbp: no more xfer for this target\n"); splx(s); return(NULL); } xfer = fw_xfer_alloc_buf(M_SBP, 24, 12); if(xfer == NULL){ printf("sbp: fw_xfer_alloc_buf failed\n"); splx(s); return NULL; } target->n_xfer ++; if (debug) printf("sbp: alloc %d xfer\n", target->n_xfer); new = 1; } else { STAILQ_REMOVE_HEAD(&target->xferlist, link); } splx(s); microtime(&xfer->tv); if (tcode == FWTCODE_WREQQ) xfer->send.len = 16; else xfer->send.len = 24; xfer->recv.len = 12; if (new) { xfer->spd = min(sdev->target->fwdev->speed, max_speed); xfer->fc = sdev->target->sbp->fd.fc; xfer->retry_req = fw_asybusy; } xfer->sc = (caddr_t)sdev; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqq.dest_hi = sdev->login->cmd_hi; fp->mode.wreqq.dest_lo = sdev->login->cmd_lo + offset; fp->mode.wreqq.tlrt = 0; fp->mode.wreqq.tcode = tcode; fp->mode.wreqq.pri = 0; xfer->dst = FWLOCALBUS | sdev->target->fwdev->dst; fp->mode.wreqq.dst = xfer->dst; return xfer; } static void sbp_mgm_orb(struct sbp_dev *sdev, int func, struct sbp_ocb *aocb) { struct fw_xfer *xfer; struct fw_pkt *fp; struct sbp_ocb *ocb; struct sbp_target *target; int s, nid; target = sdev->target; nid = target->sbp->fd.fc->nodeid | FWLOCALBUS; s = splfw(); if (func == ORB_FUN_RUNQUEUE) { ocb = STAILQ_FIRST(&target->mgm_ocb_queue); if (target->mgm_ocb_cur != NULL || ocb == NULL) { splx(s); return; } STAILQ_REMOVE_HEAD(&target->mgm_ocb_queue, ocb); goto start; } if ((ocb = sbp_get_ocb(sdev)) == NULL) { splx(s); return; } ocb->flags = OCB_ACT_MGM; ocb->sdev = sdev; bzero((void *)(uintptr_t)(volatile void *)ocb->orb, sizeof(ocb->orb)); ocb->orb[6] = htonl((nid << 16) | SBP_BIND_HI); ocb->orb[7] = htonl(SBP_DEV2ADDR( device_get_unit(target->sbp->fd.dev), target->target_id, sdev->lun_id)); SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("%s\n", orb_fun_name[(func>>16)&0xf]); END_DEBUG switch (func) { case ORB_FUN_LGI: ocb->orb[2] = htonl(nid << 16); ocb->orb[3] = htonl(sdev->dma.bus_addr); ocb->orb[4] = htonl(ORB_NOTIFY | ORB_EXV | sdev->lun_id); ocb->orb[5] = htonl(SBP_LOGIN_SIZE); fwdma_sync(&sdev->dma, BUS_DMASYNC_PREREAD); break; case ORB_FUN_ATA: ocb->orb[0] = htonl((0 << 16) | 0); ocb->orb[1] = htonl(aocb->bus_addr & 0xffffffff); /* fall through */ case ORB_FUN_RCN: case ORB_FUN_LGO: case ORB_FUN_LUR: case ORB_FUN_RST: case ORB_FUN_ATS: ocb->orb[4] = htonl(ORB_NOTIFY | func | sdev->login->id); break; } if (target->mgm_ocb_cur != NULL) { /* there is a standing ORB */ STAILQ_INSERT_TAIL(&sdev->target->mgm_ocb_queue, ocb, ocb); splx(s); return; } start: target->mgm_ocb_cur = ocb; splx(s); callout_reset(&target->mgm_ocb_timeout, 5*hz, sbp_mgm_timeout, (caddr_t)ocb); xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0); if(xfer == NULL){ return; } xfer->act.hand = sbp_mgm_callback; fp = (struct fw_pkt *)xfer->send.buf; fp->mode.wreqb.dest_hi = sdev->target->mgm_hi; fp->mode.wreqb.dest_lo = sdev->target->mgm_lo; fp->mode.wreqb.len = 8; fp->mode.wreqb.extcode = 0; fp->mode.wreqb.payload[0] = htonl(nid << 16); fp->mode.wreqb.payload[1] = htonl(ocb->bus_addr); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_print_scsi_cmd(struct sbp_ocb *ocb) { struct ccb_scsiio *csio; csio = &ocb->ccb->csio; printf("%s:%d:%d XPT_SCSI_IO: " "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" ", flags: 0x%02x, " "%db cmd/%db data/%db sense\n", device_get_nameunit(ocb->sdev->target->sbp->fd.dev), ocb->ccb->ccb_h.target_id, ocb->ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], csio->cdb_io.cdb_bytes[1], csio->cdb_io.cdb_bytes[2], csio->cdb_io.cdb_bytes[3], csio->cdb_io.cdb_bytes[4], csio->cdb_io.cdb_bytes[5], csio->cdb_io.cdb_bytes[6], csio->cdb_io.cdb_bytes[7], csio->cdb_io.cdb_bytes[8], csio->cdb_io.cdb_bytes[9], ocb->ccb->ccb_h.flags & CAM_DIR_MASK, csio->cdb_len, csio->dxfer_len, csio->sense_len); } static void sbp_scsi_status(struct sbp_status *sbp_status, struct sbp_ocb *ocb) { struct sbp_cmd_status *sbp_cmd_status; struct scsi_sense_data *sense; sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data; sense = &ocb->ccb->csio.sense_data; SBP_DEBUG(0) sbp_print_scsi_cmd(ocb); /* XXX need decode status */ sbp_show_sdev_info(ocb->sdev, 2); printf("SCSI status %x sfmt %x valid %x key %x code %x qlfr %x len %d\n", sbp_cmd_status->status, sbp_cmd_status->sfmt, sbp_cmd_status->valid, sbp_cmd_status->s_key, sbp_cmd_status->s_code, sbp_cmd_status->s_qlfr, sbp_status->len ); END_DEBUG switch (sbp_cmd_status->status) { case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_BUSY: case SCSI_STATUS_CMD_TERMINATED: if(sbp_cmd_status->sfmt == SBP_SFMT_CURR){ sense->error_code = SSD_CURRENT_ERROR; }else{ sense->error_code = SSD_DEFERRED_ERROR; } if(sbp_cmd_status->valid) sense->error_code |= SSD_ERRCODE_VALID; sense->flags = sbp_cmd_status->s_key; if(sbp_cmd_status->mark) sense->flags |= SSD_FILEMARK; if(sbp_cmd_status->eom) sense->flags |= SSD_EOM; if(sbp_cmd_status->ill_len) sense->flags |= SSD_ILI; sense->info[0] = ntohl(sbp_cmd_status->info) & 0xff; sense->info[1] =(ntohl(sbp_cmd_status->info) >> 8) & 0xff; sense->info[2] =(ntohl(sbp_cmd_status->info) >> 16) & 0xff; sense->info[3] =(ntohl(sbp_cmd_status->info) >> 24) & 0xff; if (sbp_status->len <= 1) /* XXX not scsi status. shouldn't be happened */ sense->extra_len = 0; else if (sbp_status->len <= 4) /* add_sense_code(_qual), info, cmd_spec_info */ sense->extra_len = 6; else /* fru, sense_key_spec */ sense->extra_len = 10; sense->cmd_spec_info[0] = ntohl(sbp_cmd_status->cdb) & 0xff; sense->cmd_spec_info[1] = (ntohl(sbp_cmd_status->cdb) >> 8) & 0xff; sense->cmd_spec_info[2] = (ntohl(sbp_cmd_status->cdb) >> 16) & 0xff; sense->cmd_spec_info[3] = (ntohl(sbp_cmd_status->cdb) >> 24) & 0xff; sense->add_sense_code = sbp_cmd_status->s_code; sense->add_sense_code_qual = sbp_cmd_status->s_qlfr; sense->fru = sbp_cmd_status->fru; sense->sense_key_spec[0] = ntohl(sbp_cmd_status->s_keydep) & 0xff; sense->sense_key_spec[1] = (ntohl(sbp_cmd_status->s_keydep) >>8) & 0xff; sense->sense_key_spec[2] = (ntohl(sbp_cmd_status->s_keydep) >>16) & 0xff; ocb->ccb->csio.scsi_status = sbp_cmd_status->status;; ocb->ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; /* { u_int8_t j, *tmp; tmp = sense; for( j = 0 ; j < 32 ; j+=8){ printf("sense %02x%02x %02x%02x %02x%02x %02x%02x\n", tmp[j], tmp[j+1], tmp[j+2], tmp[j+3], tmp[j+4], tmp[j+5], tmp[j+6], tmp[j+7]); } } */ break; default: sbp_show_sdev_info(ocb->sdev, 2); printf("sbp_scsi_status: unknown scsi status 0x%x\n", sbp_cmd_status->status); } } static void sbp_fix_inq_data(struct sbp_ocb *ocb) { union ccb *ccb; struct sbp_dev *sdev; struct scsi_inquiry_data *inq; ccb = ocb->ccb; sdev = ocb->sdev; if (ccb->csio.cdb_io.cdb_bytes[1] & SI_EVPD) return; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); printf("sbp_fix_inq_data\n"); END_DEBUG inq = (struct scsi_inquiry_data *) ccb->csio.data_ptr; switch (SID_TYPE(inq)) { case T_DIRECT: /* * XXX Convert Direct Access device to RBC. * I've never seen FireWire DA devices which support READ_6. */ #if 1 if (SID_TYPE(inq) == T_DIRECT) inq->device |= T_RBC; /* T_DIRECT == 0 */ #endif /* fall through */ case T_RBC: /* enable tag queuing */ #if 1 inq->flags |= SID_CmdQue; #endif /* * Override vendor/product/revision information. * Some devices sometimes return strange strings. */ #if 1 bcopy(sdev->vendor, inq->vendor, sizeof(inq->vendor)); bcopy(sdev->product, inq->product, sizeof(inq->product)); bcopy(sdev->revision+2, inq->revision, sizeof(inq->revision)); #endif break; } } static void sbp_recv1(struct fw_xfer *xfer) { struct fw_pkt *rfp; #if NEED_RESPONSE struct fw_pkt *sfp; #endif struct sbp_softc *sbp; struct sbp_dev *sdev; struct sbp_ocb *ocb; struct sbp_login_res *login_res = NULL; struct sbp_status *sbp_status; struct sbp_target *target; int orb_fun, status_valid0, status_valid, t, l, reset_agent = 0; u_int32_t addr; /* u_int32_t *ld; ld = xfer->recv.buf; printf("sbp %x %d %d %08x %08x %08x %08x\n", xfer->resp, xfer->recv.len, xfer->recv.off, ntohl(ld[0]), ntohl(ld[1]), ntohl(ld[2]), ntohl(ld[3])); printf("sbp %08x %08x %08x %08x\n", ntohl(ld[4]), ntohl(ld[5]), ntohl(ld[6]), ntohl(ld[7])); printf("sbp %08x %08x %08x %08x\n", ntohl(ld[8]), ntohl(ld[9]), ntohl(ld[10]), ntohl(ld[11])); */ sbp = (struct sbp_softc *)xfer->sc; if(xfer->resp != 0){ printf("sbp_recv: xfer->resp != 0\n"); goto done0; } if(xfer->recv.buf == NULL){ printf("sbp_recv: xfer->recv.buf == NULL\n"); goto done0; } sbp = (struct sbp_softc *)xfer->sc; rfp = (struct fw_pkt *)xfer->recv.buf; if(rfp->mode.wreqb.tcode != FWTCODE_WREQB){ printf("sbp_recv: tcode = %d\n", rfp->mode.wreqb.tcode); goto done0; } sbp_status = (struct sbp_status *)rfp->mode.wreqb.payload; addr = rfp->mode.wreqb.dest_lo; SBP_DEBUG(2) printf("received address 0x%x\n", addr); END_DEBUG t = SBP_ADDR2TRG(addr); if (t >= SBP_NUM_TARGETS) { device_printf(sbp->fd.dev, "sbp_recv1: invalid target %d\n", t); goto done0; } target = &sbp->targets[t]; l = SBP_ADDR2LUN(addr); if (l >= target->num_lun) { device_printf(sbp->fd.dev, "sbp_recv1: invalid lun %d (target=%d)\n", l, t); goto done0; } sdev = &target->luns[l]; ocb = NULL; switch (sbp_status->src) { case 0: case 1: /* check mgm_ocb_cur first */ ocb = target->mgm_ocb_cur; if (ocb != NULL) { if (OCB_MATCH(ocb, sbp_status)) { callout_stop(&target->mgm_ocb_timeout); target->mgm_ocb_cur = NULL; break; } } ocb = sbp_dequeue_ocb(sdev, sbp_status); if (ocb == NULL) { sbp_show_sdev_info(sdev, 2); #if __FreeBSD_version >= 500000 printf("No ocb(%x) on the queue\n", #else printf("No ocb(%lx) on the queue\n", #endif ntohl(sbp_status->orb_lo)); } break; case 2: /* unsolicit */ sbp_show_sdev_info(sdev, 2); printf("unsolicit status received\n"); break; default: sbp_show_sdev_info(sdev, 2); printf("unknown sbp_status->src\n"); } status_valid0 = (sbp_status->src < 2 && sbp_status->resp == ORB_RES_CMPL && sbp_status->dead == 0); status_valid = (status_valid0 && sbp_status->status == 0); if (!status_valid0 || debug > 1){ int status; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("ORB status src:%x resp:%x dead:%x" #if __FreeBSD_version >= 500000 " len:%x stat:%x orb:%x%08x\n", #else " len:%x stat:%x orb:%x%08lx\n", #endif sbp_status->src, sbp_status->resp, sbp_status->dead, sbp_status->len, sbp_status->status, ntohs(sbp_status->orb_hi), ntohl(sbp_status->orb_lo)); END_DEBUG sbp_show_sdev_info(sdev, 2); status = sbp_status->status; switch(sbp_status->resp) { case 0: if (status > MAX_ORB_STATUS0) printf("%s\n", orb_status0[MAX_ORB_STATUS0]); else printf("%s\n", orb_status0[status]); break; case 1: printf("Obj: %s, Error: %s\n", orb_status1_object[(status>>6) & 3], orb_status1_serial_bus_error[status & 0xf]); break; case 2: printf("Illegal request\n"); break; case 3: printf("Vendor dependent\n"); break; default: printf("unknown respose code %d\n", sbp_status->resp); } } /* we have to reset the fetch agent if it's dead */ if (sbp_status->dead) { if (sdev->path) { xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; } reset_agent = 1; } if (ocb == NULL) goto done; switch(ntohl(ocb->orb[4]) & ORB_FMT_MSK){ case ORB_FMT_NOP: break; case ORB_FMT_VED: break; case ORB_FMT_STD: switch(ocb->flags) { case OCB_ACT_MGM: orb_fun = ntohl(ocb->orb[4]) & ORB_FUN_MSK; switch(orb_fun) { case ORB_FUN_LGI: fwdma_sync(&sdev->dma, BUS_DMASYNC_POSTREAD); login_res = sdev->login; login_res->len = ntohs(login_res->len); login_res->id = ntohs(login_res->id); login_res->cmd_hi = ntohs(login_res->cmd_hi); login_res->cmd_lo = ntohl(login_res->cmd_lo); if (status_valid) { SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("login: len %d, ID %d, cmd %08x%08x, recon_hold %d\n", login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo, ntohs(login_res->recon_hold)); END_DEBUG sbp_busy_timeout(sdev); } else { /* forgot logout? */ sbp_show_sdev_info(sdev, 2); printf("login failed\n"); sdev->status = SBP_DEV_RESET; } break; case ORB_FUN_RCN: login_res = sdev->login; if (status_valid) { SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("reconnect: len %d, ID %d, cmd %08x%08x\n", login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo); END_DEBUG #if 1 if (sdev->status == SBP_DEV_ATTACHED) sbp_scan_dev(sdev); else sbp_agent_reset(sdev); #else sdev->status = SBP_DEV_ATTACHED; sbp_mgm_orb(sdev, ORB_FUN_ATS, NULL); #endif } else { /* reconnection hold time exceed? */ SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); printf("reconnect failed\n"); END_DEBUG sbp_login(sdev); } break; case ORB_FUN_LGO: sdev->status = SBP_DEV_RESET; break; case ORB_FUN_RST: sbp_busy_timeout(sdev); break; case ORB_FUN_LUR: case ORB_FUN_ATA: case ORB_FUN_ATS: sbp_agent_reset(sdev); break; default: sbp_show_sdev_info(sdev, 2); printf("unknown function %d\n", orb_fun); break; } sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); break; case OCB_ACT_CMD: sdev->timeout = 0; if(ocb->ccb != NULL){ union ccb *ccb; /* u_int32_t *ld; ld = ocb->ccb->csio.data_ptr; if(ld != NULL && ocb->ccb->csio.dxfer_len != 0) printf("ptr %08x %08x %08x %08x\n", ld[0], ld[1], ld[2], ld[3]); else printf("ptr NULL\n"); printf("len %d\n", sbp_status->len); */ ccb = ocb->ccb; if(sbp_status->len > 1){ sbp_scsi_status(sbp_status, ocb); }else{ if(sbp_status->resp != ORB_RES_CMPL){ ccb->ccb_h.status = CAM_REQ_CMP_ERR; }else{ ccb->ccb_h.status = CAM_REQ_CMP; } } /* fix up inq data */ if (ccb->csio.cdb_io.cdb_bytes[0] == INQUIRY) sbp_fix_inq_data(ocb); xpt_done(ccb); } break; default: break; } } sbp_free_ocb(sdev, ocb); done: if (reset_agent) sbp_agent_reset(sdev); done0: /* The received packet is usually small enough to be stored within * the buffer. In that case, the controller return ack_complete and * no respose is necessary. * * XXX fwohci.c and firewire.c should inform event_code such as * ack_complete or ack_pending to upper driver. */ #if NEED_RESPONSE xfer->send.off = 0; sfp = (struct fw_pkt *)xfer->send.buf; sfp->mode.wres.dst = rfp->mode.wreqb.src; xfer->dst = sfp->mode.wres.dst; xfer->spd = min(sdev->target->fwdev->speed, max_speed); xfer->act.hand = sbp_loginres_callback; xfer->retry_req = fw_asybusy; sfp->mode.wres.tlrt = rfp->mode.wreqb.tlrt; sfp->mode.wres.tcode = FWTCODE_WRES; sfp->mode.wres.rtcode = 0; sfp->mode.wres.pri = 0; fw_asyreq(xfer->fc, -1, xfer); #else /* recycle */ xfer->recv.len = SBP_RECV_LEN; STAILQ_INSERT_TAIL(&sbp->fwb.xferlist, xfer, link); #endif return; } static void sbp_recv(struct fw_xfer *xfer) { int s; s = splcam(); sbp_recv1(xfer); splx(s); } /* * sbp_attach() */ static int sbp_attach(device_t dev) { struct sbp_softc *sbp; struct cam_devq *devq; struct fw_xfer *xfer; int i, s, error; SBP_DEBUG(0) printf("sbp_attach (cold=%d)\n", cold); END_DEBUG if (cold) sbp_cold ++; sbp = ((struct sbp_softc *)device_get_softc(dev)); bzero(sbp, sizeof(struct sbp_softc)); sbp->fd.dev = dev; sbp->fd.fc = device_get_ivars(dev); error = bus_dma_tag_create(/*parent*/sbp->fd.fc->dmat, /* XXX shoud be 4 for sane backend? */ /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/0x100000, /*nsegments*/SBP_IND_MAX, /*maxsegsz*/SBP_SEG_MAX, /*flags*/BUS_DMA_ALLOCNOW, &sbp->dmat); if (error != 0) { printf("sbp_attach: Could not allocate DMA tag " "- error %d\n", error); return (ENOMEM); } devq = cam_simq_alloc(/*maxopenings*/SBP_NUM_OCB); if (devq == NULL) return (ENXIO); for( i = 0 ; i < SBP_NUM_TARGETS ; i++){ sbp->targets[i].fwdev = NULL; sbp->targets[i].luns = NULL; } sbp->sim = cam_sim_alloc(sbp_action, sbp_poll, "sbp", sbp, device_get_unit(dev), /*untagged*/ 1, /*tagged*/ SBP_QUEUE_LEN, devq); if (sbp->sim == NULL) { cam_simq_free(devq); return (ENXIO); } if (xpt_bus_register(sbp->sim, /*bus*/0) != CAM_SUCCESS) goto fail; if (xpt_create_path(&sbp->path, xpt_periph, cam_sim_path(sbp->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) goto fail; sbp->fwb.start_hi = SBP_BIND_HI; sbp->fwb.start_lo = SBP_DEV2ADDR(device_get_unit(sbp->fd.dev), 0, 0); /* We reserve 16 bit space (4 bytes X 64 targets X 256 luns) */ sbp->fwb.addrlen = 0xffff; sbp->fwb.act_type = FWACT_XFER; /* pre-allocate xfer */ STAILQ_INIT(&sbp->fwb.xferlist); for (i = 0; i < SBP_NUM_OCB/2; i ++) { xfer = fw_xfer_alloc_buf(M_SBP, #if NEED_RESPONSE /* send */12, #else /* send */0, #endif /* recv */SBP_RECV_LEN); xfer->act.hand = sbp_recv; #if NEED_RESPONSE xfer->fc = sbp->fd.fc; #endif xfer->sc = (caddr_t)sbp; STAILQ_INSERT_TAIL(&sbp->fwb.xferlist, xfer, link); } fw_bindadd(sbp->fd.fc, &sbp->fwb); sbp->fd.post_busreset = sbp_post_busreset; sbp->fd.post_explore = sbp_post_explore; if (sbp->fd.fc->status != -1) { s = splfw(); sbp_post_explore((void *)sbp); splx(s); } return (0); fail: cam_sim_free(sbp->sim, /*free_devq*/TRUE); return (ENXIO); } static int sbp_logout_all(struct sbp_softc *sbp) { struct sbp_target *target; struct sbp_dev *sdev; int i, j; SBP_DEBUG(0) printf("sbp_logout_all\n"); END_DEBUG for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) { target = &sbp->targets[i]; if (target->luns == NULL) continue; for (j = 0; j < target->num_lun; j++) { sdev = &target->luns[j]; callout_stop(&sdev->login_callout); if (sdev->status >= SBP_DEV_TOATTACH && sdev->status <= SBP_DEV_ATTACHED) sbp_mgm_orb(sdev, ORB_FUN_LGO, NULL); } } return 0; } static int sbp_shutdown(device_t dev) { struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); sbp_logout_all(sbp); return (0); } static int sbp_detach(device_t dev) { struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); struct firewire_comm *fc = sbp->fd.fc; struct sbp_target *target; struct sbp_dev *sdev; struct fw_xfer *xfer, *next; int i, j; SBP_DEBUG(0) printf("sbp_detach\n"); END_DEBUG for (i = 0; i < SBP_NUM_TARGETS; i ++) sbp_cam_detach_target(&sbp->targets[i]); xpt_free_path(sbp->path); xpt_bus_deregister(cam_sim_path(sbp->sim)); sbp_logout_all(sbp); /* XXX wait for logout completion */ tsleep(&i, FWPRI, "sbpdtc", hz/2); for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) { target = &sbp->targets[i]; if (target->luns == NULL) continue; callout_stop(&target->mgm_ocb_timeout); for (j = 0; j < target->num_lun; j++) { sdev = &target->luns[j]; if (sdev->status != SBP_DEV_DEAD) { for (i = 0; i < SBP_QUEUE_LEN; i++) bus_dmamap_destroy(sbp->dmat, sdev->ocb[i].dmamap); fwdma_free(sbp->fd.fc, &sdev->dma); } } for (xfer = STAILQ_FIRST(&target->xferlist); xfer != NULL; xfer = next) { next = STAILQ_NEXT(xfer, link); fw_xfer_free(xfer); } free(target->luns, M_SBP); } for (xfer = STAILQ_FIRST(&sbp->fwb.xferlist); xfer != NULL; xfer = next) { next = STAILQ_NEXT(xfer, link); fw_xfer_free(xfer); } STAILQ_INIT(&sbp->fwb.xferlist); fw_bindremove(fc, &sbp->fwb); bus_dma_tag_destroy(sbp->dmat); return (0); } static void sbp_cam_detach_target(struct sbp_target *target) { struct sbp_dev *sdev; int i; if (target->luns != NULL) { SBP_DEBUG(0) printf("sbp_detach_target %d\n", target->target_id); END_DEBUG callout_stop(&target->scan_callout); for (i = 0; i < target->num_lun; i++) { sdev = &target->luns[i]; if (sdev->status == SBP_DEV_DEAD) continue; if (sdev->status == SBP_DEV_RESET) continue; if (sdev->path) { xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 0; xpt_async(AC_LOST_DEVICE, sdev->path, NULL); xpt_free_path(sdev->path); sdev->path = NULL; } sbp_abort_all_ocbs(sdev, CAM_DEV_NOT_THERE); } } } static void sbp_target_reset(struct sbp_dev *sdev, int method) { int i; struct sbp_target *target = sdev->target; struct sbp_dev *tsdev; for (i = 0; i < target->num_lun; i++) { tsdev = &target->luns[i]; if (tsdev->status == SBP_DEV_DEAD) continue; if (tsdev->status == SBP_DEV_RESET) continue; xpt_freeze_devq(tsdev->path, 1); tsdev->freeze ++; sbp_abort_all_ocbs(tsdev, CAM_CMD_TIMEOUT); if (method == 2) tsdev->status = SBP_DEV_LOGIN; } switch(method) { case 1: printf("target reset\n"); sbp_mgm_orb(sdev, ORB_FUN_RST, NULL); break; case 2: printf("reset start\n"); sbp_reset_start(sdev); break; } } static void sbp_mgm_timeout(void *arg) { struct sbp_ocb *ocb = (struct sbp_ocb *)arg; struct sbp_dev *sdev = ocb->sdev; struct sbp_target *target = sdev->target; sbp_show_sdev_info(sdev, 2); printf("management ORB timeout\n"); target->mgm_ocb_cur = NULL; sbp_free_ocb(sdev, ocb); #if 0 /* XXX */ sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); #endif #if 0 sbp_reset_start(sdev); #endif } static void sbp_timeout(void *arg) { struct sbp_ocb *ocb = (struct sbp_ocb *)arg; struct sbp_dev *sdev = ocb->sdev; sbp_show_sdev_info(sdev, 2); printf("request timeout ... "); sdev->timeout ++; switch(sdev->timeout) { case 1: printf("agent reset\n"); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; sbp_abort_all_ocbs(sdev, CAM_CMD_TIMEOUT); sbp_agent_reset(sdev); break; case 2: case 3: sbp_target_reset(sdev, sdev->timeout - 1); break; #if 0 default: /* XXX give up */ sbp_cam_detach_target(target); if (target->luns != NULL) free(target->luns, M_SBP); target->num_lun = 0;; target->luns = NULL; target->fwdev = NULL; #endif } } static void sbp_action1(struct cam_sim *sim, union ccb *ccb) { struct sbp_softc *sbp = (struct sbp_softc *)sim->softc; struct sbp_target *target = NULL; struct sbp_dev *sdev = NULL; /* target:lun -> sdev mapping */ if (sbp != NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD && ccb->ccb_h.target_id < SBP_NUM_TARGETS) { target = &sbp->targets[ccb->ccb_h.target_id]; if (target->fwdev != NULL && ccb->ccb_h.target_lun != CAM_LUN_WILDCARD && ccb->ccb_h.target_lun < target->num_lun) { sdev = &target->luns[ccb->ccb_h.target_lun]; if (sdev->status != SBP_DEV_ATTACHED && sdev->status != SBP_DEV_PROBE) sdev = NULL; } } SBP_DEBUG(1) if (sdev == NULL) printf("invalid target %d lun %d\n", ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: case XPT_RESET_DEV: case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: case XPT_CALC_GEOMETRY: if (sdev == NULL) { SBP_DEBUG(1) printf("%s:%d:%d:func_code 0x%04x: " "Invalid target (target needed)\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); END_DEBUG ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } break; case XPT_PATH_INQ: case XPT_NOOP: /* The opcodes sometimes aimed at a target (sc is valid), * sometimes aimed at the SIM (sc is invalid and target is * CAM_TARGET_WILDCARD) */ if (sbp == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) { SBP_DEBUG(0) printf("%s:%d:%d func_code 0x%04x: " "Invalid target (no wildcard)\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); END_DEBUG ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } break; default: /* XXX Hm, we should check the input parameters */ break; } switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct ccb_scsiio *csio; struct sbp_ocb *ocb; int speed; void *cdb; csio = &ccb->csio; SBP_DEBUG(1) printf("%s:%d:%d XPT_SCSI_IO: " "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" ", flags: 0x%02x, " "%db cmd/%db data/%db sense\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], csio->cdb_io.cdb_bytes[1], csio->cdb_io.cdb_bytes[2], csio->cdb_io.cdb_bytes[3], csio->cdb_io.cdb_bytes[4], csio->cdb_io.cdb_bytes[5], csio->cdb_io.cdb_bytes[6], csio->cdb_io.cdb_bytes[7], csio->cdb_io.cdb_bytes[8], csio->cdb_io.cdb_bytes[9], ccb->ccb_h.flags & CAM_DIR_MASK, csio->cdb_len, csio->dxfer_len, csio->sense_len); END_DEBUG if(sdev == NULL){ ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } #if 0 /* if we are in probe stage, pass only probe commands */ if (sdev->status == SBP_DEV_PROBE) { char *name; name = xpt_path_periph(ccb->ccb_h.path)->periph_name; printf("probe stage, periph name: %s\n", name); if (strcmp(name, "probe") != 0) { ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } } #endif if ((ocb = sbp_get_ocb(sdev)) == NULL) return; ocb->flags = OCB_ACT_CMD; ocb->sdev = sdev; ocb->ccb = ccb; ccb->ccb_h.ccb_sdev_ptr = sdev; ocb->orb[0] = htonl(1 << 31); ocb->orb[1] = 0; ocb->orb[2] = htonl(((sbp->fd.fc->nodeid | FWLOCALBUS )<< 16) ); ocb->orb[3] = htonl(ocb->bus_addr + IND_PTR_OFFSET); speed = min(target->fwdev->speed, max_speed); ocb->orb[4] = htonl(ORB_NOTIFY | ORB_CMD_SPD(speed) | ORB_CMD_MAXP(speed + 7)); if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN){ ocb->orb[4] |= htonl(ORB_CMD_IN); } if (csio->ccb_h.flags & CAM_SCATTER_VALID) printf("sbp: CAM_SCATTER_VALID\n"); if (csio->ccb_h.flags & CAM_DATA_PHYS) printf("sbp: CAM_DATA_PHYS\n"); if (csio->ccb_h.flags & CAM_CDB_POINTER) cdb = (void *)csio->cdb_io.cdb_ptr; else cdb = (void *)&csio->cdb_io.cdb_bytes; bcopy(cdb, (void *)(uintptr_t)(volatile void *)&ocb->orb[5], csio->cdb_len); /* printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[0]), ntohl(ocb->orb[1]), ntohl(ocb->orb[2]), ntohl(ocb->orb[3])); printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[4]), ntohl(ocb->orb[5]), ntohl(ocb->orb[6]), ntohl(ocb->orb[7])); */ if (ccb->csio.dxfer_len > 0) { int s, error; s = splsoftvm(); error = bus_dmamap_load(/*dma tag*/sbp->dmat, /*dma map*/ocb->dmamap, ccb->csio.data_ptr, ccb->csio.dxfer_len, sbp_execute_ocb, ocb, /*flags*/0); splx(s); if (error) printf("sbp: bus_dmamap_load error %d\n", error); } else sbp_execute_ocb(ocb, NULL, 0, 0); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended = 1; - ccg = &ccb->ccg; + ccg = &ccb->ccg; if (ccg->block_size == 0) { printf("sbp_action1: block_size is 0.\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } SBP_DEBUG(1) printf("%s:%d:%d:%d:XPT_CALC_GEOMETRY: " #if __FreeBSD_version >= 500000 "Volume size = %jd\n", #else "Volume size = %d\n", #endif device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, #if __FreeBSD_version >= 500000 (uintmax_t) #endif ccg->volume_size); END_DEBUG - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb >= 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { SBP_DEBUG(1) printf("%s:%d:XPT_RESET_BUS: \n", device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim)); END_DEBUG ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; SBP_DEBUG(1) printf("%s:%d:%d XPT_PATH_INQ:.\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = SBP_NUM_TARGETS - 1; cpi->max_lun = SBP_NUM_LUNS - 1; cpi->initiator_id = SBP_INITIATOR; cpi->bus_id = sim->bus_id; cpi->base_transfer_speed = 400 * 1000 / 8; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "SBP", HBA_IDLEN); strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); cpi->unit_number = sim->unit_number; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; SBP_DEBUG(1) printf("%s:%d:%d XPT_GET_TRAN_SETTINGS:.\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG /* Enable disconnect and tagged queuing */ cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; cts->flags = CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB; cts->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_ABORT: ccb->ccb_h.status = CAM_UA_ABORT; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: /* XXX */ default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } return; } static void sbp_action(struct cam_sim *sim, union ccb *ccb) { int s; s = splfw(); sbp_action1(sim, ccb); splx(s); } static void sbp_execute_ocb(void *arg, bus_dma_segment_t *segments, int seg, int error) { int i; struct sbp_ocb *ocb; struct sbp_ocb *prev; bus_dma_segment_t *s; if (error) printf("sbp_execute_ocb: error=%d\n", error); ocb = (struct sbp_ocb *)arg; SBP_DEBUG(1) printf("sbp_execute_ocb: seg %d", seg); for (i = 0; i < seg; i++) #if __FreeBSD_version >= 500000 printf(", %jx:%jd", (uintmax_t)segments[i].ds_addr, (uintmax_t)segments[i].ds_len); #else printf(", %x:%d", segments[i].ds_addr, segments[i].ds_len); #endif printf("\n"); END_DEBUG if (seg == 1) { /* direct pointer */ s = &segments[0]; if (s->ds_len > SBP_SEG_MAX) panic("ds_len > SBP_SEG_MAX, fix busdma code"); ocb->orb[3] = htonl(s->ds_addr); ocb->orb[4] |= htonl(s->ds_len); } else if(seg > 1) { /* page table */ for (i = 0; i < seg; i++) { s = &segments[i]; SBP_DEBUG(0) /* XXX LSI Logic "< 16 byte" bug might be hit */ if (s->ds_len < 16) printf("sbp_execute_ocb: warning, " #if __FreeBSD_version >= 500000 "segment length(%zd) is less than 16." #else "segment length(%d) is less than 16." #endif "(seg=%d/%d)\n", s->ds_len, i+1, seg); END_DEBUG if (s->ds_len > SBP_SEG_MAX) panic("ds_len > SBP_SEG_MAX, fix busdma code"); ocb->ind_ptr[i].hi = htonl(s->ds_len << 16); ocb->ind_ptr[i].lo = htonl(s->ds_addr); } ocb->orb[4] |= htonl(ORB_CMD_PTBL | seg); } if (seg > 0) bus_dmamap_sync(ocb->sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); prev = sbp_enqueue_ocb(ocb->sdev, ocb); fwdma_sync(&ocb->sdev->dma, BUS_DMASYNC_PREWRITE); if (prev == NULL) sbp_orb_pointer(ocb->sdev, ocb); } static void sbp_poll(struct cam_sim *sim) { /* should call fwohci_intr? */ return; } static struct sbp_ocb * sbp_dequeue_ocb(struct sbp_dev *sdev, struct sbp_status *sbp_status) { struct sbp_ocb *ocb; struct sbp_ocb *next; int s = splfw(), order = 0; int flags; for (ocb = STAILQ_FIRST(&sdev->ocbs); ocb != NULL; ocb = next) { next = STAILQ_NEXT(ocb, ocb); flags = ocb->flags; SBP_DEBUG(1) sbp_show_sdev_info(sdev, 2); #if __FreeBSD_version >= 500000 printf("orb: 0x%jx next: 0x%x, flags %x\n", (uintmax_t)ocb->bus_addr, #else printf("orb: 0x%x next: 0x%lx, flags %x\n", ocb->bus_addr, #endif ntohl(ocb->orb[1]), flags); END_DEBUG if (OCB_MATCH(ocb, sbp_status)) { /* found */ STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb); if (ocb->ccb != NULL) untimeout(sbp_timeout, (caddr_t)ocb, ocb->ccb->ccb_h.timeout_ch); if (ntohl(ocb->orb[4]) & 0xffff) { bus_dmamap_sync(sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap); } if (next != NULL && sbp_status->src == 1) sbp_orb_pointer(sdev, next); break; } else order ++; } splx(s); SBP_DEBUG(0) if (ocb && order > 0) { sbp_show_sdev_info(sdev, 2); printf("unordered execution order:%d\n", order); } END_DEBUG return (ocb); } static struct sbp_ocb * sbp_enqueue_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) { int s = splfw(); struct sbp_ocb *prev; SBP_DEBUG(2) sbp_show_sdev_info(sdev, 2); #if __FreeBSD_version >= 500000 printf("sbp_enqueue_ocb orb=0x%jx in physical memory\n", (uintmax_t)ocb->bus_addr); #else printf("sbp_enqueue_ocb orb=0x%x in physical memory\n", ocb->bus_addr); #endif END_DEBUG prev = STAILQ_LAST(&sdev->ocbs, sbp_ocb, ocb); STAILQ_INSERT_TAIL(&sdev->ocbs, ocb, ocb); if (ocb->ccb != NULL) ocb->ccb->ccb_h.timeout_ch = timeout(sbp_timeout, (caddr_t)ocb, (ocb->ccb->ccb_h.timeout * hz) / 1000); if (prev != NULL ) { SBP_DEBUG(1) #if __FreeBSD_version >= 500000 printf("linking chain 0x%jx -> 0x%jx\n", (uintmax_t)prev->bus_addr, (uintmax_t)ocb->bus_addr); #else printf("linking chain 0x%x -> 0x%x\n", prev->bus_addr, ocb->bus_addr); #endif END_DEBUG prev->orb[1] = htonl(ocb->bus_addr); prev->orb[0] = 0; } splx(s); return prev; } static struct sbp_ocb * sbp_get_ocb(struct sbp_dev *sdev) { struct sbp_ocb *ocb; int s = splfw(); ocb = STAILQ_FIRST(&sdev->free_ocbs); if (ocb == NULL) { printf("ocb shortage!!!\n"); return NULL; } STAILQ_REMOVE_HEAD(&sdev->free_ocbs, ocb); splx(s); ocb->ccb = NULL; return (ocb); } static void sbp_free_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) { ocb->flags = 0; ocb->ccb = NULL; STAILQ_INSERT_TAIL(&sdev->free_ocbs, ocb, ocb); } static void sbp_abort_ocb(struct sbp_ocb *ocb, int status) { struct sbp_dev *sdev; sdev = ocb->sdev; SBP_DEBUG(0) sbp_show_sdev_info(sdev, 2); #if __FreeBSD_version >= 500000 printf("sbp_abort_ocb 0x%jx\n", (uintmax_t)ocb->bus_addr); #else printf("sbp_abort_ocb 0x%x\n", ocb->bus_addr); #endif END_DEBUG SBP_DEBUG(1) if (ocb->ccb != NULL) sbp_print_scsi_cmd(ocb); END_DEBUG if (ntohl(ocb->orb[4]) & 0xffff) { bus_dmamap_sync(sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap); } if (ocb->ccb != NULL) { untimeout(sbp_timeout, (caddr_t)ocb, ocb->ccb->ccb_h.timeout_ch); ocb->ccb->ccb_h.status = status; xpt_done(ocb->ccb); } sbp_free_ocb(sdev, ocb); } static void sbp_abort_all_ocbs(struct sbp_dev *sdev, int status) { int s; struct sbp_ocb *ocb, *next; STAILQ_HEAD(, sbp_ocb) temp; s = splfw(); bcopy(&sdev->ocbs, &temp, sizeof(temp)); STAILQ_INIT(&sdev->ocbs); for (ocb = STAILQ_FIRST(&temp); ocb != NULL; ocb = next) { next = STAILQ_NEXT(ocb, ocb); sbp_abort_ocb(ocb, status); } splx(s); } static devclass_t sbp_devclass; static device_method_t sbp_methods[] = { /* device interface */ DEVMETHOD(device_identify, sbp_identify), DEVMETHOD(device_probe, sbp_probe), DEVMETHOD(device_attach, sbp_attach), DEVMETHOD(device_detach, sbp_detach), DEVMETHOD(device_shutdown, sbp_shutdown), { 0, 0 } }; static driver_t sbp_driver = { "sbp", sbp_methods, sizeof(struct sbp_softc), }; DRIVER_MODULE(sbp, firewire, sbp_driver, sbp_devclass, 0, 0); MODULE_VERSION(sbp, 1); MODULE_DEPEND(sbp, firewire, 1, 1, 1); MODULE_DEPEND(sbp, cam, 1, 1, 1); Index: head/sys/dev/isp/isp_freebsd.c =================================================================== --- head/sys/dev/isp/isp_freebsd.c (revision 116350) +++ head/sys/dev/isp/isp_freebsd.c (revision 116351) @@ -1,3125 +1,3113 @@ /* $FreeBSD$ */ /* * Platform (FreeBSD) dependent common attachment code for Qlogic adapters. * * Copyright (c) 1997, 1998, 1999, 2000, 2001 by Matthew Jacob * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include /* for use by isp_prt below */ #include #include #include #include MODULE_VERSION(isp, 1); int isp_announced = 0; ispfwfunc *isp_get_firmware_p = NULL; static d_ioctl_t ispioctl; static void isp_intr_enable(void *); static void isp_cam_async(void *, u_int32_t, struct cam_path *, void *); static void isp_poll(struct cam_sim *); static timeout_t isp_watchdog; static void isp_kthread(void *); static void isp_action(struct cam_sim *, union ccb *); #define ISP_CDEV_MAJOR 248 static struct cdevsw isp_cdevsw = { .d_open = nullopen, .d_close = nullclose, .d_ioctl = ispioctl, .d_name = "isp", .d_maj = ISP_CDEV_MAJOR, .d_flags = D_TAPE, }; static struct ispsoftc *isplist = NULL; void isp_attach(struct ispsoftc *isp) { int primary, secondary; struct ccb_setasync csa; struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; /* * Establish (in case of 12X0) which bus is the primary. */ primary = 0; secondary = 1; /* * Create the device queue for our SIM(s). */ devq = cam_simq_alloc(isp->isp_maxcmds); if (devq == NULL) { return; } /* * Construct our SIM entry. */ ISPLOCK_2_CAMLOCK(isp); sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq); if (sim == NULL) { cam_simq_free(devq); CAMLOCK_2_ISPLOCK(isp); return; } CAMLOCK_2_ISPLOCK(isp); isp->isp_osinfo.ehook.ich_func = isp_intr_enable; isp->isp_osinfo.ehook.ich_arg = isp; ISPLOCK_2_CAMLOCK(isp); if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) { cam_sim_free(sim, TRUE); CAMLOCK_2_ISPLOCK(isp); isp_prt(isp, ISP_LOGERR, "could not establish interrupt enable hook"); return; } if (xpt_bus_register(sim, primary) != CAM_SUCCESS) { cam_sim_free(sim, TRUE); CAMLOCK_2_ISPLOCK(isp); return; } if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); config_intrhook_disestablish(&isp->isp_osinfo.ehook); CAMLOCK_2_ISPLOCK(isp); return; } xpt_setup_ccb(&csa.ccb_h, path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = isp_cam_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); CAMLOCK_2_ISPLOCK(isp); isp->isp_sim = sim; isp->isp_path = path; /* * Create a kernel thread for fibre channel instances. We * don't have dual channel FC cards. */ if (IS_FC(isp)) { ISPLOCK_2_CAMLOCK(isp); /* XXX: LOCK VIOLATION */ cv_init(&isp->isp_osinfo.kthread_cv, "isp_kthread_cv"); if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc, RFHIGHPID, 0, "%s: fc_thrd", device_get_nameunit(isp->isp_dev))) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); config_intrhook_disestablish(&isp->isp_osinfo.ehook); CAMLOCK_2_ISPLOCK(isp); isp_prt(isp, ISP_LOGERR, "could not create kthread"); return; } CAMLOCK_2_ISPLOCK(isp); } /* * If we have a second channel, construct SIM entry for that. */ if (IS_DUALBUS(isp)) { ISPLOCK_2_CAMLOCK(isp); sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq); if (sim == NULL) { xpt_bus_deregister(cam_sim_path(isp->isp_sim)); xpt_free_path(isp->isp_path); cam_simq_free(devq); config_intrhook_disestablish(&isp->isp_osinfo.ehook); return; } if (xpt_bus_register(sim, secondary) != CAM_SUCCESS) { xpt_bus_deregister(cam_sim_path(isp->isp_sim)); xpt_free_path(isp->isp_path); cam_sim_free(sim, TRUE); config_intrhook_disestablish(&isp->isp_osinfo.ehook); CAMLOCK_2_ISPLOCK(isp); return; } if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(isp->isp_sim)); xpt_free_path(isp->isp_path); xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); config_intrhook_disestablish(&isp->isp_osinfo.ehook); CAMLOCK_2_ISPLOCK(isp); return; } xpt_setup_ccb(&csa.ccb_h, path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = isp_cam_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); CAMLOCK_2_ISPLOCK(isp); isp->isp_sim2 = sim; isp->isp_path2 = path; } #ifdef ISP_TARGET_MODE cv_init(&isp->isp_osinfo.tgtcv0[0], "isp_tgcv0a"); cv_init(&isp->isp_osinfo.tgtcv0[1], "isp_tgcv0b"); cv_init(&isp->isp_osinfo.tgtcv1[0], "isp_tgcv1a"); cv_init(&isp->isp_osinfo.tgtcv1[1], "isp_tgcv1b"); #endif /* * Create device nodes */ (void) make_dev(&isp_cdevsw, device_get_unit(isp->isp_dev), UID_ROOT, GID_OPERATOR, 0600, "%s", device_get_nameunit(isp->isp_dev)); if (isp->isp_role != ISP_ROLE_NONE) { isp->isp_state = ISP_RUNSTATE; ENABLE_INTS(isp); } if (isplist == NULL) { isplist = isp; } else { struct ispsoftc *tmp = isplist; while (tmp->isp_osinfo.next) { tmp = tmp->isp_osinfo.next; } tmp->isp_osinfo.next = isp; } } static INLINE void isp_freeze_loopdown(struct ispsoftc *isp, char *msg) { if (isp->isp_osinfo.simqfrozen == 0) { isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown)", msg); isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; ISPLOCK_2_CAMLOCK(isp); xpt_freeze_simq(isp->isp_sim, 1); CAMLOCK_2_ISPLOCK(isp); } else { isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown)", msg); isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; } } static int ispioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct ispsoftc *isp; int retval = ENOTTY; isp = isplist; while (isp) { if (minor(dev) == device_get_unit(isp->isp_dev)) { break; } isp = isp->isp_osinfo.next; } if (isp == NULL) return (ENXIO); switch (cmd) { #ifdef ISP_FW_CRASH_DUMP case ISP_GET_FW_CRASH_DUMP: { u_int16_t *ptr = FCPARAM(isp)->isp_dump_data; size_t sz; retval = 0; if (IS_2200(isp)) sz = QLA2200_RISC_IMAGE_DUMP_SIZE; else sz = QLA2300_RISC_IMAGE_DUMP_SIZE; ISP_LOCK(isp); if (ptr && *ptr) { void *uaddr = *((void **) addr); if (copyout(ptr, uaddr, sz)) { retval = EFAULT; } else { *ptr = 0; } } else { retval = ENXIO; } ISP_UNLOCK(isp); break; } case ISP_FORCE_CRASH_DUMP: ISP_LOCK(isp); isp_freeze_loopdown(isp, "ispioctl(ISP_FORCE_CRASH_DUMP)"); isp_fw_dump(isp); isp_reinit(isp); ISP_UNLOCK(isp); retval = 0; break; #endif case ISP_SDBLEV: { int olddblev = isp->isp_dblev; isp->isp_dblev = *(int *)addr; *(int *)addr = olddblev; retval = 0; break; } case ISP_RESETHBA: ISP_LOCK(isp); isp_reinit(isp); ISP_UNLOCK(isp); retval = 0; break; case ISP_RESCAN: if (IS_FC(isp)) { ISP_LOCK(isp); if (isp_fc_runstate(isp, 5 * 1000000)) { retval = EIO; } else { retval = 0; } ISP_UNLOCK(isp); } break; case ISP_FC_LIP: if (IS_FC(isp)) { ISP_LOCK(isp); if (isp_control(isp, ISPCTL_SEND_LIP, 0)) { retval = EIO; } else { retval = 0; } ISP_UNLOCK(isp); } break; case ISP_FC_GETDINFO: { struct isp_fc_device *ifc = (struct isp_fc_device *) addr; struct lportdb *lp; if (ifc->loopid < 0 || ifc->loopid >= MAX_FC_TARG) { retval = EINVAL; break; } ISP_LOCK(isp); lp = &FCPARAM(isp)->portdb[ifc->loopid]; if (lp->valid) { ifc->loopid = lp->loopid; ifc->portid = lp->portid; ifc->node_wwn = lp->node_wwn; ifc->port_wwn = lp->port_wwn; retval = 0; } else { retval = ENODEV; } ISP_UNLOCK(isp); break; } case ISP_GET_STATS: { isp_stats_t *sp = (isp_stats_t *) addr; MEMZERO(sp, sizeof (*sp)); sp->isp_stat_version = ISP_STATS_VERSION; sp->isp_type = isp->isp_type; sp->isp_revision = isp->isp_revision; ISP_LOCK(isp); sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt; sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus; sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc; sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync; sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt; sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt; sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater; sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater; ISP_UNLOCK(isp); retval = 0; break; } case ISP_CLR_STATS: ISP_LOCK(isp); isp->isp_intcnt = 0; isp->isp_intbogus = 0; isp->isp_intmboxc = 0; isp->isp_intoasync = 0; isp->isp_rsltccmplt = 0; isp->isp_fphccmplt = 0; isp->isp_rscchiwater = 0; isp->isp_fpcchiwater = 0; ISP_UNLOCK(isp); retval = 0; break; case ISP_FC_GETHINFO: { struct isp_hba_device *hba = (struct isp_hba_device *) addr; MEMZERO(hba, sizeof (*hba)); ISP_LOCK(isp); hba->fc_speed = FCPARAM(isp)->isp_gbspeed; hba->fc_scsi_supported = 1; hba->fc_topology = FCPARAM(isp)->isp_topo + 1; hba->fc_loopid = FCPARAM(isp)->isp_loopid; hba->active_node_wwn = FCPARAM(isp)->isp_nodewwn; hba->active_port_wwn = FCPARAM(isp)->isp_portwwn; ISP_UNLOCK(isp); retval = 0; break; } case ISP_GET_FC_PARAM: { struct isp_fc_param *f = (struct isp_fc_param *) addr; if (!IS_FC(isp)) { retval = EINVAL; break; } f->parameter = 0; if (strcmp(f->param_name, "framelength") == 0) { f->parameter = FCPARAM(isp)->isp_maxfrmlen; retval = 0; break; } if (strcmp(f->param_name, "exec_throttle") == 0) { f->parameter = FCPARAM(isp)->isp_execthrottle; retval = 0; break; } if (strcmp(f->param_name, "fullduplex") == 0) { if (FCPARAM(isp)->isp_fwoptions & ICBOPT_FULL_DUPLEX) f->parameter = 1; retval = 0; break; } if (strcmp(f->param_name, "loopid") == 0) { f->parameter = FCPARAM(isp)->isp_loopid; retval = 0; break; } retval = EINVAL; break; } case ISP_SET_FC_PARAM: { struct isp_fc_param *f = (struct isp_fc_param *) addr; u_int32_t param = f->parameter; if (!IS_FC(isp)) { retval = EINVAL; break; } f->parameter = 0; if (strcmp(f->param_name, "framelength") == 0) { if (param != 512 && param != 1024 && param != 1024) { retval = EINVAL; break; } FCPARAM(isp)->isp_maxfrmlen = param; retval = 0; break; } if (strcmp(f->param_name, "exec_throttle") == 0) { if (param < 16 || param > 255) { retval = EINVAL; break; } FCPARAM(isp)->isp_execthrottle = param; retval = 0; break; } if (strcmp(f->param_name, "fullduplex") == 0) { if (param != 0 && param != 1) { retval = EINVAL; break; } if (param) { FCPARAM(isp)->isp_fwoptions |= ICBOPT_FULL_DUPLEX; } else { FCPARAM(isp)->isp_fwoptions &= ~ICBOPT_FULL_DUPLEX; } retval = 0; break; } if (strcmp(f->param_name, "loopid") == 0) { if (param < 0 || param > 125) { retval = EINVAL; break; } FCPARAM(isp)->isp_loopid = param; retval = 0; break; } retval = EINVAL; break; } default: break; } return (retval); } static void isp_intr_enable(void *arg) { struct ispsoftc *isp = arg; if (isp->isp_role != ISP_ROLE_NONE) { ENABLE_INTS(isp); isp->isp_osinfo.intsok = 1; } /* Release our hook so that the boot can continue. */ config_intrhook_disestablish(&isp->isp_osinfo.ehook); } /* * Put the target mode functions here, because some are inlines */ #ifdef ISP_TARGET_MODE static INLINE int is_lun_enabled(struct ispsoftc *, int, lun_id_t); static INLINE int are_any_luns_enabled(struct ispsoftc *, int); static INLINE tstate_t *get_lun_statep(struct ispsoftc *, int, lun_id_t); static INLINE void rls_lun_statep(struct ispsoftc *, tstate_t *); static INLINE int isp_psema_sig_rqe(struct ispsoftc *, int); static INLINE int isp_cv_wait_timed_rqe(struct ispsoftc *, int, int); static INLINE void isp_cv_signal_rqe(struct ispsoftc *, int, int); static INLINE void isp_vsema_rqe(struct ispsoftc *, int); static INLINE atio_private_data_t *isp_get_atpd(struct ispsoftc *, int); static cam_status create_lun_state(struct ispsoftc *, int, struct cam_path *, tstate_t **); static void destroy_lun_state(struct ispsoftc *, tstate_t *); static void isp_en_lun(struct ispsoftc *, union ccb *); static cam_status isp_abort_tgt_ccb(struct ispsoftc *, union ccb *); static timeout_t isp_refire_putback_atio; static void isp_complete_ctio(union ccb *); static void isp_target_putback_atio(union ccb *); static cam_status isp_target_start_ctio(struct ispsoftc *, union ccb *); static int isp_handle_platform_atio(struct ispsoftc *, at_entry_t *); static int isp_handle_platform_atio2(struct ispsoftc *, at2_entry_t *); static int isp_handle_platform_ctio(struct ispsoftc *, void *); static int isp_handle_platform_notify_scsi(struct ispsoftc *, in_entry_t *); static int isp_handle_platform_notify_fc(struct ispsoftc *, in_fcentry_t *); static INLINE int is_lun_enabled(struct ispsoftc *isp, int bus, lun_id_t lun) { tstate_t *tptr; tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; if (tptr == NULL) { return (0); } do { if (tptr->lun == (lun_id_t) lun && tptr->bus == bus) { return (1); } } while ((tptr = tptr->next) != NULL); return (0); } static INLINE int are_any_luns_enabled(struct ispsoftc *isp, int port) { int lo, hi; if (IS_DUALBUS(isp)) { lo = (port * (LUN_HASH_SIZE >> 1)); hi = lo + (LUN_HASH_SIZE >> 1); } else { lo = 0; hi = LUN_HASH_SIZE; } for (lo = 0; lo < hi; lo++) { if (isp->isp_osinfo.lun_hash[lo]) { return (1); } } return (0); } static INLINE tstate_t * get_lun_statep(struct ispsoftc *isp, int bus, lun_id_t lun) { tstate_t *tptr = NULL; if (lun == CAM_LUN_WILDCARD) { if (isp->isp_osinfo.tmflags[bus] & TM_WILDCARD_ENABLED) { tptr = &isp->isp_osinfo.tsdflt[bus]; tptr->hold++; return (tptr); } } else { tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; if (tptr == NULL) { return (NULL); } } do { if (tptr->lun == lun && tptr->bus == bus) { tptr->hold++; return (tptr); } } while ((tptr = tptr->next) != NULL); return (tptr); } static INLINE void rls_lun_statep(struct ispsoftc *isp, tstate_t *tptr) { if (tptr->hold) tptr->hold--; } static INLINE int isp_psema_sig_rqe(struct ispsoftc *isp, int bus) { while (isp->isp_osinfo.tmflags[bus] & TM_BUSY) { isp->isp_osinfo.tmflags[bus] |= TM_WANTED; #ifdef ISP_SMPLOCK if (cv_wait_sig(&isp->isp_osinfo.tgtcv0[bus], &isp->isp_lock)) { return (-1); } #else if (tsleep(&isp->isp_osinfo.tgtcv0[bus], PZERO, "cv_isp", 0)) { return (-1); } #endif isp->isp_osinfo.tmflags[bus] |= TM_BUSY; } return (0); } static INLINE int isp_cv_wait_timed_rqe(struct ispsoftc *isp, int bus, int timo) { #ifdef ISP_SMPLOCK if (cv_timedwait(&isp->isp_osinfo.tgtcv1[bus], &isp->isp_lock, timo)) { return (-1); } #else if (tsleep(&isp->isp_osinfo.tgtcv1[bus], PZERO, "cv_isp1", 0)) { return (-1); } #endif return (0); } static INLINE void isp_cv_signal_rqe(struct ispsoftc *isp, int bus, int status) { isp->isp_osinfo.rstatus[bus] = status; #ifdef ISP_SMPLOCK cv_signal(&isp->isp_osinfo.tgtcv1[bus]); #else wakeup(&isp->isp_osinfo.tgtcv1[bus]); #endif } static INLINE void isp_vsema_rqe(struct ispsoftc *isp, int bus) { if (isp->isp_osinfo.tmflags[bus] & TM_WANTED) { isp->isp_osinfo.tmflags[bus] &= ~TM_WANTED; #ifdef ISP_SMPLOCK cv_signal(&isp->isp_osinfo.tgtcv0[bus]); #else cv_signal(&isp->isp_osinfo.tgtcv0[bus]); #endif } isp->isp_osinfo.tmflags[bus] &= ~TM_BUSY; } static INLINE atio_private_data_t * isp_get_atpd(struct ispsoftc *isp, int tag) { atio_private_data_t *atp; for (atp = isp->isp_osinfo.atpdp; atp < &isp->isp_osinfo.atpdp[ATPDPSIZE]; atp++) { if (atp->tag == tag) return (atp); } return (NULL); } static cam_status create_lun_state(struct ispsoftc *isp, int bus, struct cam_path *path, tstate_t **rslt) { cam_status status; lun_id_t lun; int hfx; tstate_t *tptr, *new; lun = xpt_path_lun_id(path); if (lun < 0) { return (CAM_LUN_INVALID); } if (is_lun_enabled(isp, bus, lun)) { return (CAM_LUN_ALRDY_ENA); } new = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT|M_ZERO); if (new == NULL) { return (CAM_RESRC_UNAVAIL); } status = xpt_create_path(&new->owner, NULL, xpt_path_path_id(path), xpt_path_target_id(path), xpt_path_lun_id(path)); if (status != CAM_REQ_CMP) { free(new, M_DEVBUF); return (status); } new->bus = bus; new->lun = lun; SLIST_INIT(&new->atios); SLIST_INIT(&new->inots); new->hold = 1; hfx = LUN_HASH_FUNC(isp, new->bus, new->lun); tptr = isp->isp_osinfo.lun_hash[hfx]; if (tptr == NULL) { isp->isp_osinfo.lun_hash[hfx] = new; } else { while (tptr->next) tptr = tptr->next; tptr->next = new; } *rslt = new; return (CAM_REQ_CMP); } static INLINE void destroy_lun_state(struct ispsoftc *isp, tstate_t *tptr) { int hfx; tstate_t *lw, *pw; hfx = LUN_HASH_FUNC(isp, tptr->bus, tptr->lun); if (tptr->hold) { return; } pw = isp->isp_osinfo.lun_hash[hfx]; if (pw == NULL) { return; } else if (pw->lun == tptr->lun && pw->bus == tptr->bus) { isp->isp_osinfo.lun_hash[hfx] = pw->next; } else { lw = pw; pw = lw->next; while (pw) { if (pw->lun == tptr->lun && pw->bus == tptr->bus) { lw->next = pw->next; break; } lw = pw; pw = pw->next; } if (pw == NULL) { return; } } free(tptr, M_DEVBUF); } /* * we enter with our locks held. */ static void isp_en_lun(struct ispsoftc *isp, union ccb *ccb) { const char lfmt[] = "Lun now %sabled for target mode on channel %d"; struct ccb_en_lun *cel = &ccb->cel; tstate_t *tptr; u_int16_t rstat; int bus, cmd, av, wildcard; lun_id_t lun; target_id_t tgt; bus = XS_CHANNEL(ccb) & 0x1; tgt = ccb->ccb_h.target_id; lun = ccb->ccb_h.target_lun; /* * Do some sanity checking first. */ if ((lun != CAM_LUN_WILDCARD) && (lun < 0 || lun >= (lun_id_t) isp->isp_maxluns)) { ccb->ccb_h.status = CAM_LUN_INVALID; return; } if (IS_SCSI(isp)) { sdparam *sdp = isp->isp_param; sdp += bus; if (tgt != CAM_TARGET_WILDCARD && tgt != sdp->isp_initiator_id) { ccb->ccb_h.status = CAM_TID_INVALID; return; } } else { if (tgt != CAM_TARGET_WILDCARD && tgt != FCPARAM(isp)->isp_iid) { ccb->ccb_h.status = CAM_TID_INVALID; return; } /* * This is as a good a place as any to check f/w capabilities. */ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_TMODE) == 0) { isp_prt(isp, ISP_LOGERR, "firmware does not support target mode"); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; return; } /* * XXX: We *could* handle non-SCCLUN f/w, but we'd have to * XXX: dorks with our already fragile enable/disable code. */ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { isp_prt(isp, ISP_LOGERR, "firmware not SCCLUN capable"); } } if (tgt == CAM_TARGET_WILDCARD) { if (lun == CAM_LUN_WILDCARD) { wildcard = 1; } else { ccb->ccb_h.status = CAM_LUN_INVALID; return; } } else { wildcard = 0; } /* * Next check to see whether this is a target/lun wildcard action. * * If so, we know that we can accept commands for luns that haven't * been enabled yet and send them upstream. Otherwise, we have to * handle them locally (if we see them at all). */ if (wildcard) { tptr = &isp->isp_osinfo.tsdflt[bus]; if (cel->enable) { if (isp->isp_osinfo.tmflags[bus] & TM_WILDCARD_ENABLED) { ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; return; } ccb->ccb_h.status = xpt_create_path(&tptr->owner, NULL, xpt_path_path_id(ccb->ccb_h.path), xpt_path_target_id(ccb->ccb_h.path), xpt_path_lun_id(ccb->ccb_h.path)); if (ccb->ccb_h.status != CAM_REQ_CMP) { return; } SLIST_INIT(&tptr->atios); SLIST_INIT(&tptr->inots); isp->isp_osinfo.tmflags[bus] |= TM_WILDCARD_ENABLED; } else { if ((isp->isp_osinfo.tmflags[bus] & TM_WILDCARD_ENABLED) == 0) { ccb->ccb_h.status = CAM_REQ_CMP; return; } if (tptr->hold) { ccb->ccb_h.status = CAM_SCSI_BUSY; return; } xpt_free_path(tptr->owner); isp->isp_osinfo.tmflags[bus] &= ~TM_WILDCARD_ENABLED; } } /* * Now check to see whether this bus needs to be * enabled/disabled with respect to target mode. */ av = bus << 31; if (cel->enable && !(isp->isp_osinfo.tmflags[bus] & TM_TMODE_ENABLED)) { av |= ENABLE_TARGET_FLAG; av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); if (av) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; if (wildcard) { isp->isp_osinfo.tmflags[bus] &= ~TM_WILDCARD_ENABLED; xpt_free_path(tptr->owner); } return; } isp->isp_osinfo.tmflags[bus] |= TM_TMODE_ENABLED; isp_prt(isp, ISP_LOGINFO, "Target Mode enabled on channel %d", bus); } else if (cel->enable == 0 && (isp->isp_osinfo.tmflags[bus] & TM_TMODE_ENABLED) && wildcard) { if (are_any_luns_enabled(isp, bus)) { ccb->ccb_h.status = CAM_SCSI_BUSY; return; } av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); if (av) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; return; } isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED; isp_prt(isp, ISP_LOGINFO, "Target Mode disabled on channel %d", bus); } if (wildcard) { ccb->ccb_h.status = CAM_REQ_CMP; return; } if (cel->enable) { ccb->ccb_h.status = create_lun_state(isp, bus, ccb->ccb_h.path, &tptr); if (ccb->ccb_h.status != CAM_REQ_CMP) { return; } } else { tptr = get_lun_statep(isp, bus, lun); if (tptr == NULL) { ccb->ccb_h.status = CAM_LUN_INVALID; return; } } if (isp_psema_sig_rqe(isp, bus)) { rls_lun_statep(isp, tptr); if (cel->enable) destroy_lun_state(isp, tptr); ccb->ccb_h.status = CAM_REQ_CMP_ERR; return; } if (cel->enable) { u_int32_t seq = isp->isp_osinfo.rollinfo++; int c, n, ulun = lun; cmd = RQSTYPE_ENABLE_LUN; c = DFLT_CMND_CNT; n = DFLT_INOT_CNT; if (IS_FC(isp) && lun != 0) { cmd = RQSTYPE_MODIFY_LUN; n = 0; /* * For SCC firmware, we only deal with setting * (enabling or modifying) lun 0. */ ulun = 0; } rstat = LUN_ERR; if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "isp_lun_cmd failed"); goto out; } if (isp_cv_wait_timed_rqe(isp, bus, 30 * hz)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "wait for ENABLE/MODIFY LUN timed out"); goto out; } rstat = isp->isp_osinfo.rstatus[bus]; if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "ENABLE/MODIFY LUN returned 0x%x", rstat); goto out; } } else { int c, n, ulun = lun; u_int32_t seq; rstat = LUN_ERR; seq = isp->isp_osinfo.rollinfo++; cmd = -RQSTYPE_MODIFY_LUN; c = DFLT_CMND_CNT; n = DFLT_INOT_CNT; if (IS_FC(isp) && lun != 0) { n = 0; /* * For SCC firmware, we only deal with setting * (enabling or modifying) lun 0. */ ulun = 0; } if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed"); goto out; } if (isp_cv_wait_timed_rqe(isp, bus, 30 * hz)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "wait for MODIFY LUN timed out"); goto out; } rstat = isp->isp_osinfo.rstatus[bus]; if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "MODIFY LUN returned 0x%x", rstat); goto out; } if (IS_FC(isp) && lun) { goto out; } seq = isp->isp_osinfo.rollinfo++; rstat = LUN_ERR; cmd = -RQSTYPE_ENABLE_LUN; if (isp_lun_cmd(isp, cmd, bus, tgt, lun, 0, 0, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed"); goto out; } if (isp_cv_wait_timed_rqe(isp, bus, 30 * hz)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "wait for DISABLE LUN timed out"); goto out; } rstat = isp->isp_osinfo.rstatus[bus]; if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "DISABLE LUN returned 0x%x", rstat); goto out; } if (are_any_luns_enabled(isp, bus) == 0) { av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); if (av) { isp_prt(isp, ISP_LOGWARN, "disable target mode on channel %d failed", bus); goto out; } isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED; xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGINFO, "Target Mode disabled on channel %d", bus); } } out: isp_vsema_rqe(isp, bus); if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "lun %sable failed", (cel->enable) ? "en" : "dis"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; rls_lun_statep(isp, tptr); if (cel->enable) destroy_lun_state(isp, tptr); } else { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGINFO, lfmt, (cel->enable) ? "en" : "dis", bus); rls_lun_statep(isp, tptr); if (cel->enable == 0) { destroy_lun_state(isp, tptr); } ccb->ccb_h.status = CAM_REQ_CMP; } } static cam_status isp_abort_tgt_ccb(struct ispsoftc *isp, union ccb *ccb) { tstate_t *tptr; struct ccb_hdr_slist *lp; struct ccb_hdr *curelm; int found; union ccb *accb = ccb->cab.abort_ccb; if (accb->ccb_h.target_id != CAM_TARGET_WILDCARD) { if (IS_FC(isp) && (accb->ccb_h.target_id != ((fcparam *) isp->isp_param)->isp_loopid)) { return (CAM_PATH_INVALID); } else if (IS_SCSI(isp) && (accb->ccb_h.target_id != ((sdparam *) isp->isp_param)->isp_initiator_id)) { return (CAM_PATH_INVALID); } } tptr = get_lun_statep(isp, XS_CHANNEL(ccb), accb->ccb_h.target_lun); if (tptr == NULL) { return (CAM_PATH_INVALID); } if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { lp = &tptr->atios; } else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) { lp = &tptr->inots; } else { rls_lun_statep(isp, tptr); return (CAM_UA_ABORT); } curelm = SLIST_FIRST(lp); found = 0; if (curelm == &accb->ccb_h) { found = 1; SLIST_REMOVE_HEAD(lp, sim_links.sle); } else { while(curelm != NULL) { struct ccb_hdr *nextelm; nextelm = SLIST_NEXT(curelm, sim_links.sle); if (nextelm == &accb->ccb_h) { found = 1; SLIST_NEXT(curelm, sim_links.sle) = SLIST_NEXT(nextelm, sim_links.sle); break; } curelm = nextelm; } } rls_lun_statep(isp, tptr); if (found) { accb->ccb_h.status = CAM_REQ_ABORTED; return (CAM_REQ_CMP); } return(CAM_PATH_INVALID); } static cam_status isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) { void *qe; struct ccb_scsiio *cso = &ccb->csio; u_int16_t *hp, save_handle; u_int16_t nxti, optr; u_int8_t local[QENTRY_LEN]; if (isp_getrqentry(isp, &nxti, &optr, &qe)) { xpt_print_path(ccb->ccb_h.path); printf("Request Queue Overflow in isp_target_start_ctio\n"); return (CAM_RESRC_UNAVAIL); } bzero(local, QENTRY_LEN); /* * We're either moving data or completing a command here. */ if (IS_FC(isp)) { atio_private_data_t *atp; ct2_entry_t *cto = (ct2_entry_t *) local; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; cto->ct_header.rqs_entry_count = 1; cto->ct_iid = cso->init_id; if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { cto->ct_lun = ccb->ccb_h.target_lun; } atp = isp_get_atpd(isp, cso->tag_id); if (atp == NULL) { isp_prt(isp, ISP_LOGERR, "cannot find private data adjunct for tag %x", cso->tag_id); return (-1); } cto->ct_rxid = cso->tag_id; if (cso->dxfer_len == 0) { cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA; if (ccb->ccb_h.flags & CAM_SEND_STATUS) { cto->ct_flags |= CT2_SENDSTATUS; cto->rsp.m1.ct_scsi_status = cso->scsi_status; cto->ct_resid = atp->orig_datalen - atp->bytes_xfered; if (cto->ct_resid < 0) { cto->rsp.m1.ct_scsi_status |= CT2_DATA_OVER; } else if (cto->ct_resid > 0) { cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER; } } if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { int m = min(cso->sense_len, MAXRESPLEN); bcopy(&cso->sense_data, cto->rsp.m1.ct_resp, m); cto->rsp.m1.ct_senselen = m; cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID; } } else { cto->ct_flags |= CT2_FLAG_MODE0; if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT2_DATA_IN; } else { cto->ct_flags |= CT2_DATA_OUT; } cto->ct_reloff = atp->bytes_xfered; if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { cto->ct_flags |= CT2_SENDSTATUS; cto->rsp.m0.ct_scsi_status = cso->scsi_status; cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len); if (cto->ct_resid < 0) { cto->rsp.m0.ct_scsi_status |= CT2_DATA_OVER; } else if (cto->ct_resid > 0) { cto->rsp.m0.ct_scsi_status |= CT2_DATA_UNDER; } } else { atp->last_xframt = cso->dxfer_len; } /* * If we're sending data and status back together, * we can't also send back sense data as well. */ ccb->ccb_h.flags &= ~CAM_SEND_SENSE; } if (cto->ct_flags & CT2_SENDSTATUS) { isp_prt(isp, ISP_LOGTDEBUG0, "CTIO2[%x] STATUS %x origd %u curd %u resid %u", cto->ct_rxid, cso->scsi_status, atp->orig_datalen, cso->dxfer_len, cto->ct_resid); cto->ct_flags |= CT2_CCINCR; atp->state = ATPD_STATE_LAST_CTIO; } else atp->state = ATPD_STATE_CTIO; cto->ct_timeout = 10; hp = &cto->ct_syshandle; } else { ct_entry_t *cto = (ct_entry_t *) local; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; cto->ct_header.rqs_entry_count = 1; cto->ct_iid = cso->init_id; cto->ct_iid |= XS_CHANNEL(ccb) << 7; cto->ct_tgt = ccb->ccb_h.target_id; cto->ct_lun = ccb->ccb_h.target_lun; cto->ct_fwhandle = AT_GET_HANDLE(cso->tag_id); if (AT_HAS_TAG(cso->tag_id)) { cto->ct_tag_val = (u_int8_t) AT_GET_TAG(cso->tag_id); cto->ct_flags |= CT_TQAE; } if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { cto->ct_flags |= CT_NODISC; } if (cso->dxfer_len == 0) { cto->ct_flags |= CT_NO_DATA; } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT_DATA_IN; } else { cto->ct_flags |= CT_DATA_OUT; } if (ccb->ccb_h.flags & CAM_SEND_STATUS) { cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR; cto->ct_scsi_status = cso->scsi_status; cto->ct_resid = cso->resid; isp_prt(isp, ISP_LOGTDEBUG0, "CTIO[%x] SCSI STATUS 0x%x resid %d tag_id %x", cto->ct_fwhandle, cso->scsi_status, cso->resid, cso->tag_id); } ccb->ccb_h.flags &= ~CAM_SEND_SENSE; cto->ct_timeout = 10; hp = &cto->ct_syshandle; } if (isp_save_xs(isp, (XS_T *)ccb, hp)) { xpt_print_path(ccb->ccb_h.path); printf("No XFLIST pointers for isp_target_start_ctio\n"); return (CAM_RESRC_UNAVAIL); } /* * Call the dma setup routines for this entry (and any subsequent * CTIOs) if there's data to move, and then tell the f/w it's got * new things to play with. As with isp_start's usage of DMA setup, * any swizzling is done in the machine dependent layer. Because * of this, we put the request onto the queue area first in native * format. */ save_handle = *hp; switch (ISP_DMASETUP(isp, cso, (ispreq_t *) local, &nxti, optr)) { case CMD_QUEUED: ISP_ADD_REQUEST(isp, nxti); return (CAM_REQ_INPROG); case CMD_EAGAIN: ccb->ccb_h.status = CAM_RESRC_UNAVAIL; isp_destroy_handle(isp, save_handle); return (CAM_RESRC_UNAVAIL); default: isp_destroy_handle(isp, save_handle); return (XS_ERR(ccb)); } } static void isp_refire_putback_atio(void *arg) { int s = splcam(); isp_target_putback_atio(arg); splx(s); } static void isp_target_putback_atio(union ccb *ccb) { struct ispsoftc *isp; struct ccb_scsiio *cso; u_int16_t nxti, optr; void *qe; isp = XS_ISP(ccb); if (isp_getrqentry(isp, &nxti, &optr, &qe)) { (void) timeout(isp_refire_putback_atio, ccb, 10); isp_prt(isp, ISP_LOGWARN, "isp_target_putback_atio: Request Queue Overflow"); return; } bzero(qe, QENTRY_LEN); cso = &ccb->csio; if (IS_FC(isp)) { at2_entry_t local, *at = &local; MEMZERO(at, sizeof (at2_entry_t)); at->at_header.rqs_entry_type = RQSTYPE_ATIO2; at->at_header.rqs_entry_count = 1; if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) { at->at_scclun = (uint16_t) ccb->ccb_h.target_lun; } else { at->at_lun = (uint8_t) ccb->ccb_h.target_lun; } at->at_status = CT_OK; at->at_rxid = cso->tag_id; at->at_iid = cso->ccb_h.target_id; isp_put_atio2(isp, at, qe); } else { at_entry_t local, *at = &local; MEMZERO(at, sizeof (at_entry_t)); at->at_header.rqs_entry_type = RQSTYPE_ATIO; at->at_header.rqs_entry_count = 1; at->at_iid = cso->init_id; at->at_iid |= XS_CHANNEL(ccb) << 7; at->at_tgt = cso->ccb_h.target_id; at->at_lun = cso->ccb_h.target_lun; at->at_status = CT_OK; at->at_tag_val = AT_GET_TAG(cso->tag_id); at->at_handle = AT_GET_HANDLE(cso->tag_id); isp_put_atio(isp, at, qe); } ISP_TDQE(isp, "isp_target_putback_atio", (int) optr, qe); ISP_ADD_REQUEST(isp, nxti); isp_complete_ctio(ccb); } static void isp_complete_ctio(union ccb *ccb) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; xpt_done(ccb); } /* * Handle ATIO stuff that the generic code can't. * This means handling CDBs. */ static int isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) { tstate_t *tptr; int status, bus, iswildcard; struct ccb_accept_tio *atiop; /* * The firmware status (except for the QLTM_SVALID bit) * indicates why this ATIO was sent to us. * * If QLTM_SVALID is set, the firware has recommended Sense Data. * * If the DISCONNECTS DISABLED bit is set in the flags field, * we're still connected on the SCSI bus. */ status = aep->at_status; if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { /* * Bus Phase Sequence error. We should have sense data * suggested by the f/w. I'm not sure quite yet what * to do about this for CAM. */ isp_prt(isp, ISP_LOGWARN, "PHASE ERROR"); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } if ((status & ~QLTM_SVALID) != AT_CDB) { isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", status); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } bus = GET_BUS_VAL(aep->at_iid); tptr = get_lun_statep(isp, bus, aep->at_lun); if (tptr == NULL) { tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); iswildcard = 1; } else { iswildcard = 0; } if (tptr == NULL) { /* * Because we can't autofeed sense data back with * a command for parallel SCSI, we can't give back * a CHECK CONDITION. We'll give back a BUSY status * instead. This works out okay because the only * time we should, in fact, get this, is in the * case that somebody configured us without the * blackhole driver, so they get what they deserve. */ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); if (atiop == NULL) { /* * Because we can't autofeed sense data back with * a command for parallel SCSI, we can't give back * a CHECK CONDITION. We'll give back a QUEUE FULL status * instead. This works out okay because the only time we * should, in fact, get this, is in the case that we've * run out of ATIOS. */ xpt_print_path(tptr->owner); isp_prt(isp, ISP_LOGWARN, "no ATIOS for lun %d from initiator %d on channel %d", aep->at_lun, GET_IID_VAL(aep->at_iid), bus); if (aep->at_flags & AT_TQAE) isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); else isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); rls_lun_statep(isp, tptr); return (0); } SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); if (iswildcard) { atiop->ccb_h.target_id = aep->at_tgt; atiop->ccb_h.target_lun = aep->at_lun; } if (aep->at_flags & AT_NODISC) { atiop->ccb_h.flags = CAM_DIS_DISCONNECT; } else { atiop->ccb_h.flags = 0; } if (status & QLTM_SVALID) { size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data)); atiop->sense_len = amt; MEMCPY(&atiop->sense_data, aep->at_sense, amt); } else { atiop->sense_len = 0; } atiop->init_id = GET_IID_VAL(aep->at_iid); atiop->cdb_len = aep->at_cdblen; MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen); atiop->ccb_h.status = CAM_CDB_RECVD; /* * Construct a tag 'id' based upon tag value (which may be 0..255) * and the handle (which we have to preserve). */ AT_MAKE_TAGID(atiop->tag_id, aep); if (aep->at_flags & AT_TQAE) { atiop->tag_action = aep->at_tag_type; atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; } xpt_done((union ccb*)atiop); isp_prt(isp, ISP_LOGTDEBUG0, "ATIO[%x] CDB=0x%x bus %d iid%d->lun%d tag 0x%x ttype 0x%x %s", aep->at_handle, aep->at_cdb[0] & 0xff, GET_BUS_VAL(aep->at_iid), GET_IID_VAL(aep->at_iid), aep->at_lun, aep->at_tag_val & 0xff, aep->at_tag_type, (aep->at_flags & AT_NODISC)? "nondisc" : "disconnecting"); rls_lun_statep(isp, tptr); return (0); } static int isp_handle_platform_atio2(struct ispsoftc *isp, at2_entry_t *aep) { lun_id_t lun; tstate_t *tptr; struct ccb_accept_tio *atiop; atio_private_data_t *atp; /* * The firmware status (except for the QLTM_SVALID bit) * indicates why this ATIO was sent to us. * * If QLTM_SVALID is set, the firware has recommended Sense Data. */ if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) { isp_prt(isp, ISP_LOGWARN, "bogus atio (0x%x) leaked to platform", aep->at_status); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) { lun = aep->at_scclun; } else { lun = aep->at_lun; } tptr = get_lun_statep(isp, 0, lun); if (tptr == NULL) { isp_prt(isp, ISP_LOGWARN, "no state pointer for lun %d", lun); tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); } if (tptr == NULL) { /* * What we'd like to know is whether or not we have a listener * upstream that really hasn't configured yet. If we do, then * we can give a more sensible reply here. If not, then we can * reject this out of hand. * * Choices for what to send were * * Not Ready, Unit Not Self-Configured Yet * (0x2,0x3e,0x00) * * for the former and * * Illegal Request, Logical Unit Not Supported * (0x5,0x25,0x00) * * for the latter. * * We used to decide whether there was at least one listener * based upon whether the black hole driver was configured. * However, recent config(8) changes have made this hard to do * at this time. * */ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } atp = isp_get_atpd(isp, 0); atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); if (atiop == NULL || atp == NULL) { /* * Because we can't autofeed sense data back with * a command for parallel SCSI, we can't give back * a CHECK CONDITION. We'll give back a QUEUE FULL status * instead. This works out okay because the only time we * should, in fact, get this, is in the case that we've * run out of ATIOS. */ xpt_print_path(tptr->owner); isp_prt(isp, ISP_LOGWARN, "no %s for lun %d from initiator %d", (atp == NULL && atiop == NULL)? "ATIO2s *or* ATPS" : ((atp == NULL)? "ATPs" : "ATIO2s"), lun, aep->at_iid); rls_lun_statep(isp, tptr); isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); return (0); } atp->state = ATPD_STATE_ATIO; SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); tptr->atio_count--; isp_prt(isp, ISP_LOGTDEBUG0, "Take FREE ATIO2 lun %d, count now %d", lun, tptr->atio_count); if (tptr == &isp->isp_osinfo.tsdflt[0]) { atiop->ccb_h.target_id = ((fcparam *)isp->isp_param)->isp_loopid; atiop->ccb_h.target_lun = lun; } /* * We don't get 'suggested' sense data as we do with SCSI cards. */ atiop->sense_len = 0; atiop->init_id = aep->at_iid; atiop->cdb_len = ATIO2_CDBLEN; MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); atiop->ccb_h.status = CAM_CDB_RECVD; atiop->tag_id = aep->at_rxid; switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) { case ATIO2_TC_ATTR_SIMPLEQ: atiop->tag_action = MSG_SIMPLE_Q_TAG; break; case ATIO2_TC_ATTR_HEADOFQ: atiop->tag_action = MSG_HEAD_OF_Q_TAG; break; case ATIO2_TC_ATTR_ORDERED: atiop->tag_action = MSG_ORDERED_Q_TAG; break; case ATIO2_TC_ATTR_ACAQ: /* ?? */ case ATIO2_TC_ATTR_UNTAGGED: default: atiop->tag_action = 0; break; } atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; atp->tag = atiop->tag_id; atp->lun = lun; atp->orig_datalen = aep->at_datalen; atp->last_xframt = 0; atp->bytes_xfered = 0; atp->state = ATPD_STATE_CAM; xpt_done((union ccb*)atiop); isp_prt(isp, ISP_LOGTDEBUG0, "ATIO2[%x] CDB=0x%x iid%d->lun%d tattr 0x%x datalen %u", aep->at_rxid, aep->at_cdb[0] & 0xff, aep->at_iid, lun, aep->at_taskflags, aep->at_datalen); rls_lun_statep(isp, tptr); return (0); } static int isp_handle_platform_ctio(struct ispsoftc *isp, void *arg) { union ccb *ccb; int sentstatus, ok, notify_cam, resid = 0; u_int16_t tval; /* * CTIO and CTIO2 are close enough.... */ ccb = (union ccb *) isp_find_xs(isp, ((ct_entry_t *)arg)->ct_syshandle); KASSERT((ccb != NULL), ("null ccb in isp_handle_platform_ctio")); isp_destroy_handle(isp, ((ct_entry_t *)arg)->ct_syshandle); if (IS_FC(isp)) { ct2_entry_t *ct = arg; atio_private_data_t *atp = isp_get_atpd(isp, ct->ct_rxid); if (atp == NULL) { isp_prt(isp, ISP_LOGERR, "cannot find adjunct for %x after I/O", ct->ct_rxid); return (0); } sentstatus = ct->ct_flags & CT2_SENDSTATUS; ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { ccb->ccb_h.status |= CAM_SENT_SENSE; } notify_cam = ct->ct_header.rqs_seqno & 0x1; if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { resid = ct->ct_resid; atp->bytes_xfered += (atp->last_xframt - resid); atp->last_xframt = 0; } if (sentstatus || !ok) { atp->tag = 0; } isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s", ct->ct_rxid, ct->ct_status, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID"); tval = ct->ct_rxid; /* XXX: should really come after isp_complete_ctio */ atp->state = ATPD_STATE_PDON; } else { ct_entry_t *ct = arg; sentstatus = ct->ct_flags & CT_SENDSTATUS; ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; /* * We *ought* to be able to get back to the original ATIO * here, but for some reason this gets lost. It's just as * well because it's squirrelled away as part of periph * private data. * * We can live without it as long as we continue to use * the auto-replenish feature for CTIOs. */ notify_cam = ct->ct_header.rqs_seqno & 0x1; if (ct->ct_status & QLTM_SVALID) { char *sp = (char *)ct; sp += CTIO_SENSE_OFFSET; ccb->csio.sense_len = min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN); MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { resid = ct->ct_resid; } isp_prt(isp, ISP_LOGTDEBUG0, "CTIO[%x] tag %x iid %d lun %d sts %x flg %x resid %d %s", ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun, ct->ct_status, ct->ct_flags, resid, sentstatus? "FIN" : "MID"); tval = ct->ct_fwhandle; } ccb->csio.resid += resid; /* * We're here either because intermediate data transfers are done * and/or the final status CTIO (which may have joined with a * Data Transfer) is done. * * In any case, for this platform, the upper layers figure out * what to do next, so all we do here is collect status and * pass information along. Any DMA handles have already been * freed. */ if (notify_cam == 0) { isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval); return (0); } isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done", (sentstatus)? " FINAL " : "MIDTERM ", tval); if (!ok) { isp_target_putback_atio(ccb); } else { isp_complete_ctio(ccb); } return (0); } static int isp_handle_platform_notify_scsi(struct ispsoftc *isp, in_entry_t *inp) { return (0); /* XXXX */ } static int isp_handle_platform_notify_fc(struct ispsoftc *isp, in_fcentry_t *inp) { switch (inp->in_status) { case IN_PORT_LOGOUT: isp_prt(isp, ISP_LOGWARN, "port logout of iid %d", inp->in_iid); break; case IN_PORT_CHANGED: isp_prt(isp, ISP_LOGWARN, "port changed for iid %d", inp->in_iid); break; case IN_GLOBAL_LOGO: isp_prt(isp, ISP_LOGINFO, "all ports logged out"); break; case IN_ABORT_TASK: { atio_private_data_t *atp = isp_get_atpd(isp, inp->in_seqid); struct ccb_immed_notify *inot = NULL; if (atp) { tstate_t *tptr = get_lun_statep(isp, 0, atp->lun); if (tptr) { inot = (struct ccb_immed_notify *) SLIST_FIRST(&tptr->inots); if (inot) { SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle); } } isp_prt(isp, ISP_LOGWARN, "abort task RX_ID %x IID %d state %d", inp->in_seqid, inp->in_iid, atp->state); } else { isp_prt(isp, ISP_LOGWARN, "abort task RX_ID %x from iid %d, state unknown", inp->in_seqid, inp->in_iid); } if (inot) { inot->initiator_id = inp->in_iid; inot->sense_len = 0; inot->message_args[0] = MSG_ABORT_TAG; inot->message_args[1] = inp->in_seqid & 0xff; inot->message_args[2] = (inp->in_seqid >> 8) & 0xff; inot->ccb_h.status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN; xpt_done((union ccb *)inot); } break; } default: break; } return (0); } #endif static void isp_cam_async(void *cbarg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_sim *sim; struct ispsoftc *isp; sim = (struct cam_sim *)cbarg; isp = (struct ispsoftc *) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: if (IS_SCSI(isp)) { u_int16_t oflags, nflags; sdparam *sdp = isp->isp_param; int tgt; tgt = xpt_path_target_id(path); if (tgt >= 0) { sdp += cam_sim_bus(sim); ISP_LOCK(isp); nflags = sdp->isp_devparam[tgt].nvrm_flags; #ifndef ISP_TARGET_MODE nflags &= DPARM_SAFE_DFLT; if (isp->isp_loaded_fw) { nflags |= DPARM_NARROW | DPARM_ASYNC; } #else nflags = DPARM_DEFAULT; #endif oflags = sdp->isp_devparam[tgt].goal_flags; sdp->isp_devparam[tgt].goal_flags = nflags; sdp->isp_devparam[tgt].dev_update = 1; isp->isp_update |= (1 << cam_sim_bus(sim)); (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL); sdp->isp_devparam[tgt].goal_flags = oflags; ISP_UNLOCK(isp); } } break; default: isp_prt(isp, ISP_LOGWARN, "isp_cam_async: Code 0x%x", code); break; } } static void isp_poll(struct cam_sim *sim) { struct ispsoftc *isp = cam_sim_softc(sim); u_int16_t isr, sema, mbox; ISP_LOCK(isp); if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { isp_intr(isp, isr, sema, mbox); } ISP_UNLOCK(isp); } static void isp_watchdog(void *arg) { XS_T *xs = arg; struct ispsoftc *isp = XS_ISP(xs); u_int32_t handle; int iok; /* * We've decided this command is dead. Make sure we're not trying * to kill a command that's already dead by getting it's handle and * and seeing whether it's still alive. */ ISP_LOCK(isp); iok = isp->isp_osinfo.intsok; isp->isp_osinfo.intsok = 0; handle = isp_find_handle(isp, xs); if (handle) { u_int16_t isr, sema, mbox; if (XS_CMD_DONE_P(xs)) { isp_prt(isp, ISP_LOGDEBUG1, "watchdog found done cmd (handle 0x%x)", handle); ISP_UNLOCK(isp); return; } if (XS_CMD_WDOG_P(xs)) { isp_prt(isp, ISP_LOGDEBUG2, "recursive watchdog (handle 0x%x)", handle); ISP_UNLOCK(isp); return; } XS_CMD_S_WDOG(xs); if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { isp_intr(isp, isr, sema, mbox); } if (XS_CMD_DONE_P(xs)) { isp_prt(isp, ISP_LOGDEBUG2, "watchdog cleanup for handle 0x%x", handle); xpt_done((union ccb *) xs); } else if (XS_CMD_GRACE_P(xs)) { /* * Make sure the command is *really* dead before we * release the handle (and DMA resources) for reuse. */ (void) isp_control(isp, ISPCTL_ABORT_CMD, arg); /* * After this point, the comamnd is really dead. */ if (XS_XFRLEN(xs)) { ISP_DMAFREE(isp, xs, handle); } isp_destroy_handle(isp, handle); xpt_print_path(xs->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "watchdog timeout for handle 0x%x", handle); XS_SETERR(xs, CAM_CMD_TIMEOUT); XS_CMD_C_WDOG(xs); isp_done(xs); } else { u_int16_t nxti, optr; ispreq_t local, *mp= &local, *qe; XS_CMD_C_WDOG(xs); xs->ccb_h.timeout_ch = timeout(isp_watchdog, xs, hz); if (isp_getrqentry(isp, &nxti, &optr, (void **) &qe)) { ISP_UNLOCK(isp); return; } XS_CMD_S_GRACE(xs); MEMZERO((void *) mp, sizeof (*mp)); mp->req_header.rqs_entry_count = 1; mp->req_header.rqs_entry_type = RQSTYPE_MARKER; mp->req_modifier = SYNC_ALL; mp->req_target = XS_CHANNEL(xs) << 7; isp_put_request(isp, mp, qe); ISP_ADD_REQUEST(isp, nxti); } } else { isp_prt(isp, ISP_LOGDEBUG2, "watchdog with no command"); } isp->isp_osinfo.intsok = iok; ISP_UNLOCK(isp); } static void isp_kthread(void *arg) { struct ispsoftc *isp = arg; #ifdef ISP_SMPLOCK mtx_lock(&isp->isp_lock); #else mtx_lock(&Giant); #endif /* * The first loop is for our usage where we have yet to have * gotten good fibre channel state. */ for (;;) { int wasfrozen; isp_prt(isp, ISP_LOGDEBUG0, "kthread: checking FC state"); while (isp_fc_runstate(isp, 2 * 1000000) != 0) { isp_prt(isp, ISP_LOGDEBUG0, "kthread: FC state ungood"); if (FCPARAM(isp)->isp_fwstate != FW_READY || FCPARAM(isp)->isp_loopstate < LOOP_PDB_RCVD) { if (FCPARAM(isp)->loop_seen_once == 0 || isp->isp_osinfo.ktmature == 0) { break; } } #ifdef ISP_SMPLOCK msleep(isp_kthread, &isp->isp_lock, PRIBIO, "isp_fcthrd", hz); #else (void) tsleep(isp_kthread, PRIBIO, "isp_fcthrd", hz); #endif } /* * Even if we didn't get good loop state we may be * unfreezing the SIMQ so that we can kill off * commands (if we've never seen loop before, for example). */ isp->isp_osinfo.ktmature = 1; wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN; isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN; if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) { isp_prt(isp, ISP_LOGDEBUG0, "kthread: releasing simq"); ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); CAMLOCK_2_ISPLOCK(isp); } isp_prt(isp, ISP_LOGDEBUG0, "kthread: waiting until called"); #ifdef ISP_SMPLOCK cv_wait(&isp->isp_osinfo.kthread_cv, &isp->isp_lock); #else (void) tsleep(&isp->isp_osinfo.kthread_cv, PRIBIO, "fc_cv", 0); #endif } } static void isp_action(struct cam_sim *sim, union ccb *ccb) { int bus, tgt, error; struct ispsoftc *isp; struct ccb_trans_settings *cts; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n")); isp = (struct ispsoftc *)cam_sim_softc(sim); ccb->ccb_h.sim_priv.entries[0].field = 0; ccb->ccb_h.sim_priv.entries[1].ptr = isp; if (isp->isp_state != ISP_RUNSTATE && ccb->ccb_h.func_code == XPT_SCSI_IO) { CAMLOCK_2_ISPLOCK(isp); isp_init(isp); if (isp->isp_state != ISP_INITSTATE) { ISP_UNLOCK(isp); /* * Lie. Say it was a selection timeout. */ ccb->ccb_h.status = CAM_SEL_TIMEOUT | CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); xpt_done(ccb); return; } isp->isp_state = ISP_RUNSTATE; ISPLOCK_2_CAMLOCK(isp); } isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ /* * Do a couple of preliminary checks... */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } #ifdef DIAGNOSTIC if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) { ccb->ccb_h.status = CAM_PATH_INVALID; } else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) { ccb->ccb_h.status = CAM_PATH_INVALID; } if (ccb->ccb_h.status == CAM_PATH_INVALID) { isp_prt(isp, ISP_LOGERR, "invalid tgt/lun (%d.%d) in XPT_SCSI_IO", ccb->ccb_h.target_id, ccb->ccb_h.target_lun); xpt_done(ccb); break; } #endif ((struct ccb_scsiio *) ccb)->scsi_status = SCSI_STATUS_OK; CAMLOCK_2_ISPLOCK(isp); error = isp_start((XS_T *) ccb); switch (error) { case CMD_QUEUED: ccb->ccb_h.status |= CAM_SIM_QUEUED; if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { u_int64_t ticks = (u_int64_t) hz; if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) ticks = 60 * 1000 * ticks; else ticks = ccb->ccb_h.timeout * hz; ticks = ((ticks + 999) / 1000) + hz + hz; if (ticks >= 0x80000000) { isp_prt(isp, ISP_LOGERR, "timeout overflow"); ticks = 0x7fffffff; } ccb->ccb_h.timeout_ch = timeout(isp_watchdog, (caddr_t)ccb, (int)ticks); } else { callout_handle_init(&ccb->ccb_h.timeout_ch); } ISPLOCK_2_CAMLOCK(isp); break; case CMD_RQLATER: /* * This can only happen for Fibre Channel */ KASSERT((IS_FC(isp)), ("CMD_RQLATER for FC only")); if (FCPARAM(isp)->loop_seen_once == 0 && isp->isp_osinfo.ktmature) { ISPLOCK_2_CAMLOCK(isp); XS_SETERR(ccb, CAM_SEL_TIMEOUT); xpt_done(ccb); break; } #ifdef ISP_SMPLOCK cv_signal(&isp->isp_osinfo.kthread_cv); #else wakeup(&isp->isp_osinfo.kthread_cv); #endif isp_freeze_loopdown(isp, "isp_action(RQLATER)"); XS_SETERR(ccb, CAM_REQUEUE_REQ); ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; case CMD_EAGAIN: XS_SETERR(ccb, CAM_REQUEUE_REQ); ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; case CMD_COMPLETE: isp_done((struct ccb_scsiio *) ccb); ISPLOCK_2_CAMLOCK(isp); break; default: isp_prt(isp, ISP_LOGERR, "What's this? 0x%x at %d in file %s", error, __LINE__, __FILE__); XS_SETERR(ccb, CAM_REQ_CMP_ERR); xpt_done(ccb); ISPLOCK_2_CAMLOCK(isp); } break; #ifdef ISP_TARGET_MODE case XPT_EN_LUN: /* Enable LUN as a target */ { int iok; CAMLOCK_2_ISPLOCK(isp); iok = isp->isp_osinfo.intsok; isp->isp_osinfo.intsok = 0; isp_en_lun(isp, ccb); isp->isp_osinfo.intsok = iok; ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; } case XPT_NOTIFY_ACK: /* recycle notify ack */ case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */ case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */ { tstate_t *tptr = get_lun_statep(isp, XS_CHANNEL(ccb), ccb->ccb_h.target_lun); if (tptr == NULL) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); break; } ccb->ccb_h.sim_priv.entries[0].field = 0; ccb->ccb_h.sim_priv.entries[1].ptr = isp; ccb->ccb_h.flags = 0; CAMLOCK_2_ISPLOCK(isp); if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { /* * Note that the command itself may not be done- * it may not even have had the first CTIO sent. */ tptr->atio_count++; isp_prt(isp, ISP_LOGTDEBUG0, "Put FREE ATIO2, lun %d, count now %d", ccb->ccb_h.target_lun, tptr->atio_count); SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, sim_links.sle); } else if (ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) { SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle); } else { ; } rls_lun_statep(isp, tptr); ccb->ccb_h.status = CAM_REQ_INPROG; ISPLOCK_2_CAMLOCK(isp); break; } case XPT_CONT_TARGET_IO: { CAMLOCK_2_ISPLOCK(isp); ccb->ccb_h.status = isp_target_start_ctio(isp, ccb); if (ccb->ccb_h.status != CAM_REQ_INPROG) { isp_prt(isp, ISP_LOGWARN, "XPT_CONT_TARGET_IO: status 0x%x", ccb->ccb_h.status); XS_SETERR(ccb, CAM_REQUEUE_REQ); ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); } else { ISPLOCK_2_CAMLOCK(isp); ccb->ccb_h.status |= CAM_SIM_QUEUED; } break; } #endif case XPT_RESET_DEV: /* BDR the specified SCSI device */ bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); tgt = ccb->ccb_h.target_id; tgt |= (bus << 16); CAMLOCK_2_ISPLOCK(isp); error = isp_control(isp, ISPCTL_RESET_DEV, &tgt); ISPLOCK_2_CAMLOCK(isp); if (error) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; } else { ccb->ccb_h.status = CAM_REQ_CMP; } xpt_done(ccb); break; case XPT_ABORT: /* Abort the specified CCB */ { union ccb *accb = ccb->cab.abort_ccb; CAMLOCK_2_ISPLOCK(isp); switch (accb->ccb_h.func_code) { #ifdef ISP_TARGET_MODE case XPT_ACCEPT_TARGET_IO: case XPT_IMMED_NOTIFY: ccb->ccb_h.status = isp_abort_tgt_ccb(isp, ccb); break; case XPT_CONT_TARGET_IO: isp_prt(isp, ISP_LOGERR, "cannot abort CTIOs yet"); ccb->ccb_h.status = CAM_UA_ABORT; break; #endif case XPT_SCSI_IO: error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); if (error) { ccb->ccb_h.status = CAM_UA_ABORT; } else { ccb->ccb_h.status = CAM_REQ_CMP; } break; default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; } #ifdef CAM_NEW_TRAN_CODE #define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS) #else #define IS_CURRENT_SETTINGS(c) (c->flags & CCB_TRANS_CURRENT_SETTINGS) #endif case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ cts = &ccb->cts; if (!IS_CURRENT_SETTINGS(cts)) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } tgt = cts->ccb_h.target_id; CAMLOCK_2_ISPLOCK(isp); if (IS_SCSI(isp)) { #ifndef CAM_NEW_TRAN_CODE sdparam *sdp = isp->isp_param; u_int16_t *dptr; bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); sdp += bus; /* * We always update (internally) from goal_flags * so any request to change settings just gets * vectored to that location. */ dptr = &sdp->isp_devparam[tgt].goal_flags; /* * Note that these operations affect the * the goal flags (goal_flags)- not * the current state flags. Then we mark * things so that the next operation to * this HBA will cause the update to occur. */ if (cts->valid & CCB_TRANS_DISC_VALID) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { *dptr |= DPARM_DISC; } else { *dptr &= ~DPARM_DISC; } } if (cts->valid & CCB_TRANS_TQ_VALID) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { *dptr |= DPARM_TQING; } else { *dptr &= ~DPARM_TQING; } } if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) { switch (cts->bus_width) { case MSG_EXT_WDTR_BUS_16_BIT: *dptr |= DPARM_WIDE; break; default: *dptr &= ~DPARM_WIDE; } } /* * Any SYNC RATE of nonzero and SYNC_OFFSET * of nonzero will cause us to go to the * selected (from NVRAM) maximum value for * this device. At a later point, we'll * allow finer control. */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) && (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) && (cts->sync_offset > 0)) { *dptr |= DPARM_SYNC; } else { *dptr &= ~DPARM_SYNC; } *dptr |= DPARM_SAFE_DFLT; #else struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; sdparam *sdp = isp->isp_param; u_int16_t *dptr; bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); sdp += bus; /* * We always update (internally) from goal_flags * so any request to change settings just gets * vectored to that location. */ dptr = &sdp->isp_devparam[tgt].goal_flags; if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) *dptr |= DPARM_DISC; else *dptr &= ~DPARM_DISC; } if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) *dptr |= DPARM_TQING; else *dptr &= ~DPARM_TQING; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT) *dptr |= DPARM_WIDE; else *dptr &= ~DPARM_WIDE; } /* * XXX: FIX ME */ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) && (spi->valid & CTS_SPI_VALID_SYNC_RATE) && (spi->sync_period && spi->sync_offset)) { *dptr |= DPARM_SYNC; /* * XXX: CHECK FOR LEGALITY */ sdp->isp_devparam[tgt].goal_period = spi->sync_period; sdp->isp_devparam[tgt].goal_offset = spi->sync_offset; } else { *dptr &= ~DPARM_SYNC; } #endif isp_prt(isp, ISP_LOGDEBUG0, "SET bus %d targ %d to flags %x off %x per %x", bus, tgt, sdp->isp_devparam[tgt].goal_flags, sdp->isp_devparam[tgt].goal_offset, sdp->isp_devparam[tgt].goal_period); sdp->isp_devparam[tgt].dev_update = 1; isp->isp_update |= (1 << bus); } ISPLOCK_2_CAMLOCK(isp); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: cts = &ccb->cts; tgt = cts->ccb_h.target_id; CAMLOCK_2_ISPLOCK(isp); if (IS_FC(isp)) { #ifndef CAM_NEW_TRAN_CODE /* * a lot of normal SCSI things don't make sense. */ cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB; cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; /* * How do you measure the width of a high * speed serial bus? Well, in bytes. * * Offset and period make no sense, though, so we set * (above) a 'base' transfer speed to be gigabit. */ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; #else fcparam *fcp = isp->isp_param; struct ccb_trans_settings_fc *fc = &cts->xport_specific.fc; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_FC; cts->transport_version = 0; fc->valid = CTS_FC_VALID_SPEED; if (fcp->isp_gbspeed == 2) fc->bitrate = 200000; else fc->bitrate = 100000; if (tgt > 0 && tgt < MAX_FC_TARG) { struct lportdb *lp = &fcp->portdb[tgt]; fc->wwnn = lp->node_wwn; fc->wwpn = lp->port_wwn; fc->port = lp->portid; fc->valid |= CTS_FC_VALID_WWNN | CTS_FC_VALID_WWPN | CTS_FC_VALID_PORT; } #endif } else { #ifdef CAM_NEW_TRAN_CODE struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; #endif sdparam *sdp = isp->isp_param; int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); u_int16_t dval, pval, oval; sdp += bus; if (IS_CURRENT_SETTINGS(cts)) { sdp->isp_devparam[tgt].dev_refresh = 1; isp->isp_update |= (1 << bus); (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL); dval = sdp->isp_devparam[tgt].actv_flags; oval = sdp->isp_devparam[tgt].actv_offset; pval = sdp->isp_devparam[tgt].actv_period; } else { dval = sdp->isp_devparam[tgt].nvrm_flags; oval = sdp->isp_devparam[tgt].nvrm_offset; pval = sdp->isp_devparam[tgt].nvrm_period; } #ifndef CAM_NEW_TRAN_CODE cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if (dval & DPARM_DISC) { cts->flags |= CCB_TRANS_DISC_ENB; } if (dval & DPARM_TQING) { cts->flags |= CCB_TRANS_TAG_ENB; } if (dval & DPARM_WIDE) { cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } cts->valid = CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; if ((dval & DPARM_SYNC) && oval != 0) { cts->sync_period = pval; cts->sync_offset = oval; cts->valid |= CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; } #else cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (dval & DPARM_DISC) { spi->flags |= CTS_SPI_FLAGS_DISC_ENB; } if (dval & DPARM_TQING) { scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } if ((dval & DPARM_SYNC) && oval && pval) { spi->sync_offset = oval; spi->sync_period = pval; spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->valid |= CTS_SPI_VALID_SYNC_RATE; } spi->valid |= CTS_SPI_VALID_BUS_WIDTH; if (dval & DPARM_WIDE) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else { scsi->valid = 0; } #endif isp_prt(isp, ISP_LOGDEBUG0, "GET %s bus %d targ %d to flags %x off %x per %x", IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM", bus, tgt, dval, oval, pval); } ISPLOCK_2_CAMLOCK(isp); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; - u_int32_t secs_per_cylinder; - u_int32_t size_mb; ccg = &ccb->ccg; if (ccg->block_size == 0) { isp_prt(isp, ISP_LOGERR, "%d.%d XPT_CALC_GEOMETRY block size 0?", ccg->ccb_h.target_id, ccg->ccb_h.target_lun); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } - size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size); - if (size_mb > 1024) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified bus */ bus = cam_sim_bus(sim); CAMLOCK_2_ISPLOCK(isp); error = isp_control(isp, ISPCTL_RESET_BUS, &bus); ISPLOCK_2_CAMLOCK(isp); if (error) ccb->ccb_h.status = CAM_REQ_CMP_ERR; else { if (cam_sim_bus(sim) && isp->isp_path2 != NULL) xpt_async(AC_BUS_RESET, isp->isp_path2, NULL); else if (isp->isp_path != NULL) xpt_async(AC_BUS_RESET, isp->isp_path, NULL); ccb->ccb_h.status = CAM_REQ_CMP; } xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; #ifdef ISP_TARGET_MODE cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; #else cpi->target_sprt = 0; #endif cpi->hba_eng_cnt = 0; cpi->max_target = ISP_MAX_TARGETS(isp) - 1; cpi->max_lun = ISP_MAX_LUNS(isp) - 1; cpi->bus_id = cam_sim_bus(sim); if (IS_FC(isp)) { cpi->hba_misc = PIM_NOBUSRESET; /* * Because our loop ID can shift from time to time, * make our initiator ID out of range of our bus. */ cpi->initiator_id = cpi->max_target + 1; /* * Set base transfer capabilities for Fibre Channel. * Technically not correct because we don't know * what media we're running on top of- but we'll * look good if we always say 100MB/s. */ if (FCPARAM(isp)->isp_gbspeed == 2) cpi->base_transfer_speed = 200000; else cpi->base_transfer_speed = 100000; cpi->hba_inquiry = PI_TAG_ABLE; #ifdef CAM_NEW_TRAN_CODE cpi->transport = XPORT_FC; cpi->transport_version = 0; /* WHAT'S THIS FOR? */ #endif } else { sdparam *sdp = isp->isp_param; sdp += cam_sim_bus(xpt_path_sim(cpi->ccb_h.path)); cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->hba_misc = 0; cpi->initiator_id = sdp->isp_initiator_id; cpi->base_transfer_speed = 3300; #ifdef CAM_NEW_TRAN_CODE cpi->transport = XPORT_SPI; cpi->transport_version = 2; /* WHAT'S THIS FOR? */ #endif } #ifdef CAM_NEW_TRAN_CODE cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; #endif strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } #define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB) void isp_done(struct ccb_scsiio *sccb) { struct ispsoftc *isp = XS_ISP(sccb); if (XS_NOERR(sccb)) XS_SETERR(sccb, CAM_REQ_CMP); if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (sccb->scsi_status != SCSI_STATUS_OK)) { sccb->ccb_h.status &= ~CAM_STATUS_MASK; if ((sccb->scsi_status == SCSI_STATUS_CHECK_COND) && (sccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) { sccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } else { sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; } } sccb->ccb_h.status &= ~CAM_SIM_QUEUED; if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { sccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(sccb->ccb_h.path, 1); isp_prt(isp, ISP_LOGDEBUG0, "freeze devq %d.%d cam sts %x scsi sts %x", sccb->ccb_h.target_id, sccb->ccb_h.target_lun, sccb->ccb_h.status, sccb->scsi_status); } } if ((CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB)) && (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { xpt_print_path(sccb->ccb_h.path); isp_prt(isp, ISP_LOGINFO, "cam completion status 0x%x", sccb->ccb_h.status); } XS_CMD_S_DONE(sccb); if (XS_CMD_WDOG_P(sccb) == 0) { untimeout(isp_watchdog, (caddr_t)sccb, sccb->ccb_h.timeout_ch); if (XS_CMD_GRACE_P(sccb)) { isp_prt(isp, ISP_LOGDEBUG2, "finished command on borrowed time"); } XS_CMD_S_CLEAR(sccb); ISPLOCK_2_CAMLOCK(isp); xpt_done((union ccb *) sccb); CAMLOCK_2_ISPLOCK(isp); } } int isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg) { int bus, rv = 0; switch (cmd) { case ISPASYNC_NEW_TGT_PARAMS: { #ifdef CAM_NEW_TRAN_CODE struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; #endif int flags, tgt; sdparam *sdp = isp->isp_param; struct ccb_trans_settings cts; struct cam_path *tmppath; bzero(&cts, sizeof (struct ccb_trans_settings)); tgt = *((int *)arg); bus = (tgt >> 16) & 0xffff; tgt &= 0xffff; sdp += bus; ISPLOCK_2_CAMLOCK(isp); if (xpt_create_path(&tmppath, NULL, cam_sim_path(bus? isp->isp_sim2 : isp->isp_sim), tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { CAMLOCK_2_ISPLOCK(isp); isp_prt(isp, ISP_LOGWARN, "isp_async cannot make temp path for %d.%d", tgt, bus); rv = -1; break; } CAMLOCK_2_ISPLOCK(isp); flags = sdp->isp_devparam[tgt].actv_flags; #ifdef CAM_NEW_TRAN_CODE cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.protocol = PROTO_SCSI; cts.transport = XPORT_SPI; scsi = &cts.proto_specific.scsi; spi = &cts.xport_specific.spi; if (flags & DPARM_TQING) { scsi->valid |= CTS_SCSI_VALID_TQ; scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; spi->flags |= CTS_SPI_FLAGS_TAG_ENB; } if (flags & DPARM_DISC) { spi->valid |= CTS_SPI_VALID_DISC; spi->flags |= CTS_SPI_FLAGS_DISC_ENB; } spi->flags |= CTS_SPI_VALID_BUS_WIDTH; if (flags & DPARM_WIDE) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } if (flags & DPARM_SYNC) { spi->valid |= CTS_SPI_VALID_SYNC_RATE; spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->sync_period = sdp->isp_devparam[tgt].actv_period; spi->sync_offset = sdp->isp_devparam[tgt].actv_offset; } #else cts.flags = CCB_TRANS_CURRENT_SETTINGS; cts.valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; if (flags & DPARM_DISC) { cts.flags |= CCB_TRANS_DISC_ENB; } if (flags & DPARM_TQING) { cts.flags |= CCB_TRANS_TAG_ENB; } cts.valid |= CCB_TRANS_BUS_WIDTH_VALID; cts.bus_width = (flags & DPARM_WIDE)? MSG_EXT_WDTR_BUS_8_BIT : MSG_EXT_WDTR_BUS_16_BIT; cts.sync_period = sdp->isp_devparam[tgt].actv_period; cts.sync_offset = sdp->isp_devparam[tgt].actv_offset; if (flags & DPARM_SYNC) { cts.valid |= CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; } #endif isp_prt(isp, ISP_LOGDEBUG2, "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x", bus, tgt, sdp->isp_devparam[tgt].actv_period, sdp->isp_devparam[tgt].actv_offset, flags); xpt_setup_ccb(&cts.ccb_h, tmppath, 1); ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_TRANSFER_NEG, tmppath, &cts); xpt_free_path(tmppath); CAMLOCK_2_ISPLOCK(isp); break; } case ISPASYNC_BUS_RESET: bus = *((int *)arg); isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected", bus); if (bus > 0 && isp->isp_path2) { ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_BUS_RESET, isp->isp_path2, NULL); CAMLOCK_2_ISPLOCK(isp); } else if (isp->isp_path) { ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_BUS_RESET, isp->isp_path, NULL); CAMLOCK_2_ISPLOCK(isp); } break; case ISPASYNC_LIP: if (isp->isp_path) { isp_freeze_loopdown(isp, "ISPASYNC_LIP"); } isp_prt(isp, ISP_LOGINFO, "LIP Received"); break; case ISPASYNC_LOOP_RESET: if (isp->isp_path) { isp_freeze_loopdown(isp, "ISPASYNC_LOOP_RESET"); } isp_prt(isp, ISP_LOGINFO, "Loop Reset Received"); break; case ISPASYNC_LOOP_DOWN: if (isp->isp_path) { isp_freeze_loopdown(isp, "ISPASYNC_LOOP_DOWN"); } isp_prt(isp, ISP_LOGINFO, "Loop DOWN"); break; case ISPASYNC_LOOP_UP: /* * Now we just note that Loop has come up. We don't * actually do anything because we're waiting for a * Change Notify before activating the FC cleanup * thread to look at the state of the loop again. */ isp_prt(isp, ISP_LOGINFO, "Loop UP"); break; case ISPASYNC_PROMENADE: { struct cam_path *tmppath; const char *fmt = "Target %d (Loop 0x%x) Port ID 0x%x " "(role %s) %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x"; static const char *roles[4] = { "(none)", "Target", "Initiator", "Target/Initiator" }; fcparam *fcp = isp->isp_param; int tgt = *((int *) arg); int is_tgt_mask = (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT); struct lportdb *lp = &fcp->portdb[tgt]; isp_prt(isp, ISP_LOGINFO, fmt, tgt, lp->loopid, lp->portid, roles[lp->roles & 0x3], (lp->valid)? "Arrived" : "Departed", (u_int32_t) (lp->port_wwn >> 32), (u_int32_t) (lp->port_wwn & 0xffffffffLL), (u_int32_t) (lp->node_wwn >> 32), (u_int32_t) (lp->node_wwn & 0xffffffffLL)); ISPLOCK_2_CAMLOCK(isp); if (xpt_create_path(&tmppath, NULL, cam_sim_path(isp->isp_sim), (target_id_t)tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { CAMLOCK_2_ISPLOCK(isp); break; } /* * Policy: only announce targets. */ if (lp->roles & is_tgt_mask) { if (lp->valid) { xpt_async(AC_FOUND_DEVICE, tmppath, NULL); } else { xpt_async(AC_LOST_DEVICE, tmppath, NULL); } } xpt_free_path(tmppath); CAMLOCK_2_ISPLOCK(isp); break; } case ISPASYNC_CHANGE_NOTIFY: if (arg == ISPASYNC_CHANGE_PDB) { isp_prt(isp, ISP_LOGINFO, "Port Database Changed"); } else if (arg == ISPASYNC_CHANGE_SNS) { isp_prt(isp, ISP_LOGINFO, "Name Server Database Changed"); } #ifdef ISP_SMPLOCK cv_signal(&isp->isp_osinfo.kthread_cv); #else wakeup(&isp->isp_osinfo.kthread_cv); #endif break; case ISPASYNC_FABRIC_DEV: { int target, base, lim; fcparam *fcp = isp->isp_param; struct lportdb *lp = NULL; struct lportdb *clp = (struct lportdb *) arg; char *pt; switch (clp->port_type) { case 1: pt = " N_Port"; break; case 2: pt = " NL_Port"; break; case 3: pt = "F/NL_Port"; break; case 0x7f: pt = " Nx_Port"; break; case 0x81: pt = " F_port"; break; case 0x82: pt = " FL_Port"; break; case 0x84: pt = " E_port"; break; default: pt = " "; break; } isp_prt(isp, ISP_LOGINFO, "%s Fabric Device @ PortID 0x%x", pt, clp->portid); /* * If we don't have an initiator role we bail. * * We just use ISPASYNC_FABRIC_DEV for announcement purposes. */ if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) { break; } /* * Is this entry for us? If so, we bail. */ if (fcp->isp_portid == clp->portid) { break; } /* * Else, the default policy is to find room for it in * our local port database. Later, when we execute * the call to isp_pdb_sync either this newly arrived * or already logged in device will be (re)announced. */ if (fcp->isp_topo == TOPO_FL_PORT) base = FC_SNS_ID+1; else base = 0; if (fcp->isp_topo == TOPO_N_PORT) lim = 1; else lim = MAX_FC_TARG; /* * Is it already in our list? */ for (target = base; target < lim; target++) { if (target >= FL_PORT_ID && target <= FC_SNS_ID) { continue; } lp = &fcp->portdb[target]; if (lp->port_wwn == clp->port_wwn && lp->node_wwn == clp->node_wwn) { lp->fabric_dev = 1; break; } } if (target < lim) { break; } for (target = base; target < lim; target++) { if (target >= FL_PORT_ID && target <= FC_SNS_ID) { continue; } lp = &fcp->portdb[target]; if (lp->port_wwn == 0) { break; } } if (target == lim) { isp_prt(isp, ISP_LOGWARN, "out of space for fabric devices"); break; } lp->port_type = clp->port_type; lp->fc4_type = clp->fc4_type; lp->node_wwn = clp->node_wwn; lp->port_wwn = clp->port_wwn; lp->portid = clp->portid; lp->fabric_dev = 1; break; } #ifdef ISP_TARGET_MODE case ISPASYNC_TARGET_MESSAGE: { tmd_msg_t *mp = arg; isp_prt(isp, ISP_LOGALL, "bus %d iid %d tgt %d lun %d ttype %x tval %x msg[0]=%x", mp->nt_bus, (int) mp->nt_iid, (int) mp->nt_tgt, (int) mp->nt_lun, mp->nt_tagtype, mp->nt_tagval, mp->nt_msg[0]); break; } case ISPASYNC_TARGET_EVENT: { tmd_event_t *ep = arg; isp_prt(isp, ISP_LOGALL, "bus %d event code 0x%x", ep->ev_bus, ep->ev_event); break; } case ISPASYNC_TARGET_ACTION: switch (((isphdr_t *)arg)->rqs_entry_type) { default: isp_prt(isp, ISP_LOGWARN, "event 0x%x for unhandled target action", ((isphdr_t *)arg)->rqs_entry_type); break; case RQSTYPE_NOTIFY: if (IS_SCSI(isp)) { rv = isp_handle_platform_notify_scsi(isp, (in_entry_t *) arg); } else { rv = isp_handle_platform_notify_fc(isp, (in_fcentry_t *) arg); } break; case RQSTYPE_ATIO: rv = isp_handle_platform_atio(isp, (at_entry_t *) arg); break; case RQSTYPE_ATIO2: rv = isp_handle_platform_atio2(isp, (at2_entry_t *)arg); break; case RQSTYPE_CTIO2: case RQSTYPE_CTIO: rv = isp_handle_platform_ctio(isp, arg); break; case RQSTYPE_ENABLE_LUN: case RQSTYPE_MODIFY_LUN: if (IS_DUALBUS(isp)) { bus = GET_BUS_VAL(((lun_entry_t *)arg)->le_rsvd); } else { bus = 0; } isp_cv_signal_rqe(isp, bus, ((lun_entry_t *)arg)->le_status); break; } break; #endif case ISPASYNC_FW_CRASH: { u_int16_t mbox1, mbox6; mbox1 = ISP_READ(isp, OUTMAILBOX1); if (IS_DUALBUS(isp)) { mbox6 = ISP_READ(isp, OUTMAILBOX6); } else { mbox6 = 0; } isp_prt(isp, ISP_LOGERR, "Internal Firmware Error on bus %d @ RISC Address 0x%x", mbox6, mbox1); #ifdef ISP_FW_CRASH_DUMP /* * XXX: really need a thread to do this right. */ if (IS_FC(isp)) { FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; FCPARAM(isp)->isp_loopstate = LOOP_NIL; isp_freeze_loopdown(isp, "f/w crash"); isp_fw_dump(isp); } isp_reinit(isp); isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); #endif break; } case ISPASYNC_UNHANDLED_RESPONSE: break; default: isp_prt(isp, ISP_LOGERR, "unknown isp_async event %d", cmd); break; } return (rv); } /* * Locks are held before coming here. */ void isp_uninit(struct ispsoftc *isp) { ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); DISABLE_INTS(isp); } void isp_prt(struct ispsoftc *isp, int level, const char *fmt, ...) { va_list ap; if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) { return; } printf("%s: ", device_get_nameunit(isp->isp_dev)); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } Index: head/sys/dev/mpt/mpt_freebsd.c =================================================================== --- head/sys/dev/mpt/mpt_freebsd.c (revision 116350) +++ head/sys/dev/mpt/mpt_freebsd.c (revision 116351) @@ -1,1538 +1,1526 @@ /* $FreeBSD$ */ /* * FreeBSD/CAM specific routines for LSI '909 FC adapters. * FreeBSD Version. * * Copyright (c) 2000, 2001 by Greg Ansley * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Additional Copyright (c) 2002 by Matthew Jacob under same license. */ #include static void mpt_poll(struct cam_sim *); static timeout_t mpttimeout; static timeout_t mpttimeout2; static void mpt_action(struct cam_sim *, union ccb *); static int mpt_setwidth(mpt_softc_t *, int, int); static int mpt_setsync(mpt_softc_t *, int, int, int); void mpt_cam_attach(mpt_softc_t *mpt) { struct cam_devq *devq; struct cam_sim *sim; int maxq; mpt->bus = 0; maxq = (mpt->mpt_global_credits < MPT_MAX_REQUESTS(mpt))? mpt->mpt_global_credits : MPT_MAX_REQUESTS(mpt); /* * Create the device queue for our SIM(s). */ devq = cam_simq_alloc(maxq); if (devq == NULL) { return; } /* * Construct our SIM entry. */ sim = cam_sim_alloc(mpt_action, mpt_poll, "mpt", mpt, mpt->unit, 1, maxq, devq); if (sim == NULL) { cam_simq_free(devq); return; } /* * Register exactly the bus. */ if (xpt_bus_register(sim, 0) != CAM_SUCCESS) { cam_sim_free(sim, TRUE); return; } if (xpt_create_path(&mpt->path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); return; } mpt->sim = sim; } void mpt_cam_detach(mpt_softc_t *mpt) { if (mpt->sim != NULL) { xpt_free_path(mpt->path); xpt_bus_deregister(cam_sim_path(mpt->sim)); cam_sim_free(mpt->sim, TRUE); mpt->sim = NULL; } } /* This routine is used after a system crash to dump core onto the * swap device. */ static void mpt_poll(struct cam_sim *sim) { mpt_softc_t *mpt = (mpt_softc_t *) cam_sim_softc(sim); MPT_LOCK(mpt); mpt_intr(mpt); MPT_UNLOCK(mpt); } /* * This routine is called if the 9x9 does not return completion status * for a command after a CAM specified time. */ static void mpttimeout(void *arg) { request_t *req; union ccb *ccb = arg; u_int32_t oseq; mpt_softc_t *mpt; mpt = ccb->ccb_h.ccb_mpt_ptr; MPT_LOCK(mpt); req = ccb->ccb_h.ccb_req_ptr; oseq = req->sequence; mpt->timeouts++; if (mpt_intr(mpt)) { if (req->sequence != oseq) { mpt_prt(mpt, "bullet missed in timeout"); MPT_UNLOCK(mpt); return; } mpt_prt(mpt, "bullet U-turned in timeout: got us"); } mpt_prt(mpt, "time out on request index = 0x%02x sequence = 0x%08x", req->index, req->sequence); mpt_check_doorbell(mpt); mpt_prt(mpt, "Status %08x; Mask %08x; Doorbell %08x", mpt_read(mpt, MPT_OFFSET_INTR_STATUS), mpt_read(mpt, MPT_OFFSET_INTR_MASK), mpt_read(mpt, MPT_OFFSET_DOORBELL) ); printf("request state %s\n", mpt_req_state(req->debug)); if (ccb != req->ccb) { printf("time out: ccb %p != req->ccb %p\n", ccb,req->ccb); } mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf); req->debug = REQ_TIMEOUT; req->ccb = NULL; req->link.sle_next = (void *) mpt; (void) timeout(mpttimeout2, (caddr_t)req, hz / 10); ccb->ccb_h.status = CAM_CMD_TIMEOUT; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; mpt->outofbeer = 0; MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); CAMLOCK_2_MPTLOCK(mpt); MPT_UNLOCK(mpt); } static void mpttimeout2(void *arg) { request_t *req = arg; if (req->debug == REQ_TIMEOUT) { mpt_softc_t *mpt = (mpt_softc_t *) req->link.sle_next; MPT_LOCK(mpt); mpt_free_request(mpt, req); MPT_UNLOCK(mpt); } } /* * Callback routine from "bus_dmamap_load" or in simple case called directly. * * Takes a list of physical segments and builds the SGL for SCSI IO command * and forwards the commard to the IOC after one last check that CAM has not * aborted the transaction. */ static void mpt_execute_req(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { request_t *req; union ccb *ccb; mpt_softc_t *mpt; MSG_SCSI_IO_REQUEST *mpt_req; SGE_SIMPLE32 *se; req = (request_t *)arg; ccb = req->ccb; mpt = ccb->ccb_h.ccb_mpt_ptr; req = ccb->ccb_h.ccb_req_ptr; mpt_req = req->req_vbuf; if (error == 0 && nseg > MPT_SGL_MAX) { error = EFBIG; } if (error != 0) { if (error != EFBIG) mpt_prt(mpt, "bus_dmamap_load returned %d", error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status = CAM_DEV_QFRZN; if (error == EFBIG) ccb->ccb_h.status |= CAM_REQ_TOO_BIG; else ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; xpt_done(ccb); CAMLOCK_2_MPTLOCK(mpt); mpt_free_request(mpt, req); MPTLOCK_2_CAMLOCK(mpt); return; } if (nseg > MPT_NSGL_FIRST(mpt)) { int i, nleft = nseg; u_int32_t flags; bus_dmasync_op_t op; SGE_CHAIN32 *ce; mpt_req->DataLength = ccb->csio.dxfer_len; flags = MPI_SGE_FLAGS_SIMPLE_ELEMENT; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) flags |= MPI_SGE_FLAGS_HOST_TO_IOC; se = (SGE_SIMPLE32 *) &mpt_req->SGL; for (i = 0; i < MPT_NSGL_FIRST(mpt) - 1; i++, se++, dm_segs++) { u_int32_t tf; bzero(se, sizeof (*se)); se->Address = dm_segs->ds_addr; MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len); tf = flags; if (i == MPT_NSGL_FIRST(mpt) - 2) { tf |= MPI_SGE_FLAGS_LAST_ELEMENT; } MPI_pSGE_SET_FLAGS(se, tf); nleft -= 1; } /* * Tell the IOC where to find the first chain element */ mpt_req->ChainOffset = ((char *)se - (char *)mpt_req) >> 2; /* * Until we're finished with all segments... */ while (nleft) { int ntodo; /* * Construct the chain element that point to the * next segment. */ ce = (SGE_CHAIN32 *) se++; if (nleft > MPT_NSGL(mpt)) { ntodo = MPT_NSGL(mpt) - 1; ce->NextChainOffset = (MPT_RQSL(mpt) - sizeof (SGE_SIMPLE32)) >> 2; } else { ntodo = nleft; ce->NextChainOffset = 0; } ce->Length = ntodo * sizeof (SGE_SIMPLE32); ce->Address = req->req_pbuf + ((char *)se - (char *)mpt_req); ce->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT; for (i = 0; i < ntodo; i++, se++, dm_segs++) { u_int32_t tf; bzero(se, sizeof (*se)); se->Address = dm_segs->ds_addr; MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len); tf = flags; if (i == ntodo - 1) { tf |= MPI_SGE_FLAGS_LAST_ELEMENT; if (ce->NextChainOffset == 0) { tf |= MPI_SGE_FLAGS_END_OF_LIST | MPI_SGE_FLAGS_END_OF_BUFFER; } } MPI_pSGE_SET_FLAGS(se, tf); nleft -= 1; } } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; if (!(ccb->ccb_h.flags & (CAM_SG_LIST_PHYS|CAM_DATA_PHYS))) { bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op); } } else if (nseg > 0) { int i; u_int32_t flags; bus_dmasync_op_t op; mpt_req->DataLength = ccb->csio.dxfer_len; flags = MPI_SGE_FLAGS_SIMPLE_ELEMENT; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) flags |= MPI_SGE_FLAGS_HOST_TO_IOC; /* Copy the segments into our SG list */ se = (SGE_SIMPLE32 *) &mpt_req->SGL; for (i = 0; i < nseg; i++, se++, dm_segs++) { u_int32_t tf; bzero(se, sizeof (*se)); se->Address = dm_segs->ds_addr; MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len); tf = flags; if (i == nseg - 1) { tf |= MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | MPI_SGE_FLAGS_END_OF_LIST; } MPI_pSGE_SET_FLAGS(se, tf); } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; if (!(ccb->ccb_h.flags & (CAM_SG_LIST_PHYS|CAM_DATA_PHYS))) { bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op); } } else { se = (SGE_SIMPLE32 *) &mpt_req->SGL; /* * No data to transfer so we just make a single simple SGL * with zero length. */ MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | MPI_SGE_FLAGS_SIMPLE_ELEMENT | MPI_SGE_FLAGS_END_OF_LIST)); } /* * Last time we need to check if this CCB needs to be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg && (ccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) bus_dmamap_unload(mpt->buffer_dmat, req->dmap); CAMLOCK_2_MPTLOCK(mpt); mpt_free_request(mpt, req); MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); return; } ccb->ccb_h.status |= CAM_SIM_QUEUED; MPTLOCK_2_CAMLOCK(mpt); if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { ccb->ccb_h.timeout_ch = timeout(mpttimeout, (caddr_t)ccb, (ccb->ccb_h.timeout * hz) / 1000); } else { callout_handle_init(&ccb->ccb_h.timeout_ch); } if (mpt->verbose > 1) mpt_print_scsi_io_request(mpt_req); mpt_send_cmd(mpt, req); MPTLOCK_2_CAMLOCK(mpt); } static void mpt_start(union ccb *ccb) { request_t *req; struct mpt_softc *mpt; MSG_SCSI_IO_REQUEST *mpt_req; struct ccb_scsiio *csio = &ccb->csio; struct ccb_hdr *ccbh = &ccb->ccb_h; /* Get the pointer for the physical addapter */ mpt = ccb->ccb_h.ccb_mpt_ptr; CAMLOCK_2_MPTLOCK(mpt); /* Get a request structure off the free list */ if ((req = mpt_get_request(mpt)) == NULL) { if (mpt->outofbeer == 0) { mpt->outofbeer = 1; xpt_freeze_simq(mpt->sim, 1); if (mpt->verbose > 1) { mpt_prt(mpt, "FREEZEQ"); } } MPTLOCK_2_CAMLOCK(mpt); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } MPTLOCK_2_CAMLOCK(mpt); /* Link the ccb and the request structure so we can find */ /* the other knowing either the request or the ccb */ req->ccb = ccb; ccb->ccb_h.ccb_req_ptr = req; /* Now we build the command for the IOC */ mpt_req = req->req_vbuf; bzero(mpt_req, sizeof *mpt_req); mpt_req->Function = MPI_FUNCTION_SCSI_IO_REQUEST; mpt_req->Bus = mpt->bus; mpt_req->SenseBufferLength = (csio->sense_len < MPT_SENSE_SIZE) ? csio->sense_len : MPT_SENSE_SIZE; /* We use the message context to find the request structure when we */ /* Get the command competion interrupt from the FC IOC. */ mpt_req->MsgContext = req->index; /* Which physical device to do the I/O on */ mpt_req->TargetID = ccb->ccb_h.target_id; mpt_req->LUN[1] = ccb->ccb_h.target_lun; /* Set the direction of the transfer */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) mpt_req->Control = MPI_SCSIIO_CONTROL_READ; else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) mpt_req->Control = MPI_SCSIIO_CONTROL_WRITE; else mpt_req->Control = MPI_SCSIIO_CONTROL_NODATATRANSFER; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { switch(ccb->csio.tag_action) { case MSG_HEAD_OF_Q_TAG: mpt_req->Control |= MPI_SCSIIO_CONTROL_HEADOFQ; break; case MSG_ACA_TASK: mpt_req->Control |= MPI_SCSIIO_CONTROL_ACAQ; break; case MSG_ORDERED_Q_TAG: mpt_req->Control |= MPI_SCSIIO_CONTROL_ORDEREDQ; break; case MSG_SIMPLE_Q_TAG: default: mpt_req->Control |= MPI_SCSIIO_CONTROL_SIMPLEQ; break; } } else { if (mpt->is_fc) mpt_req->Control |= MPI_SCSIIO_CONTROL_SIMPLEQ; else mpt_req->Control |= MPI_SCSIIO_CONTROL_UNTAGGED; } if (mpt->is_fc == 0) { if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { mpt_req->Control |= MPI_SCSIIO_CONTROL_NO_DISCONNECT; } } /* Copy the scsi command block into place */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) bcopy(csio->cdb_io.cdb_ptr, mpt_req->CDB, csio->cdb_len); else bcopy(csio->cdb_io.cdb_bytes, mpt_req->CDB, csio->cdb_len); mpt_req->CDBLength = csio->cdb_len; mpt_req->DataLength = csio->dxfer_len; mpt_req->SenseBufferLowAddr = req->sense_pbuf; /* * If we have any data to send with this command, * map it into bus space. */ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccbh->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer to a single buffer. */ if ((ccbh->flags & CAM_DATA_PHYS) == 0) { /* * Virtual address that needs to translated into * one or more physical pages. */ int error; error = bus_dmamap_load(mpt->buffer_dmat, req->dmap, csio->data_ptr, csio->dxfer_len, mpt_execute_req, req, 0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(mpt->sim, 1); ccbh->status |= CAM_RELEASE_SIMQ; } } else { /* * We have been given a pointer to single * physical buffer. */ struct bus_dma_segment seg; seg.ds_addr = (bus_addr_t)(vm_offset_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; mpt_execute_req(req, &seg, 1, 0); } } else { /* * We have been given a list of addresses. * These case could be easily done but they are not * currently generated by the CAM subsystem so there * is no point in wasting the time right now. */ struct bus_dma_segment *segs; if ((ccbh->flags & CAM_SG_LIST_PHYS) == 0) { mpt_execute_req(req, NULL, 0, EFAULT); } else { /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; mpt_execute_req(req, segs, csio->sglist_cnt, (csio->sglist_cnt < MPT_SGL_MAX)? 0 : EFBIG); } } } else { mpt_execute_req(req, NULL, 0, 0); } } static int mpt_bus_reset(union ccb *ccb) { int error; request_t *req; mpt_softc_t *mpt; MSG_SCSI_TASK_MGMT *reset_req; /* Get the pointer for the physical adapter */ mpt = ccb->ccb_h.ccb_mpt_ptr; /* Get a request structure off the free list */ if ((req = mpt_get_request(mpt)) == NULL) { return (CAM_REQUEUE_REQ); } /* Link the ccb and the request structure so we can find */ /* the other knowing either the request or the ccb */ req->ccb = ccb; ccb->ccb_h.ccb_req_ptr = req; reset_req = req->req_vbuf; bzero(reset_req, sizeof *reset_req); reset_req->Function = MPI_FUNCTION_SCSI_TASK_MGMT; reset_req->MsgContext = req->index; reset_req->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; if (mpt->is_fc) { /* * Should really be TARGET_RESET_OPTION */ reset_req->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION; } /* Which physical device Reset */ reset_req->TargetID = ccb->ccb_h.target_id; reset_req->LUN[1] = ccb->ccb_h.target_lun; ccb->ccb_h.status |= CAM_SIM_QUEUED; error = mpt_send_handshake_cmd(mpt, sizeof (MSG_SCSI_TASK_MGMT), reset_req); if (error) { mpt_prt(mpt, "mpt_bus_reset: mpt_send_handshake return %d", error); return (CAM_REQ_CMP_ERR); } else { return (CAM_REQ_CMP); } } /* * Process an asynchronous event from the IOC. */ static void mpt_ctlop(mpt_softc_t *, void *, u_int32_t); static void mpt_event_notify_reply(mpt_softc_t *mpt, MSG_EVENT_NOTIFY_REPLY *); static void mpt_ctlop(mpt_softc_t *mpt, void *vmsg, u_int32_t reply) { MSG_DEFAULT_REPLY *dmsg = vmsg; if (dmsg->Function == MPI_FUNCTION_EVENT_NOTIFICATION) { mpt_event_notify_reply(mpt, vmsg); mpt_free_reply(mpt, (reply << 1)); } else if (dmsg->Function == MPI_FUNCTION_EVENT_ACK) { mpt_free_reply(mpt, (reply << 1)); } else if (dmsg->Function == MPI_FUNCTION_PORT_ENABLE) { MSG_PORT_ENABLE_REPLY *msg = vmsg; int index = msg->MsgContext & ~0x80000000; if (mpt->verbose > 1) { mpt_prt(mpt, "enable port reply idx %d", index); } if (index >= 0 && index < MPT_MAX_REQUESTS(mpt)) { request_t *req = &mpt->request_pool[index]; req->debug = REQ_DONE; } mpt_free_reply(mpt, (reply << 1)); } else if (dmsg->Function == MPI_FUNCTION_CONFIG) { MSG_CONFIG_REPLY *msg = vmsg; int index = msg->MsgContext & ~0x80000000; if (index >= 0 && index < MPT_MAX_REQUESTS(mpt)) { request_t *req = &mpt->request_pool[index]; req->debug = REQ_DONE; req->sequence = reply; } else { mpt_free_reply(mpt, (reply << 1)); } } else { mpt_prt(mpt, "unknown mpt_ctlop: %x", dmsg->Function); } } static void mpt_event_notify_reply(mpt_softc_t *mpt, MSG_EVENT_NOTIFY_REPLY *msg) { switch(msg->Event) { case MPI_EVENT_LOG_DATA: /* Some error occured that LSI wants logged */ printf("\tEvtLogData: IOCLogInfo: 0x%08x\n", msg->IOCLogInfo); printf("\tEvtLogData: Event Data:"); { int i; for (i = 0; i < msg->EventDataLength; i++) { printf(" %08x", msg->Data[i]); } } printf("\n"); break; case MPI_EVENT_UNIT_ATTENTION: mpt_prt(mpt, "Bus: 0x%02x TargetID: 0x%02x", (msg->Data[0] >> 8) & 0xff, msg->Data[0] & 0xff); break; case MPI_EVENT_IOC_BUS_RESET: /* We generated a bus reset */ mpt_prt(mpt, "IOC Bus Reset Port: %d", (msg->Data[0] >> 8) & 0xff); break; case MPI_EVENT_EXT_BUS_RESET: /* Someone else generated a bus reset */ mpt_prt(mpt, "Ext Bus Reset"); /* * These replies don't return EventData like the MPI * spec says they do */ /* xpt_async(AC_BUS_RESET, path, NULL); */ break; case MPI_EVENT_RESCAN: /* * In general this means a device has been added * to the loop. */ mpt_prt(mpt, "Rescan Port: %d", (msg->Data[0] >> 8) & 0xff); /* xpt_async(AC_FOUND_DEVICE, path, NULL); */ break; case MPI_EVENT_LINK_STATUS_CHANGE: mpt_prt(mpt, "Port %d: LinkState: %s", (msg->Data[1] >> 8) & 0xff, ((msg->Data[0] & 0xff) == 0)? "Failed" : "Active"); break; case MPI_EVENT_LOOP_STATE_CHANGE: switch ((msg->Data[0] >> 16) & 0xff) { case 0x01: mpt_prt(mpt, "Port 0x%x: FC LinkEvent: LIP(%02x,%02x) (Loop Initialization)\n", (msg->Data[1] >> 8) & 0xff, (msg->Data[0] >> 8) & 0xff, (msg->Data[0] ) & 0xff); switch ((msg->Data[0] >> 8) & 0xff) { case 0xF7: if ((msg->Data[0] & 0xff) == 0xF7) { printf("Device needs AL_PA\n"); } else { printf("Device %02x doesn't like FC performance\n", msg->Data[0] & 0xFF); } break; case 0xF8: if ((msg->Data[0] & 0xff) == 0xF7) { printf("Device had loop failure at its receiver prior to acquiring AL_PA\n"); } else { printf("Device %02x detected loop failure at its receiver\n", msg->Data[0] & 0xFF); } break; default: printf("Device %02x requests that device %02x reset itself\n", msg->Data[0] & 0xFF, (msg->Data[0] >> 8) & 0xFF); break; } break; case 0x02: mpt_prt(mpt, "Port 0x%x: FC LinkEvent: LPE(%02x,%02x) (Loop Port Enable)", (msg->Data[1] >> 8) & 0xff, /* Port */ (msg->Data[0] >> 8) & 0xff, /* Character 3 */ (msg->Data[0] ) & 0xff /* Character 4 */ ); break; case 0x03: mpt_prt(mpt, "Port 0x%x: FC LinkEvent: LPB(%02x,%02x) (Loop Port Bypass)", (msg->Data[1] >> 8) & 0xff, /* Port */ (msg->Data[0] >> 8) & 0xff, /* Character 3 */ (msg->Data[0] ) & 0xff /* Character 4 */ ); break; default: mpt_prt(mpt, "Port 0x%x: FC LinkEvent: Unknown FC event (%02x %02x %02x)", (msg->Data[1] >> 8) & 0xff, /* Port */ (msg->Data[0] >> 16) & 0xff, /* Event */ (msg->Data[0] >> 8) & 0xff, /* Character 3 */ (msg->Data[0] ) & 0xff /* Character 4 */ ); } break; case MPI_EVENT_LOGOUT: mpt_prt(mpt, "FC Logout Port: %d N_PortID: %02x", (msg->Data[1] >> 8) & 0xff, msg->Data[0]); break; case MPI_EVENT_EVENT_CHANGE: /* This is just an acknowledgement of our mpt_send_event_request */ break; default: mpt_prt(mpt, "Unknown event 0x%x\n", msg->Event); } if (msg->AckRequired) { MSG_EVENT_ACK *ackp; request_t *req; if ((req = mpt_get_request(mpt)) == NULL) { panic("unable to get request to acknowledge notify"); } ackp = (MSG_EVENT_ACK *) req->req_vbuf; bzero(ackp, sizeof *ackp); ackp->Function = MPI_FUNCTION_EVENT_ACK; ackp->Event = msg->Event; ackp->EventContext = msg->EventContext; ackp->MsgContext = req->index | 0x80000000; mpt_check_doorbell(mpt); mpt_send_cmd(mpt, req); } } void mpt_done(mpt_softc_t *mpt, u_int32_t reply) { int index; request_t *req; union ccb *ccb; MSG_REQUEST_HEADER *mpt_req; MSG_SCSI_IO_REPLY *mpt_reply; index = -1; /* Shutup the complier */ if ((reply & MPT_CONTEXT_REPLY) == 0) { /* context reply */ mpt_reply = NULL; index = reply & MPT_CONTEXT_MASK; } else { unsigned *pReply; bus_dmamap_sync(mpt->reply_dmat, mpt->reply_dmap, BUS_DMASYNC_POSTREAD); /* address reply (Error) */ mpt_reply = MPT_REPLY_PTOV(mpt, reply); if (mpt->verbose > 1) { pReply = (unsigned *) mpt_reply; mpt_prt(mpt, "Address Reply (index %u)", mpt_reply->MsgContext & 0xffff); printf("%08x %08x %08x %08x\n", pReply[0], pReply[1], pReply[2], pReply[3]); printf("%08x %08x %08x %08x\n", pReply[4], pReply[5], pReply[6], pReply[7]); printf("%08x %08x %08x %08x\n\n", pReply[8], pReply[9], pReply[10], pReply[11]); } index = mpt_reply->MsgContext; } /* * Address reply with MessageContext high bit set * This is most likely a notify message so we try * to process it then free it */ if ((index & 0x80000000) != 0) { if (mpt_reply != NULL) { mpt_ctlop(mpt, mpt_reply, reply); } else { mpt_prt(mpt, "mpt_done: index 0x%x, NULL reply", index); } return; } /* Did we end up with a valid index into the table? */ if (index < 0 || index >= MPT_MAX_REQUESTS(mpt)) { mpt_prt(mpt, "mpt_done: invalid index (%x) in reply", index); return; } req = &mpt->request_pool[index]; /* Make sure memory hasn't been trashed */ if (req->index != index) { printf("mpt_done: corrupted request struct"); return; } /* Short cut for task management replys; nothing more for us to do */ mpt_req = req->req_vbuf; if (mpt_req->Function == MPI_FUNCTION_SCSI_TASK_MGMT) { if (mpt->verbose > 1) { mpt_prt(mpt, "mpt_done: TASK MGMT"); } goto done; } if (mpt_req->Function == MPI_FUNCTION_PORT_ENABLE) { goto done; } /* * At this point it better be a SCSI IO command, but don't * crash if it isn't */ if (mpt_req->Function != MPI_FUNCTION_SCSI_IO_REQUEST) { goto done; } /* Recover the CAM control block from the request structure */ ccb = req->ccb; /* Can't have had a SCSI command with out a CAM control block */ if (ccb == NULL || (ccb->ccb_h.status & CAM_SIM_QUEUED) == 0) { mpt_prt(mpt, "mpt_done: corrupted ccb, index = 0x%02x seq = 0x%08x", req->index, req->sequence); printf(" request state %s\nmpt_request:\n", mpt_req_state(req->debug)); mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf); if (mpt_reply != NULL) { printf("\nmpt_done: reply:\n"); mpt_print_reply(MPT_REPLY_PTOV(mpt, reply)); } else { printf("\nmpt_done: context reply: 0x%08x\n", reply); } goto done; } untimeout(mpttimeout, ccb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { op = BUS_DMASYNC_POSTREAD; } else { op = BUS_DMASYNC_POSTWRITE; } bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op); bus_dmamap_unload(mpt->buffer_dmat, req->dmap); } ccb->csio.resid = 0; if (mpt_reply == NULL) { /* Context reply; report that the command was successfull */ ccb->ccb_h.status = CAM_REQ_CMP; ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; if (mpt->outofbeer) { ccb->ccb_h.status |= CAM_RELEASE_SIMQ; mpt->outofbeer = 0; if (mpt->verbose > 1) { mpt_prt(mpt, "THAWQ"); } } MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); CAMLOCK_2_MPTLOCK(mpt); goto done; } ccb->csio.scsi_status = mpt_reply->SCSIStatus; switch(mpt_reply->IOCStatus) { case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* * Yikes, Tagged queue full comes through this path! * * So we'll change it to a status error and anything * that returns status should probably be a status * error as well. */ ccb->csio.resid = ccb->csio.dxfer_len - mpt_reply->TransferCount; if (mpt_reply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS) { ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; } /* Fall through */ case MPI_IOCSTATUS_SUCCESS: case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: switch (ccb->csio.scsi_status) { case SCSI_STATUS_OK: ccb->ccb_h.status = CAM_REQ_CMP; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } break; case MPI_IOCSTATUS_BUSY: case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: ccb->ccb_h.status = CAM_BUSY; break; case MPI_IOCSTATUS_SCSI_INVALID_BUS: case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: ccb->ccb_h.status = CAM_UNCOR_PARITY; break; case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: ccb->ccb_h.status = CAM_REQ_CMP; break; case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: ccb->ccb_h.status = CAM_UA_TERMIO; break; case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: ccb->ccb_h.status = CAM_REQ_TERMIO; break; case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: ccb->ccb_h.status = CAM_SCSI_BUS_RESET; break; default: ccb->ccb_h.status = CAM_UNREC_HBA_ERROR; break; } if ((mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) != 0) { if (ccb->ccb_h.flags & (CAM_SENSE_PHYS | CAM_SENSE_PTR)) { ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } else { ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.sense_resid = mpt_reply->SenseCount; bcopy(req->sense_vbuf, &ccb->csio.sense_data, ccb->csio.sense_len); } } else if (mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { ccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); } } ccb->ccb_h.status &= ~CAM_SIM_QUEUED; if (mpt->outofbeer) { ccb->ccb_h.status |= CAM_RELEASE_SIMQ; mpt->outofbeer = 0; if (mpt->verbose > 1) { mpt_prt(mpt, "THAWQ"); } } MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); CAMLOCK_2_MPTLOCK(mpt); done: /* If IOC done with this request free it up */ if (mpt_reply == NULL || (mpt_reply->MsgFlags & 0x80) == 0) mpt_free_request(mpt, req); /* If address reply; give the buffer back to the IOC */ if (mpt_reply != NULL) mpt_free_reply(mpt, (reply << 1)); } static void mpt_action(struct cam_sim *sim, union ccb *ccb) { int tgt, error; mpt_softc_t *mpt; struct ccb_trans_settings *cts; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("mpt_action\n")); mpt = (mpt_softc_t *)cam_sim_softc(sim); ccb->ccb_h.ccb_mpt_ptr = mpt; switch (ccb->ccb_h.func_code) { case XPT_RESET_BUS: if (mpt->verbose > 1) mpt_prt(mpt, "XPT_RESET_BUS"); CAMLOCK_2_MPTLOCK(mpt); error = mpt_bus_reset(ccb); switch (error) { case CAM_REQ_INPROG: MPTLOCK_2_CAMLOCK(mpt); break; case CAM_REQUEUE_REQ: if (mpt->outofbeer == 0) { mpt->outofbeer = 1; xpt_freeze_simq(sim, 1); if (mpt->verbose > 1) { mpt_prt(mpt, "FREEZEQ"); } } ccb->ccb_h.status = CAM_REQUEUE_REQ; MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); break; case CAM_REQ_CMP: ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ccb->ccb_h.status |= CAM_REQ_CMP; if (mpt->outofbeer) { ccb->ccb_h.status |= CAM_RELEASE_SIMQ; mpt->outofbeer = 0; if (mpt->verbose > 1) { mpt_prt(mpt, "THAWQ"); } } MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); break; default: ccb->ccb_h.status = CAM_REQ_CMP_ERR; MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); } break; case XPT_SCSI_IO: /* Execute the requested I/O operation */ /* * Do a couple of preliminary checks... */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /* Max supported CDB length is 16 bytes */ if (ccb->csio.cdb_len > sizeof (((PTR_MSG_SCSI_IO_REQUEST)0)->CDB)) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } ccb->csio.scsi_status = SCSI_STATUS_OK; mpt_start(ccb); break; case XPT_ABORT: /* * XXX: Need to implement */ ccb->ccb_h.status = CAM_UA_ABORT; xpt_done(ccb); break; #ifdef CAM_NEW_TRAN_CODE #define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS) #else #define IS_CURRENT_SETTINGS(c) (c->flags & CCB_TRANS_CURRENT_SETTINGS) #endif #define DP_DISC_ENABLE 0x1 #define DP_DISC_DISABL 0x2 #define DP_DISC (DP_DISC_ENABLE|DP_DISC_DISABL) #define DP_TQING_ENABLE 0x4 #define DP_TQING_DISABL 0x8 #define DP_TQING (DP_TQING_ENABLE|DP_TQING_DISABL) #define DP_WIDE 0x10 #define DP_NARROW 0x20 #define DP_WIDTH (DP_WIDE|DP_NARROW) #define DP_SYNC 0x40 case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ cts = &ccb->cts; if (!IS_CURRENT_SETTINGS(cts)) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } tgt = cts->ccb_h.target_id; if (mpt->is_fc == 0) { u_int8_t dval = 0; u_int period = 0, offset = 0; #ifndef CAM_NEW_TRAN_CODE if (cts->valid & CCB_TRANS_DISC_VALID) { dval |= DP_DISC_ENABLE; } if (cts->valid & CCB_TRANS_TQ_VALID) { dval |= DP_TQING_ENABLE; } if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) { if (cts->bus_width) dval |= DP_WIDE; else dval |= DP_NARROW; } /* * Any SYNC RATE of nonzero and SYNC_OFFSET * of nonzero will cause us to go to the * selected (from NVRAM) maximum value for * this device. At a later point, we'll * allow finer control. */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) && (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID)) { dval |= DP_SYNC; period = cts->sync_period; offset = cts->sync_offset; } #else struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) dval |= DP_DISC_ENABLE; else dval |= DP_DISC_DISABL; } if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) dval |= DP_TQING_ENABLE; else dval |= DP_TQING_DISABL; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT) dval |= DP_WIDE; else dval |= DP_NARROW; } if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) && (spi->valid & CTS_SPI_VALID_SYNC_RATE) && (spi->sync_period && spi->sync_offset)) { dval |= DP_SYNC; period = spi->sync_period; offset = spi->sync_offset; } #endif CAMLOCK_2_MPTLOCK(mpt); if (dval & DP_DISC_ENABLE) { mpt->mpt_disc_enable |= (1 << tgt); } else if (dval & DP_DISC_DISABL) { mpt->mpt_disc_enable &= ~(1 << tgt); } if (dval & DP_TQING_ENABLE) { mpt->mpt_tag_enable |= (1 << tgt); } else if (dval & DP_TQING_DISABL) { mpt->mpt_tag_enable &= ~(1 << tgt); } if (dval & DP_WIDTH) { if (mpt_setwidth(mpt, tgt, dval & DP_WIDE)) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); break; } } if (dval & DP_SYNC) { if (mpt_setsync(mpt, tgt, period, offset)) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; MPTLOCK_2_CAMLOCK(mpt); xpt_done(ccb); break; } } MPTLOCK_2_CAMLOCK(mpt); if (mpt->verbose > 1) { mpt_prt(mpt, "SET tgt %d flags %x period %x off %x", tgt, dval, period, offset); } } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: cts = &ccb->cts; tgt = cts->ccb_h.target_id; if (mpt->is_fc) { #ifndef CAM_NEW_TRAN_CODE /* * a lot of normal SCSI things don't make sense. */ cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB; cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; /* * How do you measure the width of a high * speed serial bus? Well, in bytes. * * Offset and period make no sense, though, so we set * (above) a 'base' transfer speed to be gigabit. */ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; #else struct ccb_trans_settings_fc *fc = &cts->xport_specific.fc; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_FC; cts->transport_version = 0; fc->valid = CTS_FC_VALID_SPEED; fc->bitrate = 100000; /* XXX: Need for 2Gb/s */ /* XXX: need a port database for each target */ #endif } else { #ifdef CAM_NEW_TRAN_CODE struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; #endif u_int8_t dval, pval, oval; /* * We aren't going off of Port PAGE2 params for * tagged queuing or disconnect capabilities * for current settings. For goal settings, * we assert all capabilities- we've had some * problems with reading NVRAM data. */ if (IS_CURRENT_SETTINGS(cts)) { CONFIG_PAGE_SCSI_DEVICE_0 tmp; dval = 0; tmp = mpt->mpt_dev_page0[tgt]; CAMLOCK_2_MPTLOCK(mpt); if (mpt_read_cfg_page(mpt, tgt, &tmp.Header)) { mpt_prt(mpt, "cannot get target %d DP0", tgt); } else { if (mpt->verbose > 1) { mpt_prt(mpt, "SPI Tgt %d Page 0: NParms %x Information %x", tgt, tmp.NegotiatedParameters, tmp.Information); } } MPTLOCK_2_CAMLOCK(mpt); if (tmp.NegotiatedParameters & MPI_SCSIDEVPAGE0_NP_WIDE) dval |= DP_WIDE; if (mpt->mpt_disc_enable & (1 << tgt)) { dval |= DP_DISC_ENABLE; } if (mpt->mpt_tag_enable & (1 << tgt)) { dval |= DP_TQING_ENABLE; } oval = (tmp.NegotiatedParameters >> 16) & 0xff; pval = (tmp.NegotiatedParameters >> 8) & 0xff; } else { /* * XXX: Fix wrt NVRAM someday. Attempts * XXX: to read port page2 device data * XXX: just returns zero in these areas. */ dval = DP_WIDE|DP_DISC|DP_TQING; oval = (mpt->mpt_port_page0.Capabilities >> 16); pval = (mpt->mpt_port_page0.Capabilities >> 8); } #ifndef CAM_NEW_TRAN_CODE cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if (dval & DP_DISC_ENABLE) { cts->flags |= CCB_TRANS_DISC_ENB; } if (dval & DP_TQING_ENABLE) { cts->flags |= CCB_TRANS_TAG_ENB; } if (dval & DP_WIDE) { cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } cts->valid = CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; if (oval) { cts->sync_period = pval; cts->sync_offset = oval; cts->valid |= CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; } #else cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (dval & DP_DISC_ENABLE) { spi->flags |= CTS_SPI_FLAGS_DISC_ENB; } if (dval & DP_TQING_ENABLE) { scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; } if (oval && pval) { spi->sync_offset = oval; spi->sync_period = pval; spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; spi->valid |= CTS_SPI_VALID_SYNC_RATE; } spi->valid |= CTS_SPI_VALID_BUS_WIDTH; if (dval & DP_WIDE) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; } else { spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; } if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { scsi->valid = CTS_SCSI_VALID_TQ; spi->valid |= CTS_SPI_VALID_DISC; } else { scsi->valid = 0; } #endif if (mpt->verbose > 1) { mpt_prt(mpt, "GET %s tgt %d flags %x period %x off %x", IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM", tgt, dval, pval, oval); } } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; - u_int32_t secs_per_cylinder; - u_int32_t size_mb; ccg = &ccb->ccg; if (ccg->block_size == 0) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } - size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size); - if (size_mb > 1024) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->target_sprt = 0; cpi->hba_eng_cnt = 0; cpi->max_lun = 7; cpi->bus_id = cam_sim_bus(sim); if (mpt->is_fc) { cpi->max_target = 255; cpi->hba_misc = PIM_NOBUSRESET; cpi->initiator_id = cpi->max_target + 1; cpi->base_transfer_speed = 100000; cpi->hba_inquiry = PI_TAG_ABLE; } else { cpi->initiator_id = mpt->mpt_ini_id; cpi->base_transfer_speed = 3300; cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->hba_misc = 0; cpi->max_target = 15; } strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "LSI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static int mpt_setwidth(mpt_softc_t *mpt, int tgt, int onoff) { CONFIG_PAGE_SCSI_DEVICE_1 tmp; tmp = mpt->mpt_dev_page1[tgt]; if (onoff) { tmp.RequestedParameters |= MPI_SCSIDEVPAGE1_RP_WIDE; } else { tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_WIDE; } if (mpt_write_cfg_page(mpt, tgt, &tmp.Header)) { return (-1); } if (mpt_read_cfg_page(mpt, tgt, &tmp.Header)) { return (-1); } mpt->mpt_dev_page1[tgt] = tmp; if (mpt->verbose > 1) { mpt_prt(mpt, "SPI Target %d Page 1: RequestedParameters %x Config %x", tgt, mpt->mpt_dev_page1[tgt].RequestedParameters, mpt->mpt_dev_page1[tgt].Configuration); } return (0); } static int mpt_setsync(mpt_softc_t *mpt, int tgt, int period, int offset) { CONFIG_PAGE_SCSI_DEVICE_1 tmp; tmp = mpt->mpt_dev_page1[tgt]; tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK; tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_MAX_SYNC_OFFSET_MASK; tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_DT; tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_QAS; tmp.RequestedParameters &= ~MPI_SCSIDEVPAGE1_RP_IU; /* * XXX: For now, we're ignoring specific settings */ if (period && offset) { int factor, offset, np; factor = (mpt->mpt_port_page0.Capabilities >> 8) & 0xff; offset = (mpt->mpt_port_page0.Capabilities >> 16) & 0xff; np = 0; if (factor < 0x9) { np |= MPI_SCSIDEVPAGE1_RP_QAS; np |= MPI_SCSIDEVPAGE1_RP_IU; } if (factor < 0xa) { np |= MPI_SCSIDEVPAGE1_RP_DT; } np |= (factor << 8) | (offset << 16); tmp.RequestedParameters |= np; } if (mpt_write_cfg_page(mpt, tgt, &tmp.Header)) { return (-1); } if (mpt_read_cfg_page(mpt, tgt, &tmp.Header)) { return (-1); } mpt->mpt_dev_page1[tgt] = tmp; if (mpt->verbose > 1) { mpt_prt(mpt, "SPI Target %d Page 1: RParams %x Config %x", tgt, mpt->mpt_dev_page1[tgt].RequestedParameters, mpt->mpt_dev_page1[tgt].Configuration); } return (0); } Index: head/sys/dev/sym/sym_hipd.c =================================================================== --- head/sys/dev/sym/sym_hipd.c (revision 116350) +++ head/sys/dev/sym/sym_hipd.c (revision 116351) @@ -1,10407 +1,10386 @@ /* * Device driver optimized for the Symbios/LSI 53C896/53C895A/53C1010 * PCI-SCSI controllers. * * Copyright (C) 1999-2001 Gerard Roudier * * This driver also supports the following Symbios/LSI PCI-SCSI chips: * 53C810A, 53C825A, 53C860, 53C875, 53C876, 53C885, 53C895, * 53C810, 53C815, 53C825 and the 53C1510D is 53C8XX mode. * * * This driver for FreeBSD-CAM is derived from the Linux sym53c8xx driver. * Copyright (C) 1998-1999 Gerard Roudier * * The sym53c8xx driver is derived from the ncr53c8xx driver that had been * a port of the FreeBSD ncr driver to Linux-1.2.13. * * The original ncr driver has been written for 386bsd and FreeBSD by * Wolfgang Stanglmeier * Stefan Esser * Copyright (C) 1994 Wolfgang Stanglmeier * * The initialisation code, and part of the code that addresses * FreeBSD-CAM services is based on the aic7xxx driver for FreeBSD-CAM * written by Justin T. Gibbs. * * Other major contributions: * * NVRAM detection and reading. * Copyright (C) 1997 Richard Waltham * *----------------------------------------------------------------------------- * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $FreeBSD$ */ #define SYM_DRIVER_NAME "sym-1.6.5-20000902" /* #define SYM_DEBUG_GENERIC_SUPPORT */ /* #define CAM_NEW_TRAN_CODE */ #include /* * Only use the BUS stuff for PCI under FreeBSD 4 and later versions. * Note that the old BUS stuff also works for FreeBSD 4 and spares * about 1 KB for the driver object file. */ #if __FreeBSD_version >= 400000 #define FreeBSD_Bus_Dma_Abstraction #define FreeBSD_Bus_Io_Abstraction #define FreeBSD_Bus_Space_Abstraction #endif /* * Driver configuration options. */ #include "opt_sym.h" #include #ifndef FreeBSD_Bus_Io_Abstraction #include "ncr.h" /* To know if the ncr has been configured */ #endif #include #include #include #include #ifdef FreeBSD_Bus_Io_Abstraction #include #include #endif #include #include #include #ifdef FreeBSD_Bus_Space_Abstraction #include /* * Only include bus_pio if needed. * This avoids bus space primitives to be uselessly bloated * by out-of-age PIO operations. */ #ifdef SYM_CONF_IOMAPPED #include #endif #endif #include #ifdef FreeBSD_Bus_Io_Abstraction #include #include #endif #include #include #include #include #include #include #include #include #include #include /* Short and quite clear integer types */ typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef u_int8_t u8; typedef u_int16_t u16; typedef u_int32_t u32; /* * From 'cam.error_recovery_diffs.20010313.context' patch. */ #ifdef CAM_NEW_TRAN_CODE #define FreeBSD_New_Tran_Settings #endif /* CAM_NEW_TRAN_CODE */ /* * Driver definitions. */ #include #include /* * IA32 architecture does not reorder STORES and prevents * LOADS from passing STORES. It is called `program order' * by Intel and allows device drivers to deal with memory * ordering by only ensuring that the code is not reordered * by the compiler when ordering is required. * Other architectures implement a weaker ordering that * requires memory barriers (and also IO barriers when they * make sense) to be used. */ #if defined __i386__ #define MEMORY_BARRIER() do { ; } while(0) #elif defined __alpha__ #define MEMORY_BARRIER() alpha_mb() #elif defined __powerpc__ #define MEMORY_BARRIER() __asm__ volatile("eieio; sync" : : : "memory") #elif defined __ia64__ #define MEMORY_BARRIER() __asm__ volatile("mf.a; mf" : : : "memory") #elif defined __sparc64__ #define MEMORY_BARRIER() __asm__ volatile("membar #Sync" : : : "memory") #else #error "Not supported platform" #endif /* * Portable but silly implemented byte order primitives. * We define the primitives we need, since FreeBSD doesn't * seem to have them yet. */ #if BYTE_ORDER == BIG_ENDIAN #define __revb16(x) ( (((u16)(x) & (u16)0x00ffU) << 8) | \ (((u16)(x) & (u16)0xff00U) >> 8) ) #define __revb32(x) ( (((u32)(x) & 0x000000ffU) << 24) | \ (((u32)(x) & 0x0000ff00U) << 8) | \ (((u32)(x) & 0x00ff0000U) >> 8) | \ (((u32)(x) & 0xff000000U) >> 24) ) #define __htole16(v) __revb16(v) #define __htole32(v) __revb32(v) #define __le16toh(v) __htole16(v) #define __le32toh(v) __htole32(v) static __inline u16 _htole16(u16 v) { return __htole16(v); } static __inline u32 _htole32(u32 v) { return __htole32(v); } #define _le16toh _htole16 #define _le32toh _htole32 #else /* LITTLE ENDIAN */ #define __htole16(v) (v) #define __htole32(v) (v) #define __le16toh(v) (v) #define __le32toh(v) (v) #define _htole16(v) (v) #define _htole32(v) (v) #define _le16toh(v) (v) #define _le32toh(v) (v) #endif /* BYTE_ORDER */ /* * A la VMS/CAM-3 queue management. */ typedef struct sym_quehead { struct sym_quehead *flink; /* Forward pointer */ struct sym_quehead *blink; /* Backward pointer */ } SYM_QUEHEAD; #define sym_que_init(ptr) do { \ (ptr)->flink = (ptr); (ptr)->blink = (ptr); \ } while (0) static __inline struct sym_quehead *sym_que_first(struct sym_quehead *head) { return (head->flink == head) ? 0 : head->flink; } static __inline struct sym_quehead *sym_que_last(struct sym_quehead *head) { return (head->blink == head) ? 0 : head->blink; } static __inline void __sym_que_add(struct sym_quehead * new, struct sym_quehead * blink, struct sym_quehead * flink) { flink->blink = new; new->flink = flink; new->blink = blink; blink->flink = new; } static __inline void __sym_que_del(struct sym_quehead * blink, struct sym_quehead * flink) { flink->blink = blink; blink->flink = flink; } static __inline int sym_que_empty(struct sym_quehead *head) { return head->flink == head; } static __inline void sym_que_splice(struct sym_quehead *list, struct sym_quehead *head) { struct sym_quehead *first = list->flink; if (first != list) { struct sym_quehead *last = list->blink; struct sym_quehead *at = head->flink; first->blink = head; head->flink = first; last->flink = at; at->blink = last; } } #define sym_que_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned int)(&((type *)0)->member))) #define sym_insque(new, pos) __sym_que_add(new, pos, (pos)->flink) #define sym_remque(el) __sym_que_del((el)->blink, (el)->flink) #define sym_insque_head(new, head) __sym_que_add(new, head, (head)->flink) static __inline struct sym_quehead *sym_remque_head(struct sym_quehead *head) { struct sym_quehead *elem = head->flink; if (elem != head) __sym_que_del(head, elem->flink); else elem = 0; return elem; } #define sym_insque_tail(new, head) __sym_que_add(new, (head)->blink, head) static __inline struct sym_quehead *sym_remque_tail(struct sym_quehead *head) { struct sym_quehead *elem = head->blink; if (elem != head) __sym_que_del(elem->blink, head); else elem = 0; return elem; } /* * This one may be useful. */ #define FOR_EACH_QUEUED_ELEMENT(head, qp) \ for (qp = (head)->flink; qp != (head); qp = qp->flink) /* * FreeBSD does not offer our kind of queue in the CAM CCB. * So, we have to cast. */ #define sym_qptr(p) ((struct sym_quehead *) (p)) /* * Simple bitmap operations. */ #define sym_set_bit(p, n) (((u32 *)(p))[(n)>>5] |= (1<<((n)&0x1f))) #define sym_clr_bit(p, n) (((u32 *)(p))[(n)>>5] &= ~(1<<((n)&0x1f))) #define sym_is_bit(p, n) (((u32 *)(p))[(n)>>5] & (1<<((n)&0x1f))) /* * Number of tasks per device we want to handle. */ #if SYM_CONF_MAX_TAG_ORDER > 8 #error "more than 256 tags per logical unit not allowed." #endif #define SYM_CONF_MAX_TASK (1< SYM_CONF_MAX_TASK #undef SYM_CONF_MAX_TAG #define SYM_CONF_MAX_TAG SYM_CONF_MAX_TASK #endif /* * This one means 'NO TAG for this job' */ #define NO_TAG (256) /* * Number of SCSI targets. */ #if SYM_CONF_MAX_TARGET > 16 #error "more than 16 targets not allowed." #endif /* * Number of logical units per target. */ #if SYM_CONF_MAX_LUN > 64 #error "more than 64 logical units per target not allowed." #endif /* * Asynchronous pre-scaler (ns). Shall be 40 for * the SCSI timings to be compliant. */ #define SYM_CONF_MIN_ASYNC (40) /* * Number of entries in the START and DONE queues. * * We limit to 1 PAGE in order to succeed allocation of * these queues. Each entry is 8 bytes long (2 DWORDS). */ #ifdef SYM_CONF_MAX_START #define SYM_CONF_MAX_QUEUE (SYM_CONF_MAX_START+2) #else #define SYM_CONF_MAX_QUEUE (7*SYM_CONF_MAX_TASK+2) #define SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2) #endif #if SYM_CONF_MAX_QUEUE > PAGE_SIZE/8 #undef SYM_CONF_MAX_QUEUE #define SYM_CONF_MAX_QUEUE PAGE_SIZE/8 #undef SYM_CONF_MAX_START #define SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2) #endif /* * For this one, we want a short name :-) */ #define MAX_QUEUE SYM_CONF_MAX_QUEUE /* * Active debugging tags and verbosity. */ #define DEBUG_ALLOC (0x0001) #define DEBUG_PHASE (0x0002) #define DEBUG_POLL (0x0004) #define DEBUG_QUEUE (0x0008) #define DEBUG_RESULT (0x0010) #define DEBUG_SCATTER (0x0020) #define DEBUG_SCRIPT (0x0040) #define DEBUG_TINY (0x0080) #define DEBUG_TIMING (0x0100) #define DEBUG_NEGO (0x0200) #define DEBUG_TAGS (0x0400) #define DEBUG_POINTER (0x0800) #if 0 static int sym_debug = 0; #define DEBUG_FLAGS sym_debug #else /* #define DEBUG_FLAGS (0x0631) */ #define DEBUG_FLAGS (0x0000) #endif #define sym_verbose (np->verbose) /* * Insert a delay in micro-seconds and milli-seconds. */ static void UDELAY(int us) { DELAY(us); } static void MDELAY(int ms) { while (ms--) UDELAY(1000); } /* * Simple power of two buddy-like allocator. * * This simple code is not intended to be fast, but to * provide power of 2 aligned memory allocations. * Since the SCRIPTS processor only supplies 8 bit arithmetic, * this allocator allows simple and fast address calculations * from the SCRIPTS code. In addition, cache line alignment * is guaranteed for power of 2 cache line size. * * This allocator has been developped for the Linux sym53c8xx * driver, since this O/S does not provide naturally aligned * allocations. * It has the advantage of allowing the driver to use private * pages of memory that will be useful if we ever need to deal * with IO MMUs for PCI. */ #define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */ #define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */ #if 0 #define MEMO_FREE_UNUSED /* Free unused pages immediately */ #endif #define MEMO_WARN 1 #define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER) #define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT) #define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1) #define get_pages() malloc(MEMO_CLUSTER_SIZE, M_DEVBUF, M_NOWAIT) #define free_pages(p) free((p), M_DEVBUF) typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */ typedef struct m_link { /* Link between free memory chunks */ struct m_link *next; } m_link_s; #ifdef FreeBSD_Bus_Dma_Abstraction typedef struct m_vtob { /* Virtual to Bus address translation */ struct m_vtob *next; bus_dmamap_t dmamap; /* Map for this chunk */ m_addr_t vaddr; /* Virtual address */ m_addr_t baddr; /* Bus physical address */ } m_vtob_s; /* Hash this stuff a bit to speed up translations */ #define VTOB_HASH_SHIFT 5 #define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT) #define VTOB_HASH_MASK (VTOB_HASH_SIZE-1) #define VTOB_HASH_CODE(m) \ ((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK) #endif typedef struct m_pool { /* Memory pool of a given kind */ #ifdef FreeBSD_Bus_Dma_Abstraction bus_dma_tag_t dev_dmat; /* Identifies the pool */ bus_dma_tag_t dmat; /* Tag for our fixed allocations */ m_addr_t (*getp)(struct m_pool *); #ifdef MEMO_FREE_UNUSED void (*freep)(struct m_pool *, m_addr_t); #endif #define M_GETP() mp->getp(mp) #define M_FREEP(p) mp->freep(mp, p) int nump; m_vtob_s *(vtob[VTOB_HASH_SIZE]); struct m_pool *next; #else #define M_GETP() get_pages() #define M_FREEP(p) free_pages(p) #endif /* FreeBSD_Bus_Dma_Abstraction */ struct m_link h[MEMO_CLUSTER_SHIFT - MEMO_SHIFT + 1]; } m_pool_s; static void *___sym_malloc(m_pool_s *mp, int size) { int i = 0; int s = (1 << MEMO_SHIFT); int j; m_addr_t a; m_link_s *h = mp->h; if (size > MEMO_CLUSTER_SIZE) return 0; while (size > s) { s <<= 1; ++i; } j = i; while (!h[j].next) { if (s == MEMO_CLUSTER_SIZE) { h[j].next = (m_link_s *) M_GETP(); if (h[j].next) h[j].next->next = 0; break; } ++j; s <<= 1; } a = (m_addr_t) h[j].next; if (a) { h[j].next = h[j].next->next; while (j > i) { j -= 1; s >>= 1; h[j].next = (m_link_s *) (a+s); h[j].next->next = 0; } } #ifdef DEBUG printf("___sym_malloc(%d) = %p\n", size, (void *) a); #endif return (void *) a; } static void ___sym_mfree(m_pool_s *mp, void *ptr, int size) { int i = 0; int s = (1 << MEMO_SHIFT); m_link_s *q; m_addr_t a, b; m_link_s *h = mp->h; #ifdef DEBUG printf("___sym_mfree(%p, %d)\n", ptr, size); #endif if (size > MEMO_CLUSTER_SIZE) return; while (size > s) { s <<= 1; ++i; } a = (m_addr_t) ptr; while (1) { #ifdef MEMO_FREE_UNUSED if (s == MEMO_CLUSTER_SIZE) { M_FREEP(a); break; } #endif b = a ^ s; q = &h[i]; while (q->next && q->next != (m_link_s *) b) { q = q->next; } if (!q->next) { ((m_link_s *) a)->next = h[i].next; h[i].next = (m_link_s *) a; break; } q->next = q->next->next; a = a & b; s <<= 1; ++i; } } static void *__sym_calloc2(m_pool_s *mp, int size, char *name, int uflags) { void *p; p = ___sym_malloc(mp, size); if (DEBUG_FLAGS & DEBUG_ALLOC) printf ("new %-10s[%4d] @%p.\n", name, size, p); if (p) bzero(p, size); else if (uflags & MEMO_WARN) printf ("__sym_calloc2: failed to allocate %s[%d]\n", name, size); return p; } #define __sym_calloc(mp, s, n) __sym_calloc2(mp, s, n, MEMO_WARN) static void __sym_mfree(m_pool_s *mp, void *ptr, int size, char *name) { if (DEBUG_FLAGS & DEBUG_ALLOC) printf ("freeing %-10s[%4d] @%p.\n", name, size, ptr); ___sym_mfree(mp, ptr, size); } /* * Default memory pool we donnot need to involve in DMA. */ #ifndef FreeBSD_Bus_Dma_Abstraction /* * Without the `bus dma abstraction', all the memory is assumed * DMAable and a single pool is all what we need. */ static m_pool_s mp0; #else /* * With the `bus dma abstraction', we use a separate pool for * memory we donnot need to involve in DMA. */ static m_addr_t ___mp0_getp(m_pool_s *mp) { m_addr_t m = (m_addr_t) get_pages(); if (m) ++mp->nump; return m; } #ifdef MEMO_FREE_UNUSED static void ___mp0_freep(m_pool_s *mp, m_addr_t m) { free_pages(m); --mp->nump; } #endif #ifdef MEMO_FREE_UNUSED static m_pool_s mp0 = {0, 0, ___mp0_getp, ___mp0_freep}; #else static m_pool_s mp0 = {0, 0, ___mp0_getp}; #endif #endif /* FreeBSD_Bus_Dma_Abstraction */ /* * Actual memory allocation routine for non-DMAed memory. */ static void *sym_calloc(int size, char *name) { void *m; /* Lock */ m = __sym_calloc(&mp0, size, name); /* Unlock */ return m; } /* * Actual memory allocation routine for non-DMAed memory. */ static void sym_mfree(void *ptr, int size, char *name) { /* Lock */ __sym_mfree(&mp0, ptr, size, name); /* Unlock */ } /* * DMAable pools. */ #ifndef FreeBSD_Bus_Dma_Abstraction /* * Without `bus dma abstraction', all the memory is DMAable, and * only a single pool is needed (vtophys() is our friend). */ #define __sym_calloc_dma(b, s, n) sym_calloc(s, n) #define __sym_mfree_dma(b, p, s, n) sym_mfree(p, s, n) #ifdef __alpha__ #define __vtobus(b, p) alpha_XXX_dmamap((vm_offset_t)(p)) #else /*__i386__, __sparc64__*/ #define __vtobus(b, p) vtophys(p) #endif #else /* * With `bus dma abstraction', we use a separate pool per parent * BUS handle. A reverse table (hashed) is maintained for virtual * to BUS address translation. */ static void getbaddrcb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *baddr; baddr = (bus_addr_t *)arg; *baddr = segs->ds_addr; } static m_addr_t ___dma_getp(m_pool_s *mp) { m_vtob_s *vbp; void *vaddr = 0; bus_addr_t baddr = 0; vbp = __sym_calloc(&mp0, sizeof(*vbp), "VTOB"); if (!vbp) goto out_err; if (bus_dmamem_alloc(mp->dmat, &vaddr, BUS_DMA_NOWAIT, &vbp->dmamap)) goto out_err; bus_dmamap_load(mp->dmat, vbp->dmamap, vaddr, MEMO_CLUSTER_SIZE, getbaddrcb, &baddr, 0); if (baddr) { int hc = VTOB_HASH_CODE(vaddr); vbp->vaddr = (m_addr_t) vaddr; vbp->baddr = (m_addr_t) baddr; vbp->next = mp->vtob[hc]; mp->vtob[hc] = vbp; ++mp->nump; return (m_addr_t) vaddr; } out_err: if (baddr) bus_dmamap_unload(mp->dmat, vbp->dmamap); if (vaddr) bus_dmamem_free(mp->dmat, vaddr, vbp->dmamap); if (vbp->dmamap) bus_dmamap_destroy(mp->dmat, vbp->dmamap); if (vbp) __sym_mfree(&mp0, vbp, sizeof(*vbp), "VTOB"); return 0; } #ifdef MEMO_FREE_UNUSED static void ___dma_freep(m_pool_s *mp, m_addr_t m) { m_vtob_s **vbpp, *vbp; int hc = VTOB_HASH_CODE(m); vbpp = &mp->vtob[hc]; while (*vbpp && (*vbpp)->vaddr != m) vbpp = &(*vbpp)->next; if (*vbpp) { vbp = *vbpp; *vbpp = (*vbpp)->next; bus_dmamap_unload(mp->dmat, vbp->dmamap); bus_dmamem_free(mp->dmat, (void *) vbp->vaddr, vbp->dmamap); bus_dmamap_destroy(mp->dmat, vbp->dmamap); __sym_mfree(&mp0, vbp, sizeof(*vbp), "VTOB"); --mp->nump; } } #endif static __inline m_pool_s *___get_dma_pool(bus_dma_tag_t dev_dmat) { m_pool_s *mp; for (mp = mp0.next; mp && mp->dev_dmat != dev_dmat; mp = mp->next); return mp; } static m_pool_s *___cre_dma_pool(bus_dma_tag_t dev_dmat) { m_pool_s *mp = 0; mp = __sym_calloc(&mp0, sizeof(*mp), "MPOOL"); if (mp) { mp->dev_dmat = dev_dmat; if (!bus_dma_tag_create(dev_dmat, 1, MEMO_CLUSTER_SIZE, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR_32BIT, NULL, NULL, MEMO_CLUSTER_SIZE, 1, MEMO_CLUSTER_SIZE, 0, &mp->dmat)) { mp->getp = ___dma_getp; #ifdef MEMO_FREE_UNUSED mp->freep = ___dma_freep; #endif mp->next = mp0.next; mp0.next = mp; return mp; } } if (mp) __sym_mfree(&mp0, mp, sizeof(*mp), "MPOOL"); return 0; } #ifdef MEMO_FREE_UNUSED static void ___del_dma_pool(m_pool_s *p) { struct m_pool **pp = &mp0.next; while (*pp && *pp != p) pp = &(*pp)->next; if (*pp) { *pp = (*pp)->next; bus_dma_tag_destroy(p->dmat); __sym_mfree(&mp0, p, sizeof(*p), "MPOOL"); } } #endif static void *__sym_calloc_dma(bus_dma_tag_t dev_dmat, int size, char *name) { struct m_pool *mp; void *m = 0; /* Lock */ mp = ___get_dma_pool(dev_dmat); if (!mp) mp = ___cre_dma_pool(dev_dmat); if (mp) m = __sym_calloc(mp, size, name); #ifdef MEMO_FREE_UNUSED if (mp && !mp->nump) ___del_dma_pool(mp); #endif /* Unlock */ return m; } static void __sym_mfree_dma(bus_dma_tag_t dev_dmat, void *m, int size, char *name) { struct m_pool *mp; /* Lock */ mp = ___get_dma_pool(dev_dmat); if (mp) __sym_mfree(mp, m, size, name); #ifdef MEMO_FREE_UNUSED if (mp && !mp->nump) ___del_dma_pool(mp); #endif /* Unlock */ } static m_addr_t __vtobus(bus_dma_tag_t dev_dmat, void *m) { m_pool_s *mp; int hc = VTOB_HASH_CODE(m); m_vtob_s *vp = 0; m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK; /* Lock */ mp = ___get_dma_pool(dev_dmat); if (mp) { vp = mp->vtob[hc]; while (vp && (m_addr_t) vp->vaddr != a) vp = vp->next; } /* Unlock */ if (!vp) panic("sym: VTOBUS FAILED!\n"); return vp ? vp->baddr + (((m_addr_t) m) - a) : 0; } #endif /* FreeBSD_Bus_Dma_Abstraction */ /* * Verbs for DMAable memory handling. * The _uvptv_ macro avoids a nasty warning about pointer to volatile * being discarded. */ #define _uvptv_(p) ((void *)((vm_offset_t)(p))) #define _sym_calloc_dma(np, s, n) __sym_calloc_dma(np->bus_dmat, s, n) #define _sym_mfree_dma(np, p, s, n) \ __sym_mfree_dma(np->bus_dmat, _uvptv_(p), s, n) #define sym_calloc_dma(s, n) _sym_calloc_dma(np, s, n) #define sym_mfree_dma(p, s, n) _sym_mfree_dma(np, p, s, n) #define _vtobus(np, p) __vtobus(np->bus_dmat, _uvptv_(p)) #define vtobus(p) _vtobus(np, p) /* * Print a buffer in hexadecimal format. */ static void sym_printb_hex (u_char *p, int n) { while (n-- > 0) printf (" %x", *p++); } /* * Same with a label at beginning and .\n at end. */ static void sym_printl_hex (char *label, u_char *p, int n) { printf ("%s", label); sym_printb_hex (p, n); printf (".\n"); } /* * Return a string for SCSI BUS mode. */ static char *sym_scsi_bus_mode(int mode) { switch(mode) { case SMODE_HVD: return "HVD"; case SMODE_SE: return "SE"; case SMODE_LVD: return "LVD"; } return "??"; } /* * Some poor and bogus sync table that refers to Tekram NVRAM layout. */ #ifdef SYM_CONF_NVRAM_SUPPORT static u_char Tekram_sync[16] = {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10}; #endif /* * Union of supported NVRAM formats. */ struct sym_nvram { int type; #define SYM_SYMBIOS_NVRAM (1) #define SYM_TEKRAM_NVRAM (2) #ifdef SYM_CONF_NVRAM_SUPPORT union { Symbios_nvram Symbios; Tekram_nvram Tekram; } data; #endif }; /* * This one is hopefully useless, but actually useful. :-) */ #ifndef assert #define assert(expression) { \ if (!(expression)) { \ (void)panic( \ "assertion \"%s\" failed: file \"%s\", line %d\n", \ #expression, \ __FILE__, __LINE__); \ } \ } #endif /* * Some provision for a possible big endian mode supported by * Symbios chips (never seen, by the way). * For now, this stuff does not deserve any comments. :) */ #define sym_offb(o) (o) #define sym_offw(o) (o) /* * Some provision for support for BIG ENDIAN CPU. * Btw, FreeBSD does not seem to be ready yet for big endian. */ #if BYTE_ORDER == BIG_ENDIAN #define cpu_to_scr(dw) _htole32(dw) #define scr_to_cpu(dw) _le32toh(dw) #else #define cpu_to_scr(dw) (dw) #define scr_to_cpu(dw) (dw) #endif /* * Access to the chip IO registers and on-chip RAM. * We use the `bus space' interface under FreeBSD-4 and * later kernel versions. */ #ifdef FreeBSD_Bus_Space_Abstraction #if defined(SYM_CONF_IOMAPPED) #define INB_OFF(o) bus_space_read_1(np->io_tag, np->io_bsh, o) #define INW_OFF(o) bus_space_read_2(np->io_tag, np->io_bsh, o) #define INL_OFF(o) bus_space_read_4(np->io_tag, np->io_bsh, o) #define OUTB_OFF(o, v) bus_space_write_1(np->io_tag, np->io_bsh, o, (v)) #define OUTW_OFF(o, v) bus_space_write_2(np->io_tag, np->io_bsh, o, (v)) #define OUTL_OFF(o, v) bus_space_write_4(np->io_tag, np->io_bsh, o, (v)) #else /* Memory mapped IO */ #define INB_OFF(o) bus_space_read_1(np->mmio_tag, np->mmio_bsh, o) #define INW_OFF(o) bus_space_read_2(np->mmio_tag, np->mmio_bsh, o) #define INL_OFF(o) bus_space_read_4(np->mmio_tag, np->mmio_bsh, o) #define OUTB_OFF(o, v) bus_space_write_1(np->mmio_tag, np->mmio_bsh, o, (v)) #define OUTW_OFF(o, v) bus_space_write_2(np->mmio_tag, np->mmio_bsh, o, (v)) #define OUTL_OFF(o, v) bus_space_write_4(np->mmio_tag, np->mmio_bsh, o, (v)) #endif /* SYM_CONF_IOMAPPED */ #define OUTRAM_OFF(o, a, l) \ bus_space_write_region_1(np->ram_tag, np->ram_bsh, o, (a), (l)) #else /* not defined FreeBSD_Bus_Space_Abstraction */ #if BYTE_ORDER == BIG_ENDIAN #error "BIG ENDIAN support requires bus space kernel interface" #endif /* * Access to the chip IO registers and on-chip RAM. * We use legacy MMIO and IO interface for FreeBSD 3.X versions. */ /* * Define some understable verbs for IO and MMIO. */ #define io_read8(p) scr_to_cpu(inb((p))) #define io_read16(p) scr_to_cpu(inw((p))) #define io_read32(p) scr_to_cpu(inl((p))) #define io_write8(p, v) outb((p), cpu_to_scr(v)) #define io_write16(p, v) outw((p), cpu_to_scr(v)) #define io_write32(p, v) outl((p), cpu_to_scr(v)) #ifdef __alpha__ #define mmio_read8(a) readb(a) #define mmio_read16(a) readw(a) #define mmio_read32(a) readl(a) #define mmio_write8(a, b) writeb(a, b) #define mmio_write16(a, b) writew(a, b) #define mmio_write32(a, b) writel(a, b) #define memcpy_to_pci(d, s, n) memcpy_toio((u32)(d), (void *)(s), (n)) #else /*__i386__, __sparc64__*/ #define mmio_read8(a) scr_to_cpu((*(volatile unsigned char *) (a))) #define mmio_read16(a) scr_to_cpu((*(volatile unsigned short *) (a))) #define mmio_read32(a) scr_to_cpu((*(volatile unsigned int *) (a))) #define mmio_write8(a, b) (*(volatile unsigned char *) (a)) = cpu_to_scr(b) #define mmio_write16(a, b) (*(volatile unsigned short *) (a)) = cpu_to_scr(b) #define mmio_write32(a, b) (*(volatile unsigned int *) (a)) = cpu_to_scr(b) #define memcpy_to_pci(d, s, n) bcopy((s), (void *)(d), (n)) #endif /* * Normal IO */ #if defined(SYM_CONF_IOMAPPED) #define INB_OFF(o) io_read8(np->io_port + sym_offb(o)) #define OUTB_OFF(o, v) io_write8(np->io_port + sym_offb(o), (v)) #define INW_OFF(o) io_read16(np->io_port + sym_offw(o)) #define OUTW_OFF(o, v) io_write16(np->io_port + sym_offw(o), (v)) #define INL_OFF(o) io_read32(np->io_port + (o)) #define OUTL_OFF(o, v) io_write32(np->io_port + (o), (v)) #else /* Memory mapped IO */ #define INB_OFF(o) mmio_read8(np->mmio_va + sym_offb(o)) #define OUTB_OFF(o, v) mmio_write8(np->mmio_va + sym_offb(o), (v)) #define INW_OFF(o) mmio_read16(np->mmio_va + sym_offw(o)) #define OUTW_OFF(o, v) mmio_write16(np->mmio_va + sym_offw(o), (v)) #define INL_OFF(o) mmio_read32(np->mmio_va + (o)) #define OUTL_OFF(o, v) mmio_write32(np->mmio_va + (o), (v)) #endif #define OUTRAM_OFF(o, a, l) memcpy_to_pci(np->ram_va + (o), (a), (l)) #endif /* FreeBSD_Bus_Space_Abstraction */ /* * Common definitions for both bus space and legacy IO methods. */ #define INB(r) INB_OFF(offsetof(struct sym_reg,r)) #define INW(r) INW_OFF(offsetof(struct sym_reg,r)) #define INL(r) INL_OFF(offsetof(struct sym_reg,r)) #define OUTB(r, v) OUTB_OFF(offsetof(struct sym_reg,r), (v)) #define OUTW(r, v) OUTW_OFF(offsetof(struct sym_reg,r), (v)) #define OUTL(r, v) OUTL_OFF(offsetof(struct sym_reg,r), (v)) #define OUTONB(r, m) OUTB(r, INB(r) | (m)) #define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m)) #define OUTONW(r, m) OUTW(r, INW(r) | (m)) #define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m)) #define OUTONL(r, m) OUTL(r, INL(r) | (m)) #define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m)) /* * We normally want the chip to have a consistent view * of driver internal data structures when we restart it. * Thus these macros. */ #define OUTL_DSP(v) \ do { \ MEMORY_BARRIER(); \ OUTL (nc_dsp, (v)); \ } while (0) #define OUTONB_STD() \ do { \ MEMORY_BARRIER(); \ OUTONB (nc_dcntl, (STD|NOCOM)); \ } while (0) /* * Command control block states. */ #define HS_IDLE (0) #define HS_BUSY (1) #define HS_NEGOTIATE (2) /* sync/wide data transfer*/ #define HS_DISCONNECT (3) /* Disconnected by target */ #define HS_WAIT (4) /* waiting for resource */ #define HS_DONEMASK (0x80) #define HS_COMPLETE (4|HS_DONEMASK) #define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */ #define HS_UNEXPECTED (6|HS_DONEMASK) /* Unexpected disconnect */ #define HS_COMP_ERR (7|HS_DONEMASK) /* Completed with error */ /* * Software Interrupt Codes */ #define SIR_BAD_SCSI_STATUS (1) #define SIR_SEL_ATN_NO_MSG_OUT (2) #define SIR_MSG_RECEIVED (3) #define SIR_MSG_WEIRD (4) #define SIR_NEGO_FAILED (5) #define SIR_NEGO_PROTO (6) #define SIR_SCRIPT_STOPPED (7) #define SIR_REJECT_TO_SEND (8) #define SIR_SWIDE_OVERRUN (9) #define SIR_SODL_UNDERRUN (10) #define SIR_RESEL_NO_MSG_IN (11) #define SIR_RESEL_NO_IDENTIFY (12) #define SIR_RESEL_BAD_LUN (13) #define SIR_TARGET_SELECTED (14) #define SIR_RESEL_BAD_I_T_L (15) #define SIR_RESEL_BAD_I_T_L_Q (16) #define SIR_ABORT_SENT (17) #define SIR_RESEL_ABORTED (18) #define SIR_MSG_OUT_DONE (19) #define SIR_COMPLETE_ERROR (20) #define SIR_DATA_OVERRUN (21) #define SIR_BAD_PHASE (22) #define SIR_MAX (22) /* * Extended error bit codes. * xerr_status field of struct sym_ccb. */ #define XE_EXTRA_DATA (1) /* unexpected data phase */ #define XE_BAD_PHASE (1<<1) /* illegal phase (4/5) */ #define XE_PARITY_ERR (1<<2) /* unrecovered SCSI parity error */ #define XE_SODL_UNRUN (1<<3) /* ODD transfer in DATA OUT phase */ #define XE_SWIDE_OVRUN (1<<4) /* ODD transfer in DATA IN phase */ /* * Negotiation status. * nego_status field of struct sym_ccb. */ #define NS_SYNC (1) #define NS_WIDE (2) #define NS_PPR (3) /* * A CCB hashed table is used to retrieve CCB address * from DSA value. */ #define CCB_HASH_SHIFT 8 #define CCB_HASH_SIZE (1UL << CCB_HASH_SHIFT) #define CCB_HASH_MASK (CCB_HASH_SIZE-1) #define CCB_HASH_CODE(dsa) (((dsa) >> 9) & CCB_HASH_MASK) /* * Device flags. */ #define SYM_DISC_ENABLED (1) #define SYM_TAGS_ENABLED (1<<1) #define SYM_SCAN_BOOT_DISABLED (1<<2) #define SYM_SCAN_LUNS_DISABLED (1<<3) /* * Host adapter miscellaneous flags. */ #define SYM_AVOID_BUS_RESET (1) #define SYM_SCAN_TARGETS_HILO (1<<1) /* * Device quirks. * Some devices, for example the CHEETAH 2 LVD, disconnects without * saving the DATA POINTER then reselects and terminates the IO. * On reselection, the automatic RESTORE DATA POINTER makes the * CURRENT DATA POINTER not point at the end of the IO. * This behaviour just breaks our calculation of the residual. * For now, we just force an AUTO SAVE on disconnection and will * fix that in a further driver version. */ #define SYM_QUIRK_AUTOSAVE 1 /* * Misc. */ #define SYM_SNOOP_TIMEOUT (10000000) #define SYM_PCI_IO PCIR_MAPS #define SYM_PCI_MMIO (PCIR_MAPS + 4) #define SYM_PCI_RAM (PCIR_MAPS + 8) #define SYM_PCI_RAM64 (PCIR_MAPS + 12) /* * Back-pointer from the CAM CCB to our data structures. */ #define sym_hcb_ptr spriv_ptr0 /* #define sym_ccb_ptr spriv_ptr1 */ /* * We mostly have to deal with pointers. * Thus these typedef's. */ typedef struct sym_tcb *tcb_p; typedef struct sym_lcb *lcb_p; typedef struct sym_ccb *ccb_p; typedef struct sym_hcb *hcb_p; /* * Gather negotiable parameters value */ struct sym_trans { #ifdef FreeBSD_New_Tran_Settings u8 scsi_version; u8 spi_version; #endif u8 period; u8 offset; u8 width; u8 options; /* PPR options */ }; struct sym_tinfo { struct sym_trans current; struct sym_trans goal; struct sym_trans user; }; #define BUS_8_BIT MSG_EXT_WDTR_BUS_8_BIT #define BUS_16_BIT MSG_EXT_WDTR_BUS_16_BIT /* * Global TCB HEADER. * * Due to lack of indirect addressing on earlier NCR chips, * this substructure is copied from the TCB to a global * address after selection. * For SYMBIOS chips that support LOAD/STORE this copy is * not needed and thus not performed. */ struct sym_tcbh { /* * Scripts bus addresses of LUN table accessed from scripts. * LUN #0 is a special case, since multi-lun devices are rare, * and we we want to speed-up the general case and not waste * resources. */ u32 luntbl_sa; /* bus address of this table */ u32 lun0_sa; /* bus address of LCB #0 */ /* * Actual SYNC/WIDE IO registers value for this target. * 'sval', 'wval' and 'uval' are read from SCRIPTS and * so have alignment constraints. */ /*0*/ u_char uval; /* -> SCNTL4 register */ /*1*/ u_char sval; /* -> SXFER io register */ /*2*/ u_char filler1; /*3*/ u_char wval; /* -> SCNTL3 io register */ }; /* * Target Control Block */ struct sym_tcb { /* * TCB header. * Assumed at offset 0. */ /*0*/ struct sym_tcbh head; /* * LUN table used by the SCRIPTS processor. * An array of bus addresses is used on reselection. */ u32 *luntbl; /* LCBs bus address table */ /* * LUN table used by the C code. */ lcb_p lun0p; /* LCB of LUN #0 (usual case) */ #if SYM_CONF_MAX_LUN > 1 lcb_p *lunmp; /* Other LCBs [1..MAX_LUN] */ #endif /* * Bitmap that tells about LUNs that succeeded at least * 1 IO and therefore assumed to be a real device. * Avoid useless allocation of the LCB structure. */ u32 lun_map[(SYM_CONF_MAX_LUN+31)/32]; /* * Bitmap that tells about LUNs that haven't yet an LCB * allocated (not discovered or LCB allocation failed). */ u32 busy0_map[(SYM_CONF_MAX_LUN+31)/32]; /* * Transfer capabilities (SIP) */ struct sym_tinfo tinfo; /* * Keep track of the CCB used for the negotiation in order * to ensure that only 1 negotiation is queued at a time. */ ccb_p nego_cp; /* CCB used for the nego */ /* * Set when we want to reset the device. */ u_char to_reset; /* * Other user settable limits and options. * These limits are read from the NVRAM if present. */ u_char usrflags; u_short usrtags; }; /* * Global LCB HEADER. * * Due to lack of indirect addressing on earlier NCR chips, * this substructure is copied from the LCB to a global * address after selection. * For SYMBIOS chips that support LOAD/STORE this copy is * not needed and thus not performed. */ struct sym_lcbh { /* * SCRIPTS address jumped by SCRIPTS on reselection. * For not probed logical units, this address points to * SCRIPTS that deal with bad LU handling (must be at * offset zero of the LCB for that reason). */ /*0*/ u32 resel_sa; /* * Task (bus address of a CCB) read from SCRIPTS that points * to the unique ITL nexus allowed to be disconnected. */ u32 itl_task_sa; /* * Task table bus address (read from SCRIPTS). */ u32 itlq_tbl_sa; }; /* * Logical Unit Control Block */ struct sym_lcb { /* * TCB header. * Assumed at offset 0. */ /*0*/ struct sym_lcbh head; /* * Task table read from SCRIPTS that contains pointers to * ITLQ nexuses. The bus address read from SCRIPTS is * inside the header. */ u32 *itlq_tbl; /* Kernel virtual address */ /* * Busy CCBs management. */ u_short busy_itlq; /* Number of busy tagged CCBs */ u_short busy_itl; /* Number of busy untagged CCBs */ /* * Circular tag allocation buffer. */ u_short ia_tag; /* Tag allocation index */ u_short if_tag; /* Tag release index */ u_char *cb_tags; /* Circular tags buffer */ /* * Set when we want to clear all tasks. */ u_char to_clear; /* * Capabilities. */ u_char user_flags; u_char current_flags; }; /* * Action from SCRIPTS on a task. * Is part of the CCB, but is also used separately to plug * error handling action to perform from SCRIPTS. */ struct sym_actscr { u32 start; /* Jumped by SCRIPTS after selection */ u32 restart; /* Jumped by SCRIPTS on relection */ }; /* * Phase mismatch context. * * It is part of the CCB and is used as parameters for the * DATA pointer. We need two contexts to handle correctly the * SAVED DATA POINTER. */ struct sym_pmc { struct sym_tblmove sg; /* Updated interrupted SG block */ u32 ret; /* SCRIPT return address */ }; /* * LUN control block lookup. * We use a direct pointer for LUN #0, and a table of * pointers which is only allocated for devices that support * LUN(s) > 0. */ #if SYM_CONF_MAX_LUN <= 1 #define sym_lp(np, tp, lun) (!lun) ? (tp)->lun0p : 0 #else #define sym_lp(np, tp, lun) \ (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : 0 #endif /* * Status are used by the host and the script processor. * * The last four bytes (status[4]) are copied to the * scratchb register (declared as scr0..scr3) just after the * select/reselect, and copied back just after disconnecting. * Inside the script the XX_REG are used. */ /* * Last four bytes (script) */ #define QU_REG scr0 #define HS_REG scr1 #define HS_PRT nc_scr1 #define SS_REG scr2 #define SS_PRT nc_scr2 #define HF_REG scr3 #define HF_PRT nc_scr3 /* * Last four bytes (host) */ #define actualquirks phys.head.status[0] #define host_status phys.head.status[1] #define ssss_status phys.head.status[2] #define host_flags phys.head.status[3] /* * Host flags */ #define HF_IN_PM0 1u #define HF_IN_PM1 (1u<<1) #define HF_ACT_PM (1u<<2) #define HF_DP_SAVED (1u<<3) #define HF_SENSE (1u<<4) #define HF_EXT_ERR (1u<<5) #define HF_DATA_IN (1u<<6) #ifdef SYM_CONF_IARB_SUPPORT #define HF_HINT_IARB (1u<<7) #endif /* * Global CCB HEADER. * * Due to lack of indirect addressing on earlier NCR chips, * this substructure is copied from the ccb to a global * address after selection (or reselection) and copied back * before disconnect. * For SYMBIOS chips that support LOAD/STORE this copy is * not needed and thus not performed. */ struct sym_ccbh { /* * Start and restart SCRIPTS addresses (must be at 0). */ /*0*/ struct sym_actscr go; /* * SCRIPTS jump address that deal with data pointers. * 'savep' points to the position in the script responsible * for the actual transfer of data. * It's written on reception of a SAVE_DATA_POINTER message. */ u32 savep; /* Jump address to saved data pointer */ u32 lastp; /* SCRIPTS address at end of data */ u32 goalp; /* Not accessed for now from SCRIPTS */ /* * Status fields. */ u8 status[4]; }; /* * Data Structure Block * * During execution of a ccb by the script processor, the * DSA (data structure address) register points to this * substructure of the ccb. */ struct sym_dsb { /* * CCB header. * Also assumed at offset 0 of the sym_ccb structure. */ /*0*/ struct sym_ccbh head; /* * Phase mismatch contexts. * We need two to handle correctly the SAVED DATA POINTER. * MUST BOTH BE AT OFFSET < 256, due to using 8 bit arithmetic * for address calculation from SCRIPTS. */ struct sym_pmc pm0; struct sym_pmc pm1; /* * Table data for Script */ struct sym_tblsel select; struct sym_tblmove smsg; struct sym_tblmove smsg_ext; struct sym_tblmove cmd; struct sym_tblmove sense; struct sym_tblmove wresid; struct sym_tblmove data [SYM_CONF_MAX_SG]; }; /* * Our Command Control Block */ struct sym_ccb { /* * This is the data structure which is pointed by the DSA * register when it is executed by the script processor. * It must be the first entry. */ struct sym_dsb phys; /* * Pointer to CAM ccb and related stuff. */ union ccb *cam_ccb; /* CAM scsiio ccb */ u8 cdb_buf[16]; /* Copy of CDB */ u8 *sns_bbuf; /* Bounce buffer for sense data */ #define SYM_SNS_BBUF_LEN sizeof(struct scsi_sense_data) int data_len; /* Total data length */ int segments; /* Number of SG segments */ /* * Miscellaneous status'. */ u_char nego_status; /* Negotiation status */ u_char xerr_status; /* Extended error flags */ u32 extra_bytes; /* Extraneous bytes transferred */ /* * Message areas. * We prepare a message to be sent after selection. * We may use a second one if the command is rescheduled * due to CHECK_CONDITION or COMMAND TERMINATED. * Contents are IDENTIFY and SIMPLE_TAG. * While negotiating sync or wide transfer, * a SDTR or WDTR message is appended. */ u_char scsi_smsg [12]; u_char scsi_smsg2[12]; /* * Auto request sense related fields. */ u_char sensecmd[6]; /* Request Sense command */ u_char sv_scsi_status; /* Saved SCSI status */ u_char sv_xerr_status; /* Saved extended status */ int sv_resid; /* Saved residual */ /* * Map for the DMA of user data. */ #ifdef FreeBSD_Bus_Dma_Abstraction void *arg; /* Argument for some callback */ bus_dmamap_t dmamap; /* DMA map for user data */ u_char dmamapped; #define SYM_DMA_NONE 0 #define SYM_DMA_READ 1 #define SYM_DMA_WRITE 2 #endif /* * Other fields. */ u32 ccb_ba; /* BUS address of this CCB */ u_short tag; /* Tag for this transfer */ /* NO_TAG means no tag */ u_char target; u_char lun; ccb_p link_ccbh; /* Host adapter CCB hash chain */ SYM_QUEHEAD link_ccbq; /* Link to free/busy CCB queue */ u32 startp; /* Initial data pointer */ int ext_sg; /* Extreme data pointer, used */ int ext_ofs; /* to calculate the residual. */ u_char to_abort; /* Want this IO to be aborted */ }; #define CCB_BA(cp,lbl) (cp->ccb_ba + offsetof(struct sym_ccb, lbl)) /* * Host Control Block */ struct sym_hcb { /* * Global headers. * Due to poorness of addressing capabilities, earlier * chips (810, 815, 825) copy part of the data structures * (CCB, TCB and LCB) in fixed areas. */ #ifdef SYM_CONF_GENERIC_SUPPORT struct sym_ccbh ccb_head; struct sym_tcbh tcb_head; struct sym_lcbh lcb_head; #endif /* * Idle task and invalid task actions and * their bus addresses. */ struct sym_actscr idletask, notask, bad_itl, bad_itlq; vm_offset_t idletask_ba, notask_ba, bad_itl_ba, bad_itlq_ba; /* * Dummy lun table to protect us against target * returning bad lun number on reselection. */ u32 *badluntbl; /* Table physical address */ u32 badlun_sa; /* SCRIPT handler BUS address */ /* * Bus address of this host control block. */ u32 hcb_ba; /* * Bit 32-63 of the on-chip RAM bus address in LE format. * The START_RAM64 script loads the MMRS and MMWS from this * field. */ u32 scr_ram_seg; /* * Chip and controller indentification. */ #ifdef FreeBSD_Bus_Io_Abstraction device_t device; #else pcici_t pci_tag; #endif int unit; char inst_name[8]; /* * Initial value of some IO register bits. * These values are assumed to have been set by BIOS, and may * be used to probe adapter implementation differences. */ u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4, sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_scntl4, sv_stest1; /* * Actual initial value of IO register bits used by the * driver. They are loaded at initialisation according to * features that are to be enabled/disabled. */ u_char rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest3, rv_ctest4, rv_ctest5, rv_stest2, rv_ccntl0, rv_ccntl1, rv_scntl4; /* * Target data. */ struct sym_tcb target[SYM_CONF_MAX_TARGET]; /* * Target control block bus address array used by the SCRIPT * on reselection. */ u32 *targtbl; u32 targtbl_ba; /* * CAM SIM information for this instance. */ struct cam_sim *sim; struct cam_path *path; /* * Allocated hardware resources. */ #ifdef FreeBSD_Bus_Io_Abstraction struct resource *irq_res; struct resource *io_res; struct resource *mmio_res; struct resource *ram_res; int ram_id; void *intr; #endif /* * Bus stuff. * * My understanding of PCI is that all agents must share the * same addressing range and model. * But some hardware architecture guys provide complex and * brain-deaded stuff that makes shit. * This driver only support PCI compliant implementations and * deals with part of the BUS stuff complexity only to fit O/S * requirements. */ #ifdef FreeBSD_Bus_Io_Abstraction bus_space_handle_t io_bsh; bus_space_tag_t io_tag; bus_space_handle_t mmio_bsh; bus_space_tag_t mmio_tag; bus_space_handle_t ram_bsh; bus_space_tag_t ram_tag; #endif /* * DMA stuff. */ #ifdef FreeBSD_Bus_Dma_Abstraction bus_dma_tag_t bus_dmat; /* DMA tag from parent BUS */ bus_dma_tag_t data_dmat; /* DMA tag for user data */ #endif /* * Virtual and physical bus addresses of the chip. */ vm_offset_t mmio_va; /* MMIO kernel virtual address */ vm_offset_t mmio_pa; /* MMIO CPU physical address */ vm_offset_t mmio_ba; /* MMIO BUS address */ int mmio_ws; /* MMIO Window size */ vm_offset_t ram_va; /* RAM kernel virtual address */ vm_offset_t ram_pa; /* RAM CPU physical address */ vm_offset_t ram_ba; /* RAM BUS address */ int ram_ws; /* RAM window size */ u32 io_port; /* IO port address */ /* * SCRIPTS virtual and physical bus addresses. * 'script' is loaded in the on-chip RAM if present. * 'scripth' stays in main memory for all chips except the * 53C895A, 53C896 and 53C1010 that provide 8K on-chip RAM. */ u_char *scripta0; /* Copies of script and scripth */ u_char *scriptb0; /* Copies of script and scripth */ vm_offset_t scripta_ba; /* Actual script and scripth */ vm_offset_t scriptb_ba; /* bus addresses. */ vm_offset_t scriptb0_ba; u_short scripta_sz; /* Actual size of script A */ u_short scriptb_sz; /* Actual size of script B */ /* * Bus addresses, setup and patch methods for * the selected firmware. */ struct sym_fwa_ba fwa_bas; /* Useful SCRIPTA bus addresses */ struct sym_fwb_ba fwb_bas; /* Useful SCRIPTB bus addresses */ void (*fw_setup)(hcb_p np, struct sym_fw *fw); void (*fw_patch)(hcb_p np); char *fw_name; /* * General controller parameters and configuration. */ u_short device_id; /* PCI device id */ u_char revision_id; /* PCI device revision id */ u_int features; /* Chip features map */ u_char myaddr; /* SCSI id of the adapter */ u_char maxburst; /* log base 2 of dwords burst */ u_char maxwide; /* Maximum transfer width */ u_char minsync; /* Min sync period factor (ST) */ u_char maxsync; /* Max sync period factor (ST) */ u_char maxoffs; /* Max scsi offset (ST) */ u_char minsync_dt; /* Min sync period factor (DT) */ u_char maxsync_dt; /* Max sync period factor (DT) */ u_char maxoffs_dt; /* Max scsi offset (DT) */ u_char multiplier; /* Clock multiplier (1,2,4) */ u_char clock_divn; /* Number of clock divisors */ u32 clock_khz; /* SCSI clock frequency in KHz */ u32 pciclk_khz; /* Estimated PCI clock in KHz */ /* * Start queue management. * It is filled up by the host processor and accessed by the * SCRIPTS processor in order to start SCSI commands. */ volatile /* Prevent code optimizations */ u32 *squeue; /* Start queue virtual address */ u32 squeue_ba; /* Start queue BUS address */ u_short squeueput; /* Next free slot of the queue */ u_short actccbs; /* Number of allocated CCBs */ /* * Command completion queue. * It is the same size as the start queue to avoid overflow. */ u_short dqueueget; /* Next position to scan */ volatile /* Prevent code optimizations */ u32 *dqueue; /* Completion (done) queue */ u32 dqueue_ba; /* Done queue BUS address */ /* * Miscellaneous buffers accessed by the scripts-processor. * They shall be DWORD aligned, because they may be read or * written with a script command. */ u_char msgout[8]; /* Buffer for MESSAGE OUT */ u_char msgin [8]; /* Buffer for MESSAGE IN */ u32 lastmsg; /* Last SCSI message sent */ u_char scratch; /* Scratch for SCSI receive */ /* * Miscellaneous configuration and status parameters. */ u_char usrflags; /* Miscellaneous user flags */ u_char scsi_mode; /* Current SCSI BUS mode */ u_char verbose; /* Verbosity for this controller*/ u32 cache; /* Used for cache test at init. */ /* * CCB lists and queue. */ ccb_p ccbh[CCB_HASH_SIZE]; /* CCB hashed by DSA value */ SYM_QUEHEAD free_ccbq; /* Queue of available CCBs */ SYM_QUEHEAD busy_ccbq; /* Queue of busy CCBs */ /* * During error handling and/or recovery, * active CCBs that are to be completed with * error or requeued are moved from the busy_ccbq * to the comp_ccbq prior to completion. */ SYM_QUEHEAD comp_ccbq; /* * CAM CCB pending queue. */ SYM_QUEHEAD cam_ccbq; /* * IMMEDIATE ARBITRATION (IARB) control. * * We keep track in 'last_cp' of the last CCB that has been * queued to the SCRIPTS processor and clear 'last_cp' when * this CCB completes. If last_cp is not zero at the moment * we queue a new CCB, we set a flag in 'last_cp' that is * used by the SCRIPTS as a hint for setting IARB. * We donnot set more than 'iarb_max' consecutive hints for * IARB in order to leave devices a chance to reselect. * By the way, any non zero value of 'iarb_max' is unfair. :) */ #ifdef SYM_CONF_IARB_SUPPORT u_short iarb_max; /* Max. # consecutive IARB hints*/ u_short iarb_count; /* Actual # of these hints */ ccb_p last_cp; #endif /* * Command abort handling. * We need to synchronize tightly with the SCRIPTS * processor in order to handle things correctly. */ u_char abrt_msg[4]; /* Message to send buffer */ struct sym_tblmove abrt_tbl; /* Table for the MOV of it */ struct sym_tblsel abrt_sel; /* Sync params for selection */ u_char istat_sem; /* Tells the chip to stop (SEM) */ }; #define HCB_BA(np, lbl) (np->hcb_ba + offsetof(struct sym_hcb, lbl)) /* * Return the name of the controller. */ static __inline char *sym_name(hcb_p np) { return np->inst_name; } /*--------------------------------------------------------------------------*/ /*------------------------------ FIRMWARES ---------------------------------*/ /*--------------------------------------------------------------------------*/ /* * This stuff will be moved to a separate source file when * the driver will be broken into several source modules. */ /* * Macros used for all firmwares. */ #define SYM_GEN_A(s, label) ((short) offsetof(s, label)), #define SYM_GEN_B(s, label) ((short) offsetof(s, label)), #define PADDR_A(label) SYM_GEN_PADDR_A(struct SYM_FWA_SCR, label) #define PADDR_B(label) SYM_GEN_PADDR_B(struct SYM_FWB_SCR, label) #ifdef SYM_CONF_GENERIC_SUPPORT /* * Allocate firmware #1 script area. */ #define SYM_FWA_SCR sym_fw1a_scr #define SYM_FWB_SCR sym_fw1b_scr #include struct sym_fwa_ofs sym_fw1a_ofs = { SYM_GEN_FW_A(struct SYM_FWA_SCR) }; struct sym_fwb_ofs sym_fw1b_ofs = { SYM_GEN_FW_B(struct SYM_FWB_SCR) }; #undef SYM_FWA_SCR #undef SYM_FWB_SCR #endif /* SYM_CONF_GENERIC_SUPPORT */ /* * Allocate firmware #2 script area. */ #define SYM_FWA_SCR sym_fw2a_scr #define SYM_FWB_SCR sym_fw2b_scr #include struct sym_fwa_ofs sym_fw2a_ofs = { SYM_GEN_FW_A(struct SYM_FWA_SCR) }; struct sym_fwb_ofs sym_fw2b_ofs = { SYM_GEN_FW_B(struct SYM_FWB_SCR) SYM_GEN_B(struct SYM_FWB_SCR, start64) SYM_GEN_B(struct SYM_FWB_SCR, pm_handle) }; #undef SYM_FWA_SCR #undef SYM_FWB_SCR #undef SYM_GEN_A #undef SYM_GEN_B #undef PADDR_A #undef PADDR_B #ifdef SYM_CONF_GENERIC_SUPPORT /* * Patch routine for firmware #1. */ static void sym_fw1_patch(hcb_p np) { struct sym_fw1a_scr *scripta0; struct sym_fw1b_scr *scriptb0; scripta0 = (struct sym_fw1a_scr *) np->scripta0; scriptb0 = (struct sym_fw1b_scr *) np->scriptb0; /* * Remove LED support if not needed. */ if (!(np->features & FE_LED0)) { scripta0->idle[0] = cpu_to_scr(SCR_NO_OP); scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP); scripta0->start[0] = cpu_to_scr(SCR_NO_OP); } #ifdef SYM_CONF_IARB_SUPPORT /* * If user does not want to use IMMEDIATE ARBITRATION * when we are reselected while attempting to arbitrate, * patch the SCRIPTS accordingly with a SCRIPT NO_OP. */ if (!SYM_CONF_SET_IARB_ON_ARB_LOST) scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP); #endif /* * Patch some data in SCRIPTS. * - start and done queue initial bus address. * - target bus address table bus address. */ scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba); scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba); scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba); } #endif /* SYM_CONF_GENERIC_SUPPORT */ /* * Patch routine for firmware #2. */ static void sym_fw2_patch(hcb_p np) { struct sym_fw2a_scr *scripta0; struct sym_fw2b_scr *scriptb0; scripta0 = (struct sym_fw2a_scr *) np->scripta0; scriptb0 = (struct sym_fw2b_scr *) np->scriptb0; /* * Remove LED support if not needed. */ if (!(np->features & FE_LED0)) { scripta0->idle[0] = cpu_to_scr(SCR_NO_OP); scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP); scripta0->start[0] = cpu_to_scr(SCR_NO_OP); } #ifdef SYM_CONF_IARB_SUPPORT /* * If user does not want to use IMMEDIATE ARBITRATION * when we are reselected while attempting to arbitrate, * patch the SCRIPTS accordingly with a SCRIPT NO_OP. */ if (!SYM_CONF_SET_IARB_ON_ARB_LOST) scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP); #endif /* * Patch some variable in SCRIPTS. * - start and done queue initial bus address. * - target bus address table bus address. */ scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba); scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba); scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba); /* * Remove the load of SCNTL4 on reselection if not a C10. */ if (!(np->features & FE_C10)) { scripta0->resel_scntl4[0] = cpu_to_scr(SCR_NO_OP); scripta0->resel_scntl4[1] = cpu_to_scr(0); } /* * Remove a couple of work-arounds specific to C1010 if * they are not desirable. See `sym_fw2.h' for more details. */ if (!(np->device_id == PCI_ID_LSI53C1010_2 && np->revision_id < 0x1 && np->pciclk_khz < 60000)) { scripta0->datao_phase[0] = cpu_to_scr(SCR_NO_OP); scripta0->datao_phase[1] = cpu_to_scr(0); } if (!(np->device_id == PCI_ID_LSI53C1010 && /* np->revision_id < 0xff */ 1)) { scripta0->sel_done[0] = cpu_to_scr(SCR_NO_OP); scripta0->sel_done[1] = cpu_to_scr(0); } /* * Patch some other variables in SCRIPTS. * These ones are loaded by the SCRIPTS processor. */ scriptb0->pm0_data_addr[0] = cpu_to_scr(np->scripta_ba + offsetof(struct sym_fw2a_scr, pm0_data)); scriptb0->pm1_data_addr[0] = cpu_to_scr(np->scripta_ba + offsetof(struct sym_fw2a_scr, pm1_data)); } /* * Fill the data area in scripts. * To be done for all firmwares. */ static void sym_fw_fill_data (u32 *in, u32 *out) { int i; for (i = 0; i < SYM_CONF_MAX_SG; i++) { *in++ = SCR_CHMOV_TBL ^ SCR_DATA_IN; *in++ = offsetof (struct sym_dsb, data[i]); *out++ = SCR_CHMOV_TBL ^ SCR_DATA_OUT; *out++ = offsetof (struct sym_dsb, data[i]); } } /* * Setup useful script bus addresses. * To be done for all firmwares. */ static void sym_fw_setup_bus_addresses(hcb_p np, struct sym_fw *fw) { u32 *pa; u_short *po; int i; /* * Build the bus address table for script A * from the script A offset table. */ po = (u_short *) fw->a_ofs; pa = (u32 *) &np->fwa_bas; for (i = 0 ; i < sizeof(np->fwa_bas)/sizeof(u32) ; i++) pa[i] = np->scripta_ba + po[i]; /* * Same for script B. */ po = (u_short *) fw->b_ofs; pa = (u32 *) &np->fwb_bas; for (i = 0 ; i < sizeof(np->fwb_bas)/sizeof(u32) ; i++) pa[i] = np->scriptb_ba + po[i]; } #ifdef SYM_CONF_GENERIC_SUPPORT /* * Setup routine for firmware #1. */ static void sym_fw1_setup(hcb_p np, struct sym_fw *fw) { struct sym_fw1a_scr *scripta0; struct sym_fw1b_scr *scriptb0; scripta0 = (struct sym_fw1a_scr *) np->scripta0; scriptb0 = (struct sym_fw1b_scr *) np->scriptb0; /* * Fill variable parts in scripts. */ sym_fw_fill_data(scripta0->data_in, scripta0->data_out); /* * Setup bus addresses used from the C code.. */ sym_fw_setup_bus_addresses(np, fw); } #endif /* SYM_CONF_GENERIC_SUPPORT */ /* * Setup routine for firmware #2. */ static void sym_fw2_setup(hcb_p np, struct sym_fw *fw) { struct sym_fw2a_scr *scripta0; struct sym_fw2b_scr *scriptb0; scripta0 = (struct sym_fw2a_scr *) np->scripta0; scriptb0 = (struct sym_fw2b_scr *) np->scriptb0; /* * Fill variable parts in scripts. */ sym_fw_fill_data(scripta0->data_in, scripta0->data_out); /* * Setup bus addresses used from the C code.. */ sym_fw_setup_bus_addresses(np, fw); } /* * Allocate firmware descriptors. */ #ifdef SYM_CONF_GENERIC_SUPPORT static struct sym_fw sym_fw1 = SYM_FW_ENTRY(sym_fw1, "NCR-generic"); #endif /* SYM_CONF_GENERIC_SUPPORT */ static struct sym_fw sym_fw2 = SYM_FW_ENTRY(sym_fw2, "LOAD/STORE-based"); /* * Find the most appropriate firmware for a chip. */ static struct sym_fw * sym_find_firmware(struct sym_pci_chip *chip) { if (chip->features & FE_LDSTR) return &sym_fw2; #ifdef SYM_CONF_GENERIC_SUPPORT else if (!(chip->features & (FE_PFEN|FE_NOPM|FE_DAC))) return &sym_fw1; #endif else return 0; } /* * Bind a script to physical addresses. */ static void sym_fw_bind_script (hcb_p np, u32 *start, int len) { u32 opcode, new, old, tmp1, tmp2; u32 *end, *cur; int relocs; cur = start; end = start + len/4; while (cur < end) { opcode = *cur; /* * If we forget to change the length * in scripts, a field will be * padded with 0. This is an illegal * command. */ if (opcode == 0) { printf ("%s: ERROR0 IN SCRIPT at %d.\n", sym_name(np), (int) (cur-start)); MDELAY (10000); ++cur; continue; }; /* * We use the bogus value 0xf00ff00f ;-) * to reserve data area in SCRIPTS. */ if (opcode == SCR_DATA_ZERO) { *cur++ = 0; continue; } if (DEBUG_FLAGS & DEBUG_SCRIPT) printf ("%d: <%x>\n", (int) (cur-start), (unsigned)opcode); /* * We don't have to decode ALL commands */ switch (opcode >> 28) { case 0xf: /* * LOAD / STORE DSA relative, don't relocate. */ relocs = 0; break; case 0xe: /* * LOAD / STORE absolute. */ relocs = 1; break; case 0xc: /* * COPY has TWO arguments. */ relocs = 2; tmp1 = cur[1]; tmp2 = cur[2]; if ((tmp1 ^ tmp2) & 3) { printf ("%s: ERROR1 IN SCRIPT at %d.\n", sym_name(np), (int) (cur-start)); MDELAY (10000); } /* * If PREFETCH feature not enabled, remove * the NO FLUSH bit if present. */ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) { opcode = (opcode & ~SCR_NO_FLUSH); } break; case 0x0: /* * MOVE/CHMOV (absolute address) */ if (!(np->features & FE_WIDE)) opcode = (opcode | OPC_MOVE); relocs = 1; break; case 0x1: /* * MOVE/CHMOV (table indirect) */ if (!(np->features & FE_WIDE)) opcode = (opcode | OPC_MOVE); relocs = 0; break; case 0x8: /* * JUMP / CALL * dont't relocate if relative :-) */ if (opcode & 0x00800000) relocs = 0; else if ((opcode & 0xf8400000) == 0x80400000)/*JUMP64*/ relocs = 2; else relocs = 1; break; case 0x4: case 0x5: case 0x6: case 0x7: relocs = 1; break; default: relocs = 0; break; }; /* * Scriptify:) the opcode. */ *cur++ = cpu_to_scr(opcode); /* * If no relocation, assume 1 argument * and just scriptize:) it. */ if (!relocs) { *cur = cpu_to_scr(*cur); ++cur; continue; } /* * Otherwise performs all needed relocations. */ while (relocs--) { old = *cur; switch (old & RELOC_MASK) { case RELOC_REGISTER: new = (old & ~RELOC_MASK) + np->mmio_ba; break; case RELOC_LABEL_A: new = (old & ~RELOC_MASK) + np->scripta_ba; break; case RELOC_LABEL_B: new = (old & ~RELOC_MASK) + np->scriptb_ba; break; case RELOC_SOFTC: new = (old & ~RELOC_MASK) + np->hcb_ba; break; case 0: /* * Don't relocate a 0 address. * They are mostly used for patched or * script self-modified areas. */ if (old == 0) { new = old; break; } /* fall through */ default: new = 0; panic("sym_fw_bind_script: " "weird relocation %x\n", old); break; } *cur++ = cpu_to_scr(new); } }; } /*--------------------------------------------------------------------------*/ /*--------------------------- END OF FIRMARES -----------------------------*/ /*--------------------------------------------------------------------------*/ /* * Function prototypes. */ static void sym_save_initial_setting (hcb_p np); static int sym_prepare_setting (hcb_p np, struct sym_nvram *nvram); static int sym_prepare_nego (hcb_p np, ccb_p cp, int nego, u_char *msgptr); static void sym_put_start_queue (hcb_p np, ccb_p cp); static void sym_chip_reset (hcb_p np); static void sym_soft_reset (hcb_p np); static void sym_start_reset (hcb_p np); static int sym_reset_scsi_bus (hcb_p np, int enab_int); static int sym_wakeup_done (hcb_p np); static void sym_flush_busy_queue (hcb_p np, int cam_status); static void sym_flush_comp_queue (hcb_p np, int cam_status); static void sym_init (hcb_p np, int reason); static int sym_getsync(hcb_p np, u_char dt, u_char sfac, u_char *divp, u_char *fakp); static void sym_setsync (hcb_p np, ccb_p cp, u_char ofs, u_char per, u_char div, u_char fak); static void sym_setwide (hcb_p np, ccb_p cp, u_char wide); static void sym_setpprot(hcb_p np, ccb_p cp, u_char dt, u_char ofs, u_char per, u_char wide, u_char div, u_char fak); static void sym_settrans(hcb_p np, ccb_p cp, u_char dt, u_char ofs, u_char per, u_char wide, u_char div, u_char fak); static void sym_log_hard_error (hcb_p np, u_short sist, u_char dstat); static void sym_intr (void *arg); static void sym_poll (struct cam_sim *sim); static void sym_recover_scsi_int (hcb_p np, u_char hsts); static void sym_int_sto (hcb_p np); static void sym_int_udc (hcb_p np); static void sym_int_sbmc (hcb_p np); static void sym_int_par (hcb_p np, u_short sist); static void sym_int_ma (hcb_p np); static int sym_dequeue_from_squeue(hcb_p np, int i, int target, int lun, int task); static void sym_sir_bad_scsi_status (hcb_p np, int num, ccb_p cp); static int sym_clear_tasks (hcb_p np, int status, int targ, int lun, int task); static void sym_sir_task_recovery (hcb_p np, int num); static int sym_evaluate_dp (hcb_p np, ccb_p cp, u32 scr, int *ofs); static void sym_modify_dp (hcb_p np, tcb_p tp, ccb_p cp, int ofs); static int sym_compute_residual (hcb_p np, ccb_p cp); static int sym_show_msg (u_char * msg); static void sym_print_msg (ccb_p cp, char *label, u_char *msg); static void sym_sync_nego (hcb_p np, tcb_p tp, ccb_p cp); static void sym_ppr_nego (hcb_p np, tcb_p tp, ccb_p cp); static void sym_wide_nego (hcb_p np, tcb_p tp, ccb_p cp); static void sym_nego_default (hcb_p np, tcb_p tp, ccb_p cp); static void sym_nego_rejected (hcb_p np, tcb_p tp, ccb_p cp); static void sym_int_sir (hcb_p np); static void sym_free_ccb (hcb_p np, ccb_p cp); static ccb_p sym_get_ccb (hcb_p np, u_char tn, u_char ln, u_char tag_order); static ccb_p sym_alloc_ccb (hcb_p np); static ccb_p sym_ccb_from_dsa (hcb_p np, u32 dsa); static lcb_p sym_alloc_lcb (hcb_p np, u_char tn, u_char ln); static void sym_alloc_lcb_tags (hcb_p np, u_char tn, u_char ln); static int sym_snooptest (hcb_p np); static void sym_selectclock(hcb_p np, u_char scntl3); static void sym_getclock (hcb_p np, int mult); static int sym_getpciclock (hcb_p np); static void sym_complete_ok (hcb_p np, ccb_p cp); static void sym_complete_error (hcb_p np, ccb_p cp); static void sym_timeout (void *arg); static int sym_abort_scsiio (hcb_p np, union ccb *ccb, int timed_out); static void sym_reset_dev (hcb_p np, union ccb *ccb); static void sym_action (struct cam_sim *sim, union ccb *ccb); static void sym_action1 (struct cam_sim *sim, union ccb *ccb); static int sym_setup_cdb (hcb_p np, struct ccb_scsiio *csio, ccb_p cp); static void sym_setup_data_and_start (hcb_p np, struct ccb_scsiio *csio, ccb_p cp); #ifdef FreeBSD_Bus_Dma_Abstraction static int sym_fast_scatter_sg_physical(hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs); #else static int sym_scatter_virtual (hcb_p np, ccb_p cp, vm_offset_t vaddr, vm_size_t len); static int sym_scatter_sg_virtual (hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs); static int sym_scatter_physical (hcb_p np, ccb_p cp, vm_offset_t paddr, vm_size_t len); #endif static int sym_scatter_sg_physical (hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs); static void sym_action2 (struct cam_sim *sim, union ccb *ccb); static void sym_update_trans (hcb_p np, tcb_p tp, struct sym_trans *tip, struct ccb_trans_settings *cts); static void sym_update_dflags(hcb_p np, u_char *flags, struct ccb_trans_settings *cts); #ifdef FreeBSD_Bus_Io_Abstraction static struct sym_pci_chip *sym_find_pci_chip (device_t dev); static int sym_pci_probe (device_t dev); static int sym_pci_attach (device_t dev); #else static struct sym_pci_chip *sym_find_pci_chip (pcici_t tag); static const char *sym_pci_probe (pcici_t tag, pcidi_t type); static void sym_pci_attach (pcici_t tag, int unit); static int sym_pci_attach2 (pcici_t tag, int unit); #endif static void sym_pci_free (hcb_p np); static int sym_cam_attach (hcb_p np); static void sym_cam_free (hcb_p np); static void sym_nvram_setup_host (hcb_p np, struct sym_nvram *nvram); static void sym_nvram_setup_target (hcb_p np, int targ, struct sym_nvram *nvp); static int sym_read_nvram (hcb_p np, struct sym_nvram *nvp); /* * Print something which allows to retrieve the controler type, * unit, target, lun concerned by a kernel message. */ static void PRINT_TARGET (hcb_p np, int target) { printf ("%s:%d:", sym_name(np), target); } static void PRINT_LUN(hcb_p np, int target, int lun) { printf ("%s:%d:%d:", sym_name(np), target, lun); } static void PRINT_ADDR (ccb_p cp) { if (cp && cp->cam_ccb) xpt_print_path(cp->cam_ccb->ccb_h.path); } /* * Take into account this ccb in the freeze count. */ static void sym_freeze_cam_ccb(union ccb *ccb) { if (!(ccb->ccb_h.flags & CAM_DEV_QFRZDIS)) { if (!(ccb->ccb_h.status & CAM_DEV_QFRZN)) { ccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, 1); } } } /* * Set the status field of a CAM CCB. */ static __inline void sym_set_cam_status(union ccb *ccb, cam_status status) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= status; } /* * Get the status field of a CAM CCB. */ static __inline int sym_get_cam_status(union ccb *ccb) { return ccb->ccb_h.status & CAM_STATUS_MASK; } /* * Enqueue a CAM CCB. */ static void sym_enqueue_cam_ccb(hcb_p np, union ccb *ccb) { assert(!(ccb->ccb_h.status & CAM_SIM_QUEUED)); ccb->ccb_h.status = CAM_REQ_INPROG; ccb->ccb_h.timeout_ch = timeout(sym_timeout, (caddr_t) ccb, ccb->ccb_h.timeout*hz/1000); ccb->ccb_h.status |= CAM_SIM_QUEUED; ccb->ccb_h.sym_hcb_ptr = np; sym_insque_tail(sym_qptr(&ccb->ccb_h.sim_links), &np->cam_ccbq); } /* * Complete a pending CAM CCB. */ static void sym_xpt_done(hcb_p np, union ccb *ccb) { if (ccb->ccb_h.status & CAM_SIM_QUEUED) { untimeout(sym_timeout, (caddr_t) ccb, ccb->ccb_h.timeout_ch); sym_remque(sym_qptr(&ccb->ccb_h.sim_links)); ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ccb->ccb_h.sym_hcb_ptr = 0; } if (ccb->ccb_h.flags & CAM_DEV_QFREEZE) sym_freeze_cam_ccb(ccb); xpt_done(ccb); } static void sym_xpt_done2(hcb_p np, union ccb *ccb, int cam_status) { sym_set_cam_status(ccb, cam_status); sym_xpt_done(np, ccb); } /* * SYMBIOS chip clock divisor table. * * Divisors are multiplied by 10,000,000 in order to make * calculations more simple. */ #define _5M 5000000 static u32 div_10M[] = {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M}; /* * SYMBIOS chips allow burst lengths of 2, 4, 8, 16, 32, 64, * 128 transfers. All chips support at least 16 transfers * bursts. The 825A, 875 and 895 chips support bursts of up * to 128 transfers and the 895A and 896 support bursts of up * to 64 transfers. All other chips support up to 16 * transfers bursts. * * For PCI 32 bit data transfers each transfer is a DWORD. * It is a QUADWORD (8 bytes) for PCI 64 bit data transfers. * * We use log base 2 (burst length) as internal code, with * value 0 meaning "burst disabled". */ /* * Burst length from burst code. */ #define burst_length(bc) (!(bc))? 0 : 1 << (bc) /* * Burst code from io register bits. */ #define burst_code(dmode, ctest4, ctest5) \ (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1 /* * Set initial io register bits from burst code. */ static __inline void sym_init_burst(hcb_p np, u_char bc) { np->rv_ctest4 &= ~0x80; np->rv_dmode &= ~(0x3 << 6); np->rv_ctest5 &= ~0x4; if (!bc) { np->rv_ctest4 |= 0x80; } else { --bc; np->rv_dmode |= ((bc & 0x3) << 6); np->rv_ctest5 |= (bc & 0x4); } } /* * Print out the list of targets that have some flag disabled by user. */ static void sym_print_targets_flag(hcb_p np, int mask, char *msg) { int cnt; int i; for (cnt = 0, i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { if (i == np->myaddr) continue; if (np->target[i].usrflags & mask) { if (!cnt++) printf("%s: %s disabled for targets", sym_name(np), msg); printf(" %d", i); } } if (cnt) printf(".\n"); } /* * Save initial settings of some IO registers. * Assumed to have been set by BIOS. * We cannot reset the chip prior to reading the * IO registers, since informations will be lost. * Since the SCRIPTS processor may be running, this * is not safe on paper, but it seems to work quite * well. :) */ static void sym_save_initial_setting (hcb_p np) { np->sv_scntl0 = INB(nc_scntl0) & 0x0a; np->sv_scntl3 = INB(nc_scntl3) & 0x07; np->sv_dmode = INB(nc_dmode) & 0xce; np->sv_dcntl = INB(nc_dcntl) & 0xa8; np->sv_ctest3 = INB(nc_ctest3) & 0x01; np->sv_ctest4 = INB(nc_ctest4) & 0x80; np->sv_gpcntl = INB(nc_gpcntl); np->sv_stest1 = INB(nc_stest1); np->sv_stest2 = INB(nc_stest2) & 0x20; np->sv_stest4 = INB(nc_stest4); if (np->features & FE_C10) { /* Always large DMA fifo + ultra3 */ np->sv_scntl4 = INB(nc_scntl4); np->sv_ctest5 = INB(nc_ctest5) & 0x04; } else np->sv_ctest5 = INB(nc_ctest5) & 0x24; } /* * Prepare io register values used by sym_init() according * to selected and supported features. */ static int sym_prepare_setting(hcb_p np, struct sym_nvram *nvram) { u_char burst_max; u32 period; int i; /* * Wide ? */ np->maxwide = (np->features & FE_WIDE)? 1 : 0; /* * Get the frequency of the chip's clock. */ if (np->features & FE_QUAD) np->multiplier = 4; else if (np->features & FE_DBLR) np->multiplier = 2; else np->multiplier = 1; np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000; np->clock_khz *= np->multiplier; if (np->clock_khz != 40000) sym_getclock(np, np->multiplier); /* * Divisor to be used for async (timer pre-scaler). */ i = np->clock_divn - 1; while (--i >= 0) { if (10ul * SYM_CONF_MIN_ASYNC * np->clock_khz > div_10M[i]) { ++i; break; } } np->rv_scntl3 = i+1; /* * The C1010 uses hardwired divisors for async. * So, we just throw away, the async. divisor.:-) */ if (np->features & FE_C10) np->rv_scntl3 = 0; /* * Minimum synchronous period factor supported by the chip. * Btw, 'period' is in tenths of nanoseconds. */ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz; if (period <= 250) np->minsync = 10; else if (period <= 303) np->minsync = 11; else if (period <= 500) np->minsync = 12; else np->minsync = (period + 40 - 1) / 40; /* * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2). */ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2|FE_ULTRA3))) np->minsync = 25; else if (np->minsync < 12 && !(np->features & (FE_ULTRA2|FE_ULTRA3))) np->minsync = 12; /* * Maximum synchronous period factor supported by the chip. */ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz); np->maxsync = period > 2540 ? 254 : period / 10; /* * If chip is a C1010, guess the sync limits in DT mode. */ if ((np->features & (FE_C10|FE_ULTRA3)) == (FE_C10|FE_ULTRA3)) { if (np->clock_khz == 160000) { np->minsync_dt = 9; np->maxsync_dt = 50; np->maxoffs_dt = 62; } } /* * 64 bit addressing (895A/896/1010) ? */ if (np->features & FE_DAC) #if BITS_PER_LONG > 32 np->rv_ccntl1 |= (XTIMOD | EXTIBMV); #else np->rv_ccntl1 |= (DDAC); #endif /* * Phase mismatch handled by SCRIPTS (895A/896/1010) ? */ if (np->features & FE_NOPM) np->rv_ccntl0 |= (ENPMJ); /* * C1010 Errata. * In dual channel mode, contention occurs if internal cycles * are used. Disable internal cycles. */ if (np->device_id == PCI_ID_LSI53C1010 && np->revision_id < 0x2) np->rv_ccntl0 |= DILS; /* * Select burst length (dwords) */ burst_max = SYM_SETUP_BURST_ORDER; if (burst_max == 255) burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5); if (burst_max > 7) burst_max = 7; if (burst_max > np->maxburst) burst_max = np->maxburst; /* * DEL 352 - 53C810 Rev x11 - Part Number 609-0392140 - ITEM 2. * This chip and the 860 Rev 1 may wrongly use PCI cache line * based transactions on LOAD/STORE instructions. So we have * to prevent these chips from using such PCI transactions in * this driver. The generic ncr driver that does not use * LOAD/STORE instructions does not need this work-around. */ if ((np->device_id == PCI_ID_SYM53C810 && np->revision_id >= 0x10 && np->revision_id <= 0x11) || (np->device_id == PCI_ID_SYM53C860 && np->revision_id <= 0x1)) np->features &= ~(FE_WRIE|FE_ERL|FE_ERMP); /* * Select all supported special features. * If we are using on-board RAM for scripts, prefetch (PFEN) * does not help, but burst op fetch (BOF) does. * Disabling PFEN makes sure BOF will be used. */ if (np->features & FE_ERL) np->rv_dmode |= ERL; /* Enable Read Line */ if (np->features & FE_BOF) np->rv_dmode |= BOF; /* Burst Opcode Fetch */ if (np->features & FE_ERMP) np->rv_dmode |= ERMP; /* Enable Read Multiple */ #if 1 if ((np->features & FE_PFEN) && !np->ram_ba) #else if (np->features & FE_PFEN) #endif np->rv_dcntl |= PFEN; /* Prefetch Enable */ if (np->features & FE_CLSE) np->rv_dcntl |= CLSE; /* Cache Line Size Enable */ if (np->features & FE_WRIE) np->rv_ctest3 |= WRIE; /* Write and Invalidate */ if (np->features & FE_DFS) np->rv_ctest5 |= DFS; /* Dma Fifo Size */ /* * Select some other */ if (SYM_SETUP_PCI_PARITY) np->rv_ctest4 |= MPEE; /* Master parity checking */ if (SYM_SETUP_SCSI_PARITY) np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */ /* * Get parity checking, host ID and verbose mode from NVRAM */ np->myaddr = 255; sym_nvram_setup_host (np, nvram); /* * Get SCSI addr of host adapter (set by bios?). */ if (np->myaddr == 255) { np->myaddr = INB(nc_scid) & 0x07; if (!np->myaddr) np->myaddr = SYM_SETUP_HOST_ID; } /* * Prepare initial io register bits for burst length */ sym_init_burst(np, burst_max); /* * Set SCSI BUS mode. * - LVD capable chips (895/895A/896/1010) report the * current BUS mode through the STEST4 IO register. * - For previous generation chips (825/825A/875), * user has to tell us how to check against HVD, * since a 100% safe algorithm is not possible. */ np->scsi_mode = SMODE_SE; if (np->features & (FE_ULTRA2|FE_ULTRA3)) np->scsi_mode = (np->sv_stest4 & SMODE); else if (np->features & FE_DIFF) { if (SYM_SETUP_SCSI_DIFF == 1) { if (np->sv_scntl3) { if (np->sv_stest2 & 0x20) np->scsi_mode = SMODE_HVD; } else if (nvram->type == SYM_SYMBIOS_NVRAM) { if (!(INB(nc_gpreg) & 0x08)) np->scsi_mode = SMODE_HVD; } } else if (SYM_SETUP_SCSI_DIFF == 2) np->scsi_mode = SMODE_HVD; } if (np->scsi_mode == SMODE_HVD) np->rv_stest2 |= 0x20; /* * Set LED support from SCRIPTS. * Ignore this feature for boards known to use a * specific GPIO wiring and for the 895A, 896 * and 1010 that drive the LED directly. */ if ((SYM_SETUP_SCSI_LED || (nvram->type == SYM_SYMBIOS_NVRAM || (nvram->type == SYM_TEKRAM_NVRAM && np->device_id == PCI_ID_SYM53C895))) && !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01)) np->features |= FE_LED0; /* * Set irq mode. */ switch(SYM_SETUP_IRQ_MODE & 3) { case 2: np->rv_dcntl |= IRQM; break; case 1: np->rv_dcntl |= (np->sv_dcntl & IRQM); break; default: break; } /* * Configure targets according to driver setup. * If NVRAM present get targets setup from NVRAM. */ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { tcb_p tp = &np->target[i]; #ifdef FreeBSD_New_Tran_Settings tp->tinfo.user.scsi_version = tp->tinfo.current.scsi_version= 2; tp->tinfo.user.spi_version = tp->tinfo.current.spi_version = 2; #endif tp->tinfo.user.period = np->minsync; tp->tinfo.user.offset = np->maxoffs; tp->tinfo.user.width = np->maxwide ? BUS_16_BIT : BUS_8_BIT; tp->usrflags |= (SYM_DISC_ENABLED | SYM_TAGS_ENABLED); tp->usrtags = SYM_SETUP_MAX_TAG; sym_nvram_setup_target (np, i, nvram); /* * For now, guess PPR/DT support from the period * and BUS width. */ if (np->features & FE_ULTRA3) { if (tp->tinfo.user.period <= 9 && tp->tinfo.user.width == BUS_16_BIT) { tp->tinfo.user.options |= PPR_OPT_DT; tp->tinfo.user.offset = np->maxoffs_dt; #ifdef FreeBSD_New_Tran_Settings tp->tinfo.user.spi_version = 3; #endif } } if (!tp->usrtags) tp->usrflags &= ~SYM_TAGS_ENABLED; } /* * Let user know about the settings. */ i = nvram->type; printf("%s: %s NVRAM, ID %d, Fast-%d, %s, %s\n", sym_name(np), i == SYM_SYMBIOS_NVRAM ? "Symbios" : (i == SYM_TEKRAM_NVRAM ? "Tekram" : "No"), np->myaddr, (np->features & FE_ULTRA3) ? 80 : (np->features & FE_ULTRA2) ? 40 : (np->features & FE_ULTRA) ? 20 : 10, sym_scsi_bus_mode(np->scsi_mode), (np->rv_scntl0 & 0xa) ? "parity checking" : "NO parity"); /* * Tell him more on demand. */ if (sym_verbose) { printf("%s: %s IRQ line driver%s\n", sym_name(np), np->rv_dcntl & IRQM ? "totem pole" : "open drain", np->ram_ba ? ", using on-chip SRAM" : ""); printf("%s: using %s firmware.\n", sym_name(np), np->fw_name); if (np->features & FE_NOPM) printf("%s: handling phase mismatch from SCRIPTS.\n", sym_name(np)); } /* * And still more. */ if (sym_verbose > 1) { printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", sym_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5); printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", sym_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); } /* * Let user be aware of targets that have some disable flags set. */ sym_print_targets_flag(np, SYM_SCAN_BOOT_DISABLED, "SCAN AT BOOT"); if (sym_verbose) sym_print_targets_flag(np, SYM_SCAN_LUNS_DISABLED, "SCAN FOR LUNS"); return 0; } /* * Prepare the next negotiation message if needed. * * Fill in the part of message buffer that contains the * negotiation and the nego_status field of the CCB. * Returns the size of the message in bytes. */ static int sym_prepare_nego(hcb_p np, ccb_p cp, int nego, u_char *msgptr) { tcb_p tp = &np->target[cp->target]; int msglen = 0; /* * Early C1010 chips need a work-around for DT * data transfer to work. */ if (!(np->features & FE_U3EN)) tp->tinfo.goal.options = 0; /* * negotiate using PPR ? */ if (tp->tinfo.goal.options & PPR_OPT_MASK) nego = NS_PPR; /* * negotiate wide transfers ? */ else if (tp->tinfo.current.width != tp->tinfo.goal.width) nego = NS_WIDE; /* * negotiate synchronous transfers? */ else if (tp->tinfo.current.period != tp->tinfo.goal.period || tp->tinfo.current.offset != tp->tinfo.goal.offset) nego = NS_SYNC; switch (nego) { case NS_SYNC: msgptr[msglen++] = M_EXTENDED; msgptr[msglen++] = 3; msgptr[msglen++] = M_X_SYNC_REQ; msgptr[msglen++] = tp->tinfo.goal.period; msgptr[msglen++] = tp->tinfo.goal.offset; break; case NS_WIDE: msgptr[msglen++] = M_EXTENDED; msgptr[msglen++] = 2; msgptr[msglen++] = M_X_WIDE_REQ; msgptr[msglen++] = tp->tinfo.goal.width; break; case NS_PPR: msgptr[msglen++] = M_EXTENDED; msgptr[msglen++] = 6; msgptr[msglen++] = M_X_PPR_REQ; msgptr[msglen++] = tp->tinfo.goal.period; msgptr[msglen++] = 0; msgptr[msglen++] = tp->tinfo.goal.offset; msgptr[msglen++] = tp->tinfo.goal.width; msgptr[msglen++] = tp->tinfo.goal.options & PPR_OPT_DT; break; }; cp->nego_status = nego; if (nego) { tp->nego_cp = cp; /* Keep track a nego will be performed */ if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, nego == NS_SYNC ? "sync msgout" : nego == NS_WIDE ? "wide msgout" : "ppr msgout", msgptr); }; }; return msglen; } /* * Insert a job into the start queue. */ static void sym_put_start_queue(hcb_p np, ccb_p cp) { u_short qidx; #ifdef SYM_CONF_IARB_SUPPORT /* * If the previously queued CCB is not yet done, * set the IARB hint. The SCRIPTS will go with IARB * for this job when starting the previous one. * We leave devices a chance to win arbitration by * not using more than 'iarb_max' consecutive * immediate arbitrations. */ if (np->last_cp && np->iarb_count < np->iarb_max) { np->last_cp->host_flags |= HF_HINT_IARB; ++np->iarb_count; } else np->iarb_count = 0; np->last_cp = cp; #endif /* * Insert first the idle task and then our job. * The MB should ensure proper ordering. */ qidx = np->squeueput + 2; if (qidx >= MAX_QUEUE*2) qidx = 0; np->squeue [qidx] = cpu_to_scr(np->idletask_ba); MEMORY_BARRIER(); np->squeue [np->squeueput] = cpu_to_scr(cp->ccb_ba); np->squeueput = qidx; if (DEBUG_FLAGS & DEBUG_QUEUE) printf ("%s: queuepos=%d.\n", sym_name (np), np->squeueput); /* * Script processor may be waiting for reselect. * Wake it up. */ MEMORY_BARRIER(); OUTB (nc_istat, SIGP|np->istat_sem); } /* * Soft reset the chip. * * Raising SRST when the chip is running may cause * problems on dual function chips (see below). * On the other hand, LVD devices need some delay * to settle and report actual BUS mode in STEST4. */ static void sym_chip_reset (hcb_p np) { OUTB (nc_istat, SRST); UDELAY (10); OUTB (nc_istat, 0); UDELAY(2000); /* For BUS MODE to settle */ } /* * Soft reset the chip. * * Some 896 and 876 chip revisions may hang-up if we set * the SRST (soft reset) bit at the wrong time when SCRIPTS * are running. * So, we need to abort the current operation prior to * soft resetting the chip. */ static void sym_soft_reset (hcb_p np) { u_char istat; int i; OUTB (nc_istat, CABRT); for (i = 1000000 ; i ; --i) { istat = INB (nc_istat); if (istat & SIP) { INW (nc_sist); continue; } if (istat & DIP) { OUTB (nc_istat, 0); INB (nc_dstat); break; } } if (!i) printf("%s: unable to abort current chip operation.\n", sym_name(np)); sym_chip_reset (np); } /* * Start reset process. * * The interrupt handler will reinitialize the chip. */ static void sym_start_reset(hcb_p np) { (void) sym_reset_scsi_bus(np, 1); } static int sym_reset_scsi_bus(hcb_p np, int enab_int) { u32 term; int retv = 0; sym_soft_reset(np); /* Soft reset the chip */ if (enab_int) OUTW (nc_sien, RST); /* * Enable Tolerant, reset IRQD if present and * properly set IRQ mode, prior to resetting the bus. */ OUTB (nc_stest3, TE); OUTB (nc_dcntl, (np->rv_dcntl & IRQM)); OUTB (nc_scntl1, CRST); UDELAY (200); if (!SYM_SETUP_SCSI_BUS_CHECK) goto out; /* * Check for no terminators or SCSI bus shorts to ground. * Read SCSI data bus, data parity bits and control signals. * We are expecting RESET to be TRUE and other signals to be * FALSE. */ term = INB(nc_sstat0); term = ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */ term |= ((INB(nc_sstat2) & 0x01) << 26) | /* sdp1 */ ((INW(nc_sbdl) & 0xff) << 9) | /* d7-0 */ ((INW(nc_sbdl) & 0xff00) << 10) | /* d15-8 */ INB(nc_sbcl); /* req ack bsy sel atn msg cd io */ if (!(np->features & FE_WIDE)) term &= 0x3ffff; if (term != (2<<7)) { printf("%s: suspicious SCSI data while resetting the BUS.\n", sym_name(np)); printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = " "0x%lx, expecting 0x%lx\n", sym_name(np), (np->features & FE_WIDE) ? "dp1,d15-8," : "", (u_long)term, (u_long)(2<<7)); if (SYM_SETUP_SCSI_BUS_CHECK == 1) retv = 1; } out: OUTB (nc_scntl1, 0); /* MDELAY(100); */ return retv; } /* * The chip may have completed jobs. Look at the DONE QUEUE. * * On architectures that may reorder LOAD/STORE operations, * a memory barrier may be needed after the reading of the * so-called `flag' and prior to dealing with the data. */ static int sym_wakeup_done (hcb_p np) { ccb_p cp; int i, n; u32 dsa; n = 0; i = np->dqueueget; while (1) { dsa = scr_to_cpu(np->dqueue[i]); if (!dsa) break; np->dqueue[i] = 0; if ((i = i+2) >= MAX_QUEUE*2) i = 0; cp = sym_ccb_from_dsa(np, dsa); if (cp) { MEMORY_BARRIER(); sym_complete_ok (np, cp); ++n; } else printf ("%s: bad DSA (%x) in done queue.\n", sym_name(np), (u_int) dsa); } np->dqueueget = i; return n; } /* * Complete all active CCBs with error. * Used on CHIP/SCSI RESET. */ static void sym_flush_busy_queue (hcb_p np, int cam_status) { /* * Move all active CCBs to the COMP queue * and flush this queue. */ sym_que_splice(&np->busy_ccbq, &np->comp_ccbq); sym_que_init(&np->busy_ccbq); sym_flush_comp_queue(np, cam_status); } /* * Start chip. * * 'reason' means: * 0: initialisation. * 1: SCSI BUS RESET delivered or received. * 2: SCSI BUS MODE changed. */ static void sym_init (hcb_p np, int reason) { int i; u32 phys; /* * Reset chip if asked, otherwise just clear fifos. */ if (reason == 1) sym_soft_reset(np); else { OUTB (nc_stest3, TE|CSF); OUTONB (nc_ctest3, CLF); } /* * Clear Start Queue */ phys = np->squeue_ba; for (i = 0; i < MAX_QUEUE*2; i += 2) { np->squeue[i] = cpu_to_scr(np->idletask_ba); np->squeue[i+1] = cpu_to_scr(phys + (i+2)*4); } np->squeue[MAX_QUEUE*2-1] = cpu_to_scr(phys); /* * Start at first entry. */ np->squeueput = 0; /* * Clear Done Queue */ phys = np->dqueue_ba; for (i = 0; i < MAX_QUEUE*2; i += 2) { np->dqueue[i] = 0; np->dqueue[i+1] = cpu_to_scr(phys + (i+2)*4); } np->dqueue[MAX_QUEUE*2-1] = cpu_to_scr(phys); /* * Start at first entry. */ np->dqueueget = 0; /* * Install patches in scripts. * This also let point to first position the start * and done queue pointers used from SCRIPTS. */ np->fw_patch(np); /* * Wakeup all pending jobs. */ sym_flush_busy_queue(np, CAM_SCSI_BUS_RESET); /* * Init chip. */ OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */ UDELAY (2000); /* The 895 needs time for the bus mode to settle */ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0); /* full arb., ena parity, par->ATN */ OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */ sym_selectclock(np, np->rv_scntl3); /* Select SCSI clock */ OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */ OUTW (nc_respid, 1ul<myaddr); /* Id to respond to */ OUTB (nc_istat , SIGP ); /* Signal Process */ OUTB (nc_dmode , np->rv_dmode); /* Burst length, dma mode */ OUTB (nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */ OUTB (nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */ OUTB (nc_ctest3, np->rv_ctest3); /* Write and invalidate */ OUTB (nc_ctest4, np->rv_ctest4); /* Master parity checking */ /* Extended Sreq/Sack filtering not supported on the C10 */ if (np->features & FE_C10) OUTB (nc_stest2, np->rv_stest2); else OUTB (nc_stest2, EXT|np->rv_stest2); OUTB (nc_stest3, TE); /* TolerANT enable */ OUTB (nc_stime0, 0x0c); /* HTH disabled STO 0.25 sec */ /* * For now, disable AIP generation on C1010-66. */ if (np->device_id == PCI_ID_LSI53C1010_2) OUTB (nc_aipcntl1, DISAIP); /* * C10101 Errata. * Errant SGE's when in narrow. Write bits 4 & 5 of * STEST1 register to disable SGE. We probably should do * that from SCRIPTS for each selection/reselection, but * I just don't want. :) */ if (np->device_id == PCI_ID_LSI53C1010 && /* np->revision_id < 0xff */ 1) OUTB (nc_stest1, INB(nc_stest1) | 0x30); /* * DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2. * Disable overlapped arbitration for some dual function devices, * regardless revision id (kind of post-chip-design feature. ;-)) */ if (np->device_id == PCI_ID_SYM53C875) OUTB (nc_ctest0, (1<<5)); else if (np->device_id == PCI_ID_SYM53C896) np->rv_ccntl0 |= DPR; /* * Write CCNTL0/CCNTL1 for chips capable of 64 bit addressing * and/or hardware phase mismatch, since only such chips * seem to support those IO registers. */ if (np->features & (FE_DAC|FE_NOPM)) { OUTB (nc_ccntl0, np->rv_ccntl0); OUTB (nc_ccntl1, np->rv_ccntl1); } /* * If phase mismatch handled by scripts (895A/896/1010), * set PM jump addresses. */ if (np->features & FE_NOPM) { OUTL (nc_pmjad1, SCRIPTB_BA (np, pm_handle)); OUTL (nc_pmjad2, SCRIPTB_BA (np, pm_handle)); } /* * Enable GPIO0 pin for writing if LED support from SCRIPTS. * Also set GPIO5 and clear GPIO6 if hardware LED control. */ if (np->features & FE_LED0) OUTB(nc_gpcntl, INB(nc_gpcntl) & ~0x01); else if (np->features & FE_LEDC) OUTB(nc_gpcntl, (INB(nc_gpcntl) & ~0x41) | 0x20); /* * enable ints */ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST|PAR); OUTB (nc_dien , MDPE|BF|SSI|SIR|IID); /* * For 895/6 enable SBMC interrupt and save current SCSI bus mode. * Try to eat the spurious SBMC interrupt that may occur when * we reset the chip but not the SCSI BUS (at initialization). */ if (np->features & (FE_ULTRA2|FE_ULTRA3)) { OUTONW (nc_sien, SBMC); if (reason == 0) { MDELAY(100); INW (nc_sist); } np->scsi_mode = INB (nc_stest4) & SMODE; } /* * Fill in target structure. * Reinitialize usrsync. * Reinitialize usrwide. * Prepare sync negotiation according to actual SCSI bus mode. */ for (i=0;itarget[i]; tp->to_reset = 0; tp->head.sval = 0; tp->head.wval = np->rv_scntl3; tp->head.uval = 0; tp->tinfo.current.period = 0; tp->tinfo.current.offset = 0; tp->tinfo.current.width = BUS_8_BIT; tp->tinfo.current.options = 0; } /* * Download SCSI SCRIPTS to on-chip RAM if present, * and start script processor. */ if (np->ram_ba) { if (sym_verbose > 1) printf ("%s: Downloading SCSI SCRIPTS.\n", sym_name(np)); if (np->ram_ws == 8192) { OUTRAM_OFF(4096, np->scriptb0, np->scriptb_sz); OUTL (nc_mmws, np->scr_ram_seg); OUTL (nc_mmrs, np->scr_ram_seg); OUTL (nc_sfs, np->scr_ram_seg); phys = SCRIPTB_BA (np, start64); } else phys = SCRIPTA_BA (np, init); OUTRAM_OFF(0, np->scripta0, np->scripta_sz); } else phys = SCRIPTA_BA (np, init); np->istat_sem = 0; OUTL (nc_dsa, np->hcb_ba); OUTL_DSP (phys); /* * Notify the XPT about the RESET condition. */ if (reason != 0) xpt_async(AC_BUS_RESET, np->path, NULL); } /* * Get clock factor and sync divisor for a given * synchronous factor period. */ static int sym_getsync(hcb_p np, u_char dt, u_char sfac, u_char *divp, u_char *fakp) { u32 clk = np->clock_khz; /* SCSI clock frequency in kHz */ int div = np->clock_divn; /* Number of divisors supported */ u32 fak; /* Sync factor in sxfer */ u32 per; /* Period in tenths of ns */ u32 kpc; /* (per * clk) */ int ret; /* * Compute the synchronous period in tenths of nano-seconds */ if (dt && sfac <= 9) per = 125; else if (sfac <= 10) per = 250; else if (sfac == 11) per = 303; else if (sfac == 12) per = 500; else per = 40 * sfac; ret = per; kpc = per * clk; if (dt) kpc <<= 1; /* * For earliest C10 revision 0, we cannot use extra * clocks for the setting of the SCSI clocking. * Note that this limits the lowest sync data transfer * to 5 Mega-transfers per second and may result in * using higher clock divisors. */ #if 1 if ((np->features & (FE_C10|FE_U3EN)) == FE_C10) { /* * Look for the lowest clock divisor that allows an * output speed not faster than the period. */ while (div > 0) { --div; if (kpc > (div_10M[div] << 2)) { ++div; break; } } fak = 0; /* No extra clocks */ if (div == np->clock_divn) { /* Are we too fast ? */ ret = -1; } *divp = div; *fakp = fak; return ret; } #endif /* * Look for the greatest clock divisor that allows an * input speed faster than the period. */ while (div-- > 0) if (kpc >= (div_10M[div] << 2)) break; /* * Calculate the lowest clock factor that allows an output * speed not faster than the period, and the max output speed. * If fak >= 1 we will set both XCLKH_ST and XCLKH_DT. * If fak >= 2 we will also set XCLKS_ST and XCLKS_DT. */ if (dt) { fak = (kpc - 1) / (div_10M[div] << 1) + 1 - 2; /* ret = ((2+fak)*div_10M[div])/np->clock_khz; */ } else { fak = (kpc - 1) / div_10M[div] + 1 - 4; /* ret = ((4+fak)*div_10M[div])/np->clock_khz; */ } /* * Check against our hardware limits, or bugs :). */ if (fak < 0) {fak = 0; ret = -1;} if (fak > 2) {fak = 2; ret = -1;} /* * Compute and return sync parameters. */ *divp = div; *fakp = fak; return ret; } /* * Tell the SCSI layer about the new transfer parameters. */ static void sym_xpt_async_transfer_neg(hcb_p np, int target, u_int spi_valid) { struct ccb_trans_settings cts; struct cam_path *path; int sts; tcb_p tp = &np->target[target]; sts = xpt_create_path(&path, NULL, cam_sim_path(np->sim), target, CAM_LUN_WILDCARD); if (sts != CAM_REQ_CMP) return; bzero(&cts, sizeof(cts)); #ifdef FreeBSD_New_Tran_Settings #define cts__scsi (cts.proto_specific.scsi) #define cts__spi (cts.xport_specific.spi) cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.protocol = PROTO_SCSI; cts.transport = XPORT_SPI; cts.protocol_version = tp->tinfo.current.scsi_version; cts.transport_version = tp->tinfo.current.spi_version; cts__spi.valid = spi_valid; if (spi_valid & CTS_SPI_VALID_SYNC_RATE) cts__spi.sync_period = tp->tinfo.current.period; if (spi_valid & CTS_SPI_VALID_SYNC_OFFSET) cts__spi.sync_offset = tp->tinfo.current.offset; if (spi_valid & CTS_SPI_VALID_BUS_WIDTH) cts__spi.bus_width = tp->tinfo.current.width; if (spi_valid & CTS_SPI_VALID_PPR_OPTIONS) cts__spi.ppr_options = tp->tinfo.current.options; #undef cts__spi #undef cts__scsi #else cts.valid = spi_valid; if (spi_valid & CCB_TRANS_SYNC_RATE_VALID) cts.sync_period = tp->tinfo.current.period; if (spi_valid & CCB_TRANS_SYNC_OFFSET_VALID) cts.sync_offset = tp->tinfo.current.offset; if (spi_valid & CCB_TRANS_BUS_WIDTH_VALID) cts.bus_width = tp->tinfo.current.width; #endif xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1); xpt_async(AC_TRANSFER_NEG, path, &cts); xpt_free_path(path); } #ifdef FreeBSD_New_Tran_Settings #define SYM_SPI_VALID_WDTR \ CTS_SPI_VALID_BUS_WIDTH | \ CTS_SPI_VALID_SYNC_RATE | \ CTS_SPI_VALID_SYNC_OFFSET #define SYM_SPI_VALID_SDTR \ CTS_SPI_VALID_SYNC_RATE | \ CTS_SPI_VALID_SYNC_OFFSET #define SYM_SPI_VALID_PPR \ CTS_SPI_VALID_PPR_OPTIONS | \ CTS_SPI_VALID_BUS_WIDTH | \ CTS_SPI_VALID_SYNC_RATE | \ CTS_SPI_VALID_SYNC_OFFSET #else #define SYM_SPI_VALID_WDTR \ CCB_TRANS_BUS_WIDTH_VALID | \ CCB_TRANS_SYNC_RATE_VALID | \ CCB_TRANS_SYNC_OFFSET_VALID #define SYM_SPI_VALID_SDTR \ CCB_TRANS_SYNC_RATE_VALID | \ CCB_TRANS_SYNC_OFFSET_VALID #define SYM_SPI_VALID_PPR \ CCB_TRANS_BUS_WIDTH_VALID | \ CCB_TRANS_SYNC_RATE_VALID | \ CCB_TRANS_SYNC_OFFSET_VALID #endif /* * We received a WDTR. * Let everything be aware of the changes. */ static void sym_setwide(hcb_p np, ccb_p cp, u_char wide) { tcb_p tp = &np->target[cp->target]; sym_settrans(np, cp, 0, 0, 0, wide, 0, 0); /* * Tell the SCSI layer about the new transfer parameters. */ tp->tinfo.goal.width = tp->tinfo.current.width = wide; tp->tinfo.current.offset = 0; tp->tinfo.current.period = 0; tp->tinfo.current.options = 0; sym_xpt_async_transfer_neg(np, cp->target, SYM_SPI_VALID_WDTR); } /* * We received a SDTR. * Let everything be aware of the changes. */ static void sym_setsync(hcb_p np, ccb_p cp, u_char ofs, u_char per, u_char div, u_char fak) { tcb_p tp = &np->target[cp->target]; u_char wide = (cp->phys.select.sel_scntl3 & EWS) ? 1 : 0; sym_settrans(np, cp, 0, ofs, per, wide, div, fak); /* * Tell the SCSI layer about the new transfer parameters. */ tp->tinfo.goal.period = tp->tinfo.current.period = per; tp->tinfo.goal.offset = tp->tinfo.current.offset = ofs; tp->tinfo.goal.options = tp->tinfo.current.options = 0; sym_xpt_async_transfer_neg(np, cp->target, SYM_SPI_VALID_SDTR); } /* * We received a PPR. * Let everything be aware of the changes. */ static void sym_setpprot(hcb_p np, ccb_p cp, u_char dt, u_char ofs, u_char per, u_char wide, u_char div, u_char fak) { tcb_p tp = &np->target[cp->target]; sym_settrans(np, cp, dt, ofs, per, wide, div, fak); /* * Tell the SCSI layer about the new transfer parameters. */ tp->tinfo.goal.width = tp->tinfo.current.width = wide; tp->tinfo.goal.period = tp->tinfo.current.period = per; tp->tinfo.goal.offset = tp->tinfo.current.offset = ofs; tp->tinfo.goal.options = tp->tinfo.current.options = dt; sym_xpt_async_transfer_neg(np, cp->target, SYM_SPI_VALID_PPR); } /* * Switch trans mode for current job and it's target. */ static void sym_settrans(hcb_p np, ccb_p cp, u_char dt, u_char ofs, u_char per, u_char wide, u_char div, u_char fak) { SYM_QUEHEAD *qp; union ccb *ccb; tcb_p tp; u_char target = INB (nc_sdid) & 0x0f; u_char sval, wval, uval; assert (cp); if (!cp) return; ccb = cp->cam_ccb; assert (ccb); if (!ccb) return; assert (target == (cp->target & 0xf)); tp = &np->target[target]; sval = tp->head.sval; wval = tp->head.wval; uval = tp->head.uval; #if 0 printf("XXXX sval=%x wval=%x uval=%x (%x)\n", sval, wval, uval, np->rv_scntl3); #endif /* * Set the offset. */ if (!(np->features & FE_C10)) sval = (sval & ~0x1f) | ofs; else sval = (sval & ~0x3f) | ofs; /* * Set the sync divisor and extra clock factor. */ if (ofs != 0) { wval = (wval & ~0x70) | ((div+1) << 4); if (!(np->features & FE_C10)) sval = (sval & ~0xe0) | (fak << 5); else { uval = uval & ~(XCLKH_ST|XCLKH_DT|XCLKS_ST|XCLKS_DT); if (fak >= 1) uval |= (XCLKH_ST|XCLKH_DT); if (fak >= 2) uval |= (XCLKS_ST|XCLKS_DT); } } /* * Set the bus width. */ wval = wval & ~EWS; if (wide != 0) wval |= EWS; /* * Set misc. ultra enable bits. */ if (np->features & FE_C10) { uval = uval & ~(U3EN|AIPCKEN); if (dt) { assert(np->features & FE_U3EN); uval |= U3EN; } } else { wval = wval & ~ULTRA; if (per <= 12) wval |= ULTRA; } /* * Stop there if sync parameters are unchanged. */ if (tp->head.sval == sval && tp->head.wval == wval && tp->head.uval == uval) return; tp->head.sval = sval; tp->head.wval = wval; tp->head.uval = uval; /* * Disable extended Sreq/Sack filtering if per < 50. * Not supported on the C1010. */ if (per < 50 && !(np->features & FE_C10)) OUTOFFB (nc_stest2, EXT); /* * set actual value and sync_status */ OUTB (nc_sxfer, tp->head.sval); OUTB (nc_scntl3, tp->head.wval); if (np->features & FE_C10) { OUTB (nc_scntl4, tp->head.uval); } /* * patch ALL busy ccbs of this target. */ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); if (cp->target != target) continue; cp->phys.select.sel_scntl3 = tp->head.wval; cp->phys.select.sel_sxfer = tp->head.sval; if (np->features & FE_C10) { cp->phys.select.sel_scntl4 = tp->head.uval; } } } /* * log message for real hard errors * * sym0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc). * reg: r0 r1 r2 r3 r4 r5 r6 ..... rf. * * exception register: * ds: dstat * si: sist * * SCSI bus lines: * so: control lines as driven by chip. * si: control lines as seen by chip. * sd: scsi data lines as seen by chip. * * wide/fastmode: * sxfer: (see the manual) * scntl3: (see the manual) * * current script command: * dsp: script address (relative to start of script). * dbc: first word of script command. * * First 24 register of the chip: * r0..rf */ static void sym_log_hard_error(hcb_p np, u_short sist, u_char dstat) { u32 dsp; int script_ofs; int script_size; char *script_name; u_char *script_base; int i; dsp = INL (nc_dsp); if (dsp > np->scripta_ba && dsp <= np->scripta_ba + np->scripta_sz) { script_ofs = dsp - np->scripta_ba; script_size = np->scripta_sz; script_base = (u_char *) np->scripta0; script_name = "scripta"; } else if (np->scriptb_ba < dsp && dsp <= np->scriptb_ba + np->scriptb_sz) { script_ofs = dsp - np->scriptb_ba; script_size = np->scriptb_sz; script_base = (u_char *) np->scriptb0; script_name = "scriptb"; } else { script_ofs = dsp; script_size = 0; script_base = 0; script_name = "mem"; } printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n", sym_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist, (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl), (unsigned)INB (nc_sxfer), (unsigned)INB (nc_scntl3), script_name, script_ofs, (unsigned)INL (nc_dbc)); if (((script_ofs & 3) == 0) && (unsigned)script_ofs < script_size) { printf ("%s: script cmd = %08x\n", sym_name(np), scr_to_cpu((int) *(u32 *)(script_base + script_ofs))); } printf ("%s: regdump:", sym_name(np)); for (i=0; i<24;i++) printf (" %02x", (unsigned)INB_OFF(i)); printf (".\n"); /* * PCI BUS error, read the PCI ststus register. */ if (dstat & (MDPE|BF)) { u_short pci_sts; #ifdef FreeBSD_Bus_Io_Abstraction pci_sts = pci_read_config(np->device, PCIR_STATUS, 2); #else pci_sts = pci_cfgread(np->pci_tag, PCIR_STATUS, 2); #endif if (pci_sts & 0xf900) { #ifdef FreeBSD_Bus_Io_Abstraction pci_write_config(np->device, PCIR_STATUS, pci_sts, 2); #else pci_cfgwrite(np->pci_tag, PCIR_STATUS, pci_sts, 2); #endif printf("%s: PCI STATUS = 0x%04x\n", sym_name(np), pci_sts & 0xf900); } } } /* * chip interrupt handler * * In normal situations, interrupt conditions occur one at * a time. But when something bad happens on the SCSI BUS, * the chip may raise several interrupt flags before * stopping and interrupting the CPU. The additionnal * interrupt flags are stacked in some extra registers * after the SIP and/or DIP flag has been raised in the * ISTAT. After the CPU has read the interrupt condition * flag from SIST or DSTAT, the chip unstacks the other * interrupt flags and sets the corresponding bits in * SIST or DSTAT. Since the chip starts stacking once the * SIP or DIP flag is set, there is a small window of time * where the stacking does not occur. * * Typically, multiple interrupt conditions may happen in * the following situations: * * - SCSI parity error + Phase mismatch (PAR|MA) * When a parity error is detected in input phase * and the device switches to msg-in phase inside a * block MOV. * - SCSI parity error + Unexpected disconnect (PAR|UDC) * When a stupid device does not want to handle the * recovery of an SCSI parity error. * - Some combinations of STO, PAR, UDC, ... * When using non compliant SCSI stuff, when user is * doing non compliant hot tampering on the BUS, when * something really bad happens to a device, etc ... * * The heuristic suggested by SYMBIOS to handle * multiple interrupts is to try unstacking all * interrupts conditions and to handle them on some * priority based on error severity. * This will work when the unstacking has been * successful, but we cannot be 100 % sure of that, * since the CPU may have been faster to unstack than * the chip is able to stack. Hmmm ... But it seems that * such a situation is very unlikely to happen. * * If this happen, for example STO caught by the CPU * then UDC happenning before the CPU have restarted * the SCRIPTS, the driver may wrongly complete the * same command on UDC, since the SCRIPTS didn't restart * and the DSA still points to the same command. * We avoid this situation by setting the DSA to an * invalid value when the CCB is completed and before * restarting the SCRIPTS. * * Another issue is that we need some section of our * recovery procedures to be somehow uninterruptible but * the SCRIPTS processor does not provides such a * feature. For this reason, we handle recovery preferently * from the C code and check against some SCRIPTS critical * sections from the C code. * * Hopefully, the interrupt handling of the driver is now * able to resist to weird BUS error conditions, but donnot * ask me for any guarantee that it will never fail. :-) * Use at your own decision and risk. */ static void sym_intr1 (hcb_p np) { u_char istat, istatc; u_char dstat; u_short sist; /* * interrupt on the fly ? * * A `dummy read' is needed to ensure that the * clear of the INTF flag reaches the device * before the scanning of the DONE queue. */ istat = INB (nc_istat); if (istat & INTF) { OUTB (nc_istat, (istat & SIGP) | INTF | np->istat_sem); istat = INB (nc_istat); /* DUMMY READ */ if (DEBUG_FLAGS & DEBUG_TINY) printf ("F "); (void)sym_wakeup_done (np); }; if (!(istat & (SIP|DIP))) return; #if 0 /* We should never get this one */ if (istat & CABRT) OUTB (nc_istat, CABRT); #endif /* * PAR and MA interrupts may occur at the same time, * and we need to know of both in order to handle * this situation properly. We try to unstack SCSI * interrupts for that reason. BTW, I dislike a LOT * such a loop inside the interrupt routine. * Even if DMA interrupt stacking is very unlikely to * happen, we also try unstacking these ones, since * this has no performance impact. */ sist = 0; dstat = 0; istatc = istat; do { if (istatc & SIP) sist |= INW (nc_sist); if (istatc & DIP) dstat |= INB (nc_dstat); istatc = INB (nc_istat); istat |= istatc; } while (istatc & (SIP|DIP)); if (DEBUG_FLAGS & DEBUG_TINY) printf ("<%d|%x:%x|%x:%x>", (int)INB(nc_scr0), dstat,sist, (unsigned)INL(nc_dsp), (unsigned)INL(nc_dbc)); /* * On paper, a memory barrier may be needed here. * And since we are paranoid ... :) */ MEMORY_BARRIER(); /* * First, interrupts we want to service cleanly. * * Phase mismatch (MA) is the most frequent interrupt * for chip earlier than the 896 and so we have to service * it as quickly as possible. * A SCSI parity error (PAR) may be combined with a phase * mismatch condition (MA). * Programmed interrupts (SIR) are used to call the C code * from SCRIPTS. * The single step interrupt (SSI) is not used in this * driver. */ if (!(sist & (STO|GEN|HTH|SGE|UDC|SBMC|RST)) && !(dstat & (MDPE|BF|ABRT|IID))) { if (sist & PAR) sym_int_par (np, sist); else if (sist & MA) sym_int_ma (np); else if (dstat & SIR) sym_int_sir (np); else if (dstat & SSI) OUTONB_STD (); else goto unknown_int; return; }; /* * Now, interrupts that donnot happen in normal * situations and that we may need to recover from. * * On SCSI RESET (RST), we reset everything. * On SCSI BUS MODE CHANGE (SBMC), we complete all * active CCBs with RESET status, prepare all devices * for negotiating again and restart the SCRIPTS. * On STO and UDC, we complete the CCB with the corres- * ponding status and restart the SCRIPTS. */ if (sist & RST) { xpt_print_path(np->path); printf("SCSI BUS reset detected.\n"); sym_init (np, 1); return; }; OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ if (!(sist & (GEN|HTH|SGE)) && !(dstat & (MDPE|BF|ABRT|IID))) { if (sist & SBMC) sym_int_sbmc (np); else if (sist & STO) sym_int_sto (np); else if (sist & UDC) sym_int_udc (np); else goto unknown_int; return; }; /* * Now, interrupts we are not able to recover cleanly. * * Log message for hard errors. * Reset everything. */ sym_log_hard_error(np, sist, dstat); if ((sist & (GEN|HTH|SGE)) || (dstat & (MDPE|BF|ABRT|IID))) { sym_start_reset(np); return; }; unknown_int: /* * We just miss the cause of the interrupt. :( * Print a message. The timeout will do the real work. */ printf( "%s: unknown interrupt(s) ignored, " "ISTAT=0x%x DSTAT=0x%x SIST=0x%x\n", sym_name(np), istat, dstat, sist); } static void sym_intr(void *arg) { if (DEBUG_FLAGS & DEBUG_TINY) printf ("["); sym_intr1((hcb_p) arg); if (DEBUG_FLAGS & DEBUG_TINY) printf ("]"); return; } static void sym_poll(struct cam_sim *sim) { int s = splcam(); sym_intr(cam_sim_softc(sim)); splx(s); } /* * generic recovery from scsi interrupt * * The doc says that when the chip gets an SCSI interrupt, * it tries to stop in an orderly fashion, by completing * an instruction fetch that had started or by flushing * the DMA fifo for a write to memory that was executing. * Such a fashion is not enough to know if the instruction * that was just before the current DSP value has been * executed or not. * * There are some small SCRIPTS sections that deal with * the start queue and the done queue that may break any * assomption from the C code if we are interrupted * inside, so we reset if this happens. Btw, since these * SCRIPTS sections are executed while the SCRIPTS hasn't * started SCSI operations, it is very unlikely to happen. * * All the driver data structures are supposed to be * allocated from the same 4 GB memory window, so there * is a 1 to 1 relationship between DSA and driver data * structures. Since we are careful :) to invalidate the * DSA when we complete a command or when the SCRIPTS * pushes a DSA into a queue, we can trust it when it * points to a CCB. */ static void sym_recover_scsi_int (hcb_p np, u_char hsts) { u32 dsp = INL (nc_dsp); u32 dsa = INL (nc_dsa); ccb_p cp = sym_ccb_from_dsa(np, dsa); /* * If we haven't been interrupted inside the SCRIPTS * critical pathes, we can safely restart the SCRIPTS * and trust the DSA value if it matches a CCB. */ if ((!(dsp > SCRIPTA_BA (np, getjob_begin) && dsp < SCRIPTA_BA (np, getjob_end) + 1)) && (!(dsp > SCRIPTA_BA (np, ungetjob) && dsp < SCRIPTA_BA (np, reselect) + 1)) && (!(dsp > SCRIPTB_BA (np, sel_for_abort) && dsp < SCRIPTB_BA (np, sel_for_abort_1) + 1)) && (!(dsp > SCRIPTA_BA (np, done) && dsp < SCRIPTA_BA (np, done_end) + 1))) { OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ /* * If we have a CCB, let the SCRIPTS call us back for * the handling of the error with SCRATCHA filled with * STARTPOS. This way, we will be able to freeze the * device queue and requeue awaiting IOs. */ if (cp) { cp->host_status = hsts; OUTL_DSP (SCRIPTA_BA (np, complete_error)); } /* * Otherwise just restart the SCRIPTS. */ else { OUTL (nc_dsa, 0xffffff); OUTL_DSP (SCRIPTA_BA (np, start)); } } else goto reset_all; return; reset_all: sym_start_reset(np); } /* * chip exception handler for selection timeout */ static void sym_int_sto (hcb_p np) { u32 dsp = INL (nc_dsp); if (DEBUG_FLAGS & DEBUG_TINY) printf ("T"); if (dsp == SCRIPTA_BA (np, wf_sel_done) + 8) sym_recover_scsi_int(np, HS_SEL_TIMEOUT); else sym_start_reset(np); } /* * chip exception handler for unexpected disconnect */ static void sym_int_udc (hcb_p np) { printf ("%s: unexpected disconnect\n", sym_name(np)); sym_recover_scsi_int(np, HS_UNEXPECTED); } /* * chip exception handler for SCSI bus mode change * * spi2-r12 11.2.3 says a transceiver mode change must * generate a reset event and a device that detects a reset * event shall initiate a hard reset. It says also that a * device that detects a mode change shall set data transfer * mode to eight bit asynchronous, etc... * So, just reinitializing all except chip should be enough. */ static void sym_int_sbmc (hcb_p np) { u_char scsi_mode = INB (nc_stest4) & SMODE; /* * Notify user. */ xpt_print_path(np->path); printf("SCSI BUS mode change from %s to %s.\n", sym_scsi_bus_mode(np->scsi_mode), sym_scsi_bus_mode(scsi_mode)); /* * Should suspend command processing for a few seconds and * reinitialize all except the chip. */ sym_init (np, 2); } /* * chip exception handler for SCSI parity error. * * When the chip detects a SCSI parity error and is * currently executing a (CH)MOV instruction, it does * not interrupt immediately, but tries to finish the * transfer of the current scatter entry before * interrupting. The following situations may occur: * * - The complete scatter entry has been transferred * without the device having changed phase. * The chip will then interrupt with the DSP pointing * to the instruction that follows the MOV. * * - A phase mismatch occurs before the MOV finished * and phase errors are to be handled by the C code. * The chip will then interrupt with both PAR and MA * conditions set. * * - A phase mismatch occurs before the MOV finished and * phase errors are to be handled by SCRIPTS. * The chip will load the DSP with the phase mismatch * JUMP address and interrupt the host processor. */ static void sym_int_par (hcb_p np, u_short sist) { u_char hsts = INB (HS_PRT); u32 dsp = INL (nc_dsp); u32 dbc = INL (nc_dbc); u32 dsa = INL (nc_dsa); u_char sbcl = INB (nc_sbcl); u_char cmd = dbc >> 24; int phase = cmd & 7; ccb_p cp = sym_ccb_from_dsa(np, dsa); printf("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n", sym_name(np), hsts, dbc, sbcl); /* * Check that the chip is connected to the SCSI BUS. */ if (!(INB (nc_scntl1) & ISCON)) { sym_recover_scsi_int(np, HS_UNEXPECTED); return; } /* * If the nexus is not clearly identified, reset the bus. * We will try to do better later. */ if (!cp) goto reset_all; /* * Check instruction was a MOV, direction was INPUT and * ATN is asserted. */ if ((cmd & 0xc0) || !(phase & 1) || !(sbcl & 0x8)) goto reset_all; /* * Keep track of the parity error. */ OUTONB (HF_PRT, HF_EXT_ERR); cp->xerr_status |= XE_PARITY_ERR; /* * Prepare the message to send to the device. */ np->msgout[0] = (phase == 7) ? M_PARITY : M_ID_ERROR; /* * If the old phase was DATA IN phase, we have to deal with * the 3 situations described above. * For other input phases (MSG IN and STATUS), the device * must resend the whole thing that failed parity checking * or signal error. So, jumping to dispatcher should be OK. */ if (phase == 1 || phase == 5) { /* Phase mismatch handled by SCRIPTS */ if (dsp == SCRIPTB_BA (np, pm_handle)) OUTL_DSP (dsp); /* Phase mismatch handled by the C code */ else if (sist & MA) sym_int_ma (np); /* No phase mismatch occurred */ else { OUTL (nc_temp, dsp); OUTL_DSP (SCRIPTA_BA (np, dispatch)); } } else OUTL_DSP (SCRIPTA_BA (np, clrack)); return; reset_all: sym_start_reset(np); return; } /* * chip exception handler for phase errors. * * We have to construct a new transfer descriptor, * to transfer the rest of the current block. */ static void sym_int_ma (hcb_p np) { u32 dbc; u32 rest; u32 dsp; u32 dsa; u32 nxtdsp; u32 *vdsp; u32 oadr, olen; u32 *tblp; u32 newcmd; u_int delta; u_char cmd; u_char hflags, hflags0; struct sym_pmc *pm; ccb_p cp; dsp = INL (nc_dsp); dbc = INL (nc_dbc); dsa = INL (nc_dsa); cmd = dbc >> 24; rest = dbc & 0xffffff; delta = 0; /* * locate matching cp if any. */ cp = sym_ccb_from_dsa(np, dsa); /* * Donnot take into account dma fifo and various buffers in * INPUT phase since the chip flushes everything before * raising the MA interrupt for interrupted INPUT phases. * For DATA IN phase, we will check for the SWIDE later. */ if ((cmd & 7) != 1 && (cmd & 7) != 5) { u_char ss0, ss2; if (np->features & FE_DFBC) delta = INW (nc_dfbc); else { u32 dfifo; /* * Read DFIFO, CTEST[4-6] using 1 PCI bus ownership. */ dfifo = INL(nc_dfifo); /* * Calculate remaining bytes in DMA fifo. * (CTEST5 = dfifo >> 16) */ if (dfifo & (DFS << 16)) delta = ((((dfifo >> 8) & 0x300) | (dfifo & 0xff)) - rest) & 0x3ff; else delta = ((dfifo & 0xff) - rest) & 0x7f; } /* * The data in the dma fifo has not been transfered to * the target -> add the amount to the rest * and clear the data. * Check the sstat2 register in case of wide transfer. */ rest += delta; ss0 = INB (nc_sstat0); if (ss0 & OLF) rest++; if (!(np->features & FE_C10)) if (ss0 & ORF) rest++; if (cp && (cp->phys.select.sel_scntl3 & EWS)) { ss2 = INB (nc_sstat2); if (ss2 & OLF1) rest++; if (!(np->features & FE_C10)) if (ss2 & ORF1) rest++; }; /* * Clear fifos. */ OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* dma fifo */ OUTB (nc_stest3, TE|CSF); /* scsi fifo */ } /* * log the information */ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) printf ("P%x%x RL=%d D=%d ", cmd&7, INB(nc_sbcl)&7, (unsigned) rest, (unsigned) delta); /* * try to find the interrupted script command, * and the address at which to continue. */ vdsp = 0; nxtdsp = 0; if (dsp > np->scripta_ba && dsp <= np->scripta_ba + np->scripta_sz) { vdsp = (u32 *)((char*)np->scripta0 + (dsp-np->scripta_ba-8)); nxtdsp = dsp; } else if (dsp > np->scriptb_ba && dsp <= np->scriptb_ba + np->scriptb_sz) { vdsp = (u32 *)((char*)np->scriptb0 + (dsp-np->scriptb_ba-8)); nxtdsp = dsp; } /* * log the information */ if (DEBUG_FLAGS & DEBUG_PHASE) { printf ("\nCP=%p DSP=%x NXT=%x VDSP=%p CMD=%x ", cp, (unsigned)dsp, (unsigned)nxtdsp, vdsp, cmd); }; if (!vdsp) { printf ("%s: interrupted SCRIPT address not found.\n", sym_name (np)); goto reset_all; } if (!cp) { printf ("%s: SCSI phase error fixup: CCB already dequeued.\n", sym_name (np)); goto reset_all; } /* * get old startaddress and old length. */ oadr = scr_to_cpu(vdsp[1]); if (cmd & 0x10) { /* Table indirect */ tblp = (u32 *) ((char*) &cp->phys + oadr); olen = scr_to_cpu(tblp[0]); oadr = scr_to_cpu(tblp[1]); } else { tblp = (u32 *) 0; olen = scr_to_cpu(vdsp[0]) & 0xffffff; }; if (DEBUG_FLAGS & DEBUG_PHASE) { printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n", (unsigned) (scr_to_cpu(vdsp[0]) >> 24), tblp, (unsigned) olen, (unsigned) oadr); }; /* * check cmd against assumed interrupted script command. * If dt data phase, the MOVE instruction hasn't bit 4 of * the phase. */ if (((cmd & 2) ? cmd : (cmd & ~4)) != (scr_to_cpu(vdsp[0]) >> 24)) { PRINT_ADDR(cp); printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n", (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24); goto reset_all; }; /* * if old phase not dataphase, leave here. */ if (cmd & 2) { PRINT_ADDR(cp); printf ("phase change %x-%x %d@%08x resid=%d.\n", cmd&7, INB(nc_sbcl)&7, (unsigned)olen, (unsigned)oadr, (unsigned)rest); goto unexpected_phase; }; /* * Choose the correct PM save area. * * Look at the PM_SAVE SCRIPT if you want to understand * this stuff. The equivalent code is implemented in * SCRIPTS for the 895A, 896 and 1010 that are able to * handle PM from the SCRIPTS processor. */ hflags0 = INB (HF_PRT); hflags = hflags0; if (hflags & (HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED)) { if (hflags & HF_IN_PM0) nxtdsp = scr_to_cpu(cp->phys.pm0.ret); else if (hflags & HF_IN_PM1) nxtdsp = scr_to_cpu(cp->phys.pm1.ret); if (hflags & HF_DP_SAVED) hflags ^= HF_ACT_PM; } if (!(hflags & HF_ACT_PM)) { pm = &cp->phys.pm0; newcmd = SCRIPTA_BA (np, pm0_data); } else { pm = &cp->phys.pm1; newcmd = SCRIPTA_BA (np, pm1_data); } hflags &= ~(HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED); if (hflags != hflags0) OUTB (HF_PRT, hflags); /* * fillin the phase mismatch context */ pm->sg.addr = cpu_to_scr(oadr + olen - rest); pm->sg.size = cpu_to_scr(rest); pm->ret = cpu_to_scr(nxtdsp); /* * If we have a SWIDE, * - prepare the address to write the SWIDE from SCRIPTS, * - compute the SCRIPTS address to restart from, * - move current data pointer context by one byte. */ nxtdsp = SCRIPTA_BA (np, dispatch); if ((cmd & 7) == 1 && cp && (cp->phys.select.sel_scntl3 & EWS) && (INB (nc_scntl2) & WSR)) { u32 tmp; /* * Set up the table indirect for the MOVE * of the residual byte and adjust the data * pointer context. */ tmp = scr_to_cpu(pm->sg.addr); cp->phys.wresid.addr = cpu_to_scr(tmp); pm->sg.addr = cpu_to_scr(tmp + 1); tmp = scr_to_cpu(pm->sg.size); cp->phys.wresid.size = cpu_to_scr((tmp&0xff000000) | 1); pm->sg.size = cpu_to_scr(tmp - 1); /* * If only the residual byte is to be moved, * no PM context is needed. */ if ((tmp&0xffffff) == 1) newcmd = pm->ret; /* * Prepare the address of SCRIPTS that will * move the residual byte to memory. */ nxtdsp = SCRIPTB_BA (np, wsr_ma_helper); } if (DEBUG_FLAGS & DEBUG_PHASE) { PRINT_ADDR(cp); printf ("PM %x %x %x / %x %x %x.\n", hflags0, hflags, newcmd, (unsigned)scr_to_cpu(pm->sg.addr), (unsigned)scr_to_cpu(pm->sg.size), (unsigned)scr_to_cpu(pm->ret)); } /* * Restart the SCRIPTS processor. */ OUTL (nc_temp, newcmd); OUTL_DSP (nxtdsp); return; /* * Unexpected phase changes that occurs when the current phase * is not a DATA IN or DATA OUT phase are due to error conditions. * Such event may only happen when the SCRIPTS is using a * multibyte SCSI MOVE. * * Phase change Some possible cause * * COMMAND --> MSG IN SCSI parity error detected by target. * COMMAND --> STATUS Bad command or refused by target. * MSG OUT --> MSG IN Message rejected by target. * MSG OUT --> COMMAND Bogus target that discards extended * negotiation messages. * * The code below does not care of the new phase and so * trusts the target. Why to annoy it ? * If the interrupted phase is COMMAND phase, we restart at * dispatcher. * If a target does not get all the messages after selection, * the code assumes blindly that the target discards extended * messages and clears the negotiation status. * If the target does not want all our response to negotiation, * we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids * bloat for such a should_not_happen situation). * In all other situation, we reset the BUS. * Are these assumptions reasonnable ? (Wait and see ...) */ unexpected_phase: dsp -= 8; nxtdsp = 0; switch (cmd & 7) { case 2: /* COMMAND phase */ nxtdsp = SCRIPTA_BA (np, dispatch); break; #if 0 case 3: /* STATUS phase */ nxtdsp = SCRIPTA_BA (np, dispatch); break; #endif case 6: /* MSG OUT phase */ /* * If the device may want to use untagged when we want * tagged, we prepare an IDENTIFY without disc. granted, * since we will not be able to handle reselect. * Otherwise, we just don't care. */ if (dsp == SCRIPTA_BA (np, send_ident)) { if (cp->tag != NO_TAG && olen - rest <= 3) { cp->host_status = HS_BUSY; np->msgout[0] = M_IDENTIFY | cp->lun; nxtdsp = SCRIPTB_BA (np, ident_break_atn); } else nxtdsp = SCRIPTB_BA (np, ident_break); } else if (dsp == SCRIPTB_BA (np, send_wdtr) || dsp == SCRIPTB_BA (np, send_sdtr) || dsp == SCRIPTB_BA (np, send_ppr)) { nxtdsp = SCRIPTB_BA (np, nego_bad_phase); } break; #if 0 case 7: /* MSG IN phase */ nxtdsp = SCRIPTA_BA (np, clrack); break; #endif } if (nxtdsp) { OUTL_DSP (nxtdsp); return; } reset_all: sym_start_reset(np); } /* * Dequeue from the START queue all CCBs that match * a given target/lun/task condition (-1 means all), * and move them from the BUSY queue to the COMP queue * with CAM_REQUEUE_REQ status condition. * This function is used during error handling/recovery. * It is called with SCRIPTS not running. */ static int sym_dequeue_from_squeue(hcb_p np, int i, int target, int lun, int task) { int j; ccb_p cp; /* * Make sure the starting index is within range. */ assert((i >= 0) && (i < 2*MAX_QUEUE)); /* * Walk until end of START queue and dequeue every job * that matches the target/lun/task condition. */ j = i; while (i != np->squeueput) { cp = sym_ccb_from_dsa(np, scr_to_cpu(np->squeue[i])); assert(cp); #ifdef SYM_CONF_IARB_SUPPORT /* Forget hints for IARB, they may be no longer relevant */ cp->host_flags &= ~HF_HINT_IARB; #endif if ((target == -1 || cp->target == target) && (lun == -1 || cp->lun == lun) && (task == -1 || cp->tag == task)) { sym_set_cam_status(cp->cam_ccb, CAM_REQUEUE_REQ); sym_remque(&cp->link_ccbq); sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq); } else { if (i != j) np->squeue[j] = np->squeue[i]; if ((j += 2) >= MAX_QUEUE*2) j = 0; } if ((i += 2) >= MAX_QUEUE*2) i = 0; } if (i != j) /* Copy back the idle task if needed */ np->squeue[j] = np->squeue[i]; np->squeueput = j; /* Update our current start queue pointer */ return (i - j) / 2; } /* * Complete all CCBs queued to the COMP queue. * * These CCBs are assumed: * - Not to be referenced either by devices or * SCRIPTS-related queues and datas. * - To have to be completed with an error condition * or requeued. * * The device queue freeze count is incremented * for each CCB that does not prevent this. * This function is called when all CCBs involved * in error handling/recovery have been reaped. */ static void sym_flush_comp_queue(hcb_p np, int cam_status) { SYM_QUEHEAD *qp; ccb_p cp; while ((qp = sym_remque_head(&np->comp_ccbq)) != 0) { union ccb *ccb; cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq); /* Leave quiet CCBs waiting for resources */ if (cp->host_status == HS_WAIT) continue; ccb = cp->cam_ccb; if (cam_status) sym_set_cam_status(ccb, cam_status); sym_free_ccb(np, cp); sym_freeze_cam_ccb(ccb); sym_xpt_done(np, ccb); } } /* * chip handler for bad SCSI status condition * * In case of bad SCSI status, we unqueue all the tasks * currently queued to the controller but not yet started * and then restart the SCRIPTS processor immediately. * * QUEUE FULL and BUSY conditions are handled the same way. * Basically all the not yet started tasks are requeued in * device queue and the queue is frozen until a completion. * * For CHECK CONDITION and COMMAND TERMINATED status, we use * the CCB of the failed command to prepare a REQUEST SENSE * SCSI command and queue it to the controller queue. * * SCRATCHA is assumed to have been loaded with STARTPOS * before the SCRIPTS called the C code. */ static void sym_sir_bad_scsi_status(hcb_p np, int num, ccb_p cp) { tcb_p tp = &np->target[cp->target]; u32 startp; u_char s_status = cp->ssss_status; u_char h_flags = cp->host_flags; int msglen; int nego; int i; /* * Compute the index of the next job to start from SCRIPTS. */ i = (INL (nc_scratcha) - np->squeue_ba) / 4; /* * The last CCB queued used for IARB hint may be * no longer relevant. Forget it. */ #ifdef SYM_CONF_IARB_SUPPORT if (np->last_cp) np->last_cp = 0; #endif /* * Now deal with the SCSI status. */ switch(s_status) { case S_BUSY: case S_QUEUE_FULL: if (sym_verbose >= 2) { PRINT_ADDR(cp); printf (s_status == S_BUSY ? "BUSY" : "QUEUE FULL\n"); } default: /* S_INT, S_INT_COND_MET, S_CONFLICT */ sym_complete_error (np, cp); break; case S_TERMINATED: case S_CHECK_COND: /* * If we get an SCSI error when requesting sense, give up. */ if (h_flags & HF_SENSE) { sym_complete_error (np, cp); break; } /* * Dequeue all queued CCBs for that device not yet started, * and restart the SCRIPTS processor immediately. */ (void) sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1); OUTL_DSP (SCRIPTA_BA (np, start)); /* * Save some info of the actual IO. * Compute the data residual. */ cp->sv_scsi_status = cp->ssss_status; cp->sv_xerr_status = cp->xerr_status; cp->sv_resid = sym_compute_residual(np, cp); /* * Prepare all needed data structures for * requesting sense data. */ /* * identify message */ cp->scsi_smsg2[0] = M_IDENTIFY | cp->lun; msglen = 1; /* * If we are currently using anything different from * async. 8 bit data transfers with that target, * start a negotiation, since the device may want * to report us a UNIT ATTENTION condition due to * a cause we currently ignore, and we donnot want * to be stuck with WIDE and/or SYNC data transfer. * * cp->nego_status is filled by sym_prepare_nego(). */ cp->nego_status = 0; nego = 0; if (tp->tinfo.current.options & PPR_OPT_MASK) nego = NS_PPR; else if (tp->tinfo.current.width != BUS_8_BIT) nego = NS_WIDE; else if (tp->tinfo.current.offset != 0) nego = NS_SYNC; if (nego) msglen += sym_prepare_nego (np,cp, nego, &cp->scsi_smsg2[msglen]); /* * Message table indirect structure. */ cp->phys.smsg.addr = cpu_to_scr(CCB_BA (cp, scsi_smsg2)); cp->phys.smsg.size = cpu_to_scr(msglen); /* * sense command */ cp->phys.cmd.addr = cpu_to_scr(CCB_BA (cp, sensecmd)); cp->phys.cmd.size = cpu_to_scr(6); /* * patch requested size into sense command */ cp->sensecmd[0] = 0x03; cp->sensecmd[1] = cp->lun << 5; #ifdef FreeBSD_New_Tran_Settings if (tp->tinfo.current.scsi_version > 2 || cp->lun > 7) cp->sensecmd[1] = 0; #endif cp->sensecmd[4] = SYM_SNS_BBUF_LEN; cp->data_len = SYM_SNS_BBUF_LEN; /* * sense data */ bzero(cp->sns_bbuf, SYM_SNS_BBUF_LEN); cp->phys.sense.addr = cpu_to_scr(vtobus(cp->sns_bbuf)); cp->phys.sense.size = cpu_to_scr(SYM_SNS_BBUF_LEN); /* * requeue the command. */ startp = SCRIPTB_BA (np, sdata_in); cp->phys.head.savep = cpu_to_scr(startp); cp->phys.head.goalp = cpu_to_scr(startp + 16); cp->phys.head.lastp = cpu_to_scr(startp); cp->startp = cpu_to_scr(startp); cp->actualquirks = SYM_QUIRK_AUTOSAVE; cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; cp->ssss_status = S_ILLEGAL; cp->host_flags = (HF_SENSE|HF_DATA_IN); cp->xerr_status = 0; cp->extra_bytes = 0; cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA (np, select)); /* * Requeue the command. */ sym_put_start_queue(np, cp); /* * Give back to upper layer everything we have dequeued. */ sym_flush_comp_queue(np, 0); break; } } /* * After a device has accepted some management message * as BUS DEVICE RESET, ABORT TASK, etc ..., or when * a device signals a UNIT ATTENTION condition, some * tasks are thrown away by the device. We are required * to reflect that on our tasks list since the device * will never complete these tasks. * * This function move from the BUSY queue to the COMP * queue all disconnected CCBs for a given target that * match the following criteria: * - lun=-1 means any logical UNIT otherwise a given one. * - task=-1 means any task, otherwise a given one. */ static int sym_clear_tasks(hcb_p np, int cam_status, int target, int lun, int task) { SYM_QUEHEAD qtmp, *qp; int i = 0; ccb_p cp; /* * Move the entire BUSY queue to our temporary queue. */ sym_que_init(&qtmp); sym_que_splice(&np->busy_ccbq, &qtmp); sym_que_init(&np->busy_ccbq); /* * Put all CCBs that matches our criteria into * the COMP queue and put back other ones into * the BUSY queue. */ while ((qp = sym_remque_head(&qtmp)) != 0) { union ccb *ccb; cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); ccb = cp->cam_ccb; if (cp->host_status != HS_DISCONNECT || cp->target != target || (lun != -1 && cp->lun != lun) || (task != -1 && (cp->tag != NO_TAG && cp->scsi_smsg[2] != task))) { sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq); continue; } sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq); /* Preserve the software timeout condition */ if (sym_get_cam_status(ccb) != CAM_CMD_TIMEOUT) sym_set_cam_status(ccb, cam_status); ++i; #if 0 printf("XXXX TASK @%p CLEARED\n", cp); #endif } return i; } /* * chip handler for TASKS recovery * * We cannot safely abort a command, while the SCRIPTS * processor is running, since we just would be in race * with it. * * As long as we have tasks to abort, we keep the SEM * bit set in the ISTAT. When this bit is set, the * SCRIPTS processor interrupts (SIR_SCRIPT_STOPPED) * each time it enters the scheduler. * * If we have to reset a target, clear tasks of a unit, * or to perform the abort of a disconnected job, we * restart the SCRIPTS for selecting the target. Once * selected, the SCRIPTS interrupts (SIR_TARGET_SELECTED). * If it loses arbitration, the SCRIPTS will interrupt again * the next time it will enter its scheduler, and so on ... * * On SIR_TARGET_SELECTED, we scan for the more * appropriate thing to do: * * - If nothing, we just sent a M_ABORT message to the * target to get rid of the useless SCSI bus ownership. * According to the specs, no tasks shall be affected. * - If the target is to be reset, we send it a M_RESET * message. * - If a logical UNIT is to be cleared , we send the * IDENTIFY(lun) + M_ABORT. * - If an untagged task is to be aborted, we send the * IDENTIFY(lun) + M_ABORT. * - If a tagged task is to be aborted, we send the * IDENTIFY(lun) + task attributes + M_ABORT_TAG. * * Once our 'kiss of death' :) message has been accepted * by the target, the SCRIPTS interrupts again * (SIR_ABORT_SENT). On this interrupt, we complete * all the CCBs that should have been aborted by the * target according to our message. */ static void sym_sir_task_recovery(hcb_p np, int num) { SYM_QUEHEAD *qp; ccb_p cp; tcb_p tp; int target=-1, lun=-1, task; int i, k; switch(num) { /* * The SCRIPTS processor stopped before starting * the next command in order to allow us to perform * some task recovery. */ case SIR_SCRIPT_STOPPED: /* * Do we have any target to reset or unit to clear ? */ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { tp = &np->target[i]; if (tp->to_reset || (tp->lun0p && tp->lun0p->to_clear)) { target = i; break; } if (!tp->lunmp) continue; for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) { if (tp->lunmp[k] && tp->lunmp[k]->to_clear) { target = i; break; } } if (target != -1) break; } /* * If not, walk the busy queue for any * disconnected CCB to be aborted. */ if (target == -1) { FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { cp = sym_que_entry(qp,struct sym_ccb,link_ccbq); if (cp->host_status != HS_DISCONNECT) continue; if (cp->to_abort) { target = cp->target; break; } } } /* * If some target is to be selected, * prepare and start the selection. */ if (target != -1) { tp = &np->target[target]; np->abrt_sel.sel_id = target; np->abrt_sel.sel_scntl3 = tp->head.wval; np->abrt_sel.sel_sxfer = tp->head.sval; OUTL(nc_dsa, np->hcb_ba); OUTL_DSP (SCRIPTB_BA (np, sel_for_abort)); return; } /* * Now look for a CCB to abort that haven't started yet. * Btw, the SCRIPTS processor is still stopped, so * we are not in race. */ i = 0; cp = 0; FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); if (cp->host_status != HS_BUSY && cp->host_status != HS_NEGOTIATE) continue; if (!cp->to_abort) continue; #ifdef SYM_CONF_IARB_SUPPORT /* * If we are using IMMEDIATE ARBITRATION, we donnot * want to cancel the last queued CCB, since the * SCRIPTS may have anticipated the selection. */ if (cp == np->last_cp) { cp->to_abort = 0; continue; } #endif i = 1; /* Means we have found some */ break; } if (!i) { /* * We are done, so we donnot need * to synchronize with the SCRIPTS anylonger. * Remove the SEM flag from the ISTAT. */ np->istat_sem = 0; OUTB (nc_istat, SIGP); break; } /* * Compute index of next position in the start * queue the SCRIPTS intends to start and dequeue * all CCBs for that device that haven't been started. */ i = (INL (nc_scratcha) - np->squeue_ba) / 4; i = sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1); /* * Make sure at least our IO to abort has been dequeued. */ assert(i && sym_get_cam_status(cp->cam_ccb) == CAM_REQUEUE_REQ); /* * Keep track in cam status of the reason of the abort. */ if (cp->to_abort == 2) sym_set_cam_status(cp->cam_ccb, CAM_CMD_TIMEOUT); else sym_set_cam_status(cp->cam_ccb, CAM_REQ_ABORTED); /* * Complete with error everything that we have dequeued. */ sym_flush_comp_queue(np, 0); break; /* * The SCRIPTS processor has selected a target * we may have some manual recovery to perform for. */ case SIR_TARGET_SELECTED: target = (INB (nc_sdid) & 0xf); tp = &np->target[target]; np->abrt_tbl.addr = cpu_to_scr(vtobus(np->abrt_msg)); /* * If the target is to be reset, prepare a * M_RESET message and clear the to_reset flag * since we donnot expect this operation to fail. */ if (tp->to_reset) { np->abrt_msg[0] = M_RESET; np->abrt_tbl.size = 1; tp->to_reset = 0; break; } /* * Otherwise, look for some logical unit to be cleared. */ if (tp->lun0p && tp->lun0p->to_clear) lun = 0; else if (tp->lunmp) { for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) { if (tp->lunmp[k] && tp->lunmp[k]->to_clear) { lun = k; break; } } } /* * If a logical unit is to be cleared, prepare * an IDENTIFY(lun) + ABORT MESSAGE. */ if (lun != -1) { lcb_p lp = sym_lp(np, tp, lun); lp->to_clear = 0; /* We donnot expect to fail here */ np->abrt_msg[0] = M_IDENTIFY | lun; np->abrt_msg[1] = M_ABORT; np->abrt_tbl.size = 2; break; } /* * Otherwise, look for some disconnected job to * abort for this target. */ i = 0; cp = 0; FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); if (cp->host_status != HS_DISCONNECT) continue; if (cp->target != target) continue; if (!cp->to_abort) continue; i = 1; /* Means we have some */ break; } /* * If we have none, probably since the device has * completed the command before we won abitration, * send a M_ABORT message without IDENTIFY. * According to the specs, the device must just * disconnect the BUS and not abort any task. */ if (!i) { np->abrt_msg[0] = M_ABORT; np->abrt_tbl.size = 1; break; } /* * We have some task to abort. * Set the IDENTIFY(lun) */ np->abrt_msg[0] = M_IDENTIFY | cp->lun; /* * If we want to abort an untagged command, we * will send an IDENTIFY + M_ABORT. * Otherwise (tagged command), we will send * an IDENTIFY + task attributes + ABORT TAG. */ if (cp->tag == NO_TAG) { np->abrt_msg[1] = M_ABORT; np->abrt_tbl.size = 2; } else { np->abrt_msg[1] = cp->scsi_smsg[1]; np->abrt_msg[2] = cp->scsi_smsg[2]; np->abrt_msg[3] = M_ABORT_TAG; np->abrt_tbl.size = 4; } /* * Keep track of software timeout condition, since the * peripheral driver may not count retries on abort * conditions not due to timeout. */ if (cp->to_abort == 2) sym_set_cam_status(cp->cam_ccb, CAM_CMD_TIMEOUT); cp->to_abort = 0; /* We donnot expect to fail here */ break; /* * The target has accepted our message and switched * to BUS FREE phase as we expected. */ case SIR_ABORT_SENT: target = (INB (nc_sdid) & 0xf); tp = &np->target[target]; /* ** If we didn't abort anything, leave here. */ if (np->abrt_msg[0] == M_ABORT) break; /* * If we sent a M_RESET, then a hardware reset has * been performed by the target. * - Reset everything to async 8 bit * - Tell ourself to negotiate next time :-) * - Prepare to clear all disconnected CCBs for * this target from our task list (lun=task=-1) */ lun = -1; task = -1; if (np->abrt_msg[0] == M_RESET) { tp->head.sval = 0; tp->head.wval = np->rv_scntl3; tp->head.uval = 0; tp->tinfo.current.period = 0; tp->tinfo.current.offset = 0; tp->tinfo.current.width = BUS_8_BIT; tp->tinfo.current.options = 0; } /* * Otherwise, check for the LUN and TASK(s) * concerned by the cancelation. * If it is not ABORT_TAG then it is CLEAR_QUEUE * or an ABORT message :-) */ else { lun = np->abrt_msg[0] & 0x3f; if (np->abrt_msg[1] == M_ABORT_TAG) task = np->abrt_msg[2]; } /* * Complete all the CCBs the device should have * aborted due to our 'kiss of death' message. */ i = (INL (nc_scratcha) - np->squeue_ba) / 4; (void) sym_dequeue_from_squeue(np, i, target, lun, -1); (void) sym_clear_tasks(np, CAM_REQ_ABORTED, target, lun, task); sym_flush_comp_queue(np, 0); /* * If we sent a BDR, make uper layer aware of that. */ if (np->abrt_msg[0] == M_RESET) xpt_async(AC_SENT_BDR, np->path, NULL); break; } /* * Print to the log the message we intend to send. */ if (num == SIR_TARGET_SELECTED) { PRINT_TARGET(np, target); sym_printl_hex("control msgout:", np->abrt_msg, np->abrt_tbl.size); np->abrt_tbl.size = cpu_to_scr(np->abrt_tbl.size); } /* * Let the SCRIPTS processor continue. */ OUTONB_STD (); } /* * Gerard's alchemy:) that deals with with the data * pointer for both MDP and the residual calculation. * * I didn't want to bloat the code by more than 200 * lignes for the handling of both MDP and the residual. * This has been achieved by using a data pointer * representation consisting in an index in the data * array (dp_sg) and a negative offset (dp_ofs) that * have the following meaning: * * - dp_sg = SYM_CONF_MAX_SG * we are at the end of the data script. * - dp_sg < SYM_CONF_MAX_SG * dp_sg points to the next entry of the scatter array * we want to transfer. * - dp_ofs < 0 * dp_ofs represents the residual of bytes of the * previous entry scatter entry we will send first. * - dp_ofs = 0 * no residual to send first. * * The function sym_evaluate_dp() accepts an arbitray * offset (basically from the MDP message) and returns * the corresponding values of dp_sg and dp_ofs. */ static int sym_evaluate_dp(hcb_p np, ccb_p cp, u32 scr, int *ofs) { u32 dp_scr; int dp_ofs, dp_sg, dp_sgmin; int tmp; struct sym_pmc *pm; /* * Compute the resulted data pointer in term of a script * address within some DATA script and a signed byte offset. */ dp_scr = scr; dp_ofs = *ofs; if (dp_scr == SCRIPTA_BA (np, pm0_data)) pm = &cp->phys.pm0; else if (dp_scr == SCRIPTA_BA (np, pm1_data)) pm = &cp->phys.pm1; else pm = 0; if (pm) { dp_scr = scr_to_cpu(pm->ret); dp_ofs -= scr_to_cpu(pm->sg.size); } /* * If we are auto-sensing, then we are done. */ if (cp->host_flags & HF_SENSE) { *ofs = dp_ofs; return 0; } /* * Deduce the index of the sg entry. * Keep track of the index of the first valid entry. * If result is dp_sg = SYM_CONF_MAX_SG, then we are at the * end of the data. */ tmp = scr_to_cpu(cp->phys.head.goalp); dp_sg = SYM_CONF_MAX_SG; if (dp_scr != tmp) dp_sg -= (tmp - 8 - (int)dp_scr) / (2*4); dp_sgmin = SYM_CONF_MAX_SG - cp->segments; /* * Move to the sg entry the data pointer belongs to. * * If we are inside the data area, we expect result to be: * * Either, * dp_ofs = 0 and dp_sg is the index of the sg entry * the data pointer belongs to (or the end of the data) * Or, * dp_ofs < 0 and dp_sg is the index of the sg entry * the data pointer belongs to + 1. */ if (dp_ofs < 0) { int n; while (dp_sg > dp_sgmin) { --dp_sg; tmp = scr_to_cpu(cp->phys.data[dp_sg].size); n = dp_ofs + (tmp & 0xffffff); if (n > 0) { ++dp_sg; break; } dp_ofs = n; } } else if (dp_ofs > 0) { while (dp_sg < SYM_CONF_MAX_SG) { tmp = scr_to_cpu(cp->phys.data[dp_sg].size); dp_ofs -= (tmp & 0xffffff); ++dp_sg; if (dp_ofs <= 0) break; } } /* * Make sure the data pointer is inside the data area. * If not, return some error. */ if (dp_sg < dp_sgmin || (dp_sg == dp_sgmin && dp_ofs < 0)) goto out_err; else if (dp_sg > SYM_CONF_MAX_SG || (dp_sg == SYM_CONF_MAX_SG && dp_ofs > 0)) goto out_err; /* * Save the extreme pointer if needed. */ if (dp_sg > cp->ext_sg || (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) { cp->ext_sg = dp_sg; cp->ext_ofs = dp_ofs; } /* * Return data. */ *ofs = dp_ofs; return dp_sg; out_err: return -1; } /* * chip handler for MODIFY DATA POINTER MESSAGE * * We also call this function on IGNORE WIDE RESIDUE * messages that do not match a SWIDE full condition. * Btw, we assume in that situation that such a message * is equivalent to a MODIFY DATA POINTER (offset=-1). */ static void sym_modify_dp(hcb_p np, tcb_p tp, ccb_p cp, int ofs) { int dp_ofs = ofs; u32 dp_scr = INL (nc_temp); u32 dp_ret; u32 tmp; u_char hflags; int dp_sg; struct sym_pmc *pm; /* * Not supported for auto-sense. */ if (cp->host_flags & HF_SENSE) goto out_reject; /* * Apply our alchemy:) (see comments in sym_evaluate_dp()), * to the resulted data pointer. */ dp_sg = sym_evaluate_dp(np, cp, dp_scr, &dp_ofs); if (dp_sg < 0) goto out_reject; /* * And our alchemy:) allows to easily calculate the data * script address we want to return for the next data phase. */ dp_ret = cpu_to_scr(cp->phys.head.goalp); dp_ret = dp_ret - 8 - (SYM_CONF_MAX_SG - dp_sg) * (2*4); /* * If offset / scatter entry is zero we donnot need * a context for the new current data pointer. */ if (dp_ofs == 0) { dp_scr = dp_ret; goto out_ok; } /* * Get a context for the new current data pointer. */ hflags = INB (HF_PRT); if (hflags & HF_DP_SAVED) hflags ^= HF_ACT_PM; if (!(hflags & HF_ACT_PM)) { pm = &cp->phys.pm0; dp_scr = SCRIPTA_BA (np, pm0_data); } else { pm = &cp->phys.pm1; dp_scr = SCRIPTA_BA (np, pm1_data); } hflags &= ~(HF_DP_SAVED); OUTB (HF_PRT, hflags); /* * Set up the new current data pointer. * ofs < 0 there, and for the next data phase, we * want to transfer part of the data of the sg entry * corresponding to index dp_sg-1 prior to returning * to the main data script. */ pm->ret = cpu_to_scr(dp_ret); tmp = scr_to_cpu(cp->phys.data[dp_sg-1].addr); tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs; pm->sg.addr = cpu_to_scr(tmp); pm->sg.size = cpu_to_scr(-dp_ofs); out_ok: OUTL (nc_temp, dp_scr); OUTL_DSP (SCRIPTA_BA (np, clrack)); return; out_reject: OUTL_DSP (SCRIPTB_BA (np, msg_bad)); } /* * chip calculation of the data residual. * * As I used to say, the requirement of data residual * in SCSI is broken, useless and cannot be achieved * without huge complexity. * But most OSes and even the official CAM require it. * When stupidity happens to be so widely spread inside * a community, it gets hard to convince. * * Anyway, I don't care, since I am not going to use * any software that considers this data residual as * a relevant information. :) */ static int sym_compute_residual(hcb_p np, ccb_p cp) { int dp_sg, dp_sgmin, resid = 0; int dp_ofs = 0; /* * Check for some data lost or just thrown away. * We are not required to be quite accurate in this * situation. Btw, if we are odd for output and the * device claims some more data, it may well happen * than our residual be zero. :-) */ if (cp->xerr_status & (XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN)) { if (cp->xerr_status & XE_EXTRA_DATA) resid -= cp->extra_bytes; if (cp->xerr_status & XE_SODL_UNRUN) ++resid; if (cp->xerr_status & XE_SWIDE_OVRUN) --resid; } /* * If all data has been transferred, * there is no residual. */ if (cp->phys.head.lastp == cp->phys.head.goalp) return resid; /* * If no data transfer occurs, or if the data * pointer is weird, return full residual. */ if (cp->startp == cp->phys.head.lastp || sym_evaluate_dp(np, cp, scr_to_cpu(cp->phys.head.lastp), &dp_ofs) < 0) { return cp->data_len; } /* * If we were auto-sensing, then we are done. */ if (cp->host_flags & HF_SENSE) { return -dp_ofs; } /* * We are now full comfortable in the computation * of the data residual (2's complement). */ dp_sgmin = SYM_CONF_MAX_SG - cp->segments; resid = -cp->ext_ofs; for (dp_sg = cp->ext_sg; dp_sg < SYM_CONF_MAX_SG; ++dp_sg) { u_int tmp = scr_to_cpu(cp->phys.data[dp_sg].size); resid += (tmp & 0xffffff); } /* * Hopefully, the result is not too wrong. */ return resid; } /* * Print out the content of a SCSI message. */ static int sym_show_msg (u_char * msg) { u_char i; printf ("%x",*msg); if (*msg==M_EXTENDED) { for (i=1;i<8;i++) { if (i-1>msg[1]) break; printf ("-%x",msg[i]); }; return (i+1); } else if ((*msg & 0xf0) == 0x20) { printf ("-%x",msg[1]); return (2); }; return (1); } static void sym_print_msg (ccb_p cp, char *label, u_char *msg) { PRINT_ADDR(cp); if (label) printf ("%s: ", label); (void) sym_show_msg (msg); printf (".\n"); } /* * Negotiation for WIDE and SYNCHRONOUS DATA TRANSFER. * * When we try to negotiate, we append the negotiation message * to the identify and (maybe) simple tag message. * The host status field is set to HS_NEGOTIATE to mark this * situation. * * If the target doesn't answer this message immediately * (as required by the standard), the SIR_NEGO_FAILED interrupt * will be raised eventually. * The handler removes the HS_NEGOTIATE status, and sets the * negotiated value to the default (async / nowide). * * If we receive a matching answer immediately, we check it * for validity, and set the values. * * If we receive a Reject message immediately, we assume the * negotiation has failed, and fall back to standard values. * * If we receive a negotiation message while not in HS_NEGOTIATE * state, it's a target initiated negotiation. We prepare a * (hopefully) valid answer, set our parameters, and send back * this answer to the target. * * If the target doesn't fetch the answer (no message out phase), * we assume the negotiation has failed, and fall back to default * settings (SIR_NEGO_PROTO interrupt). * * When we set the values, we adjust them in all ccbs belonging * to this target, in the controller's register, and in the "phys" * field of the controller's struct sym_hcb. */ /* * chip handler for SYNCHRONOUS DATA TRANSFER REQUEST (SDTR) message. */ static void sym_sync_nego(hcb_p np, tcb_p tp, ccb_p cp) { u_char chg, ofs, per, fak, div; int req = 1; /* * Synchronous request message received. */ if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "sync msgin", np->msgin); }; /* * request or answer ? */ if (INB (HS_PRT) == HS_NEGOTIATE) { OUTB (HS_PRT, HS_BUSY); if (cp->nego_status && cp->nego_status != NS_SYNC) goto reject_it; req = 0; } /* * get requested values. */ chg = 0; per = np->msgin[3]; ofs = np->msgin[4]; /* * check values against our limits. */ if (ofs) { if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} if (req) { if (ofs > tp->tinfo.user.offset) {chg = 1; ofs = tp->tinfo.user.offset;} } } if (ofs) { if (per < np->minsync) {chg = 1; per = np->minsync;} if (req) { if (per < tp->tinfo.user.period) {chg = 1; per = tp->tinfo.user.period;} } } div = fak = 0; if (ofs && sym_getsync(np, 0, per, &div, &fak) < 0) goto reject_it; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp); printf ("sdtr: ofs=%d per=%d div=%d fak=%d chg=%d.\n", ofs, per, div, fak, chg); } /* * This was an answer message */ if (req == 0) { if (chg) /* Answer wasn't acceptable. */ goto reject_it; sym_setsync (np, cp, ofs, per, div, fak); OUTL_DSP (SCRIPTA_BA (np, clrack)); return; } /* * It was a request. Set value and * prepare an answer message */ sym_setsync (np, cp, ofs, per, div, fak); np->msgout[0] = M_EXTENDED; np->msgout[1] = 3; np->msgout[2] = M_X_SYNC_REQ; np->msgout[3] = per; np->msgout[4] = ofs; cp->nego_status = NS_SYNC; if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "sync msgout", np->msgout); } np->msgin [0] = M_NOOP; OUTL_DSP (SCRIPTB_BA (np, sdtr_resp)); return; reject_it: sym_setsync (np, cp, 0, 0, 0, 0); OUTL_DSP (SCRIPTB_BA (np, msg_bad)); } /* * chip handler for PARALLEL PROTOCOL REQUEST (PPR) message. */ static void sym_ppr_nego(hcb_p np, tcb_p tp, ccb_p cp) { u_char chg, ofs, per, fak, dt, div, wide; int req = 1; /* * Synchronous request message received. */ if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "ppr msgin", np->msgin); }; /* * get requested values. */ chg = 0; per = np->msgin[3]; ofs = np->msgin[5]; wide = np->msgin[6]; dt = np->msgin[7] & PPR_OPT_DT; /* * request or answer ? */ if (INB (HS_PRT) == HS_NEGOTIATE) { OUTB (HS_PRT, HS_BUSY); if (cp->nego_status && cp->nego_status != NS_PPR) goto reject_it; req = 0; } /* * check values against our limits. */ if (wide > np->maxwide) {chg = 1; wide = np->maxwide;} if (!wide || !(np->features & FE_ULTRA3)) dt &= ~PPR_OPT_DT; if (req) { if (wide > tp->tinfo.user.width) {chg = 1; wide = tp->tinfo.user.width;} } if (!(np->features & FE_U3EN)) /* Broken U3EN bit not supported */ dt &= ~PPR_OPT_DT; if (dt != (np->msgin[7] & PPR_OPT_MASK)) chg = 1; if (ofs) { if (dt) { if (ofs > np->maxoffs_dt) {chg = 1; ofs = np->maxoffs_dt;} } else if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} if (req) { if (ofs > tp->tinfo.user.offset) {chg = 1; ofs = tp->tinfo.user.offset;} } } if (ofs) { if (dt) { if (per < np->minsync_dt) {chg = 1; per = np->minsync_dt;} } else if (per < np->minsync) {chg = 1; per = np->minsync;} if (req) { if (per < tp->tinfo.user.period) {chg = 1; per = tp->tinfo.user.period;} } } div = fak = 0; if (ofs && sym_getsync(np, dt, per, &div, &fak) < 0) goto reject_it; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp); printf ("ppr: " "dt=%x ofs=%d per=%d wide=%d div=%d fak=%d chg=%d.\n", dt, ofs, per, wide, div, fak, chg); } /* * It was an answer. */ if (req == 0) { if (chg) /* Answer wasn't acceptable */ goto reject_it; sym_setpprot (np, cp, dt, ofs, per, wide, div, fak); OUTL_DSP (SCRIPTA_BA (np, clrack)); return; } /* * It was a request. Set value and * prepare an answer message */ sym_setpprot (np, cp, dt, ofs, per, wide, div, fak); np->msgout[0] = M_EXTENDED; np->msgout[1] = 6; np->msgout[2] = M_X_PPR_REQ; np->msgout[3] = per; np->msgout[4] = 0; np->msgout[5] = ofs; np->msgout[6] = wide; np->msgout[7] = dt; cp->nego_status = NS_PPR; if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "ppr msgout", np->msgout); } np->msgin [0] = M_NOOP; OUTL_DSP (SCRIPTB_BA (np, ppr_resp)); return; reject_it: sym_setpprot (np, cp, 0, 0, 0, 0, 0, 0); OUTL_DSP (SCRIPTB_BA (np, msg_bad)); /* * If it was a device response that should result in * ST, we may want to try a legacy negotiation later. */ if (!req && !dt) { tp->tinfo.goal.options = 0; tp->tinfo.goal.width = wide; tp->tinfo.goal.period = per; tp->tinfo.goal.offset = ofs; } return; } /* * chip handler for WIDE DATA TRANSFER REQUEST (WDTR) message. */ static void sym_wide_nego(hcb_p np, tcb_p tp, ccb_p cp) { u_char chg, wide; int req = 1; /* * Wide request message received. */ if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "wide msgin", np->msgin); }; /* * Is it a request from the device? */ if (INB (HS_PRT) == HS_NEGOTIATE) { OUTB (HS_PRT, HS_BUSY); if (cp->nego_status && cp->nego_status != NS_WIDE) goto reject_it; req = 0; } /* * get requested values. */ chg = 0; wide = np->msgin[3]; /* * check values against driver limits. */ if (wide > np->maxwide) {chg = 1; wide = np->maxwide;} if (req) { if (wide > tp->tinfo.user.width) {chg = 1; wide = tp->tinfo.user.width;} } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp); printf ("wdtr: wide=%d chg=%d.\n", wide, chg); } /* * This was an answer message */ if (req == 0) { if (chg) /* Answer wasn't acceptable. */ goto reject_it; sym_setwide (np, cp, wide); /* * Negotiate for SYNC immediately after WIDE response. * This allows to negotiate for both WIDE and SYNC on * a single SCSI command (Suggested by Justin Gibbs). */ if (tp->tinfo.goal.offset) { np->msgout[0] = M_EXTENDED; np->msgout[1] = 3; np->msgout[2] = M_X_SYNC_REQ; np->msgout[3] = tp->tinfo.goal.period; np->msgout[4] = tp->tinfo.goal.offset; if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "sync msgout", np->msgout); } cp->nego_status = NS_SYNC; OUTB (HS_PRT, HS_NEGOTIATE); OUTL_DSP (SCRIPTB_BA (np, sdtr_resp)); return; } OUTL_DSP (SCRIPTA_BA (np, clrack)); return; }; /* * It was a request, set value and * prepare an answer message */ sym_setwide (np, cp, wide); np->msgout[0] = M_EXTENDED; np->msgout[1] = 2; np->msgout[2] = M_X_WIDE_REQ; np->msgout[3] = wide; np->msgin [0] = M_NOOP; cp->nego_status = NS_WIDE; if (DEBUG_FLAGS & DEBUG_NEGO) { sym_print_msg(cp, "wide msgout", np->msgout); } OUTL_DSP (SCRIPTB_BA (np, wdtr_resp)); return; reject_it: OUTL_DSP (SCRIPTB_BA (np, msg_bad)); } /* * Reset SYNC or WIDE to default settings. * * Called when a negotiation does not succeed either * on rejection or on protocol error. * * If it was a PPR that made problems, we may want to * try a legacy negotiation later. */ static void sym_nego_default(hcb_p np, tcb_p tp, ccb_p cp) { /* * any error in negotiation: * fall back to default mode. */ switch (cp->nego_status) { case NS_PPR: #if 0 sym_setpprot (np, cp, 0, 0, 0, 0, 0, 0); #else tp->tinfo.goal.options = 0; if (tp->tinfo.goal.period < np->minsync) tp->tinfo.goal.period = np->minsync; if (tp->tinfo.goal.offset > np->maxoffs) tp->tinfo.goal.offset = np->maxoffs; #endif break; case NS_SYNC: sym_setsync (np, cp, 0, 0, 0, 0); break; case NS_WIDE: sym_setwide (np, cp, 0); break; }; np->msgin [0] = M_NOOP; np->msgout[0] = M_NOOP; cp->nego_status = 0; } /* * chip handler for MESSAGE REJECT received in response to * a WIDE or SYNCHRONOUS negotiation. */ static void sym_nego_rejected(hcb_p np, tcb_p tp, ccb_p cp) { sym_nego_default(np, tp, cp); OUTB (HS_PRT, HS_BUSY); } /* * chip exception handler for programmed interrupts. */ static void sym_int_sir (hcb_p np) { u_char num = INB (nc_dsps); u32 dsa = INL (nc_dsa); ccb_p cp = sym_ccb_from_dsa(np, dsa); u_char target = INB (nc_sdid) & 0x0f; tcb_p tp = &np->target[target]; int tmp; if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num); switch (num) { /* * Command has been completed with error condition * or has been auto-sensed. */ case SIR_COMPLETE_ERROR: sym_complete_error(np, cp); return; /* * The C code is currently trying to recover from something. * Typically, user want to abort some command. */ case SIR_SCRIPT_STOPPED: case SIR_TARGET_SELECTED: case SIR_ABORT_SENT: sym_sir_task_recovery(np, num); return; /* * The device didn't go to MSG OUT phase after having * been selected with ATN. We donnot want to handle * that. */ case SIR_SEL_ATN_NO_MSG_OUT: printf ("%s:%d: No MSG OUT phase after selection with ATN.\n", sym_name (np), target); goto out_stuck; /* * The device didn't switch to MSG IN phase after * having reseleted the initiator. */ case SIR_RESEL_NO_MSG_IN: printf ("%s:%d: No MSG IN phase after reselection.\n", sym_name (np), target); goto out_stuck; /* * After reselection, the device sent a message that wasn't * an IDENTIFY. */ case SIR_RESEL_NO_IDENTIFY: printf ("%s:%d: No IDENTIFY after reselection.\n", sym_name (np), target); goto out_stuck; /* * The device reselected a LUN we donnot know about. */ case SIR_RESEL_BAD_LUN: np->msgout[0] = M_RESET; goto out; /* * The device reselected for an untagged nexus and we * haven't any. */ case SIR_RESEL_BAD_I_T_L: np->msgout[0] = M_ABORT; goto out; /* * The device reselected for a tagged nexus that we donnot * have. */ case SIR_RESEL_BAD_I_T_L_Q: np->msgout[0] = M_ABORT_TAG; goto out; /* * The SCRIPTS let us know that the device has grabbed * our message and will abort the job. */ case SIR_RESEL_ABORTED: np->lastmsg = np->msgout[0]; np->msgout[0] = M_NOOP; printf ("%s:%d: message %x sent on bad reselection.\n", sym_name (np), target, np->lastmsg); goto out; /* * The SCRIPTS let us know that a message has been * successfully sent to the device. */ case SIR_MSG_OUT_DONE: np->lastmsg = np->msgout[0]; np->msgout[0] = M_NOOP; /* Should we really care of that */ if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) { if (cp) { cp->xerr_status &= ~XE_PARITY_ERR; if (!cp->xerr_status) OUTOFFB (HF_PRT, HF_EXT_ERR); } } goto out; /* * The device didn't send a GOOD SCSI status. * We may have some work to do prior to allow * the SCRIPTS processor to continue. */ case SIR_BAD_SCSI_STATUS: if (!cp) goto out; sym_sir_bad_scsi_status(np, num, cp); return; /* * We are asked by the SCRIPTS to prepare a * REJECT message. */ case SIR_REJECT_TO_SEND: sym_print_msg(cp, "M_REJECT to send for ", np->msgin); np->msgout[0] = M_REJECT; goto out; /* * We have been ODD at the end of a DATA IN * transfer and the device didn't send a * IGNORE WIDE RESIDUE message. * It is a data overrun condition. */ case SIR_SWIDE_OVERRUN: if (cp) { OUTONB (HF_PRT, HF_EXT_ERR); cp->xerr_status |= XE_SWIDE_OVRUN; } goto out; /* * We have been ODD at the end of a DATA OUT * transfer. * It is a data underrun condition. */ case SIR_SODL_UNDERRUN: if (cp) { OUTONB (HF_PRT, HF_EXT_ERR); cp->xerr_status |= XE_SODL_UNRUN; } goto out; /* * The device wants us to tranfer more data than * expected or in the wrong direction. * The number of extra bytes is in scratcha. * It is a data overrun condition. */ case SIR_DATA_OVERRUN: if (cp) { OUTONB (HF_PRT, HF_EXT_ERR); cp->xerr_status |= XE_EXTRA_DATA; cp->extra_bytes += INL (nc_scratcha); } goto out; /* * The device switched to an illegal phase (4/5). */ case SIR_BAD_PHASE: if (cp) { OUTONB (HF_PRT, HF_EXT_ERR); cp->xerr_status |= XE_BAD_PHASE; } goto out; /* * We received a message. */ case SIR_MSG_RECEIVED: if (!cp) goto out_stuck; switch (np->msgin [0]) { /* * We received an extended message. * We handle MODIFY DATA POINTER, SDTR, WDTR * and reject all other extended messages. */ case M_EXTENDED: switch (np->msgin [2]) { case M_X_MODIFY_DP: if (DEBUG_FLAGS & DEBUG_POINTER) sym_print_msg(cp,"modify DP",np->msgin); tmp = (np->msgin[3]<<24) + (np->msgin[4]<<16) + (np->msgin[5]<<8) + (np->msgin[6]); sym_modify_dp(np, tp, cp, tmp); return; case M_X_SYNC_REQ: sym_sync_nego(np, tp, cp); return; case M_X_PPR_REQ: sym_ppr_nego(np, tp, cp); return; case M_X_WIDE_REQ: sym_wide_nego(np, tp, cp); return; default: goto out_reject; } break; /* * We received a 1/2 byte message not handled from SCRIPTS. * We are only expecting MESSAGE REJECT and IGNORE WIDE * RESIDUE messages that haven't been anticipated by * SCRIPTS on SWIDE full condition. Unanticipated IGNORE * WIDE RESIDUE messages are aliased as MODIFY DP (-1). */ case M_IGN_RESIDUE: if (DEBUG_FLAGS & DEBUG_POINTER) sym_print_msg(cp,"ign wide residue", np->msgin); sym_modify_dp(np, tp, cp, -1); return; case M_REJECT: if (INB (HS_PRT) == HS_NEGOTIATE) sym_nego_rejected(np, tp, cp); else { PRINT_ADDR(cp); printf ("M_REJECT received (%x:%x).\n", scr_to_cpu(np->lastmsg), np->msgout[0]); } goto out_clrack; break; default: goto out_reject; } break; /* * We received an unknown message. * Ignore all MSG IN phases and reject it. */ case SIR_MSG_WEIRD: sym_print_msg(cp, "WEIRD message received", np->msgin); OUTL_DSP (SCRIPTB_BA (np, msg_weird)); return; /* * Negotiation failed. * Target does not send us the reply. * Remove the HS_NEGOTIATE status. */ case SIR_NEGO_FAILED: OUTB (HS_PRT, HS_BUSY); /* * Negotiation failed. * Target does not want answer message. */ case SIR_NEGO_PROTO: sym_nego_default(np, tp, cp); goto out; }; out: OUTONB_STD (); return; out_reject: OUTL_DSP (SCRIPTB_BA (np, msg_bad)); return; out_clrack: OUTL_DSP (SCRIPTA_BA (np, clrack)); return; out_stuck: return; } /* * Acquire a control block */ static ccb_p sym_get_ccb (hcb_p np, u_char tn, u_char ln, u_char tag_order) { tcb_p tp = &np->target[tn]; lcb_p lp = sym_lp(np, tp, ln); u_short tag = NO_TAG; SYM_QUEHEAD *qp; ccb_p cp = (ccb_p) 0; /* * Look for a free CCB */ if (sym_que_empty(&np->free_ccbq)) (void) sym_alloc_ccb(np); qp = sym_remque_head(&np->free_ccbq); if (!qp) goto out; cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); /* * If the LCB is not yet available and the LUN * has been probed ok, try to allocate the LCB. */ if (!lp && sym_is_bit(tp->lun_map, ln)) { lp = sym_alloc_lcb(np, tn, ln); if (!lp) goto out_free; } /* * If the LCB is not available here, then the * logical unit is not yet discovered. For those * ones only accept 1 SCSI IO per logical unit, * since we cannot allow disconnections. */ if (!lp) { if (!sym_is_bit(tp->busy0_map, ln)) sym_set_bit(tp->busy0_map, ln); else goto out_free; } else { /* * If we have been asked for a tagged command. */ if (tag_order) { /* * Debugging purpose. */ assert(lp->busy_itl == 0); /* * Allocate resources for tags if not yet. */ if (!lp->cb_tags) { sym_alloc_lcb_tags(np, tn, ln); if (!lp->cb_tags) goto out_free; } /* * Get a tag for this SCSI IO and set up * the CCB bus address for reselection, * and count it for this LUN. * Toggle reselect path to tagged. */ if (lp->busy_itlq < SYM_CONF_MAX_TASK) { tag = lp->cb_tags[lp->ia_tag]; if (++lp->ia_tag == SYM_CONF_MAX_TASK) lp->ia_tag = 0; lp->itlq_tbl[tag] = cpu_to_scr(cp->ccb_ba); ++lp->busy_itlq; lp->head.resel_sa = cpu_to_scr(SCRIPTA_BA (np, resel_tag)); } else goto out_free; } /* * This command will not be tagged. * If we already have either a tagged or untagged * one, refuse to overlap this untagged one. */ else { /* * Debugging purpose. */ assert(lp->busy_itl == 0 && lp->busy_itlq == 0); /* * Count this nexus for this LUN. * Set up the CCB bus address for reselection. * Toggle reselect path to untagged. */ if (++lp->busy_itl == 1) { lp->head.itl_task_sa = cpu_to_scr(cp->ccb_ba); lp->head.resel_sa = cpu_to_scr(SCRIPTA_BA (np, resel_no_tag)); } else goto out_free; } } /* * Put the CCB into the busy queue. */ sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq); /* * Remember all informations needed to free this CCB. */ cp->to_abort = 0; cp->tag = tag; cp->target = tn; cp->lun = ln; if (DEBUG_FLAGS & DEBUG_TAGS) { PRINT_LUN(np, tn, ln); printf ("ccb @%p using tag %d.\n", cp, tag); } out: return cp; out_free: sym_insque_head(&cp->link_ccbq, &np->free_ccbq); return (ccb_p) 0; } /* * Release one control block */ static void sym_free_ccb (hcb_p np, ccb_p cp) { tcb_p tp = &np->target[cp->target]; lcb_p lp = sym_lp(np, tp, cp->lun); if (DEBUG_FLAGS & DEBUG_TAGS) { PRINT_LUN(np, cp->target, cp->lun); printf ("ccb @%p freeing tag %d.\n", cp, cp->tag); } /* * If LCB available, */ if (lp) { /* * If tagged, release the tag, set the relect path */ if (cp->tag != NO_TAG) { /* * Free the tag value. */ lp->cb_tags[lp->if_tag] = cp->tag; if (++lp->if_tag == SYM_CONF_MAX_TASK) lp->if_tag = 0; /* * Make the reselect path invalid, * and uncount this CCB. */ lp->itlq_tbl[cp->tag] = cpu_to_scr(np->bad_itlq_ba); --lp->busy_itlq; } else { /* Untagged */ /* * Make the reselect path invalid, * and uncount this CCB. */ lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba); --lp->busy_itl; } /* * If no JOB active, make the LUN reselect path invalid. */ if (lp->busy_itlq == 0 && lp->busy_itl == 0) lp->head.resel_sa = cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun)); } /* * Otherwise, we only accept 1 IO per LUN. * Clear the bit that keeps track of this IO. */ else sym_clr_bit(tp->busy0_map, cp->lun); /* * We donnot queue more than 1 ccb per target * with negotiation at any time. If this ccb was * used for negotiation, clear this info in the tcb. */ if (cp == tp->nego_cp) tp->nego_cp = 0; #ifdef SYM_CONF_IARB_SUPPORT /* * If we just complete the last queued CCB, * clear this info that is no longer relevant. */ if (cp == np->last_cp) np->last_cp = 0; #endif #ifdef FreeBSD_Bus_Dma_Abstraction /* * Unmap user data from DMA map if needed. */ if (cp->dmamapped) { bus_dmamap_unload(np->data_dmat, cp->dmamap); cp->dmamapped = 0; } #endif /* * Make this CCB available. */ cp->cam_ccb = 0; cp->host_status = HS_IDLE; sym_remque(&cp->link_ccbq); sym_insque_head(&cp->link_ccbq, &np->free_ccbq); } /* * Allocate a CCB from memory and initialize its fixed part. */ static ccb_p sym_alloc_ccb(hcb_p np) { ccb_p cp = 0; int hcode; /* * Prevent from allocating more CCBs than we can * queue to the controller. */ if (np->actccbs >= SYM_CONF_MAX_START) return 0; /* * Allocate memory for this CCB. */ cp = sym_calloc_dma(sizeof(struct sym_ccb), "CCB"); if (!cp) goto out_free; /* * Allocate a bounce buffer for sense data. */ cp->sns_bbuf = sym_calloc_dma(SYM_SNS_BBUF_LEN, "SNS_BBUF"); if (!cp->sns_bbuf) goto out_free; /* * Allocate a map for the DMA of user data. */ #ifdef FreeBSD_Bus_Dma_Abstraction if (bus_dmamap_create(np->data_dmat, 0, &cp->dmamap)) goto out_free; #endif /* * Count it. */ np->actccbs++; /* * Compute the bus address of this ccb. */ cp->ccb_ba = vtobus(cp); /* * Insert this ccb into the hashed list. */ hcode = CCB_HASH_CODE(cp->ccb_ba); cp->link_ccbh = np->ccbh[hcode]; np->ccbh[hcode] = cp; /* * Initialyze the start and restart actions. */ cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA (np, idle)); cp->phys.head.go.restart = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l)); /* * Initilialyze some other fields. */ cp->phys.smsg_ext.addr = cpu_to_scr(HCB_BA(np, msgin[2])); /* * Chain into free ccb queue. */ sym_insque_head(&cp->link_ccbq, &np->free_ccbq); return cp; out_free: if (cp) { if (cp->sns_bbuf) sym_mfree_dma(cp->sns_bbuf,SYM_SNS_BBUF_LEN,"SNS_BBUF"); sym_mfree_dma(cp, sizeof(*cp), "CCB"); } return 0; } /* * Look up a CCB from a DSA value. */ static ccb_p sym_ccb_from_dsa(hcb_p np, u32 dsa) { int hcode; ccb_p cp; hcode = CCB_HASH_CODE(dsa); cp = np->ccbh[hcode]; while (cp) { if (cp->ccb_ba == dsa) break; cp = cp->link_ccbh; } return cp; } /* * Target control block initialisation. * Nothing important to do at the moment. */ static void sym_init_tcb (hcb_p np, u_char tn) { /* * Check some alignments required by the chip. */ assert (((offsetof(struct sym_reg, nc_sxfer) ^ offsetof(struct sym_tcb, head.sval)) &3) == 0); assert (((offsetof(struct sym_reg, nc_scntl3) ^ offsetof(struct sym_tcb, head.wval)) &3) == 0); } /* * Lun control block allocation and initialization. */ static lcb_p sym_alloc_lcb (hcb_p np, u_char tn, u_char ln) { tcb_p tp = &np->target[tn]; lcb_p lp = sym_lp(np, tp, ln); /* * Already done, just return. */ if (lp) return lp; /* * Check against some race. */ assert(!sym_is_bit(tp->busy0_map, ln)); /* * Initialize the target control block if not yet. */ sym_init_tcb (np, tn); /* * Allocate the LCB bus address array. * Compute the bus address of this table. */ if (ln && !tp->luntbl) { int i; tp->luntbl = sym_calloc_dma(256, "LUNTBL"); if (!tp->luntbl) goto fail; for (i = 0 ; i < 64 ; i++) tp->luntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa)); tp->head.luntbl_sa = cpu_to_scr(vtobus(tp->luntbl)); } /* * Allocate the table of pointers for LUN(s) > 0, if needed. */ if (ln && !tp->lunmp) { tp->lunmp = sym_calloc(SYM_CONF_MAX_LUN * sizeof(lcb_p), "LUNMP"); if (!tp->lunmp) goto fail; } /* * Allocate the lcb. * Make it available to the chip. */ lp = sym_calloc_dma(sizeof(struct sym_lcb), "LCB"); if (!lp) goto fail; if (ln) { tp->lunmp[ln] = lp; tp->luntbl[ln] = cpu_to_scr(vtobus(lp)); } else { tp->lun0p = lp; tp->head.lun0_sa = cpu_to_scr(vtobus(lp)); } /* * Let the itl task point to error handling. */ lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba); /* * Set the reselect pattern to our default. :) */ lp->head.resel_sa = cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun)); /* * Set user capabilities. */ lp->user_flags = tp->usrflags & (SYM_DISC_ENABLED | SYM_TAGS_ENABLED); fail: return lp; } /* * Allocate LCB resources for tagged command queuing. */ static void sym_alloc_lcb_tags (hcb_p np, u_char tn, u_char ln) { tcb_p tp = &np->target[tn]; lcb_p lp = sym_lp(np, tp, ln); int i; /* * If LCB not available, try to allocate it. */ if (!lp && !(lp = sym_alloc_lcb(np, tn, ln))) goto fail; /* * Allocate the task table and and the tag allocation * circular buffer. We want both or none. */ lp->itlq_tbl = sym_calloc_dma(SYM_CONF_MAX_TASK*4, "ITLQ_TBL"); if (!lp->itlq_tbl) goto fail; lp->cb_tags = sym_calloc(SYM_CONF_MAX_TASK, "CB_TAGS"); if (!lp->cb_tags) { sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL"); lp->itlq_tbl = 0; goto fail; } /* * Initialize the task table with invalid entries. */ for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++) lp->itlq_tbl[i] = cpu_to_scr(np->notask_ba); /* * Fill up the tag buffer with tag numbers. */ for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++) lp->cb_tags[i] = i; /* * Make the task table available to SCRIPTS, * And accept tagged commands now. */ lp->head.itlq_tbl_sa = cpu_to_scr(vtobus(lp->itlq_tbl)); return; fail: return; } /* * Test the pci bus snoop logic :-( * * Has to be called with interrupts disabled. */ #ifndef SYM_CONF_IOMAPPED static int sym_regtest (hcb_p np) { register volatile u32 data; /* * chip registers may NOT be cached. * write 0xffffffff to a read only register area, * and try to read it back. */ data = 0xffffffff; OUTL_OFF(offsetof(struct sym_reg, nc_dstat), data); data = INL_OFF(offsetof(struct sym_reg, nc_dstat)); #if 1 if (data == 0xffffffff) { #else if ((data & 0xe2f0fffd) != 0x02000080) { #endif printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n", (unsigned) data); return (0x10); }; return (0); } #endif static int sym_snooptest (hcb_p np) { u32 sym_rd, sym_wr, sym_bk, host_rd, host_wr, pc, dstat; int i, err=0; #ifndef SYM_CONF_IOMAPPED err |= sym_regtest (np); if (err) return (err); #endif restart_test: /* * Enable Master Parity Checking as we intend * to enable it for normal operations. */ OUTB (nc_ctest4, (np->rv_ctest4 & MPEE)); /* * init */ pc = SCRIPTB0_BA (np, snooptest); host_wr = 1; sym_wr = 2; /* * Set memory and register. */ np->cache = cpu_to_scr(host_wr); OUTL (nc_temp, sym_wr); /* * Start script (exchange values) */ OUTL (nc_dsa, np->hcb_ba); OUTL_DSP (pc); /* * Wait 'til done (with timeout) */ for (i=0; i=SYM_SNOOP_TIMEOUT) { printf ("CACHE TEST FAILED: timeout.\n"); return (0x20); }; /* * Check for fatal DMA errors. */ dstat = INB (nc_dstat); #if 1 /* Band aiding for broken hardwares that fail PCI parity */ if ((dstat & MDPE) && (np->rv_ctest4 & MPEE)) { printf ("%s: PCI DATA PARITY ERROR DETECTED - " "DISABLING MASTER DATA PARITY CHECKING.\n", sym_name(np)); np->rv_ctest4 &= ~MPEE; goto restart_test; } #endif if (dstat & (MDPE|BF|IID)) { printf ("CACHE TEST FAILED: DMA error (dstat=0x%02x).", dstat); return (0x80); } /* * Save termination position. */ pc = INL (nc_dsp); /* * Read memory and register. */ host_rd = scr_to_cpu(np->cache); sym_rd = INL (nc_scratcha); sym_bk = INL (nc_temp); /* * Check termination position. */ if (pc != SCRIPTB0_BA (np, snoopend)+8) { printf ("CACHE TEST FAILED: script execution failed.\n"); printf ("start=%08lx, pc=%08lx, end=%08lx\n", (u_long) SCRIPTB0_BA (np, snooptest), (u_long) pc, (u_long) SCRIPTB0_BA (np, snoopend) +8); return (0x40); }; /* * Show results. */ if (host_wr != sym_rd) { printf ("CACHE TEST FAILED: host wrote %d, chip read %d.\n", (int) host_wr, (int) sym_rd); err |= 1; }; if (host_rd != sym_wr) { printf ("CACHE TEST FAILED: chip wrote %d, host read %d.\n", (int) sym_wr, (int) host_rd); err |= 2; }; if (sym_bk != sym_wr) { printf ("CACHE TEST FAILED: chip wrote %d, read back %d.\n", (int) sym_wr, (int) sym_bk); err |= 4; }; return (err); } /* * Determine the chip's clock frequency. * * This is essential for the negotiation of the synchronous * transfer rate. * * Note: we have to return the correct value. * THERE IS NO SAFE DEFAULT VALUE. * * Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock. * 53C860 and 53C875 rev. 1 support fast20 transfers but * do not have a clock doubler and so are provided with a * 80 MHz clock. All other fast20 boards incorporate a doubler * and so should be delivered with a 40 MHz clock. * The recent fast40 chips (895/896/895A/1010) use a 40 Mhz base * clock and provide a clock quadrupler (160 Mhz). */ /* * Select SCSI clock frequency */ static void sym_selectclock(hcb_p np, u_char scntl3) { /* * If multiplier not present or not selected, leave here. */ if (np->multiplier <= 1) { OUTB(nc_scntl3, scntl3); return; } if (sym_verbose >= 2) printf ("%s: enabling clock multiplier\n", sym_name(np)); OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */ /* * Wait for the LCKFRQ bit to be set if supported by the chip. * Otherwise wait 20 micro-seconds. */ if (np->features & FE_LCKFRQ) { int i = 20; while (!(INB(nc_stest4) & LCKFRQ) && --i > 0) UDELAY (20); if (!i) printf("%s: the chip cannot lock the frequency\n", sym_name(np)); } else UDELAY (20); OUTB(nc_stest3, HSC); /* Halt the scsi clock */ OUTB(nc_scntl3, scntl3); OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ OUTB(nc_stest3, 0x00); /* Restart scsi clock */ } /* * calculate SCSI clock frequency (in KHz) */ static unsigned getfreq (hcb_p np, int gen) { unsigned int ms = 0; unsigned int f; /* * Measure GEN timer delay in order * to calculate SCSI clock frequency * * This code will never execute too * many loop iterations (if DELAY is * reasonably correct). It could get * too low a delay (too high a freq.) * if the CPU is slow executing the * loop for some reason (an NMI, for * example). For this reason we will * if multiple measurements are to be * performed trust the higher delay * (lower frequency returned). */ OUTW (nc_sien , 0); /* mask all scsi interrupts */ (void) INW (nc_sist); /* clear pending scsi interrupt */ OUTB (nc_dien , 0); /* mask all dma interrupts */ (void) INW (nc_sist); /* another one, just to be sure :) */ OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ OUTB (nc_stime1, 0); /* disable general purpose timer */ OUTB (nc_stime1, gen); /* set to nominal delay of 1<= 2) printf ("%s: Delay (GEN=%d): %u msec, %u KHz\n", sym_name(np), gen, ms, f); return f; } static unsigned sym_getfreq (hcb_p np) { u_int f1, f2; int gen = 11; (void) getfreq (np, gen); /* throw away first result */ f1 = getfreq (np, gen); f2 = getfreq (np, gen); if (f1 > f2) f1 = f2; /* trust lower result */ return f1; } /* * Get/probe chip SCSI clock frequency */ static void sym_getclock (hcb_p np, int mult) { unsigned char scntl3 = np->sv_scntl3; unsigned char stest1 = np->sv_stest1; unsigned f1; /* * For the C10 core, assume 40 MHz. */ if (np->features & FE_C10) { np->multiplier = mult; np->clock_khz = 40000 * mult; return; } np->multiplier = 1; f1 = 40000; /* * True with 875/895/896/895A with clock multiplier selected */ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { if (sym_verbose >= 2) printf ("%s: clock multiplier found\n", sym_name(np)); np->multiplier = mult; } /* * If multiplier not found or scntl3 not 7,5,3, * reset chip and get frequency from general purpose timer. * Otherwise trust scntl3 BIOS setting. */ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) { OUTB (nc_stest1, 0); /* make sure doubler is OFF */ f1 = sym_getfreq (np); if (sym_verbose) printf ("%s: chip clock is %uKHz\n", sym_name(np), f1); if (f1 < 45000) f1 = 40000; else if (f1 < 55000) f1 = 50000; else f1 = 80000; if (f1 < 80000 && mult > 1) { if (sym_verbose >= 2) printf ("%s: clock multiplier assumed\n", sym_name(np)); np->multiplier = mult; } } else { if ((scntl3 & 7) == 3) f1 = 40000; else if ((scntl3 & 7) == 5) f1 = 80000; else f1 = 160000; f1 /= np->multiplier; } /* * Compute controller synchronous parameters. */ f1 *= np->multiplier; np->clock_khz = f1; } /* * Get/probe PCI clock frequency */ static int sym_getpciclock (hcb_p np) { int f = 0; /* * For the C1010-33, this doesn't work. * For the C1010-66, this will be tested when I'll have * such a beast to play with. */ if (!(np->features & FE_C10)) { OUTB (nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */ f = (int) sym_getfreq (np); OUTB (nc_stest1, 0); } np->pciclk_khz = f; return f; } /*============= DRIVER ACTION/COMPLETION ====================*/ /* * Print something that tells about extended errors. */ static void sym_print_xerr(ccb_p cp, int x_status) { if (x_status & XE_PARITY_ERR) { PRINT_ADDR(cp); printf ("unrecovered SCSI parity error.\n"); } if (x_status & XE_EXTRA_DATA) { PRINT_ADDR(cp); printf ("extraneous data discarded.\n"); } if (x_status & XE_BAD_PHASE) { PRINT_ADDR(cp); printf ("illegal scsi phase (4/5).\n"); } if (x_status & XE_SODL_UNRUN) { PRINT_ADDR(cp); printf ("ODD transfer in DATA OUT phase.\n"); } if (x_status & XE_SWIDE_OVRUN) { PRINT_ADDR(cp); printf ("ODD transfer in DATA IN phase.\n"); } } /* * Choose the more appropriate CAM status if * the IO encountered an extended error. */ static int sym_xerr_cam_status(int cam_status, int x_status) { if (x_status) { if (x_status & XE_PARITY_ERR) cam_status = CAM_UNCOR_PARITY; else if (x_status &(XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN)) cam_status = CAM_DATA_RUN_ERR; else if (x_status & XE_BAD_PHASE) cam_status = CAM_REQ_CMP_ERR; else cam_status = CAM_REQ_CMP_ERR; } return cam_status; } /* * Complete execution of a SCSI command with extented * error, SCSI status error, or having been auto-sensed. * * The SCRIPTS processor is not running there, so we * can safely access IO registers and remove JOBs from * the START queue. * SCRATCHA is assumed to have been loaded with STARTPOS * before the SCRIPTS called the C code. */ static void sym_complete_error (hcb_p np, ccb_p cp) { struct ccb_scsiio *csio; u_int cam_status; int i; /* * Paranoid check. :) */ if (!cp || !cp->cam_ccb) return; if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_RESULT)) { printf ("CCB=%lx STAT=%x/%x/%x DEV=%d/%d\n", (unsigned long)cp, cp->host_status, cp->ssss_status, cp->host_flags, cp->target, cp->lun); MDELAY(100); } /* * Get CAM command pointer. */ csio = &cp->cam_ccb->csio; /* * Check for extended errors. */ if (cp->xerr_status) { if (sym_verbose) sym_print_xerr(cp, cp->xerr_status); if (cp->host_status == HS_COMPLETE) cp->host_status = HS_COMP_ERR; } /* * Calculate the residual. */ csio->sense_resid = 0; csio->resid = sym_compute_residual(np, cp); if (!SYM_CONF_RESIDUAL_SUPPORT) {/* If user does not want residuals */ csio->resid = 0; /* throw them away. :) */ cp->sv_resid = 0; } if (cp->host_flags & HF_SENSE) { /* Auto sense */ csio->scsi_status = cp->sv_scsi_status; /* Restore status */ csio->sense_resid = csio->resid; /* Swap residuals */ csio->resid = cp->sv_resid; cp->sv_resid = 0; if (sym_verbose && cp->sv_xerr_status) sym_print_xerr(cp, cp->sv_xerr_status); if (cp->host_status == HS_COMPLETE && cp->ssss_status == S_GOOD && cp->xerr_status == 0) { cam_status = sym_xerr_cam_status(CAM_SCSI_STATUS_ERROR, cp->sv_xerr_status); cam_status |= CAM_AUTOSNS_VALID; /* * Bounce back the sense data to user and * fix the residual. */ bzero(&csio->sense_data, csio->sense_len); bcopy(cp->sns_bbuf, &csio->sense_data, MIN(csio->sense_len, SYM_SNS_BBUF_LEN)); csio->sense_resid += csio->sense_len; csio->sense_resid -= SYM_SNS_BBUF_LEN; #if 0 /* * If the device reports a UNIT ATTENTION condition * due to a RESET condition, we should consider all * disconnect CCBs for this unit as aborted. */ if (1) { u_char *p; p = (u_char *) csio->sense_data; if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29) sym_clear_tasks(np, CAM_REQ_ABORTED, cp->target,cp->lun, -1); } #endif } else cam_status = CAM_AUTOSENSE_FAIL; } else if (cp->host_status == HS_COMPLETE) { /* Bad SCSI status */ csio->scsi_status = cp->ssss_status; cam_status = CAM_SCSI_STATUS_ERROR; } else if (cp->host_status == HS_SEL_TIMEOUT) /* Selection timeout */ cam_status = CAM_SEL_TIMEOUT; else if (cp->host_status == HS_UNEXPECTED) /* Unexpected BUS FREE*/ cam_status = CAM_UNEXP_BUSFREE; else { /* Extended error */ if (sym_verbose) { PRINT_ADDR(cp); printf ("COMMAND FAILED (%x %x %x).\n", cp->host_status, cp->ssss_status, cp->xerr_status); } csio->scsi_status = cp->ssss_status; /* * Set the most appropriate value for CAM status. */ cam_status = sym_xerr_cam_status(CAM_REQ_CMP_ERR, cp->xerr_status); } /* * Dequeue all queued CCBs for that device * not yet started by SCRIPTS. */ i = (INL (nc_scratcha) - np->squeue_ba) / 4; (void) sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1); /* * Restart the SCRIPTS processor. */ OUTL_DSP (SCRIPTA_BA (np, start)); #ifdef FreeBSD_Bus_Dma_Abstraction /* * Synchronize DMA map if needed. */ if (cp->dmamapped) { bus_dmamap_sync(np->data_dmat, cp->dmamap, (cp->dmamapped == SYM_DMA_READ ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE)); } #endif /* * Add this one to the COMP queue. * Complete all those commands with either error * or requeue condition. */ sym_set_cam_status((union ccb *) csio, cam_status); sym_remque(&cp->link_ccbq); sym_insque_head(&cp->link_ccbq, &np->comp_ccbq); sym_flush_comp_queue(np, 0); } /* * Complete execution of a successful SCSI command. * * Only successful commands go to the DONE queue, * since we need to have the SCRIPTS processor * stopped on any error condition. * The SCRIPTS processor is running while we are * completing successful commands. */ static void sym_complete_ok (hcb_p np, ccb_p cp) { struct ccb_scsiio *csio; tcb_p tp; lcb_p lp; /* * Paranoid check. :) */ if (!cp || !cp->cam_ccb) return; assert (cp->host_status == HS_COMPLETE); /* * Get command, target and lun pointers. */ csio = &cp->cam_ccb->csio; tp = &np->target[cp->target]; lp = sym_lp(np, tp, cp->lun); /* * Assume device discovered on first success. */ if (!lp) sym_set_bit(tp->lun_map, cp->lun); /* * If all data have been transferred, given than no * extended error did occur, there is no residual. */ csio->resid = 0; if (cp->phys.head.lastp != cp->phys.head.goalp) csio->resid = sym_compute_residual(np, cp); /* * Wrong transfer residuals may be worse than just always * returning zero. User can disable this feature from * sym_conf.h. Residual support is enabled by default. */ if (!SYM_CONF_RESIDUAL_SUPPORT) csio->resid = 0; #ifdef FreeBSD_Bus_Dma_Abstraction /* * Synchronize DMA map if needed. */ if (cp->dmamapped) { bus_dmamap_sync(np->data_dmat, cp->dmamap, (cp->dmamapped == SYM_DMA_READ ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE)); } #endif /* * Set status and complete the command. */ csio->scsi_status = cp->ssss_status; sym_set_cam_status((union ccb *) csio, CAM_REQ_CMP); sym_free_ccb (np, cp); sym_xpt_done(np, (union ccb *) csio); } /* * Our timeout handler. */ static void sym_timeout1(void *arg) { union ccb *ccb = (union ccb *) arg; hcb_p np = ccb->ccb_h.sym_hcb_ptr; /* * Check that the CAM CCB is still queued. */ if (!np) return; switch(ccb->ccb_h.func_code) { case XPT_SCSI_IO: (void) sym_abort_scsiio(np, ccb, 1); break; default: break; } } static void sym_timeout(void *arg) { int s = splcam(); sym_timeout1(arg); splx(s); } /* * Abort an SCSI IO. */ static int sym_abort_scsiio(hcb_p np, union ccb *ccb, int timed_out) { ccb_p cp; SYM_QUEHEAD *qp; /* * Look up our CCB control block. */ cp = 0; FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { ccb_p cp2 = sym_que_entry(qp, struct sym_ccb, link_ccbq); if (cp2->cam_ccb == ccb) { cp = cp2; break; } } if (!cp || cp->host_status == HS_WAIT) return -1; /* * If a previous abort didn't succeed in time, * perform a BUS reset. */ if (cp->to_abort) { sym_reset_scsi_bus(np, 1); return 0; } /* * Mark the CCB for abort and allow time for. */ cp->to_abort = timed_out ? 2 : 1; ccb->ccb_h.timeout_ch = timeout(sym_timeout, (caddr_t) ccb, 10*hz); /* * Tell the SCRIPTS processor to stop and synchronize with us. */ np->istat_sem = SEM; OUTB (nc_istat, SIGP|SEM); return 0; } /* * Reset a SCSI device (all LUNs of a target). */ static void sym_reset_dev(hcb_p np, union ccb *ccb) { tcb_p tp; struct ccb_hdr *ccb_h = &ccb->ccb_h; if (ccb_h->target_id == np->myaddr || ccb_h->target_id >= SYM_CONF_MAX_TARGET || ccb_h->target_lun >= SYM_CONF_MAX_LUN) { sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return; } tp = &np->target[ccb_h->target_id]; tp->to_reset = 1; sym_xpt_done2(np, ccb, CAM_REQ_CMP); np->istat_sem = SEM; OUTB (nc_istat, SIGP|SEM); return; } /* * SIM action entry point. */ static void sym_action(struct cam_sim *sim, union ccb *ccb) { int s = splcam(); sym_action1(sim, ccb); splx(s); } static void sym_action1(struct cam_sim *sim, union ccb *ccb) { hcb_p np; tcb_p tp; lcb_p lp; ccb_p cp; int tmp; u_char idmsg, *msgptr; u_int msglen; struct ccb_scsiio *csio; struct ccb_hdr *ccb_h; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("sym_action\n")); /* * Retrieve our controller data structure. */ np = (hcb_p) cam_sim_softc(sim); /* * The common case is SCSI IO. * We deal with other ones elsewhere. */ if (ccb->ccb_h.func_code != XPT_SCSI_IO) { sym_action2(sim, ccb); return; } csio = &ccb->csio; ccb_h = &csio->ccb_h; /* * Work around races. */ if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { xpt_done(ccb); return; } /* * Minimal checkings, so that we will not * go outside our tables. */ if (ccb_h->target_id == np->myaddr || ccb_h->target_id >= SYM_CONF_MAX_TARGET || ccb_h->target_lun >= SYM_CONF_MAX_LUN) { sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return; } /* * Retreive the target and lun descriptors. */ tp = &np->target[ccb_h->target_id]; lp = sym_lp(np, tp, ccb_h->target_lun); /* * Complete the 1st INQUIRY command with error * condition if the device is flagged NOSCAN * at BOOT in the NVRAM. This may speed up * the boot and maintain coherency with BIOS * device numbering. Clearing the flag allows * user to rescan skipped devices later. * We also return error for devices not flagged * for SCAN LUNS in the NVRAM since some mono-lun * devices behave badly when asked for some non * zero LUN. Btw, this is an absolute hack.:-) */ if (!(ccb_h->flags & CAM_CDB_PHYS) && (0x12 == ((ccb_h->flags & CAM_CDB_POINTER) ? csio->cdb_io.cdb_ptr[0] : csio->cdb_io.cdb_bytes[0]))) { if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) || ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) && ccb_h->target_lun != 0)) { tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return; } } /* * Get a control block for this IO. */ tmp = ((ccb_h->flags & CAM_TAG_ACTION_VALID) != 0); cp = sym_get_ccb(np, ccb_h->target_id, ccb_h->target_lun, tmp); if (!cp) { sym_xpt_done2(np, ccb, CAM_RESRC_UNAVAIL); return; } /* * Keep track of the IO in our CCB. */ cp->cam_ccb = ccb; /* * Build the IDENTIFY message. */ idmsg = M_IDENTIFY | cp->lun; if (cp->tag != NO_TAG || (lp && (lp->current_flags & SYM_DISC_ENABLED))) idmsg |= 0x40; msgptr = cp->scsi_smsg; msglen = 0; msgptr[msglen++] = idmsg; /* * Build the tag message if present. */ if (cp->tag != NO_TAG) { u_char order = csio->tag_action; switch(order) { case M_ORDERED_TAG: break; case M_HEAD_TAG: break; default: order = M_SIMPLE_TAG; } msgptr[msglen++] = order; /* * For less than 128 tags, actual tags are numbered * 1,3,5,..2*MAXTAGS+1,since we may have to deal * with devices that have problems with #TAG 0 or too * great #TAG numbers. For more tags (up to 256), * we use directly our tag number. */ #if SYM_CONF_MAX_TASK > (512/4) msgptr[msglen++] = cp->tag; #else msgptr[msglen++] = (cp->tag << 1) + 1; #endif } /* * Build a negotiation message if needed. * (nego_status is filled by sym_prepare_nego()) */ cp->nego_status = 0; if (tp->tinfo.current.width != tp->tinfo.goal.width || tp->tinfo.current.period != tp->tinfo.goal.period || tp->tinfo.current.offset != tp->tinfo.goal.offset || tp->tinfo.current.options != tp->tinfo.goal.options) { if (!tp->nego_cp && lp) msglen += sym_prepare_nego(np, cp, 0, msgptr + msglen); } /* * Fill in our ccb */ /* * Startqueue */ cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA (np, select)); cp->phys.head.go.restart = cpu_to_scr(SCRIPTA_BA (np, resel_dsa)); /* * select */ cp->phys.select.sel_id = cp->target; cp->phys.select.sel_scntl3 = tp->head.wval; cp->phys.select.sel_sxfer = tp->head.sval; cp->phys.select.sel_scntl4 = tp->head.uval; /* * message */ cp->phys.smsg.addr = cpu_to_scr(CCB_BA (cp, scsi_smsg)); cp->phys.smsg.size = cpu_to_scr(msglen); /* * command */ if (sym_setup_cdb(np, csio, cp) < 0) { sym_free_ccb(np, cp); sym_xpt_done(np, ccb); return; } /* * status */ #if 0 /* Provision */ cp->actualquirks = tp->quirks; #endif cp->actualquirks = SYM_QUIRK_AUTOSAVE; cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; cp->ssss_status = S_ILLEGAL; cp->xerr_status = 0; cp->host_flags = 0; cp->extra_bytes = 0; /* * extreme data pointer. * shall be positive, so -1 is lower than lowest.:) */ cp->ext_sg = -1; cp->ext_ofs = 0; /* * Build the data descriptor block * and start the IO. */ sym_setup_data_and_start(np, csio, cp); } /* * Setup buffers and pointers that address the CDB. * I bet, physical CDBs will never be used on the planet, * since they can be bounced without significant overhead. */ static int sym_setup_cdb(hcb_p np, struct ccb_scsiio *csio, ccb_p cp) { struct ccb_hdr *ccb_h; u32 cmd_ba; int cmd_len; ccb_h = &csio->ccb_h; /* * CDB is 16 bytes max. */ if (csio->cdb_len > sizeof(cp->cdb_buf)) { sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); return -1; } cmd_len = csio->cdb_len; if (ccb_h->flags & CAM_CDB_POINTER) { /* CDB is a pointer */ if (!(ccb_h->flags & CAM_CDB_PHYS)) { /* CDB pointer is virtual */ bcopy(csio->cdb_io.cdb_ptr, cp->cdb_buf, cmd_len); cmd_ba = CCB_BA (cp, cdb_buf[0]); } else { /* CDB pointer is physical */ #if 0 cmd_ba = ((u32)csio->cdb_io.cdb_ptr) & 0xffffffff; #else sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); return -1; #endif } } else { /* CDB is in the CAM ccb (buffer) */ bcopy(csio->cdb_io.cdb_bytes, cp->cdb_buf, cmd_len); cmd_ba = CCB_BA (cp, cdb_buf[0]); } cp->phys.cmd.addr = cpu_to_scr(cmd_ba); cp->phys.cmd.size = cpu_to_scr(cmd_len); return 0; } /* * Set up data pointers used by SCRIPTS. */ static void __inline sym_setup_data_pointers(hcb_p np, ccb_p cp, int dir) { u32 lastp, goalp; /* * No segments means no data. */ if (!cp->segments) dir = CAM_DIR_NONE; /* * Set the data pointer. */ switch(dir) { case CAM_DIR_OUT: goalp = SCRIPTA_BA (np, data_out2) + 8; lastp = goalp - 8 - (cp->segments * (2*4)); break; case CAM_DIR_IN: cp->host_flags |= HF_DATA_IN; goalp = SCRIPTA_BA (np, data_in2) + 8; lastp = goalp - 8 - (cp->segments * (2*4)); break; case CAM_DIR_NONE: default: lastp = goalp = SCRIPTB_BA (np, no_data); break; } cp->phys.head.lastp = cpu_to_scr(lastp); cp->phys.head.goalp = cpu_to_scr(goalp); cp->phys.head.savep = cpu_to_scr(lastp); cp->startp = cp->phys.head.savep; } #ifdef FreeBSD_Bus_Dma_Abstraction /* * Call back routine for the DMA map service. * If bounce buffers are used (why ?), we may sleep and then * be called there in another context. */ static void sym_execute_ccb(void *arg, bus_dma_segment_t *psegs, int nsegs, int error) { ccb_p cp; hcb_p np; union ccb *ccb; int s; s = splcam(); cp = (ccb_p) arg; ccb = cp->cam_ccb; np = (hcb_p) cp->arg; /* * Deal with weird races. */ if (sym_get_cam_status(ccb) != CAM_REQ_INPROG) goto out_abort; /* * Deal with weird errors. */ if (error) { cp->dmamapped = 0; sym_set_cam_status(cp->cam_ccb, CAM_REQ_ABORTED); goto out_abort; } /* * Build the data descriptor for the chip. */ if (nsegs) { int retv; /* 896 rev 1 requires to be careful about boundaries */ if (np->device_id == PCI_ID_SYM53C896 && np->revision_id <= 1) retv = sym_scatter_sg_physical(np, cp, psegs, nsegs); else retv = sym_fast_scatter_sg_physical(np,cp, psegs,nsegs); if (retv < 0) { sym_set_cam_status(cp->cam_ccb, CAM_REQ_TOO_BIG); goto out_abort; } } /* * Synchronize the DMA map only if we have * actually mapped the data. */ if (cp->dmamapped) { bus_dmamap_sync(np->data_dmat, cp->dmamap, (cp->dmamapped == SYM_DMA_READ ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); } /* * Set host status to busy state. * May have been set back to HS_WAIT to avoid a race. */ cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; /* * Set data pointers. */ sym_setup_data_pointers(np, cp, (ccb->ccb_h.flags & CAM_DIR_MASK)); /* * Enqueue this IO in our pending queue. */ sym_enqueue_cam_ccb(np, ccb); /* * When `#ifed 1', the code below makes the driver * panic on the first attempt to write to a SCSI device. * It is the first test we want to do after a driver * change that does not seem obviously safe. :) */ #if 0 switch (cp->cdb_buf[0]) { case 0x0A: case 0x2A: case 0xAA: panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n"); MDELAY(10000); break; default: break; } #endif /* * Activate this job. */ sym_put_start_queue(np, cp); out: splx(s); return; out_abort: sym_free_ccb(np, cp); sym_xpt_done(np, ccb); goto out; } /* * How complex it gets to deal with the data in CAM. * The Bus Dma stuff makes things still more complex. */ static void sym_setup_data_and_start(hcb_p np, struct ccb_scsiio *csio, ccb_p cp) { struct ccb_hdr *ccb_h; int dir, retv; ccb_h = &csio->ccb_h; /* * Now deal with the data. */ cp->data_len = csio->dxfer_len; cp->arg = np; /* * No direction means no data. */ dir = (ccb_h->flags & CAM_DIR_MASK); if (dir == CAM_DIR_NONE) { sym_execute_ccb(cp, NULL, 0, 0); return; } if (!(ccb_h->flags & CAM_SCATTER_VALID)) { /* Single buffer */ if (!(ccb_h->flags & CAM_DATA_PHYS)) { /* Buffer is virtual */ int s; cp->dmamapped = (dir == CAM_DIR_IN) ? SYM_DMA_READ : SYM_DMA_WRITE; s = splsoftvm(); retv = bus_dmamap_load(np->data_dmat, cp->dmamap, csio->data_ptr, csio->dxfer_len, sym_execute_ccb, cp, 0); if (retv == EINPROGRESS) { cp->host_status = HS_WAIT; xpt_freeze_simq(np->sim, 1); csio->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { /* Buffer is physical */ struct bus_dma_segment seg; seg.ds_addr = (bus_addr_t) csio->data_ptr; sym_execute_ccb(cp, &seg, 1, 0); } } else { /* Scatter/gather list */ struct bus_dma_segment *segs; if ((ccb_h->flags & CAM_SG_LIST_PHYS) != 0) { /* The SG list pointer is physical */ sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); goto out_abort; } if (!(ccb_h->flags & CAM_DATA_PHYS)) { /* SG buffer pointers are virtual */ sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); goto out_abort; } /* SG buffer pointers are physical */ segs = (struct bus_dma_segment *)csio->data_ptr; sym_execute_ccb(cp, segs, csio->sglist_cnt, 0); } return; out_abort: sym_free_ccb(np, cp); sym_xpt_done(np, (union ccb *) csio); } /* * Move the scatter list to our data block. */ static int sym_fast_scatter_sg_physical(hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs) { struct sym_tblmove *data; bus_dma_segment_t *psegs2; if (nsegs > SYM_CONF_MAX_SG) return -1; data = &cp->phys.data[SYM_CONF_MAX_SG-1]; psegs2 = &psegs[nsegs-1]; cp->segments = nsegs; while (1) { data->addr = cpu_to_scr(psegs2->ds_addr); data->size = cpu_to_scr(psegs2->ds_len); if (DEBUG_FLAGS & DEBUG_SCATTER) { printf ("%s scatter: paddr=%lx len=%ld\n", sym_name(np), (long) psegs2->ds_addr, (long) psegs2->ds_len); } if (psegs2 != psegs) { --data; --psegs2; continue; } break; } return 0; } #else /* FreeBSD_Bus_Dma_Abstraction */ /* * How complex it gets to deal with the data in CAM. * Variant without the Bus Dma Abstraction option. */ static void sym_setup_data_and_start(hcb_p np, struct ccb_scsiio *csio, ccb_p cp) { struct ccb_hdr *ccb_h; int dir, retv; ccb_h = &csio->ccb_h; /* * Now deal with the data. */ cp->data_len = 0; cp->segments = 0; /* * No direction means no data. */ dir = (ccb_h->flags & CAM_DIR_MASK); if (dir == CAM_DIR_NONE) goto end_scatter; if (!(ccb_h->flags & CAM_SCATTER_VALID)) { /* Single buffer */ if (!(ccb_h->flags & CAM_DATA_PHYS)) { /* Buffer is virtual */ retv = sym_scatter_virtual(np, cp, (vm_offset_t) csio->data_ptr, (vm_size_t) csio->dxfer_len); } else { /* Buffer is physical */ retv = sym_scatter_physical(np, cp, (vm_offset_t) csio->data_ptr, (vm_size_t) csio->dxfer_len); } } else { /* Scatter/gather list */ int nsegs; struct bus_dma_segment *segs; segs = (struct bus_dma_segment *)csio->data_ptr; nsegs = csio->sglist_cnt; if ((ccb_h->flags & CAM_SG_LIST_PHYS) != 0) { /* The SG list pointer is physical */ sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); goto out_abort; } if (!(ccb_h->flags & CAM_DATA_PHYS)) { /* SG buffer pointers are virtual */ retv = sym_scatter_sg_virtual(np, cp, segs, nsegs); } else { /* SG buffer pointers are physical */ retv = sym_scatter_sg_physical(np, cp, segs, nsegs); } } if (retv < 0) { sym_set_cam_status(cp->cam_ccb, CAM_REQ_TOO_BIG); goto out_abort; } end_scatter: /* * Set data pointers. */ sym_setup_data_pointers(np, cp, dir); /* * Enqueue this IO in our pending queue. */ sym_enqueue_cam_ccb(np, (union ccb *) csio); /* * Activate this job. */ sym_put_start_queue(np, cp); /* * Command is successfully queued. */ return; out_abort: sym_free_ccb(np, cp); sym_xpt_done(np, (union ccb *) csio); } /* * Scatter a virtual buffer into bus addressable chunks. */ static int sym_scatter_virtual(hcb_p np, ccb_p cp, vm_offset_t vaddr, vm_size_t len) { u_long pe, pn; u_long n, k; int s; cp->data_len += len; pe = vaddr + len; n = len; s = SYM_CONF_MAX_SG - 1 - cp->segments; while (n && s >= 0) { pn = (pe - 1) & ~PAGE_MASK; k = pe - pn; if (k > n) { k = n; pn = pe - n; } if (DEBUG_FLAGS & DEBUG_SCATTER) { printf ("%s scatter: va=%lx pa=%lx siz=%ld\n", sym_name(np), pn, (u_long) vtobus(pn), k); } cp->phys.data[s].addr = cpu_to_scr(vtobus(pn)); cp->phys.data[s].size = cpu_to_scr(k); pe = pn; n -= k; --s; } cp->segments = SYM_CONF_MAX_SG - 1 - s; return n ? -1 : 0; } /* * Scatter a SG list with virtual addresses into bus addressable chunks. */ static int sym_scatter_sg_virtual(hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs) { int i, retv = 0; for (i = nsegs - 1 ; i >= 0 ; --i) { retv = sym_scatter_virtual(np, cp, psegs[i].ds_addr, psegs[i].ds_len); if (retv < 0) break; } return retv; } /* * Scatter a physical buffer into bus addressable chunks. */ static int sym_scatter_physical(hcb_p np, ccb_p cp, vm_offset_t paddr, vm_size_t len) { struct bus_dma_segment seg; seg.ds_addr = paddr; seg.ds_len = len; return sym_scatter_sg_physical(np, cp, &seg, 1); } #endif /* FreeBSD_Bus_Dma_Abstraction */ /* * Scatter a SG list with physical addresses into bus addressable chunks. * We need to ensure 16MB boundaries not to be crossed during DMA of * each segment, due to some chips being flawed. */ #define BOUND_MASK ((1UL<<24)-1) static int sym_scatter_sg_physical(hcb_p np, ccb_p cp, bus_dma_segment_t *psegs, int nsegs) { u_long ps, pe, pn; u_long k; int s, t; #ifndef FreeBSD_Bus_Dma_Abstraction s = SYM_CONF_MAX_SG - 1 - cp->segments; #else s = SYM_CONF_MAX_SG - 1; #endif t = nsegs - 1; ps = psegs[t].ds_addr; pe = ps + psegs[t].ds_len; while (s >= 0) { pn = (pe - 1) & ~BOUND_MASK; if (pn <= ps) pn = ps; k = pe - pn; if (DEBUG_FLAGS & DEBUG_SCATTER) { printf ("%s scatter: paddr=%lx len=%ld\n", sym_name(np), pn, k); } cp->phys.data[s].addr = cpu_to_scr(pn); cp->phys.data[s].size = cpu_to_scr(k); #ifndef FreeBSD_Bus_Dma_Abstraction cp->data_len += k; #endif --s; if (pn == ps) { if (--t < 0) break; ps = psegs[t].ds_addr; pe = ps + psegs[t].ds_len; } else pe = pn; } cp->segments = SYM_CONF_MAX_SG - 1 - s; return t >= 0 ? -1 : 0; } #undef BOUND_MASK /* * SIM action for non performance critical stuff. */ static void sym_action2(struct cam_sim *sim, union ccb *ccb) { hcb_p np; tcb_p tp; lcb_p lp; struct ccb_hdr *ccb_h; /* * Retrieve our controller data structure. */ np = (hcb_p) cam_sim_softc(sim); ccb_h = &ccb->ccb_h; switch (ccb_h->func_code) { case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; cts = &ccb->cts; tp = &np->target[ccb_h->target_id]; /* * Update SPI transport settings in TARGET control block. * Update SCSI device settings in LUN control block. */ lp = sym_lp(np, tp, ccb_h->target_lun); #ifdef FreeBSD_New_Tran_Settings if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { #else if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { #endif sym_update_trans(np, tp, &tp->tinfo.goal, cts); if (lp) sym_update_dflags(np, &lp->current_flags, cts); } #ifdef FreeBSD_New_Tran_Settings if (cts->type == CTS_TYPE_USER_SETTINGS) { #else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { #endif sym_update_trans(np, tp, &tp->tinfo.user, cts); if (lp) sym_update_dflags(np, &lp->user_flags, cts); } sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; struct sym_trans *tip; u_char dflags; cts = &ccb->cts; tp = &np->target[ccb_h->target_id]; lp = sym_lp(np, tp, ccb_h->target_lun); #ifdef FreeBSD_New_Tran_Settings #define cts__scsi (&cts->proto_specific.scsi) #define cts__spi (&cts->xport_specific.spi) if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { tip = &tp->tinfo.current; dflags = lp ? lp->current_flags : 0; } else { tip = &tp->tinfo.user; dflags = lp ? lp->user_flags : tp->usrflags; } cts->protocol = PROTO_SCSI; cts->transport = XPORT_SPI; cts->protocol_version = tip->scsi_version; cts->transport_version = tip->spi_version; cts__spi->sync_period = tip->period; cts__spi->sync_offset = tip->offset; cts__spi->bus_width = tip->width; cts__spi->ppr_options = tip->options; cts__spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_PPR_OPTIONS; cts__spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (dflags & SYM_DISC_ENABLED) cts__spi->flags |= CTS_SPI_FLAGS_DISC_ENB; cts__spi->valid |= CTS_SPI_VALID_DISC; cts__scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; if (dflags & SYM_TAGS_ENABLED) cts__scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; cts__scsi->valid |= CTS_SCSI_VALID_TQ; #undef cts__spi #undef cts__scsi #else if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { tip = &tp->tinfo.current; dflags = lp ? lp->current_flags : 0; } else { tip = &tp->tinfo.user; dflags = lp ? lp->user_flags : tp->usrflags; } cts->sync_period = tip->period; cts->sync_offset = tip->offset; cts->bus_width = tip->width; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID; cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if (dflags & SYM_DISC_ENABLED) cts->flags |= CCB_TRANS_DISC_ENB; if (dflags & SYM_TAGS_ENABLED) cts->flags |= CCB_TRANS_TAG_ENB; cts->valid |= CCB_TRANS_DISC_VALID; cts->valid |= CCB_TRANS_TQ_VALID; #endif sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u32 size_mb; - u32 secs_per_cylinder; - int extended; - - /* - * Silly DOS geometry. - */ - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - extended = 1; - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; + cam_calc_geometry(&ccb->ccg, /*extended*/1); sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_MDP_ABLE|PI_SDTR_ABLE|PI_TAG_ABLE; if ((np->features & FE_WIDE) != 0) cpi->hba_inquiry |= PI_WIDE_16; cpi->target_sprt = 0; cpi->hba_misc = 0; if (np->usrflags & SYM_SCAN_TARGETS_HILO) cpi->hba_misc |= PIM_SCANHILO; if (np->usrflags & SYM_AVOID_BUS_RESET) cpi->hba_misc |= PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = (np->features & FE_WIDE) ? 15 : 7; /* Semantic problem:)LUN number max = max number of LUNs - 1 */ cpi->max_lun = SYM_CONF_MAX_LUN-1; if (SYM_SETUP_MAX_LUN < SYM_CONF_MAX_LUN) cpi->max_lun = SYM_SETUP_MAX_LUN-1; cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = np->myaddr; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Symbios", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); #ifdef FreeBSD_New_Tran_Settings cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_ST; if (np->features & FE_ULTRA3) { cpi->transport_version = 3; cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_DT_ST; } #endif sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } case XPT_ABORT: { union ccb *abort_ccb = ccb->cab.abort_ccb; switch(abort_ccb->ccb_h.func_code) { case XPT_SCSI_IO: if (sym_abort_scsiio(np, abort_ccb, 0) == 0) { sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } default: sym_xpt_done2(np, ccb, CAM_UA_ABORT); break; } break; } case XPT_RESET_DEV: { sym_reset_dev(np, ccb); break; } case XPT_RESET_BUS: { sym_reset_scsi_bus(np, 0); if (sym_verbose) { xpt_print_path(np->path); printf("SCSI BUS reset delivered.\n"); } sym_init (np, 1); sym_xpt_done2(np, ccb, CAM_REQ_CMP); break; } case XPT_ACCEPT_TARGET_IO: case XPT_CONT_TARGET_IO: case XPT_EN_LUN: case XPT_NOTIFY_ACK: case XPT_IMMED_NOTIFY: case XPT_TERM_IO: default: sym_xpt_done2(np, ccb, CAM_REQ_INVALID); break; } } /* * Asynchronous notification handler. */ static void sym_async(void *cb_arg, u32 code, struct cam_path *path, void *arg) { hcb_p np; struct cam_sim *sim; u_int tn; tcb_p tp; int s; s = splcam(); sim = (struct cam_sim *) cb_arg; np = (hcb_p) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: tn = xpt_path_target_id(path); if (tn >= SYM_CONF_MAX_TARGET) break; tp = &np->target[tn]; tp->to_reset = 0; tp->head.sval = 0; tp->head.wval = np->rv_scntl3; tp->head.uval = 0; tp->tinfo.current.period = tp->tinfo.goal.period = 0; tp->tinfo.current.offset = tp->tinfo.goal.offset = 0; tp->tinfo.current.width = tp->tinfo.goal.width = BUS_8_BIT; tp->tinfo.current.options = tp->tinfo.goal.options = 0; break; default: break; } splx(s); } /* * Update transfer settings of a target. */ static void sym_update_trans(hcb_p np, tcb_p tp, struct sym_trans *tip, struct ccb_trans_settings *cts) { /* * Update the infos. */ #ifdef FreeBSD_New_Tran_Settings #define cts__spi (&cts->xport_specific.spi) if ((cts__spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) tip->width = cts__spi->bus_width; if ((cts__spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0) tip->offset = cts__spi->sync_offset; if ((cts__spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0) tip->period = cts__spi->sync_period; if ((cts__spi->valid & CTS_SPI_VALID_PPR_OPTIONS) != 0) tip->options = (cts__spi->ppr_options & PPR_OPT_DT); if (cts->protocol_version != PROTO_VERSION_UNSPECIFIED && cts->protocol_version != PROTO_VERSION_UNKNOWN) tip->scsi_version = cts->protocol_version; if (cts->transport_version != XPORT_VERSION_UNSPECIFIED && cts->transport_version != XPORT_VERSION_UNKNOWN) tip->spi_version = cts->transport_version; #undef cts__spi #else if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) tip->width = cts->bus_width; if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) tip->offset = cts->sync_offset; if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) tip->period = cts->sync_period; #endif /* * Scale against driver configuration limits. */ if (tip->width > SYM_SETUP_MAX_WIDE) tip->width = SYM_SETUP_MAX_WIDE; if (tip->offset > SYM_SETUP_MAX_OFFS) tip->offset = SYM_SETUP_MAX_OFFS; if (tip->period < SYM_SETUP_MIN_SYNC) tip->period = SYM_SETUP_MIN_SYNC; /* * Scale against actual controller BUS width. */ if (tip->width > np->maxwide) tip->width = np->maxwide; #ifdef FreeBSD_New_Tran_Settings /* * Only accept DT if controller supports and SYNC/WIDE asked. */ if (!((np->features & (FE_C10|FE_ULTRA3)) == (FE_C10|FE_ULTRA3)) || !(tip->width == BUS_16_BIT && tip->offset)) { tip->options &= ~PPR_OPT_DT; } #else /* * For now, only assume DT if period <= 9, BUS 16 and offset != 0. */ tip->options = 0; if ((np->features & (FE_C10|FE_ULTRA3)) == (FE_C10|FE_ULTRA3) && tip->period <= 9 && tip->width == BUS_16_BIT && tip->offset) { tip->options |= PPR_OPT_DT; } #endif /* * Scale period factor and offset against controller limits. */ if (tip->options & PPR_OPT_DT) { if (tip->period < np->minsync_dt) tip->period = np->minsync_dt; if (tip->period > np->maxsync_dt) tip->period = np->maxsync_dt; if (tip->offset > np->maxoffs_dt) tip->offset = np->maxoffs_dt; } else { if (tip->period < np->minsync) tip->period = np->minsync; if (tip->period > np->maxsync) tip->period = np->maxsync; if (tip->offset > np->maxoffs) tip->offset = np->maxoffs; } } /* * Update flags for a device (logical unit). */ static void sym_update_dflags(hcb_p np, u_char *flags, struct ccb_trans_settings *cts) { #ifdef FreeBSD_New_Tran_Settings #define cts__scsi (&cts->proto_specific.scsi) #define cts__spi (&cts->xport_specific.spi) if ((cts__spi->valid & CTS_SPI_VALID_DISC) != 0) { if ((cts__spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) *flags |= SYM_DISC_ENABLED; else *flags &= ~SYM_DISC_ENABLED; } if ((cts__scsi->valid & CTS_SCSI_VALID_TQ) != 0) { if ((cts__scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) *flags |= SYM_TAGS_ENABLED; else *flags &= ~SYM_TAGS_ENABLED; } #undef cts__spi #undef cts__scsi #else if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) *flags |= SYM_DISC_ENABLED; else *flags &= ~SYM_DISC_ENABLED; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) *flags |= SYM_TAGS_ENABLED; else *flags &= ~SYM_TAGS_ENABLED; } #endif } /*============= DRIVER INITIALISATION ==================*/ #ifdef FreeBSD_Bus_Io_Abstraction static device_method_t sym_pci_methods[] = { DEVMETHOD(device_probe, sym_pci_probe), DEVMETHOD(device_attach, sym_pci_attach), { 0, 0 } }; static driver_t sym_pci_driver = { "sym", sym_pci_methods, sizeof(struct sym_hcb) }; static devclass_t sym_devclass; DRIVER_MODULE(sym, pci, sym_pci_driver, sym_devclass, 0, 0); #else /* Pre-FreeBSD_Bus_Io_Abstraction */ static u_long sym_unit; static struct pci_device sym_pci_driver = { "sym", sym_pci_probe, sym_pci_attach, &sym_unit, NULL }; #if __FreeBSD_version >= 400000 COMPAT_PCI_DRIVER (sym, sym_pci_driver); #else DATA_SET (pcidevice_set, sym_pci_driver); #endif #endif /* FreeBSD_Bus_Io_Abstraction */ static struct sym_pci_chip sym_pci_dev_table[] = { {PCI_ID_SYM53C810, 0x0f, "810", 4, 8, 4, 64, FE_ERL} , #ifdef SYM_DEBUG_GENERIC_SUPPORT {PCI_ID_SYM53C810, 0xff, "810a", 4, 8, 4, 1, FE_BOF} , #else {PCI_ID_SYM53C810, 0xff, "810a", 4, 8, 4, 1, FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF} , #endif {PCI_ID_SYM53C815, 0xff, "815", 4, 8, 4, 64, FE_BOF|FE_ERL} , {PCI_ID_SYM53C825, 0x0f, "825", 6, 8, 4, 64, FE_WIDE|FE_BOF|FE_ERL|FE_DIFF} , {PCI_ID_SYM53C825, 0xff, "825a", 6, 8, 4, 2, FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|FE_DIFF} , {PCI_ID_SYM53C860, 0xff, "860", 4, 8, 5, 1, FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN} , {PCI_ID_SYM53C875, 0x01, "875", 6, 16, 5, 2, FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_DIFF} , {PCI_ID_SYM53C875, 0xff, "875", 6, 16, 5, 2, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_DIFF} , {PCI_ID_SYM53C875_2, 0xff, "875", 6, 16, 5, 2, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_DIFF} , {PCI_ID_SYM53C885, 0xff, "885", 6, 16, 5, 2, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_DIFF} , #ifdef SYM_DEBUG_GENERIC_SUPPORT {PCI_ID_SYM53C895, 0xff, "895", 6, 31, 7, 2, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS| FE_RAM|FE_LCKFRQ} , #else {PCI_ID_SYM53C895, 0xff, "895", 6, 31, 7, 2, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_LCKFRQ} , #endif {PCI_ID_SYM53C896, 0xff, "896", 6, 31, 7, 4, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ} , {PCI_ID_SYM53C895A, 0xff, "895a", 6, 31, 7, 4, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_RAM8K|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ} , {PCI_ID_LSI53C1010, 0x00, "1010-33", 6, 31, 7, 8, FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN| FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC| FE_C10} , {PCI_ID_LSI53C1010, 0xff, "1010-33", 6, 31, 7, 8, FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN| FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC| FE_C10|FE_U3EN} , {PCI_ID_LSI53C1010_2, 0xff, "1010-66", 6, 31, 7, 8, FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN| FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_66MHZ|FE_CRC| FE_C10|FE_U3EN} , {PCI_ID_LSI53C1510D, 0xff, "1510d", 6, 31, 7, 4, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| FE_RAM|FE_IO256|FE_LEDC} }; #define sym_pci_num_devs \ (sizeof(sym_pci_dev_table) / sizeof(sym_pci_dev_table[0])) /* * Look up the chip table. * * Return a pointer to the chip entry if found, * zero otherwise. */ static struct sym_pci_chip * #ifdef FreeBSD_Bus_Io_Abstraction sym_find_pci_chip(device_t dev) #else sym_find_pci_chip(pcici_t pci_tag) #endif { struct sym_pci_chip *chip; int i; u_short device_id; u_char revision; #ifdef FreeBSD_Bus_Io_Abstraction if (pci_get_vendor(dev) != PCI_VENDOR_NCR) return 0; device_id = pci_get_device(dev); revision = pci_get_revid(dev); #else if (pci_cfgread(pci_tag, PCIR_VENDOR, 2) != PCI_VENDOR_NCR) return 0; device_id = pci_cfgread(pci_tag, PCIR_DEVICE, 2); revision = pci_cfgread(pci_tag, PCIR_REVID, 1); #endif for (i = 0; i < sym_pci_num_devs; i++) { chip = &sym_pci_dev_table[i]; if (device_id != chip->device_id) continue; if (revision > chip->revision_id) continue; return chip; } return 0; } /* * Tell upper layer if the chip is supported. */ #ifdef FreeBSD_Bus_Io_Abstraction static int sym_pci_probe(device_t dev) { struct sym_pci_chip *chip; chip = sym_find_pci_chip(dev); if (chip && sym_find_firmware(chip)) { device_set_desc(dev, chip->name); return (chip->lp_probe_bit & SYM_SETUP_LP_PROBE_MAP)? -2000 : 0; } return ENXIO; } #else /* Pre-FreeBSD_Bus_Io_Abstraction */ static const char * sym_pci_probe(pcici_t pci_tag, pcidi_t type) { struct sym_pci_chip *chip; chip = sym_find_pci_chip(pci_tag); if (chip && sym_find_firmware(chip)) { #if NNCR > 0 /* Only claim chips we are allowed to take precedence over the ncr */ if (!(chip->lp_probe_bit & SYM_SETUP_LP_PROBE_MAP)) #else if (1) #endif return chip->name; } return 0; } #endif /* * Attach a sym53c8xx device. */ #ifdef FreeBSD_Bus_Io_Abstraction static int sym_pci_attach(device_t dev) #else static void sym_pci_attach(pcici_t pci_tag, int unit) { int err = sym_pci_attach2(pci_tag, unit); if (err) printf("sym: failed to attach unit %d - err=%d.\n", unit, err); } static int sym_pci_attach2(pcici_t pci_tag, int unit) #endif { struct sym_pci_chip *chip; u_short command; u_char cachelnsz; struct sym_hcb *np = 0; struct sym_nvram nvram; struct sym_fw *fw = 0; int i; #ifdef FreeBSD_Bus_Dma_Abstraction bus_dma_tag_t bus_dmat; /* * I expected to be told about a parent * DMA tag, but didn't find any. */ bus_dmat = NULL; #endif /* * Only probed devices should be attached. * We just enjoy being paranoid. :) */ #ifdef FreeBSD_Bus_Io_Abstraction chip = sym_find_pci_chip(dev); #else chip = sym_find_pci_chip(pci_tag); #endif if (chip == NULL || (fw = sym_find_firmware(chip)) == NULL) return (ENXIO); /* * Allocate immediately the host control block, * since we are only expecting to succeed. :) * We keep track in the HCB of all the resources that * are to be released on error. */ #ifdef FreeBSD_Bus_Dma_Abstraction np = __sym_calloc_dma(bus_dmat, sizeof(*np), "HCB"); if (np) np->bus_dmat = bus_dmat; else goto attach_failed; #else np = sym_calloc_dma(sizeof(*np), "HCB"); if (!np) goto attach_failed; #endif /* * Copy some useful infos to the HCB. */ np->hcb_ba = vtobus(np); np->verbose = bootverbose; #ifdef FreeBSD_Bus_Io_Abstraction np->device = dev; np->unit = device_get_unit(dev); np->device_id = pci_get_device(dev); np->revision_id = pci_get_revid(dev); #else np->pci_tag = pci_tag; np->unit = unit; np->device_id = pci_cfgread(pci_tag, PCIR_DEVICE, 2); np->revision_id = pci_cfgread(pci_tag, PCIR_REVID, 1); #endif np->features = chip->features; np->clock_divn = chip->nr_divisor; np->maxoffs = chip->offset_max; np->maxburst = chip->burst_max; np->scripta_sz = fw->a_size; np->scriptb_sz = fw->b_size; np->fw_setup = fw->setup; np->fw_patch = fw->patch; np->fw_name = fw->name; /* * Edit its name. */ snprintf(np->inst_name, sizeof(np->inst_name), "sym%d", np->unit); /* * Initialyze the CCB free and busy queues. */ sym_que_init(&np->free_ccbq); sym_que_init(&np->busy_ccbq); sym_que_init(&np->comp_ccbq); sym_que_init(&np->cam_ccbq); /* * Allocate a tag for the DMA of user data. */ #ifdef FreeBSD_Bus_Dma_Abstraction if (bus_dma_tag_create(np->bus_dmat, 1, (1<<24), BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, SYM_CONF_MAX_SG, (1<<24), 0, &np->data_dmat)) { device_printf(dev, "failed to create DMA tag.\n"); goto attach_failed; } #endif /* * Read and apply some fix-ups to the PCI COMMAND * register. We want the chip to be enabled for: * - BUS mastering * - PCI parity checking (reporting would also be fine) * - Write And Invalidate. */ #ifdef FreeBSD_Bus_Io_Abstraction command = pci_read_config(dev, PCIR_COMMAND, 2); #else command = pci_cfgread(pci_tag, PCIR_COMMAND, 2); #endif command |= PCIM_CMD_BUSMASTEREN; command |= PCIM_CMD_PERRESPEN; command |= /* PCIM_CMD_MWIEN */ 0x0010; #ifdef FreeBSD_Bus_Io_Abstraction pci_write_config(dev, PCIR_COMMAND, command, 2); #else pci_cfgwrite(pci_tag, PCIR_COMMAND, command, 2); #endif /* * Let the device know about the cache line size, * if it doesn't yet. */ #ifdef FreeBSD_Bus_Io_Abstraction cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); #else cachelnsz = pci_cfgread(pci_tag, PCIR_CACHELNSZ, 1); #endif if (!cachelnsz) { cachelnsz = 8; #ifdef FreeBSD_Bus_Io_Abstraction pci_write_config(dev, PCIR_CACHELNSZ, cachelnsz, 1); #else pci_cfgwrite(pci_tag, PCIR_CACHELNSZ, cachelnsz, 1); #endif } /* * Alloc/get/map/retrieve everything that deals with MMIO. */ #ifdef FreeBSD_Bus_Io_Abstraction if ((command & PCIM_CMD_MEMEN) != 0) { int regs_id = SYM_PCI_MMIO; np->mmio_res = bus_alloc_resource(dev, SYS_RES_MEMORY, ®s_id, 0, ~0, 1, RF_ACTIVE); } if (!np->mmio_res) { device_printf(dev, "failed to allocate MMIO resources\n"); goto attach_failed; } np->mmio_bsh = rman_get_bushandle(np->mmio_res); np->mmio_tag = rman_get_bustag(np->mmio_res); np->mmio_pa = rman_get_start(np->mmio_res); np->mmio_va = (vm_offset_t) rman_get_virtual(np->mmio_res); np->mmio_ba = np->mmio_pa; #else if ((command & PCIM_CMD_MEMEN) != 0) { vm_offset_t vaddr, paddr; if (!pci_map_mem(pci_tag, SYM_PCI_MMIO, &vaddr, &paddr)) { printf("%s: failed to map MMIO window\n", sym_name(np)); goto attach_failed; } np->mmio_va = vaddr; np->mmio_pa = paddr; np->mmio_ba = paddr; } #endif /* * Allocate the IRQ. */ #ifdef FreeBSD_Bus_Io_Abstraction i = 0; np->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &i, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!np->irq_res) { device_printf(dev, "failed to allocate IRQ resource\n"); goto attach_failed; } #endif #ifdef SYM_CONF_IOMAPPED /* * User want us to use normal IO with PCI. * Alloc/get/map/retrieve everything that deals with IO. */ #ifdef FreeBSD_Bus_Io_Abstraction if ((command & PCI_COMMAND_IO_ENABLE) != 0) { int regs_id = SYM_PCI_IO; np->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, ®s_id, 0, ~0, 1, RF_ACTIVE); } if (!np->io_res) { device_printf(dev, "failed to allocate IO resources\n"); goto attach_failed; } np->io_bsh = rman_get_bushandle(np->io_res); np->io_tag = rman_get_bustag(np->io_res); np->io_port = rman_get_start(np->io_res); #else if ((command & PCI_COMMAND_IO_ENABLE) != 0) { pci_port_t io_port; if (!pci_map_port (pci_tag, SYM_PCI_IO, &io_port)) { printf("%s: failed to map IO window\n", sym_name(np)); goto attach_failed; } np->io_port = io_port; } #endif #endif /* SYM_CONF_IOMAPPED */ /* * If the chip has RAM. * Alloc/get/map/retrieve the corresponding resources. */ if ((np->features & (FE_RAM|FE_RAM8K)) && (command & PCIM_CMD_MEMEN) != 0) { #ifdef FreeBSD_Bus_Io_Abstraction int regs_id = SYM_PCI_RAM; if (np->features & FE_64BIT) regs_id = SYM_PCI_RAM64; np->ram_res = bus_alloc_resource(dev, SYS_RES_MEMORY, ®s_id, 0, ~0, 1, RF_ACTIVE); if (!np->ram_res) { device_printf(dev,"failed to allocate RAM resources\n"); goto attach_failed; } np->ram_id = regs_id; np->ram_bsh = rman_get_bushandle(np->ram_res); np->ram_tag = rman_get_bustag(np->ram_res); np->ram_pa = rman_get_start(np->ram_res); np->ram_va = (vm_offset_t) rman_get_virtual(np->ram_res); np->ram_ba = np->ram_pa; #else vm_offset_t vaddr, paddr; int regs_id = SYM_PCI_RAM; if (np->features & FE_64BIT) regs_id = SYM_PCI_RAM64; if (!pci_map_mem(pci_tag, regs_id, &vaddr, &paddr)) { printf("%s: failed to map RAM window\n", sym_name(np)); goto attach_failed; } np->ram_va = vaddr; np->ram_pa = paddr; np->ram_ba = paddr; #endif } /* * Save setting of some IO registers, so we will * be able to probe specific implementations. */ sym_save_initial_setting (np); /* * Reset the chip now, since it has been reported * that SCSI clock calibration may not work properly * if the chip is currently active. */ sym_chip_reset (np); /* * Try to read the user set-up. */ (void) sym_read_nvram(np, &nvram); /* * Prepare controller and devices settings, according * to chip features, user set-up and driver set-up. */ (void) sym_prepare_setting(np, &nvram); /* * Check the PCI clock frequency. * Must be performed after prepare_setting since it destroys * STEST1 that is used to probe for the clock doubler. */ i = sym_getpciclock(np); if (i > 37000) #ifdef FreeBSD_Bus_Io_Abstraction device_printf(dev, "PCI BUS clock seems too high: %u KHz.\n",i); #else printf("%s: PCI BUS clock seems too high: %u KHz.\n", sym_name(np), i); #endif /* * Allocate the start queue. */ np->squeue = (u32 *) sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"SQUEUE"); if (!np->squeue) goto attach_failed; np->squeue_ba = vtobus(np->squeue); /* * Allocate the done queue. */ np->dqueue = (u32 *) sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"DQUEUE"); if (!np->dqueue) goto attach_failed; np->dqueue_ba = vtobus(np->dqueue); /* * Allocate the target bus address array. */ np->targtbl = (u32 *) sym_calloc_dma(256, "TARGTBL"); if (!np->targtbl) goto attach_failed; np->targtbl_ba = vtobus(np->targtbl); /* * Allocate SCRIPTS areas. */ np->scripta0 = sym_calloc_dma(np->scripta_sz, "SCRIPTA0"); np->scriptb0 = sym_calloc_dma(np->scriptb_sz, "SCRIPTB0"); if (!np->scripta0 || !np->scriptb0) goto attach_failed; /* * Allocate some CCB. We need at least ONE. */ if (!sym_alloc_ccb(np)) goto attach_failed; /* * Calculate BUS addresses where we are going * to load the SCRIPTS. */ np->scripta_ba = vtobus(np->scripta0); np->scriptb_ba = vtobus(np->scriptb0); np->scriptb0_ba = np->scriptb_ba; if (np->ram_ba) { np->scripta_ba = np->ram_ba; if (np->features & FE_RAM8K) { np->ram_ws = 8192; np->scriptb_ba = np->scripta_ba + 4096; #if BITS_PER_LONG > 32 np->scr_ram_seg = cpu_to_scr(np->scripta_ba >> 32); #endif } else np->ram_ws = 4096; } /* * Copy scripts to controller instance. */ bcopy(fw->a_base, np->scripta0, np->scripta_sz); bcopy(fw->b_base, np->scriptb0, np->scriptb_sz); /* * Setup variable parts in scripts and compute * scripts bus addresses used from the C code. */ np->fw_setup(np, fw); /* * Bind SCRIPTS with physical addresses usable by the * SCRIPTS processor (as seen from the BUS = BUS addresses). */ sym_fw_bind_script(np, (u32 *) np->scripta0, np->scripta_sz); sym_fw_bind_script(np, (u32 *) np->scriptb0, np->scriptb_sz); #ifdef SYM_CONF_IARB_SUPPORT /* * If user wants IARB to be set when we win arbitration * and have other jobs, compute the max number of consecutive * settings of IARB hints before we leave devices a chance to * arbitrate for reselection. */ #ifdef SYM_SETUP_IARB_MAX np->iarb_max = SYM_SETUP_IARB_MAX; #else np->iarb_max = 4; #endif #endif /* * Prepare the idle and invalid task actions. */ np->idletask.start = cpu_to_scr(SCRIPTA_BA (np, idle)); np->idletask.restart = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l)); np->idletask_ba = vtobus(&np->idletask); np->notask.start = cpu_to_scr(SCRIPTA_BA (np, idle)); np->notask.restart = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l)); np->notask_ba = vtobus(&np->notask); np->bad_itl.start = cpu_to_scr(SCRIPTA_BA (np, idle)); np->bad_itl.restart = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l)); np->bad_itl_ba = vtobus(&np->bad_itl); np->bad_itlq.start = cpu_to_scr(SCRIPTA_BA (np, idle)); np->bad_itlq.restart = cpu_to_scr(SCRIPTB_BA (np,bad_i_t_l_q)); np->bad_itlq_ba = vtobus(&np->bad_itlq); /* * Allocate and prepare the lun JUMP table that is used * for a target prior the probing of devices (bad lun table). * A private table will be allocated for the target on the * first INQUIRY response received. */ np->badluntbl = sym_calloc_dma(256, "BADLUNTBL"); if (!np->badluntbl) goto attach_failed; np->badlun_sa = cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun)); for (i = 0 ; i < 64 ; i++) /* 64 luns/target, no less */ np->badluntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa)); /* * Prepare the bus address array that contains the bus * address of each target control block. * For now, assume all logical units are wrong. :) */ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { np->targtbl[i] = cpu_to_scr(vtobus(&np->target[i])); np->target[i].head.luntbl_sa = cpu_to_scr(vtobus(np->badluntbl)); np->target[i].head.lun0_sa = cpu_to_scr(vtobus(&np->badlun_sa)); } /* * Now check the cache handling of the pci chipset. */ if (sym_snooptest (np)) { #ifdef FreeBSD_Bus_Io_Abstraction device_printf(dev, "CACHE INCORRECTLY CONFIGURED.\n"); #else printf("%s: CACHE INCORRECTLY CONFIGURED.\n", sym_name(np)); #endif goto attach_failed; }; /* * Now deal with CAM. * Hopefully, we will succeed with that one.:) */ if (!sym_cam_attach(np)) goto attach_failed; /* * Sigh! we are done. */ return 0; /* * We have failed. * We will try to free all the resources we have * allocated, but if we are a boot device, this * will not help that much.;) */ attach_failed: if (np) sym_pci_free(np); return ENXIO; } /* * Free everything that have been allocated for this device. */ static void sym_pci_free(hcb_p np) { SYM_QUEHEAD *qp; ccb_p cp; tcb_p tp; lcb_p lp; int target, lun; int s; /* * First free CAM resources. */ s = splcam(); sym_cam_free(np); splx(s); /* * Now every should be quiet for us to * free other resources. */ #ifdef FreeBSD_Bus_Io_Abstraction if (np->ram_res) bus_release_resource(np->device, SYS_RES_MEMORY, np->ram_id, np->ram_res); if (np->mmio_res) bus_release_resource(np->device, SYS_RES_MEMORY, SYM_PCI_MMIO, np->mmio_res); if (np->io_res) bus_release_resource(np->device, SYS_RES_IOPORT, SYM_PCI_IO, np->io_res); if (np->irq_res) bus_release_resource(np->device, SYS_RES_IRQ, 0, np->irq_res); #else /* * YEAH!!! * It seems there is no means to free MMIO resources. */ #endif if (np->scriptb0) sym_mfree_dma(np->scriptb0, np->scriptb_sz, "SCRIPTB0"); if (np->scripta0) sym_mfree_dma(np->scripta0, np->scripta_sz, "SCRIPTA0"); if (np->squeue) sym_mfree_dma(np->squeue, sizeof(u32)*(MAX_QUEUE*2), "SQUEUE"); if (np->dqueue) sym_mfree_dma(np->dqueue, sizeof(u32)*(MAX_QUEUE*2), "DQUEUE"); while ((qp = sym_remque_head(&np->free_ccbq)) != 0) { cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); #ifdef FreeBSD_Bus_Dma_Abstraction bus_dmamap_destroy(np->data_dmat, cp->dmamap); #endif sym_mfree_dma(cp->sns_bbuf, SYM_SNS_BBUF_LEN, "SNS_BBUF"); sym_mfree_dma(cp, sizeof(*cp), "CCB"); } if (np->badluntbl) sym_mfree_dma(np->badluntbl, 256,"BADLUNTBL"); for (target = 0; target < SYM_CONF_MAX_TARGET ; target++) { tp = &np->target[target]; for (lun = 0 ; lun < SYM_CONF_MAX_LUN ; lun++) { lp = sym_lp(np, tp, lun); if (!lp) continue; if (lp->itlq_tbl) sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL"); if (lp->cb_tags) sym_mfree(lp->cb_tags, SYM_CONF_MAX_TASK, "CB_TAGS"); sym_mfree_dma(lp, sizeof(*lp), "LCB"); } #if SYM_CONF_MAX_LUN > 1 if (tp->lunmp) sym_mfree(tp->lunmp, SYM_CONF_MAX_LUN*sizeof(lcb_p), "LUNMP"); #endif } if (np->targtbl) sym_mfree_dma(np->targtbl, 256, "TARGTBL"); #ifdef FreeBSD_Bus_Dma_Abstraction if (np->data_dmat) bus_dma_tag_destroy(np->data_dmat); #endif sym_mfree_dma(np, sizeof(*np), "HCB"); } /* * Allocate CAM resources and register a bus to CAM. */ static int sym_cam_attach(hcb_p np) { struct cam_devq *devq = 0; struct cam_sim *sim = 0; struct cam_path *path = 0; struct ccb_setasync csa; int err, s; s = splcam(); /* * Establish our interrupt handler. */ #ifdef FreeBSD_Bus_Io_Abstraction err = bus_setup_intr(np->device, np->irq_res, INTR_TYPE_CAM | INTR_ENTROPY, sym_intr, np, &np->intr); if (err) { device_printf(np->device, "bus_setup_intr() failed: %d\n", err); goto fail; } #else err = 0; if (!pci_map_int (np->pci_tag, sym_intr, np, &cam_imask)) { printf("%s: failed to map interrupt\n", sym_name(np)); goto fail; } #endif /* * Create the device queue for our sym SIM. */ devq = cam_simq_alloc(SYM_CONF_MAX_START); if (!devq) goto fail; /* * Construct our SIM entry. */ sim = cam_sim_alloc(sym_action, sym_poll, "sym", np, np->unit, 1, SYM_SETUP_MAX_TAG, devq); if (!sim) goto fail; devq = 0; if (xpt_bus_register(sim, 0) != CAM_SUCCESS) goto fail; np->sim = sim; sim = 0; if (xpt_create_path(&path, 0, cam_sim_path(np->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { goto fail; } np->path = path; /* * Hmmm... This should be useful, but I donnot want to * know about. */ #if __FreeBSD_version < 400000 #ifdef __alpha__ #ifdef FreeBSD_Bus_Io_Abstraction alpha_register_pci_scsi(pci_get_bus(np->device), pci_get_slot(np->device), np->sim); #else alpha_register_pci_scsi(pci_tag->bus, pci_tag->slot, np->sim); #endif #endif #endif /* * Establish our async notification handler. */ xpt_setup_ccb(&csa.ccb_h, np->path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = sym_async; csa.callback_arg = np->sim; xpt_action((union ccb *)&csa); /* * Start the chip now, without resetting the BUS, since * it seems that this must stay under control of CAM. * With LVD/SE capable chips and BUS in SE mode, we may * get a spurious SMBC interrupt. */ sym_init (np, 0); splx(s); return 1; fail: if (sim) cam_sim_free(sim, FALSE); if (devq) cam_simq_free(devq); sym_cam_free(np); splx(s); return 0; } /* * Free everything that deals with CAM. */ static void sym_cam_free(hcb_p np) { #ifdef FreeBSD_Bus_Io_Abstraction if (np->intr) bus_teardown_intr(np->device, np->irq_res, np->intr); #else /* pci_unmap_int(np->pci_tag); */ /* Does nothing */ #endif if (np->sim) { xpt_bus_deregister(cam_sim_path(np->sim)); cam_sim_free(np->sim, /*free_devq*/ TRUE); } if (np->path) xpt_free_path(np->path); } /*============ OPTIONNAL NVRAM SUPPORT =================*/ /* * Get host setup from NVRAM. */ static void sym_nvram_setup_host (hcb_p np, struct sym_nvram *nvram) { #ifdef SYM_CONF_NVRAM_SUPPORT /* * Get parity checking, host ID, verbose mode * and miscellaneous host flags from NVRAM. */ switch(nvram->type) { case SYM_SYMBIOS_NVRAM: if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE)) np->rv_scntl0 &= ~0x0a; np->myaddr = nvram->data.Symbios.host_id & 0x0f; if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS) np->verbose += 1; if (nvram->data.Symbios.flags1 & SYMBIOS_SCAN_HI_LO) np->usrflags |= SYM_SCAN_TARGETS_HILO; if (nvram->data.Symbios.flags2 & SYMBIOS_AVOID_BUS_RESET) np->usrflags |= SYM_AVOID_BUS_RESET; break; case SYM_TEKRAM_NVRAM: np->myaddr = nvram->data.Tekram.host_id & 0x0f; break; default: break; } #endif } /* * Get target setup from NVRAM. */ #ifdef SYM_CONF_NVRAM_SUPPORT static void sym_Symbios_setup_target(hcb_p np,int target, Symbios_nvram *nvram); static void sym_Tekram_setup_target(hcb_p np,int target, Tekram_nvram *nvram); #endif static void sym_nvram_setup_target (hcb_p np, int target, struct sym_nvram *nvp) { #ifdef SYM_CONF_NVRAM_SUPPORT switch(nvp->type) { case SYM_SYMBIOS_NVRAM: sym_Symbios_setup_target (np, target, &nvp->data.Symbios); break; case SYM_TEKRAM_NVRAM: sym_Tekram_setup_target (np, target, &nvp->data.Tekram); break; default: break; } #endif } #ifdef SYM_CONF_NVRAM_SUPPORT /* * Get target set-up from Symbios format NVRAM. */ static void sym_Symbios_setup_target(hcb_p np, int target, Symbios_nvram *nvram) { tcb_p tp = &np->target[target]; Symbios_target *tn = &nvram->target[target]; tp->tinfo.user.period = tn->sync_period ? (tn->sync_period + 3) / 4 : 0; tp->tinfo.user.width = tn->bus_width == 0x10 ? BUS_16_BIT : BUS_8_BIT; tp->usrtags = (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0; if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE)) tp->usrflags &= ~SYM_DISC_ENABLED; if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)) tp->usrflags |= SYM_SCAN_BOOT_DISABLED; if (!(tn->flags & SYMBIOS_SCAN_LUNS)) tp->usrflags |= SYM_SCAN_LUNS_DISABLED; } /* * Get target set-up from Tekram format NVRAM. */ static void sym_Tekram_setup_target(hcb_p np, int target, Tekram_nvram *nvram) { tcb_p tp = &np->target[target]; struct Tekram_target *tn = &nvram->target[target]; int i; if (tn->flags & TEKRAM_SYNC_NEGO) { i = tn->sync_index & 0xf; tp->tinfo.user.period = Tekram_sync[i]; } tp->tinfo.user.width = (tn->flags & TEKRAM_WIDE_NEGO) ? BUS_16_BIT : BUS_8_BIT; if (tn->flags & TEKRAM_TAGGED_COMMANDS) { tp->usrtags = 2 << nvram->max_tags_index; } if (tn->flags & TEKRAM_DISCONNECT_ENABLE) tp->usrflags |= SYM_DISC_ENABLED; /* If any device does not support parity, we will not use this option */ if (!(tn->flags & TEKRAM_PARITY_CHECK)) np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */ } #ifdef SYM_CONF_DEBUG_NVRAM /* * Dump Symbios format NVRAM for debugging purpose. */ static void sym_display_Symbios_nvram(hcb_p np, Symbios_nvram *nvram) { int i; /* display Symbios nvram host data */ printf("%s: HOST ID=%d%s%s%s%s%s%s\n", sym_name(np), nvram->host_id & 0x0f, (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"", (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"", (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"", (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"", (nvram->flags2 & SYMBIOS_AVOID_BUS_RESET)?" NO_RESET" :"", (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :""); /* display Symbios nvram drive data */ for (i = 0 ; i < 15 ; i++) { struct Symbios_target *tn = &nvram->target[i]; printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n", sym_name(np), i, (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "", (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "", (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "", (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "", tn->bus_width, tn->sync_period / 4, tn->timeout); } } /* * Dump TEKRAM format NVRAM for debugging purpose. */ static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120}; static void sym_display_Tekram_nvram(hcb_p np, Tekram_nvram *nvram) { int i, tags, boot_delay; char *rem; /* display Tekram nvram host data */ tags = 2 << nvram->max_tags_index; boot_delay = 0; if (nvram->boot_delay_index < 6) boot_delay = Tekram_boot_delay[nvram->boot_delay_index]; switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) { default: case 0: rem = ""; break; case 1: rem = " REMOVABLE=boot device"; break; case 2: rem = " REMOVABLE=all"; break; } printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n", sym_name(np), nvram->host_id & 0x0f, (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"", (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"", (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"", (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"", (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"", (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"", (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"", (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"", rem, boot_delay, tags); /* display Tekram nvram drive data */ for (i = 0; i <= 15; i++) { int sync, j; struct Tekram_target *tn = &nvram->target[i]; j = tn->sync_index & 0xf; sync = Tekram_sync[j]; printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n", sym_name(np), i, (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "", (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "", (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "", (tn->flags & TEKRAM_START_CMD) ? " START" : "", (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "", (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "", sync); } } #endif /* SYM_CONF_DEBUG_NVRAM */ #endif /* SYM_CONF_NVRAM_SUPPORT */ /* * Try reading Symbios or Tekram NVRAM */ #ifdef SYM_CONF_NVRAM_SUPPORT static int sym_read_Symbios_nvram (hcb_p np, Symbios_nvram *nvram); static int sym_read_Tekram_nvram (hcb_p np, Tekram_nvram *nvram); #endif static int sym_read_nvram(hcb_p np, struct sym_nvram *nvp) { #ifdef SYM_CONF_NVRAM_SUPPORT /* * Try to read SYMBIOS nvram. * Try to read TEKRAM nvram if Symbios nvram not found. */ if (SYM_SETUP_SYMBIOS_NVRAM && !sym_read_Symbios_nvram (np, &nvp->data.Symbios)) { nvp->type = SYM_SYMBIOS_NVRAM; #ifdef SYM_CONF_DEBUG_NVRAM sym_display_Symbios_nvram(np, &nvp->data.Symbios); #endif } else if (SYM_SETUP_TEKRAM_NVRAM && !sym_read_Tekram_nvram (np, &nvp->data.Tekram)) { nvp->type = SYM_TEKRAM_NVRAM; #ifdef SYM_CONF_DEBUG_NVRAM sym_display_Tekram_nvram(np, &nvp->data.Tekram); #endif } else nvp->type = 0; #else nvp->type = 0; #endif return nvp->type; } #ifdef SYM_CONF_NVRAM_SUPPORT /* * 24C16 EEPROM reading. * * GPOI0 - data in/data out * GPIO1 - clock * Symbios NVRAM wiring now also used by Tekram. */ #define SET_BIT 0 #define CLR_BIT 1 #define SET_CLK 2 #define CLR_CLK 3 /* * Set/clear data/clock bit in GPIO0 */ static void S24C16_set_bit(hcb_p np, u_char write_bit, u_char *gpreg, int bit_mode) { UDELAY (5); switch (bit_mode){ case SET_BIT: *gpreg |= write_bit; break; case CLR_BIT: *gpreg &= 0xfe; break; case SET_CLK: *gpreg |= 0x02; break; case CLR_CLK: *gpreg &= 0xfd; break; } OUTB (nc_gpreg, *gpreg); UDELAY (5); } /* * Send START condition to NVRAM to wake it up. */ static void S24C16_start(hcb_p np, u_char *gpreg) { S24C16_set_bit(np, 1, gpreg, SET_BIT); S24C16_set_bit(np, 0, gpreg, SET_CLK); S24C16_set_bit(np, 0, gpreg, CLR_BIT); S24C16_set_bit(np, 0, gpreg, CLR_CLK); } /* * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!! */ static void S24C16_stop(hcb_p np, u_char *gpreg) { S24C16_set_bit(np, 0, gpreg, SET_CLK); S24C16_set_bit(np, 1, gpreg, SET_BIT); } /* * Read or write a bit to the NVRAM, * read if GPIO0 input else write if GPIO0 output */ static void S24C16_do_bit(hcb_p np, u_char *read_bit, u_char write_bit, u_char *gpreg) { S24C16_set_bit(np, write_bit, gpreg, SET_BIT); S24C16_set_bit(np, 0, gpreg, SET_CLK); if (read_bit) *read_bit = INB (nc_gpreg); S24C16_set_bit(np, 0, gpreg, CLR_CLK); S24C16_set_bit(np, 0, gpreg, CLR_BIT); } /* * Output an ACK to the NVRAM after reading, * change GPIO0 to output and when done back to an input */ static void S24C16_write_ack(hcb_p np, u_char write_bit, u_char *gpreg, u_char *gpcntl) { OUTB (nc_gpcntl, *gpcntl & 0xfe); S24C16_do_bit(np, 0, write_bit, gpreg); OUTB (nc_gpcntl, *gpcntl); } /* * Input an ACK from NVRAM after writing, * change GPIO0 to input and when done back to an output */ static void S24C16_read_ack(hcb_p np, u_char *read_bit, u_char *gpreg, u_char *gpcntl) { OUTB (nc_gpcntl, *gpcntl | 0x01); S24C16_do_bit(np, read_bit, 1, gpreg); OUTB (nc_gpcntl, *gpcntl); } /* * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK, * GPIO0 must already be set as an output */ static void S24C16_write_byte(hcb_p np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl) { int x; for (x = 0; x < 8; x++) S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg); S24C16_read_ack(np, ack_data, gpreg, gpcntl); } /* * READ a byte from the NVRAM and then send an ACK to say we have got it, * GPIO0 must already be set as an input */ static void S24C16_read_byte(hcb_p np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl) { int x; u_char read_bit; *read_data = 0; for (x = 0; x < 8; x++) { S24C16_do_bit(np, &read_bit, 1, gpreg); *read_data |= ((read_bit & 0x01) << (7 - x)); } S24C16_write_ack(np, ack_data, gpreg, gpcntl); } /* * Read 'len' bytes starting at 'offset'. */ static int sym_read_S24C16_nvram (hcb_p np, int offset, u_char *data, int len) { u_char gpcntl, gpreg; u_char old_gpcntl, old_gpreg; u_char ack_data; int retv = 1; int x; /* save current state of GPCNTL and GPREG */ old_gpreg = INB (nc_gpreg); old_gpcntl = INB (nc_gpcntl); gpcntl = old_gpcntl & 0x1c; /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ OUTB (nc_gpreg, old_gpreg); OUTB (nc_gpcntl, gpcntl); /* this is to set NVRAM into a known state with GPIO0/1 both low */ gpreg = old_gpreg; S24C16_set_bit(np, 0, &gpreg, CLR_CLK); S24C16_set_bit(np, 0, &gpreg, CLR_BIT); /* now set NVRAM inactive with GPIO0/1 both high */ S24C16_stop(np, &gpreg); /* activate NVRAM */ S24C16_start(np, &gpreg); /* write device code and random address MSB */ S24C16_write_byte(np, &ack_data, 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl); if (ack_data & 0x01) goto out; /* write random address LSB */ S24C16_write_byte(np, &ack_data, offset & 0xff, &gpreg, &gpcntl); if (ack_data & 0x01) goto out; /* regenerate START state to set up for reading */ S24C16_start(np, &gpreg); /* rewrite device code and address MSB with read bit set (lsb = 0x01) */ S24C16_write_byte(np, &ack_data, 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl); if (ack_data & 0x01) goto out; /* now set up GPIO0 for inputting data */ gpcntl |= 0x01; OUTB (nc_gpcntl, gpcntl); /* input all requested data - only part of total NVRAM */ for (x = 0; x < len; x++) S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl); /* finally put NVRAM back in inactive mode */ gpcntl &= 0xfe; OUTB (nc_gpcntl, gpcntl); S24C16_stop(np, &gpreg); retv = 0; out: /* return GPIO0/1 to original states after having accessed NVRAM */ OUTB (nc_gpcntl, old_gpcntl); OUTB (nc_gpreg, old_gpreg); return retv; } #undef SET_BIT /* 0 */ #undef CLR_BIT /* 1 */ #undef SET_CLK /* 2 */ #undef CLR_CLK /* 3 */ /* * Try reading Symbios NVRAM. * Return 0 if OK. */ static int sym_read_Symbios_nvram (hcb_p np, Symbios_nvram *nvram) { static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0}; u_char *data = (u_char *) nvram; int len = sizeof(*nvram); u_short csum; int x; /* probe the 24c16 and read the SYMBIOS 24c16 area */ if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len)) return 1; /* check valid NVRAM signature, verify byte count and checksum */ if (nvram->type != 0 || bcmp(nvram->trailer, Symbios_trailer, 6) || nvram->byte_count != len - 12) return 1; /* verify checksum */ for (x = 6, csum = 0; x < len - 6; x++) csum += data[x]; if (csum != nvram->checksum) return 1; return 0; } /* * 93C46 EEPROM reading. * * GPOI0 - data in * GPIO1 - data out * GPIO2 - clock * GPIO4 - chip select * * Used by Tekram. */ /* * Pulse clock bit in GPIO0 */ static void T93C46_Clk(hcb_p np, u_char *gpreg) { OUTB (nc_gpreg, *gpreg | 0x04); UDELAY (2); OUTB (nc_gpreg, *gpreg); } /* * Read bit from NVRAM */ static void T93C46_Read_Bit(hcb_p np, u_char *read_bit, u_char *gpreg) { UDELAY (2); T93C46_Clk(np, gpreg); *read_bit = INB (nc_gpreg); } /* * Write bit to GPIO0 */ static void T93C46_Write_Bit(hcb_p np, u_char write_bit, u_char *gpreg) { if (write_bit & 0x01) *gpreg |= 0x02; else *gpreg &= 0xfd; *gpreg |= 0x10; OUTB (nc_gpreg, *gpreg); UDELAY (2); T93C46_Clk(np, gpreg); } /* * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!! */ static void T93C46_Stop(hcb_p np, u_char *gpreg) { *gpreg &= 0xef; OUTB (nc_gpreg, *gpreg); UDELAY (2); T93C46_Clk(np, gpreg); } /* * Send read command and address to NVRAM */ static void T93C46_Send_Command(hcb_p np, u_short write_data, u_char *read_bit, u_char *gpreg) { int x; /* send 9 bits, start bit (1), command (2), address (6) */ for (x = 0; x < 9; x++) T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg); *read_bit = INB (nc_gpreg); } /* * READ 2 bytes from the NVRAM */ static void T93C46_Read_Word(hcb_p np, u_short *nvram_data, u_char *gpreg) { int x; u_char read_bit; *nvram_data = 0; for (x = 0; x < 16; x++) { T93C46_Read_Bit(np, &read_bit, gpreg); if (read_bit & 0x01) *nvram_data |= (0x01 << (15 - x)); else *nvram_data &= ~(0x01 << (15 - x)); } } /* * Read Tekram NvRAM data. */ static int T93C46_Read_Data(hcb_p np, u_short *data,int len,u_char *gpreg) { u_char read_bit; int x; for (x = 0; x < len; x++) { /* output read command and address */ T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg); if (read_bit & 0x01) return 1; /* Bad */ T93C46_Read_Word(np, &data[x], gpreg); T93C46_Stop(np, gpreg); } return 0; } /* * Try reading 93C46 Tekram NVRAM. */ static int sym_read_T93C46_nvram (hcb_p np, Tekram_nvram *nvram) { u_char gpcntl, gpreg; u_char old_gpcntl, old_gpreg; int retv = 1; /* save current state of GPCNTL and GPREG */ old_gpreg = INB (nc_gpreg); old_gpcntl = INB (nc_gpcntl); /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in, 1/2/4 out */ gpreg = old_gpreg & 0xe9; OUTB (nc_gpreg, gpreg); gpcntl = (old_gpcntl & 0xe9) | 0x09; OUTB (nc_gpcntl, gpcntl); /* input all of NVRAM, 64 words */ retv = T93C46_Read_Data(np, (u_short *) nvram, sizeof(*nvram) / sizeof(short), &gpreg); /* return GPIO0/1/2/4 to original states after having accessed NVRAM */ OUTB (nc_gpcntl, old_gpcntl); OUTB (nc_gpreg, old_gpreg); return retv; } /* * Try reading Tekram NVRAM. * Return 0 if OK. */ static int sym_read_Tekram_nvram (hcb_p np, Tekram_nvram *nvram) { u_char *data = (u_char *) nvram; int len = sizeof(*nvram); u_short csum; int x; switch (np->device_id) { case PCI_ID_SYM53C885: case PCI_ID_SYM53C895: case PCI_ID_SYM53C896: x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS, data, len); break; case PCI_ID_SYM53C875: x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS, data, len); if (!x) break; default: x = sym_read_T93C46_nvram(np, nvram); break; } if (x) return 1; /* verify checksum */ for (x = 0, csum = 0; x < len - 1; x += 2) csum += data[x] + (data[x+1] << 8); if (csum != 0x1234) return 1; return 0; } #endif /* SYM_CONF_NVRAM_SUPPORT */ Index: head/sys/dev/trm/trm.c =================================================================== --- head/sys/dev/trm/trm.c (revision 116350) +++ head/sys/dev/trm/trm.c (revision 116351) @@ -1,3721 +1,3702 @@ /* * O.S : FreeBSD CAM * FILE NAME : trm.c * BY : C.L. Huang (ching@tekram.com.tw) * Erich Chen (erich@tekram.com.tw) * Description: Device Driver for Tekram SCSI adapters * DC395U/UW/F ,DC315/U(TRM-S1040) * DC395U2D/U2W(TRM-S2080) * PCI SCSI Bus Master Host Adapter * (SCSI chip set used Tekram ASIC TRM-S1040,TRM-S2080) *(C)Copyright 1995-2001 Tekram Technology Co.,Ltd. */ /* * HISTORY: * * REV# DATE NAME DESCRIPTION * 1.05 05/01/1999 ERICH CHEN First released for 3.x.x (CAM) * 1.06 07/29/1999 ERICH CHEN Modify for NEW PCI * 1.07 12/12/1999 ERICH CHEN Modify for 3.3.x ,DCB no free * 1.08 06/12/2000 ERICH CHEN Modify for 4.x.x * 1.09 11/03/2000 ERICH CHEN Modify for 4.1.R ,new sim * 1.10 10/10/2001 Oscar Feng Fixed CAM rescan hang up bug. * 1.11 10/13/2001 Oscar Feng Fixed wrong Async speed display bug. */ /* * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * Imported into FreeBSD source repository, and updated to compile under * FreeBSD-3.0-DEVELOPMENT, by Stefan Esser , 1996-12-17 */ /* * Updated to compile under FreeBSD 5.0-CURRENT by Olivier Houchard * , 2002-03-04 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #if __FreeBSD_version >= 500000 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define trm_reg_read8(reg) bus_space_read_1(pACB->tag, pACB->bsh, reg) #define trm_reg_read16(reg) bus_space_read_2(pACB->tag, pACB->bsh, reg) #define trm_reg_read32(reg) bus_space_read_4(pACB->tag, pACB->bsh, reg) #define trm_reg_write8(value,reg) bus_space_write_1(pACB->tag, pACB->bsh,\ reg, value) #define trm_reg_write16(value,reg) bus_space_write_2(pACB->tag, pACB->bsh,\ reg, value) #define trm_reg_write32(value,reg) bus_space_write_4(pACB->tag, pACB->bsh,\ reg, value) #define PCI_Vendor_ID_TEKRAM 0x1DE1 #define PCI_Device_ID_TRM_S1040 0x0391 #define PCI_DEVICEID_TRMS1040 0x03911DE1 #define PCI_DEVICEID_TRMS2080 0x03921DE1 #ifdef trm_DEBUG1 #define TRM_DPRINTF(fmt, arg...) printf("trm: " fmt, ##arg) #else #define TRM_DPRINTF(fmt, arg...) {} #endif /* TRM_DEBUG */ static void trm_check_eeprom(PNVRAMTYPE pEEpromBuf,PACB pACB); static void NVRAM_trm_read_all(PNVRAMTYPE pEEpromBuf,PACB pACB); static u_int8_t NVRAM_trm_get_data(PACB pACB, u_int8_t bAddr); static void NVRAM_trm_write_all(PNVRAMTYPE pEEpromBuf,PACB pACB); static void NVRAM_trm_set_data(PACB pACB, u_int8_t bAddr, u_int8_t bData); static void NVRAM_trm_write_cmd(PACB pACB, u_int8_t bCmd, u_int8_t bAddr); static void NVRAM_trm_wait_30us(PACB pACB); static void trm_Interrupt(void *vpACB); static void trm_DataOutPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_DataInPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_CommandPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_StatusPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_MsgOutPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_MsgInPhase0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_DataOutPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_DataInPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_CommandPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_StatusPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_MsgOutPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_MsgInPhase1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_Nop0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_Nop1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status); static void trm_SetXferRate(PACB pACB, PSRB pSRB,PDCB pDCB); static void trm_DataIO_transfer(PACB pACB, PSRB pSRB, u_int16_t ioDir); static void trm_Disconnect(PACB pACB); static void trm_Reselect(PACB pACB); static void trm_SRBdone(PACB pACB, PDCB pDCB, PSRB pSRB); static void trm_DoingSRB_Done(PACB pACB); static void trm_ScsiRstDetect(PACB pACB); static void trm_ResetSCSIBus(PACB pACB); static void trm_RequestSense(PACB pACB, PDCB pDCB, PSRB pSRB); static void trm_EnableMsgOutAbort2(PACB pACB, PSRB pSRB); static void trm_EnableMsgOutAbort1(PACB pACB, PSRB pSRB); static void trm_SendSRB(PACB pACB, PSRB pSRB); static int trm_probe(device_t tag); static int trm_attach(device_t tag); static void trm_reset(PACB pACB); static u_int16_t trm_StartSCSI(PACB pACB, PDCB pDCB, PSRB pSRB); static int trm_initAdapter(PACB pACB, u_int16_t unit); static void trm_initDCB(PACB pACB, PDCB pDCB, u_int16_t unit, u_int32_t i, u_int32_t j); static int trm_initSRB(PACB pACB); static void trm_initACB(PACB pACB, u_int8_t adaptType, u_int16_t unit); /* CAM SIM entry points */ #define ccb_trmsrb_ptr spriv_ptr0 #define ccb_trmacb_ptr spriv_ptr1 static void trm_action(struct cam_sim *psim, union ccb *pccb); static void trm_poll(struct cam_sim *psim); static void * trm_SCSI_phase0[] = { trm_DataOutPhase0, /* phase:0 */ trm_DataInPhase0, /* phase:1 */ trm_CommandPhase0, /* phase:2 */ trm_StatusPhase0, /* phase:3 */ trm_Nop0, /* phase:4 */ trm_Nop1, /* phase:5 */ trm_MsgOutPhase0, /* phase:6 */ trm_MsgInPhase0, /* phase:7 */ }; /* * * stateV = (void *) trm_SCSI_phase1[phase] * */ static void * trm_SCSI_phase1[] = { trm_DataOutPhase1, /* phase:0 */ trm_DataInPhase1, /* phase:1 */ trm_CommandPhase1, /* phase:2 */ trm_StatusPhase1, /* phase:3 */ trm_Nop0, /* phase:4 */ trm_Nop1, /* phase:5 */ trm_MsgOutPhase1, /* phase:6 */ trm_MsgInPhase1, /* phase:7 */ }; NVRAMTYPE trm_eepromBuf[TRM_MAX_ADAPTER_NUM]; /* *Fast20: 000 50ns, 20.0 Mbytes/s * 001 75ns, 13.3 Mbytes/s * 010 100ns, 10.0 Mbytes/s * 011 125ns, 8.0 Mbytes/s * 100 150ns, 6.6 Mbytes/s * 101 175ns, 5.7 Mbytes/s * 110 200ns, 5.0 Mbytes/s * 111 250ns, 4.0 Mbytes/s * *Fast40: 000 25ns, 40.0 Mbytes/s * 001 50ns, 20.0 Mbytes/s * 010 75ns, 13.3 Mbytes/s * 011 100ns, 10.0 Mbytes/s * 100 125ns, 8.0 Mbytes/s * 101 150ns, 6.6 Mbytes/s * 110 175ns, 5.7 Mbytes/s * 111 200ns, 5.0 Mbytes/s */ /* real period: */ u_int8_t dc395x_clock_period[] = { 12,/* 48 ns 20 MB/sec */ 18,/* 72 ns 13.3 MB/sec */ 25,/* 100 ns 10.0 MB/sec */ 31,/* 124 ns 8.0 MB/sec */ 37,/* 148 ns 6.6 MB/sec */ 43,/* 172 ns 5.7 MB/sec */ 50,/* 200 ns 5.0 MB/sec */ 62 /* 248 ns 4.0 MB/sec */ }; u_int8_t dc395u2x_clock_period[]={ 10,/* 25 ns 40.0 MB/sec */ 12,/* 48 ns 20.0 MB/sec */ 18,/* 72 ns 13.3 MB/sec */ 25,/* 100 ns 10.0 MB/sec */ 31,/* 124 ns 8.0 MB/sec */ 37,/* 148 ns 6.6 MB/sec */ 43,/* 172 ns 5.7 MB/sec */ 50,/* 200 ns 5.0 MB/sec */ }; #define dc395x_tinfo_period dc395x_clock_period #define dc395u2x_tinfo_period dc395u2x_clock_period static PSRB trm_GetSRB(PACB pACB) { int intflag; PSRB pSRB; intflag = splcam(); pSRB = pACB->pFreeSRB; if (pSRB) { pACB->pFreeSRB = pSRB->pNextSRB; pSRB->pNextSRB = NULL; } splx(intflag); return (pSRB); } static void trm_RewaitSRB0(PDCB pDCB, PSRB pSRB) { PSRB psrb1; int intflag; intflag = splcam(); if ((psrb1 = pDCB->pWaitingSRB)) { pSRB->pNextSRB = psrb1; pDCB->pWaitingSRB = pSRB; } else { pSRB->pNextSRB = NULL; pDCB->pWaitingSRB = pSRB; pDCB->pWaitingLastSRB = pSRB; } splx(intflag); } static void trm_RewaitSRB(PDCB pDCB, PSRB pSRB) { PSRB psrb1; int intflag; intflag = splcam(); pDCB->GoingSRBCnt--; psrb1 = pDCB->pGoingSRB; if (pSRB == psrb1) /* * if this SRB is GoingSRB * remove this SRB from GoingSRB Q */ pDCB->pGoingSRB = psrb1->pNextSRB; else { /* * if this SRB is not current GoingSRB * remove this SRB from GoingSRB Q */ while (pSRB != psrb1->pNextSRB) psrb1 = psrb1->pNextSRB; psrb1->pNextSRB = pSRB->pNextSRB; if (pSRB == pDCB->pGoingLastSRB) pDCB->pGoingLastSRB = psrb1; } if ((psrb1 = pDCB->pWaitingSRB)) { /* * if WaitingSRB Q is not NULL * Q back this SRB into WaitingSRB */ pSRB->pNextSRB = psrb1; pDCB->pWaitingSRB = pSRB; } else { pSRB->pNextSRB = NULL; pDCB->pWaitingSRB = pSRB; pDCB->pWaitingLastSRB = pSRB; } splx(intflag); } static void trm_DoWaitingSRB(PACB pACB) { int intflag; PDCB ptr, ptr1; PSRB pSRB; intflag = splcam(); if (!(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV))) { ptr = pACB->pDCBRunRobin; if (!ptr) { ptr = pACB->pLinkDCB; pACB->pDCBRunRobin = ptr; } ptr1 = ptr; for (;ptr1 ;) { pACB->pDCBRunRobin = ptr1->pNextDCB; if (!(ptr1->MaxActiveCommandCnt > ptr1->GoingSRBCnt) || !(pSRB = ptr1->pWaitingSRB)) { if (pACB->pDCBRunRobin == ptr) break; ptr1 = ptr1->pNextDCB; } else { if (!trm_StartSCSI(pACB, ptr1, pSRB)) { /* * If trm_StartSCSI return 0 : * current interrupt status is interrupt enable * It's said that SCSI processor is unoccupied */ ptr1->GoingSRBCnt++; if (ptr1->pWaitingLastSRB == pSRB) { ptr1->pWaitingSRB = NULL; ptr1->pWaitingLastSRB = NULL; } else ptr1->pWaitingSRB = pSRB->pNextSRB; pSRB->pNextSRB = NULL; if (ptr1->pGoingSRB) ptr1->pGoingLastSRB->pNextSRB = pSRB; else ptr1->pGoingSRB = pSRB; ptr1->pGoingLastSRB = pSRB; } break; } } } splx(intflag); return; } static void trm_SRBwaiting(PDCB pDCB, PSRB pSRB) { if (pDCB->pWaitingSRB) { pDCB->pWaitingLastSRB->pNextSRB = pSRB; pDCB->pWaitingLastSRB = pSRB; pSRB->pNextSRB = NULL; } else { pDCB->pWaitingSRB = pSRB; pDCB->pWaitingLastSRB = pSRB; } } static u_int32_t trm_get_sense_bufaddr(PACB pACB, PSRB pSRB) { int offset; offset = pSRB->TagNumber; return (pACB->sense_busaddr + (offset * sizeof(struct scsi_sense_data))); } static struct scsi_sense_data * trm_get_sense_buf(PACB pACB, PSRB pSRB) { int offset; offset = pSRB->TagNumber; return (&pACB->sense_buffers[offset]); } static void trm_ExecuteSRB(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { int flags; PACB pACB; PSRB pSRB; union ccb *ccb; u_long totalxferlen=0; flags = splcam(); pSRB = (PSRB)arg; ccb = pSRB->pccb; pACB = (PACB)ccb->ccb_h.ccb_trmacb_ptr; TRM_DPRINTF("trm_ExecuteSRB..........\n"); if (nseg != 0) { PSEG psg; bus_dma_segment_t *end_seg; int op; /* Copy the segments into our SG list */ end_seg = dm_segs + nseg; psg = pSRB->pSRBSGL; while (dm_segs < end_seg) { psg->address = dm_segs->ds_addr; psg->length = (u_long)dm_segs->ds_len; totalxferlen += dm_segs->ds_len; psg++; dm_segs++; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { op = BUS_DMASYNC_PREREAD; } else { op = BUS_DMASYNC_PREWRITE; } bus_dmamap_sync(pACB->buffer_dmat, pSRB->dmamap, op); } pSRB->RetryCnt = 0; pSRB->SRBTotalXferLength = totalxferlen; pSRB->SRBSGCount = nseg; pSRB->SRBSGIndex = 0; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pSRB->MsgCnt = 0; pSRB->SRBStatus = 0; pSRB->SRBFlag = 0; pSRB->SRBState = 0; pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(pACB->buffer_dmat, pSRB->dmamap); pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB; xpt_done(ccb); splx(flags); return; } ccb->ccb_h.status |= CAM_SIM_QUEUED; #if 0 /* XXX Need a timeout handler */ ccb->ccb_h.timeout_ch = timeout(trmtimeout, (caddr_t)srb, (ccb->ccb_h.timeout * hz) / 1000); #endif trm_SendSRB(pACB, pSRB); splx(flags); return; } static void trm_SendSRB(PACB pACB, PSRB pSRB) { PDCB pDCB; pDCB = pSRB->pSRBDCB; if (!(pDCB->MaxActiveCommandCnt > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) || (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV))) { TRM_DPRINTF("pDCB->MaxCommand=%d \n",pDCB->MaxActiveCommandCnt); TRM_DPRINTF("pDCB->GoingSRBCnt=%d \n",pDCB->GoingSRBCnt); TRM_DPRINTF("pACB->pActiveDCB=%8x \n",(u_int)pACB->pActiveDCB); TRM_DPRINTF("pACB->ACBFlag=%x \n",pACB->ACBFlag); trm_SRBwaiting(pDCB, pSRB); goto SND_EXIT; } if (pDCB->pWaitingSRB) { trm_SRBwaiting(pDCB, pSRB); pSRB = pDCB->pWaitingSRB; pDCB->pWaitingSRB = pSRB->pNextSRB; pSRB->pNextSRB = NULL; } if (!trm_StartSCSI(pACB, pDCB, pSRB)) { /* * If trm_StartSCSI return 0 : * current interrupt status is interrupt enable * It's said that SCSI processor is unoccupied */ pDCB->GoingSRBCnt++; /* stack waiting SRB*/ if (pDCB->pGoingSRB) { pDCB->pGoingLastSRB->pNextSRB = pSRB; pDCB->pGoingLastSRB = pSRB; } else { pDCB->pGoingSRB = pSRB; pDCB->pGoingLastSRB = pSRB; } } else { /* * If trm_StartSCSI return 1 : * current interrupt status is interrupt disreenable * It's said that SCSI processor has more one SRB need to do */ trm_RewaitSRB0(pDCB, pSRB); } SND_EXIT: return; } static void trm_action(struct cam_sim *psim, union ccb *pccb) { PACB pACB; int actionflags; u_int target_id,target_lun; CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("trm_action\n")); actionflags = splcam(); pACB = (PACB) cam_sim_softc(psim); target_id = pccb->ccb_h.target_id; target_lun = pccb->ccb_h.target_lun; switch (pccb->ccb_h.func_code) { case XPT_NOOP: TRM_DPRINTF(" XPT_NOOP \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Execute the requested I/O operation */ case XPT_SCSI_IO: { PDCB pDCB = NULL; PSRB pSRB; struct ccb_scsiio *pcsio; pcsio = &pccb->csio; TRM_DPRINTF(" XPT_SCSI_IO \n"); TRM_DPRINTF("trm: target_id= %d target_lun= %d \n" ,target_id, target_lun); TRM_DPRINTF( "pACB->scan_devices[target_id][target_lun]= %d \n" ,pACB->scan_devices[target_id][target_lun]); if((pccb->ccb_h.status & CAM_STATUS_MASK) !=CAM_REQ_INPROG) { xpt_done(pccb); splx(actionflags); return; } pDCB = &pACB->DCBarray[target_id][target_lun]; if(!(pDCB->DCBstatus & DS_IN_QUEUE)) { pACB->scan_devices[target_id][target_lun] = 1; trm_initDCB(pACB, pDCB, pACB->AdapterUnit, target_id, target_lun); } /* * Assign an SRB and connect it with this ccb. */ pSRB = trm_GetSRB(pACB); if (!pSRB) { /* Freeze SIMQ */ pccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(pccb); splx(actionflags); return; } pSRB->pSRBDCB = pDCB; pccb->ccb_h.ccb_trmsrb_ptr = pSRB; pccb->ccb_h.ccb_trmacb_ptr = pACB; pSRB->pccb = pccb; pSRB->ScsiCmdLen = pcsio->cdb_len; /* * move layer of CAM command block to layer of SCSI * Request Block for SCSI processor command doing */ if((pccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if((pccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(pcsio->cdb_io.cdb_ptr,pSRB->CmdBlock ,pcsio->cdb_len); } else { pccb->ccb_h.status = CAM_REQ_INVALID; pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB= pSRB; xpt_done(pccb); splx(actionflags); return; } } else bcopy(pcsio->cdb_io.cdb_bytes, pSRB->CmdBlock, pcsio->cdb_len); if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { if ((pccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { int vmflags; int error; vmflags = splsoftvm(); error = bus_dmamap_load( pACB->buffer_dmat, pSRB->dmamap, pcsio->data_ptr, pcsio->dxfer_len, trm_ExecuteSRB, pSRB, 0); if (error == EINPROGRESS) { xpt_freeze_simq( pACB->psim, 1); pccb->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(vmflags); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)pcsio->data_ptr; seg.ds_len = pcsio->dxfer_len; trm_ExecuteSRB(pSRB, &seg, 1, 0); } } else { /* CAM_SCATTER_VALID */ struct bus_dma_segment *segs; if ((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB; pccb->ccb_h.status = CAM_PROVIDE_FAIL; xpt_done(pccb); splx(actionflags); return; } /* cam SG list is physical, * cam data is virtual */ segs = (struct bus_dma_segment *) pcsio->data_ptr; trm_ExecuteSRB(pSRB, segs, pcsio->sglist_cnt, 1); } /* CAM_SCATTER_VALID */ } else trm_ExecuteSRB(pSRB, NULL, 0, 0); } break; case XPT_GDEV_TYPE: TRM_DPRINTF(" XPT_GDEV_TYPE \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; case XPT_GDEVLIST: TRM_DPRINTF(" XPT_GDEVLIST \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Path routing inquiry * Path Inquiry CCB */ case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &pccb->cpi; TRM_DPRINTF(" XPT_PATH_INQ \n"); cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 15 ; cpi->max_lun = pACB->max_lun; /* 7 or 0 */ cpi->initiator_id = pACB->AdaptSCSIID; cpi->bus_id = cam_sim_bus(psim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Tekram_TRM", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(psim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); } break; /* * Release a frozen SIM queue * Release SIM Queue */ case XPT_REL_SIMQ: TRM_DPRINTF(" XPT_REL_SIMQ \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Set Asynchronous Callback Parameters * Set Asynchronous Callback CCB */ case XPT_SASYNC_CB: TRM_DPRINTF(" XPT_SASYNC_CB \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Set device type information * Set Device Type CCB */ case XPT_SDEV_TYPE: TRM_DPRINTF(" XPT_SDEV_TYPE \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * (Re)Scan the SCSI Bus * Rescan the given bus, or bus/target/lun */ case XPT_SCAN_BUS: TRM_DPRINTF(" XPT_SCAN_BUS \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Get EDT entries matching the given pattern */ case XPT_DEV_MATCH: TRM_DPRINTF(" XPT_DEV_MATCH \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Turn on debugging for a bus, target or lun */ case XPT_DEBUG: TRM_DPRINTF(" XPT_DEBUG \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * XPT_ABORT = 0x10, Abort the specified CCB * Abort XPT request CCB */ case XPT_ABORT: TRM_DPRINTF(" XPT_ABORT \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Reset the specified SCSI bus * Reset SCSI Bus CCB */ case XPT_RESET_BUS: { int i; TRM_DPRINTF(" XPT_RESET_BUS \n"); trm_reset(pACB); pACB->ACBFlag=0; for (i=0; i<500; i++) DELAY(1000); pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); } break; /* * Bus Device Reset the specified SCSI device * Reset SCSI Device CCB */ case XPT_RESET_DEV: /* * Don't (yet?) support vendor * specific commands. */ TRM_DPRINTF(" XPT_RESET_DEV \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Terminate the I/O process * Terminate I/O Process Request CCB */ case XPT_TERM_IO: TRM_DPRINTF(" XPT_TERM_IO \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Scan Logical Unit */ case XPT_SCAN_LUN: TRM_DPRINTF(" XPT_SCAN_LUN \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Get/Set transfer rate/width/disconnection/tag queueing * settings * (GET) default/user transfer settings for the target */ case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; int intflag; struct trm_transinfo *tinfo; PDCB pDCB; TRM_DPRINTF(" XPT_GET_TRAN_SETTINGS \n"); cts = &pccb->cts; pDCB = &pACB->DCBarray[target_id][target_lun]; intflag = splcam(); /* * disable interrupt */ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { /* current transfer settings */ if (pDCB->tinfo.disc_tag & TRM_CUR_DISCENB) cts->flags = CCB_TRANS_DISC_ENB; else cts->flags = 0;/* no tag & disconnect */ if (pDCB->tinfo.disc_tag & TRM_CUR_TAGENB) cts->flags |= CCB_TRANS_TAG_ENB; tinfo = &pDCB->tinfo.current; TRM_DPRINTF("CURRENT: cts->flags= %2x \n", cts->flags); } else { /* default(user) transfer settings */ if (pDCB->tinfo.disc_tag & TRM_USR_DISCENB) cts->flags = CCB_TRANS_DISC_ENB; else cts->flags = 0; if (pDCB->tinfo.disc_tag & TRM_USR_TAGENB) cts->flags |= CCB_TRANS_TAG_ENB; tinfo = &pDCB->tinfo.user; TRM_DPRINTF("USER: cts->flags= %2x \n", cts->flags); } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; cts->bus_width = tinfo->width; TRM_DPRINTF("pDCB->SyncPeriod: %d \n", pDCB->SyncPeriod); TRM_DPRINTF("period: %d \n", tinfo->period); TRM_DPRINTF("offset: %d \n", tinfo->offset); TRM_DPRINTF("width: %d \n", tinfo->width); splx(intflag); cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); } break; /* * Get/Set transfer rate/width/disconnection/tag queueing * settings * (Set) transfer rate/width negotiation settings */ case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; u_int update_type; int intflag; PDCB pDCB; TRM_DPRINTF(" XPT_SET_TRAN_SETTINGS \n"); cts = &pccb->cts; update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) update_type |= TRM_TRANS_GOAL; if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) update_type |= TRM_TRANS_USER; intflag = splcam(); pDCB = &pACB->DCBarray[target_id][target_lun]; if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { /*ccb disc enables */ if (update_type & TRM_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) pDCB->tinfo.disc_tag |= TRM_CUR_DISCENB; else pDCB->tinfo.disc_tag &= ~TRM_CUR_DISCENB; } if (update_type & TRM_TRANS_USER) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) pDCB->tinfo.disc_tag |= TRM_USR_DISCENB; else pDCB->tinfo.disc_tag &= ~TRM_USR_DISCENB; } } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { /* if ccb tag q active */ if (update_type & TRM_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) pDCB->tinfo.disc_tag |= TRM_CUR_TAGENB; else pDCB->tinfo.disc_tag &= ~TRM_CUR_TAGENB; } if (update_type & TRM_TRANS_USER) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) pDCB->tinfo.disc_tag |= TRM_USR_TAGENB; else pDCB->tinfo.disc_tag &= ~TRM_USR_TAGENB; } } /* Minimum sync period factor */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { /* if ccb sync active */ /* TRM-S1040 MinSyncPeriod = 4 clocks/byte */ if ((cts->sync_period != 0) && (cts->sync_period < 125)) cts->sync_period = 125; /* 1/(125*4) minsync 2 MByte/sec */ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { if (cts->sync_offset == 0) cts->sync_period = 0; /* TRM-S1040 MaxSyncOffset = 15 bytes*/ if (cts->sync_offset > 15) cts->sync_offset = 15; } } if ((update_type & TRM_TRANS_USER) != 0) { pDCB->tinfo.user.period = cts->sync_period; pDCB->tinfo.user.offset = cts->sync_offset; pDCB->tinfo.user.width = cts->bus_width; } if ((update_type & TRM_TRANS_GOAL) != 0) { pDCB->tinfo.goal.period = cts->sync_period; pDCB->tinfo.goal.offset = cts->sync_offset; pDCB->tinfo.goal.width = cts->bus_width; } splx(intflag); pccb->ccb_h.status = CAM_REQ_CMP; xpt_done(pccb); break; } /* * Calculate the geometry parameters for a device give * the sector size and volume size. */ - case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended; - + case XPT_CALC_GEOMETRY: TRM_DPRINTF(" XPT_CALC_GEOMETRY \n"); - ccg = &pccb->ccg; - size_mb = ccg->volume_size / - ((1024L * 1024L) / ccg->block_size); - extended = 1; - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - pccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&pccb->ccg, /*extended*/1); xpt_done(pccb); - } break; case XPT_ENG_INQ: TRM_DPRINTF(" XPT_ENG_INQ \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * HBA execute engine request * This structure must match SCSIIO size */ case XPT_ENG_EXEC: TRM_DPRINTF(" XPT_ENG_EXEC \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * XPT_EN_LUN = 0x30, Enable LUN as a target * Target mode structures. */ case XPT_EN_LUN: /* * Don't (yet?) support vendor * specific commands. */ TRM_DPRINTF(" XPT_EN_LUN \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Execute target I/O request */ case XPT_TARGET_IO: /* * Don't (yet?) support vendor * specific commands. */ TRM_DPRINTF(" XPT_TARGET_IO \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Accept Host Target Mode CDB */ case XPT_ACCEPT_TARGET_IO: /* * Don't (yet?) support vendor * specific commands. */ TRM_DPRINTF(" XPT_ACCEPT_TARGET_IO \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Continue Host Target I/O Connection */ case XPT_CONT_TARGET_IO: /* * Don't (yet?) support vendor * specific commands. */ TRM_DPRINTF(" XPT_CONT_TARGET_IO \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Notify Host Target driver of event */ case XPT_IMMED_NOTIFY: TRM_DPRINTF(" XPT_IMMED_NOTIFY \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * Acknowledgement of event */ case XPT_NOTIFY_ACK: TRM_DPRINTF(" XPT_NOTIFY_ACK \n"); pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; /* * XPT_VUNIQUE = 0x80 */ case XPT_VUNIQUE: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; default: pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; } splx(actionflags); } static void trm_poll(struct cam_sim *psim) { } static void trm_ResetDevParam(PACB pACB) { PDCB pDCB, pdcb; PNVRAMTYPE pEEpromBuf; u_int8_t PeriodIndex; pDCB = pACB->pLinkDCB; if (pDCB == NULL) return; pdcb = pDCB; do { pDCB->SyncMode &= ~(SYNC_NEGO_DONE+ WIDE_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; pEEpromBuf = &trm_eepromBuf[pACB->AdapterUnit]; pDCB->DevMode = pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0; pDCB->AdpMode = pEEpromBuf->NvramChannelCfg; PeriodIndex = pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarPeriod & 0x07; if (pACB->AdaptType == 1) /* is U2? */ pDCB->MaxNegoPeriod = dc395u2x_clock_period[PeriodIndex]; else pDCB->MaxNegoPeriod = dc395x_clock_period[PeriodIndex]; if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) && (pACB->Config & HCC_WIDE_CARD)) pDCB->SyncMode |= WIDE_NEGO_ENABLE; pDCB = pDCB->pNextDCB; } while (pdcb != pDCB); } static void trm_RecoverSRB(PACB pACB) { PDCB pDCB, pdcb; PSRB psrb, psrb2; u_int16_t cnt, i; pDCB = pACB->pLinkDCB; if (pDCB == NULL) return; pdcb = pDCB; do { cnt = pdcb->GoingSRBCnt; psrb = pdcb->pGoingSRB; for (i = 0; i < cnt; i++) { psrb2 = psrb; psrb = psrb->pNextSRB; if (pdcb->pWaitingSRB) { psrb2->pNextSRB = pdcb->pWaitingSRB; pdcb->pWaitingSRB = psrb2; } else { pdcb->pWaitingSRB = psrb2; pdcb->pWaitingLastSRB = psrb2; psrb2->pNextSRB = NULL; } } pdcb->GoingSRBCnt = 0; pdcb->pGoingSRB = NULL; pdcb = pdcb->pNextDCB; } while (pdcb != pDCB); } static void trm_reset(PACB pACB) { int intflag; u_int16_t i; TRM_DPRINTF("trm: RESET"); intflag = splcam(); trm_reg_write8(0x00, TRMREG_DMA_INTEN); trm_reg_write8(0x00, TRMREG_SCSI_INTEN); trm_ResetSCSIBus(pACB); for (i = 0; i < 500; i++) DELAY(1000); trm_reg_write8(0x7F, TRMREG_SCSI_INTEN); /* Enable DMA interrupt */ trm_reg_write8(EN_SCSIINTR, TRMREG_DMA_INTEN); /* Clear DMA FIFO */ trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL); /* Clear SCSI FIFO */ trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL); trm_ResetDevParam(pACB); trm_DoingSRB_Done(pACB); pACB->pActiveDCB = NULL; pACB->ACBFlag = 0;/* RESET_DETECT, RESET_DONE ,RESET_DEV */ trm_DoWaitingSRB(pACB); /* Tell the XPT layer that a bus reset occured */ if (pACB->ppath != NULL) xpt_async(AC_BUS_RESET, pACB->ppath, NULL); splx(intflag); return; } static u_int16_t trm_StartSCSI(PACB pACB, PDCB pDCB, PSRB pSRB) { u_int16_t return_code; u_int8_t scsicommand, i,command,identify_message; u_int8_t * ptr; union ccb *pccb; struct ccb_scsiio *pcsio; pccb = pSRB->pccb; pcsio = &pccb->csio; trm_reg_write8(pACB->AdaptSCSIID, TRMREG_SCSI_HOSTID); trm_reg_write8(pDCB->TargetID, TRMREG_SCSI_TARGETID); trm_reg_write8(pDCB->SyncPeriod, TRMREG_SCSI_SYNC); trm_reg_write8(pDCB->SyncOffset, TRMREG_SCSI_OFFSET); pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */ /* Flush FIFO */ trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL); identify_message = pDCB->IdentifyMsg; if ((pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE)) { if (((pDCB->SyncMode & WIDE_NEGO_ENABLE) && !(pDCB->SyncMode & WIDE_NEGO_DONE)) || ((pDCB->SyncMode & SYNC_NEGO_ENABLE) && !(pDCB->SyncMode & SYNC_NEGO_DONE))) { if (!(pDCB->IdentifyMsg & 7) || (pSRB->CmdBlock[0] != INQUIRY)) { scsicommand = SCMD_SEL_ATNSTOP; pSRB->SRBState = SRB_MSGOUT; goto polling; } } /* * Send identify message */ trm_reg_write8((identify_message & 0xBF) ,TRMREG_SCSI_FIFO); scsicommand = SCMD_SEL_ATN; pSRB->SRBState = SRB_START_; } else { /* not inquiry,request sense,auto request sense */ /* * Send identify message */ trm_reg_write8(identify_message,TRMREG_SCSI_FIFO); scsicommand = SCMD_SEL_ATN; pSRB->SRBState = SRB_START_; if (pDCB->SyncMode & EN_TAG_QUEUING) { /* Send Tag message */ trm_reg_write8(MSG_SIMPLE_QTAG, TRMREG_SCSI_FIFO); trm_reg_write8(pSRB->TagNumber, TRMREG_SCSI_FIFO); scsicommand = SCMD_SEL_ATN3; } } polling: /* * Send CDB ..command block ......... */ if (pSRB->SRBFlag & AUTO_REQSENSE) { trm_reg_write8(REQUEST_SENSE, TRMREG_SCSI_FIFO); trm_reg_write8((pDCB->IdentifyMsg << 5), TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); trm_reg_write8(pcsio->sense_len, TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); } else { ptr = (u_int8_t *) pSRB->CmdBlock; for (i = 0; i < pSRB->ScsiCmdLen ; i++) { command = *ptr++; trm_reg_write8(command,TRMREG_SCSI_FIFO); } } if (trm_reg_read16(TRMREG_SCSI_STATUS) & SCSIINTERRUPT) { /* * If trm_StartSCSI return 1 : * current interrupt status is interrupt disreenable * It's said that SCSI processor has more one SRB need to do, * SCSI processor has been occupied by one SRB. */ pSRB->SRBState = SRB_READY; return_code = 1; } else { /* * If trm_StartSCSI return 0 : * current interrupt status is interrupt enable * It's said that SCSI processor is unoccupied */ pSRB->ScsiPhase = SCSI_NOP1; /* SCSI bus free Phase */ pACB->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB; return_code = 0; trm_reg_write16(DO_DATALATCH | DO_HWRESELECT, TRMREG_SCSI_CONTROL);/* it's important for atn stop*/ /* * SCSI cammand */ trm_reg_write8(scsicommand,TRMREG_SCSI_COMMAND); } return (return_code); } static void trm_Interrupt(vpACB) void *vpACB; { PACB pACB; PDCB pDCB; PSRB pSRB; u_int16_t phase; void (*stateV)(PACB, PSRB, u_int16_t *); u_int16_t scsi_status=0; u_int8_t scsi_intstatus; pACB = vpACB; scsi_status = trm_reg_read16(TRMREG_SCSI_STATUS); if (!(scsi_status & SCSIINTERRUPT)) { TRM_DPRINTF("trm_Interrupt: TRMREG_SCSI_STATUS scsi_status = NULL ,return......"); return; } TRM_DPRINTF("scsi_status=%2x,",scsi_status); scsi_intstatus = trm_reg_read8(TRMREG_SCSI_INTSTATUS); TRM_DPRINTF("scsi_intstatus=%2x,",scsi_intstatus); if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) { trm_Disconnect(pACB); return; } if (scsi_intstatus & INT_RESELECTED) { trm_Reselect(pACB); return; } if (scsi_intstatus & INT_SCSIRESET) { trm_ScsiRstDetect(pACB); return; } if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) { pDCB = pACB->pActiveDCB; pSRB = pDCB->pActiveSRB; if (pDCB) { if (pDCB->DCBFlag & ABORT_DEV_) trm_EnableMsgOutAbort1(pACB, pSRB); } phase = (u_int16_t) pSRB->ScsiPhase; /* phase: */ stateV = (void *) trm_SCSI_phase0[phase]; stateV(pACB, pSRB, &scsi_status); pSRB->ScsiPhase = scsi_status & PHASEMASK; /* phase:0,1,2,3,4,5,6,7 */ phase = (u_int16_t) scsi_status & PHASEMASK; stateV = (void *) trm_SCSI_phase1[phase]; stateV(pACB, pSRB, &scsi_status); } } static void trm_MsgOutPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { if (pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT)) *pscsi_status = PH_BUS_FREE; /*.. initial phase*/ } static void trm_MsgOutPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { u_int8_t bval; u_int16_t i, cnt; u_int8_t * ptr; PDCB pDCB; trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL); pDCB = pACB->pActiveDCB; if (!(pSRB->SRBState & SRB_MSGOUT)) { cnt = pSRB->MsgCnt; if (cnt) { ptr = (u_int8_t *) pSRB->MsgOutBuf; for (i = 0; i < cnt; i++) { trm_reg_write8(*ptr, TRMREG_SCSI_FIFO); ptr++; } pSRB->MsgCnt = 0; if ((pDCB->DCBFlag & ABORT_DEV_) && (pSRB->MsgOutBuf[0] == MSG_ABORT)) { pSRB->SRBState = SRB_ABORT_SENT; } } else { bval = MSG_ABORT; if ((pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE)) { if (pDCB->SyncMode & SYNC_NEGO_ENABLE) { goto mop1; } } trm_reg_write8(bval, TRMREG_SCSI_FIFO); } } else { mop1: /* message out phase */ if (!(pSRB->SRBState & SRB_DO_WIDE_NEGO) && (pDCB->SyncMode & WIDE_NEGO_ENABLE)) { /* * WIDE DATA TRANSFER REQUEST code (03h) */ pDCB->SyncMode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP); trm_reg_write8((pDCB->IdentifyMsg & 0xBF), TRMREG_SCSI_FIFO); trm_reg_write8(MSG_EXTENDED,TRMREG_SCSI_FIFO); /* (01h) */ trm_reg_write8(2,TRMREG_SCSI_FIFO); /* Message length (02h) */ trm_reg_write8(3,TRMREG_SCSI_FIFO); /* wide data xfer (03h) */ trm_reg_write8(1,TRMREG_SCSI_FIFO); /* width:0(8bit),1(16bit),2(32bit) */ pSRB->SRBState |= SRB_DO_WIDE_NEGO; } else if (!(pSRB->SRBState & SRB_DO_SYNC_NEGO) && (pDCB->SyncMode & SYNC_NEGO_ENABLE)) { /* * SYNCHRONOUS DATA TRANSFER REQUEST code (01h) */ if (!(pDCB->SyncMode & WIDE_NEGO_DONE)) trm_reg_write8((pDCB->IdentifyMsg & 0xBF), TRMREG_SCSI_FIFO); trm_reg_write8(MSG_EXTENDED,TRMREG_SCSI_FIFO); /* (01h) */ trm_reg_write8(3,TRMREG_SCSI_FIFO); /* Message length (03h) */ trm_reg_write8(1,TRMREG_SCSI_FIFO); /* SYNCHRONOUS DATA TRANSFER REQUEST code (01h) */ trm_reg_write8(pDCB->MaxNegoPeriod,TRMREG_SCSI_FIFO); /* Transfer peeriod factor */ trm_reg_write8((pACB->AdaptType == 1) ? 31 : 15, TRMREG_SCSI_FIFO); /* REQ/ACK offset */ pSRB->SRBState |= SRB_DO_SYNC_NEGO; } } trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_FIFO_OUT, TRMREG_SCSI_COMMAND); } static void trm_CommandPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { } static void trm_CommandPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { PDCB pDCB; u_int8_t * ptr; u_int16_t i, cnt; union ccb *pccb; struct ccb_scsiio *pcsio; pccb = pSRB->pccb; pcsio = &pccb->csio; trm_reg_write16(DO_CLRATN | DO_CLRFIFO , TRMREG_SCSI_CONTROL); if (!(pSRB->SRBFlag & AUTO_REQSENSE)) { cnt = (u_int16_t) pSRB->ScsiCmdLen; ptr = (u_int8_t *) pSRB->CmdBlock; for (i = 0; i < cnt; i++) { trm_reg_write8(*ptr, TRMREG_SCSI_FIFO); ptr++; } } else { trm_reg_write8(REQUEST_SENSE, TRMREG_SCSI_FIFO); pDCB = pACB->pActiveDCB; /* target id */ trm_reg_write8((pDCB->IdentifyMsg << 5), TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); /* sizeof(struct scsi_sense_data) */ trm_reg_write8(pcsio->sense_len, TRMREG_SCSI_FIFO); trm_reg_write8(0, TRMREG_SCSI_FIFO); } pSRB->SRBState = SRB_COMMAND; trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop*/ /* * SCSI cammand */ trm_reg_write8(SCMD_FIFO_OUT, TRMREG_SCSI_COMMAND); } static void trm_DataOutPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { PDCB pDCB; u_int8_t TempDMAstatus,SGIndexTemp; u_int16_t scsi_status; PSEG pseg; u_long TempSRBXferredLength,dLeftCounter=0; pDCB = pSRB->pSRBDCB; scsi_status = *pscsi_status; if (!(pSRB->SRBState & SRB_XFERPAD)) { if (scsi_status & PARITYERROR) pSRB->SRBStatus |= PARITY_ERROR; if (!(scsi_status & SCSIXFERDONE)) { /* * when data transfer from DMA FIFO to SCSI FIFO * if there was some data left in SCSI FIFO */ dLeftCounter = (u_long) (trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x3F); if (pDCB->SyncPeriod & WIDE_SYNC) { /* * if WIDE scsi SCSI FIFOCNT unit is word * so need to * 2 */ dLeftCounter <<= 1; } } /* * caculate all the residue data that not yet tranfered * SCSI transfer counter + left in SCSI FIFO data * * .....TRM_SCSI_COUNTER (24bits) * The counter always decrement by one for every SCSI byte *transfer. * .....TRM_SCSI_FIFOCNT (5bits) * The counter is SCSI FIFO offset counter */ dLeftCounter += trm_reg_read32(TRMREG_SCSI_COUNTER); if (dLeftCounter == 1) { dLeftCounter = 0; trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL); } if ((dLeftCounter == 0) || (scsi_status & SCSIXFERCNT_2_ZERO)) { TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS); while (!(TempDMAstatus & DMAXFERCOMP)) { TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS); } pSRB->SRBTotalXferLength = 0; } else { /* Update SG list */ /* * if transfer not yet complete * there were some data residue in SCSI FIFO or * SCSI transfer counter not empty */ if (pSRB->SRBTotalXferLength != dLeftCounter) { /* * data that had transferred length */ TempSRBXferredLength = pSRB->SRBTotalXferLength - dLeftCounter; /* * next time to be transferred length */ pSRB->SRBTotalXferLength = dLeftCounter; /* * parsing from last time disconnect SRBSGIndex */ pseg = pSRB->pSRBSGL + pSRB->SRBSGIndex; for (SGIndexTemp = pSRB->SRBSGIndex; SGIndexTemp < pSRB->SRBSGCount; SGIndexTemp++) { /* * find last time which SG transfer be * disconnect */ if (TempSRBXferredLength >= pseg->length) TempSRBXferredLength -= pseg->length; else { /* * update last time disconnected SG * list */ pseg->length -= TempSRBXferredLength; /* residue data length */ pseg->address += TempSRBXferredLength; /* residue data pointer */ pSRB->SRBSGIndex = SGIndexTemp; break; } pseg++; } } } } trm_reg_write8(STOPDMAXFER ,TRMREG_DMA_CONTROL); } static void trm_DataOutPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { u_int16_t ioDir; /* * do prepare befor transfer when data out phase */ ioDir = XFERDATAOUT; trm_DataIO_transfer(pACB, pSRB, ioDir); } static void trm_DataInPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { u_int8_t TempDMAstatus, SGIndexTemp; u_int16_t scsi_status; PSEG pseg; u_long TempSRBXferredLength,dLeftCounter = 0; scsi_status = *pscsi_status; if (!(pSRB->SRBState & SRB_XFERPAD)) { if (scsi_status & PARITYERROR) pSRB->SRBStatus |= PARITY_ERROR; dLeftCounter += trm_reg_read32(TRMREG_SCSI_COUNTER); if ((dLeftCounter == 0) || (scsi_status & SCSIXFERCNT_2_ZERO)) { TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS); while (!(TempDMAstatus & DMAXFERCOMP)) TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS); pSRB->SRBTotalXferLength = 0; } else { /* * parsing the case: * when a transfer not yet complete * but be disconnected by uper layer * if transfer not yet complete * there were some data residue in SCSI FIFO or * SCSI transfer counter not empty */ if (pSRB->SRBTotalXferLength != dLeftCounter) { /* * data that had transferred length */ TempSRBXferredLength = pSRB->SRBTotalXferLength - dLeftCounter; /* * next time to be transferred length */ pSRB->SRBTotalXferLength = dLeftCounter; /* * parsing from last time disconnect SRBSGIndex */ pseg = pSRB->pSRBSGL + pSRB->SRBSGIndex; for (SGIndexTemp = pSRB->SRBSGIndex; SGIndexTemp < pSRB->SRBSGCount; SGIndexTemp++) { /* * find last time which SG transfer be disconnect */ if (TempSRBXferredLength >= pseg->length) TempSRBXferredLength -= pseg->length; else { /* * update last time disconnected SG list */ pseg->length -= TempSRBXferredLength; /* residue data length */ pseg->address += TempSRBXferredLength; /* residue data pointer */ pSRB->SRBSGIndex = SGIndexTemp; break; } pseg++; } } } } } static void trm_DataInPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { u_int16_t ioDir; /* * do prepare befor transfer when data in phase */ ioDir = XFERDATAIN; trm_DataIO_transfer(pACB, pSRB, ioDir); } static void trm_DataIO_transfer(PACB pACB, PSRB pSRB, u_int16_t ioDir) { u_int8_t bval; PDCB pDCB; pDCB = pSRB->pSRBDCB; if (pSRB->SRBSGIndex < pSRB->SRBSGCount) { if (pSRB->SRBTotalXferLength != 0) { /* * load what physical address of Scatter/Gather list table want to be transfer */ TRM_DPRINTF(" SG->address=%8x \n",pSRB->pSRBSGL->address); TRM_DPRINTF(" SG->length=%8x \n",pSRB->pSRBSGL->length); TRM_DPRINTF(" pDCB->SyncPeriod=%x \n",pDCB->SyncPeriod); TRM_DPRINTF(" pSRB->pSRBSGL=%8x \n",(unsigned int)pSRB->pSRBSGL); TRM_DPRINTF(" pSRB->SRBSGPhyAddr=%8x \n",pSRB->SRBSGPhyAddr); TRM_DPRINTF(" pSRB->SRBSGIndex=%d \n",pSRB->SRBSGIndex); TRM_DPRINTF(" pSRB->SRBSGCount=%d \n",pSRB->SRBSGCount); TRM_DPRINTF(" pSRB->SRBTotalXferLength=%d \n",pSRB->SRBTotalXferLength); pSRB->SRBState = SRB_DATA_XFER; trm_reg_write32(0, TRMREG_DMA_XHIGHADDR); trm_reg_write32( (pSRB->SRBSGPhyAddr + ((u_long)pSRB->SRBSGIndex << 3)), TRMREG_DMA_XLOWADDR); /* * load how many bytes in the Scatter/Gather * list table */ trm_reg_write32( ((u_long)(pSRB->SRBSGCount - pSRB->SRBSGIndex) << 3), TRMREG_DMA_XCNT); /* * load total transfer length (24bits) max value * 16Mbyte */ trm_reg_write32(pSRB->SRBTotalXferLength, TRMREG_SCSI_COUNTER); /* Start DMA transfer */ trm_reg_write16(ioDir, TRMREG_DMA_COMMAND); /* Start SCSI transfer */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ bval = (ioDir == XFERDATAOUT) ? SCMD_DMA_OUT : SCMD_DMA_IN; trm_reg_write8(bval, TRMREG_SCSI_COMMAND); } else { /* xfer pad */ if (pSRB->SRBSGCount) { pSRB->AdaptStatus = H_OVER_UNDER_RUN; pSRB->SRBStatus |= OVER_RUN; } if (pDCB->SyncPeriod & WIDE_SYNC) trm_reg_write32(2,TRMREG_SCSI_COUNTER); else trm_reg_write32(1,TRMREG_SCSI_COUNTER); if (ioDir == XFERDATAOUT) trm_reg_write16(0, TRMREG_SCSI_FIFO); else trm_reg_read16(TRMREG_SCSI_FIFO); pSRB->SRBState |= SRB_XFERPAD; trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ bval = (ioDir == XFERDATAOUT) ? SCMD_FIFO_OUT : SCMD_FIFO_IN; trm_reg_write8(bval, TRMREG_SCSI_COMMAND); } } } static void trm_StatusPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { pSRB->TargetStatus = trm_reg_read8(TRMREG_SCSI_FIFO); pSRB->SRBState = SRB_COMPLETED; *pscsi_status = PH_BUS_FREE; /*.. initial phase*/ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); } static void trm_StatusPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { if (trm_reg_read16(TRMREG_DMA_COMMAND) & 0x0001) { if (!(trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x40)) trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL); if (!(trm_reg_read16(TRMREG_DMA_FIFOCNT) & 0x8000)) trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL); } else { if (!(trm_reg_read16(TRMREG_DMA_FIFOCNT) & 0x8000)) trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL); if (!(trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x40)) trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL); } pSRB->SRBState = SRB_STATUS; trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_COMP, TRMREG_SCSI_COMMAND); } /* *scsiiom * trm_MsgInPhase0: one of trm_SCSI_phase0[] vectors * stateV = (void *) trm_SCSI_phase0[phase] * if phase =7 * extended message codes: * * code description * * 02h Reserved * 00h MODIFY DATA POINTER * 01h SYNCHRONOUS DATA TRANSFER REQUEST * 03h WIDE DATA TRANSFER REQUEST * 04h - 7Fh Reserved * 80h - FFh Vendor specific * */ static void trm_MsgInPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { u_int8_t message_in_code,bIndex,message_in_tag_id; PDCB pDCB; PSRB pSRBTemp; pDCB = pACB->pActiveDCB; message_in_code = trm_reg_read8(TRMREG_SCSI_FIFO); if (!(pSRB->SRBState & SRB_EXTEND_MSGIN)) { if (message_in_code == MSG_DISCONNECT) { pSRB->SRBState = SRB_DISCONNECT; *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if (message_in_code == MSG_SAVE_PTR) { *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if ((message_in_code == MSG_EXTENDED) || ((message_in_code >= MSG_SIMPLE_QTAG) && (message_in_code <= MSG_ORDER_QTAG))) { pSRB->SRBState |= SRB_EXTEND_MSGIN; pSRB->MsgInBuf[0] = message_in_code; /* extended message (01h) */ pSRB->MsgCnt = 1; pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; /* extended message length (n) */ *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if (message_in_code == MSG_REJECT_) { /* Reject message */ if (pDCB->SyncMode & WIDE_NEGO_ENABLE) { /* do wide nego reject */ pDCB = pSRB->pSRBDCB; pDCB->SyncMode |= WIDE_NEGO_DONE; pDCB->SyncMode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP | WIDE_NEGO_ENABLE); pSRB->SRBState &= ~(SRB_DO_WIDE_NEGO+SRB_MSGIN); if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) && !(pDCB->SyncMode & SYNC_NEGO_DONE)) { /* Set ATN, in case ATN was clear */ pSRB->SRBState |= SRB_MSGOUT; trm_reg_write16( DO_SETATN, TRMREG_SCSI_CONTROL); } else { /* Clear ATN */ trm_reg_write16( DO_CLRATN, TRMREG_SCSI_CONTROL); } } else if (pDCB->SyncMode & SYNC_NEGO_ENABLE) { /* do sync nego reject */ trm_reg_write16(DO_CLRATN,TRMREG_SCSI_CONTROL); if (pSRB->SRBState & SRB_DO_SYNC_NEGO) { pDCB = pSRB->pSRBDCB; pDCB->SyncMode &= ~(SYNC_NEGO_ENABLE+SYNC_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; /* * * program SCSI control register * */ trm_reg_write8(pDCB->SyncPeriod, TRMREG_SCSI_SYNC); trm_reg_write8(pDCB->SyncOffset, TRMREG_SCSI_OFFSET); trm_SetXferRate(pACB,pSRB,pDCB); } } *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if (message_in_code == MSG_IGNOREWIDE) { trm_reg_write32(1, TRMREG_SCSI_COUNTER); trm_reg_read8(TRMREG_SCSI_FIFO); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else { /* Restore data pointer message */ /* Save data pointer message */ /* Completion message */ /* NOP message */ *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } } else { /* * Parsing incomming extented messages */ *pSRB->pMsgPtr = message_in_code; pSRB->MsgCnt++; pSRB->pMsgPtr++; TRM_DPRINTF("pSRB->MsgInBuf[0]=%2x \n ",pSRB->MsgInBuf[0]); TRM_DPRINTF("pSRB->MsgInBuf[1]=%2x \n ",pSRB->MsgInBuf[1]); TRM_DPRINTF("pSRB->MsgInBuf[2]=%2x \n ",pSRB->MsgInBuf[2]); TRM_DPRINTF("pSRB->MsgInBuf[3]=%2x \n ",pSRB->MsgInBuf[3]); TRM_DPRINTF("pSRB->MsgInBuf[4]=%2x \n ",pSRB->MsgInBuf[4]); if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) && (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG)) { /* * is QUEUE tag message : * * byte 0: * HEAD QUEUE TAG (20h) * ORDERED QUEUE TAG (21h) * SIMPLE QUEUE TAG (22h) * byte 1: * Queue tag (00h - FFh) */ if (pSRB->MsgCnt == 2) { pSRB->SRBState = 0; message_in_tag_id = pSRB->MsgInBuf[1]; pSRB = pDCB->pGoingSRB; pSRBTemp = pDCB->pGoingLastSRB; if (pSRB) { for (;;) { if (pSRB->TagNumber != message_in_tag_id) { if (pSRB == pSRBTemp) { goto mingx0; } pSRB = pSRB->pNextSRB; } else break; } if (pDCB->DCBFlag & ABORT_DEV_) { pSRB->SRBState = SRB_ABORT_SENT; trm_EnableMsgOutAbort1( pACB, pSRB); } if (!(pSRB->SRBState & SRB_DISCONNECT)) { TRM_DPRINTF("SRB not yet disconnect........ \n "); goto mingx0; } pDCB->pActiveSRB = pSRB; pSRB->SRBState = SRB_DATA_XFER; } else { mingx0: pSRB = &pACB->TmpSRB; pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; trm_EnableMsgOutAbort2( pACB, pSRB); } } *pscsi_status = PH_BUS_FREE; /* .. initial phase */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgInBuf[2] == 3) && (pSRB->MsgCnt == 4)) { /* * is Wide data xfer Extended message : * ====================================== * WIDE DATA TRANSFER REQUEST * ====================================== * byte 0 : Extended message (01h) * byte 1 : Extended message length (02h) * byte 2 : WIDE DATA TRANSFER code (03h) * byte 3 : Transfer width exponent */ pDCB = pSRB->pSRBDCB; pSRB->SRBState &= ~(SRB_EXTEND_MSGIN+SRB_DO_WIDE_NEGO); if ((pSRB->MsgInBuf[1] != 2)) { /* Length is wrong, reject it */ pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); pSRB->MsgCnt = 1; pSRB->MsgInBuf[0] = MSG_REJECT_; trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } if (pDCB->SyncMode & WIDE_NEGO_ENABLE) { /* Do wide negoniation */ if (pSRB->MsgInBuf[3] > 2) { /* > 32 bit */ /* reject_msg: */ pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); pSRB->MsgCnt = 1; pSRB->MsgInBuf[0] = MSG_REJECT_; trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } if (pSRB->MsgInBuf[3] == 2) { pSRB->MsgInBuf[3] = 1; /* do 16 bits */ } else { if (!(pDCB->SyncMode & WIDE_NEGO_DONE)) { pSRB->SRBState &= ~(SRB_DO_WIDE_NEGO+SRB_MSGIN); pDCB->SyncMode |= WIDE_NEGO_DONE; pDCB->SyncMode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP | WIDE_NEGO_ENABLE); if (pSRB->MsgInBuf[3] != 0) { /* is Wide data xfer */ pDCB->SyncPeriod |= WIDE_SYNC; pDCB->tinfo.current.width = MSG_EXT_WDTR_BUS_16_BIT; pDCB->tinfo.goal.width = MSG_EXT_WDTR_BUS_16_BIT; } } } } else pSRB->MsgInBuf[3] = 0; pSRB->SRBState |= SRB_MSGOUT; trm_reg_write16(DO_SETATN,TRMREG_SCSI_CONTROL); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ /* it's important for atn stop */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* * SCSI command */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgInBuf[2] == 1) && (pSRB->MsgCnt == 5)) { /* * is 8bit transfer Extended message : * ================================= * SYNCHRONOUS DATA TRANSFER REQUEST * ================================= * byte 0 : Extended message (01h) * byte 1 : Extended message length (03) * byte 2 : SYNCHRONOUS DATA TRANSFER code (01h) * byte 3 : Transfer period factor * byte 4 : REQ/ACK offset */ pSRB->SRBState &= ~(SRB_EXTEND_MSGIN+SRB_DO_SYNC_NEGO); if ((pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1)) { /* reject_msg: */ pSRB->MsgCnt = 1; pSRB->MsgInBuf[0] = MSG_REJECT_; trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else if (!(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4])) { /* set async */ pDCB = pSRB->pSRBDCB; /* disable sync & sync nego */ pDCB->SyncMode &= ~(SYNC_NEGO_ENABLE+SYNC_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; pDCB->tinfo.goal.period = 0; pDCB->tinfo.goal.offset = 0; pDCB->tinfo.current.period = 0; pDCB->tinfo.current.offset = 0; pDCB->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT; /* * * program SCSI control register * */ trm_reg_write8(pDCB->SyncPeriod,TRMREG_SCSI_SYNC); trm_reg_write8(pDCB->SyncOffset,TRMREG_SCSI_OFFSET); trm_SetXferRate(pACB,pSRB,pDCB); *pscsi_status = PH_BUS_FREE; /* .. initial phase */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); return; } else { /* set sync */ pDCB = pSRB->pSRBDCB; pDCB->SyncMode |= SYNC_NEGO_ENABLE+SYNC_NEGO_DONE; pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3]; /* Transfer period factor */ pDCB->SyncOffset = pSRB->MsgInBuf[4]; /* REQ/ACK offset */ if(pACB->AdaptType == 1) { for(bIndex = 0; bIndex < 7; bIndex++) { if(pSRB->MsgInBuf[3] <= dc395u2x_clock_period[bIndex]) { pDCB->tinfo.goal.period = dc395u2x_tinfo_period[bIndex]; pDCB->tinfo.current.period = dc395u2x_tinfo_period[bIndex]; pDCB->tinfo.goal.offset = pDCB->SyncOffset; pDCB->tinfo.current.offset = pDCB->SyncOffset; pDCB->SyncPeriod |= (bIndex|LVDS_SYNC); break; } } } else { for(bIndex = 0; bIndex < 7; bIndex++) { if(pSRB->MsgInBuf[3] <= dc395x_clock_period[bIndex]) { pDCB->tinfo.goal.period = dc395x_tinfo_period[bIndex]; pDCB->tinfo.current.period = dc395x_tinfo_period[bIndex]; pDCB->tinfo.goal.offset = pDCB->SyncOffset; pDCB->tinfo.current.offset = pDCB->SyncOffset; pDCB->SyncPeriod |= (bIndex|ALT_SYNC); break; } } } /* * * program SCSI control register * */ trm_reg_write8(pDCB->SyncPeriod, TRMREG_SCSI_SYNC); trm_reg_write8(pDCB->SyncOffset, TRMREG_SCSI_OFFSET); trm_SetXferRate(pACB,pSRB,pDCB); *pscsi_status=PH_BUS_FREE;/*.. initial phase*/ trm_reg_write16(DO_DATALATCH,TRMREG_SCSI_CONTROL);/* it's important for atn stop*/ /* ** SCSI command */ trm_reg_write8(SCMD_MSGACCEPT,TRMREG_SCSI_COMMAND); return; } } *pscsi_status = PH_BUS_FREE; /* .. initial phase */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop */ /* * SCSI cammand */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); } } static void trm_MsgInPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL); trm_reg_write32(1,TRMREG_SCSI_COUNTER); if (!(pSRB->SRBState & SRB_MSGIN)) { pSRB->SRBState &= SRB_DISCONNECT; pSRB->SRBState |= SRB_MSGIN; } trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop*/ /* * SCSI cammand */ trm_reg_write8(SCMD_FIFO_IN, TRMREG_SCSI_COMMAND); } static void trm_Nop0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { } static void trm_Nop1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status) { } static void trm_SetXferRate(PACB pACB,PSRB pSRB, PDCB pDCB) { union ccb *pccb; struct ccb_trans_settings neg; u_int16_t cnt, i; u_int8_t bval; PDCB pDCBTemp; /* * set all lun device's period , offset */ TRM_DPRINTF("trm_SetXferRate\n"); pccb = pSRB->pccb; neg.sync_period = pDCB->tinfo.goal.period; neg.sync_offset = pDCB->tinfo.goal.offset; neg.valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; xpt_setup_ccb(&neg.ccb_h, pccb->ccb_h.path, /* priority */1); xpt_async(AC_TRANSFER_NEG, pccb->ccb_h.path, &neg); if (!(pDCB->IdentifyMsg & 0x07)) { pDCBTemp = pACB->pLinkDCB; cnt = pACB->DeviceCnt; bval = pDCB->TargetID; for (i = 0; i < cnt; i++) { if (pDCBTemp->TargetID == bval) { pDCBTemp->SyncPeriod = pDCB->SyncPeriod; pDCBTemp->SyncOffset = pDCB->SyncOffset; pDCBTemp->SyncMode = pDCB->SyncMode; } pDCBTemp = pDCBTemp->pNextDCB; } } return; } /* * scsiiom * trm_Interrupt * * * ---SCSI bus phase * * PH_DATA_OUT 0x00 Data out phase * PH_DATA_IN 0x01 Data in phase * PH_COMMAND 0x02 Command phase * PH_STATUS 0x03 Status phase * PH_BUS_FREE 0x04 Invalid phase used as bus free * PH_BUS_FREE 0x05 Invalid phase used as bus free * PH_MSG_OUT 0x06 Message out phase * PH_MSG_IN 0x07 Message in phase * */ static void trm_Disconnect(PACB pACB) { PDCB pDCB; PSRB pSRB, psrb; u_int16_t i,j, cnt; u_int target_id,target_lun; TRM_DPRINTF("trm_Disconnect...............\n "); pDCB = pACB->pActiveDCB; if (!pDCB) { TRM_DPRINTF(" Exception Disconnect DCB=NULL..............\n "); j = 400; while (--j) DELAY(1); /* 1 msec */ trm_reg_write16((DO_CLRFIFO | DO_HWRESELECT), TRMREG_SCSI_CONTROL); return; } pSRB = pDCB->pActiveSRB; /* bug pSRB=0 */ target_id = pSRB->pccb->ccb_h.target_id; target_lun = pSRB->pccb->ccb_h.target_lun; TRM_DPRINTF(":pDCB->pActiveSRB= %8x \n ",(u_int) pDCB->pActiveSRB); pACB->pActiveDCB = 0; pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ trm_reg_write16((DO_CLRFIFO | DO_HWRESELECT), TRMREG_SCSI_CONTROL); if (pSRB->SRBState & SRB_UNEXPECT_RESEL) { pSRB->SRBState = 0; trm_DoWaitingSRB(pACB); } else if (pSRB->SRBState & SRB_ABORT_SENT) { pDCB->DCBFlag = 0; cnt = pDCB->GoingSRBCnt; pDCB->GoingSRBCnt = 0; pSRB = pDCB->pGoingSRB; for (i = 0; i < cnt; i++) { psrb = pSRB->pNextSRB; pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB; pSRB = psrb; } pDCB->pGoingSRB = 0; trm_DoWaitingSRB(pACB); } else { if ((pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) || !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED))) { /* Selection time out */ if (!(pACB->scan_devices[target_id][target_lun]) && pSRB->CmdBlock[0] != 0x00 && /* TEST UNIT READY */ pSRB->CmdBlock[0] != INQUIRY) { pSRB->SRBState = SRB_READY; trm_RewaitSRB(pDCB, pSRB); } else { pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; goto disc1; } } else if (pSRB->SRBState & SRB_DISCONNECT) { /* * SRB_DISCONNECT */ trm_DoWaitingSRB(pACB); } else if (pSRB->SRBState & SRB_COMPLETED) { disc1: /* * SRB_COMPLETED */ pDCB->pActiveSRB = 0; pSRB->SRBState = SRB_FREE; trm_SRBdone(pACB, pDCB, pSRB); } } return; } static void trm_Reselect(PACB pACB) { PDCB pDCB; PSRB pSRB; u_int16_t RselTarLunId; TRM_DPRINTF("trm_Reselect................. \n"); pDCB = pACB->pActiveDCB; if (pDCB) { /* Arbitration lost but Reselection win */ pSRB = pDCB->pActiveSRB; pSRB->SRBState = SRB_READY; trm_RewaitSRB(pDCB, pSRB); } /* Read Reselected Target Id and LUN */ RselTarLunId = trm_reg_read16(TRMREG_SCSI_TARGETID) & 0x1FFF; pDCB = pACB->pLinkDCB; while (RselTarLunId != *((u_int16_t *) &pDCB->TargetID)) { /* get pDCB of the reselect id */ pDCB = pDCB->pNextDCB; } pACB->pActiveDCB = pDCB; if (pDCB->SyncMode & EN_TAG_QUEUING) { pSRB = &pACB->TmpSRB; pDCB->pActiveSRB = pSRB; } else { pSRB = pDCB->pActiveSRB; if (!pSRB || !(pSRB->SRBState & SRB_DISCONNECT)) { /* * abort command */ pSRB = &pACB->TmpSRB; pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; trm_EnableMsgOutAbort1(pACB, pSRB); } else { if (pDCB->DCBFlag & ABORT_DEV_) { pSRB->SRBState = SRB_ABORT_SENT; trm_EnableMsgOutAbort1(pACB, pSRB); } else pSRB->SRBState = SRB_DATA_XFER; } } pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */ /* * Program HA ID, target ID, period and offset */ trm_reg_write8((u_int8_t) RselTarLunId,TRMREG_SCSI_TARGETID); /* target ID */ trm_reg_write8(pACB->AdaptSCSIID,TRMREG_SCSI_HOSTID); /* host ID */ trm_reg_write8(pDCB->SyncPeriod,TRMREG_SCSI_SYNC); /* period */ trm_reg_write8(pDCB->SyncOffset,TRMREG_SCSI_OFFSET); /* offset */ trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL); /* it's important for atn stop*/ /* * SCSI cammand */ trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND); /* to rls the /ACK signal */ } static void trm_SRBdone(PACB pACB, PDCB pDCB, PSRB pSRB) { PSRB psrb; u_int8_t bval, bval1,status; union ccb *pccb; struct ccb_scsiio *pcsio; PSCSI_INQDATA ptr; int intflag; u_int target_id,target_lun; PDCB pTempDCB; pccb = pSRB->pccb; if (pccb == NULL) return; pcsio = &pccb->csio; target_id = pSRB->pccb->ccb_h.target_id; target_lun = pSRB->pccb->ccb_h.target_lun; if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(pACB->buffer_dmat, pSRB->dmamap, op); bus_dmamap_unload(pACB->buffer_dmat, pSRB->dmamap); } /* * * target status * */ status = pSRB->TargetStatus; pcsio->scsi_status=SCSI_STAT_GOOD; pccb->ccb_h.status = CAM_REQ_CMP; if (pSRB->SRBFlag & AUTO_REQSENSE) { /* * status of auto request sense */ pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = SCSI_STATUS_CHECK_COND; if (status == SCSI_STATUS_CHECK_COND) { pccb->ccb_h.status = CAM_SEL_TIMEOUT; goto ckc_e; } *((u_long *) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; *((u_long *) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1]; pSRB->SRBTotalXferLength = pSRB->Segment1[1]; pSRB->pSRBSGL->address = pSRB->SgSenseTemp.address; pSRB->pSRBSGL->length = pSRB->SgSenseTemp.length; pcsio->scsi_status = SCSI_STATUS_CHECK_COND; bzero(&pcsio->sense_data, pcsio->sense_len); bcopy(trm_get_sense_buf(pACB, pSRB), &pcsio->sense_data, pcsio->sense_len); pcsio->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; goto ckc_e; } /* * target status */ if (status) { if (status == SCSI_STATUS_CHECK_COND) { if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { TRM_DPRINTF("trm_RequestSense..................\n"); trm_RequestSense(pACB, pDCB, pSRB); return; } pcsio->scsi_status = SCSI_STATUS_CHECK_COND; pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; goto ckc_e; } else if (status == SCSI_STAT_QUEUEFULL) { bval = (u_int8_t) pDCB->GoingSRBCnt; bval--; pDCB->MaxActiveCommandCnt = bval; trm_RewaitSRB(pDCB, pSRB); pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; return; } else if (status == SCSI_STAT_SEL_TIMEOUT) { pSRB->AdaptStatus = H_SEL_TIMEOUT; pSRB->TargetStatus = 0; pcsio->scsi_status = SCSI_STAT_SEL_TIMEOUT; pccb->ccb_h.status = CAM_SEL_TIMEOUT; } else if (status == SCSI_STAT_BUSY) { TRM_DPRINTF("trm: target busy at %s %d\n", __FILE__, __LINE__); pcsio->scsi_status = SCSI_STAT_BUSY; pccb->ccb_h.status = CAM_SCSI_BUSY; return; /* The device busy, try again later? */ } else if (status == SCSI_STAT_RESCONFLICT) { TRM_DPRINTF("trm: target reserved at %s %d\n", __FILE__, __LINE__); pcsio->scsi_status = SCSI_STAT_RESCONFLICT; pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; /*XXX*/ return; } else { pSRB->AdaptStatus = 0; if (pSRB->RetryCnt) { pSRB->RetryCnt--; pSRB->TargetStatus = 0; pSRB->SRBSGIndex = 0; if (trm_StartSCSI(pACB, pDCB, pSRB)) { /* * If trm_StartSCSI return 1 : * current interrupt status is interrupt * disreenable * It's said that SCSI processor has more * one SRB need to do */ trm_RewaitSRB(pDCB, pSRB); } return; } else { TRM_DPRINTF("trm: driver stuffup at %s %d\n", __FILE__, __LINE__); pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; } } } else { /* * process initiator status.......................... * Adapter (initiator) status */ status = pSRB->AdaptStatus; if (status & H_OVER_UNDER_RUN) { pSRB->TargetStatus = 0; pccb->ccb_h.status = CAM_DATA_RUN_ERR; /* Illegal length (over/under run) */ } else if (pSRB->SRBStatus & PARITY_ERROR) { TRM_DPRINTF("trm: driver stuffup %s %d\n", __FILE__, __LINE__); pDCB->tinfo.goal.period = 0; pDCB->tinfo.goal.offset = 0; /* Driver failed to perform operation */ pccb->ccb_h.status = CAM_UNCOR_PARITY; } else { /* no error */ pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; pccb->ccb_h.status = CAM_REQ_CMP; /* there is no error, (sense is invalid) */ } } ckc_e: if (pACB->scan_devices[target_id][target_lun]) { /* * if SCSI command in "scan devices" duty */ if (pSRB->CmdBlock[0] == TEST_UNIT_READY) pACB->scan_devices[target_id][target_lun] = 0; /* SCSI command phase :test unit ready */ else if (pSRB->CmdBlock[0] == INQUIRY) { /* * SCSI command phase :inquiry scsi device data * (type,capacity,manufacture.... */ if (pccb->ccb_h.status == CAM_SEL_TIMEOUT) goto NO_DEV; ptr = (PSCSI_INQDATA) pcsio->data_ptr; /* page fault */ TRM_DPRINTF("trm_SRBdone..PSCSI_INQDATA:%2x \n", ptr->DevType); bval1 = ptr->DevType & SCSI_DEVTYPE; if (bval1 == SCSI_NODEV) { NO_DEV: TRM_DPRINTF("trm_SRBdone NO Device:target_id= %d ,target_lun= %d \n", target_id, target_lun); intflag = splcam(); pACB->scan_devices[target_id][target_lun] = 0; /* no device set scan device flag =0*/ /* pDCB Q link */ /* move the head of DCB to tempDCB*/ pTempDCB=pACB->pLinkDCB; /* search current DCB for pass link */ while (pTempDCB->pNextDCB != pDCB) { pTempDCB = pTempDCB->pNextDCB; } /* * when the current DCB found than connect * current DCB tail */ /* to the DCB tail that before current DCB */ pTempDCB->pNextDCB = pDCB->pNextDCB; /* * if there was only one DCB ,connect his tail * to his head */ if (pACB->pLinkDCB == pDCB) pACB->pLinkDCB = pTempDCB->pNextDCB; if (pACB->pDCBRunRobin == pDCB) pACB->pDCBRunRobin = pTempDCB->pNextDCB; pDCB->DCBstatus &= ~DS_IN_QUEUE; pACB->DeviceCnt--; if (pACB->DeviceCnt == 0) { pACB->pLinkDCB = NULL; pACB->pDCBRunRobin = NULL; } splx(intflag); } else { #ifdef trm_DEBUG1 int j; for (j = 0; j < 28; j++) { TRM_DPRINTF("ptr=%2x ", ((u_int8_t *)ptr)[j]); } #endif pDCB->DevType = bval1; if (bval1 == SCSI_DASD || bval1 == SCSI_OPTICAL) { if ((((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) && (ptr->Flags & SCSI_INQ_CMDQUEUE) && (pDCB->DevMode & TAG_QUEUING_) && (pDCB->DevMode & EN_DISCONNECT_)) { if (pDCB->DevMode & TAG_QUEUING_) { pDCB-> MaxActiveCommandCnt = pACB->TagMaxNum; pDCB->SyncMode |= EN_TAG_QUEUING; pDCB->tinfo.disc_tag |= TRM_CUR_TAGENB; } else { pDCB->SyncMode |= EN_ATN_STOP; pDCB->tinfo.disc_tag &= ~TRM_CUR_TAGENB; } } } } /* pSRB->CmdBlock[0] == INQUIRY */ } /* pACB->scan_devices[target_id][target_lun] */ } intflag = splcam(); /* ReleaseSRB(pDCB, pSRB); */ if (pSRB == pDCB->pGoingSRB) pDCB->pGoingSRB = pSRB->pNextSRB; else { psrb = pDCB->pGoingSRB; while (psrb->pNextSRB != pSRB) { psrb = psrb->pNextSRB; } psrb->pNextSRB = pSRB->pNextSRB; if (pSRB == pDCB->pGoingLastSRB) { pDCB->pGoingLastSRB = psrb; } } pSRB->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = pSRB; pDCB->GoingSRBCnt--; trm_DoWaitingSRB(pACB); splx(intflag); /* Notify cmd done */ xpt_done (pccb); } static void trm_DoingSRB_Done(PACB pACB) { PDCB pDCB, pdcb; PSRB psrb, psrb2; u_int16_t cnt, i; union ccb *pccb; pDCB = pACB->pLinkDCB; if (pDCB == NULL) return; pdcb = pDCB; do { cnt = pdcb->GoingSRBCnt; psrb = pdcb->pGoingSRB; for (i = 0; i < cnt; i++) { psrb2 = psrb->pNextSRB; pccb = psrb->pccb; pccb->ccb_h.status = CAM_SEL_TIMEOUT; /* ReleaseSRB(pDCB, pSRB); */ psrb->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = psrb; xpt_done(pccb); psrb = psrb2; } pdcb->GoingSRBCnt = 0;; pdcb->pGoingSRB = NULL; pdcb = pdcb->pNextDCB; } while (pdcb != pDCB); } static void trm_ResetSCSIBus(PACB pACB) { int intflag; intflag = splcam(); pACB->ACBFlag |= RESET_DEV; trm_reg_write16(DO_RSTSCSI,TRMREG_SCSI_CONTROL); while (!(trm_reg_read16(TRMREG_SCSI_INTSTATUS) & INT_SCSIRESET)); splx(intflag); return; } static void trm_ScsiRstDetect(PACB pACB) { int intflag; u_long wlval; TRM_DPRINTF("trm_ScsiRstDetect \n"); wlval = 1000; while (--wlval) DELAY(1000); intflag = splcam(); trm_reg_write8(STOPDMAXFER,TRMREG_DMA_CONTROL); trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL); if (pACB->ACBFlag & RESET_DEV) pACB->ACBFlag |= RESET_DONE; else { pACB->ACBFlag |= RESET_DETECT; trm_ResetDevParam(pACB); /* trm_DoingSRB_Done(pACB); ???? */ trm_RecoverSRB(pACB); pACB->pActiveDCB = NULL; pACB->ACBFlag = 0; trm_DoWaitingSRB(pACB); } splx(intflag); return; } static void trm_RequestSense(PACB pACB, PDCB pDCB, PSRB pSRB) { union ccb *pccb; struct ccb_scsiio *pcsio; pccb = pSRB->pccb; pcsio = &pccb->csio; pSRB->SRBFlag |= AUTO_REQSENSE; pSRB->Segment0[0] = *((u_long *) &(pSRB->CmdBlock[0])); pSRB->Segment0[1] = *((u_long *) &(pSRB->CmdBlock[4])); pSRB->Segment1[0] = (u_long) ((pSRB->ScsiCmdLen << 8) + pSRB->SRBSGCount); pSRB->Segment1[1] = pSRB->SRBTotalXferLength; /* ?????????? */ /* $$$$$$ Status of initiator/target $$$$$$$$ */ pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; /* $$$$$$ Status of initiator/target $$$$$$$$ */ pSRB->SRBTotalXferLength = sizeof(pcsio->sense_data); pSRB->SgSenseTemp.address = pSRB->pSRBSGL->address; pSRB->SgSenseTemp.length = pSRB->pSRBSGL->length; pSRB->pSRBSGL->address = trm_get_sense_bufaddr(pACB, pSRB); pSRB->pSRBSGL->length = (u_long) sizeof(struct scsi_sense_data); pSRB->SRBSGCount = 1; pSRB->SRBSGIndex = 0; *((u_long *) &(pSRB->CmdBlock[0])) = 0x00000003; pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5; *((u_int16_t *) &(pSRB->CmdBlock[4])) = pcsio->sense_len; pSRB->ScsiCmdLen = 6; if (trm_StartSCSI(pACB, pDCB, pSRB)) /* * If trm_StartSCSI return 1 : * current interrupt status is interrupt disreenable * It's said that SCSI processor has more one SRB need to do */ trm_RewaitSRB(pDCB, pSRB); } static void trm_EnableMsgOutAbort2(PACB pACB, PSRB pSRB) { pSRB->MsgCnt = 1; trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL); } static void trm_EnableMsgOutAbort1(PACB pACB, PSRB pSRB) { pSRB->MsgOutBuf[0] = MSG_ABORT; trm_EnableMsgOutAbort2(pACB, pSRB); } static void trm_initDCB(PACB pACB, PDCB pDCB, u_int16_t unit,u_int32_t i,u_int32_t j) { PNVRAMTYPE pEEpromBuf; u_int8_t bval,PeriodIndex; u_int target_id,target_lun; PDCB pTempDCB; int intflag; target_id = i; target_lun = j; /* * Using the lun 0 device to init other DCB first, if the device * has been initialized. * I don't want init sync arguments one by one, it is the same. */ if (target_lun != 0 && (pACB->DCBarray[target_id][0].DCBstatus & DS_IN_QUEUE)) bcopy(&pACB->DCBarray[target_id][0], pDCB, sizeof(TRM_DCB)); intflag = splcam(); if (pACB->pLinkDCB == 0) { pACB->pLinkDCB = pDCB; /* * RunRobin impersonate the role * that let each device had good proportion * about SCSI command proceeding */ pACB->pDCBRunRobin = pDCB; pDCB->pNextDCB = pDCB; } else { pTempDCB=pACB->pLinkDCB; /* search the last nod of DCB link */ while (pTempDCB->pNextDCB != pACB->pLinkDCB) pTempDCB = pTempDCB->pNextDCB; /* connect current DCB with last DCB tail */ pTempDCB->pNextDCB = pDCB; /* connect current DCB tail to this DCB Q head */ pDCB->pNextDCB=pACB->pLinkDCB; } splx(intflag); pACB->DeviceCnt++; pDCB->TargetID = target_id; pDCB->TargetLUN = target_lun; pDCB->pWaitingSRB = NULL; pDCB->pGoingSRB = NULL; pDCB->GoingSRBCnt = 0; pDCB->pActiveSRB = NULL; pDCB->MaxActiveCommandCnt = 1; pDCB->DCBFlag = 0; pDCB->DCBstatus |= DS_IN_QUEUE; /* $$$$$$$ */ pEEpromBuf = &trm_eepromBuf[unit]; pDCB->DevMode = pEEpromBuf->NvramTarget[target_id].NvmTarCfg0; pDCB->AdpMode = pEEpromBuf->NvramChannelCfg; /* $$$$$$$ */ /* * disconnect enable ? */ if (pDCB->DevMode & NTC_DO_DISCONNECT) { bval = 0xC0; pDCB->tinfo.disc_tag |= TRM_USR_DISCENB ; } else { bval = 0x80; pDCB->tinfo.disc_tag &= ~(TRM_USR_DISCENB); } bval |= target_lun; pDCB->IdentifyMsg = bval; if (target_lun != 0 && (pACB->DCBarray[target_id][0].DCBstatus & DS_IN_QUEUE)) return; /* $$$$$$$ */ /* * tag Qing enable ? */ if (pDCB->DevMode & TAG_QUEUING_) { pDCB->tinfo.disc_tag |= TRM_USR_TAGENB ; } else pDCB->tinfo.disc_tag &= ~(TRM_USR_TAGENB); /* $$$$$$$ */ /* * wide nego ,sync nego enable ? */ pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; PeriodIndex = pEEpromBuf->NvramTarget[target_id].NvmTarPeriod & 0x07; if(pACB->AdaptType==1) {/* is U2? */ pDCB->MaxNegoPeriod=dc395u2x_clock_period[ PeriodIndex ]; pDCB->tinfo.user.period=pDCB->MaxNegoPeriod; pDCB->tinfo.user.offset=(pDCB->SyncMode & SYNC_NEGO_ENABLE) ? 31 : 0; } else { pDCB->MaxNegoPeriod=dc395x_clock_period[ PeriodIndex ]; pDCB->tinfo.user.period=pDCB->MaxNegoPeriod; pDCB->tinfo.user.offset=(pDCB->SyncMode & SYNC_NEGO_ENABLE) ? 15 : 0; } pDCB->SyncMode = 0; if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) && (pACB->Config & HCC_WIDE_CARD)) pDCB->SyncMode |= WIDE_NEGO_ENABLE; /* enable wide nego */ if (pDCB->DevMode & NTC_DO_SYNC_NEGO) pDCB->SyncMode |= SYNC_NEGO_ENABLE; /* enable sync nego */ /* $$$$$$$ */ /* * Fill in tinfo structure. */ pDCB->tinfo.user.width = (pDCB->SyncMode & WIDE_NEGO_ENABLE) ? MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT; pDCB->tinfo.current.period = 0; pDCB->tinfo.current.offset = 0; pDCB->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT; } static void trm_srbmapSG(void *arg, bus_dma_segment_t *segs, int nseg, int error) { PSRB pSRB; pSRB=(PSRB) arg; pSRB->SRBSGPhyAddr=segs->ds_addr; return; } static int trm_initSRB(PACB pACB) { u_int16_t i; PSRB pSRB; for(i = 0; i < TRM_MAX_SRB_CNT; i++) { pSRB = (PSRB)&pACB->pFreeSRB[i]; /* DMA tag for our S/G structures */ if(bus_dma_tag_create( /*parent_dmat*/pSRB->parent_dmat, /*alignment*/ 1, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ TRM_MAX_SG_LISTENTRY * sizeof(SGentry), /*nsegments*/ 1, /*maxsegsz*/ TRM_MAXTRANSFER_SIZE, /*flags*/ 0, /*dmat*/ &pSRB->sg_dmat) != 0) { return ENXIO; } if(bus_dmamem_alloc(pSRB->sg_dmat, (void **)&pSRB->pSRBSGL, BUS_DMA_NOWAIT, &pSRB->sg_dmamap) !=0 ) { return ENXIO; } bus_dmamap_load(pSRB->sg_dmat, pSRB->sg_dmamap, pSRB->pSRBSGL, TRM_MAX_SG_LISTENTRY * sizeof(SGentry), trm_srbmapSG, pSRB, /*flags*/0); if(i != TRM_MAX_SRB_CNT - 1) { /* ** link all SRB */ pSRB->pNextSRB = &pACB->pFreeSRB[i+1]; } else { /* ** load NULL to NextSRB of the last SRB */ pSRB->pNextSRB = NULL; } pSRB->TagNumber = i; } return (0); } static void trm_initACB(PACB pACB, u_int8_t adaptType, u_int16_t unit) { PNVRAMTYPE pEEpromBuf; pEEpromBuf = &trm_eepromBuf[unit]; pACB->max_id = 15; if (pEEpromBuf->NvramChannelCfg & NAC_SCANLUN) pACB->max_lun = 7; else pACB->max_lun = 0; TRM_DPRINTF("trm: pACB->max_id= %d pACB->max_lun= %d \n", pACB->max_id, pACB->max_lun); pACB->pLinkDCB = NULL; pACB->pDCBRunRobin = NULL; pACB->pActiveDCB = NULL; pACB->AdapterUnit = (u_int8_t)unit; pACB->AdaptSCSIID = pEEpromBuf->NvramScsiId; pACB->AdaptSCSILUN = 0; pACB->DeviceCnt = 0; pACB->AdaptType = adaptType; pACB->TagMaxNum = 2 << pEEpromBuf->NvramMaxTag; pACB->ACBFlag = 0; return; } static void NVRAM_trm_write_all(PNVRAMTYPE pEEpromBuf,PACB pACB) { u_int8_t *bpEeprom = (u_int8_t *) pEEpromBuf; u_int8_t bAddr; /* Enable SEEPROM */ trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) | EN_EEPROM), TRMREG_GEN_CONTROL); /* * Write enable */ NVRAM_trm_write_cmd(pACB, 0x04, 0xFF); trm_reg_write8(0, TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) { NVRAM_trm_set_data(pACB, bAddr, *bpEeprom); } /* * Write disable */ NVRAM_trm_write_cmd(pACB, 0x04, 0x00); trm_reg_write8(0 , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); /* Disable SEEPROM */ trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) & ~EN_EEPROM), TRMREG_GEN_CONTROL); return; } static void NVRAM_trm_set_data(PACB pACB, u_int8_t bAddr, u_int8_t bData) { int i; u_int8_t bSendData; /* * Send write command & address */ NVRAM_trm_write_cmd(pACB, 0x05, bAddr); /* * Write data */ for (i = 0; i < 8; i++, bData <<= 1) { bSendData = NVR_SELECT; if (bData & 0x80) /* Start from bit 7 */ bSendData |= NVR_BITOUT; trm_reg_write8(bSendData , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); } trm_reg_write8(NVR_SELECT , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); /* * Disable chip select */ trm_reg_write8(0 , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8(NVR_SELECT ,TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); /* * Wait for write ready */ while (1) { trm_reg_write8((NVR_SELECT | NVR_CLOCK), TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8(NVR_SELECT, TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); if (trm_reg_read8(TRMREG_GEN_NVRAM) & NVR_BITIN) { break; } } /* * Disable chip select */ trm_reg_write8(0, TRMREG_GEN_NVRAM); return; } static void NVRAM_trm_read_all(PNVRAMTYPE pEEpromBuf, PACB pACB) { u_int8_t *bpEeprom = (u_int8_t*) pEEpromBuf; u_int8_t bAddr; /* * Enable SEEPROM */ trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) | EN_EEPROM), TRMREG_GEN_CONTROL); for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) *bpEeprom = NVRAM_trm_get_data(pACB, bAddr); /* * Disable SEEPROM */ trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) & ~EN_EEPROM), TRMREG_GEN_CONTROL); return; } static u_int8_t NVRAM_trm_get_data(PACB pACB, u_int8_t bAddr) { int i; u_int8_t bReadData, bData = 0; /* * Send read command & address */ NVRAM_trm_write_cmd(pACB, 0x06, bAddr); for (i = 0; i < 8; i++) { /* * Read data */ trm_reg_write8((NVR_SELECT | NVR_CLOCK) , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8(NVR_SELECT , TRMREG_GEN_NVRAM); /* * Get data bit while falling edge */ bReadData = trm_reg_read8(TRMREG_GEN_NVRAM); bData <<= 1; if (bReadData & NVR_BITIN) { bData |= 1; } NVRAM_trm_wait_30us(pACB); } /* * Disable chip select */ trm_reg_write8(0, TRMREG_GEN_NVRAM); return (bData); } static void NVRAM_trm_wait_30us(PACB pACB) { /* ScsiPortStallExecution(30); wait 30 us */ trm_reg_write8(5, TRMREG_GEN_TIMER); while (!(trm_reg_read8(TRMREG_GEN_STATUS) & GTIMEOUT)); return; } static void NVRAM_trm_write_cmd(PACB pACB, u_int8_t bCmd, u_int8_t bAddr) { int i; u_int8_t bSendData; for (i = 0; i < 3; i++, bCmd <<= 1) { /* * Program SB+OP code */ bSendData = NVR_SELECT; if (bCmd & 0x04) bSendData |= NVR_BITOUT; /* start from bit 2 */ trm_reg_write8(bSendData, TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); } for (i = 0; i < 7; i++, bAddr <<= 1) { /* * Program address */ bSendData = NVR_SELECT; if (bAddr & 0x40) /* Start from bit 6 */ bSendData |= NVR_BITOUT; trm_reg_write8(bSendData , TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); } trm_reg_write8(NVR_SELECT, TRMREG_GEN_NVRAM); NVRAM_trm_wait_30us(pACB); } static void trm_check_eeprom(PNVRAMTYPE pEEpromBuf, PACB pACB) { u_int16_t *wpEeprom = (u_int16_t *) pEEpromBuf; u_int16_t wAddr, wCheckSum; u_long dAddr, *dpEeprom; NVRAM_trm_read_all(pEEpromBuf,pACB); wCheckSum = 0; for (wAddr = 0, wpEeprom = (u_int16_t *) pEEpromBuf; wAddr < 64; wAddr++, wpEeprom++) { wCheckSum += *wpEeprom; } if (wCheckSum != 0x1234) { /* * Checksum error, load default */ pEEpromBuf->NvramSubVendorID[0] = (u_int8_t) PCI_Vendor_ID_TEKRAM; pEEpromBuf->NvramSubVendorID[1] = (u_int8_t) (PCI_Vendor_ID_TEKRAM >> 8); pEEpromBuf->NvramSubSysID[0] = (u_int8_t) PCI_Device_ID_TRM_S1040; pEEpromBuf->NvramSubSysID[1] = (u_int8_t) (PCI_Device_ID_TRM_S1040 >> 8); pEEpromBuf->NvramSubClass = 0x00; pEEpromBuf->NvramVendorID[0] = (u_int8_t) PCI_Vendor_ID_TEKRAM; pEEpromBuf->NvramVendorID[1] = (u_int8_t) (PCI_Vendor_ID_TEKRAM >> 8); pEEpromBuf->NvramDeviceID[0] = (u_int8_t) PCI_Device_ID_TRM_S1040; pEEpromBuf->NvramDeviceID[1] = (u_int8_t) (PCI_Device_ID_TRM_S1040 >> 8); pEEpromBuf->NvramReserved = 0x00; for (dAddr = 0, dpEeprom = (u_long *) pEEpromBuf->NvramTarget; dAddr < 16; dAddr++, dpEeprom++) { *dpEeprom = 0x00000077; /* NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */ } *dpEeprom++ = 0x04000F07; /* NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */ *dpEeprom++ = 0x00000015; /* NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 */ for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++) *dpEeprom = 0x00; pEEpromBuf->NvramCheckSum = 0x00; for (wAddr = 0, wCheckSum = 0, wpEeprom = (u_int16_t *) pEEpromBuf; wAddr < 63; wAddr++, wpEeprom++) wCheckSum += *wpEeprom; *wpEeprom = 0x1234 - wCheckSum; NVRAM_trm_write_all(pEEpromBuf,pACB); } return; } static int trm_initAdapter(PACB pACB, u_int16_t unit) { PNVRAMTYPE pEEpromBuf; u_int16_t wval; u_int8_t bval; pEEpromBuf = &trm_eepromBuf[unit]; /* 250ms selection timeout */ trm_reg_write8(SEL_TIMEOUT, TRMREG_SCSI_TIMEOUT); /* Mask all the interrupt */ trm_reg_write8(0x00, TRMREG_DMA_INTEN); trm_reg_write8(0x00, TRMREG_SCSI_INTEN); /* Reset SCSI module */ trm_reg_write16(DO_RSTMODULE, TRMREG_SCSI_CONTROL); /* program configuration 0 */ pACB->Config = HCC_AUTOTERM | HCC_PARITY; if (trm_reg_read8(TRMREG_GEN_STATUS) & WIDESCSI) pACB->Config |= HCC_WIDE_CARD; if (pEEpromBuf->NvramChannelCfg & NAC_POWERON_SCSI_RESET) pACB->Config |= HCC_SCSI_RESET; if (pACB->Config & HCC_PARITY) bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; else bval = PHASELATCH | INITIATOR | BLOCKRST ; trm_reg_write8(bval,TRMREG_SCSI_CONFIG0); /* program configuration 1 */ trm_reg_write8(0x13, TRMREG_SCSI_CONFIG1); /* program Host ID */ bval = pEEpromBuf->NvramScsiId; trm_reg_write8(bval, TRMREG_SCSI_HOSTID); /* set ansynchronous transfer */ trm_reg_write8(0x00, TRMREG_SCSI_OFFSET); /* Trun LED control off*/ wval = trm_reg_read16(TRMREG_GEN_CONTROL) & 0x7F; trm_reg_write16(wval, TRMREG_GEN_CONTROL); /* DMA config */ wval = trm_reg_read16(TRMREG_DMA_CONFIG) | DMA_ENHANCE; trm_reg_write16(wval, TRMREG_DMA_CONFIG); /* Clear pending interrupt status */ trm_reg_read8(TRMREG_SCSI_INTSTATUS); /* Enable SCSI interrupt */ trm_reg_write8(0x7F, TRMREG_SCSI_INTEN); trm_reg_write8(EN_SCSIINTR, TRMREG_DMA_INTEN); return (0); } static void trm_mapSRB(void *arg, bus_dma_segment_t *segs, int nseg, int error) { PACB pACB; pACB = (PACB)arg; pACB->srb_physbase = segs->ds_addr; } static void trm_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *baddr; baddr = (bus_addr_t *)arg; *baddr = segs->ds_addr; } static PACB trm_init(u_int16_t unit, device_t dev) { PACB pACB; int rid = PCIR_MAPS, i = 0, j = 0; u_int16_t adaptType = 0; pACB = (PACB) device_get_softc(dev); if (!pACB) { printf("trm%d: cannot allocate ACB !\n", unit); return (NULL); } bzero (pACB, sizeof (struct _ACB)); pACB->iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (pACB->iores == NULL) { printf("trm_init: bus_alloc_resource failed!\n"); return (NULL); } switch (pci_get_devid(dev)) { case PCI_DEVICEID_TRMS1040: adaptType = 0; break; case PCI_DEVICEID_TRMS2080: adaptType = 1; break; default: printf("trm_init %d: unknown adapter type!\n", unit); goto bad; } pACB->dev = dev; pACB->tag = rman_get_bustag(pACB->iores); pACB->bsh = rman_get_bushandle(pACB->iores); if (bus_dma_tag_create(/*parent_dmat*/ pACB->parent_dmat, /*alignment*/ 1, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ MAXBSIZE, /*nsegments*/ TRM_NSEG, /*maxsegsz*/ TRM_MAXTRANSFER_SIZE, /*flags*/ BUS_DMA_ALLOCNOW, &pACB->buffer_dmat) != 0) goto bad; /* DMA tag for our ccb structures */ if(bus_dma_tag_create( /*parent_dmat*/pACB->parent_dmat, /*alignment*/ 1, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ TRM_MAX_SRB_CNT * sizeof(TRM_SRB), /*nsegments*/ 1, /*maxsegsz*/ TRM_MAXTRANSFER_SIZE, /*flags*/ 0, /*dmat*/ &pACB->srb_dmat) != 0) { printf("trm_init %d: bus_dma_tag_create SRB failure\n", unit); goto bad; } if (bus_dmamem_alloc(pACB->srb_dmat, (void **)&pACB->pFreeSRB, BUS_DMA_NOWAIT, &pACB->srb_dmamap) != 0) { printf("trm_init %d: bus_dmamem_alloc SRB failure\n", unit); goto bad; } bus_dmamap_load(pACB->srb_dmat, pACB->srb_dmamap, pACB->pFreeSRB, TRM_MAX_SRB_CNT * sizeof(TRM_SRB), trm_mapSRB, pACB, /* flags */0); /* Create, allocate, and map DMA buffers for autosense data */ if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, sizeof(struct scsi_sense_data) * TRM_MAX_SRB_CNT, /*nsegments*/1, /*maxsegsz*/TRM_MAXTRANSFER_SIZE, /*flags*/0, &pACB->sense_dmat) != 0) { if (bootverbose) device_printf(dev, "cannot create sense buffer dmat\n"); goto bad; } if (bus_dmamem_alloc(pACB->sense_dmat, (void **)&pACB->sense_buffers, BUS_DMA_NOWAIT, &pACB->sense_dmamap) != 0) goto bad; bus_dmamap_load(pACB->sense_dmat, pACB->sense_dmamap, pACB->sense_buffers, sizeof(struct scsi_sense_data) * TRM_MAX_SRB_CNT, trm_dmamap_cb, &pACB->sense_busaddr, /*flags*/0); trm_check_eeprom(&trm_eepromBuf[unit],pACB); trm_initACB(pACB, adaptType, unit); for (i = 0; i < (pACB->max_id + 1); i++) { if (pACB->AdaptSCSIID == i) continue; for(j = 0; j < (pACB->max_lun + 1); j++) { pACB->scan_devices[i][j] = 1; /* we assume we need to scan all devices */ trm_initDCB(pACB, &pACB->DCBarray[i][j], unit, i, j); } } bzero(pACB->pFreeSRB, TRM_MAX_SRB_CNT * sizeof(TRM_SRB)); if (trm_initSRB(pACB)) { printf("trm_initSRB: error\n"); goto bad; } if (trm_initAdapter(pACB, unit)) { printf("trm_initAdapter: initial ERROR\n"); goto bad; } return (pACB); bad: if (pACB->iores) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, pACB->iores); if (pACB->sense_dmamap) { bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap); bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers, pACB->sense_dmamap); bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap); } if (pACB->sense_dmat) bus_dma_tag_destroy(pACB->sense_dmat); if (pACB->srb_dmamap) { bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap); bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB, pACB->srb_dmamap); bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap); } if (pACB->srb_dmat) bus_dma_tag_destroy(pACB->srb_dmat); if (pACB->buffer_dmat) bus_dma_tag_destroy(pACB->buffer_dmat); return (NULL); } static int trm_attach(device_t dev) { struct cam_devq *device_Q; u_long device_id; PACB pACB = 0; int rid = 0; int unit = device_get_unit(dev); device_id = pci_get_devid(dev); /* * These cards do not allow memory mapped accesses */ if ((pACB = trm_init((u_int16_t) unit, dev)) == NULL) { printf("trm%d: trm_init error!\n",unit); return (ENXIO); } /* After setting up the adapter, map our interrupt */ /* * Now let the CAM generic SCSI layer find the SCSI devices on the bus * start queue to reset to the idle loop. * Create device queue of SIM(s) * (MAX_START_JOB - 1) : max_sim_transactions */ pACB->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (pACB->irq == NULL || bus_setup_intr(dev, pACB->irq, INTR_TYPE_CAM, trm_Interrupt, pACB, &pACB->ih)) { printf("trm%d: register Interrupt handler error!\n", unit); goto bad; } device_Q = cam_simq_alloc(TRM_MAX_START_JOB); if (device_Q == NULL){ printf("trm%d: device_Q == NULL !\n",unit); goto bad; } /* * Now tell the generic SCSI layer * about our bus. * If this is the xpt layer creating a sim, then it's OK * to wait for an allocation. * XXX Should we pass in a flag to indicate that wait is OK? * * SIM allocation * * SCSI Interface Modules * The sim driver creates a sim for each controller. The sim device * queue is separately created in order to allow resource sharing betwee * sims. For instance, a driver may create one sim for each channel of * a multi-channel controller and use the same queue for each channel. * In this way, the queue resources are shared across all the channels * of the multi-channel controller. * trm_action : sim_action_func * trm_poll : sim_poll_func * "trm" : sim_name ,if sim_name = "xpt" ..M_DEVBUF,M_WAITOK * pACB : *softc if sim_name <> "xpt" ..M_DEVBUF,M_NOWAIT * pACB->unit : unit * 1 : max_dev_transactions * MAX_TAGS : max_tagged_dev_transactions * * *******Construct our first channel SIM entry */ pACB->psim = cam_sim_alloc(trm_action, trm_poll, "trm", pACB, unit, 1, TRM_MAX_TAGS_CMD_QUEUE, device_Q); if (pACB->psim == NULL) { printf("trm%d: SIM allocate fault !\n",unit); cam_simq_free(device_Q); /* SIM allocate fault*/ goto bad; } if (xpt_bus_register(pACB->psim, 0) != CAM_SUCCESS) { printf("trm%d: xpt_bus_register fault !\n",unit); goto bad; } if (xpt_create_path(&pACB->ppath, NULL, cam_sim_path(pACB->psim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { printf("trm%d: xpt_create_path fault !\n",unit); xpt_bus_deregister(cam_sim_path(pACB->psim)); goto bad; } return (0); bad: if (pACB->iores) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, pACB->iores); if (pACB->srb_dmamap) { bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap); bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB, pACB->srb_dmamap); bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap); } if (pACB->srb_dmat) bus_dma_tag_destroy(pACB->srb_dmat); if (pACB->sense_dmamap) { bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap); bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers, pACB->sense_dmamap); bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap); } if (pACB->sense_dmat) bus_dma_tag_destroy(pACB->sense_dmat); if (pACB->buffer_dmat) bus_dma_tag_destroy(pACB->buffer_dmat); if (pACB->ih) bus_teardown_intr(dev, pACB->irq, pACB->ih); if (pACB->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, pACB->irq); if (pACB->psim) cam_sim_free(pACB->psim, TRUE); return (ENXIO); } /* * pci_device * trm_probe (device_t tag, pcidi_t type) * */ static int trm_probe(device_t dev) { switch (pci_get_devid(dev)) { case PCI_DEVICEID_TRMS1040: device_set_desc(dev, "Tekram DC395U/UW/F DC315/U Fast20 Wide SCSI Adapter"); return (0); case PCI_DEVICEID_TRMS2080: device_set_desc(dev, "Tekram DC395U2D/U2W Fast40 Wide SCSI Adapter"); return 0; default: return (ENXIO); } } static int trm_detach(device_t dev) { PACB pACB = device_get_softc(dev); bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, pACB->iores); bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap); bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB, pACB->srb_dmamap); bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap); bus_dma_tag_destroy(pACB->srb_dmat); bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap); bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers, pACB->sense_dmamap); bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap); bus_dma_tag_destroy(pACB->sense_dmat); bus_dma_tag_destroy(pACB->buffer_dmat); bus_teardown_intr(dev, pACB->irq, pACB->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, pACB->irq); xpt_async(AC_LOST_DEVICE, pACB->ppath, NULL); xpt_free_path(pACB->ppath); xpt_bus_deregister(cam_sim_path(pACB->psim)); cam_sim_free(pACB->psim, TRUE); return (0); } static device_method_t trm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, trm_probe), DEVMETHOD(device_attach, trm_attach), DEVMETHOD(device_detach, trm_detach), { 0, 0 } }; static driver_t trm_driver = { "trm", trm_methods, sizeof(struct _ACB) }; static devclass_t trm_devclass; DRIVER_MODULE(trm, pci, trm_driver, trm_devclass, 0, 0); Index: head/sys/pci/ncr.c =================================================================== --- head/sys/pci/ncr.c (revision 116350) +++ head/sys/pci/ncr.c (revision 116351) @@ -1,7159 +1,7140 @@ /************************************************************************** ** ** ** Device driver for the NCR 53C8XX PCI-SCSI-Controller Family. ** **------------------------------------------------------------------------- ** ** Written for 386bsd and FreeBSD by ** Wolfgang Stanglmeier ** Stefan Esser ** **------------------------------------------------------------------------- ** ** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** *************************************************************************** */ #include __FBSDID("$FreeBSD$"); #define NCR_DATE "pl30 98/1/1" #define NCR_VERSION (2) #define MAX_UNITS (16) #define NCR_GETCC_WITHMSG #if defined (__FreeBSD__) && defined(_KERNEL) #include "opt_ncr.h" #endif /*========================================================== ** ** Configuration and Debugging ** ** May be overwritten in ** **========================================================== */ /* ** SCSI address of this device. ** The boot routines should have set it. ** If not, use this. */ #ifndef SCSI_NCR_MYADDR #define SCSI_NCR_MYADDR (7) #endif /* SCSI_NCR_MYADDR */ /* ** The default synchronous period factor ** (0=asynchronous) ** If maximum synchronous frequency is defined, use it instead. */ #ifndef SCSI_NCR_MAX_SYNC #ifndef SCSI_NCR_DFLT_SYNC #define SCSI_NCR_DFLT_SYNC (12) #endif /* SCSI_NCR_DFLT_SYNC */ #else #if SCSI_NCR_MAX_SYNC == 0 #define SCSI_NCR_DFLT_SYNC 0 #else #define SCSI_NCR_DFLT_SYNC (250000 / SCSI_NCR_MAX_SYNC) #endif #endif /* ** The minimal asynchronous pre-scaler period (ns) ** Shall be 40. */ #ifndef SCSI_NCR_MIN_ASYNC #define SCSI_NCR_MIN_ASYNC (40) #endif /* SCSI_NCR_MIN_ASYNC */ /* ** The maximal bus with (in log2 byte) ** (0=8 bit, 1=16 bit) */ #ifndef SCSI_NCR_MAX_WIDE #define SCSI_NCR_MAX_WIDE (1) #endif /* SCSI_NCR_MAX_WIDE */ /*========================================================== ** ** Configuration and Debugging ** **========================================================== */ /* ** Number of targets supported by the driver. ** n permits target numbers 0..n-1. ** Default is 7, meaning targets #0..#6. ** #7 .. is myself. */ #define MAX_TARGET (16) /* ** Number of logic units supported by the driver. ** n enables logic unit numbers 0..n-1. ** The common SCSI devices require only ** one lun, so take 1 as the default. */ #ifndef MAX_LUN #define MAX_LUN (8) #endif /* MAX_LUN */ /* ** The maximum number of jobs scheduled for starting. ** There should be one slot per target, and one slot ** for each tag of each target in use. */ #define MAX_START (256) /* ** The maximum number of segments a transfer is split into. */ #define MAX_SCATTER (33) /* ** The maximum transfer length (should be >= 64k). ** MUST NOT be greater than (MAX_SCATTER-1) * PAGE_SIZE. */ #define MAX_SIZE ((MAX_SCATTER-1) * (long) PAGE_SIZE) /* ** other */ #define NCR_SNOOP_TIMEOUT (1000000) /*========================================================== ** ** Include files ** **========================================================== */ #include #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include /*========================================================== ** ** Debugging tags ** **========================================================== */ #define DEBUG_ALLOC (0x0001) #define DEBUG_PHASE (0x0002) #define DEBUG_POLL (0x0004) #define DEBUG_QUEUE (0x0008) #define DEBUG_RESULT (0x0010) #define DEBUG_SCATTER (0x0020) #define DEBUG_SCRIPT (0x0040) #define DEBUG_TINY (0x0080) #define DEBUG_TIMING (0x0100) #define DEBUG_NEGO (0x0200) #define DEBUG_TAGS (0x0400) #define DEBUG_FREEZE (0x0800) #define DEBUG_RESTART (0x1000) /* ** Enable/Disable debug messages. ** Can be changed at runtime too. */ #ifdef SCSI_NCR_DEBUG #define DEBUG_FLAGS ncr_debug #else /* SCSI_NCR_DEBUG */ #define SCSI_NCR_DEBUG 0 #define DEBUG_FLAGS 0 #endif /* SCSI_NCR_DEBUG */ /*========================================================== ** ** assert () ** **========================================================== ** ** modified copy from 386bsd:/usr/include/sys/assert.h ** **---------------------------------------------------------- */ #ifdef DIAGNOSTIC #define assert(expression) { \ if (!(expression)) { \ (void)printf("assertion \"%s\" failed: " \ "file \"%s\", line %d\n", \ #expression, __FILE__, __LINE__); \ Debugger(""); \ } \ } #else #define assert(expression) { \ if (!(expression)) { \ (void)printf("assertion \"%s\" failed: " \ "file \"%s\", line %d\n", \ #expression, __FILE__, __LINE__); \ } \ } #endif /*========================================================== ** ** Access to the controller chip. ** **========================================================== */ #ifdef __alpha__ /* XXX */ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) #endif #define INB(r) bus_space_read_1(np->bst, np->bsh, offsetof(struct ncr_reg, r)) #define INW(r) bus_space_read_2(np->bst, np->bsh, offsetof(struct ncr_reg, r)) #define INL(r) bus_space_read_4(np->bst, np->bsh, offsetof(struct ncr_reg, r)) #define OUTB(r, val) bus_space_write_1(np->bst, np->bsh, \ offsetof(struct ncr_reg, r), val) #define OUTW(r, val) bus_space_write_2(np->bst, np->bsh, \ offsetof(struct ncr_reg, r), val) #define OUTL(r, val) bus_space_write_4(np->bst, np->bsh, \ offsetof(struct ncr_reg, r), val) #define OUTL_OFF(o, val) bus_space_write_4(np->bst, np->bsh, o, val) #define INB_OFF(o) bus_space_read_1(np->bst, np->bsh, o) #define INW_OFF(o) bus_space_read_2(np->bst, np->bsh, o) #define INL_OFF(o) bus_space_read_4(np->bst, np->bsh, o) #define READSCRIPT_OFF(base, off) \ (base ? *((volatile u_int32_t *)((volatile char *)base + (off))) : \ bus_space_read_4(np->bst2, np->bsh2, off)) #define WRITESCRIPT_OFF(base, off, val) \ do { \ if (base) \ *((volatile u_int32_t *) \ ((volatile char *)base + (off))) = (val); \ else \ bus_space_write_4(np->bst2, np->bsh2, off, val); \ } while (0) #define READSCRIPT(r) \ READSCRIPT_OFF(np->script, offsetof(struct script, r)) #define WRITESCRIPT(r, val) \ WRITESCRIPT_OFF(np->script, offsetof(struct script, r), val) /* ** Set bit field ON, OFF */ #define OUTONB(r, m) OUTB(r, INB(r) | (m)) #define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m)) #define OUTONW(r, m) OUTW(r, INW(r) | (m)) #define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m)) #define OUTONL(r, m) OUTL(r, INL(r) | (m)) #define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m)) /*========================================================== ** ** Command control block states. ** **========================================================== */ #define HS_IDLE (0) #define HS_BUSY (1) #define HS_NEGOTIATE (2) /* sync/wide data transfer*/ #define HS_DISCONNECT (3) /* Disconnected by target */ #define HS_COMPLETE (4) #define HS_SEL_TIMEOUT (5) /* Selection timeout */ #define HS_RESET (6) /* SCSI reset */ #define HS_ABORTED (7) /* Transfer aborted */ #define HS_TIMEOUT (8) /* Software timeout */ #define HS_FAIL (9) /* SCSI or PCI bus errors */ #define HS_UNEXPECTED (10) /* Unexpected disconnect */ #define HS_STALL (11) /* QUEUE FULL or BUSY */ #define HS_DONEMASK (0xfc) /*========================================================== ** ** Software Interrupt Codes ** **========================================================== */ #define SIR_SENSE_RESTART (1) #define SIR_SENSE_FAILED (2) #define SIR_STALL_RESTART (3) #define SIR_STALL_QUEUE (4) #define SIR_NEGO_SYNC (5) #define SIR_NEGO_WIDE (6) #define SIR_NEGO_FAILED (7) #define SIR_NEGO_PROTO (8) #define SIR_REJECT_RECEIVED (9) #define SIR_REJECT_SENT (10) #define SIR_IGN_RESIDUE (11) #define SIR_MISSING_SAVE (12) #define SIR_MAX (12) /*========================================================== ** ** Extended error codes. ** xerr_status field of struct nccb. ** **========================================================== */ #define XE_OK (0) #define XE_EXTRA_DATA (1) /* unexpected data phase */ #define XE_BAD_PHASE (2) /* illegal phase (4/5) */ /*========================================================== ** ** Negotiation status. ** nego_status field of struct nccb. ** **========================================================== */ #define NS_SYNC (1) #define NS_WIDE (2) /*========================================================== ** ** XXX These are no longer used. Remove once the ** script is updated. ** "Special features" of targets. ** quirks field of struct tcb. ** actualquirks field of struct nccb. ** **========================================================== */ #define QUIRK_AUTOSAVE (0x01) #define QUIRK_NOMSG (0x02) #define QUIRK_NOSYNC (0x10) #define QUIRK_NOWIDE16 (0x20) #define QUIRK_NOTAGS (0x40) #define QUIRK_UPDATE (0x80) /*========================================================== ** ** Misc. ** **========================================================== */ #define CCB_MAGIC (0xf2691ad2) #define MAX_TAGS (32) /* hard limit */ /*========================================================== ** ** OS dependencies. ** **========================================================== */ #define PRINT_ADDR(ccb) xpt_print_path((ccb)->ccb_h.path) /*========================================================== ** ** Declaration of structs. ** **========================================================== */ struct tcb; struct lcb; struct nccb; struct ncb; struct script; typedef struct ncb * ncb_p; typedef struct tcb * tcb_p; typedef struct lcb * lcb_p; typedef struct nccb * nccb_p; struct link { ncrcmd l_cmd; ncrcmd l_paddr; }; struct usrcmd { u_long target; u_long lun; u_long data; u_long cmd; }; #define UC_SETSYNC 10 #define UC_SETTAGS 11 #define UC_SETDEBUG 12 #define UC_SETORDER 13 #define UC_SETWIDE 14 #define UC_SETFLAG 15 #define UF_TRACE (0x01) /*--------------------------------------- ** ** Timestamps for profiling ** **--------------------------------------- */ /* Type of the kernel variable `ticks'. XXX should be declared with the var. */ typedef int ticks_t; struct tstamp { ticks_t start; ticks_t end; ticks_t select; ticks_t command; ticks_t data; ticks_t status; ticks_t disconnect; }; /* ** profiling data (per device) */ struct profile { u_long num_trans; u_long num_bytes; u_long num_disc; u_long num_break; u_long num_int; u_long num_fly; u_long ms_setup; u_long ms_data; u_long ms_disc; u_long ms_post; }; /*========================================================== ** ** Declaration of structs: target control block ** **========================================================== */ #define NCR_TRANS_CUR 0x01 /* Modify current neogtiation status */ #define NCR_TRANS_ACTIVE 0x03 /* Assume this is the active target */ #define NCR_TRANS_GOAL 0x04 /* Modify negotiation goal */ #define NCR_TRANS_USER 0x08 /* Modify user negotiation settings */ struct ncr_transinfo { u_int8_t width; u_int8_t period; u_int8_t offset; }; struct ncr_target_tinfo { /* Hardware version of our sync settings */ u_int8_t disc_tag; #define NCR_CUR_DISCENB 0x01 #define NCR_CUR_TAGENB 0x02 #define NCR_USR_DISCENB 0x04 #define NCR_USR_TAGENB 0x08 u_int8_t sval; struct ncr_transinfo current; struct ncr_transinfo goal; struct ncr_transinfo user; /* Hardware version of our wide settings */ u_int8_t wval; }; struct tcb { /* ** during reselection the ncr jumps to this point ** with SFBR set to the encoded target number ** with bit 7 set. ** if it's not this target, jump to the next. ** ** JUMP IF (SFBR != #target#) ** @(next tcb) */ struct link jump_tcb; /* ** load the actual values for the sxfer and the scntl3 ** register (sync/wide mode). ** ** SCR_COPY (1); ** @(sval field of this tcb) ** @(sxfer register) ** SCR_COPY (1); ** @(wval field of this tcb) ** @(scntl3 register) */ ncrcmd getscr[6]; /* ** if next message is "identify" ** then load the message to SFBR, ** else load 0 to SFBR. ** ** CALL ** */ struct link call_lun; /* ** now look for the right lun. ** ** JUMP ** @(first nccb of this lun) */ struct link jump_lcb; /* ** pointer to interrupted getcc nccb */ nccb_p hold_cp; /* ** pointer to nccb used for negotiating. ** Avoid to start a nego for all queued commands ** when tagged command queuing is enabled. */ nccb_p nego_cp; /* ** statistical data */ u_long transfers; u_long bytes; /* ** user settable limits for sync transfer ** and tagged commands. */ struct ncr_target_tinfo tinfo; /* ** the lcb's of this tcb */ lcb_p lp[MAX_LUN]; }; /*========================================================== ** ** Declaration of structs: lun control block ** **========================================================== */ struct lcb { /* ** during reselection the ncr jumps to this point ** with SFBR set to the "Identify" message. ** if it's not this lun, jump to the next. ** ** JUMP IF (SFBR != #lun#) ** @(next lcb of this target) */ struct link jump_lcb; /* ** if next message is "simple tag", ** then load the tag to SFBR, ** else load 0 to SFBR. ** ** CALL ** */ struct link call_tag; /* ** now look for the right nccb. ** ** JUMP ** @(first nccb of this lun) */ struct link jump_nccb; /* ** start of the nccb chain */ nccb_p next_nccb; /* ** Control of tagged queueing */ u_char reqnccbs; u_char reqlink; u_char actlink; u_char usetags; u_char lasttag; }; /*========================================================== ** ** Declaration of structs: COMMAND control block ** **========================================================== ** ** This substructure is copied from the nccb to a ** global address after selection (or reselection) ** and copied back before disconnect. ** ** These fields are accessible to the script processor. ** **---------------------------------------------------------- */ struct head { /* ** Execution of a nccb starts at this point. ** It's a jump to the "SELECT" label ** of the script. ** ** After successful selection the script ** processor overwrites it with a jump to ** the IDLE label of the script. */ struct link launch; /* ** Saved data pointer. ** Points to the position in the script ** responsible for the actual transfer ** of data. ** It's written after reception of a ** "SAVE_DATA_POINTER" message. ** The goalpointer points after ** the last transfer command. */ u_int32_t savep; u_int32_t lastp; u_int32_t goalp; /* ** The virtual address of the nccb ** containing this header. */ nccb_p cp; /* ** space for some timestamps to gather ** profiling data about devices and this driver. */ struct tstamp stamp; /* ** status fields. */ u_char status[8]; }; /* ** The status bytes are used by the host and the script processor. ** ** The first four byte are copied to the scratchb register ** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect, ** and copied back just after disconnecting. ** Inside the script the XX_REG are used. ** ** The last four bytes are used inside the script by "COPY" commands. ** Because source and destination must have the same alignment ** in a longword, the fields HAVE to be at the choosen offsets. ** xerr_st (4) 0 (0x34) scratcha ** sync_st (5) 1 (0x05) sxfer ** wide_st (7) 3 (0x03) scntl3 */ /* ** First four bytes (script) */ #define QU_REG scr0 #define HS_REG scr1 #define HS_PRT nc_scr1 #define SS_REG scr2 #define PS_REG scr3 /* ** First four bytes (host) */ #define actualquirks phys.header.status[0] #define host_status phys.header.status[1] #define s_status phys.header.status[2] #define parity_status phys.header.status[3] /* ** Last four bytes (script) */ #define xerr_st header.status[4] /* MUST be ==0 mod 4 */ #define sync_st header.status[5] /* MUST be ==1 mod 4 */ #define nego_st header.status[6] #define wide_st header.status[7] /* MUST be ==3 mod 4 */ /* ** Last four bytes (host) */ #define xerr_status phys.xerr_st #define sync_status phys.sync_st #define nego_status phys.nego_st #define wide_status phys.wide_st /*========================================================== ** ** Declaration of structs: Data structure block ** **========================================================== ** ** During execution of a nccb by the script processor, ** the DSA (data structure address) register points ** to this substructure of the nccb. ** This substructure contains the header with ** the script-processor-changable data and ** data blocks for the indirect move commands. ** **---------------------------------------------------------- */ struct dsb { /* ** Header. ** Has to be the first entry, ** because it's jumped to by the ** script processor */ struct head header; /* ** Table data for Script */ struct scr_tblsel select; struct scr_tblmove smsg ; struct scr_tblmove smsg2 ; struct scr_tblmove cmd ; struct scr_tblmove scmd ; struct scr_tblmove sense ; struct scr_tblmove data [MAX_SCATTER]; }; /*========================================================== ** ** Declaration of structs: Command control block. ** **========================================================== ** ** During execution of a nccb by the script processor, ** the DSA (data structure address) register points ** to this substructure of the nccb. ** This substructure contains the header with ** the script-processor-changable data and then ** data blocks for the indirect move commands. ** **---------------------------------------------------------- */ struct nccb { /* ** This filler ensures that the global header is ** cache line size aligned. */ ncrcmd filler[4]; /* ** during reselection the ncr jumps to this point. ** If a "SIMPLE_TAG" message was received, ** then SFBR is set to the tag. ** else SFBR is set to 0 ** If looking for another tag, jump to the next nccb. ** ** JUMP IF (SFBR != #TAG#) ** @(next nccb of this lun) */ struct link jump_nccb; /* ** After execution of this call, the return address ** (in the TEMP register) points to the following ** data structure block. ** So copy it to the DSA register, and start ** processing of this data structure. ** ** CALL ** */ struct link call_tmp; /* ** This is the data structure which is ** to be executed by the script processor. */ struct dsb phys; /* ** If a data transfer phase is terminated too early ** (after reception of a message (i.e. DISCONNECT)), ** we have to prepare a mini script to transfer ** the rest of the data. */ ncrcmd patch[8]; /* ** The general SCSI driver provides a ** pointer to a control block. */ union ccb *ccb; /* ** We prepare a message to be sent after selection, ** and a second one to be sent after getcc selection. ** Contents are IDENTIFY and SIMPLE_TAG. ** While negotiating sync or wide transfer, ** a SDTM or WDTM message is appended. */ u_char scsi_smsg [8]; u_char scsi_smsg2[8]; /* ** Lock this nccb. ** Flag is used while looking for a free nccb. */ u_long magic; /* ** Physical address of this instance of nccb */ u_long p_nccb; /* ** Completion time out for this job. ** It's set to time of start + allowed number of seconds. */ time_t tlimit; /* ** All nccbs of one hostadapter are chained. */ nccb_p link_nccb; /* ** All nccbs of one target/lun are chained. */ nccb_p next_nccb; /* ** Sense command */ u_char sensecmd[6]; /* ** Tag for this transfer. ** It's patched into jump_nccb. ** If it's not zero, a SIMPLE_TAG ** message is included in smsg. */ u_char tag; }; #define CCB_PHYS(cp,lbl) (cp->p_nccb + offsetof(struct nccb, lbl)) /*========================================================== ** ** Declaration of structs: NCR device descriptor ** **========================================================== */ struct ncb { /* ** The global header. ** Accessible to both the host and the ** script-processor. ** We assume it is cache line size aligned. */ struct head header; int unit; /*----------------------------------------------- ** Scripts .. **----------------------------------------------- ** ** During reselection the ncr jumps to this point. ** The SFBR register is loaded with the encoded target id. ** ** Jump to the first target. ** ** JUMP ** @(next tcb) */ struct link jump_tcb; /*----------------------------------------------- ** Configuration .. **----------------------------------------------- ** ** virtual and physical addresses ** of the 53c810 chip. */ int reg_rid; struct resource *reg_res; bus_space_tag_t bst; bus_space_handle_t bsh; int sram_rid; struct resource *sram_res; bus_space_tag_t bst2; bus_space_handle_t bsh2; struct resource *irq_res; void *irq_handle; /* ** Scripts instance virtual address. */ struct script *script; struct scripth *scripth; /* ** Scripts instance physical address. */ u_long p_script; u_long p_scripth; /* ** The SCSI address of the host adapter. */ u_char myaddr; /* ** timing parameters */ u_char minsync; /* Minimum sync period factor */ u_char maxsync; /* Maximum sync period factor */ u_char maxoffs; /* Max scsi offset */ u_char clock_divn; /* Number of clock divisors */ u_long clock_khz; /* SCSI clock frequency in KHz */ u_long features; /* Chip features map */ u_char multiplier; /* Clock multiplier (1,2,4) */ u_char maxburst; /* log base 2 of dwords burst */ /* ** BIOS supplied PCI bus options */ u_char rv_scntl3; u_char rv_dcntl; u_char rv_dmode; u_char rv_ctest3; u_char rv_ctest4; u_char rv_ctest5; u_char rv_gpcntl; u_char rv_stest2; /*----------------------------------------------- ** CAM SIM information for this instance **----------------------------------------------- */ struct cam_sim *sim; struct cam_path *path; /*----------------------------------------------- ** Job control **----------------------------------------------- ** ** Commands from user */ struct usrcmd user; /* ** Target data */ struct tcb target[MAX_TARGET]; /* ** Start queue. */ u_int32_t squeue [MAX_START]; u_short squeueput; /* ** Timeout handler */ time_t heartbeat; u_short ticks; u_short latetime; time_t lasttime; struct callout_handle timeout_ch; /*----------------------------------------------- ** Debug and profiling **----------------------------------------------- ** ** register dump */ struct ncr_reg regdump; time_t regtime; /* ** Profiling data */ struct profile profile; u_long disc_phys; u_long disc_ref; /* ** Head of list of all nccbs for this controller. */ nccb_p link_nccb; /* ** message buffers. ** Should be longword aligned, ** because they're written with a ** COPY script command. */ u_char msgout[8]; u_char msgin [8]; u_int32_t lastmsg; /* ** Buffer for STATUS_IN phase. */ u_char scratch; /* ** controller chip dependent maximal transfer width. */ u_char maxwide; #ifdef NCR_IOMAPPED /* ** address of the ncr control registers in io space */ pci_port_t port; #endif }; #define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) #define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl)) /*========================================================== ** ** ** Script for NCR-Processor. ** ** Use ncr_script_fill() to create the variable parts. ** Use ncr_script_copy_and_bind() to make a copy and ** bind to physical addresses. ** ** **========================================================== ** ** We have to know the offsets of all labels before ** we reach them (for forward jumps). ** Therefore we declare a struct here. ** If you make changes inside the script, ** DONT FORGET TO CHANGE THE LENGTHS HERE! ** **---------------------------------------------------------- */ /* ** Script fragments which are loaded into the on-board RAM ** of 825A, 875 and 895 chips. */ struct script { ncrcmd start [ 7]; ncrcmd start0 [ 2]; ncrcmd start1 [ 3]; ncrcmd startpos [ 1]; ncrcmd trysel [ 8]; ncrcmd skip [ 8]; ncrcmd skip2 [ 3]; ncrcmd idle [ 2]; ncrcmd select [ 18]; ncrcmd prepare [ 4]; ncrcmd loadpos [ 14]; ncrcmd prepare2 [ 24]; ncrcmd setmsg [ 5]; ncrcmd clrack [ 2]; ncrcmd dispatch [ 33]; ncrcmd no_data [ 17]; ncrcmd checkatn [ 10]; ncrcmd command [ 15]; ncrcmd status [ 27]; ncrcmd msg_in [ 26]; ncrcmd msg_bad [ 6]; ncrcmd complete [ 13]; ncrcmd cleanup [ 12]; ncrcmd cleanup0 [ 9]; ncrcmd signal [ 12]; ncrcmd save_dp [ 5]; ncrcmd restore_dp [ 5]; ncrcmd disconnect [ 12]; ncrcmd disconnect0 [ 5]; ncrcmd disconnect1 [ 23]; ncrcmd msg_out [ 9]; ncrcmd msg_out_done [ 7]; ncrcmd badgetcc [ 6]; ncrcmd reselect [ 8]; ncrcmd reselect1 [ 8]; ncrcmd reselect2 [ 8]; ncrcmd resel_tmp [ 5]; ncrcmd resel_lun [ 18]; ncrcmd resel_tag [ 24]; ncrcmd data_in [MAX_SCATTER * 4 + 7]; ncrcmd data_out [MAX_SCATTER * 4 + 7]; }; /* ** Script fragments which stay in main memory for all chips. */ struct scripth { ncrcmd tryloop [MAX_START*5+2]; ncrcmd msg_parity [ 6]; ncrcmd msg_reject [ 8]; ncrcmd msg_ign_residue [ 32]; ncrcmd msg_extended [ 18]; ncrcmd msg_ext_2 [ 18]; ncrcmd msg_wdtr [ 27]; ncrcmd msg_ext_3 [ 18]; ncrcmd msg_sdtr [ 27]; ncrcmd msg_out_abort [ 10]; ncrcmd getcc [ 4]; ncrcmd getcc1 [ 5]; #ifdef NCR_GETCC_WITHMSG ncrcmd getcc2 [ 29]; #else ncrcmd getcc2 [ 14]; #endif ncrcmd getcc3 [ 6]; ncrcmd aborttag [ 4]; ncrcmd abort [ 22]; ncrcmd snooptest [ 9]; ncrcmd snoopend [ 2]; }; /*========================================================== ** ** ** Function headers. ** ** **========================================================== */ #ifdef _KERNEL static nccb_p ncr_alloc_nccb (ncb_p np, u_long target, u_long lun); static void ncr_complete (ncb_p np, nccb_p cp); static int ncr_delta (int * from, int * to); static void ncr_exception (ncb_p np); static void ncr_free_nccb (ncb_p np, nccb_p cp); static void ncr_freeze_devq (ncb_p np, struct cam_path *path); static void ncr_selectclock (ncb_p np, u_char scntl3); static void ncr_getclock (ncb_p np, u_char multiplier); static nccb_p ncr_get_nccb (ncb_p np, u_long t,u_long l); #if 0 static u_int32_t ncr_info (int unit); #endif static void ncr_init (ncb_p np, char * msg, u_long code); static void ncr_intr (void *vnp); static void ncr_int_ma (ncb_p np, u_char dstat); static void ncr_int_sir (ncb_p np); static void ncr_int_sto (ncb_p np); #if 0 static void ncr_min_phys (struct buf *bp); #endif static void ncr_poll (struct cam_sim *sim); static void ncb_profile (ncb_p np, nccb_p cp); static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len); static void ncr_script_fill (struct script * scr, struct scripth *scrh); static int ncr_scatter (struct dsb* phys, vm_offset_t vaddr, vm_size_t datalen); static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p); static void ncr_setsync (ncb_p np, nccb_p cp,u_char scntl3,u_char sxfer, u_char period); static void ncr_setwide (ncb_p np, nccb_p cp, u_char wide, u_char ack); static int ncr_show_msg (u_char * msg); static int ncr_snooptest (ncb_p np); static void ncr_action (struct cam_sim *sim, union ccb *ccb); static void ncr_timeout (void *arg); static void ncr_wakeup (ncb_p np, u_long code); static int ncr_probe (device_t dev); static int ncr_attach (device_t dev); #endif /* _KERNEL */ /*========================================================== ** ** ** Global static data. ** ** **========================================================== */ static const u_long ncr_version = NCR_VERSION * 11 + (u_long) sizeof (struct ncb) * 7 + (u_long) sizeof (struct nccb) * 5 + (u_long) sizeof (struct lcb) * 3 + (u_long) sizeof (struct tcb) * 2; #ifdef _KERNEL static int ncr_debug = SCSI_NCR_DEBUG; SYSCTL_INT(_debug, OID_AUTO, ncr_debug, CTLFLAG_RW, &ncr_debug, 0, ""); static int ncr_cache; /* to be aligned _NOT_ static */ /*========================================================== ** ** ** Global static data: auto configure ** ** **========================================================== */ #define NCR_810_ID (0x00011000ul) #define NCR_815_ID (0x00041000ul) #define NCR_820_ID (0x00021000ul) #define NCR_825_ID (0x00031000ul) #define NCR_860_ID (0x00061000ul) #define NCR_875_ID (0x000f1000ul) #define NCR_875_ID2 (0x008f1000ul) #define NCR_885_ID (0x000d1000ul) #define NCR_895_ID (0x000c1000ul) #define NCR_896_ID (0x000b1000ul) #define NCR_895A_ID (0x00121000ul) #define NCR_1510D_ID (0x000a1000ul) static char *ncr_name (ncb_p np) { static char name[10]; snprintf(name, sizeof(name), "ncr%d", np->unit); return (name); } /*========================================================== ** ** ** Scripts for NCR-Processor. ** ** Use ncr_script_bind for binding to physical addresses. ** ** **========================================================== ** ** NADDR generates a reference to a field of the controller data. ** PADDR generates a reference to another part of the script. ** RADDR generates a reference to a script processor register. ** FADDR generates a reference to a script processor register ** with offset. ** **---------------------------------------------------------- */ #define RELOC_SOFTC 0x40000000 #define RELOC_LABEL 0x50000000 #define RELOC_REGISTER 0x60000000 #define RELOC_KVAR 0x70000000 #define RELOC_LABELH 0x80000000 #define RELOC_MASK 0xf0000000 #define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label)) #define PADDR(label) (RELOC_LABEL | offsetof(struct script, label)) #define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label)) #define RADDR(label) (RELOC_REGISTER | REG(label)) #define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs))) #define KVAR(which) (RELOC_KVAR | (which)) #define KVAR_SECOND (0) #define KVAR_TICKS (1) #define KVAR_NCR_CACHE (2) #define SCRIPT_KVAR_FIRST (0) #define SCRIPT_KVAR_LAST (3) /* * Kernel variables referenced in the scripts. * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY. */ static void *script_kvars[] = { &time_second, &ticks, &ncr_cache }; static struct script script0 = { /*--------------------------< START >-----------------------*/ { /* ** Claim to be still alive ... */ SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)), KVAR (KVAR_SECOND), NADDR (heartbeat), /* ** Make data structure address invalid. ** clear SIGP. */ SCR_LOAD_REG (dsa, 0xff), 0, SCR_FROM_REG (ctest2), 0, }/*-------------------------< START0 >----------------------*/,{ /* ** Hook for interrupted GetConditionCode. ** Will be patched to ... IFTRUE by ** the interrupt handler. */ SCR_INT ^ IFFALSE (0), SIR_SENSE_RESTART, }/*-------------------------< START1 >----------------------*/,{ /* ** Hook for stalled start queue. ** Will be patched to IFTRUE by the interrupt handler. */ SCR_INT ^ IFFALSE (0), SIR_STALL_RESTART, /* ** Then jump to a certain point in tryloop. ** Due to the lack of indirect addressing the code ** is self modifying here. */ SCR_JUMP, }/*-------------------------< STARTPOS >--------------------*/,{ PADDRH(tryloop), }/*-------------------------< TRYSEL >----------------------*/,{ /* ** Now: ** DSA: Address of a Data Structure ** or Address of the IDLE-Label. ** ** TEMP: Address of a script, which tries to ** start the NEXT entry. ** ** Save the TEMP register into the SCRATCHA register. ** Then copy the DSA to TEMP and RETURN. ** This is kind of an indirect jump. ** (The script processor has NO stack, so the ** CALL is actually a jump and link, and the ** RETURN is an indirect jump.) ** ** If the slot was empty, DSA contains the address ** of the IDLE part of this script. The processor ** jumps to IDLE and waits for a reselect. ** It will wake up and try the same slot again ** after the SIGP bit becomes set by the host. ** ** If the slot was not empty, DSA contains ** the address of the phys-part of a nccb. ** The processor jumps to this address. ** phys starts with head, ** head starts with launch, ** so actually the processor jumps to ** the lauch part. ** If the entry is scheduled for execution, ** then launch contains a jump to SELECT. ** If it's not scheduled, it contains a jump to IDLE. */ SCR_COPY (4), RADDR (temp), RADDR (scratcha), SCR_COPY (4), RADDR (dsa), RADDR (temp), SCR_RETURN, 0 }/*-------------------------< SKIP >------------------------*/,{ /* ** This entry has been canceled. ** Next time use the next slot. */ SCR_COPY (4), RADDR (scratcha), PADDR (startpos), /* ** patch the launch field. ** should look like an idle process. */ SCR_COPY_F (4), RADDR (dsa), PADDR (skip2), SCR_COPY (8), PADDR (idle), }/*-------------------------< SKIP2 >-----------------------*/,{ 0, SCR_JUMP, PADDR(start), }/*-------------------------< IDLE >------------------------*/,{ /* ** Nothing to do? ** Wait for reselect. */ SCR_JUMP, PADDR(reselect), }/*-------------------------< SELECT >----------------------*/,{ /* ** DSA contains the address of a scheduled ** data structure. ** ** SCRATCHA contains the address of the script, ** which starts the next entry. ** ** Set Initiator mode. ** ** (Target mode is left as an exercise for the reader) */ SCR_CLR (SCR_TRG), 0, SCR_LOAD_REG (HS_REG, 0xff), 0, /* ** And try to select this target. */ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), PADDR (reselect), /* ** Now there are 4 possibilities: ** ** (1) The ncr looses arbitration. ** This is ok, because it will try again, ** when the bus becomes idle. ** (But beware of the timeout function!) ** ** (2) The ncr is reselected. ** Then the script processor takes the jump ** to the RESELECT label. ** ** (3) The ncr completes the selection. ** Then it will execute the next statement. ** ** (4) There is a selection timeout. ** Then the ncr should interrupt the host and stop. ** Unfortunately, it seems to continue execution ** of the script. But it will fail with an ** IID-interrupt on the next WHEN. */ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), 0, /* ** Send the IDENTIFY and SIMPLE_TAG messages ** (and the MSG_EXT_SDTR message) */ SCR_MOVE_TBL ^ SCR_MSG_OUT, offsetof (struct dsb, smsg), #ifdef undef /* XXX better fail than try to deal with this ... */ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)), -16, #endif SCR_CLR (SCR_ATN), 0, SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), /* ** Selection complete. ** Next time use the next slot. */ SCR_COPY (4), RADDR (scratcha), PADDR (startpos), }/*-------------------------< PREPARE >----------------------*/,{ /* ** The ncr doesn't have an indirect load ** or store command. So we have to ** copy part of the control block to a ** fixed place, where we can access it. ** ** We patch the address part of a ** COPY command with the DSA-register. */ SCR_COPY_F (4), RADDR (dsa), PADDR (loadpos), /* ** then we do the actual copy. */ SCR_COPY (sizeof (struct head)), /* ** continued after the next label ... */ }/*-------------------------< LOADPOS >---------------------*/,{ 0, NADDR (header), /* ** Mark this nccb as not scheduled. */ SCR_COPY (8), PADDR (idle), NADDR (header.launch), /* ** Set a time stamp for this selection */ SCR_COPY (sizeof (ticks)), KVAR (KVAR_TICKS), NADDR (header.stamp.select), /* ** load the savep (saved pointer) into ** the TEMP register (actual pointer) */ SCR_COPY (4), NADDR (header.savep), RADDR (temp), /* ** Initialize the status registers */ SCR_COPY (4), NADDR (header.status), RADDR (scr0), }/*-------------------------< PREPARE2 >---------------------*/,{ /* ** Load the synchronous mode register */ SCR_COPY (1), NADDR (sync_st), RADDR (sxfer), /* ** Load the wide mode and timing register */ SCR_COPY (1), NADDR (wide_st), RADDR (scntl3), /* ** Initialize the msgout buffer with a NOOP message. */ SCR_LOAD_REG (scratcha, MSG_NOOP), 0, SCR_COPY (1), RADDR (scratcha), NADDR (msgout), SCR_COPY (1), RADDR (scratcha), NADDR (msgin), /* ** Message in phase ? */ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** Extended or reject message ? */ SCR_FROM_REG (sbdl), 0, SCR_JUMP ^ IFTRUE (DATA (MSG_EXTENDED)), PADDR (msg_in), SCR_JUMP ^ IFTRUE (DATA (MSG_MESSAGE_REJECT)), PADDRH (msg_reject), /* ** normal processing */ SCR_JUMP, PADDR (dispatch), }/*-------------------------< SETMSG >----------------------*/,{ SCR_COPY (1), RADDR (scratcha), NADDR (msgout), SCR_SET (SCR_ATN), 0, }/*-------------------------< CLRACK >----------------------*/,{ /* ** Terminate possible pending message phase. */ SCR_CLR (SCR_ACK), 0, }/*-----------------------< DISPATCH >----------------------*/,{ SCR_FROM_REG (HS_REG), 0, SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), SIR_NEGO_FAILED, /* ** remove bogus output signals */ SCR_REG_REG (socl, SCR_AND, CACK|CATN), 0, SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)), 0, SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)), 0, SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)), PADDR (msg_out), SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)), PADDR (msg_in), SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)), PADDR (command), SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)), PADDR (status), /* ** Discard one illegal phase byte, if required. */ SCR_LOAD_REG (scratcha, XE_BAD_PHASE), 0, SCR_COPY (1), RADDR (scratcha), NADDR (xerr_st), SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)), 8, SCR_MOVE_ABS (1) ^ SCR_ILG_OUT, NADDR (scratch), SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)), 8, SCR_MOVE_ABS (1) ^ SCR_ILG_IN, NADDR (scratch), SCR_JUMP, PADDR (dispatch), }/*-------------------------< NO_DATA >--------------------*/,{ /* ** The target wants to tranfer too much data ** or in the wrong direction. ** Remember that in extended error. */ SCR_LOAD_REG (scratcha, XE_EXTRA_DATA), 0, SCR_COPY (1), RADDR (scratcha), NADDR (xerr_st), /* ** Discard one data byte, if required. */ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)), 8, SCR_MOVE_ABS (1) ^ SCR_DATA_OUT, NADDR (scratch), SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)), 8, SCR_MOVE_ABS (1) ^ SCR_DATA_IN, NADDR (scratch), /* ** .. and repeat as required. */ SCR_CALL, PADDR (dispatch), SCR_JUMP, PADDR (no_data), }/*-------------------------< CHECKATN >--------------------*/,{ /* ** If AAP (bit 1 of scntl0 register) is set ** and a parity error is detected, ** the script processor asserts ATN. ** ** The target should switch to a MSG_OUT phase ** to get the message. */ SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)), PADDR (dispatch), /* ** count it */ SCR_REG_REG (PS_REG, SCR_ADD, 1), 0, /* ** Prepare a MSG_INITIATOR_DET_ERR message ** (initiator detected error). ** The target should retry the transfer. */ SCR_LOAD_REG (scratcha, MSG_INITIATOR_DET_ERR), 0, SCR_JUMP, PADDR (setmsg), }/*-------------------------< COMMAND >--------------------*/,{ /* ** If this is not a GETCC transfer ... */ SCR_FROM_REG (SS_REG), 0, /*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), 28, /* ** ... set a timestamp ... */ SCR_COPY (sizeof (ticks)), KVAR (KVAR_TICKS), NADDR (header.stamp.command), /* ** ... and send the command */ SCR_MOVE_TBL ^ SCR_COMMAND, offsetof (struct dsb, cmd), SCR_JUMP, PADDR (dispatch), /* ** Send the GETCC command */ /*>>>*/ SCR_MOVE_TBL ^ SCR_COMMAND, offsetof (struct dsb, scmd), SCR_JUMP, PADDR (dispatch), }/*-------------------------< STATUS >--------------------*/,{ /* ** set the timestamp. */ SCR_COPY (sizeof (ticks)), KVAR (KVAR_TICKS), NADDR (header.stamp.status), /* ** If this is a GETCC transfer, */ SCR_FROM_REG (SS_REG), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (SCSI_STATUS_CHECK_COND)), 40, /* ** get the status */ SCR_MOVE_ABS (1) ^ SCR_STATUS, NADDR (scratch), /* ** Save status to scsi_status. ** Mark as complete. ** And wait for disconnect. */ SCR_TO_REG (SS_REG), 0, SCR_REG_REG (SS_REG, SCR_OR, SCSI_STATUS_SENSE), 0, SCR_LOAD_REG (HS_REG, HS_COMPLETE), 0, SCR_JUMP, PADDR (checkatn), /* ** If it was no GETCC transfer, ** save the status to scsi_status. */ /*>>>*/ SCR_MOVE_ABS (1) ^ SCR_STATUS, NADDR (scratch), SCR_TO_REG (SS_REG), 0, /* ** if it was no check condition ... */ SCR_JUMP ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), PADDR (checkatn), /* ** ... mark as complete. */ SCR_LOAD_REG (HS_REG, HS_COMPLETE), 0, SCR_JUMP, PADDR (checkatn), }/*-------------------------< MSG_IN >--------------------*/,{ /* ** Get the first byte of the message ** and save it to SCRATCHA. ** ** The script processor doesn't negate the ** ACK signal after this transfer. */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[0]), /* ** Check for message parity error. */ SCR_TO_REG (scratcha), 0, SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, /* ** Parity was ok, handle this message. */ SCR_JUMP ^ IFTRUE (DATA (MSG_CMDCOMPLETE)), PADDR (complete), SCR_JUMP ^ IFTRUE (DATA (MSG_SAVEDATAPOINTER)), PADDR (save_dp), SCR_JUMP ^ IFTRUE (DATA (MSG_RESTOREPOINTERS)), PADDR (restore_dp), SCR_JUMP ^ IFTRUE (DATA (MSG_DISCONNECT)), PADDR (disconnect), SCR_JUMP ^ IFTRUE (DATA (MSG_EXTENDED)), PADDRH (msg_extended), SCR_JUMP ^ IFTRUE (DATA (MSG_NOOP)), PADDR (clrack), SCR_JUMP ^ IFTRUE (DATA (MSG_MESSAGE_REJECT)), PADDRH (msg_reject), SCR_JUMP ^ IFTRUE (DATA (MSG_IGN_WIDE_RESIDUE)), PADDRH (msg_ign_residue), /* ** Rest of the messages left as ** an exercise ... ** ** Unimplemented messages: ** fall through to MSG_BAD. */ }/*-------------------------< MSG_BAD >------------------*/,{ /* ** unimplemented message - reject it. */ SCR_INT, SIR_REJECT_SENT, SCR_LOAD_REG (scratcha, MSG_MESSAGE_REJECT), 0, SCR_JUMP, PADDR (setmsg), }/*-------------------------< COMPLETE >-----------------*/,{ /* ** Complete message. ** ** If it's not the get condition code, ** copy TEMP register to LASTP in header. */ SCR_FROM_REG (SS_REG), 0, /*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (SCSI_STATUS_SENSE, SCSI_STATUS_SENSE)), 12, SCR_COPY (4), RADDR (temp), NADDR (header.lastp), /*>>>*/ /* ** When we terminate the cycle by clearing ACK, ** the target may disconnect immediately. ** ** We don't want to be told of an ** "unexpected disconnect", ** so we disable this feature. */ SCR_REG_REG (scntl2, SCR_AND, 0x7f), 0, /* ** Terminate cycle ... */ SCR_CLR (SCR_ACK|SCR_ATN), 0, /* ** ... and wait for the disconnect. */ SCR_WAIT_DISC, 0, }/*-------------------------< CLEANUP >-------------------*/,{ /* ** dsa: Pointer to nccb ** or xxxxxxFF (no nccb) ** ** HS_REG: Host-Status (<>0!) */ SCR_FROM_REG (dsa), 0, SCR_JUMP ^ IFTRUE (DATA (0xff)), PADDR (signal), /* ** dsa is valid. ** save the status registers */ SCR_COPY (4), RADDR (scr0), NADDR (header.status), /* ** and copy back the header to the nccb. */ SCR_COPY_F (4), RADDR (dsa), PADDR (cleanup0), SCR_COPY (sizeof (struct head)), NADDR (header), }/*-------------------------< CLEANUP0 >--------------------*/,{ 0, /* ** If command resulted in "check condition" ** status and is not yet completed, ** try to get the condition code. */ SCR_FROM_REG (HS_REG), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)), 16, SCR_FROM_REG (SS_REG), 0, SCR_JUMP ^ IFTRUE (DATA (SCSI_STATUS_CHECK_COND)), PADDRH(getcc2), }/*-------------------------< SIGNAL >----------------------*/,{ /* ** if status = queue full, ** reinsert in startqueue and stall queue. */ /*>>>*/ SCR_FROM_REG (SS_REG), 0, SCR_INT ^ IFTRUE (DATA (SCSI_STATUS_QUEUE_FULL)), SIR_STALL_QUEUE, /* ** And make the DSA register invalid. */ SCR_LOAD_REG (dsa, 0xff), /* invalid */ 0, /* ** if job completed ... */ SCR_FROM_REG (HS_REG), 0, /* ** ... signal completion to the host */ SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)), 0, /* ** Auf zu neuen Schandtaten! */ SCR_JUMP, PADDR(start), }/*-------------------------< SAVE_DP >------------------*/,{ /* ** SAVE_DP message: ** Copy TEMP register to SAVEP in header. */ SCR_COPY (4), RADDR (temp), NADDR (header.savep), SCR_JUMP, PADDR (clrack), }/*-------------------------< RESTORE_DP >---------------*/,{ /* ** RESTORE_DP message: ** Copy SAVEP in header to TEMP register. */ SCR_COPY (4), NADDR (header.savep), RADDR (temp), SCR_JUMP, PADDR (clrack), }/*-------------------------< DISCONNECT >---------------*/,{ /* ** If QUIRK_AUTOSAVE is set, ** do a "save pointer" operation. */ SCR_FROM_REG (QU_REG), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)), 12, /* ** like SAVE_DP message: ** Copy TEMP register to SAVEP in header. */ SCR_COPY (4), RADDR (temp), NADDR (header.savep), /*>>>*/ /* ** Check if temp==savep or temp==goalp: ** if not, log a missing save pointer message. ** In fact, it's a comparison mod 256. ** ** Hmmm, I hadn't thought that I would be urged to ** write this kind of ugly self modifying code. ** ** It's unbelievable, but the ncr53c8xx isn't able ** to subtract one register from another. */ SCR_FROM_REG (temp), 0, /* ** You are not expected to understand this .. ** ** CAUTION: only little endian architectures supported! XXX */ SCR_COPY_F (1), NADDR (header.savep), PADDR (disconnect0), }/*-------------------------< DISCONNECT0 >--------------*/,{ /*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (1)), 20, /* ** neither this */ SCR_COPY_F (1), NADDR (header.goalp), PADDR (disconnect1), }/*-------------------------< DISCONNECT1 >--------------*/,{ SCR_INT ^ IFFALSE (DATA (1)), SIR_MISSING_SAVE, /*>>>*/ /* ** DISCONNECTing ... ** ** disable the "unexpected disconnect" feature, ** and remove the ACK signal. */ SCR_REG_REG (scntl2, SCR_AND, 0x7f), 0, SCR_CLR (SCR_ACK|SCR_ATN), 0, /* ** Wait for the disconnect. */ SCR_WAIT_DISC, 0, /* ** Profiling: ** Set a time stamp, ** and count the disconnects. */ SCR_COPY (sizeof (ticks)), KVAR (KVAR_TICKS), NADDR (header.stamp.disconnect), SCR_COPY (4), NADDR (disc_phys), RADDR (temp), SCR_REG_REG (temp, SCR_ADD, 0x01), 0, SCR_COPY (4), RADDR (temp), NADDR (disc_phys), /* ** Status is: DISCONNECTED. */ SCR_LOAD_REG (HS_REG, HS_DISCONNECT), 0, SCR_JUMP, PADDR (cleanup), }/*-------------------------< MSG_OUT >-------------------*/,{ /* ** The target requests a message. */ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), /* ** If it was no ABORT message ... */ SCR_JUMP ^ IFTRUE (DATA (MSG_ABORT)), PADDRH (msg_out_abort), /* ** ... wait for the next phase ** if it's a message out, send it again, ... */ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)), PADDR (msg_out), }/*-------------------------< MSG_OUT_DONE >--------------*/,{ /* ** ... else clear the message ... */ SCR_LOAD_REG (scratcha, MSG_NOOP), 0, SCR_COPY (4), RADDR (scratcha), NADDR (msgout), /* ** ... and process the next phase */ SCR_JUMP, PADDR (dispatch), }/*------------------------< BADGETCC >---------------------*/,{ /* ** If SIGP was set, clear it and try again. */ SCR_FROM_REG (ctest2), 0, SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), PADDRH (getcc2), SCR_INT, SIR_SENSE_FAILED, }/*-------------------------< RESELECT >--------------------*/,{ /* ** This NOP will be patched with LED OFF ** SCR_REG_REG (gpreg, SCR_OR, 0x01) */ SCR_NO_OP, 0, /* ** make the DSA invalid. */ SCR_LOAD_REG (dsa, 0xff), 0, SCR_CLR (SCR_TRG), 0, /* ** Sleep waiting for a reselection. ** If SIGP is set, special treatment. ** ** Zu allem bereit .. */ SCR_WAIT_RESEL, PADDR(reselect2), }/*-------------------------< RESELECT1 >--------------------*/,{ /* ** This NOP will be patched with LED ON ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) */ SCR_NO_OP, 0, /* ** ... zu nichts zu gebrauchen ? ** ** load the target id into the SFBR ** and jump to the control block. ** ** Look at the declarations of ** - struct ncb ** - struct tcb ** - struct lcb ** - struct nccb ** to understand what's going on. */ SCR_REG_SFBR (ssid, SCR_AND, 0x8F), 0, SCR_TO_REG (sdid), 0, SCR_JUMP, NADDR (jump_tcb), }/*-------------------------< RESELECT2 >-------------------*/,{ /* ** This NOP will be patched with LED ON ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) */ SCR_NO_OP, 0, /* ** If it's not connected :( ** -> interrupted by SIGP bit. ** Jump to start. */ SCR_FROM_REG (ctest2), 0, SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), PADDR (start), SCR_JUMP, PADDR (reselect), }/*-------------------------< RESEL_TMP >-------------------*/,{ /* ** The return address in TEMP ** is in fact the data structure address, ** so copy it to the DSA register. */ SCR_COPY (4), RADDR (temp), RADDR (dsa), SCR_JUMP, PADDR (prepare), }/*-------------------------< RESEL_LUN >-------------------*/,{ /* ** come back to this point ** to get an IDENTIFY message ** Wait for a msg_in phase. */ /*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), 48, /* ** message phase ** It's not a sony, it's a trick: ** read the data without acknowledging it. */ SCR_FROM_REG (sbdl), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (MSG_IDENTIFYFLAG, 0x98)), 32, /* ** It WAS an Identify message. ** get it and ack it! */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin), SCR_CLR (SCR_ACK), 0, /* ** Mask out the lun. */ SCR_REG_REG (sfbr, SCR_AND, 0x07), 0, SCR_RETURN, 0, /* ** No message phase or no IDENTIFY message: ** return 0. */ /*>>>*/ SCR_LOAD_SFBR (0), 0, SCR_RETURN, 0, }/*-------------------------< RESEL_TAG >-------------------*/,{ /* ** come back to this point ** to get a SIMPLE_TAG message ** Wait for a MSG_IN phase. */ /*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), 64, /* ** message phase ** It's a trick - read the data ** without acknowledging it. */ SCR_FROM_REG (sbdl), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (MSG_SIMPLE_Q_TAG)), 48, /* ** It WAS a SIMPLE_TAG message. ** get it and ack it! */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin), SCR_CLR (SCR_ACK), 0, /* ** Wait for the second byte (the tag) */ /*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), 24, /* ** Get it and ack it! */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin), SCR_CLR (SCR_ACK|SCR_CARRY), 0, SCR_RETURN, 0, /* ** No message phase or no SIMPLE_TAG message ** or no second byte: return 0. */ /*>>>*/ SCR_LOAD_SFBR (0), 0, SCR_SET (SCR_CARRY), 0, SCR_RETURN, 0, }/*-------------------------< DATA_IN >--------------------*/,{ /* ** Because the size depends on the ** #define MAX_SCATTER parameter, ** it is filled in at runtime. ** ** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), ** PADDR (no_data), ** SCR_COPY (sizeof (ticks)), ** KVAR (KVAR_TICKS), ** NADDR (header.stamp.data), ** SCR_MOVE_TBL ^ SCR_DATA_IN, ** offsetof (struct dsb, data[ 0]), ** ** ##===========< i=1; i========= ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), ** || PADDR (checkatn), ** || SCR_MOVE_TBL ^ SCR_DATA_IN, ** || offsetof (struct dsb, data[ i]), ** ##========================================== ** ** SCR_CALL, ** PADDR (checkatn), ** SCR_JUMP, ** PADDR (no_data), */ 0 }/*-------------------------< DATA_OUT >-------------------*/,{ /* ** Because the size depends on the ** #define MAX_SCATTER parameter, ** it is filled in at runtime. ** ** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)), ** PADDR (no_data), ** SCR_COPY (sizeof (ticks)), ** KVAR (KVAR_TICKS), ** NADDR (header.stamp.data), ** SCR_MOVE_TBL ^ SCR_DATA_OUT, ** offsetof (struct dsb, data[ 0]), ** ** ##===========< i=1; i========= ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), ** || PADDR (dispatch), ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, ** || offsetof (struct dsb, data[ i]), ** ##========================================== ** ** SCR_CALL, ** PADDR (dispatch), ** SCR_JUMP, ** PADDR (no_data), ** **--------------------------------------------------------- */ (u_long)0 }/*--------------------------------------------------------*/ }; static struct scripth scripth0 = { /*-------------------------< TRYLOOP >---------------------*/{ /* ** Load an entry of the start queue into dsa ** and try to start it by jumping to TRYSEL. ** ** Because the size depends on the ** #define MAX_START parameter, it is filled ** in at runtime. ** **----------------------------------------------------------- ** ** ##===========< I=0; i=========== ** || SCR_COPY (4), ** || NADDR (squeue[i]), ** || RADDR (dsa), ** || SCR_CALL, ** || PADDR (trysel), ** ##========================================== ** ** SCR_JUMP, ** PADDRH(tryloop), ** **----------------------------------------------------------- */ 0 }/*-------------------------< MSG_PARITY >---------------*/,{ /* ** count it */ SCR_REG_REG (PS_REG, SCR_ADD, 0x01), 0, /* ** send a "message parity error" message. */ SCR_LOAD_REG (scratcha, MSG_PARITY_ERROR), 0, SCR_JUMP, PADDR (setmsg), }/*-------------------------< MSG_MESSAGE_REJECT >---------------*/,{ /* ** If a negotiation was in progress, ** negotiation failed. */ SCR_FROM_REG (HS_REG), 0, SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), SIR_NEGO_FAILED, /* ** else make host log this message */ SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), SIR_REJECT_RECEIVED, SCR_JUMP, PADDR (clrack), }/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ /* ** Terminate cycle */ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get residue size. */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[1]), /* ** Check for message parity error. */ SCR_TO_REG (scratcha), 0, SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, /* ** Size is 0 .. ignore message. */ SCR_JUMP ^ IFTRUE (DATA (0)), PADDR (clrack), /* ** Size is not 1 .. have to interrupt. */ /*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)), 40, /* ** Check for residue byte in swide register */ SCR_FROM_REG (scntl2), 0, /*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), 16, /* ** There IS data in the swide register. ** Discard it. */ SCR_REG_REG (scntl2, SCR_OR, WSR), 0, SCR_JUMP, PADDR (clrack), /* ** Load again the size to the sfbr register. */ /*>>>*/ SCR_FROM_REG (scratcha), 0, /*>>>*/ SCR_INT, SIR_IGN_RESIDUE, SCR_JUMP, PADDR (clrack), }/*-------------------------< MSG_EXTENDED >-------------*/,{ /* ** Terminate cycle */ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get length. */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[1]), /* ** Check for message parity error. */ SCR_TO_REG (scratcha), 0, SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, /* */ SCR_JUMP ^ IFTRUE (DATA (3)), PADDRH (msg_ext_3), SCR_JUMP ^ IFFALSE (DATA (2)), PADDR (msg_bad), }/*-------------------------< MSG_EXT_2 >----------------*/,{ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get extended message code. */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[2]), /* ** Check for message parity error. */ SCR_TO_REG (scratcha), 0, SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, SCR_JUMP ^ IFTRUE (DATA (MSG_EXT_WDTR)), PADDRH (msg_wdtr), /* ** unknown extended message */ SCR_JUMP, PADDR (msg_bad) }/*-------------------------< MSG_WDTR >-----------------*/,{ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get data bus width */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[3]), SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), /* ** let the host do the real work. */ SCR_INT, SIR_NEGO_WIDE, /* ** let the target fetch our answer. */ SCR_SET (SCR_ATN), 0, SCR_CLR (SCR_ACK), 0, SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), SIR_NEGO_PROTO, /* ** Send the MSG_EXT_WDTR */ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, NADDR (msgout), SCR_CLR (SCR_ATN), 0, SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), SCR_JUMP, PADDR (msg_out_done), }/*-------------------------< MSG_EXT_3 >----------------*/,{ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get extended message code. */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[2]), /* ** Check for message parity error. */ SCR_TO_REG (scratcha), 0, SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, SCR_JUMP ^ IFTRUE (DATA (MSG_EXT_SDTR)), PADDRH (msg_sdtr), /* ** unknown extended message */ SCR_JUMP, PADDR (msg_bad) }/*-------------------------< MSG_SDTR >-----------------*/,{ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), /* ** get period and offset */ SCR_MOVE_ABS (2) ^ SCR_MSG_IN, NADDR (msgin[3]), SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), PADDRH (msg_parity), /* ** let the host do the real work. */ SCR_INT, SIR_NEGO_SYNC, /* ** let the target fetch our answer. */ SCR_SET (SCR_ATN), 0, SCR_CLR (SCR_ACK), 0, SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), SIR_NEGO_PROTO, /* ** Send the MSG_EXT_SDTR */ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, NADDR (msgout), SCR_CLR (SCR_ATN), 0, SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), SCR_JUMP, PADDR (msg_out_done), }/*-------------------------< MSG_OUT_ABORT >-------------*/,{ /* ** After ABORT message, ** ** expect an immediate disconnect, ... */ SCR_REG_REG (scntl2, SCR_AND, 0x7f), 0, SCR_CLR (SCR_ACK|SCR_ATN), 0, SCR_WAIT_DISC, 0, /* ** ... and set the status to "ABORTED" */ SCR_LOAD_REG (HS_REG, HS_ABORTED), 0, SCR_JUMP, PADDR (cleanup), }/*-------------------------< GETCC >-----------------------*/,{ /* ** The ncr doesn't have an indirect load ** or store command. So we have to ** copy part of the control block to a ** fixed place, where we can modify it. ** ** We patch the address part of a COPY command ** with the address of the dsa register ... */ SCR_COPY_F (4), RADDR (dsa), PADDRH (getcc1), /* ** ... then we do the actual copy. */ SCR_COPY (sizeof (struct head)), }/*-------------------------< GETCC1 >----------------------*/,{ 0, NADDR (header), /* ** Initialize the status registers */ SCR_COPY (4), NADDR (header.status), RADDR (scr0), }/*-------------------------< GETCC2 >----------------------*/,{ /* ** Get the condition code from a target. ** ** DSA points to a data structure. ** Set TEMP to the script location ** that receives the condition code. ** ** Because there is no script command ** to load a longword into a register, ** we use a CALL command. */ /*<<<*/ SCR_CALLR, 24, /* ** Get the condition code. */ SCR_MOVE_TBL ^ SCR_DATA_IN, offsetof (struct dsb, sense), /* ** No data phase may follow! */ SCR_CALL, PADDR (checkatn), SCR_JUMP, PADDR (no_data), /*>>>*/ /* ** The CALL jumps to this point. ** Prepare for a RESTORE_POINTER message. ** Save the TEMP register into the saved pointer. */ SCR_COPY (4), RADDR (temp), NADDR (header.savep), /* ** Load scratcha, because in case of a selection timeout, ** the host will expect a new value for startpos in ** the scratcha register. */ SCR_COPY (4), PADDR (startpos), RADDR (scratcha), #ifdef NCR_GETCC_WITHMSG /* ** If QUIRK_NOMSG is set, select without ATN. ** and don't send a message. */ SCR_FROM_REG (QU_REG), 0, SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)), PADDRH(getcc3), /* ** Then try to connect to the target. ** If we are reselected, special treatment ** of the current job is required before ** accepting the reselection. */ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), PADDR(badgetcc), /* ** Send the IDENTIFY message. ** In case of short transfer, remove ATN. */ SCR_MOVE_TBL ^ SCR_MSG_OUT, offsetof (struct dsb, smsg2), SCR_CLR (SCR_ATN), 0, /* ** save the first byte of the message. */ SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), SCR_JUMP, PADDR (prepare2), #endif }/*-------------------------< GETCC3 >----------------------*/,{ /* ** Try to connect to the target. ** If we are reselected, special treatment ** of the current job is required before ** accepting the reselection. ** ** Silly target won't accept a message. ** Select without ATN. */ SCR_SEL_TBL ^ offsetof (struct dsb, select), PADDR(badgetcc), /* ** Force error if selection timeout */ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), 0, /* ** don't negotiate. */ SCR_JUMP, PADDR (prepare2), }/*-------------------------< ABORTTAG >-------------------*/,{ /* ** Abort a bad reselection. ** Set the message to ABORT vs. ABORT_TAG */ SCR_LOAD_REG (scratcha, MSG_ABORT_TAG), 0, SCR_JUMPR ^ IFFALSE (CARRYSET), 8, }/*-------------------------< ABORT >----------------------*/,{ SCR_LOAD_REG (scratcha, MSG_ABORT), 0, SCR_COPY (1), RADDR (scratcha), NADDR (msgout), SCR_SET (SCR_ATN), 0, SCR_CLR (SCR_ACK), 0, /* ** and send it. ** we expect an immediate disconnect */ SCR_REG_REG (scntl2, SCR_AND, 0x7f), 0, SCR_MOVE_ABS (1) ^ SCR_MSG_OUT, NADDR (msgout), SCR_COPY (1), RADDR (sfbr), NADDR (lastmsg), SCR_CLR (SCR_ACK|SCR_ATN), 0, SCR_WAIT_DISC, 0, SCR_JUMP, PADDR (start), }/*-------------------------< SNOOPTEST >-------------------*/,{ /* ** Read the variable. */ SCR_COPY (4), KVAR (KVAR_NCR_CACHE), RADDR (scratcha), /* ** Write the variable. */ SCR_COPY (4), RADDR (temp), KVAR (KVAR_NCR_CACHE), /* ** Read back the variable. */ SCR_COPY (4), KVAR (KVAR_NCR_CACHE), RADDR (temp), }/*-------------------------< SNOOPEND >-------------------*/,{ /* ** And stop. */ SCR_INT, 99, }/*--------------------------------------------------------*/ }; /*========================================================== ** ** ** Fill in #define dependent parts of the script ** ** **========================================================== */ static void ncr_script_fill (struct script * scr, struct scripth * scrh) { int i; ncrcmd *p; p = scrh->tryloop; for (i=0; itryloop + sizeof (scrh->tryloop)); p = scr->data_in; *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)); *p++ =PADDR (no_data); *p++ =SCR_COPY (sizeof (ticks)); *p++ =(ncrcmd) KVAR (KVAR_TICKS); *p++ =NADDR (header.stamp.data); *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; *p++ =offsetof (struct dsb, data[ 0]); for (i=1; idata_in + sizeof (scr->data_in)); p = scr->data_out; *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)); *p++ =PADDR (no_data); *p++ =SCR_COPY (sizeof (ticks)); *p++ =(ncrcmd) KVAR (KVAR_TICKS); *p++ =NADDR (header.stamp.data); *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; *p++ =offsetof (struct dsb, data[ 0]); for (i=1; idata_out + sizeof (scr->data_out)); } /*========================================================== ** ** ** Copy and rebind a script. ** ** **========================================================== */ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) { ncrcmd opcode, new, old, tmp1, tmp2; ncrcmd *start, *end; int relocs, offset; start = src; end = src + len/4; offset = 0; while (src < end) { opcode = *src++; WRITESCRIPT_OFF(dst, offset, opcode); offset += 4; /* ** If we forget to change the length ** in struct script, a field will be ** padded with 0. This is an illegal ** command. */ if (opcode == 0) { printf ("%s: ERROR0 IN SCRIPT at %d.\n", ncr_name(np), (int) (src-start-1)); DELAY (1000000); }; if (DEBUG_FLAGS & DEBUG_SCRIPT) printf ("%p: <%x>\n", (src-1), (unsigned)opcode); /* ** We don't have to decode ALL commands */ switch (opcode >> 28) { case 0xc: /* ** COPY has TWO arguments. */ relocs = 2; tmp1 = src[0]; if ((tmp1 & RELOC_MASK) == RELOC_KVAR) tmp1 = 0; tmp2 = src[1]; if ((tmp2 & RELOC_MASK) == RELOC_KVAR) tmp2 = 0; if ((tmp1 ^ tmp2) & 3) { printf ("%s: ERROR1 IN SCRIPT at %d.\n", ncr_name(np), (int) (src-start-1)); DELAY (1000000); } /* ** If PREFETCH feature not enabled, remove ** the NO FLUSH bit if present. */ if ((opcode & SCR_NO_FLUSH) && !(np->features&FE_PFEN)) WRITESCRIPT_OFF(dst, offset - 4, (opcode & ~SCR_NO_FLUSH)); break; case 0x0: /* ** MOVE (absolute address) */ relocs = 1; break; case 0x8: /* ** JUMP / CALL ** dont't relocate if relative :-) */ if (opcode & 0x00800000) relocs = 0; else relocs = 1; break; case 0x4: case 0x5: case 0x6: case 0x7: relocs = 1; break; default: relocs = 0; break; }; if (relocs) { while (relocs--) { old = *src++; switch (old & RELOC_MASK) { case RELOC_REGISTER: new = (old & ~RELOC_MASK) + rman_get_start(np->reg_res); break; case RELOC_LABEL: new = (old & ~RELOC_MASK) + np->p_script; break; case RELOC_LABELH: new = (old & ~RELOC_MASK) + np->p_scripth; break; case RELOC_SOFTC: new = (old & ~RELOC_MASK) + vtophys(np); break; case RELOC_KVAR: if (((old & ~RELOC_MASK) < SCRIPT_KVAR_FIRST) || ((old & ~RELOC_MASK) > SCRIPT_KVAR_LAST)) panic("ncr KVAR out of range"); new = vtophys(script_kvars[old & ~RELOC_MASK]); break; case 0: /* Don't relocate a 0 address. */ if (old == 0) { new = old; break; } /* FALLTHROUGH */ default: panic("ncr_script_copy_and_bind: weird relocation %x @ %d\n", old, (int)(src - start)); break; } WRITESCRIPT_OFF(dst, offset, new); offset += 4; } } else { WRITESCRIPT_OFF(dst, offset, *src++); offset += 4; } }; } /*========================================================== ** ** ** Auto configuration. ** ** **========================================================== */ #if 0 /*---------------------------------------------------------- ** ** Reduce the transfer length to the max value ** we can transfer safely. ** ** Reading a block greater then MAX_SIZE from the ** raw (character) device exercises a memory leak ** in the vm subsystem. This is common to ALL devices. ** We have submitted a description of this bug to ** . ** It should be fixed in the current release. ** **---------------------------------------------------------- */ void ncr_min_phys (struct buf *bp) { if ((unsigned long)bp->b_bcount > MAX_SIZE) bp->b_bcount = MAX_SIZE; } #endif #if 0 /*---------------------------------------------------------- ** ** Maximal number of outstanding requests per target. ** **---------------------------------------------------------- */ u_int32_t ncr_info (int unit) { return (1); /* may be changed later */ } #endif /*---------------------------------------------------------- ** ** NCR chip devices table and chip look up function. ** Features bit are defined in ncrreg.h. Is it the ** right place? ** **---------------------------------------------------------- */ typedef struct { unsigned long device_id; unsigned short minrevid; char *name; unsigned char maxburst; unsigned char maxoffs; unsigned char clock_divn; unsigned int features; } ncr_chip; static ncr_chip ncr_chip_table[] = { {NCR_810_ID, 0x00, "ncr 53c810 fast10 scsi", 4, 8, 4, FE_ERL} , {NCR_810_ID, 0x10, "ncr 53c810a fast10 scsi", 4, 8, 4, FE_ERL|FE_LDSTR|FE_PFEN|FE_BOF} , {NCR_815_ID, 0x00, "ncr 53c815 fast10 scsi", 4, 8, 4, FE_ERL|FE_BOF} , {NCR_820_ID, 0x00, "ncr 53c820 fast10 wide scsi", 4, 8, 4, FE_WIDE|FE_ERL} , {NCR_825_ID, 0x00, "ncr 53c825 fast10 wide scsi", 4, 8, 4, FE_WIDE|FE_ERL|FE_BOF} , {NCR_825_ID, 0x10, "ncr 53c825a fast10 wide scsi", 7, 8, 4, FE_WIDE|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_860_ID, 0x00, "ncr 53c860 fast20 scsi", 4, 8, 5, FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_LDSTR|FE_PFEN} , {NCR_875_ID, 0x00, "ncr 53c875 fast20 wide scsi", 7, 16, 5, FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_875_ID, 0x02, "ncr 53c875 fast20 wide scsi", 7, 16, 5, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_875_ID2, 0x00, "ncr 53c875j fast20 wide scsi", 7, 16, 5, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_885_ID, 0x00, "ncr 53c885 fast20 wide scsi", 7, 16, 5, FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_895_ID, 0x00, "ncr 53c895 fast40 wide scsi", 7, 31, 7, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_896_ID, 0x00, "ncr 53c896 fast40 wide scsi", 7, 31, 7, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_895A_ID, 0x00, "ncr 53c895a fast40 wide scsi", 7, 31, 7, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} , {NCR_1510D_ID, 0x00, "ncr 53c1510d fast40 wide scsi", 7, 31, 7, FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} }; static int ncr_chip_lookup(u_long device_id, u_char revision_id) { int i, found; found = -1; for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) { if (device_id == ncr_chip_table[i].device_id && ncr_chip_table[i].minrevid <= revision_id) { if (found < 0 || ncr_chip_table[found].minrevid < ncr_chip_table[i].minrevid) { found = i; } } } return found; } /*---------------------------------------------------------- ** ** Probe the hostadapter. ** **---------------------------------------------------------- */ static int ncr_probe (device_t dev) { int i; i = ncr_chip_lookup(pci_get_devid(dev), pci_get_revid(dev)); if (i >= 0) { device_set_desc(dev, ncr_chip_table[i].name); return (0); } return (ENXIO); } /*========================================================== ** ** NCR chip clock divisor table. ** Divisors are multiplied by 10,000,000 in order to make ** calculations more simple. ** **========================================================== */ #define _5M 5000000 static u_long div_10M[] = {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M}; /*=============================================================== ** ** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 ** transfers. 32,64,128 are only supported by 875 and 895 chips. ** We use log base 2 (burst length) as internal code, with ** value 0 meaning "burst disabled". ** **=============================================================== */ /* * Burst length from burst code. */ #define burst_length(bc) (!(bc))? 0 : 1 << (bc) /* * Burst code from io register bits. */ #define burst_code(dmode, ctest4, ctest5) \ (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1 /* * Set initial io register bits from burst code. */ static void ncr_init_burst(ncb_p np, u_char bc) { np->rv_ctest4 &= ~0x80; np->rv_dmode &= ~(0x3 << 6); np->rv_ctest5 &= ~0x4; if (!bc) { np->rv_ctest4 |= 0x80; } else { --bc; np->rv_dmode |= ((bc & 0x3) << 6); np->rv_ctest5 |= (bc & 0x4); } } /*========================================================== ** ** ** Auto configuration: attach and init a host adapter. ** ** **========================================================== */ static int ncr_attach (device_t dev) { ncb_p np = (struct ncb*) device_get_softc(dev); u_char rev = 0; u_long period; int i, rid; u_int8_t usrsync; u_int8_t usrwide; struct cam_devq *devq; /* ** allocate and initialize structures. */ np->unit = device_get_unit(dev); /* ** Try to map the controller chip to ** virtual and physical memory. */ np->reg_rid = 0x14; np->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &np->reg_rid, 0, ~0, 1, RF_ACTIVE); if (!np->reg_res) { device_printf(dev, "could not map memory\n"); return ENXIO; } /* ** Make the controller's registers available. ** Now the INB INW INL OUTB OUTW OUTL macros ** can be used safely. */ np->bst = rman_get_bustag(np->reg_res); np->bsh = rman_get_bushandle(np->reg_res); #ifdef NCR_IOMAPPED /* ** Try to map the controller chip into iospace. */ if (!pci_map_port (config_id, 0x10, &np->port)) return; #endif /* ** Save some controller register default values */ np->rv_scntl3 = INB(nc_scntl3) & 0x77; np->rv_dmode = INB(nc_dmode) & 0xce; np->rv_dcntl = INB(nc_dcntl) & 0xa9; np->rv_ctest3 = INB(nc_ctest3) & 0x01; np->rv_ctest4 = INB(nc_ctest4) & 0x88; np->rv_ctest5 = INB(nc_ctest5) & 0x24; np->rv_gpcntl = INB(nc_gpcntl); np->rv_stest2 = INB(nc_stest2) & 0x20; if (bootverbose >= 2) { printf ("\tBIOS values: SCNTL3:%02x DMODE:%02x DCNTL:%02x\n", np->rv_scntl3, np->rv_dmode, np->rv_dcntl); printf ("\t CTEST3:%02x CTEST4:%02x CTEST5:%02x\n", np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); } np->rv_dcntl |= NOCOM; /* ** Do chip dependent initialization. */ rev = pci_get_revid(dev); /* ** Get chip features from chips table. */ i = ncr_chip_lookup(pci_get_devid(dev), rev); if (i >= 0) { np->maxburst = ncr_chip_table[i].maxburst; np->maxoffs = ncr_chip_table[i].maxoffs; np->clock_divn = ncr_chip_table[i].clock_divn; np->features = ncr_chip_table[i].features; } else { /* Should'nt happen if probe() is ok */ np->maxburst = 4; np->maxoffs = 8; np->clock_divn = 4; np->features = FE_ERL; } np->maxwide = np->features & FE_WIDE ? 1 : 0; np->clock_khz = np->features & FE_CLK80 ? 80000 : 40000; if (np->features & FE_QUAD) np->multiplier = 4; else if (np->features & FE_DBLR) np->multiplier = 2; else np->multiplier = 1; /* ** Get the frequency of the chip's clock. ** Find the right value for scntl3. */ if (np->features & (FE_ULTRA|FE_ULTRA2)) ncr_getclock(np, np->multiplier); #ifdef NCR_TEKRAM_EEPROM if (bootverbose) { printf ("%s: Tekram EEPROM read %s\n", ncr_name(np), read_tekram_eeprom (np, NULL) ? "succeeded" : "failed"); } #endif /* NCR_TEKRAM_EEPROM */ /* * If scntl3 != 0, we assume BIOS is present. */ if (np->rv_scntl3) np->features |= FE_BIOS; /* * Divisor to be used for async (timer pre-scaler). */ i = np->clock_divn - 1; while (i >= 0) { --i; if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) { ++i; break; } } np->rv_scntl3 = i+1; /* * Minimum synchronous period factor supported by the chip. * Btw, 'period' is in tenths of nanoseconds. */ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz; if (period <= 250) np->minsync = 10; else if (period <= 303) np->minsync = 11; else if (period <= 500) np->minsync = 12; else np->minsync = (period + 40 - 1) / 40; /* * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2). */ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2))) np->minsync = 25; else if (np->minsync < 12 && !(np->features & FE_ULTRA2)) np->minsync = 12; /* * Maximum synchronous period factor supported by the chip. */ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz); np->maxsync = period > 2540 ? 254 : period / 10; /* * Now, some features available with Symbios compatible boards. * LED support through GPIO0 and DIFF support. */ #ifdef SCSI_NCR_SYMBIOS_COMPAT if (!(np->rv_gpcntl & 0x01)) np->features |= FE_LED0; #if 0 /* Not safe enough without NVRAM support or user settable option */ if (!(INB(nc_gpreg) & 0x08)) np->features |= FE_DIFF; #endif #endif /* SCSI_NCR_SYMBIOS_COMPAT */ /* * Prepare initial IO registers settings. * Trust BIOS only if we believe we have one and if we want to. */ #ifdef SCSI_NCR_TRUST_BIOS if (!(np->features & FE_BIOS)) { #else if (1) { #endif np->rv_dmode = 0; np->rv_dcntl = NOCOM; np->rv_ctest3 = 0; np->rv_ctest4 = MPEE; np->rv_ctest5 = 0; np->rv_stest2 = 0; if (np->features & FE_ERL) np->rv_dmode |= ERL; /* Enable Read Line */ if (np->features & FE_BOF) np->rv_dmode |= BOF; /* Burst Opcode Fetch */ if (np->features & FE_ERMP) np->rv_dmode |= ERMP; /* Enable Read Multiple */ if (np->features & FE_CLSE) np->rv_dcntl |= CLSE; /* Cache Line Size Enable */ if (np->features & FE_WRIE) np->rv_ctest3 |= WRIE; /* Write and Invalidate */ if (np->features & FE_PFEN) np->rv_dcntl |= PFEN; /* Prefetch Enable */ if (np->features & FE_DFS) np->rv_ctest5 |= DFS; /* Dma Fifo Size */ if (np->features & FE_DIFF) np->rv_stest2 |= 0x20; /* Differential mode */ ncr_init_burst(np, np->maxburst); /* Max dwords burst length */ } else { np->maxburst = burst_code(np->rv_dmode, np->rv_ctest4, np->rv_ctest5); } /* ** Get on-chip SRAM address, if supported */ if ((np->features & FE_RAM) && sizeof(struct script) <= 4096) { np->sram_rid = 0x18; np->sram_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &np->sram_rid, 0, ~0, 1, RF_ACTIVE); } /* ** Allocate structure for script relocation. */ if (np->sram_res != NULL) { np->script = NULL; np->p_script = rman_get_start(np->sram_res); np->bst2 = rman_get_bustag(np->sram_res); np->bsh2 = rman_get_bushandle(np->sram_res); } else if (sizeof (struct script) > PAGE_SIZE) { np->script = (struct script*) vm_page_alloc_contig (round_page(sizeof (struct script)), 0, 0xffffffff, PAGE_SIZE); } else { np->script = (struct script *) malloc (sizeof (struct script), M_DEVBUF, M_WAITOK); } /* XXX JGibbs - Use contigmalloc */ if (sizeof (struct scripth) > PAGE_SIZE) { np->scripth = (struct scripth*) vm_page_alloc_contig (round_page(sizeof (struct scripth)), 0, 0xffffffff, PAGE_SIZE); } else { np->scripth = (struct scripth *) malloc (sizeof (struct scripth), M_DEVBUF, M_WAITOK); } #ifdef SCSI_NCR_PCI_CONFIG_FIXUP /* ** If cache line size is enabled, check PCI config space and ** try to fix it up if necessary. */ #ifdef PCIR_CACHELNSZ /* To be sure that new PCI stuff is present */ { u_char cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); u_short command = pci_read_config(dev, PCIR_COMMAND, 2); if (!cachelnsz) { cachelnsz = 8; printf("%s: setting PCI cache line size register to %d.\n", ncr_name(np), (int)cachelnsz); pci_write_config(dev, PCIR_CACHELNSZ, cachelnsz, 1); } if (!(command & (1<<4))) { command |= (1<<4); printf("%s: setting PCI command write and invalidate.\n", ncr_name(np)); pci_write_config(dev, PCIR_COMMAND, command, 2); } } #endif /* PCIR_CACHELNSZ */ #endif /* SCSI_NCR_PCI_CONFIG_FIXUP */ /* Initialize per-target user settings */ usrsync = 0; if (SCSI_NCR_DFLT_SYNC) { usrsync = SCSI_NCR_DFLT_SYNC; if (usrsync > np->maxsync) usrsync = np->maxsync; if (usrsync < np->minsync) usrsync = np->minsync; }; usrwide = (SCSI_NCR_MAX_WIDE); if (usrwide > np->maxwide) usrwide=np->maxwide; for (i=0;itarget[i]; tp->tinfo.user.period = usrsync; tp->tinfo.user.offset = usrsync != 0 ? np->maxoffs : 0; tp->tinfo.user.width = usrwide; tp->tinfo.disc_tag = NCR_CUR_DISCENB | NCR_CUR_TAGENB | NCR_USR_DISCENB | NCR_USR_TAGENB; } /* ** Bells and whistles ;-) */ if (bootverbose) printf("%s: minsync=%d, maxsync=%d, maxoffs=%d, %d dwords burst, %s dma fifo\n", ncr_name(np), np->minsync, np->maxsync, np->maxoffs, burst_length(np->maxburst), (np->rv_ctest5 & DFS) ? "large" : "normal"); /* ** Print some complementary information that can be helpfull. */ if (bootverbose) printf("%s: %s, %s IRQ driver%s\n", ncr_name(np), np->rv_stest2 & 0x20 ? "differential" : "single-ended", np->rv_dcntl & IRQM ? "totem pole" : "open drain", np->sram_res ? ", using on-chip SRAM" : ""); /* ** Patch scripts to physical addresses */ ncr_script_fill (&script0, &scripth0); if (np->script) np->p_script = vtophys(np->script); np->p_scripth = vtophys(np->scripth); ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script, sizeof(struct script)); ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth, sizeof(struct scripth)); /* ** Patch the script for LED support. */ if (np->features & FE_LED0) { WRITESCRIPT(reselect[0], SCR_REG_REG(gpreg, SCR_OR, 0x01)); WRITESCRIPT(reselect1[0], SCR_REG_REG(gpreg, SCR_AND, 0xfe)); WRITESCRIPT(reselect2[0], SCR_REG_REG(gpreg, SCR_AND, 0xfe)); } /* ** init data structure */ np->jump_tcb.l_cmd = SCR_JUMP; np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); /* ** Get SCSI addr of host adapter (set by bios?). */ np->myaddr = INB(nc_scid) & 0x07; if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; #ifdef NCR_DUMP_REG /* ** Log the initial register contents */ { int reg; for (reg=0; reg<256; reg+=4) { if (reg%16==0) printf ("reg[%2x]", reg); printf (" %08x", (int)pci_conf_read (config_id, reg)); if (reg%16==12) printf ("\n"); } } #endif /* NCR_DUMP_REG */ /* ** Reset chip. */ OUTB (nc_istat, SRST); DELAY (1000); OUTB (nc_istat, 0 ); /* ** Now check the cache handling of the pci chipset. */ if (ncr_snooptest (np)) { printf ("CACHE INCORRECTLY CONFIGURED.\n"); return EINVAL; }; /* ** Install the interrupt handler. */ rid = 0; np->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (np->irq_res == NULL) { device_printf(dev, "interruptless mode: reduced performance.\n"); } else { bus_setup_intr(dev, np->irq_res, INTR_TYPE_CAM | INTR_ENTROPY, ncr_intr, np, &np->irq_handle); } /* ** Create the device queue. We only allow MAX_START-1 concurrent ** transactions so we can be sure to have one element free in our ** start queue to reset to the idle loop. */ devq = cam_simq_alloc(MAX_START - 1); if (devq == NULL) return ENOMEM; /* ** Now tell the generic SCSI layer ** about our bus. */ np->sim = cam_sim_alloc(ncr_action, ncr_poll, "ncr", np, np->unit, 1, MAX_TAGS, devq); if (np->sim == NULL) { cam_simq_free(devq); return ENOMEM; } if (xpt_bus_register(np->sim, 0) != CAM_SUCCESS) { cam_sim_free(np->sim, /*free_devq*/ TRUE); return ENOMEM; } if (xpt_create_path(&np->path, /*periph*/NULL, cam_sim_path(np->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(np->sim)); cam_sim_free(np->sim, /*free_devq*/TRUE); return ENOMEM; } /* ** start the timeout daemon */ ncr_timeout (np); np->lasttime=0; return 0; } /*========================================================== ** ** ** Process pending device interrupts. ** ** **========================================================== */ static void ncr_intr(vnp) void *vnp; { ncb_p np = vnp; int oldspl = splcam(); if (DEBUG_FLAGS & DEBUG_TINY) printf ("["); if (INB(nc_istat) & (INTF|SIP|DIP)) { /* ** Repeat until no outstanding ints */ do { ncr_exception (np); } while (INB(nc_istat) & (INTF|SIP|DIP)); np->ticks = 100; }; if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n"); splx (oldspl); } /*========================================================== ** ** ** Start execution of a SCSI command. ** This is called from the generic SCSI driver. ** ** **========================================================== */ static void ncr_action (struct cam_sim *sim, union ccb *ccb) { ncb_p np; np = (ncb_p) cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { nccb_p cp; lcb_p lp; tcb_p tp; int oldspl; struct ccb_scsiio *csio; u_int8_t *msgptr; u_int msglen; u_int msglen2; int segments; u_int8_t nego; u_int8_t idmsg; u_int8_t qidx; tp = &np->target[ccb->ccb_h.target_id]; csio = &ccb->csio; oldspl = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { xpt_done(ccb); splx(oldspl); return; } ccb->ccb_h.status |= CAM_SIM_QUEUED; /*--------------------------------------------------- ** ** Assign an nccb / bind ccb ** **---------------------------------------------------- */ cp = ncr_get_nccb (np, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); if (cp == NULL) { /* XXX JGibbs - Freeze SIMQ */ ccb->ccb_h.status = CAM_RESRC_UNAVAIL; xpt_done(ccb); return; }; cp->ccb = ccb; /*--------------------------------------------------- ** ** timestamp ** **---------------------------------------------------- */ /* ** XXX JGibbs - Isn't this expensive ** enough to be conditionalized?? */ bzero (&cp->phys.header.stamp, sizeof (struct tstamp)); cp->phys.header.stamp.start = ticks; nego = 0; if (tp->nego_cp == NULL) { if (tp->tinfo.current.width != tp->tinfo.goal.width) { tp->nego_cp = cp; nego = NS_WIDE; } else if ((tp->tinfo.current.period != tp->tinfo.goal.period) || (tp->tinfo.current.offset != tp->tinfo.goal.offset)) { tp->nego_cp = cp; nego = NS_SYNC; }; }; /*--------------------------------------------------- ** ** choose a new tag ... ** **---------------------------------------------------- */ lp = tp->lp[ccb->ccb_h.target_lun]; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0 && (ccb->csio.tag_action != CAM_TAG_ACTION_NONE) && (nego == 0)) { /* ** assign a tag to this nccb */ while (!cp->tag) { nccb_p cp2 = lp->next_nccb; lp->lasttag = lp->lasttag % 255 + 1; while (cp2 && cp2->tag != lp->lasttag) cp2 = cp2->next_nccb; if (cp2) continue; cp->tag=lp->lasttag; if (DEBUG_FLAGS & DEBUG_TAGS) { PRINT_ADDR(ccb); printf ("using tag #%d.\n", cp->tag); }; }; } else { cp->tag=0; }; /*---------------------------------------------------- ** ** Build the identify / tag / sdtr message ** **---------------------------------------------------- */ idmsg = MSG_IDENTIFYFLAG | ccb->ccb_h.target_lun; if (tp->tinfo.disc_tag & NCR_CUR_DISCENB) idmsg |= MSG_IDENTIFY_DISCFLAG; msgptr = cp->scsi_smsg; msglen = 0; msgptr[msglen++] = idmsg; if (cp->tag) { msgptr[msglen++] = ccb->csio.tag_action; msgptr[msglen++] = cp->tag; } switch (nego) { case NS_SYNC: msgptr[msglen++] = MSG_EXTENDED; msgptr[msglen++] = MSG_EXT_SDTR_LEN; msgptr[msglen++] = MSG_EXT_SDTR; msgptr[msglen++] = tp->tinfo.goal.period; msgptr[msglen++] = tp->tinfo.goal.offset;; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(ccb); printf ("sync msgout: "); ncr_show_msg (&cp->scsi_smsg [msglen-5]); printf (".\n"); }; break; case NS_WIDE: msgptr[msglen++] = MSG_EXTENDED; msgptr[msglen++] = MSG_EXT_WDTR_LEN; msgptr[msglen++] = MSG_EXT_WDTR; msgptr[msglen++] = tp->tinfo.goal.width; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(ccb); printf ("wide msgout: "); ncr_show_msg (&cp->scsi_smsg [msglen-4]); printf (".\n"); }; break; }; /*---------------------------------------------------- ** ** Build the identify message for getcc. ** **---------------------------------------------------- */ cp->scsi_smsg2 [0] = idmsg; msglen2 = 1; /*---------------------------------------------------- ** ** Build the data descriptors ** **---------------------------------------------------- */ /* XXX JGibbs - Handle other types of I/O */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { segments = ncr_scatter(&cp->phys, (vm_offset_t)csio->data_ptr, (vm_size_t)csio->dxfer_len); if (segments < 0) { ccb->ccb_h.status = CAM_REQ_TOO_BIG; ncr_free_nccb(np, cp); splx(oldspl); xpt_done(ccb); return; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in); cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; } else { /* CAM_DIR_OUT */ cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out); cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; } } else { cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data); cp->phys.header.goalp = cp->phys.header.savep; } cp->phys.header.lastp = cp->phys.header.savep; /*---------------------------------------------------- ** ** fill in nccb ** **---------------------------------------------------- ** ** ** physical -> virtual backlink ** Generic SCSI command */ cp->phys.header.cp = cp; /* ** Startqueue */ cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, select); cp->phys.header.launch.l_cmd = SCR_JUMP; /* ** select */ cp->phys.select.sel_id = ccb->ccb_h.target_id; cp->phys.select.sel_scntl3 = tp->tinfo.wval; cp->phys.select.sel_sxfer = tp->tinfo.sval; /* ** message */ cp->phys.smsg.addr = CCB_PHYS (cp, scsi_smsg); cp->phys.smsg.size = msglen; cp->phys.smsg2.addr = CCB_PHYS (cp, scsi_smsg2); cp->phys.smsg2.size = msglen2; /* ** command */ /* XXX JGibbs - Support other command types */ cp->phys.cmd.addr = vtophys (csio->cdb_io.cdb_bytes); cp->phys.cmd.size = csio->cdb_len; /* ** sense command */ cp->phys.scmd.addr = CCB_PHYS (cp, sensecmd); cp->phys.scmd.size = 6; /* ** patch requested size into sense command */ cp->sensecmd[0] = 0x03; cp->sensecmd[1] = ccb->ccb_h.target_lun << 5; cp->sensecmd[4] = sizeof(struct scsi_sense_data); cp->sensecmd[4] = csio->sense_len; /* ** sense data */ cp->phys.sense.addr = vtophys (&csio->sense_data); cp->phys.sense.size = csio->sense_len; /* ** status */ cp->actualquirks = QUIRK_NOMSG; cp->host_status = nego ? HS_NEGOTIATE : HS_BUSY; cp->s_status = SCSI_STATUS_ILLEGAL; cp->parity_status = 0; cp->xerr_status = XE_OK; cp->sync_status = tp->tinfo.sval; cp->nego_status = nego; cp->wide_status = tp->tinfo.wval; /*---------------------------------------------------- ** ** Critical region: start this job. ** **---------------------------------------------------- */ /* ** reselect pattern and activate this job. */ cp->jump_nccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag))); cp->tlimit = time_second + ccb->ccb_h.timeout / 1000 + 2; cp->magic = CCB_MAGIC; /* ** insert into start queue. */ qidx = np->squeueput + 1; if (qidx >= MAX_START) qidx=0; np->squeue [qidx ] = NCB_SCRIPT_PHYS (np, idle); np->squeue [np->squeueput] = CCB_PHYS (cp, phys); np->squeueput = qidx; if(DEBUG_FLAGS & DEBUG_QUEUE) printf("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np), np->squeueput, (unsigned)(READSCRIPT(startpos[0]) - (NCB_SCRIPTH_PHYS (np, tryloop)))); /* ** Script processor may be waiting for reselect. ** Wake it up. */ OUTB (nc_istat, SIGP); /* ** and reenable interrupts */ splx (oldspl); break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; tcb_p tp; u_int update_type; int s; cts = &ccb->cts; update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) update_type |= NCR_TRANS_GOAL; if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) update_type |= NCR_TRANS_USER; s = splcam(); tp = &np->target[ccb->ccb_h.target_id]; /* Tag and disc enables */ if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if (update_type & NCR_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) tp->tinfo.disc_tag |= NCR_CUR_DISCENB; else tp->tinfo.disc_tag &= ~NCR_CUR_DISCENB; } if (update_type & NCR_TRANS_USER) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) tp->tinfo.disc_tag |= NCR_USR_DISCENB; else tp->tinfo.disc_tag &= ~NCR_USR_DISCENB; } } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if (update_type & NCR_TRANS_GOAL) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) tp->tinfo.disc_tag |= NCR_CUR_TAGENB; else tp->tinfo.disc_tag &= ~NCR_CUR_TAGENB; } if (update_type & NCR_TRANS_USER) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) tp->tinfo.disc_tag |= NCR_USR_TAGENB; else tp->tinfo.disc_tag &= ~NCR_USR_TAGENB; } } /* Filter bus width and sync negotiation settings */ if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { if (cts->bus_width > np->maxwide) cts->bus_width = np->maxwide; } if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { if (cts->sync_period != 0 && (cts->sync_period < np->minsync)) cts->sync_period = np->minsync; } if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { if (cts->sync_offset == 0) cts->sync_period = 0; if (cts->sync_offset > np->maxoffs) cts->sync_offset = np->maxoffs; } } if ((update_type & NCR_TRANS_USER) != 0) { if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) tp->tinfo.user.period = cts->sync_period; if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) tp->tinfo.user.offset = cts->sync_offset; if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) tp->tinfo.user.width = cts->bus_width; } if ((update_type & NCR_TRANS_GOAL) != 0) { if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) tp->tinfo.goal.period = cts->sync_period; if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) tp->tinfo.goal.offset = cts->sync_offset; if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) tp->tinfo.goal.width = cts->bus_width; } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; struct ncr_transinfo *tinfo; tcb_p tp; int s; cts = &ccb->cts; tp = &np->target[ccb->ccb_h.target_id]; s = splcam(); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { tinfo = &tp->tinfo.current; if (tp->tinfo.disc_tag & NCR_CUR_DISCENB) cts->flags |= CCB_TRANS_DISC_ENB; else cts->flags &= ~CCB_TRANS_DISC_ENB; if (tp->tinfo.disc_tag & NCR_CUR_TAGENB) cts->flags |= CCB_TRANS_TAG_ENB; else cts->flags &= ~CCB_TRANS_TAG_ENB; } else { tinfo = &tp->tinfo.user; if (tp->tinfo.disc_tag & NCR_USR_DISCENB) cts->flags |= CCB_TRANS_DISC_ENB; else cts->flags &= ~CCB_TRANS_DISC_ENB; if (tp->tinfo.disc_tag & NCR_USR_TAGENB) cts->flags |= CCB_TRANS_TAG_ENB; else cts->flags &= ~CCB_TRANS_TAG_ENB; } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; cts->bus_width = tinfo->width; splx(s); cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended; - /* XXX JGibbs - I'm sure the NCR uses a different strategy, * but it should be able to deal with Adaptec * geometry too. */ - extended = 1; - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb > 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { OUTB (nc_scntl1, CRST); ccb->ccb_h.status = CAM_REQ_CMP; DELAY(10000); /* Wait until our interrupt handler sees it */ xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; if ((np->features & FE_WIDE) != 0) cpi->hba_inquiry |= PI_WIDE_16; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = (np->features & FE_WIDE) ? 15 : 7; cpi->max_lun = MAX_LUN - 1; cpi->initiator_id = np->myaddr; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Symbios", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /*========================================================== ** ** ** Complete execution of a SCSI command. ** Signal completion to the generic SCSI driver. ** ** **========================================================== */ static void ncr_complete (ncb_p np, nccb_p cp) { union ccb *ccb; tcb_p tp; /* ** Sanity check */ if (!cp || (cp->magic!=CCB_MAGIC) || !cp->ccb) return; cp->magic = 1; cp->tlimit= 0; /* ** No Reselect anymore. */ cp->jump_nccb.l_cmd = (SCR_JUMP); /* ** No starting. */ cp->phys.header.launch.l_paddr= NCB_SCRIPT_PHYS (np, idle); /* ** timestamp */ ncb_profile (np, cp); if (DEBUG_FLAGS & DEBUG_TINY) printf ("CCB=%x STAT=%x/%x\n", (int)(intptr_t)cp & 0xfff, cp->host_status,cp->s_status); ccb = cp->ccb; cp->ccb = NULL; tp = &np->target[ccb->ccb_h.target_id]; /* ** We do not queue more than 1 nccb per target ** with negotiation at any time. If this nccb was ** used for negotiation, clear this info in the tcb. */ if (cp == tp->nego_cp) tp->nego_cp = NULL; /* ** Check for parity errors. */ /* XXX JGibbs - What about reporting them??? */ if (cp->parity_status) { PRINT_ADDR(ccb); printf ("%d parity error(s), fallback.\n", cp->parity_status); /* ** fallback to asynch transfer. */ tp->tinfo.goal.period = 0; tp->tinfo.goal.offset = 0; }; /* ** Check for extended errors. */ if (cp->xerr_status != XE_OK) { PRINT_ADDR(ccb); switch (cp->xerr_status) { case XE_EXTRA_DATA: printf ("extraneous data discarded.\n"); break; case XE_BAD_PHASE: printf ("illegal scsi phase (4/5).\n"); break; default: printf ("extended error %d.\n", cp->xerr_status); break; }; if (cp->host_status==HS_COMPLETE) cp->host_status = HS_FAIL; }; /* ** Check the status. */ if (cp->host_status == HS_COMPLETE) { if (cp->s_status == SCSI_STATUS_OK) { /* ** All went well. */ /* XXX JGibbs - Properly calculate residual */ tp->bytes += ccb->csio.dxfer_len; tp->transfers ++; ccb->ccb_h.status = CAM_REQ_CMP; } else if ((cp->s_status & SCSI_STATUS_SENSE) != 0) { /* * XXX Could be TERMIO too. Should record * original status. */ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; cp->s_status &= ~SCSI_STATUS_SENSE; if (cp->s_status == SCSI_STATUS_OK) { ccb->ccb_h.status = CAM_AUTOSNS_VALID|CAM_SCSI_STATUS_ERROR; } else { ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; } } else { ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = cp->s_status; } } else if (cp->host_status == HS_SEL_TIMEOUT) { /* ** Device failed selection */ ccb->ccb_h.status = CAM_SEL_TIMEOUT; } else if (cp->host_status == HS_TIMEOUT) { /* ** No response */ ccb->ccb_h.status = CAM_CMD_TIMEOUT; } else if (cp->host_status == HS_STALL) { ccb->ccb_h.status = CAM_REQUEUE_REQ; } else { /* ** Other protocol messes */ PRINT_ADDR(ccb); printf ("COMMAND FAILED (%x %x) @%p.\n", cp->host_status, cp->s_status, cp); ccb->ccb_h.status = CAM_CMD_TIMEOUT; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } /* ** Free this nccb */ ncr_free_nccb (np, cp); /* ** signal completion to generic driver. */ xpt_done (ccb); } /*========================================================== ** ** ** Signal all (or one) control block done. ** ** **========================================================== */ static void ncr_wakeup (ncb_p np, u_long code) { /* ** Starting at the default nccb and following ** the links, complete all jobs with a ** host_status greater than "disconnect". ** ** If the "code" parameter is not zero, ** complete all jobs that are not IDLE. */ nccb_p cp = np->link_nccb; while (cp) { switch (cp->host_status) { case HS_IDLE: break; case HS_DISCONNECT: if(DEBUG_FLAGS & DEBUG_TINY) printf ("D"); /* FALLTHROUGH */ case HS_BUSY: case HS_NEGOTIATE: if (!code) break; cp->host_status = code; /* FALLTHROUGH */ default: ncr_complete (np, cp); break; }; cp = cp -> link_nccb; }; } static void ncr_freeze_devq (ncb_p np, struct cam_path *path) { nccb_p cp; int i; int count; int firstskip; /* ** Starting at the first nccb and following ** the links, complete all jobs that match ** the passed in path and are in the start queue. */ cp = np->link_nccb; count = 0; firstskip = 0; while (cp) { switch (cp->host_status) { case HS_BUSY: case HS_NEGOTIATE: if ((cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) && (xpt_path_comp(path, cp->ccb->ccb_h.path) >= 0)) { /* Mark for removal from the start queue */ for (i = 1; i < MAX_START; i++) { int idx; idx = np->squeueput - i; if (idx < 0) idx = MAX_START + idx; if (np->squeue[idx] == CCB_PHYS(cp, phys)) { np->squeue[idx] = NCB_SCRIPT_PHYS (np, skip); if (i > firstskip) firstskip = i; break; } } cp->host_status=HS_STALL; ncr_complete (np, cp); count++; } break; default: break; } cp = cp->link_nccb; } if (count > 0) { int j; int bidx; /* Compress the start queue */ j = 0; bidx = np->squeueput; i = np->squeueput - firstskip; if (i < 0) i = MAX_START + i; for (;;) { bidx = i - j; if (bidx < 0) bidx = MAX_START + bidx; if (np->squeue[i] == NCB_SCRIPT_PHYS (np, skip)) { j++; } else if (j != 0) { np->squeue[bidx] = np->squeue[i]; if (np->squeue[bidx] == NCB_SCRIPT_PHYS(np, idle)) break; } i = (i + 1) % MAX_START; } np->squeueput = bidx; } } /*========================================================== ** ** ** Start NCR chip. ** ** **========================================================== */ static void ncr_init(ncb_p np, char * msg, u_long code) { int i; /* ** Reset chip. */ OUTB (nc_istat, SRST); DELAY (1000); OUTB (nc_istat, 0); /* ** Message. */ if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg); /* ** Clear Start Queue */ for (i=0;i squeue [i] = NCB_SCRIPT_PHYS (np, idle); /* ** Start at first entry. */ np->squeueput = 0; WRITESCRIPT(startpos[0], NCB_SCRIPTH_PHYS (np, tryloop)); WRITESCRIPT(start0 [0], SCR_INT ^ IFFALSE (0)); /* ** Wakeup all pending jobs. */ ncr_wakeup (np, code); /* ** Init chip. */ OUTB (nc_istat, 0x00 ); /* Remove Reset, abort ... */ OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */ ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */ OUTB (nc_scid , RRE|np->myaddr);/* host adapter SCSI address */ OUTW (nc_respid, 1ul<myaddr);/* id to respond to */ OUTB (nc_istat , SIGP ); /* Signal Process */ OUTB (nc_dmode , np->rv_dmode); /* XXX modify burstlen ??? */ OUTB (nc_dcntl , np->rv_dcntl); OUTB (nc_ctest3, np->rv_ctest3); OUTB (nc_ctest5, np->rv_ctest5); OUTB (nc_ctest4, np->rv_ctest4);/* enable master parity checking */ OUTB (nc_stest2, np->rv_stest2|EXT); /* Extended Sreq/Sack filtering */ OUTB (nc_stest3, TE ); /* TolerANT enable */ OUTB (nc_stime0, 0x0b ); /* HTH = disabled, STO = 0.1 sec. */ if (bootverbose >= 2) { printf ("\tACTUAL values:SCNTL3:%02x DMODE:%02x DCNTL:%02x\n", np->rv_scntl3, np->rv_dmode, np->rv_dcntl); printf ("\t CTEST3:%02x CTEST4:%02x CTEST5:%02x\n", np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); } /* ** Enable GPIO0 pin for writing if LED support. */ if (np->features & FE_LED0) { OUTOFFB (nc_gpcntl, 0x01); } /* ** Fill in target structure. */ for (i=0;itarget[i]; tp->tinfo.sval = 0; tp->tinfo.wval = np->rv_scntl3; tp->tinfo.current.period = 0; tp->tinfo.current.offset = 0; tp->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT; } /* ** enable ints */ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST); OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID); /* ** Start script processor. */ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start)); /* * Notify the XPT of the event */ if (code == HS_RESET) xpt_async(AC_BUS_RESET, np->path, NULL); } static void ncr_poll(struct cam_sim *sim) { ncr_intr(cam_sim_softc(sim)); } /*========================================================== ** ** Get clock factor and sync divisor for a given ** synchronous factor period. ** Returns the clock factor (in sxfer) and scntl3 ** synchronous divisor field. ** **========================================================== */ static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p) { u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */ int div = np->clock_divn; /* Number of divisors supported */ u_long fak; /* Sync factor in sxfer */ u_long per; /* Period in tenths of ns */ u_long kpc; /* (per * clk) */ /* ** Compute the synchronous period in tenths of nano-seconds */ if (sfac <= 10) per = 250; else if (sfac == 11) per = 303; else if (sfac == 12) per = 500; else per = 40 * sfac; /* ** Look for the greatest clock divisor that allows an ** input speed faster than the period. */ kpc = per * clk; while (--div >= 0) if (kpc >= (div_10M[div] * 4)) break; /* ** Calculate the lowest clock factor that allows an output ** speed not faster than the period. */ fak = (kpc - 1) / div_10M[div] + 1; #if 0 /* You can #if 1 if you think this optimization is usefull */ per = (fak * div_10M[div]) / clk; /* ** Why not to try the immediate lower divisor and to choose ** the one that allows the fastest output speed ? ** We dont want input speed too much greater than output speed. */ if (div >= 1 && fak < 6) { u_long fak2, per2; fak2 = (kpc - 1) / div_10M[div-1] + 1; per2 = (fak2 * div_10M[div-1]) / clk; if (per2 < per && fak2 <= 6) { fak = fak2; per = per2; --div; } } #endif if (fak < 4) fak = 4; /* Should never happen, too bad ... */ /* ** Compute and return sync parameters for the ncr */ *fakp = fak - 4; *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0); } /*========================================================== ** ** Switch sync mode for current job and its target ** **========================================================== */ static void ncr_setsync(ncb_p np, nccb_p cp, u_char scntl3, u_char sxfer, u_char period) { union ccb *ccb; struct ccb_trans_settings neg; tcb_p tp; int div; u_int target = INB (nc_sdid) & 0x0f; u_int period_10ns; assert (cp); if (!cp) return; ccb = cp->ccb; assert (ccb); if (!ccb) return; assert (target == ccb->ccb_h.target_id); tp = &np->target[target]; if (!scntl3 || !(sxfer & 0x1f)) scntl3 = np->rv_scntl3; scntl3 = (scntl3 & 0xf0) | (tp->tinfo.wval & EWS) | (np->rv_scntl3 & 0x07); /* ** Deduce the value of controller sync period from scntl3. ** period is in tenths of nano-seconds. */ div = ((scntl3 >> 4) & 0x7); if ((sxfer & 0x1f) && div) period_10ns = (((sxfer>>5)+4)*div_10M[div-1])/np->clock_khz; else period_10ns = 0; tp->tinfo.goal.period = period; tp->tinfo.goal.offset = sxfer & 0x1f; tp->tinfo.current.period = period; tp->tinfo.current.offset = sxfer & 0x1f; /* ** Stop there if sync parameters are unchanged */ if (tp->tinfo.sval == sxfer && tp->tinfo.wval == scntl3) return; tp->tinfo.sval = sxfer; tp->tinfo.wval = scntl3; if (sxfer & 0x1f) { /* ** Disable extended Sreq/Sack filtering */ if (period_10ns <= 2000) OUTOFFB (nc_stest2, EXT); } /* ** Tell the SCSI layer about the ** new transfer parameters. */ neg.sync_period = period; neg.sync_offset = sxfer & 0x1f; neg.valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, /*priority*/1); xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); /* ** set actual value and sync_status */ OUTB (nc_sxfer, sxfer); np->sync_st = sxfer; OUTB (nc_scntl3, scntl3); np->wide_st = scntl3; /* ** patch ALL nccbs of this target. */ for (cp = np->link_nccb; cp; cp = cp->link_nccb) { if (!cp->ccb) continue; if (cp->ccb->ccb_h.target_id != target) continue; cp->sync_status = sxfer; cp->wide_status = scntl3; }; } /*========================================================== ** ** Switch wide mode for current job and its target ** SCSI specs say: a SCSI device that accepts a WDTR ** message shall reset the synchronous agreement to ** asynchronous mode. ** **========================================================== */ static void ncr_setwide (ncb_p np, nccb_p cp, u_char wide, u_char ack) { union ccb *ccb; struct ccb_trans_settings neg; u_int target = INB (nc_sdid) & 0x0f; tcb_p tp; u_char scntl3; u_char sxfer; assert (cp); if (!cp) return; ccb = cp->ccb; assert (ccb); if (!ccb) return; assert (target == ccb->ccb_h.target_id); tp = &np->target[target]; tp->tinfo.current.width = wide; tp->tinfo.goal.width = wide; tp->tinfo.current.period = 0; tp->tinfo.current.offset = 0; scntl3 = (tp->tinfo.wval & (~EWS)) | (wide ? EWS : 0); sxfer = ack ? 0 : tp->tinfo.sval; /* ** Stop there if sync/wide parameters are unchanged */ if (tp->tinfo.sval == sxfer && tp->tinfo.wval == scntl3) return; tp->tinfo.sval = sxfer; tp->tinfo.wval = scntl3; /* Tell the SCSI layer about the new transfer params */ neg.bus_width = (scntl3 & EWS) ? MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT; neg.sync_period = 0; neg.sync_offset = 0; neg.valid = CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, /*priority*/1); xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); /* ** set actual value and sync_status */ OUTB (nc_sxfer, sxfer); np->sync_st = sxfer; OUTB (nc_scntl3, scntl3); np->wide_st = scntl3; /* ** patch ALL nccbs of this target. */ for (cp = np->link_nccb; cp; cp = cp->link_nccb) { if (!cp->ccb) continue; if (cp->ccb->ccb_h.target_id != target) continue; cp->sync_status = sxfer; cp->wide_status = scntl3; }; } /*========================================================== ** ** ** ncr timeout handler. ** ** **========================================================== ** ** Misused to keep the driver running when ** interrupts are not configured correctly. ** **---------------------------------------------------------- */ static void ncr_timeout (void *arg) { ncb_p np = arg; time_t thistime = time_second; ticks_t step = np->ticks; u_long count = 0; long signed t; nccb_p cp; if (np->lasttime != thistime) { /* ** block ncr interrupts */ int oldspl = splcam(); np->lasttime = thistime; /*---------------------------------------------------- ** ** handle ncr chip timeouts ** ** Assumption: ** We have a chance to arbitrate for the ** SCSI bus at least every 10 seconds. ** **---------------------------------------------------- */ t = thistime - np->heartbeat; if (t<2) np->latetime=0; else np->latetime++; if (np->latetime>2) { /* ** If there are no requests, the script ** processor will sleep on SEL_WAIT_RESEL. ** But we have to check whether it died. ** Let's try to wake it up. */ OUTB (nc_istat, SIGP); }; /*---------------------------------------------------- ** ** handle nccb timeouts ** **---------------------------------------------------- */ for (cp=np->link_nccb; cp; cp=cp->link_nccb) { /* ** look for timed out nccbs. */ if (!cp->host_status) continue; count++; if (cp->tlimit > thistime) continue; /* ** Disable reselect. ** Remove it from startqueue. */ cp->jump_nccb.l_cmd = (SCR_JUMP); if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) { printf ("%s: timeout nccb=%p (skip)\n", ncr_name (np), cp); cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip); }; switch (cp->host_status) { case HS_BUSY: case HS_NEGOTIATE: /* FALLTHROUGH */ case HS_DISCONNECT: cp->host_status=HS_TIMEOUT; }; cp->tag = 0; /* ** wakeup this nccb. */ ncr_complete (np, cp); }; splx (oldspl); } np->timeout_ch = timeout (ncr_timeout, (caddr_t) np, step ? step : 1); if (INB(nc_istat) & (INTF|SIP|DIP)) { /* ** Process pending interrupts. */ int oldspl = splcam(); if (DEBUG_FLAGS & DEBUG_TINY) printf ("{"); ncr_exception (np); if (DEBUG_FLAGS & DEBUG_TINY) printf ("}"); splx (oldspl); }; } /*========================================================== ** ** log message for real hard errors ** ** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)." ** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf." ** ** exception register: ** ds: dstat ** si: sist ** ** SCSI bus lines: ** so: control lines as driver by NCR. ** si: control lines as seen by NCR. ** sd: scsi data lines as seen by NCR. ** ** wide/fastmode: ** sxfer: (see the manual) ** scntl3: (see the manual) ** ** current script command: ** dsp: script address (relative to start of script). ** dbc: first word of script command. ** ** First 16 register of the chip: ** r0..rf ** **========================================================== */ static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat) { u_int32_t dsp; int script_ofs; int script_size; char *script_name; u_char *script_base; int i; dsp = INL (nc_dsp); if (np->p_script < dsp && dsp <= np->p_script + sizeof(struct script)) { script_ofs = dsp - np->p_script; script_size = sizeof(struct script); script_base = (u_char *) np->script; script_name = "script"; } else if (np->p_scripth < dsp && dsp <= np->p_scripth + sizeof(struct scripth)) { script_ofs = dsp - np->p_scripth; script_size = sizeof(struct scripth); script_base = (u_char *) np->scripth; script_name = "scripth"; } else { script_ofs = dsp; script_size = 0; script_base = 0; script_name = "mem"; } printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n", ncr_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist, (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl), (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs, (unsigned)INL (nc_dbc)); if (((script_ofs & 3) == 0) && (unsigned)script_ofs < script_size) { printf ("%s: script cmd = %08x\n", ncr_name(np), (int)READSCRIPT_OFF(script_base, script_ofs)); } printf ("%s: regdump:", ncr_name(np)); for (i=0; i<16;i++) printf (" %02x", (unsigned)INB_OFF(i)); printf (".\n"); } /*========================================================== ** ** ** ncr chip exception handler. ** ** **========================================================== */ static void ncr_exception (ncb_p np) { u_char istat, dstat; u_short sist; /* ** interrupt on the fly ? */ while ((istat = INB (nc_istat)) & INTF) { if (DEBUG_FLAGS & DEBUG_TINY) printf ("F "); OUTB (nc_istat, INTF); np->profile.num_fly++; ncr_wakeup (np, 0); }; if (!(istat & (SIP|DIP))) { return; } /* ** Steinbach's Guideline for Systems Programming: ** Never test for an error condition you don't know how to handle. */ sist = (istat & SIP) ? INW (nc_sist) : 0; dstat = (istat & DIP) ? INB (nc_dstat) : 0; np->profile.num_int++; if (DEBUG_FLAGS & DEBUG_TINY) printf ("<%d|%x:%x|%x:%x>", INB(nc_scr0), dstat,sist, (unsigned)INL(nc_dsp), (unsigned)INL(nc_dbc)); if ((dstat==DFE) && (sist==PAR)) return; /*========================================================== ** ** First the normal cases. ** **========================================================== */ /*------------------------------------------- ** SCSI reset **------------------------------------------- */ if (sist & RST) { ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET); return; }; /*------------------------------------------- ** selection timeout ** ** IID excluded from dstat mask! ** (chip bug) **------------------------------------------- */ if ((sist & STO) && !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR))) { ncr_int_sto (np); return; }; /*------------------------------------------- ** Phase mismatch. **------------------------------------------- */ if ((sist & MA) && !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR|IID))) { ncr_int_ma (np, dstat); return; }; /*---------------------------------------- ** move command with length 0 **---------------------------------------- */ if ((dstat & IID) && !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR)) && ((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) { /* ** Target wants more data than available. ** The "no_data" script will do it. */ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data)); return; }; /*------------------------------------------- ** Programmed interrupt **------------------------------------------- */ if ((dstat & SIR) && !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|IID)) && (INB(nc_dsps) <= SIR_MAX)) { ncr_int_sir (np); return; }; /*======================================== ** log message for real hard errors **======================================== */ ncr_log_hard_error(np, sist, dstat); /*======================================== ** do the register dump **======================================== */ if (time_second - np->regtime > 10) { int i; np->regtime = time_second; for (i=0; iregdump); i++) ((volatile char*)&np->regdump)[i] = INB_OFF(i); np->regdump.nc_dstat = dstat; np->regdump.nc_sist = sist; }; /*---------------------------------------- ** clean up the dma fifo **---------------------------------------- */ if ( (INB(nc_sstat0) & (ILF|ORF|OLF) ) || (INB(nc_sstat1) & (FF3210) ) || (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) || /* wide .. */ !(dstat & DFE)) { printf ("%s: have to clear fifos.\n", ncr_name (np)); OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */ } /*---------------------------------------- ** handshake timeout **---------------------------------------- */ if (sist & HTH) { printf ("%s: handshake timeout\n", ncr_name(np)); OUTB (nc_scntl1, CRST); DELAY (1000); OUTB (nc_scntl1, 0x00); OUTB (nc_scr0, HS_FAIL); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); return; } /*---------------------------------------- ** unexpected disconnect **---------------------------------------- */ if ((sist & UDC) && !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR|IID))) { OUTB (nc_scr0, HS_UNEXPECTED); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); return; }; /*---------------------------------------- ** cannot disconnect **---------------------------------------- */ if ((dstat & IID) && !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR)) && ((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) { /* ** Unexpected data cycle while waiting for disconnect. */ if (INB(nc_sstat2) & LDSC) { /* ** It's an early reconnect. ** Let's continue ... */ OUTB (nc_dcntl, np->rv_dcntl | STD); /* ** info message */ printf ("%s: INFO: LDSC while IID.\n", ncr_name (np)); return; }; printf ("%s: target %d doesn't release the bus.\n", ncr_name (np), INB (nc_sdid)&0x0f); /* ** return without restarting the NCR. ** timeout will do the real work. */ return; }; /*---------------------------------------- ** single step **---------------------------------------- */ if ((dstat & SSI) && !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && !(dstat & (MDPE|BF|ABRT|SIR|IID))) { OUTB (nc_dcntl, np->rv_dcntl | STD); return; }; /* ** @RECOVER@ HTH, SGE, ABRT. ** ** We should try to recover from these interrupts. ** They may occur if there are problems with synch transfers, or ** if targets are switched on or off while the driver is running. */ if (sist & SGE) { /* clear scsi offsets */ OUTB (nc_ctest3, np->rv_ctest3 | CLF); } /* ** Freeze controller to be able to read the messages. */ if (DEBUG_FLAGS & DEBUG_FREEZE) { int i; unsigned char val; for (i=0; i<0x60; i++) { switch (i%16) { case 0: printf ("%s: reg[%d0]: ", ncr_name(np),i/16); break; case 4: case 8: case 12: printf (" "); break; }; val = bus_space_read_1(np->bst, np->bsh, i); printf (" %x%x", val/16, val%16); if (i%16==15) printf (".\n"); }; untimeout (ncr_timeout, (caddr_t) np, np->timeout_ch); printf ("%s: halted!\n", ncr_name(np)); /* ** don't restart controller ... */ OUTB (nc_istat, SRST); return; }; #ifdef NCR_FREEZE /* ** Freeze system to be able to read the messages. */ printf ("ncr: fatal error: system halted - press reset to reboot ..."); (void) splhigh(); for (;;); #endif /* ** sorry, have to kill ALL jobs ... */ ncr_init (np, "fatal error", HS_FAIL); } /*========================================================== ** ** ncr chip exception handler for selection timeout ** **========================================================== ** ** There seems to be a bug in the 53c810. ** Although a STO-Interrupt is pending, ** it continues executing script commands. ** But it will fail and interrupt (IID) on ** the next instruction where it's looking ** for a valid phase. ** **---------------------------------------------------------- */ static void ncr_int_sto (ncb_p np) { u_long dsa, scratcha, diff; nccb_p cp; if (DEBUG_FLAGS & DEBUG_TINY) printf ("T"); /* ** look for nccb and set the status. */ dsa = INL (nc_dsa); cp = np->link_nccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_nccb; if (cp) { cp-> host_status = HS_SEL_TIMEOUT; ncr_complete (np, cp); }; /* ** repair start queue */ scratcha = INL (nc_scratcha); diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop); /* assert ((diff <= MAX_START * 20) && !(diff % 20));*/ if ((diff <= MAX_START * 20) && !(diff % 20)) { WRITESCRIPT(startpos[0], scratcha); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start)); return; }; ncr_init (np, "selection timeout", HS_FAIL); } /*========================================================== ** ** ** ncr chip exception handler for phase errors. ** ** **========================================================== ** ** We have to construct a new transfer descriptor, ** to transfer the rest of the current block. ** **---------------------------------------------------------- */ static void ncr_int_ma (ncb_p np, u_char dstat) { u_int32_t dbc; u_int32_t rest; u_int32_t dsa; u_int32_t dsp; u_int32_t nxtdsp; volatile void *vdsp_base; size_t vdsp_off; u_int32_t oadr, olen; u_int32_t *tblp, *newcmd; u_char cmd, sbcl, ss0, ss2, ctest5; u_short delta; nccb_p cp; dsp = INL (nc_dsp); dsa = INL (nc_dsa); dbc = INL (nc_dbc); ss0 = INB (nc_sstat0); ss2 = INB (nc_sstat2); sbcl= INB (nc_sbcl); cmd = dbc >> 24; rest= dbc & 0xffffff; ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0; if (ctest5 & DFS) delta=(((ctest5<<8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff; else delta=(INB (nc_dfifo) - rest) & 0x7f; /* ** The data in the dma fifo has not been transfered to ** the target -> add the amount to the rest ** and clear the data. ** Check the sstat2 register in case of wide transfer. */ if (!(dstat & DFE)) rest += delta; if (ss0 & OLF) rest++; if (ss0 & ORF) rest++; if (INB(nc_scntl3) & EWS) { if (ss2 & OLF1) rest++; if (ss2 & ORF1) rest++; }; OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ /* ** locate matching cp */ cp = np->link_nccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_nccb; if (!cp) { printf ("%s: SCSI phase error fixup: CCB already dequeued (%p)\n", ncr_name (np), (void *) np->header.cp); return; } if (cp != np->header.cp) { printf ("%s: SCSI phase error fixup: CCB address mismatch " "(%p != %p) np->nccb = %p\n", ncr_name (np), (void *)cp, (void *)np->header.cp, (void *)np->link_nccb); /* return;*/ } /* ** find the interrupted script command, ** and the address at which to continue. */ if (dsp == vtophys (&cp->patch[2])) { vdsp_base = cp; vdsp_off = offsetof(struct nccb, patch[0]); nxtdsp = READSCRIPT_OFF(vdsp_base, vdsp_off + 3*4); } else if (dsp == vtophys (&cp->patch[6])) { vdsp_base = cp; vdsp_off = offsetof(struct nccb, patch[4]); nxtdsp = READSCRIPT_OFF(vdsp_base, vdsp_off + 3*4); } else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) { vdsp_base = np->script; vdsp_off = dsp - np->p_script - 8; nxtdsp = dsp; } else { vdsp_base = np->scripth; vdsp_off = dsp - np->p_scripth - 8; nxtdsp = dsp; }; /* ** log the information */ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) { printf ("P%x%x ",cmd&7, sbcl&7); printf ("RL=%d D=%d SS0=%x ", (unsigned) rest, (unsigned) delta, ss0); }; if (DEBUG_FLAGS & DEBUG_PHASE) { printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ", cp, np->header.cp, dsp, nxtdsp, (volatile char*)vdsp_base+vdsp_off, cmd); }; /* ** get old startaddress and old length. */ oadr = READSCRIPT_OFF(vdsp_base, vdsp_off + 1*4); if (cmd & 0x10) { /* Table indirect */ tblp = (u_int32_t *) ((char*) &cp->phys + oadr); olen = tblp[0]; oadr = tblp[1]; } else { tblp = (u_int32_t *) 0; olen = READSCRIPT_OFF(vdsp_base, vdsp_off) & 0xffffff; }; if (DEBUG_FLAGS & DEBUG_PHASE) { printf ("OCMD=%x\nTBLP=%p OLEN=%lx OADR=%lx\n", (unsigned) (READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24), (void *) tblp, (u_long) olen, (u_long) oadr); }; /* ** if old phase not dataphase, leave here. */ if (cmd != (READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24)) { PRINT_ADDR(cp->ccb); printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n", (unsigned)cmd, (unsigned)READSCRIPT_OFF(vdsp_base, vdsp_off) >> 24); return; } if (cmd & 0x06) { PRINT_ADDR(cp->ccb); printf ("phase change %x-%x %d@%08x resid=%d.\n", cmd&7, sbcl&7, (unsigned)olen, (unsigned)oadr, (unsigned)rest); OUTB (nc_dcntl, np->rv_dcntl | STD); return; }; /* ** choose the correct patch area. ** if savep points to one, choose the other. */ newcmd = cp->patch; if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4; /* ** fillin the commands */ newcmd[0] = ((cmd & 0x0f) << 24) | rest; newcmd[1] = oadr + olen - rest; newcmd[2] = SCR_JUMP; newcmd[3] = nxtdsp; if (DEBUG_FLAGS & DEBUG_PHASE) { PRINT_ADDR(cp->ccb); printf ("newcmd[%d] %x %x %x %x.\n", (int)(newcmd - cp->patch), (unsigned)newcmd[0], (unsigned)newcmd[1], (unsigned)newcmd[2], (unsigned)newcmd[3]); } /* ** fake the return address (to the patch). ** and restart script processor at dispatcher. */ np->profile.num_break++; OUTL (nc_temp, vtophys (newcmd)); if ((cmd & 7) == 0) OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); else OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn)); } /*========================================================== ** ** ** ncr chip exception handler for programmed interrupts. ** ** **========================================================== */ static int ncr_show_msg (u_char * msg) { u_char i; printf ("%x",*msg); if (*msg==MSG_EXTENDED) { for (i=1;i<8;i++) { if (i-1>msg[1]) break; printf ("-%x",msg[i]); }; return (i+1); } else if ((*msg & 0xf0) == 0x20) { printf ("-%x",msg[1]); return (2); }; return (1); } static void ncr_int_sir (ncb_p np) { u_char scntl3; u_char chg, ofs, per, fak, wide; u_char num = INB (nc_dsps); nccb_p cp=0; u_long dsa; u_int target = INB (nc_sdid) & 0x0f; tcb_p tp = &np->target[target]; int i; if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num); switch (num) { case SIR_SENSE_RESTART: case SIR_STALL_RESTART: break; default: /* ** lookup the nccb */ dsa = INL (nc_dsa); cp = np->link_nccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_nccb; assert (cp); if (!cp) goto out; assert (cp == np->header.cp); if (cp != np->header.cp) goto out; } switch (num) { /*-------------------------------------------------------------------- ** ** Processing of interrupted getcc selects ** **-------------------------------------------------------------------- */ case SIR_SENSE_RESTART: /*------------------------------------------ ** Script processor is idle. ** Look for interrupted "check cond" **------------------------------------------ */ if (DEBUG_FLAGS & DEBUG_RESTART) printf ("%s: int#%d",ncr_name (np),num); cp = (nccb_p) 0; for (i=0; itarget[i]; if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+"); cp = tp->hold_cp; if (!cp) continue; if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+"); if ((cp->host_status==HS_BUSY) && (cp->s_status==SCSI_STATUS_CHECK_COND)) break; if (DEBUG_FLAGS & DEBUG_RESTART) printf ("- (remove)"); tp->hold_cp = cp = (nccb_p) 0; }; if (cp) { if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+ restart job ..\n"); OUTL (nc_dsa, CCB_PHYS (cp, phys)); OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc)); return; }; /* ** no job, resume normal processing */ if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n"); WRITESCRIPT(start0[0], SCR_INT ^ IFFALSE (0)); break; case SIR_SENSE_FAILED: /*------------------------------------------- ** While trying to select for ** getting the condition code, ** a target reselected us. **------------------------------------------- */ if (DEBUG_FLAGS & DEBUG_RESTART) { PRINT_ADDR(cp->ccb); printf ("in getcc reselect by t%d.\n", INB(nc_ssid) & 0x0f); } /* ** Mark this job */ cp->host_status = HS_BUSY; cp->s_status = SCSI_STATUS_CHECK_COND; np->target[cp->ccb->ccb_h.target_id].hold_cp = cp; /* ** And patch code to restart it. */ WRITESCRIPT(start0[0], SCR_INT); break; /*----------------------------------------------------------------------------- ** ** Was Sie schon immer ueber transfermode negotiation wissen wollten ... ** ** We try to negotiate sync and wide transfer only after ** a successfull inquire command. We look at byte 7 of the ** inquire data to determine the capabilities if the target. ** ** When we try to negotiate, we append the negotiation message ** to the identify and (maybe) simple tag message. ** The host status field is set to HS_NEGOTIATE to mark this ** situation. ** ** If the target doesn't answer this message immidiately ** (as required by the standard), the SIR_NEGO_FAIL interrupt ** will be raised eventually. ** The handler removes the HS_NEGOTIATE status, and sets the ** negotiated value to the default (async / nowide). ** ** If we receive a matching answer immediately, we check it ** for validity, and set the values. ** ** If we receive a Reject message immediately, we assume the ** negotiation has failed, and fall back to standard values. ** ** If we receive a negotiation message while not in HS_NEGOTIATE ** state, it's a target initiated negotiation. We prepare a ** (hopefully) valid answer, set our parameters, and send back ** this answer to the target. ** ** If the target doesn't fetch the answer (no message out phase), ** we assume the negotiation has failed, and fall back to default ** settings. ** ** When we set the values, we adjust them in all nccbs belonging ** to this target, in the controller's register, and in the "phys" ** field of the controller's struct ncb. ** ** Possible cases: hs sir msg_in value send goto ** We try try to negotiate: ** -> target doesnt't msgin NEG FAIL noop defa. - dispatch ** -> target rejected our msg NEG FAIL reject defa. - dispatch ** -> target answered (ok) NEG SYNC sdtr set - clrack ** -> target answered (!ok) NEG SYNC sdtr defa. REJ--->msg_bad ** -> target answered (ok) NEG WIDE wdtr set - clrack ** -> target answered (!ok) NEG WIDE wdtr defa. REJ--->msg_bad ** -> any other msgin NEG FAIL noop defa. - dispatch ** ** Target tries to negotiate: ** -> incoming message --- SYNC sdtr set SDTR - ** -> incoming message --- WIDE wdtr set WDTR - ** We sent our answer: ** -> target doesn't msgout --- PROTO ? defa. - dispatch ** **----------------------------------------------------------------------------- */ case SIR_NEGO_FAILED: /*------------------------------------------------------- ** ** Negotiation failed. ** Target doesn't send an answer message, ** or target rejected our message. ** ** Remove negotiation request. ** **------------------------------------------------------- */ OUTB (HS_PRT, HS_BUSY); /* FALLTHROUGH */ case SIR_NEGO_PROTO: /*------------------------------------------------------- ** ** Negotiation failed. ** Target doesn't fetch the answer message. ** **------------------------------------------------------- */ if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("negotiation failed sir=%x status=%x.\n", num, cp->nego_status); }; /* ** any error in negotiation: ** fall back to default mode. */ switch (cp->nego_status) { case NS_SYNC: ncr_setsync (np, cp, 0, 0xe0, 0); break; case NS_WIDE: ncr_setwide (np, cp, 0, 0); break; }; np->msgin [0] = MSG_NOOP; np->msgout[0] = MSG_NOOP; cp->nego_status = 0; OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); break; case SIR_NEGO_SYNC: /* ** Synchronous request message received. */ if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("sync msgin: "); (void) ncr_show_msg (np->msgin); printf (".\n"); }; /* ** get requested values. */ chg = 0; per = np->msgin[3]; ofs = np->msgin[4]; if (ofs==0) per=255; /* ** check values against driver limits. */ if (per < np->minsync) {chg = 1; per = np->minsync;} if (per < tp->tinfo.user.period) {chg = 1; per = tp->tinfo.user.period;} if (ofs > tp->tinfo.user.offset) {chg = 1; ofs = tp->tinfo.user.offset;} /* ** Check against controller limits. */ fak = 7; scntl3 = 0; if (ofs != 0) { ncr_getsync(np, per, &fak, &scntl3); if (fak > 7) { chg = 1; ofs = 0; } } if (ofs == 0) { fak = 7; per = 0; scntl3 = 0; } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", per, scntl3, ofs, fak, chg); } if (INB (HS_PRT) == HS_NEGOTIATE) { OUTB (HS_PRT, HS_BUSY); switch (cp->nego_status) { case NS_SYNC: /* ** This was an answer message */ if (chg) { /* ** Answer wasn't acceptable. */ ncr_setsync (np, cp, 0, 0xe0, 0); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ ncr_setsync (np,cp,scntl3,(fak<<5)|ofs, per); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_WIDE: ncr_setwide (np, cp, 0, 0); break; }; }; /* ** It was a request. Set value and ** prepare an answer message */ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs, per); np->msgout[0] = MSG_EXTENDED; np->msgout[1] = 3; np->msgout[2] = MSG_EXT_SDTR; np->msgout[3] = per; np->msgout[4] = ofs; cp->nego_status = NS_SYNC; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("sync msgout: "); (void) ncr_show_msg (np->msgout); printf (".\n"); } if (!ofs) { OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); return; } np->msgin [0] = MSG_NOOP; break; case SIR_NEGO_WIDE: /* ** Wide request message received. */ if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("wide msgin: "); (void) ncr_show_msg (np->msgin); printf (".\n"); }; /* ** get requested values. */ chg = 0; wide = np->msgin[3]; /* ** check values against driver limits. */ if (wide > tp->tinfo.user.width) {chg = 1; wide = tp->tinfo.user.width;} if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("wide: wide=%d chg=%d.\n", wide, chg); } if (INB (HS_PRT) == HS_NEGOTIATE) { OUTB (HS_PRT, HS_BUSY); switch (cp->nego_status) { case NS_WIDE: /* ** This was an answer message */ if (chg) { /* ** Answer wasn't acceptable. */ ncr_setwide (np, cp, 0, 1); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ ncr_setwide (np, cp, wide, 1); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_SYNC: ncr_setsync (np, cp, 0, 0xe0, 0); break; }; }; /* ** It was a request, set value and ** prepare an answer message */ ncr_setwide (np, cp, wide, 1); np->msgout[0] = MSG_EXTENDED; np->msgout[1] = 2; np->msgout[2] = MSG_EXT_WDTR; np->msgout[3] = wide; np->msgin [0] = MSG_NOOP; cp->nego_status = NS_WIDE; if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->ccb); printf ("wide msgout: "); (void) ncr_show_msg (np->msgout); printf (".\n"); } break; /*-------------------------------------------------------------------- ** ** Processing of special messages ** **-------------------------------------------------------------------- */ case SIR_REJECT_RECEIVED: /*----------------------------------------------- ** ** We received a MSG_MESSAGE_REJECT message. ** **----------------------------------------------- */ PRINT_ADDR(cp->ccb); printf ("MSG_MESSAGE_REJECT received (%x:%x).\n", (unsigned)np->lastmsg, np->msgout[0]); break; case SIR_REJECT_SENT: /*----------------------------------------------- ** ** We received an unknown message ** **----------------------------------------------- */ PRINT_ADDR(cp->ccb); printf ("MSG_MESSAGE_REJECT sent for "); (void) ncr_show_msg (np->msgin); printf (".\n"); break; /*-------------------------------------------------------------------- ** ** Processing of special messages ** **-------------------------------------------------------------------- */ case SIR_IGN_RESIDUE: /*----------------------------------------------- ** ** We received an IGNORE RESIDUE message, ** which couldn't be handled by the script. ** **----------------------------------------------- */ PRINT_ADDR(cp->ccb); printf ("MSG_IGN_WIDE_RESIDUE received, but not yet implemented.\n"); break; case SIR_MISSING_SAVE: /*----------------------------------------------- ** ** We received an DISCONNECT message, ** but the datapointer wasn't saved before. ** **----------------------------------------------- */ PRINT_ADDR(cp->ccb); printf ("MSG_DISCONNECT received, but datapointer not saved:\n" "\tdata=%x save=%x goal=%x.\n", (unsigned) INL (nc_temp), (unsigned) np->header.savep, (unsigned) np->header.goalp); break; /*-------------------------------------------------------------------- ** ** Processing of a "SCSI_STATUS_QUEUE_FULL" status. ** ** XXX JGibbs - We should do the same thing for BUSY status. ** ** The current command has been rejected, ** because there are too many in the command queue. ** We have started too many commands for that target. ** **-------------------------------------------------------------------- */ case SIR_STALL_QUEUE: cp->xerr_status = XE_OK; cp->host_status = HS_COMPLETE; cp->s_status = SCSI_STATUS_QUEUE_FULL; ncr_freeze_devq(np, cp->ccb->ccb_h.path); ncr_complete(np, cp); /* FALLTHROUGH */ case SIR_STALL_RESTART: /*----------------------------------------------- ** ** Enable selecting again, ** if NO disconnected jobs. ** **----------------------------------------------- */ /* ** Look for a disconnected job. */ cp = np->link_nccb; while (cp && cp->host_status != HS_DISCONNECT) cp = cp->link_nccb; /* ** if there is one, ... */ if (cp) { /* ** wait for reselection */ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, reselect)); return; }; /* ** else remove the interrupt. */ printf ("%s: queue empty.\n", ncr_name (np)); WRITESCRIPT(start1[0], SCR_INT ^ IFFALSE (0)); break; }; out: OUTB (nc_dcntl, np->rv_dcntl | STD); } /*========================================================== ** ** ** Aquire a control block ** ** **========================================================== */ static nccb_p ncr_get_nccb (ncb_p np, u_long target, u_long lun) { lcb_p lp; int s; nccb_p cp = NULL; /* Keep our timeout handler out */ s = splsoftclock(); /* ** Lun structure available ? */ lp = np->target[target].lp[lun]; if (lp) { cp = lp->next_nccb; /* ** Look for free CCB */ while (cp && cp->magic) { cp = cp->next_nccb; } } /* ** if nothing available, create one. */ if (cp == NULL) cp = ncr_alloc_nccb(np, target, lun); if (cp != NULL) { if (cp->magic) { printf("%s: Bogus free cp found\n", ncr_name(np)); splx(s); return (NULL); } cp->magic = 1; } splx(s); return (cp); } /*========================================================== ** ** ** Release one control block ** ** **========================================================== */ static void ncr_free_nccb (ncb_p np, nccb_p cp) { /* ** sanity */ assert (cp != NULL); cp -> host_status = HS_IDLE; cp -> magic = 0; } /*========================================================== ** ** ** Allocation of resources for Targets/Luns/Tags. ** ** **========================================================== */ static nccb_p ncr_alloc_nccb (ncb_p np, u_long target, u_long lun) { tcb_p tp; lcb_p lp; nccb_p cp; assert (np != NULL); if (target>=MAX_TARGET) return(NULL); if (lun >=MAX_LUN ) return(NULL); tp=&np->target[target]; if (!tp->jump_tcb.l_cmd) { /* ** initialize it. */ tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target))); tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr; tp->getscr[0] = (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); tp->getscr[1] = vtophys (&tp->tinfo.sval); tp->getscr[2] = rman_get_start(np->reg_res) + offsetof (struct ncr_reg, nc_sxfer); tp->getscr[3] = (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); tp->getscr[4] = vtophys (&tp->tinfo.wval); tp->getscr[5] = rman_get_start(np->reg_res) + offsetof (struct ncr_reg, nc_scntl3); assert (((offsetof(struct ncr_reg, nc_sxfer) ^ (offsetof(struct tcb ,tinfo) + offsetof(struct ncr_target_tinfo, sval))) & 3) == 0); assert (((offsetof(struct ncr_reg, nc_scntl3) ^ (offsetof(struct tcb, tinfo) + offsetof(struct ncr_target_tinfo, wval))) &3) == 0); tp->call_lun.l_cmd = (SCR_CALL); tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun); tp->jump_lcb.l_cmd = (SCR_JUMP); tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb); } /* ** Logic unit control block */ lp = tp->lp[lun]; if (!lp) { /* ** Allocate a lcb */ lp = (lcb_p) malloc (sizeof (struct lcb), M_DEVBUF, M_NOWAIT | M_ZERO); if (!lp) return(NULL); /* ** Initialize it */ lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun))); lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr; lp->call_tag.l_cmd = (SCR_CALL); lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag); lp->jump_nccb.l_cmd = (SCR_JUMP); lp->jump_nccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag); lp->actlink = 1; /* ** Chain into LUN list */ tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb); tp->lp[lun] = lp; } /* ** Allocate a nccb */ cp = (nccb_p) malloc (sizeof (struct nccb), M_DEVBUF, M_NOWAIT|M_ZERO); if (!cp) return (NULL); if (DEBUG_FLAGS & DEBUG_ALLOC) { printf ("new nccb @%p.\n", cp); } /* ** Fill in physical addresses */ cp->p_nccb = vtophys (cp); /* ** Chain into reselect list */ cp->jump_nccb.l_cmd = SCR_JUMP; cp->jump_nccb.l_paddr = lp->jump_nccb.l_paddr; lp->jump_nccb.l_paddr = CCB_PHYS (cp, jump_nccb); cp->call_tmp.l_cmd = SCR_CALL; cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp); /* ** Chain into wakeup list */ cp->link_nccb = np->link_nccb; np->link_nccb = cp; /* ** Chain into CCB list */ cp->next_nccb = lp->next_nccb; lp->next_nccb = cp; return (cp); } /*========================================================== ** ** ** Build Scatter Gather Block ** ** **========================================================== ** ** The transfer area may be scattered among ** several non adjacent physical pages. ** ** We may use MAX_SCATTER blocks. ** **---------------------------------------------------------- */ static int ncr_scatter (struct dsb* phys, vm_offset_t vaddr, vm_size_t datalen) { u_long paddr, pnext; u_short segment = 0; u_long segsize, segaddr; u_long size, csize = 0; u_long chunk = MAX_SIZE; int free; bzero (&phys->data, sizeof (phys->data)); if (!datalen) return (0); paddr = vtophys (vaddr); /* ** insert extra break points at a distance of chunk. ** We try to reduce the number of interrupts caused ** by unexpected phase changes due to disconnects. ** A typical harddisk may disconnect before ANY block. ** If we wanted to avoid unexpected phase changes at all ** we had to use a break point every 512 bytes. ** Of course the number of scatter/gather blocks is ** limited. */ free = MAX_SCATTER - 1; if (vaddr & PAGE_MASK) free -= datalen / PAGE_SIZE; if (free>1) while ((chunk * free >= 2 * datalen) && (chunk>=1024)) chunk /= 2; if(DEBUG_FLAGS & DEBUG_SCATTER) printf("ncr?:\tscattering virtual=%p size=%d chunk=%d.\n", (void *) vaddr, (unsigned) datalen, (unsigned) chunk); /* ** Build data descriptors. */ while (datalen && (segment < MAX_SCATTER)) { /* ** this segment is empty */ segsize = 0; segaddr = paddr; pnext = paddr; if (!csize) csize = chunk; while ((datalen) && (paddr == pnext) && (csize)) { /* ** continue this segment */ pnext = (paddr & (~PAGE_MASK)) + PAGE_SIZE; /* ** Compute max size */ size = pnext - paddr; /* page size */ if (size > datalen) size = datalen; /* data size */ if (size > csize ) size = csize ; /* chunksize */ segsize += size; vaddr += size; csize -= size; datalen -= size; paddr = vtophys (vaddr); }; if(DEBUG_FLAGS & DEBUG_SCATTER) printf ("\tseg #%d addr=%x size=%d (rest=%d).\n", segment, (unsigned) segaddr, (unsigned) segsize, (unsigned) datalen); phys->data[segment].addr = segaddr; phys->data[segment].size = segsize; segment++; } if (datalen) { printf("ncr?: scatter/gather failed (residue=%d).\n", (unsigned) datalen); return (-1); }; return (segment); } /*========================================================== ** ** ** Test the pci bus snoop logic :-( ** ** Has to be called with interrupts disabled. ** ** **========================================================== */ #ifndef NCR_IOMAPPED static int ncr_regtest (struct ncb* np) { register volatile u_int32_t data; /* ** ncr registers may NOT be cached. ** write 0xffffffff to a read only register area, ** and try to read it back. */ data = 0xffffffff; OUTL_OFF(offsetof(struct ncr_reg, nc_dstat), data); data = INL_OFF(offsetof(struct ncr_reg, nc_dstat)); #if 1 if (data == 0xffffffff) { #else if ((data & 0xe2f0fffd) != 0x02000080) { #endif printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n", (unsigned) data); return (0x10); }; return (0); } #endif static int ncr_snooptest (struct ncb* np) { u_int32_t ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc; int i, err=0; #ifndef NCR_IOMAPPED err |= ncr_regtest (np); if (err) return (err); #endif /* ** init */ pc = NCB_SCRIPTH_PHYS (np, snooptest); host_wr = 1; ncr_wr = 2; /* ** Set memory and register. */ ncr_cache = host_wr; OUTL (nc_temp, ncr_wr); /* ** Start script (exchange values) */ OUTL (nc_dsp, pc); /* ** Wait 'til done (with timeout) */ for (i=0; i=NCR_SNOOP_TIMEOUT) { printf ("CACHE TEST FAILED: timeout.\n"); return (0x20); }; /* ** Check termination position. */ if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) { printf ("CACHE TEST FAILED: script execution failed.\n"); printf ("start=%08lx, pc=%08lx, end=%08lx\n", (u_long) NCB_SCRIPTH_PHYS (np, snooptest), (u_long) pc, (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8); return (0x40); }; /* ** Show results. */ if (host_wr != ncr_rd) { printf ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n", (int) host_wr, (int) ncr_rd); err |= 1; }; if (host_rd != ncr_wr) { printf ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n", (int) ncr_wr, (int) host_rd); err |= 2; }; if (ncr_bk != ncr_wr) { printf ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n", (int) ncr_wr, (int) ncr_bk); err |= 4; }; return (err); } /*========================================================== ** ** ** Profiling the drivers and targets performance. ** ** **========================================================== */ /* ** Compute the difference in milliseconds. **/ static int ncr_delta (int *from, int *to) { if (!from) return (-1); if (!to) return (-2); return ((to - from) * 1000 / hz); } #define PROFILE cp->phys.header.stamp static void ncb_profile (ncb_p np, nccb_p cp) { int co, da, st, en, di, se, post,work,disc; u_long diff; PROFILE.end = ticks; st = ncr_delta (&PROFILE.start,&PROFILE.status); if (st<0) return; /* status not reached */ da = ncr_delta (&PROFILE.start,&PROFILE.data); if (da<0) return; /* No data transfer phase */ co = ncr_delta (&PROFILE.start,&PROFILE.command); if (co<0) return; /* command not executed */ en = ncr_delta (&PROFILE.start,&PROFILE.end), di = ncr_delta (&PROFILE.start,&PROFILE.disconnect), se = ncr_delta (&PROFILE.start,&PROFILE.select); post = en - st; /* ** @PROFILE@ Disconnect time invalid if multiple disconnects */ if (di>=0) disc = se-di; else disc = 0; work = (st - co) - disc; diff = (np->disc_phys - np->disc_ref) & 0xff; np->disc_ref += diff; np->profile.num_trans += 1; if (cp->ccb) np->profile.num_bytes += cp->ccb->csio.dxfer_len; np->profile.num_disc += diff; np->profile.ms_setup += co; np->profile.ms_data += work; np->profile.ms_disc += disc; np->profile.ms_post += post; } #undef PROFILE /*========================================================== ** ** Determine the ncr's clock frequency. ** This is essential for the negotiation ** of the synchronous transfer rate. ** **========================================================== ** ** Note: we have to return the correct value. ** THERE IS NO SAVE DEFAULT VALUE. ** ** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock. ** 53C860 and 53C875 rev. 1 support fast20 transfers but ** do not have a clock doubler and so are provided with a ** 80 MHz clock. All other fast20 boards incorporate a doubler ** and so should be delivered with a 40 MHz clock. ** The future fast40 chips (895/895) use a 40 Mhz base clock ** and provide a clock quadrupler (160 Mhz). The code below ** tries to deal as cleverly as possible with all this stuff. ** **---------------------------------------------------------- */ /* * Select NCR SCSI clock frequency */ static void ncr_selectclock(ncb_p np, u_char scntl3) { if (np->multiplier < 2) { OUTB(nc_scntl3, scntl3); return; } if (bootverbose >= 2) printf ("%s: enabling clock multiplier\n", ncr_name(np)); OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */ if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */ int i = 20; while (!(INB(nc_stest4) & LCKFRQ) && --i > 0) DELAY(20); if (!i) printf("%s: the chip cannot lock the frequency\n", ncr_name(np)); } else /* Wait 20 micro-seconds for doubler */ DELAY(20); OUTB(nc_stest3, HSC); /* Halt the scsi clock */ OUTB(nc_scntl3, scntl3); OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ OUTB(nc_stest3, 0x00); /* Restart scsi clock */ } /* * calculate NCR SCSI clock frequency (in KHz) */ static unsigned ncrgetfreq (ncb_p np, int gen) { int ms = 0; /* * Measure GEN timer delay in order * to calculate SCSI clock frequency * * This code will never execute too * many loop iterations (if DELAY is * reasonably correct). It could get * too low a delay (too high a freq.) * if the CPU is slow executing the * loop for some reason (an NMI, for * example). For this reason we will * if multiple measurements are to be * performed trust the higher delay * (lower frequency returned). */ OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */ OUTW (nc_sien , 0); /* mask all scsi interrupts */ (void) INW (nc_sist); /* clear pending scsi interrupt */ OUTB (nc_dien , 0); /* mask all dma interrupts */ (void) INW (nc_sist); /* another one, just to be sure :) */ OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ OUTB (nc_stime1, 0); /* disable general purpose timer */ OUTB (nc_stime1, gen); /* set to nominal delay of (1<= 2) printf ("\tDelay (GEN=%d): %u msec\n", gen, ms); /* * adjust for prescaler, and convert into KHz */ return ms ? ((1 << gen) * 4440) / ms : 0; } static void ncr_getclock (ncb_p np, u_char multiplier) { unsigned char scntl3; unsigned char stest1; scntl3 = INB(nc_scntl3); stest1 = INB(nc_stest1); np->multiplier = 1; if (multiplier > 1) { np->multiplier = multiplier; np->clock_khz = 40000 * multiplier; } else { if ((scntl3 & 7) == 0) { unsigned f1, f2; /* throw away first result */ (void) ncrgetfreq (np, 11); f1 = ncrgetfreq (np, 11); f2 = ncrgetfreq (np, 11); if (bootverbose >= 2) printf ("\tNCR clock is %uKHz, %uKHz\n", f1, f2); if (f1 > f2) f1 = f2; /* trust lower result */ if (f1 > 45000) { scntl3 = 5; /* >45Mhz: assume 80MHz */ } else { scntl3 = 3; /* <45Mhz: assume 40MHz */ } } else if ((scntl3 & 7) == 5) np->clock_khz = 80000; /* Probably a 875 rev. 1 ? */ } } /*=========================================================================*/ #ifdef NCR_TEKRAM_EEPROM struct tekram_eeprom_dev { u_char devmode; #define TKR_PARCHK 0x01 #define TKR_TRYSYNC 0x02 #define TKR_ENDISC 0x04 #define TKR_STARTUNIT 0x08 #define TKR_USETAGS 0x10 #define TKR_TRYWIDE 0x20 u_char syncparam; /* max. sync transfer rate (table ?) */ u_char filler1; u_char filler2; }; struct tekram_eeprom { struct tekram_eeprom_dev dev[16]; u_char adaptid; u_char adaptmode; #define TKR_ADPT_GT2DRV 0x01 #define TKR_ADPT_GT1GB 0x02 #define TKR_ADPT_RSTBUS 0x04 #define TKR_ADPT_ACTNEG 0x08 #define TKR_ADPT_NOSEEK 0x10 #define TKR_ADPT_MORLUN 0x20 u_char delay; /* unit ? ( table ??? ) */ u_char tags; /* use 4 times as many ... */ u_char filler[60]; }; static void tekram_write_bit (ncb_p np, int bit) { u_char val = 0x10 + ((bit & 1) << 1); DELAY(10); OUTB (nc_gpreg, val); DELAY(10); OUTB (nc_gpreg, val | 0x04); DELAY(10); OUTB (nc_gpreg, val); DELAY(10); } static int tekram_read_bit (ncb_p np) { OUTB (nc_gpreg, 0x10); DELAY(10); OUTB (nc_gpreg, 0x14); DELAY(10); return INB (nc_gpreg) & 1; } static u_short read_tekram_eeprom_reg (ncb_p np, int reg) { int bit; u_short result = 0; int cmd = 0x80 | reg; OUTB (nc_gpreg, 0x10); tekram_write_bit (np, 1); for (bit = 7; bit >= 0; bit--) { tekram_write_bit (np, cmd >> bit); } for (bit = 0; bit < 16; bit++) { result <<= 1; result |= tekram_read_bit (np); } OUTB (nc_gpreg, 0x00); return result; } static int read_tekram_eeprom(ncb_p np, struct tekram_eeprom *buffer) { u_short *p = (u_short *) buffer; u_short sum = 0; int i; if (INB (nc_gpcntl) != 0x09) { return 0; } for (i = 0; i < 64; i++) { u_short val; if((i&0x0f) == 0) printf ("%02x:", i*2); val = read_tekram_eeprom_reg (np, i); if (p) *p++ = val; sum += val; if((i&0x01) == 0x00) printf (" "); printf ("%02x%02x", val & 0xff, (val >> 8) & 0xff); if((i&0x0f) == 0x0f) printf ("\n"); } printf ("Sum = %04x\n", sum); return sum == 0x1234; } #endif /* NCR_TEKRAM_EEPROM */ static device_method_t ncr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ncr_probe), DEVMETHOD(device_attach, ncr_attach), { 0, 0 } }; static driver_t ncr_driver = { "ncr", ncr_methods, sizeof(struct ncb), }; static devclass_t ncr_devclass; DRIVER_MODULE(ncr, pci, ncr_driver, ncr_devclass, 0, 0); MODULE_DEPEND(ncr, pci, 1, 1, 1); /*=========================================================================*/ #endif /* _KERNEL */