/* @(#)scsi-openserver.c	1.28 01/03/18 Copyright 1998 J. Schilling, Santa Cruz Operation */
#ifndef lint
static	char __sccsid[] =
	"@(#)scsi-openserver.c	1.28 01/03/18 Copyright 1998 J. Schilling, Santa Cruz Operation";
#endif
/*
 *	Interface for the SCO SCSI implementation.
 *
 *	Warning: you may change this source, but if you do that
 *	you need to change the _scg_version and _scg_auth* string below.
 *	You may not return "schily" for an SCG_AUTHOR request anymore.
 *	Choose your name instead of "schily" and make clear that the version
 *	string is related to a modified source.
 *
 *	Copyright (c) 1998 J. Schilling, Santa Cruz Operation
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#undef	sense

#include <sys/scsicmd.h>

/*
 *	Warning: you may change this source, but if you do that
 *	you need to change the _scg_version and _scg_auth* string below.
 *	You may not return "schily" for an SCG_AUTHOR request anymore.
 *	Choose your name instead of "schily" and make clear that the version
 *	string is related to a modified source.
 */
LOCAL	char	_scg_trans_version[] = "scsi-openserver.c-1.28";	/* The version for this transport*/

#define	MAX_SCG		16		/* Max # of cdrom devices */
#define	MAX_TGT		16		/* Not really needed      */
#define	MAX_LUN		8		/* Not really needed      */

#define	MAX_DMA		(32*1024)	/* Maybe even more ???    */
#define	MAXPATH	       256			/* max length of devicepath  */
#define MAXLINE		80			/* max length of input line  */
#define MAXSCSI		99			/* max number of mscsi lines */
#define	MAXDRVN		10			/* max length of drivername  */

#define DEV_DIR		"/tmp"
#define	DEV_NAME	"scg.s%1dt%1dl%1d"

		/* We will only deal with disks, cdroms and tapes !	*/
		/* Sflp devices (floptical) 21Mb scsi floppy devices	*/
		/* will be ignored. Hmm, I can't remember that I ever 	*/
		/* have seen one ;^)					*/

#define	DEV_ROOT	"/dev/rdsk/0s0"
#define DEV_SDSK	"/dev/rdsk/%ds0"
#define DEV_SROM	"/dev/rcd%d"
#define	DEV_STP		"/dev/xStp%d"

#define	SCAN_DEV	"%s%s%d%d%d%d"

#define	SCSI_CFG	"/etc/sconf -r"		/* no. of configured devices  */
#define	SCSI_DEV	"/etc/sconf -g %d"	/* read line 'n' of mscsi tbl */

#define	DRV_ATAPI	"wd"

typedef struct scg2sdi {

	int	valid;
	int	open;
	int	atapi;
	int	fd;
	int	lmscsi;

} scg2sdi_t;

LOCAL	scg2sdi_t	sdidevs [MAX_SCG][MAX_TGT][MAX_LUN];

typedef struct amscsi {
	char	typ[MAXDRVN];
	char	drv[MAXDRVN];
	int	hba;
	int	bus;
	int	scg;
	int	tgt;
	int	lun;
	char	dev[MAXPATH];

} amscsi_t;

struct scg_local {
	short	scgfiles[MAX_SCG][MAX_TGT][MAX_LUN];
};
#define scglocal(p)	((struct scg_local *)((p)->local)) 

LOCAL	int	sort_mscsi	__PR((const void *l1, const void *l2));
LOCAL	int	openserver_init	__PR((SCSI *scgp));
LOCAL	void	cp_scg2sco	__PR((struct scsicmd2 *sco, struct scg_cmd *scg));
LOCAL	void	cp_sco2scg	__PR((struct scsicmd2 *sco, struct scg_cmd *scg));

