2bb789d7a3
NOTE: These changes will require recompilation of any userland applications, like cdrecord, xmcd, etc., that use the CAM passthrough interface. A make world is recommended. camcontrol.[c8]: - We now support two new commands, "tags" and "negotiate". - The tags commands allows users to view the number of tagged openings for a device as well as a number of other related parameters, and it allows users to set tagged openings for a device. - The negotiate command allows users to enable and disable disconnection and tagged queueing, set sync rates, offsets and bus width. Note that not all of those features are available for all controllers. Only the adv, ahc, and ncr drivers fully support all of the features at this point. Some cards do not allow the setting of sync rates, offsets and the like, and some of the drivers don't have any facilities to do so. Some drivers, like the adw driver, only support enabling or disabling sync negotiation, but do not support setting sync rates. - new description in the camcontrol man page of how to format a disk - cleanup of the camcontrol inquiry command - add support in the 'devlist' command for skipping unconfigured devices if -v was not specified on the command line. - make use of the new base_transfer_speed in the path inquiry CCB. - fix CCB bzero cases cam_xpt.c, cam_sim.[ch], cam_ccb.h: - new flags on many CCB function codes to designate whether they're non-immediate, use a user-supplied CCB, and can only be passed from userland programs via the xpt device. Use these flags in the transport layer and pass driver to categorize CCBs. - new flag in the transport layer device matching code for device nodes that indicates whether a device is unconfigured - bump the CAM version from 0x10 to 0x11 - Change the CAM ioctls to use the version as their group code, so we can force users to recompile code even when the CCB size doesn't change. - add + fill in a new value in the path inquiry CCB, base_transfer_speed. Remove a corresponding field from the cam_sim structure, and add code to every SIM to set this field to the proper value. - Fix the set transfer settings code in the transport layer. scsi_cd.c: - make some variables volatile instead of just casting them in various places - fix a race condition in the changer code - attach unless we get a "logical unit not supported" error. This should fix all of the cases where people have devices that return weird errors when they don't have media in the drive. scsi_da.c: - attach unless we get a "logical unit not supported" error scsi_pass.c: - for immediate CCBs, just malloc a CCB to send the user request in. This gets rid of the 'held' count problem in camcontrol tags. scsi_pass.h: - change the CAM ioctls to use the CAM version as their group code. adv driver: - Allow changing the sync rate and offset separately. adw driver - Allow changing the sync rate and offset separately. aha driver: - Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs. ahc driver: - Allow setting offset and sync rate separately bt driver: - Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs. NCR driver: - Fix the ultra/ultra 2 negotiation bug - allow setting both the sync rate and offset separately Other HBA drivers: - Put code in to set the base_transfer_speed field for XPT_GET_TRAN_SETTINGS CCBs. Reviewed by: gibbs, mjacob (isp), imp (aha)
1674 lines
36 KiB
C
1674 lines
36 KiB
C
/* $NecBSD: bs.c,v 1.1 1997/07/18 09:18:59 kmatsuda Exp $ */
|
|
/* $NetBSD$ */
|
|
/*
|
|
* [NetBSD for NEC PC98 series]
|
|
* Copyright (c) 1994, 1995, 1996 NetBSD/pc98 porting staff.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/*
|
|
* Copyright (c) 1994, 1995, 1996 Naofumi HONDA. All rights reserved.
|
|
*/
|
|
|
|
#ifdef __NetBSD__
|
|
#include <i386/Cbus/dev/bs/bsif.h>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <i386/isa/bs/bsif.h>
|
|
#endif
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_sim.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
#include <cam/cam_debug.h>
|
|
|
|
#include <cam/scsi/scsi_all.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
|
|
/*****************************************************************
|
|
* Inline phase funcs
|
|
*****************************************************************/
|
|
/* static inline declare */
|
|
static BS_INLINE struct targ_info *bs_reselect __P((struct bs_softc *));
|
|
static BS_INLINE void bs_sat_continue __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
static BS_INLINE struct targ_info *bs_selected __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
static BS_INLINE u_int8_t bs_read_1byte __P((struct bs_softc *));
|
|
static BS_INLINE void bs_write_1byte __P((struct bs_softc *, u_int8_t));
|
|
static BS_INLINE void bs_commandout __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
static BS_INLINE void bs_status_check __P((struct bs_softc *, struct targ_info *));
|
|
static BS_INLINE void bs_msgin __P((struct bs_softc *, struct targ_info *));
|
|
static BS_INLINE void bs_msgout __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
static BS_INLINE void bs_disconnect_phase __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
static void bs_phase_error __P((struct targ_info *, struct bsccb *));
|
|
static int bs_scsi_cmd_poll_internal __P((struct targ_info *));
|
|
static int bs_xfer __P((struct bs_softc *, char *, int));
|
|
static void bs_io_xfer __P((struct targ_info *));
|
|
static void bs_quick_abort __P((struct targ_info *, u_int));
|
|
static void bs_msgin_error __P((struct targ_info *, u_int));
|
|
static void bs_msgin_ext __P((struct targ_info *));
|
|
static void bs_msg_reject __P((struct targ_info *));
|
|
static void bshoststart __P((struct bs_softc *, struct targ_info *));
|
|
|
|
/*****************************************************************
|
|
* SIM interface
|
|
*****************************************************************/
|
|
void
|
|
bs_scsi_cmd(struct cam_sim *sim, union ccb *ccb)
|
|
{
|
|
struct bs_softc *bsc = (struct bs_softc *) cam_sim_softc(sim);
|
|
int s, target = (u_int) (ccb->ccb_h.target_id);
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
|
|
switch (ccb->ccb_h.func_code) {
|
|
case XPT_SCSI_IO: /* Execute the requested I/O operation */
|
|
ti = bsc->sc_ti[target];
|
|
if ((cb = bs_get_ccb()) == NULL) {
|
|
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
|
|
xpt_done(ccb);
|
|
return;
|
|
}
|
|
|
|
/* make up ccb! */
|
|
cb->ccb = ccb;
|
|
cb->lun = ccb->ccb_h.target_lun;
|
|
cb->cmd = ccb->csio.cdb_io.cdb_bytes;
|
|
cb->cmdlen = (int) ccb->csio.cdb_len;
|
|
cb->data = ccb->csio.data_ptr;
|
|
cb->datalen = (int) ccb->csio.dxfer_len;
|
|
cb->rcnt = 0;
|
|
cb->msgoutlen = 0;
|
|
cb->bsccb_flags = 0;
|
|
bs_targ_flags(ti, cb);
|
|
cb->tcmax = 0;/*(xs->timeout >> 10); default HN2*/
|
|
if (cb->tcmax < BS_DEFAULT_TIMEOUT_SECOND)
|
|
cb->tcmax = BS_DEFAULT_TIMEOUT_SECOND;
|
|
|
|
s = splbio();
|
|
|
|
TAILQ_INSERT_TAIL(&ti->ti_ctab, cb, ccb_chain);
|
|
|
|
if (ti->ti_phase == FREE) {
|
|
if (ti->ti_state == BS_TARG_START)
|
|
bs_start_syncmsg(ti, NULL, BS_SYNCMSG_ASSERT);
|
|
bscmdstart(ti, BSCMDSTART);
|
|
}
|
|
|
|
splx(s);
|
|
break;
|
|
case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */
|
|
case XPT_EN_LUN: /* Enable LUN as a target */
|
|
case XPT_TARGET_IO: /* Execute target I/O request */
|
|
case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */
|
|
case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/
|
|
case XPT_ABORT: /* Abort the specified CCB */
|
|
/* XXX Implement */
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
xpt_done(ccb);
|
|
break;
|
|
case XPT_SET_TRAN_SETTINGS:
|
|
/* 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 s;*/
|
|
|
|
cts = &ccb->cts;
|
|
ti = bsc->sc_ti[ccb->ccb_h.target_id];
|
|
/*s = splcam();*/
|
|
if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) {
|
|
if (ti->ti_cfgflags & BS_SCSI_DISC)
|
|
cts->flags = CCB_TRANS_DISC_ENB;
|
|
else
|
|
cts->flags = 0;
|
|
if (ti->ti_cfgflags & BS_SCSI_QTAG)
|
|
cts->flags |= CCB_TRANS_TAG_ENB;
|
|
cts->sync_period = ti->ti_syncnow.period;
|
|
cts->sync_offset = ti->ti_syncnow.offset;
|
|
cts->bus_width = 0;/*HN2*/
|
|
|
|
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;
|
|
|
|
/*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;
|
|
|
|
ccg = &ccb->ccg;
|
|
size_mb = ccg->volume_size
|
|
/ ((1024L * 1024L) / ccg->block_size);
|
|
|
|
ccg->heads = 8;
|
|
ccg->secs_per_track = 34;
|
|
|
|
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 */
|
|
bshw_chip_reset(bsc); /* XXX need perfect RESET? */
|
|
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 = NTARGETS - 1;
|
|
cpi->max_lun = 7;
|
|
cpi->initiator_id = bsc->sc_hostid;
|
|
cpi->bus_id = cam_sim_bus(sim);
|
|
cpi->base_transfer_speed = 3300;
|
|
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
|
|
strncpy(cpi->hba_vid, "NEC", 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("bs: non support func_code = %d ", ccb->ccb_h.func_code);*/
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
xpt_done(ccb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
* ### NEXUS START and TERMINATE ###
|
|
**************************************************/
|
|
/*
|
|
* FLAGS : BSCMDRESTART restart in case of error.
|
|
*/
|
|
int
|
|
bscmdstart(ti, flags)
|
|
struct targ_info *ti;
|
|
int flags;
|
|
{
|
|
struct bsccb *cb;
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
|
|
if ((cb = ti->ti_ctab.tqh_first) == NULL)
|
|
{
|
|
if (bsc->sc_nexus == NULL)
|
|
bshoststart(bsc, NULL);
|
|
return 0;
|
|
}
|
|
|
|
ti->ti_lun = cb->lun;
|
|
ti->ti_error = 0;
|
|
ti->ti_scsp.data = cb->data;
|
|
ti->ti_scsp.datalen = cb->datalen;
|
|
ti->ti_scsp.seglen = 0;
|
|
if (cb->rcnt)
|
|
cb->bsccb_flags &= ~(BSSAT | BSLINK);
|
|
ti->ti_flags &= ~BSCFLAGSMASK;
|
|
ti->ti_flags |= cb->bsccb_flags & BSCFLAGSMASK;
|
|
cb->tc = cb->tcmax;
|
|
|
|
/* GO GO */
|
|
if (ti->ti_phase == FREE)
|
|
{
|
|
if (bsc->sc_nexus == NULL)
|
|
bshoststart(bsc, ti);
|
|
else
|
|
{
|
|
if (flags & BSCMDRESTART)
|
|
bs_hostque_head(bsc, ti);
|
|
else
|
|
bs_hostque_tail(bsc, ti);
|
|
BS_SETUP_PHASE(HOSTQUEUE)
|
|
}
|
|
}
|
|
else if (bsc->sc_nexus == NULL)
|
|
bshoststart(bsc, NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct bsccb *
|
|
bscmddone(ti)
|
|
struct targ_info *ti;
|
|
{
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
struct bsccb *cb = ti->ti_ctab.tqh_first;
|
|
union ccb *ccb;
|
|
int error;
|
|
|
|
if (ti->ti_state == BS_TARG_SYNCH)
|
|
{
|
|
if (bs_analyze_syncmsg(ti, cb))
|
|
return cb;
|
|
}
|
|
|
|
if (bsc->sc_p.datalen != 0)
|
|
ti->ti_error |= BSDMAABNORMAL;
|
|
|
|
cb->error = ti->ti_error;
|
|
|
|
do
|
|
{
|
|
ccb = cb->ccb;
|
|
error = CAM_REQ_CMP;
|
|
|
|
if (cb->bsccb_flags & (BSITSDONE | BSSENSECCB | BSCASTAT))
|
|
{
|
|
if (cb->bsccb_flags & BSSENSECCB)
|
|
{
|
|
cb->error &= ~BSDMAABNORMAL;
|
|
if (cb->error == 0)
|
|
ti->ti_flags |= BSCASTAT;
|
|
|
|
ti->ti_flags |= BSERROROK;
|
|
}
|
|
else if (cb->bsccb_flags & BSCASTAT)
|
|
{
|
|
if (ti->ti_flags & BSCASTAT)
|
|
{
|
|
ti->ti_flags &= ~BSCASTAT;
|
|
error = CAM_AUTOSNS_VALID|CAM_SCSI_STATUS_ERROR;
|
|
if (ccb)
|
|
ccb->csio.sense_data = ti->sense;/* XXX may not be csio.... */
|
|
}
|
|
else
|
|
error = CAM_AUTOSENSE_FAIL;
|
|
ti->ti_flags |= BSERROROK;
|
|
} else
|
|
bs_panic(bsc, "internal error");
|
|
}
|
|
|
|
while (cb->error)
|
|
{
|
|
if (ti->ti_flags & BSERROROK)
|
|
break;
|
|
|
|
if (cb->rcnt >= bsc->sc_retry || (cb->error & BSFATALIO))
|
|
{
|
|
if (cb->error & (BSSELTIMEOUT | BSTIMEOUT))
|
|
error = CAM_CMD_TIMEOUT;
|
|
else if (cb->error & BSTARGETBUSY)
|
|
error = CAM_SCSI_STATUS_ERROR;
|
|
else
|
|
error = CAM_REQ_CMP_ERR;
|
|
break;
|
|
}
|
|
|
|
if (cb->error & BSREQSENSE)
|
|
{
|
|
/* must clear the target's sense state */
|
|
cb->rcnt++;
|
|
cb->bsccb_flags |= (BSITSDONE | BSCASTAT);
|
|
cb->error &= ~BSREQSENSE;
|
|
return bs_request_sense(ti);
|
|
}
|
|
|
|
/* XXX: compat with upper driver */
|
|
if ((cb->error & BSDMAABNORMAL) &&
|
|
BSHW_CMD_CHECK(cb, BSERROROK))
|
|
{
|
|
cb->error &= ~BSDMAABNORMAL;
|
|
continue;
|
|
}
|
|
if (/*(xs && xs->bp) || can't know whether bufferd i/o or not */
|
|
(cb->error & BSSELTIMEOUT) == 0)
|
|
bs_debug_print(bsc, ti);
|
|
cb->rcnt++;
|
|
return cb;
|
|
}
|
|
|
|
#ifdef BS_DIAG
|
|
cb->bsccb_flags |= BSITSDONE;
|
|
#endif /* BS_DIAG */
|
|
if (bsc->sc_poll)
|
|
{
|
|
bsc->sc_flags |= BSJOBDONE;
|
|
if (bsc->sc_outccb == cb)
|
|
bsc->sc_flags |= BSPOLLDONE;
|
|
}
|
|
|
|
TAILQ_REMOVE(&ti->ti_ctab, cb, ccb_chain);
|
|
|
|
if (ccb)
|
|
{
|
|
ccb->ccb_h.status = error;
|
|
ccb->csio.scsi_status = ti->ti_status;/*XXX*/
|
|
xpt_done(ccb);
|
|
}
|
|
|
|
bs_free_ccb(cb);
|
|
cb = ti->ti_ctab.tqh_first;
|
|
|
|
}
|
|
while (cb != NULL && (cb->bsccb_flags & BSITSDONE) != 0);
|
|
|
|
/* complete */
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************
|
|
* ### PHASE FUNCTIONS ###
|
|
**************************************************/
|
|
/**************************************************
|
|
* <SELECTION PHASE>
|
|
**************************************************/
|
|
static void
|
|
bshoststart(bsc, ti)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
{
|
|
struct bsccb *cb;
|
|
int s;
|
|
|
|
if (bsc->sc_flags & BSINACTIVE)
|
|
return;
|
|
|
|
again:
|
|
if (ti == NULL)
|
|
{
|
|
if ((ti = bsc->sc_sttab.tqh_first) == NULL)
|
|
return;
|
|
bs_hostque_delete(bsc, ti);
|
|
}
|
|
|
|
if ((cb = ti->ti_ctab.tqh_first) == NULL)
|
|
{
|
|
bs_printf(ti, "bshoststart", "Warning: No ccb");
|
|
BS_SETUP_PHASE(FREE);
|
|
ti = NULL;
|
|
goto again;
|
|
}
|
|
|
|
#ifdef BS_DIAG
|
|
if (cb->bsccb_flags & BSITSDONE)
|
|
bs_panic(bsc, "bshoststart: already done");
|
|
|
|
if (bsc->sc_nexus || (ti->ti_flags & BSNEXUS))
|
|
{
|
|
char *s = ((ti->ti_flags & BSNEXUS) ?
|
|
"nexus already established" : "scsi board busy");
|
|
|
|
bs_debug_print(bsc, ti);
|
|
bs_printf(ti, "bshoststart", s);
|
|
}
|
|
#endif /* BS_DIAG */
|
|
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select++;
|
|
#endif /* BS_STATICS */
|
|
|
|
if (ti->ti_cfgflags & BS_SCSI_WAIT)
|
|
{
|
|
struct targ_info *tmpti;
|
|
|
|
for (tmpti = bsc->sc_titab.tqh_first; tmpti;
|
|
tmpti = tmpti->ti_tchain.tqe_next)
|
|
if (tmpti->ti_phase >= DISCONNECTED)
|
|
goto retry;
|
|
}
|
|
|
|
/* start selection */
|
|
ti->ti_status = ST_UNK;
|
|
if (bs_check_sat(ti))
|
|
{
|
|
if ((bshw_get_auxstat(bsc) & STR_BUSY) == 0)
|
|
{
|
|
BS_LOAD_SDP
|
|
bshw_set_dst_id(bsc, ti->ti_id, ti->ti_lun);
|
|
bshw_setup_ctrl_reg(bsc, ti->ti_cfgflags);
|
|
bshw_cmd_pass(bsc, 0);
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
bshw_issue_satcmd(bsc, cb, bs_check_link(ti, cb));
|
|
if (bs_check_smit(ti) || bsc->sc_p.datalen <= 0)
|
|
bshw_set_count(bsc, 0);
|
|
else
|
|
bs_dma_xfer(ti, BSHW_CMD_CHECK(cb, BSREAD));
|
|
|
|
s = splhigh();
|
|
if ((bshw_get_auxstat(bsc) & STR_BUSY) == 0)
|
|
{
|
|
/* XXX:
|
|
* Reload a lun again here.
|
|
*/
|
|
bshw_set_lun(bsc, ti->ti_lun);
|
|
bshw_start_sat(bsc, bs_check_disc(ti));
|
|
if ((bshw_get_auxstat(bsc) & STR_LCI) == 0)
|
|
{
|
|
splx(s);
|
|
BS_HOST_START
|
|
BS_SELECTION_START
|
|
BS_SETUP_PHASE(SATSEL);
|
|
ti->ti_omsgoutlen = 0;
|
|
ti->ti_msgout = bs_identify_msg(ti);
|
|
#ifdef BS_DIAG
|
|
ti->ti_flags |= BSNEXUS;
|
|
#endif /* BS_DIAG */
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_win++;
|
|
#endif /* BS_STATICS */
|
|
return;
|
|
}
|
|
}
|
|
splx(s);
|
|
|
|
if (bs_check_smit(ti) == 0)
|
|
bshw_dmaabort(bsc, ti);
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_miss_in_assert++;
|
|
#endif /* BS_STATICS */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s = splhigh();
|
|
if ((bshw_get_auxstat(bsc) & STR_BUSY) == 0)
|
|
{
|
|
bshw_set_dst_id(bsc, ti->ti_id, ti->ti_lun);
|
|
bshw_setup_ctrl_reg(bsc, ti->ti_cfgflags);
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
bshw_assert_select(bsc);
|
|
|
|
if ((bshw_get_auxstat(bsc) & STR_LCI) == 0)
|
|
{
|
|
splx(s);
|
|
BS_HOST_START
|
|
BS_SELECTION_START
|
|
BS_SETUP_PHASE(SELECTASSERT);
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_win++;
|
|
#endif /* BS_STATICS */
|
|
return;
|
|
}
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_miss_in_assert++;
|
|
#endif /* BS_STATICS */
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
/* RETRY LATER */
|
|
retry:
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_miss++;
|
|
#endif /* BS_STATICS */
|
|
bs_hostque_head(bsc, ti);
|
|
BS_SETUP_PHASE(HOSTQUEUE)
|
|
}
|
|
|
|
static BS_INLINE struct targ_info *
|
|
bs_selected(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
|
|
if (bsc->sc_busstat != BSR_SELECTED)
|
|
{
|
|
bs_phase_error(ti, cb);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef BS_DIAG
|
|
if (bsc->sc_selwait != ti)
|
|
panic("%s selection internal error\n", bsc->sc_dvname);
|
|
|
|
ti->ti_flags |= BSNEXUS;
|
|
#endif /* BS_DIAG */
|
|
|
|
/* clear select wait state */
|
|
BS_SETUP_PHASE(SELECTED);
|
|
BS_SELECTION_TERMINATE;
|
|
BS_LOAD_SDP
|
|
return ti;
|
|
}
|
|
|
|
/**************************************************
|
|
* <RESELECTION>
|
|
**************************************************/
|
|
static BS_INLINE struct targ_info *
|
|
bs_reselect(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
u_int target;
|
|
struct targ_info *ti;
|
|
|
|
/* check collision */
|
|
if ((ti = bsc->sc_selwait) != NULL)
|
|
{
|
|
if (ti->ti_phase == SATSEL)
|
|
{
|
|
#ifdef BS_DIAG
|
|
ti->ti_flags &= ~BSNEXUS;
|
|
#endif /* BS_DIAG */
|
|
ti->ti_msgout = 0;
|
|
if (bs_check_smit(ti) == 0)
|
|
bshw_dmaabort(bsc, ti);
|
|
}
|
|
bs_hostque_head(bsc, ti);
|
|
BS_SELECTION_TERMINATE
|
|
BS_SETUP_PHASE(HOSTQUEUE)
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].select_miss_by_reselect++;
|
|
bs_statics[ti->ti_id].select_miss++;
|
|
#endif /* BS_STATICS */
|
|
}
|
|
|
|
/* who are you ? */
|
|
target = bshw_get_src_id(bsc);
|
|
if ((ti = bsc->sc_ti[target]) == NULL)
|
|
{
|
|
bs_debug_print_all(bsc);
|
|
printf("reselect: miss reselect. target(%d)\n", target);
|
|
bs_reset_nexus(bsc);
|
|
return NULL;
|
|
}
|
|
|
|
/* confirm nexus */
|
|
BS_HOST_START
|
|
bshw_setup_ctrl_reg(bsc, ti->ti_cfgflags);
|
|
if (ti->ti_ctab.tqh_first == NULL || ti->ti_phase != DISCONNECTED)
|
|
{
|
|
bs_printf(ti, "reselect", "phase mismatch");
|
|
BS_SETUP_PHASE(UNDEF)
|
|
bs_force_abort(ti);
|
|
bs_hostque_delete(bsc, ti);
|
|
}
|
|
else
|
|
bsc->sc_dtgnum --;
|
|
|
|
/* recover host */
|
|
bshw_set_dst_id(bsc, ti->ti_id, ti->ti_lun);
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
BS_RESTORE_SDP
|
|
BS_SETUP_PHASE(RESELECTED)
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].reselect++;
|
|
#endif /* BS_STATICS */
|
|
return ti;
|
|
}
|
|
|
|
static BS_INLINE void
|
|
bs_sat_continue(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
|
|
BS_SETUP_PHASE(SATRESEL);
|
|
bshw_set_dst_id(bsc, ti->ti_id, ti->ti_lun);
|
|
bshw_cmd_pass(bsc, 0x44);
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
bshw_issue_satcmd(bsc, cb, 0);
|
|
if (bs_check_smit(ti) || bsc->sc_p.datalen <= 0)
|
|
bshw_set_count(bsc, 0);
|
|
else
|
|
bs_dma_xfer(ti, BSHW_CMD_CHECK(cb, BSREAD));
|
|
bshw_set_lun(bsc, ti->ti_lun); /* XXX */
|
|
bshw_start_sat(bsc, 0);
|
|
}
|
|
|
|
/*************************************************
|
|
* <DATA PHASE>
|
|
*************************************************/
|
|
#define DR (STR_BSY | STR_DBR)
|
|
|
|
void
|
|
bs_poll_timeout(bsc, s)
|
|
struct bs_softc *bsc;
|
|
char *s;
|
|
{
|
|
struct targ_info *ti;
|
|
|
|
bs_printf(NULL, s, "timeout");
|
|
bsc->sc_flags |= BSRESET;
|
|
if ((ti = bsc->sc_nexus) && ti->ti_ctab.tqh_first)
|
|
ti->ti_error |= BSTIMEOUT;
|
|
}
|
|
|
|
static BS_INLINE u_int8_t
|
|
bs_read_1byte(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
register u_int wc;
|
|
|
|
bshw_start_sxfer(bsc);
|
|
for (wc = bsc->sc_wc; (bshw_get_auxstat(bsc) & DR) != DR && --wc; );
|
|
if (wc)
|
|
return bshw_read_data(bsc);
|
|
else
|
|
bs_poll_timeout(bsc, "read_1byte");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BS_INLINE void
|
|
bs_write_1byte(bsc, data)
|
|
struct bs_softc *bsc;
|
|
u_int8_t data;
|
|
{
|
|
register u_int wc;
|
|
|
|
bshw_start_sxfer(bsc);
|
|
for (wc = bsc->sc_wc; (bshw_get_auxstat(bsc) & DR) != DR && --wc; );
|
|
if (wc)
|
|
bshw_write_data(bsc, data);
|
|
else
|
|
bs_poll_timeout(bsc, "write_1byte");
|
|
}
|
|
|
|
static int
|
|
bs_xfer(bsc, data, len)
|
|
struct bs_softc *bsc;
|
|
char *data;
|
|
int len;
|
|
{
|
|
u_int8_t aux;
|
|
u_int count, wc;
|
|
|
|
bshw_set_count(bsc, len);
|
|
bshw_start_xfer(bsc);
|
|
|
|
for (count = 0, wc = bsc->sc_wc; count < len && --wc; )
|
|
{
|
|
if (((aux = bshw_get_auxstat(bsc)) & DR) == DR)
|
|
{
|
|
if (bsc->sc_busstat & BSHW_READ)
|
|
*(data++) = bshw_read_data(bsc);
|
|
else
|
|
bshw_write_data(bsc, *(data++));
|
|
count++;
|
|
wc = bsc->sc_wc;
|
|
}
|
|
|
|
if (aux & STR_INT)
|
|
break;
|
|
}
|
|
|
|
if (wc == 0)
|
|
bs_poll_timeout(bsc, "bs_xfer");
|
|
|
|
return count;
|
|
}
|
|
#undef DR
|
|
|
|
static void
|
|
bs_io_xfer(ti)
|
|
struct targ_info *ti;
|
|
{
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
struct sc_p *sp = &bsc->sc_p;
|
|
u_int count, dummy;
|
|
|
|
/* switch dma trasnfr mode */
|
|
bshw_set_poll_trans(bsc, ti->ti_cfgflags);
|
|
sp->seglen = 0;
|
|
sp->bufp = NULL;
|
|
|
|
if (sp->datalen <= 0)
|
|
{
|
|
ti->ti_error |= BSDMAABNORMAL;
|
|
dummy = 0;
|
|
count = bs_xfer(bsc, (u_int8_t *) &dummy, 1);
|
|
}
|
|
else
|
|
count = bs_xfer(bsc, sp->data, sp->datalen);
|
|
|
|
sp->data += count;
|
|
sp->datalen -= count;
|
|
}
|
|
|
|
/************************************************
|
|
* <COMMAND PHASE>
|
|
************************************************/
|
|
static BS_INLINE void
|
|
bs_commandout(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
u_int8_t scsi_cmd[16];
|
|
int len;
|
|
|
|
BS_SETUP_PHASE(CMDPHASE);
|
|
|
|
if (bs_check_link(ti, cb))
|
|
{
|
|
bcopy(cb->cmd, scsi_cmd, cb->cmdlen);
|
|
scsi_cmd[cb->cmdlen - 1] |= 0x01;
|
|
len = bs_xfer(bsc, scsi_cmd, cb->cmdlen);
|
|
}
|
|
else
|
|
len = bs_xfer(bsc, cb->cmd, cb->cmdlen);
|
|
|
|
if (len != cb->cmdlen)
|
|
ti->ti_error |= BSCMDABNORMAL;
|
|
}
|
|
|
|
/************************************************
|
|
* <STATUS IN>
|
|
************************************************/
|
|
static BS_INLINE void
|
|
bs_status_check(bsc, ti)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
{
|
|
|
|
if (ti->ti_status == ST_GOOD || ti->ti_status == ST_INTERGOOD)
|
|
return;
|
|
|
|
switch (ti->ti_status)
|
|
{
|
|
case ST_MET:
|
|
case ST_INTERMET:
|
|
case ST_CHKCOND:
|
|
ti->ti_error |= BSREQSENSE;
|
|
break;
|
|
|
|
case ST_BUSY:
|
|
ti->ti_error |= BSTARGETBUSY;
|
|
break;
|
|
|
|
default:
|
|
ti->ti_error |= BSSTATUSERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/************************************************
|
|
* <MSG IN>
|
|
************************************************/
|
|
#define MSGWAIT(cnt) { if (ti->ti_msginptr < (cnt)) return; }
|
|
|
|
static void
|
|
bs_quick_abort(ti, msg)
|
|
struct targ_info *ti;
|
|
u_int msg;
|
|
{
|
|
struct bsccb *cb;
|
|
|
|
if ((cb = ti->ti_ctab.tqh_first) == NULL)
|
|
return;
|
|
|
|
cb->msgoutlen = 1;
|
|
cb->msgout[0] = msg;
|
|
cb->rcnt++;
|
|
|
|
ti->ti_error |= BSMSGERROR;
|
|
}
|
|
|
|
static void
|
|
bs_msgin_error(ti, count)
|
|
struct targ_info *ti;
|
|
u_int count;
|
|
{
|
|
int n;
|
|
|
|
MSGWAIT(count);
|
|
|
|
bs_printf(ti, "msgin", "illegal msg");
|
|
|
|
for (n = 0; n < ti->ti_msginptr; n ++)
|
|
printf("[0x%x] ", (u_int) ti->ti_msgin[n]);
|
|
printf("\n");
|
|
|
|
bs_quick_abort(ti, MSG_REJECT);
|
|
ti->ti_msginptr = 0;
|
|
}
|
|
|
|
static void
|
|
bs_msgin_ext(ti)
|
|
struct targ_info *ti;
|
|
{
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
struct bsccb *cb = ti->ti_ctab.tqh_first;
|
|
int count;
|
|
u_int reqlen;
|
|
u_int32_t *ptr;
|
|
struct msgbase msg;
|
|
|
|
MSGWAIT(2);
|
|
|
|
reqlen = ti->ti_msgin[1];
|
|
if (reqlen == 0)
|
|
reqlen = 256;
|
|
|
|
if (ti->ti_msginptr >= MAXMSGLEN)
|
|
ti->ti_msginptr = 3; /* XXX */
|
|
|
|
MSGWAIT(reqlen + 2);
|
|
|
|
switch (MKMSG_EXTEND(ti->ti_msgin[1], ti->ti_msgin[2]))
|
|
{
|
|
case MKMSG_EXTEND(MSG_EXTEND_MDPLEN, MSG_EXTEND_MDPCODE):
|
|
ptr = (u_int32_t *)(&ti->ti_msgin[3]);
|
|
count = (int) htonl((long) (*ptr));
|
|
|
|
bsc->sc_p.seglen = ti->ti_scsp.seglen = 0;
|
|
if (bsc->sc_p.datalen - count >= 0 &&
|
|
bsc->sc_p.datalen - count <= cb->datalen)
|
|
{
|
|
bsc->sc_p.datalen -= count;
|
|
bsc->sc_p.data += count;
|
|
}
|
|
else
|
|
bs_msgin_error(ti, 7);
|
|
break;
|
|
|
|
case MKMSG_EXTEND(MSG_EXTEND_SYNCHLEN, MSG_EXTEND_SYNCHCODE):
|
|
ti->ti_syncnow.period = ti->ti_msgin[3];
|
|
ti->ti_syncnow.offset = ti->ti_msgin[4];
|
|
if (ti->ti_syncnow.offset == 0)
|
|
ti->ti_syncnow.period = 0;
|
|
|
|
if (ti->ti_syncnow.state != BS_SYNCMSG_ASSERT)
|
|
{
|
|
bs_start_syncmsg(ti, NULL, BS_SYNCMSG_REQUESTED);
|
|
bscmdstart(ti, BSCMDSTART);
|
|
}
|
|
else
|
|
BS_SETUP_SYNCSTATE(BS_SYNCMSG_ACCEPT)
|
|
break;
|
|
|
|
case MKMSG_EXTEND(MSG_EXTEND_WIDELEN, MSG_EXTEND_WIDECODE):
|
|
msg.msglen = MSG_EXTEND_WIDELEN + 2;
|
|
msg.msg[0] = MSG_EXTEND;
|
|
msg.msg[1] = MSG_EXTEND_WIDELEN;
|
|
msg.msg[2] = MSG_EXTEND_WIDECODE;
|
|
msg.msg[3] = 0;
|
|
msg.flag = 0;
|
|
bs_make_msg_ccb(ti, cb->lun, cb, &msg, 0);
|
|
break;
|
|
|
|
default:
|
|
bs_msgin_error(ti, 0);
|
|
return;
|
|
}
|
|
|
|
ti->ti_msginptr = 0;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
bs_msg_reject(ti)
|
|
struct targ_info *ti;
|
|
{
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
struct bsccb *cb = ti->ti_ctab.tqh_first;
|
|
char *s = "unexpected msg reject";
|
|
|
|
switch (ti->ti_ophase)
|
|
{
|
|
case CMDPHASE:
|
|
s = "cmd rejected";
|
|
cb->bsccb_flags &= ~BSLINK;
|
|
BS_SETUP_MSGPHASE(IOCOMPLETED);
|
|
break;
|
|
|
|
case MSGOUT:
|
|
if (ti->ti_msgout & 0x80)
|
|
{
|
|
s = "identify msg rejected";
|
|
cb->bsccb_flags &= ~BSDISC;
|
|
BS_SETUP_MSGPHASE(IOCOMPLETED);
|
|
}
|
|
else if (ti->ti_msgout == MSG_EXTEND)
|
|
{
|
|
switch (ti->ti_emsgout)
|
|
{
|
|
case MSG_EXTEND_SYNCHCODE:
|
|
BS_SETUP_SYNCSTATE(BS_SYNCMSG_REJECT);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bs_debug_print(bsc, ti);
|
|
bs_printf(ti, "msgin", s);
|
|
ti->ti_error |= BSMSGERROR;
|
|
}
|
|
|
|
static BS_INLINE void
|
|
bs_msgin(bsc, ti)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
{
|
|
|
|
BS_SETUP_PHASE(MSGIN);
|
|
|
|
switch (ti->ti_msgin[0])
|
|
{
|
|
case MSG_SAVESP:
|
|
BS_SAVE_SDP
|
|
break;
|
|
|
|
case MSG_RESTORESP:
|
|
BS_RESTORE_SDP
|
|
bs_printf(ti, "msgin", "restore scsi pointer");
|
|
break;
|
|
|
|
case MSG_REJECT:
|
|
bs_msg_reject(ti);
|
|
break;
|
|
|
|
case 0xf:
|
|
break;
|
|
|
|
case MSG_I_ERROR:/* all I -> T : nothing to do*/
|
|
case MSG_ABORT:
|
|
case MSG_PARITY:
|
|
case MSG_RESET:
|
|
case 0xe:
|
|
bs_msgin_error(ti, 1);
|
|
goto resume;
|
|
|
|
case MSG_NOOP:
|
|
break;
|
|
|
|
case MSG_EXTEND:
|
|
bs_msgin_ext(ti);
|
|
goto resume;
|
|
|
|
case 0xd:
|
|
bs_msgin_error(ti, 2);
|
|
goto resume;
|
|
|
|
case MSG_DISCON:
|
|
BS_SETUP_MSGPHASE(DISCONNECTASSERT);
|
|
break;
|
|
|
|
case MSG_COMP:
|
|
BS_SETUP_MSGPHASE(IOCOMPLETED);
|
|
break;
|
|
|
|
case MSG_LCOMP:
|
|
case MSG_LCOMP_F:
|
|
bs_status_check(bsc, ti);
|
|
if (bscmddone(ti) == NULL)
|
|
{
|
|
if (bscmdstart(ti, BSCMDSTART) == 0)
|
|
{
|
|
bs_printf(ti, "msgin", "cmd line miss");
|
|
bs_force_abort(ti);
|
|
}
|
|
}
|
|
else
|
|
bscmdstart(ti, BSCMDRESTART);
|
|
#ifdef BS_STATICS
|
|
bs_linkcmd_count[ti->ti_id]++;
|
|
#endif /* BS_STATICS */
|
|
BS_LOAD_SDP
|
|
ti->ti_status = ST_UNK;
|
|
break;
|
|
|
|
default:
|
|
if (ti->ti_msgin[0] & 0x80)
|
|
{
|
|
if ((ti->ti_msgin[0] & 0x07) != ti->ti_lun)
|
|
{
|
|
ti->ti_lun = (ti->ti_msgin[0] & 0x07);
|
|
bshw_set_dst_id(bsc, ti->ti_id, ti->ti_lun);
|
|
bshw_set_sync_reg(bsc, ti->ti_sync);
|
|
|
|
bs_printf(ti, "msgin", "lun error");
|
|
bs_quick_abort(ti, MSG_ABORT);
|
|
}
|
|
break;
|
|
}
|
|
else if (ti->ti_msgin[0] < 0x20)
|
|
bs_msgin_error(ti, 1);
|
|
else if (ti->ti_msgin[0] < 0x30)
|
|
bs_msgin_error(ti, 2);
|
|
else
|
|
bs_msgin_error(ti, 1);
|
|
goto resume;
|
|
}
|
|
|
|
ti->ti_msginptr = 0;
|
|
|
|
resume:
|
|
return;
|
|
}
|
|
|
|
/************************************************
|
|
* <MSG OUT>
|
|
************************************************/
|
|
static BS_INLINE void
|
|
bs_msgout(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
u_int8_t msg[MAXMSGLEN + 1];
|
|
|
|
if (ti->ti_phase == MSGOUT)
|
|
{
|
|
if (cb->rcnt ++ < bsc->sc_retry)
|
|
cb->msgoutlen = ti->ti_omsgoutlen;
|
|
}
|
|
else
|
|
BS_SETUP_PHASE(MSGOUT);
|
|
|
|
if (ti->ti_ophase == SELECTED)
|
|
{
|
|
identify:
|
|
if (cb->msgoutlen == 0)
|
|
{
|
|
ti->ti_msgout = bs_identify_msg(ti);
|
|
ti->ti_omsgoutlen = 0;
|
|
bs_write_1byte(bsc, ti->ti_msgout);
|
|
}
|
|
else
|
|
{
|
|
if (cb->msgout[0] != MSG_RESET &&
|
|
cb->msgout[0] != MSG_ABORT)
|
|
{
|
|
msg[0] = bs_identify_msg(ti);
|
|
bcopy(cb->msgout, &msg[1], cb->msgoutlen);
|
|
bs_xfer(bsc, msg, cb->msgoutlen + 1);
|
|
}
|
|
else
|
|
bs_xfer(bsc, cb->msgout, cb->msgoutlen);
|
|
|
|
ti->ti_msgout = cb->msgout[0];
|
|
ti->ti_emsgout = cb->msgout[2];
|
|
ti->ti_omsgoutlen = cb->msgoutlen;
|
|
cb->msgoutlen = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ti->ti_ophase == SATSEL)
|
|
{
|
|
/* XXX:
|
|
* Maybe identify msg rejected due to
|
|
* a parity error in target side.
|
|
*/
|
|
|
|
bs_printf(ti, "msgout", "msg identify retry (SAT)");
|
|
goto identify;
|
|
}
|
|
|
|
if (cb->msgoutlen == 0)
|
|
{
|
|
ti->ti_msgout = MSG_REJECT;
|
|
ti->ti_omsgoutlen = 0;
|
|
bs_write_1byte(bsc, ti->ti_msgout);
|
|
}
|
|
else
|
|
{
|
|
ti->ti_msgout = cb->msgout[0];
|
|
ti->ti_emsgout = cb->msgout[2];
|
|
ti->ti_omsgoutlen = cb->msgoutlen;
|
|
bs_xfer(bsc, cb->msgout, cb->msgoutlen);
|
|
cb->msgoutlen = 0;
|
|
}
|
|
}
|
|
|
|
/************************************************
|
|
* <DISCONNECT>
|
|
************************************************/
|
|
static BS_INLINE void
|
|
bs_disconnect_phase(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
|
|
switch (bsc->sc_msgphase)
|
|
{
|
|
default:
|
|
panic("%s unknown msg phase\n", bsc->sc_dvname);
|
|
break;
|
|
|
|
case DISCONNECTASSERT:
|
|
case FREE:
|
|
#ifdef BS_STATICS
|
|
bs_statics[ti->ti_id].disconnected++;
|
|
#endif /* BS_STATICS */
|
|
if (ti->ti_cfgflags & BS_SCSI_SAVESP)
|
|
BS_SAVE_SDP;
|
|
BS_HOST_TERMINATE;
|
|
BS_SETUP_PHASE(DISCONNECTED);
|
|
bsc->sc_dtgnum ++;
|
|
bshoststart(bsc, NULL);
|
|
break;
|
|
|
|
case IOCOMPLETED:
|
|
bs_status_check(bsc, ti);
|
|
cb = bscmddone(ti);
|
|
#ifdef BS_DIAG
|
|
ti->ti_flags &= ~BSNEXUS;
|
|
#endif /* BS_DIAG */
|
|
BS_SETUP_PHASE(FREE);
|
|
if (cb || bsc->sc_sttab.tqh_first == NULL)
|
|
{
|
|
BS_HOST_TERMINATE;
|
|
bscmdstart(ti, BSCMDSTART);
|
|
}
|
|
else
|
|
{
|
|
/* give a chance to other target */
|
|
bscmdstart(ti, BSCMDSTART);
|
|
BS_HOST_TERMINATE;
|
|
bshoststart(bsc, NULL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
BS_SETUP_MSGPHASE(FREE);
|
|
}
|
|
|
|
/**************************************************
|
|
* <PHASE ERROR>
|
|
**************************************************/
|
|
#define scsi_status (bsc->sc_busstat)
|
|
|
|
struct bs_err {
|
|
u_char *pe_msg;
|
|
u_int pe_err;
|
|
u_int pe_ph;
|
|
};
|
|
|
|
struct bs_err bs_cmderr[] = {
|
|
/*0*/ { "illegal cmd", BSABNORMAL, UNDEF },
|
|
/*1*/ { "unexpected bus free", BSABNORMAL, FREE },
|
|
/*2*/ { NULL, BSSELTIMEOUT, FREE},
|
|
/*3*/ { "scsi bus parity error", BSPARITY, UNDEF },
|
|
/*4*/ { "scsi bus parity error", BSPARITY, UNDEF },
|
|
/*5*/ { "unknown" , BSFATALIO, UNDEF },
|
|
/*6*/ { "miss reselection (target mode)", BSFATALIO, UNDEF },
|
|
/*7*/ { "wrong status byte", BSPARITY, STATUSIN },
|
|
};
|
|
|
|
static void
|
|
bs_phase_error(ti, cb)
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
struct bs_softc *bsc = ti->ti_bsc;
|
|
struct bs_err *pep;
|
|
|
|
if ((scsi_status & BSR_CM) == BSR_CMDERR &&
|
|
(scsi_status & BSR_PHVALID) == 0)
|
|
{
|
|
pep = &bs_cmderr[scsi_status & BSR_PM];
|
|
ti->ti_error |= pep->pe_err;
|
|
if (pep->pe_msg)
|
|
{
|
|
bs_debug_print(bsc, ti);
|
|
bs_printf(ti, "bsintr", pep->pe_msg);
|
|
}
|
|
BS_SETUP_PHASE(pep->pe_ph);
|
|
}
|
|
else
|
|
{
|
|
ti->ti_error |= BSABNORMAL;
|
|
bs_debug_print(bsc, ti);
|
|
bs_printf(ti, "bsintr", "phase error");
|
|
BS_SETUP_PHASE(UNDEF);
|
|
}
|
|
|
|
BS_SETUP_MSGPHASE(FREE);
|
|
switch (ti->ti_phase)
|
|
{
|
|
case FREE:
|
|
BS_SETUP_PHASE(UNDEF);
|
|
cb = bscmddone(ti);
|
|
#ifdef BS_DIAG
|
|
ti->ti_flags &= ~BSNEXUS;
|
|
#endif /* BS_DIAG */
|
|
BS_HOST_TERMINATE;
|
|
BS_SETUP_PHASE(FREE);
|
|
bscmdstart(ti, ((cb == NULL) ? BSCMDSTART : BSCMDRESTART));
|
|
break;
|
|
|
|
case STATUSIN:
|
|
ti->ti_error |= BSSTATUSERROR;
|
|
ti->ti_status = bshw_get_status_insat(bsc); /* XXX SAT */
|
|
bs_debug_print(bsc, ti);
|
|
break;
|
|
|
|
case UNDEF:
|
|
default:
|
|
ti->ti_error |= BSABNORMAL;
|
|
bs_reset_nexus(bsc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
* ### SCSI PHASE SEQUENCER ###
|
|
**************************************************/
|
|
static BS_INLINE void bs_ack_wait __P((struct bs_softc *, struct targ_info *, struct bsccb *));
|
|
|
|
static BS_INLINE void
|
|
bs_ack_wait(bsc, ti, cb)
|
|
struct bs_softc *bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
{
|
|
int wc = bsc->sc_wc;
|
|
|
|
for (wc = bsc->sc_wc; bshw_get_busstat(bsc) != BSR_ACKREQ && wc > 0; )
|
|
wc --;
|
|
|
|
if (wc <= 0)
|
|
{
|
|
bs_printf(ti, "bs_ack_wait", "timeout I");
|
|
return;
|
|
}
|
|
|
|
bshw_get_auxstat(bsc);
|
|
scsi_status = bshw_get_busstat(bsc);
|
|
|
|
if (cb->msgoutlen > 0)
|
|
{
|
|
bshw_assert_atn(bsc);
|
|
delay(800);
|
|
BS_SETUP_PHASE(ATTENTIONASSERT);
|
|
}
|
|
|
|
bshw_negate_ack(bsc);
|
|
|
|
#ifdef WAITNEXTP
|
|
for (wc = bsc->sc_wc; bshw_get_busstat(bsc) == BSR_ACKREQ && wc > 0; )
|
|
wc --;
|
|
|
|
if (wc <= 0)
|
|
bs_printf(ti, "bs_ack_wait", "timeout II");
|
|
#endif /* WAITNEXTP */
|
|
}
|
|
|
|
int
|
|
bs_sequencer(bsc)
|
|
struct bs_softc *bsc;
|
|
{
|
|
register struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
|
|
/**************************************************
|
|
* Check reset
|
|
**************************************************/
|
|
if (bsc->sc_flags & (BSRESET | BSINACTIVE))
|
|
{
|
|
if (bsc->sc_flags & BSRESET)
|
|
bs_reset_nexus(bsc);
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************
|
|
* Get status & bus phase
|
|
**************************************************/
|
|
if ((bshw_get_auxstat(bsc) & STR_INT) == 0)
|
|
return 0;
|
|
|
|
scsi_status = bshw_get_busstat(bsc);
|
|
if (scsi_status == ((u_int8_t) -1))
|
|
{
|
|
bs_debug_print_all(bsc);
|
|
return 1;
|
|
}
|
|
/**************************************************
|
|
* Check reselection, or nexus
|
|
**************************************************/
|
|
if (scsi_status == BSR_RESEL)
|
|
{
|
|
bs_reselect(bsc);
|
|
return 1;
|
|
}
|
|
|
|
ti = bsc->sc_nexus;
|
|
if (ti == NULL || (cb = ti->ti_ctab.tqh_first) == NULL)
|
|
{
|
|
bs_debug_print_all(bsc);
|
|
bs_printf(ti, "bsintr", "no nexus");
|
|
bs_reset_nexus(bsc);
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************
|
|
* Debug section
|
|
**************************************************/
|
|
#ifdef BS_DEBUG
|
|
if (bs_debug_flag)
|
|
{
|
|
bs_debug_print(bsc, ti);
|
|
if (bs_debug_flag > 1)
|
|
Debugger();
|
|
}
|
|
#endif /* BS_DEBUG */
|
|
|
|
/**************************************************
|
|
* internal scsi phase
|
|
**************************************************/
|
|
switch (ti->ti_phase)
|
|
{
|
|
case SELECTASSERT:
|
|
bs_selected(bsc, ti, cb);
|
|
return 1;
|
|
|
|
case SATSEL:
|
|
BS_SELECTION_TERMINATE;
|
|
|
|
case SATRESEL:
|
|
if (bsc->sc_flags & (BSDMASTART | BSSMITSTART))
|
|
{
|
|
if (bsc->sc_flags & BSSMITSTART)
|
|
{
|
|
bs_debug_print_all(bsc);
|
|
bs_reset_nexus(bsc);
|
|
bs_printf(ti, "bsintr", "smit transfer");
|
|
return 1;
|
|
}
|
|
|
|
BS_SETUP_PHASE(DATAPHASE); /* XXX */
|
|
bs_dma_xfer_end(ti);
|
|
ti->ti_phase = ti->ti_ophase; /* XXX */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* XXX:
|
|
* check check check for safety !!
|
|
*/
|
|
if (bsc->sc_selwait)
|
|
{
|
|
/* Ghaaa! phase error! retry! */
|
|
bs_phase_error(ti, cb);
|
|
return 1;
|
|
}
|
|
|
|
if (bsc->sc_flags & (BSDMASTART | BSSMITSTART))
|
|
{
|
|
if (bsc->sc_flags & BSDMASTART)
|
|
bs_dma_xfer_end(ti);
|
|
else
|
|
bs_smit_xfer_end(ti);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/**************************************************
|
|
* hw scsi phase
|
|
**************************************************/
|
|
if (scsi_status & BSR_PHVALID)
|
|
{
|
|
/**************************************************
|
|
* Normal SCSI phase.
|
|
**************************************************/
|
|
if ((scsi_status & BSR_CM) == BSR_CMDABT)
|
|
{
|
|
bs_phase_error(ti, cb);
|
|
return 1;
|
|
}
|
|
|
|
switch (scsi_status & BSR_PM)
|
|
{
|
|
case BSR_DATAOUT:
|
|
case BSR_DATAIN:
|
|
BS_SETUP_PHASE(DATAPHASE);
|
|
|
|
if (bsc->sc_p.datalen <= 0 ||
|
|
(ti->ti_flags & BSFORCEIOPOLL))
|
|
{
|
|
bs_io_xfer(ti);
|
|
return 1;
|
|
}
|
|
|
|
if (bs_check_smit(ti) &&
|
|
(bsc->sc_p.datalen % sizeof(u_int32_t)) == 0)
|
|
{
|
|
bs_lc_smit_xfer(ti, scsi_status & BSR_IOR);
|
|
return 1;
|
|
}
|
|
|
|
bs_dma_xfer(ti, scsi_status & BSR_IOR);
|
|
bshw_start_xfer(bsc);
|
|
return 1;
|
|
|
|
case BSR_CMDOUT:
|
|
bs_commandout(bsc, ti, cb);
|
|
return 1;
|
|
|
|
case BSR_STATIN:
|
|
if (bs_check_sat(ti))
|
|
{
|
|
BS_SETUP_PHASE(SATCOMPSEQ);
|
|
bshw_set_count(bsc, 0);
|
|
bshw_cmd_pass(bsc, 0x41);
|
|
bshw_start_sat(bsc, 0);
|
|
}
|
|
else
|
|
{
|
|
BS_SETUP_PHASE(STATUSIN);
|
|
ti->ti_status = bs_read_1byte(bsc);
|
|
}
|
|
return 1;
|
|
|
|
case BSR_UNSPINFO0:
|
|
case BSR_UNSPINFO1:
|
|
bs_debug_print(bsc, ti);
|
|
bs_printf(ti, "bsintr", "illegal bus phase");
|
|
return 1;
|
|
|
|
case BSR_MSGOUT:
|
|
bs_msgout(bsc, ti, cb);
|
|
return 1;
|
|
|
|
case BSR_MSGIN:/* msg in */
|
|
if (bs_check_sat(ti))
|
|
{
|
|
if (ti->ti_phase == RESELECTED)
|
|
{
|
|
bs_sat_continue(bsc, ti, cb);
|
|
return 1;
|
|
}
|
|
/* XXX */
|
|
if (ti->ti_status == ST_UNK)
|
|
ti->ti_status = bshw_get_status_insat(bsc);
|
|
}
|
|
|
|
ti->ti_msgin[ti->ti_msginptr ++] = bs_read_1byte(bsc);
|
|
bs_msgin(bsc, ti);
|
|
if (bsc->sc_cfgflags & BSC_FASTACK)
|
|
bs_ack_wait(bsc, ti, cb);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/**************************************************
|
|
* Special SCSI phase
|
|
**************************************************/
|
|
switch (scsi_status)
|
|
{
|
|
case BSR_SATSDP:/* SAT with save data pointer */
|
|
BS_SAVE_SDP
|
|
bshw_cmd_pass(bsc, 0x41);
|
|
bshw_start_sat(bsc, 0);
|
|
BS_SETUP_PHASE(SATSDP)
|
|
return 1;
|
|
|
|
case BSR_SATFIN:/* SAT COMPLETE */
|
|
ti->ti_status = bshw_get_status_insat(bsc);
|
|
BS_SETUP_MSGPHASE(IOCOMPLETED);
|
|
bs_disconnect_phase(bsc, ti, cb);
|
|
return 1;
|
|
|
|
case BSR_ACKREQ:/* negate ACK */
|
|
if (cb->msgoutlen > 0)
|
|
{
|
|
bshw_assert_atn(bsc);
|
|
delay(800);
|
|
BS_SETUP_PHASE(ATTENTIONASSERT);
|
|
}
|
|
bshw_negate_ack(bsc);
|
|
return 1;
|
|
|
|
case BSR_DISC:/* disconnect */
|
|
bs_disconnect_phase(bsc, ti, cb);
|
|
return 1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bs_phase_error(ti, cb);
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* INTERNAL POLLING FUNCTIONS
|
|
*****************************************************************/
|
|
static int
|
|
bs_scsi_cmd_poll_internal(cti)
|
|
struct targ_info *cti;
|
|
{
|
|
struct bs_softc *bsc = cti->ti_bsc;
|
|
struct targ_info *ti;
|
|
struct bsccb *cb;
|
|
int i, waits, delay_count;
|
|
|
|
bsc->sc_poll++;
|
|
|
|
/* setup timeout count */
|
|
if ((ti = bsc->sc_nexus) == NULL ||
|
|
(cb = ti->ti_ctab.tqh_first) == NULL)
|
|
waits = BS_DEFAULT_TIMEOUT_SECOND * 1000000;
|
|
else
|
|
waits = cb->tcmax * 1000000;
|
|
|
|
/* force all current jobs into the polling state. */
|
|
for (i = 0; i < NTARGETS; i++)
|
|
{
|
|
if ((ti = bsc->sc_ti[i]) != NULL)
|
|
{
|
|
ti->ti_flags |= BSFORCEIOPOLL;
|
|
if ((cb = ti->ti_ctab.tqh_first) != NULL)
|
|
cb->bsccb_flags |= BSFORCEIOPOLL;
|
|
}
|
|
}
|
|
|
|
/* do io */
|
|
bsc->sc_flags &= ~BSJOBDONE;
|
|
do
|
|
{
|
|
delay_count = ((bsc->sc_flags & BSDMASTART) ? 1000000 : 100);
|
|
delay(delay_count);
|
|
waits -= delay_count;
|
|
bs_sequencer(bsc);
|
|
}
|
|
while (waits >= 0 && (bsc->sc_flags & (BSUNDERRESET | BSJOBDONE)) == 0);
|
|
|
|
/* done */
|
|
bsc->sc_poll--;
|
|
if (waits < 0 || (bsc->sc_flags & BSUNDERRESET))
|
|
{
|
|
bs_printf(NULL, "cmd_poll", "timeout or fatal");
|
|
return HASERROR;
|
|
}
|
|
|
|
return COMPLETE;
|
|
}
|
|
|
|
int
|
|
bs_scsi_cmd_poll(cti, targetcb)
|
|
struct targ_info *cti;
|
|
struct bsccb *targetcb;
|
|
{
|
|
struct bs_softc *bsc = cti->ti_bsc;
|
|
struct targ_info *ti;
|
|
int s, error = COMPLETE;
|
|
|
|
s = splbio();
|
|
bs_terminate_timeout(bsc);
|
|
|
|
if (bsc->sc_hstate == BSC_TARG_CHECK)
|
|
{
|
|
if ((error = bs_scsi_cmd_poll_internal(cti)) != COMPLETE)
|
|
bs_reset_nexus(bsc);
|
|
}
|
|
else
|
|
{
|
|
if (bsc->sc_outccb)
|
|
bs_panic(bsc, "bs_cmd_poll: internal error");
|
|
|
|
bsc->sc_flags &= ~BSPOLLDONE;
|
|
bsc->sc_outccb = targetcb;
|
|
|
|
while ((bsc->sc_flags & BSPOLLDONE) == 0)
|
|
{
|
|
if (bs_scsi_cmd_poll_internal(cti) != COMPLETE)
|
|
{
|
|
if ((ti = bsc->sc_nexus) && ti->ti_ctab.tqh_first)
|
|
ti->ti_error |= (BSTIMEOUT | BSABNORMAL);
|
|
bs_reset_nexus(bsc);
|
|
}
|
|
}
|
|
|
|
bsc->sc_outccb = NULL;
|
|
}
|
|
|
|
bs_start_timeout(bsc);
|
|
softintr(bsc->sc_irq);
|
|
splx(s);
|
|
return error;
|
|
}
|