TWE_UNLOCK_TWE(sc, lock);  if (xs->flags & SCSI_POLL)
TWE_UNLOCK(sc, lock);  if (wait & SCSI_POLL)
twe_scsi_cmd(xs)	struct scsi_xfer *xs;{	struct scsi_link *link = xs->sc_link;	struct twe_softc *sc = link->adapter_softc;	struct twe_ccb *ccb;	struct twe_cmd *cmd;	struct scsi_inquiry_data inq;	struct scsi_sense_data sd;	struct {		struct scsi_mode_header hd;		struct scsi_blk_desc bd;		union scsi_disk_pages dp;	} mpd;	struct scsi_read_cap_data rcd;	u_int8_t target = link->target;	u_int32_t blockno, blockcnt;	struct scsi_rw *rw;	struct scsi_rw_big *rwb;	int error, op, flags;	twe_lock_t lock;	if (target >= TWE_MAX_UNITS || !sc->sc_hdr[target].hd_present ||	    link->lun != 0) {		xs->error = XS_DRIVER_STUFFUP;		return (COMPLETE);	}	TWE_DPRINTF(TWE_D_CMD, ("twe_scsi_cmd "));	xs->error = XS_NOERROR;	switch (xs->cmd->opcode) {	case TEST_UNIT_READY:	case START_STOP:#if 0	case VERIFY:#endif		TWE_DPRINTF(TWE_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode,		    target));		break;	case REQUEST_SENSE:		TWE_DPRINTF(TWE_D_CMD, ("REQUEST SENSE tgt %d ", target));		bzero(&sd, sizeof sd);		sd.error_code = 0x70;		sd.segment = 0;		sd.flags = SKEY_NO_SENSE;		*(u_int32_t*)sd.info = htole32(0);		sd.extra_len = 0;		twe_copy_internal_data(xs, &sd, sizeof sd);		break;	case INQUIRY:		TWE_DPRINTF(TWE_D_CMD, ("INQUIRY tgt %d devtype %x ", target,		    sc->sc_hdr[target].hd_devtype));		bzero(&inq, sizeof inq);		inq.device =		    (sc->sc_hdr[target].hd_devtype & 4) ? T_CDROM : T_DIRECT;		inq.dev_qual2 =		    (sc->sc_hdr[target].hd_devtype & 1) ? SID_REMOVABLE : 0;		inq.version = 2;		inq.response_format = 2;		inq.additional_length = 32;		strcpy(inq.vendor, "3WARE  ");		sprintf(inq.product, "Host drive  #%02d", target);		strcpy(inq.revision, "   ");		twe_copy_internal_data(xs, &inq, sizeof inq);		break;	case MODE_SENSE:		TWE_DPRINTF(TWE_D_CMD, ("MODE SENSE tgt %d ", target));		bzero(&mpd, sizeof mpd);		switch (((struct scsi_mode_sense *)xs->cmd)->page) {		case 4:			/* scsi_disk.h says this should be 0x16 */			mpd.dp.rigid_geometry.pg_length = 0x16;			mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd +			    mpd.dp.rigid_geometry.pg_length;			mpd.hd.blk_desc_len = sizeof mpd.bd;			/* XXX */			mpd.hd.dev_spec =			    (sc->sc_hdr[target].hd_devtype & 2) ? 0x80 : 0;			_lto3b(TWE_SECTOR_SIZE, mpd.bd.blklen);			mpd.dp.rigid_geometry.pg_code = 4;			_lto3b(sc->sc_hdr[target].hd_size /			    sc->sc_hdr[target].hd_heads /			    sc->sc_hdr[target].hd_secs,			    mpd.dp.rigid_geometry.ncyl);			mpd.dp.rigid_geometry.nheads =			    sc->sc_hdr[target].hd_heads;			twe_copy_internal_data(xs, (u_int8_t *)&mpd,			    sizeof mpd);			break;		default:			printf("%s: mode sense page %d not simulated\n",			    sc->sc_dev.dv_xname,			    ((struct scsi_mode_sense *)xs->cmd)->page);			xs->error = XS_DRIVER_STUFFUP;			return (TRY_AGAIN_LATER);		}		break;	case READ_CAPACITY:		TWE_DPRINTF(TWE_D_CMD, ("READ CAPACITY tgt %d ", target));		bzero(&rcd, sizeof rcd);		_lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr);		_lto4b(TWE_SECTOR_SIZE, rcd.length);		twe_copy_internal_data(xs, &rcd, sizeof rcd);		break;	case PREVENT_ALLOW:		TWE_DPRINTF(TWE_D_CMD, ("PREVENT/ALLOW "));		return (COMPLETE);	case READ_COMMAND:	case READ_BIG:	case WRITE_COMMAND:	case WRITE_BIG:	case SYNCHRONIZE_CACHE:		lock = TWE_LOCK_TWE(sc);		flags = 0;		if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {			/* A read or write operation. */			if (xs->cmdlen == 6) {				rw = (struct scsi_rw *)xs->cmd;				blockno = _3btol(rw->addr) &				    (SRW_TOPADDR << 16 | 0xffff);				blockcnt = rw->length ? rw->length : 0x100;			} else {				rwb = (struct scsi_rw_big *)xs->cmd;				blockno = _4btol(rwb->addr);				blockcnt = _2btol(rwb->length);				/* reflect DPO & FUA flags */				if (xs->cmd->opcode == WRITE_BIG &&				    rwb->byte2 & 0x18)					flags = TWE_FLAGS_CACHEDISABLE;			}			if (blockno >= sc->sc_hdr[target].hd_size ||			    blockno + blockcnt > sc->sc_hdr[target].hd_size) {				printf("%s: out of bounds %u-%u >= %u\n",				    sc->sc_dev.dv_xname, blockno, blockcnt,				    sc->sc_hdr[target].hd_size);				xs->error = XS_DRIVER_STUFFUP;				scsi_done(xs);				TWE_UNLOCK_TWE(sc, lock);				return (COMPLETE);			}		}		switch (xs->cmd->opcode) {		case READ_COMMAND:	op = TWE_CMD_READ;	break;		case READ_BIG:		op = TWE_CMD_READ;	break;		case WRITE_COMMAND:	op = TWE_CMD_WRITE;	break;		case WRITE_BIG:		op = TWE_CMD_WRITE;	break;		default:		op = TWE_CMD_NOP;	break;		}		if ((ccb = twe_get_ccb(sc)) == NULL) {			xs->error = XS_DRIVER_STUFFUP;			scsi_done(xs);			TWE_UNLOCK_TWE(sc, lock);			return (COMPLETE);		}		ccb->ccb_xs = xs;		ccb->ccb_data = xs->data;		ccb->ccb_length = xs->datalen;		ccb->ccb_state = TWE_CCB_READY;		cmd = ccb->ccb_cmd;		cmd->cmd_unit_host = TWE_UNITHOST(target, 0); /* XXX why 0? */		cmd->cmd_op = op;		cmd->cmd_flags = flags;		cmd->cmd_io.count = htole16(blockcnt);		cmd->cmd_io.lba = htole32(blockno);		if ((error = twe_cmd(ccb, ((xs->flags & SCSI_NOSLEEP)?		    BUS_DMA_NOWAIT : BUS_DMA_WAITOK), xs->flags & SCSI_POLL))) {			TWE_UNLOCK_TWE(sc, lock);			TWE_DPRINTF(TWE_D_CMD, ("failed %p ", xs));			if (xs->flags & SCSI_POLL) {				xs->error = XS_TIMEOUT;				return (TRY_AGAIN_LATER);			} else {				xs->error = XS_DRIVER_STUFFUP;				scsi_done(xs);				return (COMPLETE);			}		}		TWE_UNLOCK_TWE(sc, lock);		if (xs->flags & SCSI_POLL)			return (COMPLETE);		else			return (SUCCESSFULLY_QUEUED);	default:		TWE_DPRINTF(TWE_D_CMD, ("unknown opc %d ", xs->cmd->opcode));		xs->error = XS_DRIVER_STUFFUP;	}	return (COMPLETE);}