/* -------------------------------------------------------------------------
** SCO OpenServer does not have a generic scsi device driver, which can
** be used to access any configured scsi device. 
**
** But we can use the "Srom" scsi peripherial driver (for CD-ROM devices) 
** etc. to send SCSIUSERCMD2's to any target device controlled by the 
** corresponding target driver.
**
** This pass-through implementation for libscg currently allows to 
** handle the following devices classes:
**
**	1. DISK		handled by Sdsk
**	2. CD-ROM	handled by Srom
**	3. TAPES	handled by Stp
**
** NOTE: The libscg OpenServer passthrough routines have changed with 
**       cdrecord-1.8 to enable the -scanbus option. Therefore the
**	 addressing scheme is now the same as used on many other platforms
**	 like Solaris, Linux etc.
**
**   ===============================================================
**   RUN 'cdrecord -scanbus' TO SEE THE DEVICE ADDRESSES YOU CAN USE
**   ===============================================================
**
*/

/*
 * Return version information for the low level SCSI transport code.
 * This has been introduced to make it easier to trace down problems
 * in applications.
 */
LOCAL char *
scgo_version(scgp, what)
	SCSI	*scgp;
	int	what;
{
	if (scgp != (SCSI *)0) {
		switch (what) {

		case SCG_VERSION:
			return (_scg_trans_version);
		/*
		 * If you changed this source, you are not allowed to
		 * return "schily" for the SCG_AUTHOR request.
		 */
		case SCG_AUTHOR:
			return (_scg_auth_schily);
		case SCG_SCCS_ID:
			return (__sccsid);
		}
	}
	return ((char *)0);
}

/* ---------------------------------------------------------------
** This routine sorts the amscsi_t lines on the following columns
** in ascending order:
**
**	1. drv  - driver name
**	2. bus  - scsibus per controller
**	3. tgt  - target id of device
**	4. lun  - lun of the device
**
*/


LOCAL int
sort_mscsi(l1, l2)
	const void	*l1;
	const void	*l2;
{
	amscsi_t	*t1 = (amscsi_t *) l1;
	amscsi_t	*t2 = (amscsi_t *) l2;

	if ( strcmp(t1->drv, t2->drv) == 0 ) {
		if (t1->bus < t2->bus) {
			return (-1);

		} else if (t1->bus > t2->bus) {
			return (1);

		} else if (t1->tgt < t2->tgt) {
			return (-1);

		} else if (t1->tgt > t2->tgt) {
			return (1);

		} else if (t1->lun < t2->lun) {
			return (-1);

		} else if (t1->lun > t2->lun) {
			return (1);
		} else {
			return (0);
		}
	} else {
		return (strcmp(t1->drv, t2->drv));
	}
}

/* ---------------------------------------------------------------
** This routine is introduced to find all scsi devices which are
** currently configured into the kernel. This is done by reading
** the dynamic kernel mscsi tables and parse the resulting lines.
** As the output of 'sconf' is not directly usable the information
** found is to be sorted and re-arranged to be used with the libscg
** routines.
**
** NOTE: One problem is currently still not solved ! If you don't
**       have a media in your CD-ROM/CD-Writer we are not able to 
**       do an open() and therefore will set the drive to be not 
**       available (valid=0).
**
**       This will for example cause cdrecord to not list the drive 
**	 in the -scanbus output.
**
*/

