diff options
author | non <non@FreeBSD.org> | 2001-07-14 00:38:51 +0000 |
---|---|---|
committer | non <non@FreeBSD.org> | 2001-07-14 00:38:51 +0000 |
commit | e91272502526170cc4f62c2357a8b7d3ee9b2c27 (patch) | |
tree | 01f4c48045db66f0e363a543a36a88e13470d6c9 /sys/cam/scsi/scsi_low.c | |
parent | a67c5263965b3cce0db11089854bd6f3f56f2f8e (diff) | |
download | FreeBSD-src-e91272502526170cc4f62c2357a8b7d3ee9b2c27.zip FreeBSD-src-e91272502526170cc4f62c2357a8b7d3ee9b2c27.tar.gz |
Catch up with NetBSD/pc98.
o Much cleanly separate NetBSD(XS) / FreeBSD(CAM) codes.
o Improve tagged queing support (full QTAG).
o Improve quirk support.
o Improve parity error retry.
o Impliment wide negotheation.
o Cmd link support.
o Add copyright of CAM part.
o Change for CAM_NEW_TRAN_CODE.
o Work around for buggy KME UJDCD450.
o stg: add disconnet condition.
o nsp: use suspend I/O.
and more. I thank Honda-san.
conf/options.pc98: add CT_USE_RELOCATE_OFFSET and CT_BUS_WEIGHT
dev/{ct,ncv,nsp,stg}/*_{pccard,isa}.c: add splcam() before calling
attach/detach functions.
Tested by: bsd-nomads
Obtained from: NetBSD/pc98
Diffstat (limited to 'sys/cam/scsi/scsi_low.c')
-rw-r--r-- | sys/cam/scsi/scsi_low.c | 4992 |
1 files changed, 3671 insertions, 1321 deletions
diff --git a/sys/cam/scsi/scsi_low.c b/sys/cam/scsi/scsi_low.c index 2fd12ca..6099cd4 100644 --- a/sys/cam/scsi/scsi_low.c +++ b/sys/cam/scsi/scsi_low.c @@ -1,23 +1,35 @@ /* $FreeBSD$ */ -/* $NecBSD: scsi_low.c,v 1.24 1999/07/26 06:27:01 honda Exp $ */ +/* $NecBSD: scsi_low.c,v 1.24.10.8 2001/06/26 07:39:44 honda Exp $ */ /* $NetBSD$ */ #define SCSI_LOW_STATICS -#define SCSI_LOW_WARNINGS +#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 -#define SCSI_LOW_INFORM -#endif -#ifdef __FreeBSD__ -#define CAM -#endif +#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 + * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001 * NetBSD/pc98 porting staff. All rights reserved. - * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * 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 @@ -47,31 +59,32 @@ * 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_nexus) - * 2) Identify msgin => lun nexus (ti->ti_li) - * 3) Qtag msg => slccb nexus (ti->ti_nexus) + * 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 <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> -#ifdef __NetBSD__ -#include <sys/disklabel.h> -#endif + #ifdef __FreeBSD__ #if __FreeBSD_version >= 500001 #include <sys/bio.h> +#else +#include <machine/clock.h> #endif #include <sys/devicestat.h> -#endif +#endif /* __FreeBSD__ */ + #include <sys/buf.h> #include <sys/queue.h> #include <sys/malloc.h> -#include <sys/device_port.h> #include <sys/errno.h> -#ifdef __NetBSD__ +#ifdef __NetBSD__ +#include <sys/device.h> #include <vm/vm.h> #include <machine/bus.h> @@ -85,9 +98,11 @@ #include <dev/scsipi/scsipi_disk.h> #include <dev/scsipi/scsi_all.h> #include <dev/scsipi/scsiconf.h> +#include <sys/scsiio.h> #include <i386/Cbus/dev/scsi_low.h> -#endif +#endif /* __NetBSD__ */ + #ifdef __FreeBSD__ #include <cam/cam.h> #include <cam/cam_ccb.h> @@ -105,50 +120,80 @@ #else #include <sys/cons.h> #endif -#define delay(time) DELAY(time) +#endif /* __FreeBSD__ */ -/* from sys/dev/usb/usb_port.h - XXX Change this when FreeBSD has memset - */ -#define memset(d, v, s) \ - do{ \ - if ((v) == 0) \ - bzero((d), (s)); \ - else \ - panic("Non zero filler for memset, cannot handle!"); \ - } while (0) -#endif +/************************************************************** + * 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 __P((struct scsi_low_softc *, struct targ_info *, u_char *)); static void scsi_low_engage __P((void *)); -static void scsi_low_info __P((struct scsi_low_softc *, struct targ_info *, u_char *)); -static void scsi_low_init_msgsys __P((struct scsi_low_softc *, struct targ_info *)); static struct slccb *scsi_low_establish_ccb __P((struct targ_info *, struct lun_info *, scsi_low_tag_t)); static int scsi_low_done __P((struct scsi_low_softc *, struct slccb *)); +static int scsi_low_setup_done __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_bus_release __P((struct scsi_low_softc *, struct targ_info *)); static void scsi_low_twiddle_wait __P((void)); static struct lun_info *scsi_low_alloc_li __P((struct targ_info *, int, int)); -static struct targ_info *scsi_low_alloc_ti __P((struct scsi_low_softc *, int, int)); -static void scsi_low_calcf __P((struct targ_info *, struct lun_info *)); -static struct lun_info *scsi_low_establish_lun __P((struct targ_info *, int)); -#ifndef CAM -static void scsi_low_scsi_minphys __P((struct buf *)); -#endif -#ifdef SCSI_LOW_TARGET_OPEN -static int scsi_low_target_open __P((struct scsipi_link *, struct cfdata *)); -#endif /* SCSI_LOW_TARGET_OPEN */ -#ifdef CAM -void scsi_low_scsi_action(struct cam_sim *sim, union ccb *ccb); -static void scsi_low_poll (struct cam_sim *sim); -#else -static int scsi_low_scsi_cmd __P((struct scsipi_xfer *)); -#endif +static struct targ_info *scsi_low_alloc_ti __P((struct scsi_low_softc *, int)); +static void scsi_low_calcf_lun __P((struct lun_info *)); +static void scsi_low_calcf_target __P((struct targ_info *)); +static void scsi_low_calcf_show __P((struct lun_info *)); static void scsi_low_reset_nexus __P((struct scsi_low_softc *, int)); +static void scsi_low_reset_nexus_target __P((struct scsi_low_softc *, struct targ_info *, int)); +static void scsi_low_reset_nexus_lun __P((struct scsi_low_softc *, struct lun_info *, int)); static int scsi_low_init __P((struct scsi_low_softc *, u_int)); static void scsi_low_start __P((struct scsi_low_softc *)); static void scsi_low_free_ti __P((struct scsi_low_softc *)); -static void scsi_low_clear_ccb __P((struct slccb *)); + +static int scsi_low_alloc_qtag __P((struct slccb *)); +static int scsi_low_dealloc_qtag __P((struct slccb *)); +static int scsi_low_enqueue __P((struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *, u_int, u_int)); +static int scsi_low_message_enqueue __P((struct scsi_low_softc *, struct targ_info *, struct lun_info *, u_int)); +static void scsi_low_unit_ready_cmd __P((struct slccb *)); +static void scsi_low_timeout __P((void *)); +static int scsi_low_timeout_check __P((struct scsi_low_softc *)); +#ifdef SCSI_LOW_START_UP_CHECK +static int scsi_low_start_up __P((struct scsi_low_softc *)); +#endif /* SCSI_LOW_START_UP_CHECK */ +static int scsi_low_abort_ccb __P((struct scsi_low_softc *, struct slccb *)); +static struct slccb *scsi_low_revoke_ccb __P((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 struct scsi_low_statics { @@ -159,6 +204,1517 @@ struct scsi_low_statics { 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 __P((struct scsi_low_softc *, struct targ_info *, struct lun_info *)); +static void scsi_low_test_cmdlnk __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_test_atten __P((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 __P((struct slccb *)); +SCSI_LOW_INLINE void scsi_low_deactivate_qtag __P((struct slccb *)); +SCSI_LOW_INLINE void scsi_low_ccb_message_assert __P((struct slccb *, u_int)); +SCSI_LOW_INLINE void scsi_low_ccb_message_exec __P((struct scsi_low_softc *, struct slccb *)); +SCSI_LOW_INLINE void scsi_low_ccb_message_retry __P((struct slccb *)); +SCSI_LOW_INLINE void scsi_low_ccb_message_clear __P((struct slccb *)); +SCSI_LOW_INLINE void scsi_low_init_msgsys __P((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 __P((struct scsi_low_softc *, u_int, u_int, void *)); +static int scsi_low_translate_error_code __P((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 __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_scsi_minphys_xs __P((struct buf *)); +#ifdef SCSI_LOW_TARGET_OPEN +static int scsi_low_target_open __P((struct scsipi_link *, struct cfdata *)); +#endif /* SCSI_LOW_TARGET_OPEN */ +static int scsi_low_scsi_cmd_xs __P((struct scsipi_xfer *)); +static int scsi_low_enable_xs __P((void *, int)); +static int scsi_low_ioctl_xs __P((struct scsipi_link *, u_long, caddr_t, int, struct proc *)); + +static int scsi_low_attach_xs __P((struct scsi_low_softc *)); +static int scsi_low_world_start_xs __P((struct scsi_low_softc *)); +static int scsi_low_dettach_xs __P((struct scsi_low_softc *)); +static int scsi_low_ccb_setup_xs __P((struct scsi_low_softc *, struct slccb *)); +static int scsi_low_done_xs __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_timeout_xs __P((struct scsi_low_softc *, int, int)); +static u_int scsi_low_translate_quirks_xs __P((u_int)); +static void scsi_low_setup_quirks_xs __P((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 __P((struct cam_sim *)); +static void scsi_low_cam_rescan_callback __P((struct cam_periph *, union ccb *)); +static void scsi_low_rescan_bus_cam __P((struct scsi_low_softc *)); +void scsi_low_scsi_action_cam __P((struct cam_sim *, union ccb *)); + +static int scsi_low_attach_cam __P((struct scsi_low_softc *)); +static int scsi_low_world_start_cam __P((struct scsi_low_softc *)); +static int scsi_low_dettach_cam __P((struct scsi_low_softc *)); +static int scsi_low_ccb_setup_cam __P((struct scsi_low_softc *, struct slccb *)); +static int scsi_low_done_cam __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_timeout_cam __P((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); +#if __FreeBSD_version < 400001 + free(periph, M_DEVBUF); +#endif +} + +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); +#if __FreeBSD_version < 400001 + struct cam_periph *xpt_periph = malloc(sizeof(struct cam_periph), + M_DEVBUF, M_WAITOK); +#endif + 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; + 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 __P((struct scsi_low_msg_log *)); +static void scsi_low_msg_log_write __P((struct scsi_low_msg_log *, u_int8_t *, +int)); +static void scsi_low_msg_log_show __P((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 **************************************************************/ @@ -167,17 +1723,15 @@ scsi_low_engage(arg) void *arg; { struct scsi_low_softc *slp = arg; - int s = splbio(); + int s = SCSI_LOW_SPLSCSI(); switch (slp->sl_rstep) { case 0: slp->sl_rstep ++; (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_ENGAGE); -#ifdef __FreeBSD__ - slp->engage_ch = -#endif - timeout(scsi_low_engage, slp, 1); + (*slp->sl_osdep_fp->scsi_low_osdep_timeout) (slp, + SCSI_LOW_TIMEOUT_CH_ENGAGE, SCSI_LOW_TIMEOUT_START); break; case 1: @@ -197,14 +1751,15 @@ 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) { -#ifdef __FreeBSD__ - untimeout(scsi_low_engage, slp, slp->engage_ch); -#else /* NetBSD */ - untimeout(scsi_low_engage, slp); -#endif + (*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; @@ -213,12 +1768,19 @@ scsi_low_init(slp, flags) /* reset current nexus */ scsi_low_reset_nexus(slp, flags); if ((slp->sl_flags & HW_INACTIVE) != 0) - return EBUSY; + { + rv = EBUSY; + goto out; + } - if (flags == SCSI_LOW_RESTART_SOFT) - return 0; + if (flags != SCSI_LOW_RESTART_SOFT) + { + rv = ((*slp->sl_funcs->scsi_low_init) (slp, flags)); + } - return ((*slp->sl_funcs->scsi_low_init) (slp, flags)); +out: + slp->sl_flags &= ~HW_INITIALIZING; + return rv; } /************************************************************** @@ -230,6 +1792,7 @@ scsi_low_alloc_li(ti, lun, alloc) int lun; int alloc; { + struct scsi_low_softc *slp = ti->ti_sc; struct lun_info *li; li = LIST_FIRST(&ti->ti_litab); @@ -252,20 +1815,32 @@ scsi_low_alloc_li(ti, lun, alloc) if (alloc == 0) return li; - li = malloc(sizeof(struct lun_info), M_DEVBUF, M_NOWAIT); + li = SCSI_LOW_MALLOC(ti->ti_lunsize); if (li == NULL) panic("no lun info mem\n"); - memset(li, 0, sizeof(struct lun_info)); + SCSI_LOW_BZERO(li, ti->ti_lunsize); li->li_lun = lun; li->li_ti = ti; -#if defined(SDEV_NOPARITY) && defined(SDEV_NODISC) - li->li_quirks = SDEV_NOPARITY | SDEV_NODISC; -#endif /* SDEV_NOPARITY && SDEV_NODISC */ - li->li_cfgflags = 0xffff0000 | SCSI_LOW_SYNC; + 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; } @@ -273,31 +1848,40 @@ scsi_low_alloc_li(ti, lun, alloc) * allocate targ_info **************************************************************/ static struct targ_info * -scsi_low_alloc_ti(slp, targ, targ_size) +scsi_low_alloc_ti(slp, targ) struct scsi_low_softc *slp; - int targ, targ_size; + int targ; { struct targ_info *ti; if (TAILQ_FIRST(&slp->sl_titab) == NULL) TAILQ_INIT(&slp->sl_titab); - ti = malloc(targ_size, M_DEVBUF, M_NOWAIT); + ti = SCSI_LOW_MALLOC(slp->sl_targsize); if (ti == NULL) panic("%s short of memory\n", slp->sl_xname); - memset(ti, 0, targ_size); + 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); - TAILQ_INIT(&ti->ti_discq); LIST_INIT(&ti->ti_litab); - /* host specific structure initialization per target */ - (void) ((*slp->sl_funcs->scsi_low_targ_init) (slp, ti)); + 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; } @@ -310,13 +1894,24 @@ scsi_low_free_ti(slp) for (ti = TAILQ_FIRST(&slp->sl_titab); ti; ti = tib) { - tib = TAILQ_NEXT(ti, ti_chain); 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); - free(li, M_DEVBUF); + SCSI_LOW_FREE(li); } - free(ti, M_DEVBUF); + + 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); } } @@ -324,577 +1919,521 @@ scsi_low_free_ti(slp) * 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 */ - int s = splbio(); - /* check */ - if ((ti = slp->sl_nexus) != NULL && (cb = ti->ti_nexus) != NULL) + /* selection restart */ + if (slp->sl_retry_sel != 0) { - cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; - if (cb->ccb_tc < 0) - goto bus_reset; - } - else if (slp->sl_disc > 0) - { - struct targ_info *ti; + slp->sl_retry_sel = 0; + if (slp->sl_Tnexus != NULL) + goto step1; - TAILQ_FOREACH(ti, &slp->sl_titab, ti_chain) + cb = TAILQ_FIRST(&slp->sl_start); + if (cb == NULL) + goto step1; + + if (cb->ccb_selrcnt >= SCSI_LOW_MAX_SELECTION_RETRY) { - TAILQ_FOREACH(cb, &ti->ti_discq, ccb_chain) - { - cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; - if (cb->ccb_tc < 0) - goto bus_reset; - } + cb->ccb_flags |= CCB_NORETRY; + cb->ccb_error |= SELTIMEOUTIO; + if (scsi_low_revoke_ccb(slp, cb, 1) != NULL) + panic("%s: ccb not finished\n", slp->sl_xname); } + + if (slp->sl_Tnexus == NULL) + scsi_low_start(slp); } - else + + /* call hardware timeout */ +step1: + if (slp->sl_funcs->scsi_low_timeout != NULL) { - cb = TAILQ_FIRST(&slp->sl_start); - if (cb != 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_flags & HW_POWERCTRL) != 0) + else if (slp->sl_disc == 0) { - if ((slp->sl_flags & (HW_POWDOWN | HW_RESUME)) != 0) - goto out; + if ((cb = TAILQ_FIRST(&slp->sl_start)) == NULL) + return 0; - if (slp->sl_active != 0) - { - slp->sl_powc = SCSI_LOW_POWDOWN_TC; - slp->sl_active = 0; - goto out; - } + 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; - slp->sl_powc --; - if (slp->sl_powc < 0) + for (li = LIST_FIRST(&ti->ti_litab); li != NULL; + li = LIST_NEXT(li, lun_chain)) { - slp->sl_powc = SCSI_LOW_POWDOWN_TC; - slp->sl_flags |= HW_POWDOWN; - (*slp->sl_funcs->scsi_low_power) - (slp, SCSI_LOW_POWDOWN); + 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; -out: -#ifdef __FreeBSD__ - slp->timeout_ch = -#endif - timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + if (slp->sl_active != 0) + { + slp->sl_powc = SCSI_LOW_POWDOWN_TC; + slp->sl_active = 0; + return 0; + } - splx(s); - return; + 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); -#ifdef __FreeBSD__ - slp->timeout_ch = -#endif - timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); - - splx(s); + return ERESTART; } -/************************************************************** - * CCB - **************************************************************/ -GENERIC_CCB_STATIC_ALLOC(scsi_low, slccb) -GENERIC_CCB(scsi_low, slccb, ccb_chain) -/************************************************************** - * SCSI INTERFACE (XS) - **************************************************************/ -#define SCSI_LOW_MINPHYS 0x10000 +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; -#ifdef __NetBSD__ -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 */ -}; -#endif + if (cb == NULL) + return EINVAL; + if ((cb->ccb_omsgoutflag & + (SCSI_LOW_MSG_ABORT | SCSI_LOW_MSG_ABORT_QTAG)) != 0) + return EBUSY; -#ifdef CAM -static void -scsi_low_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) -{ - xpt_free_path(ccb->ccb_h.path); - free(ccb, M_DEVBUF); -#if defined(__FreeBSD__) && __FreeBSD_version < 400001 - free(periph, M_DEVBUF); -#endif -} + 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; -static void -scsi_low_rescan_bus(struct scsi_low_softc *slp) -{ - struct cam_path *path; - union ccb *ccb = malloc(sizeof(union ccb), M_DEVBUF, M_WAITOK); -#if defined(__FreeBSD__) && __FreeBSD_version < 400001 - struct cam_periph *xpt_periph = malloc(sizeof(struct cam_periph), - M_DEVBUF, M_WAITOK); -#endif - cam_status status; + cb->ccb_error |= ABORTIO; + cb->ccb_flags |= CCB_NORETRY; + scsi_low_ccb_message_assert(cb, msg); - bzero(ccb, sizeof(union ccb)); + 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\n", slp->sl_xname); - status = xpt_create_path(&path, xpt_periph, - cam_sim_path(slp->sim), -1, 0); - if (status != CAM_REQ_CMP) - return; + cb->ccb_flags |= CCB_STARTQ; + TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); - 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); + if (slp->sl_Tnexus == NULL) + scsi_low_start(slp); + } + else + { + if (scsi_low_revoke_ccb(slp, cb, 1) != NULL) + panic("%s: revoked ccb retried\n", slp->sl_xname); + } + return 0; } -#endif +/************************************************************** + * Generic SCSI INTERFACE + **************************************************************/ int -scsi_low_attach(slp, openings, ntargs, nluns, targ_size) +scsi_low_attach(slp, openings, ntargs, nluns, targsize, lunsize) struct scsi_low_softc *slp; - int openings, ntargs, nluns, targ_size; + int openings, ntargs, nluns, targsize, lunsize; { struct targ_info *ti; struct lun_info *li; -#ifdef CAM - struct cam_devq *devq; -#else - struct scsipi_adapter *sap; -#endif - int i, nccb; + 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\n"); -#ifdef CAM - OS_DEPEND(sprintf(slp->sl_xname, "%s%d", - DEVPORT_DEVNAME(slp->sl_dev), DEVPORT_DEVUNIT(slp->sl_dev))); -#else - OS_DEPEND(strncpy(slp->sl_xname, DEVPORT_DEVNAME(slp->sl_dev), 16)); -#endif 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 (targ_size < sizeof(struct targ_info)) - targ_size = sizeof(struct targ_info); + 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, targ_size); + ti = scsi_low_alloc_ti(slp, i); + ti->ti_lunsize = lunsize; li = scsi_low_alloc_li(ti, 0, 1); } -#ifndef CAM - sap = malloc(sizeof(*sap), M_DEVBUF, M_NOWAIT); - if (sap == NULL) - return ENOMEM; - - memset(sap, 0, sizeof(*sap)); - sap->scsipi_cmd = scsi_low_scsi_cmd; - sap->scsipi_minphys = scsi_low_scsi_minphys; -#ifdef SCSI_LOW_TARGET_OPEN - sap->open_target_lu = scsi_low_target_open; -#endif /* SCSI_LOW_TARGET_OPEN */ -#endif - - if (scsi_low_init(slp, SCSI_LOW_RESTART_HARD) != 0) - return EINVAL; - /* initialize queue */ - nccb = openings * (ntargs - 1); + nccb = openings * ntargs; if (nccb >= SCSI_LOW_NCCB || nccb <= 0) nccb = SCSI_LOW_NCCB; scsi_low_init_ccbque(nccb); TAILQ_INIT(&slp->sl_start); - slp->sl_openings = openings; - slp->sl_ntargs = ntargs; - slp->sl_nluns = nluns; + /* 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; + } -#ifdef CAM - /* - * Prepare the scsibus_data area for the upperlevel - * scsi code. - */ - devq = cam_simq_alloc(256/*MAX_START*/); - if (devq == NULL) - return (0); - /* scbus->adapter_link = &slp->sc_link; */ - /* - * ask the adapter what subunits are present - */ - - slp->sim = cam_sim_alloc(scsi_low_scsi_action, scsi_low_poll, - DEVPORT_DEVNAME(slp->sl_dev), slp, - DEVPORT_DEVUNIT(slp->sl_dev), 1, 32/*MAX_TAGS*/, devq); - if (slp->sim == NULL) { - cam_simq_free(devq); - return 0; - } - - if (xpt_bus_register(slp->sim, 0) != CAM_SUCCESS) { - free(slp->sim, M_DEVBUF); - return 0; - } - - if (xpt_create_path(&slp->path, /*periph*/NULL, - cam_sim_path(slp->sim), CAM_TARGET_WILDCARD, - CAM_LUN_WILDCARD) != CAM_REQ_CMP) { - xpt_bus_deregister(cam_sim_path(slp->sim)); - cam_sim_free(slp->sim, /*free_simq*/TRUE); - free(slp->sim, M_DEVBUF); - return 0; - } -#else /* !CAM */ - slp->sl_link.adapter_softc = slp; - slp->sl_link.scsipi_scsi.adapter_target = slp->sl_hostid; - slp->sl_link.scsipi_scsi.max_target = ntargs - 1; - slp->sl_link.scsipi_scsi.max_lun = nluns - 1; - slp->sl_link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE; - slp->sl_link.openings = openings; - slp->sl_link.type = BUS_SCSI; - slp->sl_link.adapter_softc = slp; - slp->sl_link.adapter = sap; - slp->sl_link.device = &scsi_low_dev; -#endif + /* 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_max_retry = SCSI_LOW_MAX_RETRY; -#ifdef __FreeBSD__ - slp->timeout_ch = -#endif - timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); -#ifdef CAM - if (!cold) - scsi_low_rescan_bus(slp); -#endif + 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); - return 0; -} + /* fake call */ + scsi_low_abort_ccb(slp, scsi_low_find_ccb(slp, 0, 0, NULL)); -#ifndef CAM -static void -scsi_low_scsi_minphys(bp) - struct buf *bp; -{ +#ifdef SCSI_LOW_START_UP_CHECK + /* probing devices */ + scsi_low_start_up(slp); +#endif /* SCSI_LOW_START_UP_CHECK */ - if (bp->b_bcount > SCSI_LOW_MINPHYS) - bp->b_bcount = SCSI_LOW_MINPHYS; - minphys(bp); + /* call os depend attach done*/ + (*slp->sl_osdep_fp->scsi_low_osdep_world_start) (slp); + splx(s); + return 0; } -#endif int scsi_low_dettach(slp) struct scsi_low_softc *slp; { + int s, rv; - if (slp->sl_disc > 0 || TAILQ_FIRST(&slp->sl_start) != NULL) + s = SCSI_LOW_SPLSCSI(); + if (scsi_low_is_busy(slp) != 0) + { + splx(s); return EBUSY; + } - /* - * scsipi does not have dettach bus fucntion. - * - scsipi_dettach_scsibus(&slp->sl_link); - */ + scsi_low_deactivate(slp); -#ifdef CAM - xpt_async(AC_LOST_DEVICE, slp->path, NULL); - xpt_free_path(slp->path); - xpt_bus_deregister(cam_sim_path(slp->sim)); - cam_sim_free(slp->sim, /* free_devq */ TRUE); -#endif + 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; } -#ifdef CAM -static void -scsi_low_poll(struct cam_sim *sim) -{ - struct scsi_low_softc *slp = (struct scsi_low_softc *) cam_sim_softc(sim); - (*slp->sl_funcs->scsi_low_poll) (slp); -} - -void -scsi_low_scsi_action(struct cam_sim *sim, union ccb *ccb) -{ - struct scsi_low_softc *slp = (struct scsi_low_softc *) cam_sim_softc(sim); - int s, target = (u_int) (ccb->ccb_h.target_id); +/************************************************************** + * 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; +{ -#if 0 - printf("scsi_low_scsi_action() func code 0x%x Target: %d, LUN: %d\n", - ccb->ccb_h.func_code, target, ccb->ccb_h.target_lun); -#endif - 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 */ - if (((cb = scsi_low_get_ccb()) == NULL)) { - ccb->ccb_h.status = CAM_RESRC_UNAVAIL; - xpt_done(ccb); - return; - } + cb->ti = ti; + cb->li = li; + + scsi_low_ccb_message_assert(cb, msg); - cb->ccb = ccb; - cb->ccb_tag = SCSI_LOW_UNKTAG; - cb->bp = (struct buf *)NULL; - cb->ti = ti = slp->sl_ti[target]; - cb->li = scsi_low_alloc_li(ti, ccb->ccb_h.target_lun, 1); - cb->ccb_flags = 0; - cb->ccb_rcnt = 0; + cb->ccb_otag = cb->ccb_tag = SCSI_LOW_UNKTAG; + scsi_low_alloc_qtag(cb); - s = splcam(); + 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); + } - if (slp->sl_nexus == NULL) { - scsi_low_start(slp); - } + slp->sl_nio ++; - 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*/ - 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: - /* XXX Implement */ - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - xpt_done(ccb); - break; - case XPT_GET_TRAN_SETTINGS: { - struct ccb_trans_settings *cts; - struct targ_info *ti; - int lun = ccb->ccb_h.target_lun; - /*int s;*/ + if (slp->sl_Tnexus == NULL) + scsi_low_start(slp); + return 0; +} - cts = &ccb->cts; - ti = slp->sl_ti[ccb->ccb_h.target_id]; - s = splcam(); - li = LIST_FIRST(&ti->ti_litab); - if (li != NULL && li->li_lun != lun) - while ((li = LIST_NEXT(li, lun_chain)) != NULL) - if (li->li_lun == lun) - break; - if (li != NULL && (cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { - if (li->li_cfgflags & SCSI_LOW_DISC) - cts->flags = CCB_TRANS_DISC_ENB; - else - cts->flags = 0; - if (li->li_cfgflags & SCSI_LOW_QTAG) - cts->flags |= CCB_TRANS_TAG_ENB; +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; - cts->sync_period = ti->ti_maxsynch.period; - cts->sync_offset = ti->ti_maxsynch.offset; - cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + tmsgflags = ti->ti_setup_msg; + ti->ti_setup_msg = 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 - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + flags |= CCB_NORETRY; + if ((cb = SCSI_LOW_ALLOC_CCB(1)) == NULL) + return ENOMEM; - 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; + cb->osdep = NULL; + cb->bp = NULL; + scsi_low_enqueue(slp, ti, li, cb, flags, tmsgflags); + return 0; +} - 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; - xpt_done(ccb); - break; - } - case XPT_RESET_BUS: /* Reset the specified SCSI bus */ -#if 0 - scsi_low_bus_reset(slp); -#endif - 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; - cpi->target_sprt = 0; - cpi->hba_misc = 0; - cpi->hba_eng_cnt = 0; - cpi->max_target = SCSI_LOW_NTARGETS - 1; - cpi->max_lun = 7; - cpi->initiator_id = 7; /* HOST_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, "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; - } +/************************************************************** + * 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 __P((struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *)); +static int scsi_low_sense_abort_start __P((struct scsi_low_softc *, struct targ_info *, struct lun_info *, struct slccb *)); +static int scsi_low_resume __P((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; } -#else /* !CAM */ + static int -scsi_low_scsi_cmd(xs) - struct scsipi_xfer *xs; -{ - struct scsi_low_softc *slp = xs->sc_link->adapter_softc; +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; - int s, lun, timeo; +{ - if (slp->sl_cfgflags & CFG_NOATTEN) + 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) { - if (xs->sc_link->scsipi_scsi.lun > 0) - { - xs->error = XS_DRIVER_STUFFUP; - return COMPLETE; - } + 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 */ } - if ((cb = scsi_low_get_ccb(xs->flags & SCSI_NOSLEEP)) == NULL) - return TRY_AGAIN_LATER; - - lun = xs->sc_link->scsipi_scsi.lun; - cb->xs = xs; - cb->ccb_tag = SCSI_LOW_UNKTAG; - cb->ti = ti = slp->sl_ti[xs->sc_link->scsipi_scsi.target]; - cb->li = scsi_low_alloc_li(ti, lun, 1); - cb->ccb_flags = 0; - cb->ccb_rcnt = 0; - - s = splbio(); + return SCSI_LOW_START_NO_QTAG; +} - TAILQ_INSERT_TAIL(&slp->sl_start, cb, ccb_chain); - if (slp->sl_nexus == NULL) - scsi_low_start(slp); +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; +{ - if ((xs->flags & SCSI_POLL) == 0) + switch(li->li_state) { - splx(s); - return SUCCESSFULLY_QUEUED; - } + 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; -#define SCSI_LOW_POLL_INTERVAL 1000 /* 1 ms */ - timeo = xs->timeout * (1000 / SCSI_LOW_POLL_INTERVAL); + 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; - while ((xs->flags & ITSDONE) == 0 && timeo -- > 0) - { - delay(SCSI_LOW_POLL_INTERVAL); - (*slp->sl_funcs->scsi_low_poll) (slp); - } + 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; - if ((xs->flags & ITSDONE) == 0) - { - cb->ccb_error |= (TIMEOUTIO | ABORTIO); - SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_NULL); - scsi_low_disconnected(slp, ti); - scsi_low_init(slp, SCSI_LOW_RESTART_HARD); + default: + panic("%s: no setup phase\n", slp->sl_xname); } - scsipi_done(xs); - splx(s); - return COMPLETE; + return SCSI_LOW_START_NO_QTAG; } -#endif - -/************************************************************** - * Start & Done - **************************************************************/ -#ifdef __NetBSD__ -static struct scsipi_start_stop ss_cmd = { START_STOP, 0, {0,0,}, SSS_START, }; -static struct scsipi_test_unit_ready unit_ready_cmd; -#endif -#ifdef __FreeBSD__ -static struct scsi_start_stop_unit ss_cmd = { START_STOP, 0, {0,0,}, SSS_START, }; -static struct scsi_test_unit_ready unit_ready_cmd; -#endif -static void scsi_low_unit_ready_cmd __P((struct slccb *)); -static void -scsi_low_unit_ready_cmd(cb) - struct slccb *cb; +static int +scsi_low_resume(slp) + struct scsi_low_softc *slp; { - cb->ccb_scp.scp_cmd = (u_int8_t *) &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; + 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; { -#ifdef CAM - union ccb *ccb; -#else - struct scsipi_xfer *xs; -#endif struct targ_info *ti; struct lun_info *li; struct slccb *cb; int rv; - /* check hardware exists ? */ - if ((slp->sl_flags & HW_INACTIVE) != 0) + /* check hardware exists or under initializations ? */ + if ((slp->sl_flags & (HW_INACTIVE | HW_INITIALIZING)) != 0) return; /* check hardware power up ? */ @@ -903,155 +2442,95 @@ scsi_low_start(slp) slp->sl_active ++; if (slp->sl_flags & (HW_POWDOWN | HW_RESUME)) { - if (slp->sl_flags & HW_RESUME) - return; - 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); -#ifdef __FreeBSD__ - slp->engage_ch = -#endif - timeout(scsi_low_engage, slp, 1); + if (scsi_low_resume(slp) == EJUSTRETURN) return; - } } } /* setup nexus */ #ifdef SCSI_LOW_DIAGNOSTIC - ti = slp->sl_nexus; - if (ti != NULL) + if (slp->sl_Tnexus || slp->sl_Lnexus || slp->sl_Qnexus) { scsi_low_info(slp, NULL, "NEXUS INCOSISTENT"); - panic("%s: inconsistent(target)\n", slp->sl_xname); + panic("%s: inconsistent\n", slp->sl_xname); } #endif /* SCSI_LOW_DIAGNOSTIC */ - TAILQ_FOREACH(cb, &slp->sl_start, ccb_chain) + for (cb = TAILQ_FIRST(&slp->sl_start); cb != NULL; + cb = TAILQ_NEXT(cb, ccb_chain)) { - ti = cb->ti; li = cb->li; - if (ti->ti_phase == PH_NULL) - goto scsi_low_cmd_start; - if (ti->ti_phase == PH_DISC && li->li_disc < li->li_maxnexus) + + 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: -#ifdef CAM - ccb = cb->ccb; -#else - xs = cb->xs; -#endif -#ifdef SCSI_LOW_DIAGNOSTIC - if (ti->ti_nexus != NULL || ti->ti_li != NULL) - { - scsi_low_info(slp, NULL, "NEXUS INCOSISTENT"); - panic("%s: inconsistent(lun or ccb)\n", slp->sl_xname); - } -#endif /* SCSI_LOW_DIAGNOSTIC */ + 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 */ - ti->ti_nexus = cb; - ti->ti_li = li; - slp->sl_nexus = ti; + slp->sl_Qnexus = cb; + slp->sl_Lnexus = li; + slp->sl_Tnexus = ti; /* initialize msgsys */ scsi_low_init_msgsys(slp, ti); - /* target lun state check */ -#ifdef CAM - li->li_maxstate = UNIT_OK; -#else - if ((xs->flags & SCSI_POLL) != 0) - li->li_maxstate = UNIT_NEGSTART; - else - li->li_maxstate = UNIT_OK; -#endif - - /* exec cmds */ -scsi_low_cmd_exec: - if ((cb->ccb_flags & CCB_SENSE) != 0) - { - memset(&cb->ccb_sense, 0, sizeof(cb->ccb_sense)); - memset(&cb->ccb_sense_cmd, 0, sizeof(cb->ccb_sense_cmd)); - cb->ccb_sense_cmd.opcode = REQUEST_SENSE; - cb->ccb_sense_cmd.byte2 = (li->li_lun << 5); - cb->ccb_sense_cmd.length = sizeof(cb->ccb_sense); - cb->ccb_scp.scp_cmd = (u_int8_t *) &cb->ccb_sense_cmd; - cb->ccb_scp.scp_cmdlen = sizeof(cb->ccb_sense_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; - } - else if (li->li_state >= li->li_maxstate) + /* exec cmd */ + if ((cb->ccb_flags & (CCB_SENSE | CCB_CLEARQ)) != 0) { -#ifdef CAM - 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 >> 10); -#else - 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 >> 10); -#endif - + /* CA state or forced abort */ + rv = scsi_low_sense_abort_start(slp, ti, li, cb); } - else switch(li->li_state) + else if (li->li_state >= SCSI_LOW_LUN_OK) { - case UNIT_SLEEP: - scsi_low_unit_ready_cmd(cb); - break; - - case UNIT_START: - cb->ccb_scp.scp_cmd = (u_int8_t *) &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 UNIT_SYNCH: - if (ti->ti_maxsynch.offset > 0) + cb->ccb_flags &= ~CCB_INTERNAL; + rv = (*slp->sl_osdep_fp->scsi_low_osdep_ccb_setup) (slp, cb); + if (cb->ccb_msgoutflag != 0) { - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_SYNCH, 0); - scsi_low_unit_ready_cmd(cb); - break; + scsi_low_ccb_message_exec(slp, cb); } - li->li_state = UNIT_WIDE; + } + else + { + cb->ccb_flags |= CCB_INTERNAL; + rv = scsi_low_setup_start(slp, ti, li, cb); + } - case UNIT_WIDE: -#ifdef SCSI_LOW_SUPPORT_WIDE - if (ti->ti_width > 0) - { - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_WIDE, 0); - scsi_low_unit_ready_cmd(cb); - break; - } -#endif /* SCSI_LOW_SUPPORT_WIDE */ - li->li_state = UNIT_OK; + /* allocate qtag */ +#define SCSI_LOW_QTAG_OK (SCSI_LOW_QTAG | SCSI_LOW_DISC) - case UNIT_OK: - goto scsi_low_cmd_exec; + 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 */ @@ -1066,8 +2545,17 @@ scsi_low_cmd_exec: 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 = ti; + slp->sl_selid = cb; rv = ((*slp->sl_funcs->scsi_low_start_bus) (slp, cb)); if (rv == SCSI_LOW_START_OK) { @@ -1077,245 +2565,339 @@ scsi_low_cmd_exec: return; } + scsi_low_arbit_fail(slp, cb); #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_fail ++; #endif /* SCSI_LOW_STATICS */ - SCSI_LOW_SETUP_PHASE(ti, PH_NULL); - scsi_low_clear_nexus(slp, ti); } void -scsi_low_clear_nexus(slp, ti) +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 */ - ti->ti_nexus = NULL; - ti->ti_li = NULL; - slp->sl_nexus = NULL; + slp->sl_Qnexus = NULL; + slp->sl_Lnexus = NULL; + slp->sl_Tnexus = NULL; /* clear selection assert */ slp->sl_selid = NULL; /* clear nexus data */ - slp->sl_nexus_call = 0; slp->sl_scp.scp_direction = SCSI_LOW_RWUNK; + + /* clear phase change counter */ + slp->sl_ph_count = 0; } static int -scsi_low_done(slp, cb) +scsi_low_setup_done(slp, cb) struct scsi_low_softc *slp; struct slccb *cb; { -#ifdef CAM - union ccb *ccb; -#else - struct scsipi_xfer *xs; -#endif struct targ_info *ti; struct lun_info *li; ti = cb->ti; li = cb->li; -#ifdef CAM - ccb = cb->ccb; -#else - xs = cb->xs; -#endif - if (cb->ccb_error == 0) + + 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) { - if ((cb->ccb_flags & CCB_SENSE) != 0) + cb->ccb_error |= ABORTIO; + return SCSI_LOW_DONE_COMPLETE; + } + + switch(li->li_state) + { + case SCSI_LOW_LUN_INQ: + if (cb->ccb_error != 0) { - cb->ccb_flags &= ~CCB_SENSE; -#ifdef CAM - memcpy(&ccb->csio.sense_data, - &cb->ccb_sense, - sizeof(ccb->csio.sense_data)); - ccb->ccb_h.status = CAM_AUTOSNS_VALID - | CAM_REQ_CMP_ERR; -#else - xs->sense.scsi_sense = cb->ccb_sense; - xs->error = XS_SENSE; -#endif + 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 switch (ti->ti_status) + else if ((li->li_inq.sd_version & 7) >= 2 || + (li->li_inq.sd_len >= 4)) { - case ST_GOOD: - if (slp->sl_scp.scp_datalen == 0) + 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 CAM - ccb->ccb_h.status = CAM_REQ_CMP; -#else - xs->error = XS_NOERROR; -#endif - break; +#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; -#define SCSIPI_SCSI_CD_COMPLETELY_BUGGY "YES" -#ifdef SCSIPI_SCSI_CD_COMPLETELY_BUGGY -#ifdef CAM - if (cb->bp == NULL && - slp->sl_scp.scp_datalen < cb->ccb_scp.scp_datalen) -#else - if (xs->bp == NULL && - slp->sl_scp.scp_datalen < cb->ccb_scp.scp_datalen) -#endif + 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) { -#ifdef CAM - ccb->ccb_h.status = CAM_REQ_CMP; -#else - xs->error = XS_NOERROR; -#endif + 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; - } -#endif /* SCSIPI_SCSI_CD_COMPLETELY_BUGGY */ cb->ccb_error |= PDMAERR; -#ifdef CAM - ccb->ccb_h.status = CAM_UNREC_HBA_ERROR; -#else - xs->error = XS_DRIVER_STUFFUP; -#endif + 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_MET: -#ifdef CAM - if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { + case ST_CMDTERM: + if (cb->ccb_flags & (CCB_AUTOSENSE | CCB_INTERNAL)) + { + cb->ccb_rcnt --; cb->ccb_flags |= CCB_SENSE; goto retry; } - ccb->ccb_h.status = CAM_AUTOSENSE_FAIL | CAM_REQ_CMP_ERR; - break; -#else - cb->ccb_flags |= CCB_SENSE; - xs->error = XS_SENSE; - goto retry; -#endif - - case ST_BUSY: - cb->ccb_error |= BUSYERR; -#ifdef CAM - ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; -#else - xs->error = XS_BUSY; -#endif + cb->ccb_error |= (UACAERR | STATERR | ABORTIO); break; + case ST_UNKNOWN: default: cb->ccb_error |= FATALIO; -#ifdef CAM - ccb->ccb_h.status = CAM_UNREC_HBA_ERROR; -#else - xs->error = XS_DRIVER_STUFFUP; -#endif break; } } else { - cb->ccb_flags &= ~CCB_SENSE; - if (ti->ti_phase == PH_SELSTART) - { -#ifdef CAM - ccb->ccb_h.status = CAM_CMD_TIMEOUT; -#else - xs->error = XS_TIMEOUT; -#endif - slp->sl_error |= SELTIMEOUTIO; - if (li->li_state == UNIT_SLEEP) - cb->ccb_error |= ABORTIO; - } - else - { -#ifdef CAM - ccb->ccb_h.status = CAM_UNREC_HBA_ERROR; -#else - xs->error = XS_DRIVER_STUFFUP; -#endif - } - - if ((cb->ccb_error & ABORTIO) != 0) + if (cb->ccb_flags & CCB_SENSE) { - cb->ccb_rcnt = slp->sl_max_retry; -#ifdef CAM - ccb->ccb_h.status = CAM_REQ_ABORTED; -#endif + cb->ccb_error |= (SENSEERR | ABORTIO); } + cb->ccb_flags &= ~(CCB_CLEARQ | CCB_SENSE); } - /* target state check */ - if (li->li_state < li->li_maxstate) + /* internal ccb */ + if ((cb->ccb_flags & CCB_INTERNAL) != 0) { - if (cb->ccb_rcnt < slp->sl_max_retry) - { - li->li_state ++; - cb->ccb_rcnt = 0; + if (scsi_low_setup_done(slp, cb) == SCSI_LOW_DONE_RETRY) goto retry; - } } - /* internal retry check */ -#ifdef CAM - if (ccb->ccb_h.status == CAM_REQ_CMP) + /* check a ccb msgout flag */ + if (cb->ccb_omsgoutflag != 0) { - ccb->csio.resid = 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; + } } - else + + /* call OS depend done */ + if (cb->osdep != NULL) { - if (((ccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) && - (cb->ccb_rcnt < slp->sl_max_retry)) + rv = (*slp->sl_osdep_fp->scsi_low_osdep_done) (slp, cb); + if (rv == EJUSTRETURN) goto retry; -#else - if (xs->error == XS_NOERROR) - { - xs->resid = 0; } - else + else if (cb->ccb_error != 0) { - if (xs->error != XS_SENSE && - cb->ccb_rcnt < slp->sl_max_retry) - goto retry; -#endif + if (cb->ccb_rcnt >= slp->sl_max_retry) + cb->ccb_error |= ABORTIO; -#ifdef SCSI_LOW_WARNINGS -#ifdef CAM - if (cb->bp != NULL) -#else - if (xs->bp != NULL) -#endif - { - scsi_low_print(slp, ti); - printf("%s: WARNING: File system IO abort\n", - slp->sl_xname); - } -#endif /* SCSI_LOW_WARNINGS */ + if ((cb->ccb_flags & CCB_NORETRY) == 0 && + (cb->ccb_error & ABORTIO) == 0) + goto retry; } -#ifdef CAM - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == 0) - ccb->ccb_h.status |= CAM_REQ_CMP_ERR; - ccb->csio.scsi_status = ti->ti_status; - xpt_done(ccb); -#else - xs->flags |= ITSDONE; - if ((xs->flags & SCSI_POLL) == 0) - scsipi_done(xs); -#endif - /* free our target */ - TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); +#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: - cb->ccb_rcnt ++; - if (TAILQ_FIRST(&slp->sl_start) != cb) +#ifdef SCSI_LOW_DEBUG + if (SCSI_LOW_DEBUG_GO(SCSI_LOW_DEBUG_DONE, cb->ti->ti_id) != 0) { - TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); - TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); + 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; } @@ -1323,12 +2905,53 @@ retry: * Reset **************************************************************/ static void -scsi_low_clear_ccb(cb) - struct slccb *cb; +scsi_low_reset_nexus_target(slp, ti, fdone) + struct scsi_low_softc *slp; + struct targ_info *ti; + int fdone; { + struct lun_info *li; - cb->ccb_flags &= ~CCB_SENSE; - cb->ccb_tag = SCSI_LOW_UNKTAG; + 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 @@ -1337,57 +2960,40 @@ scsi_low_reset_nexus(slp, fdone) int fdone; { struct targ_info *ti; - struct lun_info *li; - struct slccb *cb, *ncb; + struct slccb *cb, *topcb; - /* current nexus */ - ti = slp->sl_nexus; - if (ti != NULL && (cb = ti->ti_nexus) != NULL) + if ((cb = slp->sl_Qnexus) != NULL) { - scsi_low_clear_ccb(cb); - if (fdone != 0 && cb->ccb_rcnt ++ >= slp->sl_max_retry) - { - cb->ccb_error |= FATALIO; - scsi_low_done(slp, cb); - } + topcb = scsi_low_revoke_ccb(slp, cb, fdone); } - - /* disconnected nexus */ - TAILQ_FOREACH(ti, &slp->sl_titab, ti_chain) + else { - for (cb = TAILQ_FIRST(&ti->ti_discq); cb != NULL; cb = ncb) - { - ncb = TAILQ_NEXT(cb, ccb_chain); - TAILQ_REMOVE(&ti->ti_discq, cb, ccb_chain); - TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); - scsi_low_clear_ccb(cb); - if (fdone != 0 && cb->ccb_rcnt ++ >= slp->sl_max_retry) - { - cb->ccb_error |= FATALIO; - scsi_low_done(slp, cb); - } - } - - LIST_FOREACH(li, &ti->ti_litab, lun_chain) - { - li->li_state = UNIT_SLEEP; - li->li_disc = 0; - ((*slp->sl_funcs->scsi_low_targ_init) (slp, ti)); - scsi_low_calcf(ti, li); - } + 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); - scsi_low_clear_nexus(slp, ti); - SCSI_LOW_SETUP_PHASE(ti, PH_NULL); } - slp->sl_flags &= ~HW_PDMASTART; + 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) @@ -1396,7 +3002,7 @@ scsi_low_twiddle_wait(void) cnputc('\b'); cnputc(tw_chars[tw_pos++]); tw_pos %= (sizeof(tw_chars) - 1); - delay(TWIDDLEWAIT); + SCSI_LOW_DELAY(TWIDDLEWAIT); } void @@ -1437,21 +3043,6 @@ scsi_low_restart(slp, flags, s) **************************************************************/ #define MSGCMD_LUN(msg) (msg & 0x07) -static struct lun_info * -scsi_low_establish_lun(ti, lun) - struct targ_info *ti; - int lun; -{ - struct lun_info *li; - - li = scsi_low_alloc_li(ti, lun, 0); - if (li == NULL) - return li; - - ti->ti_li = li; - return li; -} - static struct slccb * scsi_low_establish_ccb(ti, li, tag) struct targ_info *ti; @@ -1461,12 +3052,12 @@ scsi_low_establish_ccb(ti, li, tag) struct scsi_low_softc *slp = ti->ti_sc; struct slccb *cb; - /* - * Search ccb matching with lun and tag. - */ - cb = TAILQ_FIRST(&ti->ti_discq); + if (li == NULL) + return NULL; + + cb = TAILQ_FIRST(&li->li_discq); for ( ; cb != NULL; cb = TAILQ_NEXT(cb, ccb_chain)) - if (cb->li == li && cb->ccb_tag == tag) + if (cb->ccb_tag == tag) goto found; return cb; @@ -1474,19 +3065,43 @@ scsi_low_establish_ccb(ti, li, tag) * establish our ccb nexus */ found: - TAILQ_REMOVE(&ti->ti_discq, cb, ccb_chain); - TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); - ti->ti_nexus = cb; +#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_nexus_call = 1; - (*slp->sl_funcs->scsi_low_establish_nexus) (slp, ti); + (*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; } @@ -1496,21 +3111,25 @@ scsi_low_reselected(slp, targ) u_int targ; { struct targ_info *ti; + struct slccb *cb; u_char *s; /* * Check select vs reselected collision. */ - if ((ti = slp->sl_selid) != NULL) + if ((cb = slp->sl_selid) != NULL) { - scsi_low_clear_nexus(slp, ti); - SCSI_LOW_SETUP_PHASE(ti, PH_NULL); + scsi_low_arbit_fail(slp, cb); #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_conflict ++; #endif /* SCSI_LOW_STATICS */ } - else if (slp->sl_nexus != NULL) + + /* + * Check if no current active nexus. + */ + if (slp->sl_Tnexus != NULL) { s = "host busy"; goto world_restart; @@ -1529,25 +3148,23 @@ scsi_low_reselected(slp, targ) * Check the target scsi status. */ ti = slp->sl_ti[targ]; - if (ti->ti_phase != PH_DISC) + if (ti->ti_phase != PH_DISC && ti->ti_phase != PH_NULL) { s = "phase mismatch"; goto world_restart; } /* - * Setup lun and init msgsys + * Setup init msgsys */ slp->sl_error = 0; scsi_low_init_msgsys(slp, ti); /* * Establish our target nexus - * Remark: ccb and scsi pointer not yet restored - * if lun != SCSI_LOW_UNKLUN. */ SCSI_LOW_SETUP_PHASE(ti, PH_RESEL); - slp->sl_nexus = ti; + slp->sl_Tnexus = ti; #ifdef SCSI_LOW_STATICS scsi_low_statics.nexus_reselected ++; #endif /* SCSI_LOW_STATICS */ @@ -1560,49 +3177,6 @@ world_restart: return NULL; } -int -scsi_low_disconnected(slp, ti) - struct scsi_low_softc *slp; - struct targ_info *ti; -{ - struct slccb *cb = ti->ti_nexus; - - /* check phase completion */ - switch (slp->sl_msgphase) - { - case MSGPH_DISC: - if (cb != NULL) - { - TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); - TAILQ_INSERT_TAIL(&ti->ti_discq, cb, ccb_chain); - cb->ccb_error |= slp->sl_error; - cb->li->li_disc ++; - slp->sl_disc ++; - } - SCSI_LOW_SETUP_PHASE(ti, PH_DISC); -#ifdef SCSI_LOW_STATICS - scsi_low_statics.nexus_disconnected ++; -#endif /* SCSI_LOW_STATICS */ - break; - - case MSGPH_NULL: - slp->sl_error |= FATALIO; - - case MSGPH_CMDC: - if (cb != NULL) - { - cb->ccb_error |= slp->sl_error; - scsi_low_done(slp, cb); - } - SCSI_LOW_SETUP_PHASE(ti, PH_NULL); - break; - } - - scsi_low_clear_nexus(slp, ti); - scsi_low_start(slp); - return 1; -} - /************************************************************** * cmd out pointer setup **************************************************************/ @@ -1611,24 +3185,31 @@ scsi_low_cmd(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { - struct slccb *cb = ti->ti_nexus; + struct slccb *cb = slp->sl_Qnexus; + slp->sl_ph_count ++; if (cb == NULL) { /* - * no slccb, abort! + * 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; - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); - scsi_low_info(slp, ti, "CMDOUT: slccb nexus not found"); + 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 if (slp->sl_nexus_call == 0) + else { - slp->sl_nexus_call = 1; - (*slp->sl_funcs->scsi_low_establish_nexus) (slp, ti); +#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; } @@ -1643,27 +3224,31 @@ scsi_low_data(slp, ti, bp, direction) struct buf **bp; int direction; { - struct slccb *cb = ti->ti_nexus; + struct slccb *cb = slp->sl_Qnexus; - if (cb == NULL) + if (cb != NULL && direction == cb->ccb_sscp.scp_direction) { - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); - scsi_low_info(slp, ti, "DATA PHASE: slccb nexus not found"); - return EINVAL; + *bp = cb->bp; + return 0; } - if (direction != cb->ccb_scp.scp_direction) + 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) { - scsi_low_info(slp, ti, "DATA PHASE: xfer direction mismatch"); - return EINVAL; + 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); } -#ifdef CAM - *bp = (cb == NULL) ? NULL : cb->bp; -#else - *bp = (cb->xs == NULL) ? NULL : cb->xs->bp; -#endif - return 0; + *bp = NULL; + return EINVAL; } /************************************************************** @@ -1672,69 +3257,83 @@ scsi_low_data(slp, ti, bp, direction) #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 __P((struct targ_info *, u_int)); -static int scsi_low_errfunc_wide __P((struct targ_info *, u_int)); -static int scsi_low_errfunc_identify __P((struct targ_info *, u_int)); +static int scsi_low_errfunc_synch __P((struct scsi_low_softc *, u_int)); +static int scsi_low_errfunc_wide __P((struct scsi_low_softc *, u_int)); +static int scsi_low_errfunc_identify __P((struct scsi_low_softc *, u_int)); +static int scsi_low_errfunc_qtag __P((struct scsi_low_softc *, u_int)); -static int scsi_low_msgfunc_synch __P((struct targ_info *)); -static int scsi_low_msgfunc_wide __P((struct targ_info *)); -static int scsi_low_msgfunc_identify __P((struct targ_info *)); -static int scsi_low_msgfunc_user __P((struct targ_info *)); -static int scsi_low_msgfunc_abort __P((struct targ_info *)); +static int scsi_low_msgfunc_synch __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_wide __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_identify __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_abort __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_qabort __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_qtag __P((struct scsi_low_softc *)); +static int scsi_low_msgfunc_reset __P((struct scsi_low_softc *)); struct scsi_low_msgout_data { u_int md_flags; u_int8_t md_msg; - int (*md_msgfunc) __P((struct targ_info *)); - int (*md_errfunc) __P((struct targ_info *, u_int)); + int (*md_msgfunc) __P((struct scsi_low_softc *)); + int (*md_errfunc) __P((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_abort, NULL}, -/* 1 */ {SCSI_LOW_MSG_ABORT, MSG_ABORT, scsi_low_msgfunc_abort, NULL}, -/* 2 */ {SCSI_LOW_MSG_REJECT, MSG_REJECT, NULL, NULL}, -/* 3 */ {SCSI_LOW_MSG_PARITY, MSG_PARITY, NULL, NULL}, -/* 4 */ {SCSI_LOW_MSG_ERROR, MSG_I_ERROR, NULL, NULL}, -/* 5 */ {SCSI_LOW_MSG_IDENTIFY, 0, scsi_low_msgfunc_identify, scsi_low_errfunc_identify}, -/* 6 */ {SCSI_LOW_MSG_SYNCH, 0, scsi_low_msgfunc_synch, scsi_low_errfunc_synch}, -/* 7 */ {SCSI_LOW_MSG_WIDE, 0, scsi_low_msgfunc_wide, scsi_low_errfunc_wide}, -/* 8 */ {SCSI_LOW_MSG_USER, 0, scsi_low_msgfunc_user, NULL}, -/* 9 */ {SCSI_LOW_MSG_NOOP, MSG_NOOP, NULL, NULL}, -/* 10 */ {SCSI_LOW_MSG_ALL, 0}, +/* 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 __P((struct targ_info *)); -static int scsi_low_synch __P((struct targ_info *)); -static int scsi_low_msginfunc_msg_reject __P((struct targ_info *)); -static int scsi_low_msginfunc_rejop __P((struct targ_info *)); -static int scsi_low_msginfunc_rdp __P((struct targ_info *)); -static int scsi_low_msginfunc_sdp __P((struct targ_info *)); -static int scsi_low_msginfunc_disc __P((struct targ_info *)); -static int scsi_low_msginfunc_cc __P((struct targ_info *)); -static int scsi_low_msginfunc_parity __P((struct targ_info *)); -static int scsi_low_msginfunc_noop __P((struct targ_info *)); -static void scsi_low_retry_phase __P((struct targ_info *)); +static int scsi_low_msginfunc_ext __P((struct scsi_low_softc *)); +static int scsi_low_synch __P((struct scsi_low_softc *)); +static int scsi_low_wide __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_msg_reject __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_rejop __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_rp __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_sdp __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_disc __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_cc __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_lcc __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_parity __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_noop __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_simple_qtag __P((struct scsi_low_softc *)); +static int scsi_low_msginfunc_i_wide_residue __P((struct scsi_low_softc *)); struct scsi_low_msgin_data { u_int md_len; - int (*md_msgfunc) __P((struct targ_info *)); + int (*md_msgfunc) __P((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_rdp}, +/* 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_rejop}, -/* b */ {1, scsi_low_msginfunc_rejop}, +/* 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}, @@ -1755,10 +3354,10 @@ struct scsi_low_msgin_data scsi_low_msgin_data[] = { /* 0x1d */ {1, scsi_low_msginfunc_rejop}, /* 0x1e */ {1, scsi_low_msginfunc_rejop}, /* 0x1f */ {1, scsi_low_msginfunc_rejop}, -/* 0x20 */ {2, 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_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}, @@ -1774,35 +3373,16 @@ struct scsi_low_msgin_data scsi_low_msgin_data[] = { /* 0x30 */ {1, scsi_low_msginfunc_rejop} /* default rej op */ }; -static 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; - ti->ti_tflags &= ~TARG_ASSERT_ATN; - SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_NULL); -} - /************************************************************** * msgout **************************************************************/ static int -scsi_low_msgfunc_synch(ti) - struct targ_info *ti; +scsi_low_msgfunc_synch(slp) + struct scsi_low_softc *slp; { - struct lun_info *li = ti->ti_li; + struct targ_info *ti = slp->sl_Tnexus; int ptr = ti->ti_msgoutlen; - if (li == NULL) - { - scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); - return EINVAL; - } - - ti->ti_msgoutstr[ptr + 0] = MSG_EXTEND; 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; @@ -1811,19 +3391,12 @@ scsi_low_msgfunc_synch(ti) } static int -scsi_low_msgfunc_wide(ti) - struct targ_info *ti; +scsi_low_msgfunc_wide(slp) + struct scsi_low_softc *slp; { - struct lun_info *li = ti->ti_li; + struct targ_info *ti = slp->sl_Tnexus; int ptr = ti->ti_msgoutlen; - if (li == NULL) - { - scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); - return EINVAL; - } - - ti->ti_msgoutstr[ptr + 0] = MSG_EXTEND; ti->ti_msgoutstr[ptr + 1] = MSG_EXTEND_WIDELEN; ti->ti_msgoutstr[ptr + 2] = MSG_EXTEND_WIDECODE; ti->ti_msgoutstr[ptr + 3] = ti->ti_width; @@ -1831,57 +3404,91 @@ scsi_low_msgfunc_wide(ti) } static int -scsi_low_msgfunc_identify(ti) - struct targ_info *ti; +scsi_low_msgfunc_identify(slp) + struct scsi_low_softc *slp; { - int ptr = ti->ti_msgoutlen;; + 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; - if (ti->ti_li == NULL) + msg = MSG_IDENTIFY; + if (cb == NULL) { - ti->ti_msgoutstr[ptr + 0] = 0x80; - scsi_low_info(ti->ti_sc, ti, "MSGOUT: lun unknown"); - scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); + slp->sl_error |= FATALIO; + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); + SCSI_LOW_INFO(slp, ti, "MSGOUT: nexus unknown"); } else { - ti->ti_msgoutstr[ptr + 0] = ID_MSG_SETUP(ti); + 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_user(ti) - struct targ_info *ti; +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; { -#ifdef SCSI_LOW_SUPPORT_USER_MSGOUT - struct slccb *cb = ti->ti_nexus; - int ptr = ti->ti_msgoutlen;; - if (ti->ti_nexus == NULL) + 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 { - bcopy(cb->msgout, ti->ti_msgoutstr + ptr, SCSI_LOW_MAX_MSGLEN); - return cb->msgoutlen; + 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); + } } -#else /* !SCSI_LOW_SUPPORT_USER_MSGOUT */ - return 0; -#endif /* !SCSI_LOW_SUPPORT_USER_MSGOUT */ -} - -static int -scsi_low_msgfunc_abort(ti) - struct targ_info *ti; -{ - struct scsi_low_softc *slp = ti->ti_sc; - - /* The target should releases bus */ - SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_CMDC); - slp->sl_error |= /* ABORTIO */ FATALIO; - return 1; + return 2; } /* @@ -1889,88 +3496,127 @@ scsi_low_msgfunc_abort(ti) * responces in msgin (after msgout). */ static int -scsi_low_errfunc_identify(ti, msgflags) - struct targ_info *ti; +scsi_low_errfunc_identify(slp, msgflags) + struct scsi_low_softc *slp; u_int msgflags; { - struct lun_info *li = ti->ti_li; - li->li_flags &= ~SCSI_LOW_DISC; + 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(ti, msgflags) - struct targ_info *ti; +scsi_low_errfunc_synch(slp, msgflags) + struct scsi_low_softc *slp; u_int msgflags; { + struct targ_info *ti = slp->sl_Tnexus; - /* XXX: - * illegal behavior, however - * there are buggy devices! - */ MSGIN_PERIOD(ti) = 0; MSGIN_OFFSET(ti) = 0; - scsi_low_synch(ti); + scsi_low_synch(slp); return 0; } static int -scsi_low_errfunc_wide(ti, msgflags) - struct targ_info *ti; +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; { - ti->ti_width = 0; + + 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) +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 (ti->ti_ophase != ti->ti_phase) + 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. */ - if (ti->ti_ophase == PH_MSGOUT && !(ti->ti_tflags & TARG_ASSERT_ATN)) - { 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 */ } - /* - * OK. clear flags. - */ - ti->ti_tflags &= ~TARG_ASSERT_ATN; - /* STEP III. - * We have no msgs. send MSG_LOOP (OK?) + * We have no msgs. send MSG_NOOP (OK?) */ - if (scsi_low_is_msgout_continue(ti) == 0) + 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 ++) { @@ -1982,31 +3628,34 @@ scsi_low_msgout(slp, ti) ti->ti_msgoutstr[ti->ti_msgoutlen] = mdp->md_msg; if (mdp->md_msgfunc != NULL) - len = (*mdp->md_msgfunc) (ti); + 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 ((slp->sl_cfgflags & CFG_MSGUNIFY) == 0 || + 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) - { -#ifdef SCSI_LOW_DIAGNOSTIC - printf("SCSI_LOW_ATTENTION(msgout): 0x%x\n", ti->ti_msgflags); -#endif /* SCSI_LOW_DIAGNOSTIC */ - scsi_low_attention(slp, ti); - } + if (scsi_low_is_msgout_continue(ti, 0) == 0) + slp->sl_clear_atten = 1; - /* - * OK. advance old phase. - */ - ti->ti_ophase = ti->ti_phase; return ti->ti_msgoutlen; } @@ -2014,86 +3663,207 @@ scsi_low_msgout(slp, ti) * msgin **************************************************************/ static int -scsi_low_msginfunc_noop(ti) - struct targ_info *ti; +scsi_low_msginfunc_noop(slp) + struct scsi_low_softc *slp; { return 0; } static int -scsi_low_msginfunc_rejop(ti) - struct targ_info *ti; +scsi_low_msginfunc_rejop(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; + struct targ_info *ti = slp->sl_Tnexus; u_int8_t msg = ti->ti_msgin[0]; - printf("%s: MSGIN: msg 0x%x reject\n", slp->sl_xname, (u_int) msg); + 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(ti) - struct targ_info *ti; +scsi_low_msginfunc_cc(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; + 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_disc(ti) +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\n", 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; { - struct scsi_low_softc *slp = ti->ti_sc; SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_DISC); return 0; } static int -scsi_low_msginfunc_sdp(ti) - struct targ_info *ti; +scsi_low_msginfunc_sdp(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; + struct slccb *cb = slp->sl_Qnexus; - if (ti->ti_nexus != NULL) - ti->ti_nexus->ccb_sscp = slp->sl_scp; + 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, ti, SCSI_LOW_MSG_REJECT, 0); + scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_REJECT, 0); return 0; } static int -scsi_low_msginfunc_rdp(ti) - struct targ_info *ti; +scsi_low_msginfunc_rp(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; - if (ti->ti_nexus != NULL) - slp->sl_scp = ti->ti_nexus->ccb_sscp; + if (slp->sl_Qnexus != NULL) + slp->sl_scp = slp->sl_Qnexus->ccb_sscp; else - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_REJECT, 0); return 0; } static int -scsi_low_synch(ti) - struct targ_info *ti; +scsi_low_synch(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; -#ifdef SCSI_LOW_INFORM - struct lun_info *li = ti->ti_li; - u_int speed; -#endif - u_int period = 0, offset = 0; + 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) + 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); @@ -2128,28 +3898,137 @@ scsi_low_synch(ti) return error; } -#ifdef SCSI_LOW_INFORM + ti->ti_osynch = ti->ti_maxsynch; + if (offset > 0) + { + ti->ti_setup_msg_done |= SCSI_LOW_MSG_SYNCH; + } + /* inform data */ - printf("%s(%d:%d): <%s> offset %d period %dns ", - slp->sl_xname, ti->ti_id, li->li_lun, s, offset, period * 4); - if (period != 0) + if ((slp->sl_show_result & SHOW_SYNCH_NEG) != 0) { - speed = 1000 * 10 / (period * 4); - printf("%d.%d M/s", speed / 10, speed % 10); +#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"); } - printf("\n"); -#endif + 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_ext(ti) - struct targ_info *ti; +scsi_low_msginfunc_simple_qtag(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; - struct slccb *cb = ti->ti_nexus; - struct lun_info *li = ti->ti_li; + 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; @@ -2179,16 +4058,26 @@ scsi_low_msginfunc_ext(ti) if (li == NULL) break; - retry = scsi_low_synch(ti); + 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; - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_WIDE, 0); + 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: @@ -2199,59 +4088,29 @@ scsi_low_msginfunc_ext(ti) return EINVAL; } -static void -scsi_low_retry_phase(ti) - struct targ_info *ti; -{ - - switch (ti->ti_sphase) - { - case PH_MSGOUT: - ti->ti_msgflags |= ti->ti_omsgflags; - break; - - default: - break; - } -} - static int -scsi_low_msginfunc_parity(ti) - struct targ_info *ti; +scsi_low_msginfunc_parity(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; + struct targ_info *ti = slp->sl_Tnexus; - if (ti->ti_sphase != PH_MSGOUT) - slp->sl_error |= PARITYERR; - scsi_low_retry_phase(ti); + /* only I -> T, invalid! */ + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); return 0; } static int -scsi_low_msginfunc_msg_reject(ti) - struct targ_info *ti; +scsi_low_msginfunc_msg_reject(slp) + struct scsi_low_softc *slp; { - struct scsi_low_softc *slp = ti->ti_sc; - struct lun_info *li = ti->ti_li; + struct targ_info *ti = slp->sl_Tnexus; struct scsi_low_msgout_data *mdp; u_int msgflags; - if (li == NULL) - { - /* not yet lun nexus established! */ - goto out; - } - - switch (ti->ti_sphase) + if (ti->ti_emsgflags != 0) { - case PH_CMD: - slp->sl_error |= CMDREJECT; - break; - - case PH_MSGOUT: - if (ti->ti_emsgflags == 0) - break; - + 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 ++) @@ -2260,50 +4119,60 @@ scsi_low_msginfunc_msg_reject(ti) { ti->ti_emsgflags &= ~mdp->md_flags; if (mdp->md_errfunc != NULL) - (*mdp->md_errfunc) (ti, msgflags); + (*mdp->md_errfunc) (slp, msgflags); break; } } - break; - - default: - break; + return 0; } - -out: - scsi_low_info(slp, ti, "msg rejected"); - slp->sl_error |= MSGERR; - return 0; + else + { + SCSI_LOW_INFO(slp, ti, "MSGIN: rejected msg not found"); + slp->sl_error |= MSGERR; + } + return EINVAL; } -void +int scsi_low_msgin(slp, ti, c) struct scsi_low_softc *slp; struct targ_info *ti; - u_int8_t c; + 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) { - ti->ti_sphase = ti->ti_ophase; - ti->ti_ophase = ti->ti_phase; MSGINPTR_CLR(ti); -#ifdef SCSI_LOW_DIAGNOSTIC - ti->ti_msgin_hist_pointer = 0; -#endif /* SCSI_LOW_DIAGNOSTIC */ + 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 ++] = c; + 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; @@ -2311,6 +4180,19 @@ scsi_low_msgin(slp, ti, c) } /* + * 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]; @@ -2322,188 +4204,645 @@ scsi_low_msgin(slp, ti, c) if (ti->ti_msginlen == 0) { ti->ti_msginlen = sdp->md_len; -#ifdef SCSI_LOW_DIAGNOSTIC - if (ti->ti_msgin_hist_pointer < MSGIN_HISTORY_LEN) - { - ti->ti_msgin_history[ti->ti_msgin_hist_pointer] = msg; - ti->ti_msgin_hist_pointer ++; - } -#endif /* SCSI_LOW_DIAGNOSTIC */ } /* * Check comletion. */ if (ti->ti_msginptr < ti->ti_msginlen) - return; + return EJUSTRETURN; /* * Do process. */ if ((msg & MSG_IDENTIFY) == 0) { - (void) ((*sdp->md_msgfunc) (ti)); + if (((*sdp->md_msgfunc) (slp)) == EJUSTRETURN) + return EJUSTRETURN; } else { - li = ti->ti_li; + li = slp->sl_Lnexus; if (li == NULL) { - li = scsi_low_establish_lun(ti, MSGCMD_LUN(msg)); + 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 (ti->ti_nexus == NULL) + if (slp->sl_Qnexus == NULL && li->li_nqio == 0) { - /* XXX: - * move the following functions to - * tag queue msg process in the future. - */ 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; + } } - - if (MSGCMD_LUN(msg) != li->li_lun) - goto badlun; } + goto out; /* - * Msg process completed, reset msin pointer and assert ATN if desired. + * Msg process completed, reset msgin pointer and assert ATN if desired. */ - if (ti->ti_msginptr >= ti->ti_msginlen) +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) { - ti->ti_sphase = ti->ti_phase; - MSGINPTR_CLR(ti); + 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; - if (scsi_low_is_msgout_continue(ti) != 0) + 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 + **********************************************************/ +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 */ +} + +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 - printf("SCSI_LOW_ATTETION(msgin): 0x%x\n", - ti->ti_msgflags); + if (etag >= sizeof(li->li_qtagbits) * NBBY) + panic("scsi_low_dealloc_tag: illegal tag"); #endif /* SCSI_LOW_DIAGNOSTIC */ - scsi_low_attention(slp, ti); - } + 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; +} + +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\n", slp->sl_xname); } - return; +#endif /* SCSI_LOW_DIAGNOSTIC */ -badlun: - scsi_low_info(slp, ti, "MSGIN: identify lun mismatch"); - scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); + 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\n", 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; + } } +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 **************************************************************/ -#define MAXOFFSET 0x10 - static void -scsi_low_calcf(ti, li) - struct targ_info *ti; +scsi_low_calcf_lun(li) struct lun_info *li; { - u_int period; - u_int8_t offset; + 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 && -#ifdef SDEV_NODISC - (li->li_quirks & SDEV_NODISC) == 0 && -#endif /* SDEV_NODISC */ - (li->li_cfgflags & SCSI_LOW_DISC) != 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 && -#ifdef SDEV_NOPARITY - (li->li_quirks & SDEV_NOPARITY) == 0 && -#endif /* SDEV_NOPARITY */ - (li->li_cfgflags & SCSI_LOW_NOPARITY) == 0) + (diskflags & SCSI_LOW_DISK_PARITY) != 0 && + (cfgflags & SCSI_LOW_NOPARITY) == 0) li->li_flags &= ~SCSI_LOW_NOPARITY; - li->li_flags &= ~SCSI_LOW_SYNC; - if ((li->li_cfgflags & SCSI_LOW_SYNC) && - (slp->sl_cfgflags & CFG_ASYNC) == 0) + /* qtag */ + if ((slp->sl_cfgflags & CFG_NOQTAG) == 0 && + (cfgflags & SCSI_LOW_QTAG) != 0 && + (diskflags & SCSI_LOW_DISK_QTAG) != 0) { - offset = SCSI_LOW_OFFSET(li->li_cfgflags); - if (offset > ti->ti_maxsynch.offset) - offset = ti->ti_maxsynch.offset; - li->li_flags |= SCSI_LOW_SYNC; + li->li_flags |= SCSI_LOW_QTAG; + li->li_maxnexus = SCSI_LOW_MAXNEXUS; + li->li_maxnqio = li->li_maxnexus; } else - offset = 0; - - if (offset > 0) { - period = SCSI_LOW_PERIOD(li->li_cfgflags); - if (period > SCSI_LOW_MAX_SYNCH_SPEED) - period = SCSI_LOW_MAX_SYNCH_SPEED; - if (period != 0) - period = 1000 * 10 / (period * 4); - if (period < ti->ti_maxsynch.period) - period = ti->ti_maxsynch.period; + li->li_flags &= ~SCSI_LOW_QTAG; + li->li_maxnexus = 0; + li->li_maxnqio = li->li_maxnexus; } - else - period = 0; + /* 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 */ } -#ifdef SCSI_LOW_TARGET_OPEN -static int -scsi_low_target_open(link, cf) - struct scsipi_link *link; - struct cfdata *cf; +static void +scsi_low_calcf_show(li) + struct lun_info *li; { - u_int target = link->scsipi_scsi.target; - u_int lun = link->scsipi_scsi.lun; + 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 __P((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; - 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; + printf("%s: scsi_low: probing all devices ....\n", slp->sl_xname); - li->li_quirks = (u_int) link->quirks; - li->li_cfgflags = cf->cf_flags; - if (li->li_state > UNIT_SYNCH) - li->li_state = UNIT_SYNCH; + 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; + } - scsi_low_calcf(ti, li); + if ((slp->sl_show_result & SHOW_PROBE_RES) != 0) + { + printf("%s: scsi_low: target %d lun ", + slp->sl_xname, target); + } - printf("%s(%d:%d): max period(%dns) max offset(%d) flags 0x%b\n", - slp->sl_xname, target, lun, - ti->ti_maxsynch.period * 4, - ti->ti_maxsynch.offset, - li->li_flags, SCSI_LOW_BITS); + 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; } -#endif /* SCSI_LOW_TARGET_OPEN */ + +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; { - printf("%s: SCSI_LOW: %s\n", slp->sl_xname, 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) { - TAILQ_FOREACH(ti, &slp->sl_titab, ti_chain) + 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[] = @@ -2517,76 +4856,87 @@ scsi_low_print(slp, ti) struct scsi_low_softc *slp; struct targ_info *ti; { - struct slccb *cb = NULL; + struct lun_info *li; + struct slccb *cb; + struct sc_p *sp; - if (ti == NULL) - ti = slp->sl_nexus; - if (ti != NULL) - cb = ti->ti_nexus; + 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: TARGET(0x%lx) T_NEXUS(0x%lx) C_NEXUS(0x%lx) NDISCS(%d)\n", - slp->sl_xname, (u_long) ti, (u_long) slp->sl_nexus, - (u_long) cb, slp->sl_disc); + 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) { - struct sc_p *sp = &slp->sl_scp; - struct lun_info *li = ti->ti_li; - u_int flags = 0; + 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>\n", slp->sl_xname, + 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]); - -printf("MSGIN: ptr(%x) [%x][%x][%x][%x][%x] STATUSIN: 0x%x T_FLAGS: 0x%x\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]), - ti->ti_status, ti->ti_tflags); -#ifdef SCSI_LOW_DIAGNOSTIC -printf("MSGIN HISTORY: (%d) [0x%x] => [0x%x] => [0x%x] => [0x%x] => [0x%x]\n", - ti->ti_msgin_hist_pointer, - (u_int) (ti->ti_msgin_history[0]), - (u_int) (ti->ti_msgin_history[1]), - (u_int) (ti->ti_msgin_history[2]), - (u_int) (ti->ti_msgin_history[3]), - (u_int) (ti->ti_msgin_history[4])); -#endif /* SCSI_LOW_DIAGNOSTIC */ - -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); - -printf("SCP: datalen 0x%x dataaddr 0x%lx ", - sp->scp_datalen, - (u_long) sp->scp_data); + phase[(int) ti->ti_phase], ti->ti_disc, + nqio, maxnqio); if (cb != NULL) { -printf("CCB: cmdlen %x cmdaddr %lx cmd[0] %x datalen %x", - cb->ccb_scp.scp_cmdlen, - (u_long) cb->ccb_scp.scp_cmd, +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_datalen); + 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("\n"); + +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("error flags %b\n", slp->sl_error, SCSI_LOW_ERRORBITS); + + 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); } |