LOCAL int 
openserver_init(scgp)
	SCSI	*scgp;
{
	FILE		*cmd;
	int		nscg  = -1, lhba  = -1, lbus = -1;
	int		nSrom = -1, nSdsk = -1, nStp = -1;
	int		atapi, fd, nopen = 0;
	int		pos = 0, len = 0, nlm = 0;
	int		s = 0, t = 0, l = 0;
	int		mscsi;
	char		sconf[MAXLINE];
	char		lines[MAXLINE];
	char		drvid[MAXDRVN];
	amscsi_t	cmtbl[MAXSCSI];
	char		dname[MAXPATH];
	char		**evsave;
extern	char		**environ;


	for (s = 0; s < MAX_SCG; s++) {
		for (t = 0; t < MAX_TGT; t++) {
			for (l = 0; l < MAX_LUN ; l++) {
			  sdidevs[s][t][l].valid  =  0;
			  sdidevs[s][t][l].open   = -1;
			  sdidevs[s][t][l].atapi  = -1;
			  sdidevs[s][t][l].fd     = -1;
			  sdidevs[s][t][l].lmscsi = -1;
			}
		}
	}

	/* read sconf -r and get number of kernel mscsi lines ! */

	evsave = environ;
	environ = 0;
	if ((cmd = popen(SCSI_CFG,"r")) == NULL) {
		if (scgp->errstr)
			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
				"Error popen() for \"%s\"",
				SCSI_CFG);
		environ = evsave;
		return (-1);
	}
	environ = evsave;

	if (fgets(lines, MAXLINE, cmd) == NULL) {
		errno = EIO;
		if (scgp->errstr)
			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
				"Error reading popen() for \"%s\"",
				SCSI_CFG);
		return (-1);
	} else
		nlm = atoi(lines);

	if (scgp->debug > 0) {
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
		js_fprintf((FILE *)scgp->errfile, "mscsi lines = %d\n", nlm);
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
	}

	if (pclose(cmd) == -1) {
		if (scgp->errstr)
			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
				"Error pclose() for \"%s\"",
				SCSI_CFG);
		return (-1);
	}


	for (l = 0; l < nlm; l++) {

		/* initialize cmtbl entry */

		cmtbl[l].hba = -1;
		cmtbl[l].bus = -1;
		cmtbl[l].tgt = -1;
		cmtbl[l].lun = -1;
		cmtbl[l].scg = -1;

		memset( cmtbl[l].typ, '\0', MAXDRVN);
		memset( cmtbl[l].drv, '\0', MAXDRVN);
		memset( cmtbl[l].dev, '\0', MAXDRVN);

		/* read sconf -g 'n' and get line of kernel mscsi table! */
		/* the order the lines will be received in will determine */
		/* the device name we can use to open the device         */

		js_snprintf(sconf, sizeof(sconf),
			SCSI_DEV, l + 1); /* enumeration starts with 1 */

		evsave = environ;
		environ = 0;
		if ((cmd = popen(sconf,"r")) == NULL) {
			if (scgp->errstr)
				js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
					"Error popen() for \"%s\"",
					sconf);
			environ = evsave;
			return (-1);
		}
		environ = evsave;

		if (fgets(lines, MAXLINE, cmd) == NULL) 
			break;

		if (pclose(cmd) == -1) {
			if (scgp->errstr)
				js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
					"Error pclose() for \"%s\"",
					sconf);
			return (-1);
		}

		sscanf(lines, SCAN_DEV,  cmtbl[l].typ, 
					 cmtbl[l].drv,
					&cmtbl[l].hba, 
					&cmtbl[l].bus, 
					&cmtbl[l].tgt, 
					&cmtbl[l].lun);

		if ( strstr(cmtbl[l].typ, "Sdsk") != NULL ){
			js_snprintf(cmtbl[l].dev,  sizeof(cmtbl[l].dev),
					DEV_SDSK, ++nSdsk);
		}

		if ( strstr(cmtbl[l].typ, "Srom") != NULL ){
			js_snprintf(cmtbl[l].dev,  sizeof(cmtbl[l].dev),
					DEV_SROM, ++nSrom);
		}

		if ( strstr(cmtbl[l].typ, "Stp") != NULL ){
			js_snprintf(cmtbl[l].dev, sizeof(cmtbl[l].dev),
					DEV_STP, ++nStp);
		}

		if (scgp->debug > 0) {
			js_fprintf((FILE *)scgp->errfile,
				"%-4s = %5s(%d,%d,%d,%d) -> %s\n", 
				cmtbl[l].typ, 
				cmtbl[l].drv, 
				cmtbl[l].hba, 
				cmtbl[l].bus, 
				cmtbl[l].tgt, 
				cmtbl[l].lun,
				cmtbl[l].dev);
		}

	}

	if (scgp->debug > 0) {
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
		js_fprintf((FILE *)scgp->errfile, "%2d DISK  \n", nSdsk + 1);
		js_fprintf((FILE *)scgp->errfile, "%2d CD-ROM\n", nSrom + 1);
		js_fprintf((FILE *)scgp->errfile, "%2d TAPE  \n", nStp  + 1);
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
	}

	/* ok, now let's sort this array of scsi devices	*/

	qsort((void *) cmtbl, nlm, sizeof(amscsi_t), sort_mscsi);

	if (scgp->debug > 0) {
		for (l = 0; l < nlm; l++)
		js_fprintf((FILE *)scgp->errfile,
			"%-4s = %5s(%d,%d,%d,%d) -> %s\n", 
			cmtbl[l].typ, 
			cmtbl[l].drv, 
			cmtbl[l].hba, 
			cmtbl[l].bus, 
			cmtbl[l].tgt, 
			cmtbl[l].lun,
			cmtbl[l].dev);
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
	}



	/* find root disk controller to make it scg 0     	*/

	for (l = 0; l < nlm; l++)
		if (strcmp(cmtbl[l].dev, DEV_ROOT) == 0)
			t = l;


	if (scgp->debug > 0) {
		js_fprintf((FILE *)scgp->errfile,
			"root = %5s(%d,%d,%d,%d) -> %s\n", 
			cmtbl[t].drv, 
			cmtbl[t].hba, 
			cmtbl[t].bus, 
			cmtbl[t].tgt, 
			cmtbl[t].lun,
			cmtbl[t].dev);
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
	}

	/* calculate scg from drv, hba and bus 			*/

	strcpy(drvid, "");

	for (l = 0, s = t; l < nlm; l++, s = (t + l) % nlm) {

		if (strcmp(drvid, cmtbl[s].drv) != 0) {
			strcpy(drvid, cmtbl[s].drv);
			lhba = cmtbl[s].hba;
			lbus = cmtbl[s].bus;
			cmtbl[s].scg = ++nscg;

		} else if (cmtbl[s].hba != lhba) {
			lhba = cmtbl[s].hba;
			lbus = cmtbl[s].bus;
			cmtbl[s].scg = ++nscg;

		} else if (cmtbl[s].bus != lbus) {
			lbus = cmtbl[s].bus;
			cmtbl[s].scg = ++nscg;
		} else {
			cmtbl[s].scg = nscg;
		}
		sdidevs[cmtbl[s].scg][cmtbl[s].tgt][cmtbl[s].lun].valid  = 1;
		sdidevs[cmtbl[s].scg][cmtbl[s].tgt][cmtbl[s].lun].open   = 0;

		if (strcmp(cmtbl[s].drv, DRV_ATAPI) == 0) 
			sdidevs[cmtbl[s].scg][cmtbl[s].tgt][cmtbl[s].lun].atapi = 1;

		sdidevs[cmtbl[s].scg][cmtbl[s].tgt][cmtbl[s].lun].lmscsi = s;

	}


	/* open all device nodes */

	for (s = 0; s < MAX_SCG; s++) {
		for (t = 0; t < MAX_TGT; t++) {
			for (l = 0; l < MAX_LUN ; l++) {

			  if ( sdidevs[s][t][l].valid == 0) 
			  	continue;

			  /* Open pass-through device node */

				mscsi = sdidevs[s][t][l].lmscsi;

				strcpy(dname, cmtbl[mscsi].dev);

	/* ------------------------------------------------------------------
	** NOTE: If we can't open the device, we will set the device invalid!
	** ------------------------------------------------------------------
	*/

				if ((fd = open(dname, O_RDONLY | O_NONBLOCK)) < 0) {
					sdidevs[s][t][l].valid = 0;
					continue;
				}

				if (scgp->debug > 0) {
					js_fprintf((FILE *)scgp->errfile,
						"%d,%d,%d => %5s(%d,%d,%d,%d) -> %d : %s \n", 
						s, t, l,
						cmtbl[mscsi].drv, 
						cmtbl[mscsi].hba, 
						cmtbl[mscsi].bus, 
						cmtbl[mscsi].tgt, 
						cmtbl[mscsi].lun,
						cmtbl[mscsi].scg,
						cmtbl[mscsi].dev);
				}

				sdidevs[s][t][l].fd   = fd;
				sdidevs[s][t][l].open = 1;
				nopen++;
				scglocal(scgp)->scgfiles[s][t][l] = (short) fd;
			}
		}
	}

	if (scgp->debug > 0) {
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
		js_fprintf((FILE *)scgp->errfile, "nopen = %d devices   \n", nopen);
		js_fprintf((FILE *)scgp->errfile, "-------------------- \n");
	}

	return (nopen);
}


LOCAL int
scgo_open(scgp, device)
	SCSI	*scgp;
	char	*device;
{
	int	busno	= scg_scsibus(scgp);
	int	tgt	= scg_target(scgp);
	int	tlun	= scg_lun(scgp);
	int	f, b, t, l;
	int	nopen = 0;
	char	devname[64];

	if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
		errno = EINVAL;
		if (scgp->errstr)
			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
				"Illegal value for busno, target or lun '%d,%d,%d'",
				busno, tgt, tlun);
		return (-1);
	}

	if (scgp->local == NULL) {
		scgp->local = malloc(sizeof(struct scg_local));
		if (scgp->local == NULL)
			return (0);

		for (b = 0; b < MAX_SCG; b++) {
			for (t = 0; t < MAX_TGT; t++) {
				for (l = 0; l < MAX_LUN ; l++)
					scglocal(scgp)->scgfiles[b][t][l] = (short)-1;
			}
		}
	}

	if (*device != '\0') {          /* we don't allow old dev usage */
		errno = EINVAL;
		if (scgp->errstr)
			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
			"Open by 'devname' no longer supported on this OS");
		return (-1);
	} 

	return ( openserver_init(scgp) );

}

LOCAL int
scgo_close(scgp)
	SCSI	*scgp;
{
	register int	f;
	register int	b;
	register int	t;
	register int	l;

	if (scgp->local == NULL)
		return (-1);

	for (b=0; b < MAX_SCG; b++) {
		for (t=0; t < MAX_TGT; t++) {
			for (l=0; l < MAX_LUN ; l++) {

				f = scglocal(scgp)->scgfiles[b][t][l];
				if (f >= 0)
					close(f);

				sdidevs[b][t][l].fd    = -1;
				sdidevs[b][t][l].open  =  0;
				sdidevs[b][t][l].valid =  0;

				scglocal(scgp)->scgfiles[b][t][l] = (short)-1;
			}
		}
	}
	return (0);
}

LOCAL long
scgo_maxdma(scgp, amt)
	SCSI	*scgp;
	long	amt;
{
	return (MAX_DMA);
}


LOCAL void *
scgo_getbuf(scgp, amt)
	SCSI	*scgp;
	long	amt;
{
	if (scgp->debug > 0) {
		js_fprintf((FILE *)scgp->errfile,
			"scgo_getbuf: %ld bytes\n", amt);
	}
	scgp->bufbase = valloc((size_t)(amt));

	return (scgp->bufbase);
}

LOCAL void
scgo_freebuf(scgp)
	SCSI	*scgp;
{
	if (scgp->bufbase)
		free(scgp->bufbase);
	scgp->bufbase = NULL;
}

LOCAL BOOL
scgo_havebus(scgp, busno)
	SCSI	*scgp;
	int	busno;
{
	register int	t;
	register int	l;

	if (busno < 0 || busno >= MAX_SCG)
		return (FALSE);

	if (scgp->local == NULL)
		return (FALSE);

	for (t=0; t < MAX_TGT; t++) {
		for (l=0; l < MAX_LUN ; l++)
			if (scglocal(scgp)->scgfiles[busno][t][l] >= 0)
				return (TRUE);
	}
	return (FALSE);
}

LOCAL int
scgo_fileno(scgp, busno, tgt, tlun)
	SCSI	*scgp;
	int	busno;
	int	tgt;
	int	tlun;
{
	if (busno < 0 || busno >= MAX_SCG ||
	    tgt   < 0 || tgt   >= MAX_TGT ||
	    tlun  < 0 || tlun  >= MAX_LUN)
		return (-1);

	if (scgp->local == NULL)
		return (-1);

	return ((int)scglocal(scgp)->scgfiles[busno][tgt][tlun]);
}

LOCAL int
scgo_initiator_id(scgp)
	SCSI	*scgp;
{
	return (-1);

	/* We don't know the initiator ID yet, but we can if we parse the 
	** output of the command 'cat /dev/string/cfg | grep "%adapter"'  
	**
	** Sample line:                                                   
	**
 	**   %adapter 0xE800-0xE8FF 11 - type=alad ha=0 bus=0 id=7 fts=sto  
	**
	** This tells us that the alad controller 0 has an id of 7 !
	** The parsing should be done in openserver_init().
	**
	*/
}

LOCAL int
scgo_isatapi(scgp)
	SCSI	*scgp;
{
	return (sdidevs[scg_scsibus(scgp)][scg_target(scgp)][scg_lun(scgp)].atapi);
}

LOCAL int
scgo_reset(scgp, what)
	SCSI	*scgp;
	int	what;
{
	errno = EINVAL;
	return(-1);		/* no scsi reset available */
}

LOCAL void
cp_scg2sco(sco, scg)
	struct scsicmd2	*sco;
	struct scg_cmd	*scg;
{
	sco->cmd.data_ptr = (char *) scg->addr;
	sco->cmd.data_len = scg->size;
	sco->cmd.cdb_len  = scg->cdb_len;

	sco->sense_len    = scg->sense_len;
	sco->sense_ptr    = scg->u_sense.cmd_sense;

	if (!(scg->flags & SCG_RECV_DATA) && (scg->size > 0))
		sco->cmd.is_write = 1;

	if (scg->cdb_len == SC_G0_CDBLEN )
		memcpy(sco->cmd.cdb, &scg->cdb.g0_cdb, scg->cdb_len);

	if (scg->cdb_len == SC_G1_CDBLEN )
		memcpy(sco->cmd.cdb, &scg->cdb.g1_cdb, scg->cdb_len);

	if (scg->cdb_len == SC_G5_CDBLEN )
		memcpy(sco->cmd.cdb, &scg->cdb.g5_cdb, scg->cdb_len);
}


LOCAL void
cp_sco2scg(sco, scg)
	struct scsicmd2	*sco;
	struct scg_cmd	*scg;
{
	scg->size      = sco->cmd.data_len;

	memset(&scg->scb, 0, sizeof(scg->scb));

	if (sco->sense_len > SCG_MAX_SENSE)
		scg->sense_count = SCG_MAX_SENSE;
	else
		scg->sense_count = sco->sense_len;

	scg->resid = 0;
	scg->u_scb.cmd_scb[0] = sco->cmd.target_sts;

}


LOCAL int
scgo_send(scgp)
	SCSI		*scgp;
{
	struct scg_cmd	*sp = scgp->scmd;
	struct scsicmd2	scsi_cmd;
	int		i;
	Uchar		sense_buf[SCG_MAX_SENSE];

	if (scgp->fd < 0) {
		sp->error = SCG_FATAL;
		return (0);
	}

	memset(&scsi_cmd, 0, sizeof(scsi_cmd));
	memset(sense_buf,  0, sizeof(sense_buf));
	scsi_cmd.sense_ptr = sense_buf;
	scsi_cmd.sense_len = sizeof(sense_buf);
	cp_scg2sco(&scsi_cmd, sp);

	errno = 0;
	for (;;) {
		int	ioctlStatus;

		if ((ioctlStatus = ioctl(scgp->fd, SCSIUSERCMD2, &scsi_cmd)) < 0) {
			if (errno == EINTR)
				continue;

			cp_sco2scg(&scsi_cmd, sp);
			sp->ux_errno = errno;
			if (errno == 0)
				sp->ux_errno = EIO;
			sp->error    = SCG_RETRYABLE;

			return (0);
		}
		break;
	}

	cp_sco2scg(&scsi_cmd, sp);
	sp->ux_errno = errno;
	if (scsi_cmd.cmd.target_sts & 0x02) {
		if (errno == 0)
			sp->ux_errno = EIO;
		sp->error    = SCG_RETRYABLE;
	} else {
		sp->error    = SCG_NO_ERROR;
	}

	return 0;
}


#define	sense	u_sense.Sense
