/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/* @(#)scsi-wnt.c	1.45 04/07/19 Copyright 1998-2004 J. Schilling, A.L. Faber, J.A. Key */
/*
 *	Interface for the Win32 ASPI library.
 *		You need wnaspi32.dll and aspi32.sys
 *		Both can be installed from ASPI_ME
 *
 *	Warning: you may change this source, but if you do that
 *	you need to change the _usal_version and _usal_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-2004 J. Schilling
 *	Copyright (c) 1999 A.L. Faber for the first implementation
 *			   of this interface.
 *	TODO:
 *	-	DMA resid handling
 *	-	better handling of maxDMA
 *	-	SCSI reset support
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


/*
 * Include for Win32 ASPI AspiRouter
 *
 * NOTE: aspi-win32.h includes Windows.h and Windows.h includes
 *	 Base.h which has a second typedef for BOOL.
 *	 We define BOOL to make all local code use BOOL
 *	 from Windows.h and use the hidden __SBOOL for
 *	 our global interfaces.
 */
#define	BOOL	WBOOL		/* This is the Win BOOL		*/
#define	format	__format
#include <usal/aspi-win32.h>
#include <usal/spti-wnt.h>
#undef format

#ifdef	__CYGWIN32__		/* Use dlopen()			*/
#include <dlfcn.h>
#endif

/*
 *	Warning: you may change this source, but if you do that
 *	you need to change the _usal_version and _usal_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.
 */
static	char	_usal_trans_version[] = "scsi-wnt.c-1.45";	/* The version for this transport*/
static	char	_usal_itrans_version[] = "SPTI-scsi-wnt.c-1.45";	/* The version for SPTI */

/*
 * Local defines and constants
 */
/*#define DEBUG_WNTASPI*/

#define	MAX_SCG		16	/* Max # of SCSI controllers	*/
#define	MAX_TGT		16	/* Max # of SCSI Targets	*/
#define	MAX_LUN		8	/* Max # of SCSI LUNs		*/

#ifdef DEBUG_WNTASPI
#endif

struct usal_local {
	int	dummy;
};
#define	usallocal(p)	((struct usal_local *)((p)->local))

/*
 * Local variables
 */
static	int	busses;
static	DWORD	(*pfnGetASPI32SupportInfo)(void)		= NULL;
static	DWORD	(*pfnSendASPI32Command)(LPSRB)			= NULL;
static	BOOL	(*pfnGetASPI32Buffer)(PASPI32BUFF)		= NULL;
static	BOOL	(*pfnFreeASPI32Buffer)(PASPI32BUFF)		= NULL;
static	BOOL	(*pfnTranslateASPI32Address)(PDWORD, PDWORD)	= NULL;

static	int	DriverLoaded			= 0;    /* ASPI or SPTI */
static	HANDLE	hAspiLib			= NULL;	/* Used for Loadlib */

#define	MAX_DMA_WNT	(63L*1024L) /* ASPI-Driver  allows up to 64k ??? */

/*
 * Local function prototypes
 */
static	void	exit_func(void);
#ifdef DEBUG_WNTASPI
static	void	DebugScsiSend(SCSI *usalp, SRB_ExecSCSICmd *s, int bDisplayBuffer);
#endif
static	void	copy_sensedata(SRB_ExecSCSICmd *cp, struct usal_cmd *sp);
static	void	set_error(SRB_ExecSCSICmd *cp, struct usal_cmd *sp);
static	BOOL	open_driver(SCSI *usalp);
static	BOOL	load_aspi(SCSI *usalp);
static	BOOL	close_driver(void);
static	int	ha_inquiry(SCSI *usalp, int id, SRB_HAInquiry	*ip);
#ifdef	__USED__
static	int	resetSCSIBus(SCSI *usalp);
#endif
static	int	scsiabort(SCSI *usalp, SRB_ExecSCSICmd *sp);


/* SPTI Start ---------------------------------------------------------------*/
/*
 * From scsipt.c - Copyright (C) 1999 Jay A. Key
 * Homepage: http://akrip.sourceforge.net/
 * Native NT support functions via the SCSI Pass Through interface instead
 * of ASPI.  Although based on information from the NT 4.0 DDK from
 * Microsoft, the information has been sufficiently distilled to allow
 * compilation w/o having the DDK installed.
 * added to scsi-wnt.c by Richard Stemmer, rs@epost.de
 * See http://www.ste-home.de/cdrtools-spti/
 */

#define	PREFER_SPTI	1		/* Prefer SPTI if available, else try ASPI, force ASPI with dev=ASPI: */
/* #define	CREATE_NONSHARED 1 */	/* open CDROM-Device not SHARED if possible */
/* #define	_DEBUG_SCSIPT 1   */
#ifdef _DEBUG_SCSIPT
FILE *usalp_errfile; /* File for SPTI-Debug-Messages */
#endif

#define	SENSE_LEN_SPTI		32	/* Sense length for ASPI is only 14 */
#define	NUM_MAX_NTSCSI_DRIVES	26	/* a: ... z:			*/
#define	NUM_FLOPPY_DRIVES	2
#define	NUM_MAX_NTSCSI_HA	NUM_MAX_NTSCSI_DRIVES

#define	NTSCSI_HA_INQUIRY_SIZE	36

#define	SCSI_CMD_INQUIRY	0x12

typedef struct {
	BYTE	ha;			/* SCSI Bus #			*/
	BYTE	tgt;			/* SCSI Target #		*/
	BYTE	lun;			/* SCSI Lun #			*/
	BYTE	PortNumber;		/* SCSI Card # (\\.\SCSI%d)	*/
	BYTE	PathId;			/* SCSI Bus/Channel # on card n	*/
	BYTE	driveLetter;		/* Win32 drive letter (e.g. c:)	*/
	BOOL	bUsed;			/* Win32 drive letter is used	*/
	HANDLE	hDevice;		/* Win32 handle for ioctl()	*/
	BYTE	inqData[NTSCSI_HA_INQUIRY_SIZE];
} DRIVE;

typedef struct {
	BYTE	numAdapters;
	DRIVE	drive[NUM_MAX_NTSCSI_DRIVES];
} SPTIGLOBAL;

static	int	InitSCSIPT(SCSI *usalp);
static	int	DeinitSCSIPT(void);
static	void	GetDriveInformation(BYTE i, DRIVE *pDrive);
static	BYTE	SPTIGetNumAdapters(void);
static	BYTE	SPTIGetDeviceIndex(BYTE ha, BYTE tgt, BYTE lun);
static	DWORD	SPTIHandleHaInquiry(LPSRB_HAInquiry lpsrb);
static	DWORD	SPTIExecSCSICommand(LPSRB_ExecSCSICmd lpsrb, int sptTimeOutValue, BOOL bBeenHereBefore);
static	HANDLE	GetFileHandle(BYTE i, BOOL openshared);

static	BOOL	bSCSIPTInit = FALSE;
static	SPTIGLOBAL sptiglobal;
static	BOOL	UsingSPTI = FALSE;
static	BOOL	ForceAccess = FALSE;
static	int	sptihamax;
static	USHORT	sptihasortarr[NUM_MAX_NTSCSI_HA];

/*
 * Initialization of SCSI Pass Through Interface code.  Responsible for
 * setting up the array of SCSI devices.  This code will be a little
 * different from the normal code -- it will query each drive letter from
 * C: through Z: to see if it is  a CD.  When we identify a CD, we then
 * send CDB with the INQUIRY command to it -- NT will automagically fill in
 * the PathId, TargetId, and Lun for us.
 */
static int InitSCSIPT(SCSI *usalp) {
	BYTE	i;
	BYTE	j;
	char	buf[4];
	UINT	uDriveType;
	int	retVal = 0;
	USHORT hasortval;
	char adapter_name[20];
	HANDLE	fh;
	ULONG	returned;
	BOOL	status;
	char	InquiryBuffer[2048];
	PSCSI_ADAPTER_BUS_INFO	ai;
	BYTE	bus;
	int explicite_number=-1;

	if (bSCSIPTInit)
		return (0);

	/*
	 * Detect all Busses on all SCSI-Adapters
	 * Fill up map array that allows us to later assign devices to
	 * bus numbers.
	 */
	sptihamax = 0;
	i = 0;
	do {
		snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\SCSI%d:", i);
		fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
						FILE_SHARE_READ | FILE_SHARE_WRITE,
						NULL,
						OPEN_EXISTING, 0, NULL);
		if (fh != INVALID_HANDLE_VALUE) {
			status	= DeviceIoControl(fh,
						IOCTL_SCSI_GET_INQUIRY_DATA,
						NULL,
						0,
						InquiryBuffer,
						2048,
						&returned,
						FALSE);
			if (status) {
				ai = (PSCSI_ADAPTER_BUS_INFO) InquiryBuffer;
				for (bus = 0; bus < ai->NumberOfBusses; bus++) {
					sptihasortarr[sptihamax] = ((i<<8) | bus);
					sptihamax++;
				}
			}
			CloseHandle(fh);
		}
		i++;
	} while (fh != INVALID_HANDLE_VALUE);

	errno = 0;
	memset(&sptiglobal, 0, sizeof (SPTIGLOBAL));
	for (i = 0; i < NUM_MAX_NTSCSI_DRIVES; i++)
		sptiglobal.drive[i].hDevice = INVALID_HANDLE_VALUE;

	for (i = NUM_FLOPPY_DRIVES; i < NUM_MAX_NTSCSI_DRIVES; i++) {
		snprintf(buf, sizeof (buf), "%c:\\", (char)('A'+i));
		uDriveType = GetDriveType(buf);
#ifdef	CDROM_ONLY
		if (uDriveType == DRIVE_CDROM) {
#else
		if (TRUE) {
#endif
			GetDriveInformation(i, &sptiglobal.drive[i]);

			if (sptiglobal.drive[i].bUsed) {
				retVal++;
				hasortval = (sptiglobal.drive[i].PortNumber<<8) | sptiglobal.drive[i].PathId;
				for (j = 0; j < sptihamax; j++) {
					if (hasortval <= sptihasortarr[j])
						break;
				}
				if (j == sptihamax) {
					sptihasortarr[j] = hasortval;
					sptihamax++;
				} else if (hasortval < sptihasortarr[j]) {
					memmove(&sptihasortarr[j+1], &sptihasortarr[j], (sptihamax-j) * sizeof (USHORT));
					sptihasortarr[j] = hasortval;
					sptihamax++;
				}

				/* shortcut for device names */
				if(uDriveType==DRIVE_CDROM && usalp->device && ('A'+i)==toupper(usalp->device[0]))
					explicite_number=i;
			}
		}
	}
		/* looks like a workaround for diverging ASPI and SPTI hostadapter numbers,
		   most likely an attempt to keep the world of fake numbers
		   consistent;
		   EB */
	if (sptihamax > 0) {
		for (i = NUM_FLOPPY_DRIVES; i < NUM_MAX_NTSCSI_DRIVES; i++)
			if (sptiglobal.drive[i].bUsed)
				for (j = 0; j < sptihamax; j++) {
					if (sptihasortarr[j] == ((sptiglobal.drive[i].PortNumber<<8) | sptiglobal.drive[i].PathId)) {
						sptiglobal.drive[i].ha = j;
						break;
					}
				}
	}
	sptiglobal.numAdapters = SPTIGetNumAdapters();

	bSCSIPTInit = TRUE;
	if(explicite_number>0) {
		usal_scsibus(usalp)=sptiglobal.drive[explicite_number].ha;
		usal_target(usalp) =sptiglobal.drive[explicite_number].tgt;
		usal_lun(usalp)    =sptiglobal.drive[explicite_number].lun;
		/* now unset it to avoid further confusion */
		usalp->device[0]='\0';

		#ifdef _DEBUG_SCSIPT
		fprintf(stderr, "named SCSIPT drive type %d found as %c, choosing %d, %d, %d\n", 
				uDriveType,
				'A'+explicite_number, 
				usal_scsibus(usalp), 
				usal_target(usalp), 
				usal_lun(usalp));
		#endif
	}

	if (retVal > 0)
		UsingSPTI = TRUE;

	return (retVal);
}


static int
DeinitSCSIPT(void)
{
	BYTE	i;

	if (!bSCSIPTInit)
		return (0);

	for (i = NUM_FLOPPY_DRIVES; i < NUM_MAX_NTSCSI_DRIVES; i++) {
		if (sptiglobal.drive[i].bUsed) {
			CloseHandle(sptiglobal.drive[i].hDevice);
		}
	}

	sptiglobal.numAdapters = SPTIGetNumAdapters();

	memset(&sptiglobal, 0, sizeof (SPTIGLOBAL));
	bSCSIPTInit = FALSE;
	return (-1);
}


/*
 * Returns the number of "adapters" present.
 */
static BYTE
SPTIGetNumAdapters(void)
{
	BYTE	buf[256];
	WORD	i;
	BYTE	numAdapters = 0;

	memset(buf, 0, 256);

	/*
	 * PortNumber 0 should exist, so pre-mark it.  This avoids problems
	 * when the primary IDE drives are on PortNumber 0, but can't be opened
	 * because of insufficient privelege (ie. non-admin).
	 */
	buf[0] = 1;
	for (i = 0; i < NUM_MAX_NTSCSI_DRIVES; i++) {
		if (sptiglobal.drive[i].bUsed)
			buf[sptiglobal.drive[i].ha] = 1;
	}

	for (i = 0; i <= 255; i++)
		if (buf[i])
			numAdapters = (BYTE)(i + 1);

/*	numAdapters++; */

	return (numAdapters);
}

#include <ctype.h>
static BOOL
w2k_or_newer(void)
{
	OSVERSIONINFO osver;

	memset(&osver, 0, sizeof (osver));
	osver.dwOSVersionInfoSize = sizeof (osver);
	GetVersionEx(&osver);
	if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
		/*
		 * Win2000 is NT-5.0, Win-XP is NT-5.1
		 */
		if (osver.dwMajorVersion > 4)
			return (TRUE);
	}
	return (FALSE);
}

static BOOL
w2kstyle_create(void)
{
	OSVERSIONINFO osver;

/*	return FALSE; */
	memset(&osver, 0, sizeof (osver));
	osver.dwOSVersionInfoSize = sizeof (osver);
	GetVersionEx(&osver);
	if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
		/*
		 * Win2000 is NT-5.0, Win-XP is NT-5.1
		 */
		if (osver.dwMajorVersion > 4)
			return (TRUE);

		if (osver.dwMajorVersion == 4) {		/* NT-4.x */
			char	*vers = osver.szCSDVersion;

			if (strlen(vers) == 0)
				return (FALSE);

			/*
			 * Servicepack is installed, skip over non-digit part
			 */
			while (*vers != '\0' && !isdigit(*vers))
				vers++;
			if (*vers == '\0')
				return (FALSE);

			if (isdigit(vers[0]) &&
			    (atoi(vers) >= 4 || isdigit(vers[1])))	/* Fom Service Pack 4 */
				return (TRUE);				/* same as for W2K */
		}
	}
	return (FALSE);
}


/*
 * Universal function to get a file handle to the CD device.  Since
 * NT 4.0 wants just the GENERIC_READ flag, and Win2K wants both
 * GENERIC_READ and GENERIC_WRITE (why a read-only CD device needs
 * GENERIC_WRITE access is beyond me...), the easist workaround is to just
 * try them both.
 */
static HANDLE
GetFileHandle(BYTE i, BOOL openshared)
{
	char	buf[12];
	HANDLE	fh;
	DWORD	dwFlags = GENERIC_READ;
	DWORD	dwAccessMode = 0;

	dwAccessMode = FILE_SHARE_READ;
	if (w2kstyle_create()) { /* if Win2K or greater, add GENERIC_WRITE */
		dwFlags |= GENERIC_WRITE;
		dwAccessMode |= FILE_SHARE_WRITE;
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "SPTI: GetFileHandle(): Setting for Win2K\n");
#endif
	}
	snprintf(buf, sizeof (buf), "\\\\.\\%c:", (char)('A'+i));
#ifdef CREATE_NONSHARED
	if (openshared) {
		fh = CreateFile(buf, dwFlags, dwAccessMode, NULL,
						OPEN_EXISTING, 0, NULL);
	} else {
		fh = CreateFile(buf, dwFlags, 0, NULL,
						OPEN_EXISTING, 0, NULL);
	}
	if (!openshared && fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
#endif
		fh = CreateFile(buf, dwFlags, dwAccessMode, NULL,
						OPEN_EXISTING, 0, NULL);
	if (fh == INVALID_HANDLE_VALUE) {
		/*
		 * it went foobar somewhere, so try it with the GENERIC_WRITE
		 * bit flipped
		 */
		dwFlags ^= GENERIC_WRITE;
		dwAccessMode ^= FILE_SHARE_WRITE;
#ifdef CREATE_NONSHARED
		if (openshared) {
			fh = CreateFile(buf, dwFlags, dwAccessMode, NULL,
						OPEN_EXISTING, 0, NULL);
		} else {
			fh = CreateFile(buf, dwFlags, 0, NULL,
						OPEN_EXISTING, 0, NULL);
		}
		if (!openshared && fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
#endif
			fh = CreateFile(buf, dwFlags, dwAccessMode, NULL,
						OPEN_EXISTING, 0, NULL);
	}
#ifdef _DEBUG_SCSIPT
	if (fh == INVALID_HANDLE_VALUE)
		fprintf(usalp_errfile, "SPTI: CreateFile() failed! -> %d\n", GetLastError());
	else
		fprintf(usalp_errfile, "SPTI: CreateFile() returned %d\n", GetLastError());
#endif

	return (fh);
}


/*
 * fills in a pDrive structure with information from a SCSI_INQUIRY
 * and obtains the ha:tgt:lun values via IOCTL_SCSI_GET_ADDRESS
 */
static void GetDriveInformation(BYTE i, DRIVE *pDrive)
{
	HANDLE		fh;
	BOOL		status;
	SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
	SCSI_ADDRESS	scsiAddr;
	ULONG		length;
	ULONG		returned;
	BYTE		inqData[NTSCSI_HA_INQUIRY_SIZE];

#ifdef _DEBUG_SCSIPT
	fprintf(usalp_errfile, "SPTI: Checking drive %c:", 'A'+i);
#endif

	fh = GetFileHandle(i, TRUE);	/* No NONSHARED Create for inquiry */

	if (fh == INVALID_HANDLE_VALUE) {
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "       : fh == INVALID_HANDLE_VALUE\n");
#endif
		return;
	}

#ifdef _DEBUG_SCSIPT
	fprintf(usalp_errfile, "       : Index %d: fh == %08X\n", i, fh);
#endif


	/*
	 * Get the drive inquiry data
	 */
	memset(&swb, 0, sizeof (swb));
	memset(inqData, 0, sizeof (inqData));
	swb.spt.Length		= sizeof (SCSI_PASS_THROUGH_DIRECT);
	swb.spt.CdbLength	= 6;
	swb.spt.SenseInfoLength	= 24;
	swb.spt.DataIn		= SCSI_IOCTL_DATA_IN;
	swb.spt.DataTransferLength = 100;
	swb.spt.TimeOutValue	= 2;
	swb.spt.DataBuffer	= inqData;
	swb.spt.SenseInfoOffset	= offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
	swb.spt.Cdb[0]		= SCSI_CMD_INQUIRY;
	swb.spt.Cdb[4]		= NTSCSI_HA_INQUIRY_SIZE;

	length = sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
	status = DeviceIoControl(fh,
			    IOCTL_SCSI_PASS_THROUGH_DIRECT,
			    &swb,
			    sizeof (swb),
			    &swb,
			    sizeof (swb),
			    &returned,
			    NULL);

	if (!status) {
		CloseHandle(fh);
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "SPTI: Error DeviceIoControl() -> %d\n", GetLastError());
#endif
		return;
	}

	memcpy(pDrive->inqData, inqData, NTSCSI_HA_INQUIRY_SIZE);

	/*
	 * get the address (path/tgt/lun) of the drive via IOCTL_SCSI_GET_ADDRESS
	 */
	memset(&scsiAddr, 0, sizeof (SCSI_ADDRESS));
	scsiAddr.Length = sizeof (SCSI_ADDRESS);
	if (DeviceIoControl(fh, IOCTL_SCSI_GET_ADDRESS, NULL, 0,
			&scsiAddr, sizeof (SCSI_ADDRESS), &returned,
			NULL)) {
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "Device %c: Port=%d, PathId=%d, TargetId=%d, Lun=%d\n",
			(char)i+'A', scsiAddr.PortNumber, scsiAddr.PathId,
			scsiAddr.TargetId, scsiAddr.Lun);
#endif
		pDrive->bUsed		= TRUE;
		pDrive->ha		= scsiAddr.PortNumber; /* preliminary */
		pDrive->PortNumber	= scsiAddr.PortNumber;
		pDrive->PathId		= scsiAddr.PathId;
		pDrive->tgt		= scsiAddr.TargetId;
		pDrive->lun		= scsiAddr.Lun;
		pDrive->driveLetter	= i;
		pDrive->hDevice		= INVALID_HANDLE_VALUE;

	} else if (GetLastError() == 50) { /* support USB/FIREWIRE devices where this call is not supported assign drive letter as device ID */
		pDrive->bUsed = TRUE;
		pDrive->ha = i;
		pDrive->PortNumber = i+64; /* hopefully no conflict with other PortNumber */
		pDrive->PathId	= 0;
		pDrive->tgt = 0;
		pDrive->lun = 0;
		pDrive->driveLetter = i;
		pDrive->hDevice = INVALID_HANDLE_VALUE;
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "USB/Firewire Device %c: Port=%d, TargetId=%d, Lun=%d\n", (char)i+'A', i, 0, 0);
#endif
	} else {
		pDrive->bUsed	= FALSE;
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "SPTI: Device %s: Error DeviceIoControl(): %d\n", (char)i+'A', GetLastError());
#endif
		CloseHandle(fh);
		return;
	}
#ifdef _DEBUG_SCSIPT
	fprintf(usalp_errfile,  "SPTI: Adding drive %c: (%d:%d:%d)\n", 'A'+i,
					pDrive->ha, pDrive->tgt, pDrive->lun);
#endif
	CloseHandle(fh);
}



static DWORD
SPTIHandleHaInquiry(LPSRB_HAInquiry lpsrb)
{
	DWORD	*pMTL;

	lpsrb->HA_Count    = sptiglobal.numAdapters;
	if (lpsrb->SRB_HaId >= sptiglobal.numAdapters) {
		lpsrb->SRB_Status = SS_INVALID_HA;
		return (SS_INVALID_HA);
	}
	lpsrb->HA_SCSI_ID  = 7;			/* who cares... we're not really an ASPI manager */
	memcpy(lpsrb->HA_ManagerId,  "AKASPI v0.000001", 16);
	memcpy(lpsrb->HA_Identifier, "SCSI Adapter    ", 16);
	lpsrb->HA_Identifier[13] = (char)('0'+lpsrb->SRB_HaId);
	memset(lpsrb->HA_Unique, 0, 16);
	lpsrb->HA_Unique[3] = 8;
	pMTL = (LPDWORD)&lpsrb->HA_Unique[4];
	*pMTL = 64 * 1024;

	lpsrb->SRB_Status = SS_COMP;
	return (SS_COMP);
}

/*
 * Looks up the index in the drive array for a given ha:tgt:lun triple
 */
static BYTE
SPTIGetDeviceIndex(BYTE ha, BYTE tgt, BYTE lun)
{
	BYTE	i;

#ifdef _DEBUG_SCSIPT
	fprintf(usalp_errfile,  "SPTI: SPTIGetDeviceIndex, %d, %d, %d\n", ha,
			tgt, lun);
#endif

	for (i = NUM_FLOPPY_DRIVES; i < NUM_MAX_NTSCSI_DRIVES; i++) {
		if (sptiglobal.drive[i].bUsed) {
			DRIVE	*lpd;

			lpd = &sptiglobal.drive[i];
			if ((lpd->ha == ha) && (lpd->tgt == tgt) && (lpd->lun == lun))
				return (i);
		}
	}

	return (0);
}

/*
 * Converts ASPI-style SRB to SCSI Pass Through IOCTL
 */

static DWORD
SPTIExecSCSICommand(LPSRB_ExecSCSICmd lpsrb, int sptTimeOutValue, BOOL bBeenHereBefore)
{
	BOOL	status;
	SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
	ULONG	length;
	ULONG	returned;
	BYTE	idx;
	BYTE	j;

	idx = SPTIGetDeviceIndex(lpsrb->SRB_HaId, lpsrb->SRB_Target, lpsrb->SRB_Lun);

	if (idx == 0) {
		lpsrb->SRB_Status = SS_NO_DEVICE;
		return (SS_NO_DEVICE);
	}

	if (lpsrb->CDBByte[0] == SCSI_CMD_INQUIRY) {
		lpsrb->SRB_Status = SS_COMP;
		memcpy(lpsrb->SRB_BufPointer, sptiglobal.drive[idx].inqData, NTSCSI_HA_INQUIRY_SIZE);
		return (SS_COMP);
	}

	if (sptiglobal.drive[idx].hDevice == INVALID_HANDLE_VALUE)
		sptiglobal.drive[idx].hDevice = GetFileHandle(sptiglobal.drive[idx].driveLetter, FALSE);

	memset(&swb, 0, sizeof (swb));
	swb.spt.Length		= sizeof (SCSI_PASS_THROUGH);
	swb.spt.CdbLength	= lpsrb->SRB_CDBLen;
	if (lpsrb->SRB_Flags & SRB_DIR_IN)
		swb.spt.DataIn	= SCSI_IOCTL_DATA_IN;
	else if (lpsrb->SRB_Flags & SRB_DIR_OUT)
		swb.spt.DataIn	= SCSI_IOCTL_DATA_OUT;
	else
		swb.spt.DataIn	= SCSI_IOCTL_DATA_UNSPECIFIED;
	swb.spt.DataTransferLength = lpsrb->SRB_BufLen;
	swb.spt.TimeOutValue	= sptTimeOutValue;
	swb.spt.SenseInfoLength	= lpsrb->SRB_SenseLen;
	swb.spt.DataBuffer	= lpsrb->SRB_BufPointer;
	swb.spt.SenseInfoOffset	=  offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
	memcpy(swb.spt.Cdb, lpsrb->CDBByte, lpsrb->SRB_CDBLen);
	length = sizeof (swb);

#ifdef _DEBUG_SCSIPT
	fprintf(usalp_errfile, "SPTI: SPTIExecSCSICmd: calling DeviceIoControl()");
	fprintf(usalp_errfile, "       : cmd == 0x%02X", swb.spt.Cdb[0]);
#endif
	status = DeviceIoControl(sptiglobal.drive[idx].hDevice,
			    IOCTL_SCSI_PASS_THROUGH_DIRECT,
			    &swb,
			    length,
			    &swb,
			    length,
			    &returned,
			    NULL);

	lpsrb->SRB_SenseLen = swb.spt.SenseInfoLength;
	memcpy(lpsrb->SenseArea, swb.ucSenseBuf, lpsrb->SRB_SenseLen);
	if (status && swb.spt.ScsiStatus == 0) {
		lpsrb->SRB_Status = SS_COMP;
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "       : SRB_Status == SS_COMP\n");
#endif
	} else {
		DWORD	dwErrCode;

		lpsrb->SRB_Status = SS_ERR;
/*		lpsrb->SRB_TargStat =  0x0004;*/
		lpsrb->SRB_TargStat =  swb.spt.ScsiStatus;

		dwErrCode = GetLastError();
#ifdef _DEBUG_SCSIPT
		fprintf(usalp_errfile, "       : error == %d   handle == %08X\n", dwErrCode, sptiglobal.drive[idx].hDevice);
#endif
		/*
		 * KLUDGE ALERT! KLUDGE ALERT! KLUDGE ALERT!
		 * Whenever a disk changer switches disks, it may render the device
		 * handle invalid.  We try to catch these errors here and recover
		 * from them.
		 */
		if (!bBeenHereBefore &&
			((dwErrCode == ERROR_MEDIA_CHANGED) || (dwErrCode == ERROR_INVALID_HANDLE))) {
			if (dwErrCode != ERROR_INVALID_HANDLE)
				CloseHandle(sptiglobal.drive[idx].hDevice);
			GetDriveInformation(idx, &sptiglobal.drive[idx]);
			if (sptihamax > 0) {
				if (sptiglobal.drive[idx].bUsed)
					for (j = 0; j < sptihamax; j++) {
						if (sptihasortarr[j] ==
						    ((sptiglobal.drive[idx].PortNumber << 8) | sptiglobal.drive[idx].PathId)) {
							sptiglobal.drive[idx].ha = j;
							break;
						}
				}
			}
#ifdef _DEBUG_SCSIPT
			fprintf(usalp_errfile, "SPTI: SPTIExecSCSICommand: Retrying after ERROR_MEDIA_CHANGED\n");
#endif
			return (SPTIExecSCSICommand(lpsrb, sptTimeOutValue, TRUE));
		}
	}
	return (lpsrb->SRB_Status);
}
/* SPTI End -----------------------------------------------------------------*/


static void
exit_func()
{
	if (!close_driver())
		errmsgno(EX_BAD, "Cannot close Win32-ASPI-Driver.\n");
}

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

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

static int
usalo_help(SCSI *usalp, FILE *f)
{
	__usal_help(f, "ASPI", "Generic transport independent SCSI",
		"ASPI:", "bus,target,lun", "ASPI:1,2,0", TRUE, FALSE);
	__usal_help(f, "SPTI", "Generic SCSI for Windows NT/2000/XP",
		"SPTI:", "bus,target,lun", "SPTI:1,2,0", TRUE, FALSE);
	return (0);
}

static int
usalo_open(SCSI *usalp, char *device)
{
	int	busno	= usal_scsibus(usalp);
	int	tgt	= usal_target(usalp);
	int	tlun	= usal_lun(usalp);

	usalp->device = NULL;

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

	if (device != NULL && (strcmp(device, "SPTI") == 0 || strcmp(device, "ASPI") == 0))
		goto devok;

	if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) {
/*
		errno = EINVAL;
		if (usalp->errstr)
			snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
				"Open by 'devname' not supported on this OS");
		return (-1);
*/

		UsingSPTI = TRUE;
		usalp->device = device;

		/* not the finest solution but prevents breaking on various
		 * places for no good reasons... */
		usal_scsibus(usalp)=0;
		usal_target(usalp)=0;
		usal_lun(usalp)=0;
		goto openbydev;
	}
devok:
	if (DriverLoaded <= 0) {	/* do not change access method on open driver */
		ForceAccess = FALSE;
#ifdef PREFER_SPTI
		UsingSPTI = TRUE;
#else
		UsingSPTI = FALSE;
#endif
		if (!w2k_or_newer())
			UsingSPTI = FALSE;

		if (usalp->debug > 0) {
			fprintf((FILE *)usalp->errfile,
				"usalo_open: Prefered SCSI transport: %s\n",
					UsingSPTI ? "SPTI":"ASPI");
		}
		if (device != NULL && strcmp(device, "SPTI") == 0) {
			UsingSPTI = TRUE;
			ForceAccess = TRUE;
		} else if (device != NULL && strcmp(device, "ASPI") == 0) {
			UsingSPTI = FALSE;
			ForceAccess = TRUE;
		}
		if (device != NULL && usalp->debug > 0) {
			fprintf((FILE *)usalp->errfile,
				"usalo_open: Selected SCSI transport: %s\n",
					UsingSPTI ? "SPTI":"ASPI");
		}
	}

	/*
	 *  Check if variables are within the range
	 */
	if (tgt >= 0 && tgt >= 0 && tlun >= 0) {
		/*
		 * This is the non -scanbus case.
		 */
		;
	} else if (tgt == -2 && tgt == -2 &&
		    (tgt == -2 || tlun >= 0)) {
		/*
		 * This is the dev=ASPI case.
		 */
		;
	} else if (tgt != -1 || tgt != -1 || tlun != -1) {
		errno = EINVAL;
		return (-1);
	}

openbydev:
	if (usalp->local == NULL) {
		usalp->local = malloc(sizeof (struct usal_local));
		if (usalp->local == NULL)
			return (0);
	}
	/*
	 * Try to open ASPI-Router
	 */
	if (!open_driver(usalp))
		return (-1);

	/*
	 * More than we have ...
	 */
	if (busno >= busses) {
		close_driver();
		return (-1);
	}

	/*
	 * Install Exit Function which closes the ASPI-Router
	 */
	atexit(exit_func);

	/*
	 * Success after all
	 */
	return (1);
}

static int
usalo_close(SCSI *usalp)
{
	//printf("closing\n");
	exit_func();
	return (0);
}

static long
usalo_maxdma(SCSI *usalp, long amt)
{
	return (MAX_DMA_WNT);
}

static void *
usalo_getbuf(SCSI *usalp, long amt)
{
	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile,
				"usalo_getbuf: %ld bytes\n", amt);
	}
	usalp->bufbase = malloc((size_t)(amt));
	return (usalp->bufbase);
}

static void
usalo_freebuf(SCSI *usalp)
{
	if (usalp->bufbase)
		free(usalp->bufbase);
	usalp->bufbase = NULL;
}

static __SBOOL
usalo_havebus(SCSI *usalp, int busno)
{
	if (busno < 0 || busno >= busses)
		return (FALSE);

	return (TRUE);
}

static int
usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
{
	if (busno < 0 || busno >= busses ||
	    tgt < 0 || tgt >= MAX_TGT ||
	    tlun < 0 || tlun >= MAX_LUN)
		return (-1);

	/*
	 * Return fake
	 */
	return (1);
}


static int
usalo_initiator_id(SCSI *usalp)
{
	SRB_HAInquiry	s;

	if (ha_inquiry(usalp, usal_scsibus(usalp), &s) < 0)
		return (-1);
	return (s.HA_SCSI_ID);
}

static int
usalo_isatapi(SCSI *usalp)
{
	return (-1);	/* XXX Need to add real test */
}


/*
 * XXX usalo_reset not yet tested
 */
static int
usalo_reset(SCSI *usalp, int what)
{

	DWORD			Status = 0;
	DWORD			EventStatus = WAIT_OBJECT_0;
	HANDLE			Event	 = NULL;
	SRB_BusDeviceReset	s;

	if (what == SCG_RESET_NOP) {
		if (UsingSPTI)
			return (-1);
		else
			return (0);  /* Can ASPI really reset? */
	}
	if (what != SCG_RESET_BUS) {
		errno = EINVAL;
		return (-1);
	}
	if (UsingSPTI) {
		fprintf((FILE *)usalp->errfile,
					"Reset SCSI device not implemented with SPTI\n");
		return (-1);
	}

	/*
	 * XXX Does this reset TGT or BUS ???
	 */
	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile,
				"Attempting to reset SCSI device\n");
	}

	/*
	 * Check if ASPI library is loaded
	 */
	if (DriverLoaded <= 0) {
		fprintf((FILE *)usalp->errfile,
				"error in usalo_reset: ASPI driver not loaded !\n");
		return (-1);
	}

	memset(&s, 0, sizeof (s));	/* Clear SRB_BesDeviceReset structure */

	Event = CreateEvent(NULL, TRUE, FALSE, NULL);

	/*
	 * Set structure variables
	 */
	s.SRB_Cmd	= SC_RESET_DEV;			/* ASPI command code = SC_RESET_DEV	*/
	s.SRB_HaId	= usal_scsibus(usalp);		/* ASPI host adapter number		*/
	s.SRB_Flags	= SRB_EVENT_NOTIFY;		/* Flags				*/
	s.SRB_Target	= usal_target(usalp);		/* Target's SCSI ID			*/
	s.SRB_Lun	= usal_lun(usalp);		/* Target's LUN number			*/
	s.SRB_PostProc	= (LPVOID)Event;		/* Post routine				*/

	/*
	 * Initiate SCSI command
	 */
	Status = pfnSendASPI32Command((LPSRB)&s);

	/*
	 * Check status
	 */
	if (Status == SS_PENDING) {
		/*
		 * Wait till command completes
		 */
		EventStatus = WaitForSingleObject(Event, INFINITE);
	}


	/**************************************************/
	/* Reset event to non-signaled state.		  */
	/**************************************************/

	if (EventStatus == WAIT_OBJECT_0) {
		/*
		 * Clear event
		 */
		ResetEvent(Event);
	}

	/*
	 * Close the event handle
	 */
	CloseHandle(Event);

	/*
	 * Check condition
	 */
	if (s.SRB_Status != SS_COMP) {
		fprintf((FILE *)usalp->errfile,
					"ERROR! 0x%08X\n", s.SRB_Status);

		/*
		 * Indicate that error has occured
		 */
		return (-1);
	}

	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile,
					"Reset SCSI device completed\n");
	}

	/*
	 * Everything went OK
	 */
	return (0);
}


#ifdef DEBUG_WNTASPI
static void
DebugScsiSend(SCSI *usalp, SRB_ExecSCSICmd *s, int bDisplayBuffer)
{
	int i;

	fprintf((FILE *)usalp->errfile, "\n\nDebugScsiSend\n");
	fprintf((FILE *)usalp->errfile, "s->SRB_Cmd          = 0x%02x\n", s->SRB_Cmd);
	fprintf((FILE *)usalp->errfile, "s->SRB_HaId         = 0x%02x\n", s->SRB_HaId);
	fprintf((FILE *)usalp->errfile, "s->SRB_Flags        = 0x%02x\n", s->SRB_Flags);
	fprintf((FILE *)usalp->errfile, "s->SRB_Target       = 0x%02x\n", s->SRB_Target);
	fprintf((FILE *)usalp->errfile, "s->SRB_Lun          = 0x%02x\n", s->SRB_Lun);
	fprintf((FILE *)usalp->errfile, "s->SRB_BufLen       = 0x%02x\n", s->SRB_BufLen);
	fprintf((FILE *)usalp->errfile, "s->SRB_BufPointer   = %x\n",	   s->SRB_BufPointer);
	fprintf((FILE *)usalp->errfile, "s->SRB_CDBLen       = 0x%02x\n", s->SRB_CDBLen);
	fprintf((FILE *)usalp->errfile, "s->SRB_SenseLen     = 0x%02x\n", s->SRB_SenseLen);
	fprintf((FILE *)usalp->errfile, "s->CDBByte          =");
	for (i = 0; i < min(s->SRB_CDBLen, 16); i++) {
		fprintf((FILE *)usalp->errfile, " %02X ", s->CDBByte[i]);
	}
	fprintf((FILE *)usalp->errfile, "\n");

	/*
	if (bDisplayBuffer != 0 && s->SRB_BufLen >= 8) {

		fprintf((FILE *)usalp->errfile, "s->SRB_BufPointer   =");
		for (i = 0; i < 8; i++) {
			fprintf((FILE *)usalp->errfile,
					" %02X ", ((char *)s->SRB_BufPointer)[i]);
		}
		fprintf((FILE *)usalp->errfile, "\n");
	}
*/
	fprintf((FILE *)usalp->errfile, "Debug done\n");
}
#endif

static void
copy_sensedata(SRB_ExecSCSICmd *cp, struct usal_cmd *sp)
{
	sp->sense_count	= cp->SRB_SenseLen;
	if (sp->sense_count > sp->sense_len)
		sp->sense_count = sp->sense_len;

	memset(&sp->u_sense.Sense, 0x00, sizeof (sp->u_sense.Sense));
	memcpy(&sp->u_sense.Sense, cp->SenseArea, sp->sense_count);

	sp->u_scb.cmd_scb[0] = cp->SRB_TargStat;
}

/*
 * Set error flags
 */
static void
set_error(SRB_ExecSCSICmd *cp, struct usal_cmd *sp)
{
	switch (cp->SRB_Status) {

	case SS_COMP:			/* 0x01 SRB completed without error  */
		sp->error = SCG_NO_ERROR;
		sp->ux_errno = 0;
		break;

	case SS_ERR:			/* 0x04 SRB completed with error    */
		/*
		 * If the SCSI Status byte is != 0, we definitely could send
		 * the command to the target. We signal NO transport error.
		 */
		sp->error = SCG_NO_ERROR;
		sp->ux_errno = EIO;
		if (cp->SRB_TargStat)
			break;

	case SS_PENDING:		/* 0x00 SRB being processed	    */
		/*
		 * XXX Could SS_PENDING happen ???
		 */
	case SS_ABORTED:		/* 0x02 SRB aborted		    */
	case SS_ABORT_FAIL:		/* 0x03 Unable to abort SRB	    */
	default:
		sp->error = SCG_RETRYABLE;
		sp->ux_errno = EIO;
		break;

	case SS_INVALID_CMD:		/* 0x80 Invalid ASPI command	    */
	case SS_INVALID_HA:		/* 0x81 Invalid host adapter number */
	case SS_NO_DEVICE:		/* 0x82 SCSI device not installed   */

	case SS_INVALID_SRB:		/* 0xE0 Invalid parameter set in SRB */
	case SS_ILLEGAL_MODE:		/* 0xE2 Unsupported Windows mode    */
	case SS_NO_ASPI:		/* 0xE3 No ASPI managers	    */
	case SS_FAILED_INIT:		/* 0xE4 ASPI for windows failed init */
	case SS_MISMATCHED_COMPONENTS:	/* 0xE7 The DLLs/EXEs of ASPI don't */
					/*	version check		    */
	case SS_NO_ADAPTERS:		/* 0xE8 No host adapters to manager */

	case SS_ASPI_IS_SHUTDOWN:	/* 0xEA Call came to ASPI after	    */
					/*	PROCESS_DETACH		    */
	case SS_BAD_INSTALL:		/* 0xEB The DLL or other components */
					/*	are installed wrong	    */
		sp->error = SCG_FATAL;
		sp->ux_errno = EINVAL;
		break;

#ifdef	XXX
	case SS_OLD_MANAGER:		/* 0xE1 ASPI manager doesn't support */
					/*	windows			    */
#endif
	case SS_BUFFER_ALIGN:		/* 0xE1 Buffer not aligned (replaces */
					/*	SS_OLD_MANAGER in Win32)    */
		sp->error = SCG_FATAL;
		sp->ux_errno = EFAULT;
		break;

	case SS_ASPI_IS_BUSY:		/* 0xE5 No resources available to   */
					/*	execute command		    */
		sp->error = SCG_RETRYABLE;
		sp->ux_errno = EBUSY;
		break;

#ifdef	XXX
	case SS_BUFFER_TO_BIG:		/* 0xE6 Buffer size too big to handle*/
#endif
	case SS_BUFFER_TOO_BIG:		/* 0xE6 Correct spelling of 'too'   */
	case SS_INSUFFICIENT_RESOURCES:	/* 0xE9 Couldn't allocate resources */
					/*	needed to init		    */
		sp->error = SCG_RETRYABLE;
		sp->ux_errno = ENOMEM;
		break;
	}
}


struct aspi_cmd {
	SRB_ExecSCSICmd		s;
	char			pad[32];
};

static int
usalo_send(SCSI *usalp)
{
	struct usal_cmd		*sp = usalp->scmd;
	DWORD			Status = 0;
	DWORD			EventStatus = WAIT_OBJECT_0;
	HANDLE			Event	 = NULL;
	struct aspi_cmd		ac;
	SRB_ExecSCSICmd		*s;

	s = &ac.s;

	/*
	 * Check if ASPI library is loaded
	 */
	if (DriverLoaded <= 0) {
		errmsgno(EX_BAD, "error in usalo_send: ASPI driver not loaded.\n");
		sp->error = SCG_FATAL;
		return (0);
	}

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

	/*
	 * Initialize variables
	 */
	sp->error		= SCG_NO_ERROR;
	sp->sense_count		= 0;
	sp->u_scb.cmd_scb[0]	= 0;
	sp->resid		= 0;

	memset(&ac, 0, sizeof (ac));	/* Clear SRB structure */

	/*
	 * Check cbd_len > the maximum command pakket that can be handled by ASPI
	 */
	if (sp->cdb_len > 16) {
		sp->error = SCG_FATAL;
		sp->ux_errno = EINVAL;
		fprintf((FILE *)usalp->errfile,
			"sp->cdb_len > sizeof (SRB_ExecSCSICmd.CDBByte). Fatal error in usalo_send, exiting...\n");
		return (-1);
	}
	/*
	 * copy cdrecord command into SRB
	 */
	movebytes(&sp->cdb, &(s->CDBByte), sp->cdb_len);

	Event = CreateEvent(NULL, TRUE, FALSE, NULL);

	/*
	 * Fill ASPI structure
	 */
	s->SRB_Cmd	 = SC_EXEC_SCSI_CMD;		/* SCSI Command			*/
	s->SRB_HaId	 = usal_scsibus(usalp);		/* Host adapter number		*/
	s->SRB_Flags	 = SRB_EVENT_NOTIFY;		/* Flags			*/
	s->SRB_Target	 = usal_target(usalp);		/* Target SCSI ID		*/
	s->SRB_Lun	 = usal_lun(usalp);		/* Target SCSI LUN		*/
	s->SRB_BufLen	 = sp->size;			/* # of bytes transferred	*/
	s->SRB_BufPointer = sp->addr;			/* pointer to data buffer	*/
	s->SRB_CDBLen	 = sp->cdb_len;			/* SCSI command length		*/
	s->SRB_PostProc	 = Event;			/* Post proc event		*/
	if (UsingSPTI)
		s->SRB_SenseLen	= SENSE_LEN_SPTI;	/* Length of sense buffer, SPTI returns SenseInfoLength */
	else
		s->SRB_SenseLen	= SENSE_LEN;		/* fixed length 14 for ASPI */
	/*
	 * Do we receive data from this ASPI command?
	 */
	if (sp->flags & SCG_RECV_DATA) {

		s->SRB_Flags |= SRB_DIR_IN;
	} else {
		/*
		 * Set direction to output
		 */
		if (sp->size > 0) {
			s->SRB_Flags |= SRB_DIR_OUT;
		}
	}

#ifdef DEBUG_WNTASPI
	/*
	 * Dump some debug information when enabled
	 */
	DebugScsiSend(usalp, s, TRUE);
/*	DebugScsiSend(usalp, s, (s->SRB_Flags&SRB_DIR_OUT) == SRB_DIR_OUT);*/
#endif

	/*
	 * ------------ Send SCSI command --------------------------
	 */

	ResetEvent(Event);			/* Clear event handle	    */
	if (UsingSPTI) {
#ifdef _DEBUG_SCSIPT
		usalp_errfile = (FILE *)usalp->errfile;
#endif
		Status = SPTIExecSCSICommand(s, sp->timeout, FALSE);
	}
	else
		Status = pfnSendASPI32Command((LPSRB)s); /* Initiate SCSI command */
	if (Status == SS_PENDING) {		/* If in progress	    */
		/*
		 * Wait untill command completes, or times out.
		 */
		EventStatus = WaitForSingleObject(Event, sp->timeout*1000L);
/*		EventStatus = WaitForSingleObject(Event, 10L);*/

		if (EventStatus == WAIT_OBJECT_0)
			ResetEvent(Event);	/* Clear event, time out    */

		if (s->SRB_Status == SS_PENDING) { /* Check if we got a timeout */
			if (usalp->debug > 0) {
				fprintf((FILE *)usalp->errfile,
						"Timeout....\n");
			}
			scsiabort(usalp, s);
			ResetEvent(Event);	/* Clear event, time out    */
			CloseHandle(Event);	/* Close the event handle   */

			sp->error = SCG_TIMEOUT;
			return (1);		/* Return error		    */
		}
	}
	CloseHandle(Event);			/* Close the event handle   */

	/*
	 * Check ASPI command status
	 */
	if (s->SRB_Status != SS_COMP) {
		if (usalp->debug > 0) {
			fprintf((FILE *)usalp->errfile,
				"Error in usalo_send: s->SRB_Status is 0x%x\n", s->SRB_Status);
		}

		set_error(s, sp);		/* Set error flags	    */
		copy_sensedata(s, sp);		/* Copy sense and status    */

		if (usalp->debug > 0) {
			fprintf((FILE *)usalp->errfile,
				"Mapped to: error %d errno: %d\n", sp->error, sp->ux_errno);
		}
		return (1);
	}

	/*
	 * Return success
	 */
	return (0);
}

/***************************************************************************
 *									   *
 *  BOOL open_driver()							   *
 *									   *
 *  Opens the ASPI Router device driver and sets device_handle.		   *
 *  Returns:								   *
 *    TRUE - Success							   *
 *    FALSE - Unsuccessful opening of device driver			   *
 *									   *
 *  Preconditions: ASPI Router driver has be loaded			   *
 *									   *
 ***************************************************************************/
static BOOL
open_driver(SCSI *usalp)
{
	DWORD	astatus;
	BYTE	HACount;
	BYTE	ASPIStatus;
	int	i;

#ifdef DEBUG_WNTASPI
	fprintf((FILE *)usalp->errfile, "enter open_driver\n");
#endif

	/*
	 * Check if ASPI library is already loaded yet
	 */
	if (DriverLoaded > 0) {
		DriverLoaded++;
		return (TRUE);
	}

	/*
	 * Load the ASPI library or SPTI
	 */
#ifdef _DEBUG_SCSIPT
	usalp_errfile = (FILE *)usalp->errfile;
#endif
#ifdef	PREFER_SPTI
	if (UsingSPTI)
		if (InitSCSIPT(usalp) > 0) DriverLoaded++;
#endif
#ifdef	PREFER_SPTI
	if ((!UsingSPTI || !ForceAccess) && DriverLoaded <= 0) {
#else
	if (!UsingSPTI || !ForceAccess) {
#endif
		if (load_aspi(usalp)) {
			DriverLoaded++;
			UsingSPTI = FALSE;
		}
	}

#ifndef	PREFER_SPTI
	if ((UsingSPTI || !ForceAccess) && DriverLoaded <= 0)
		if (InitSCSIPT(usalp) > 0)
			DriverLoaded++;
#endif	/*PREFER_SPTI*/

	if (DriverLoaded <= 0) {
		if (UsingSPTI) {
			if (errno == 0)
				errno = ENOSYS;
		}
		fprintf((FILE *)usalp->errfile, "Can not load %s driver! ",
						UsingSPTI ? "SPTI":"ASPI");
		return (FALSE);
	}

	if (UsingSPTI) {
		if (usalp->debug > 0)
			fprintf((FILE *)usalp->errfile, "using SPTI Transport\n");

		if (!sptiglobal.numAdapters)
			astatus = (DWORD)(MAKEWORD(0, SS_NO_ADAPTERS));
		else
			astatus = (DWORD)(MAKEWORD(sptiglobal.numAdapters, SS_COMP));
	} else {
		astatus = pfnGetASPI32SupportInfo();
	}

	ASPIStatus = HIBYTE(LOWORD(astatus));
	HACount    = LOBYTE(LOWORD(astatus));

	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile,
			"open_driver %lX HostASPIStatus=0x%x HACount=0x%x\n", astatus, ASPIStatus, HACount);
	}

	if (ASPIStatus != SS_COMP && ASPIStatus != SS_NO_ADAPTERS) {
		fprintf((FILE *)usalp->errfile, "Could not find any host adapters\n");
		fprintf((FILE *)usalp->errfile, "ASPIStatus == 0x%02X", ASPIStatus);
		return (FALSE);
	}
	busses = HACount;

#ifdef DEBUG_WNTASPI
	fprintf((FILE *)usalp->errfile, "open_driver HostASPIStatus=0x%x HACount=0x%x\n", ASPIStatus, HACount);
	fprintf((FILE *)usalp->errfile, "leaving open_driver\n");
#endif

	for (i = 0; i < busses; i++) {
		SRB_HAInquiry	s;

		ha_inquiry(usalp, i, &s);
	}

	/*
	 * Indicate that library loaded/initialized properly
	 */
	return (TRUE);
}

static BOOL
load_aspi(SCSI *usalp)
{
#ifdef	__CYGWIN32__
	hAspiLib = dlopen("WNASPI32", RTLD_NOW);
#else
	hAspiLib = LoadLibrary("WNASPI32");
#endif
	/*
	 * Check if ASPI library is loaded correctly
	 */
	if (hAspiLib == NULL) {
#ifdef	not_done_later
		fprintf((FILE *)usalp->errfile, "Can not load ASPI driver! ");
#endif
		return (FALSE);
	}

	/*
	 * Get a pointer to GetASPI32SupportInfo function
	 * and a pointer to SendASPI32Command function
	 */
#ifdef	__CYGWIN32__
	pfnGetASPI32SupportInfo = (DWORD(*)(void))dlsym(hAspiLib, "GetASPI32SupportInfo");
	pfnSendASPI32Command = (DWORD(*)(LPSRB))dlsym(hAspiLib, "SendASPI32Command");
#else
	pfnGetASPI32SupportInfo = (DWORD(*)(void))GetProcAddress(hAspiLib, "GetASPI32SupportInfo");
	pfnSendASPI32Command = (DWORD(*)(LPSRB))GetProcAddress(hAspiLib, "SendASPI32Command");
#endif

	if ((pfnGetASPI32SupportInfo == NULL) || (pfnSendASPI32Command == NULL)) {
		fprintf((FILE *)usalp->errfile,
				"ASPI function not found in library! ");
		return (FALSE);
	}

	/*
	 * The following functions are currently not used by libusal.
	 * If we start to use them, we need to check whether the founctions
	 * could be found in the ASPI library that just has been loaded.
	 */
#ifdef	__CYGWIN32__
	pfnGetASPI32Buffer = (BOOL(*)(PASPI32BUFF))dlsym(hAspiLib, "GetASPI32Buffer");
	pfnFreeASPI32Buffer = (BOOL(*)(PASPI32BUFF))dlsym(hAspiLib, "FreeASPI32Buffer");
	pfnTranslateASPI32Address = (BOOL(*)(PDWORD, PDWORD))dlsym(hAspiLib, "TranslateASPI32Address");
#else
	pfnGetASPI32Buffer = (BOOL(*)(PASPI32BUFF))GetProcAddress(hAspiLib, "GetASPI32Buffer");
	pfnFreeASPI32Buffer = (BOOL(*)(PASPI32BUFF))GetProcAddress(hAspiLib, "FreeASPI32Buffer");
	pfnTranslateASPI32Address = (BOOL(*)(PDWORD, PDWORD))GetProcAddress(hAspiLib, "TranslateASPI32Address");
#endif
	return (TRUE);
}

/***************************************************************************
 *									   *
 *  BOOL close_driver()							   *
 *									   *
 *  Closes the device driver						   *
 *  Returns:								   *
 *    TRUE - Success							   *
 *    FALSE - Unsuccessful closing of device driver			   *
 *									   *
 *  Preconditions: ASPI Router driver has be opened with open_driver	   *
 *									   *
 ***************************************************************************/
static BOOL
close_driver()
{
	if (--DriverLoaded > 0)
		return (TRUE);
	/*
	 * If library is loaded
	 */
	DeinitSCSIPT();
	/*
	 * Clear all variables
	 */
	if (hAspiLib) {
		pfnGetASPI32SupportInfo	= NULL;
		pfnSendASPI32Command	= NULL;
		pfnGetASPI32Buffer	= NULL;
		pfnFreeASPI32Buffer	= NULL;
		pfnTranslateASPI32Address = NULL;

		/*
		 * Free ASPI library, we do not need it any longer
		 */
#ifdef	__CYGWIN32__
		dlclose(hAspiLib);
#else
		FreeLibrary(hAspiLib);
#endif
		hAspiLib = NULL;
	}

	/*
	 * Indicate that shutdown has been finished properly
	 */
	return (TRUE);
}

static int
ha_inquiry(SCSI *usalp, int id, SRB_HAInquiry *ip)
{
	DWORD		Status;

	ip->SRB_Cmd	 = SC_HA_INQUIRY;
	ip->SRB_HaId	 = id;
	ip->SRB_Flags	 = 0;
	ip->SRB_Hdr_Rsvd = 0;

	if (UsingSPTI)
		Status = SPTIHandleHaInquiry(ip);
	else
		Status = pfnSendASPI32Command((LPSRB)ip);

	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile, "Status : %ld\n",	Status);
		fprintf((FILE *)usalp->errfile, "hacount: %d\n", ip->HA_Count);
		fprintf((FILE *)usalp->errfile, "SCSI id: %d\n", ip->HA_SCSI_ID);
		fprintf((FILE *)usalp->errfile, "Manager: '%.16s'\n", ip->HA_ManagerId);
		fprintf((FILE *)usalp->errfile, "Identif: '%.16s'\n", ip->HA_Identifier);
		usal_prbytes("Unique:", ip->HA_Unique, 16);
	}
	if (ip->SRB_Status != SS_COMP)
		return (-1);
	return (0);
}

#ifdef	__USED__
static int
resetSCSIBus(SCSI *usalp)
{
	DWORD			Status;
	HANDLE			Event;
	SRB_BusDeviceReset	s;

	if (UsingSPTI) {
		fprintf((FILE *)usalp->errfile,
					"Reset SCSI bus not implemented with SPTI\n");
		return (FALSE);
	}

	fprintf((FILE *)usalp->errfile, "Attempting to reset SCSI bus\n");

	Event = CreateEvent(NULL, TRUE, FALSE, NULL);

	memset(&s, 0, sizeof (s));	/* Clear SRB_BesDeviceReset structure */

	/*
	 * Set structure variables
	 */
	s.SRB_Cmd = SC_RESET_DEV;
	s.SRB_PostProc = (LPVOID)Event;

	/*
	 * Clear event
	 */
	ResetEvent(Event);

	/*
	 * Initiate SCSI command
	 */
	Status = pfnSendASPI32Command((LPSRB)&s);

	/*
	 * Check status
	 */
	if (Status == SS_PENDING) {
		/*
		 * Wait till command completes
		 */
		WaitForSingleObject(Event, INFINITE);
	}

	/*
	 * Close the event handle
	 */
	CloseHandle(Event);

	/*
	 * Check condition
	 */
	if (s.SRB_Status != SS_COMP) {
		fprintf((FILE *)usalp->errfile, "ERROR  0x%08X\n", s.SRB_Status);

		/*
		 * Indicate that error has occured
		 */
		return (FALSE);
	}

	/*
	 * Everything went OK
	 */
	return (TRUE);
}
#endif	/* __USED__ */

static int
scsiabort(SCSI *usalp, SRB_ExecSCSICmd *sp)
{
	DWORD			Status = 0;
	SRB_Abort		s;

	if (UsingSPTI) {
		fprintf((FILE *)usalp->errfile,
					"Abort SCSI not implemented with SPTI\n");
		return (FALSE);
	}

	if (usalp->debug > 0) {
		fprintf((FILE *)usalp->errfile,
				"Attempting to abort SCSI command\n");
	}

	/*
	 * Check if ASPI library is loaded
	 */
	if (DriverLoaded <= 0) {
		fprintf((FILE *)usalp->errfile,
				"error in scsiabort: ASPI driver not loaded !\n");
		return (FALSE);
	}

	/*
	 * Set structure variables
	 */
	s.SRB_Cmd	= SC_ABORT_SRB;			/* ASPI command code = SC_ABORT_SRB	*/
	s.SRB_HaId	= usal_scsibus(usalp);		/* ASPI host adapter number		*/
	s.SRB_Flags	= 0;				/* Flags				*/
	s.SRB_ToAbort	= (LPSRB)&sp;			/* sp					*/

	/*
	 * Initiate SCSI abort
	 */
	Status = pfnSendASPI32Command((LPSRB)&s);

	/*
	 * Check condition
	 */
	if (s.SRB_Status != SS_COMP) {
		fprintf((FILE *)usalp->errfile, "Abort ERROR! 0x%08X\n", s.SRB_Status);

		/*
		 * Indicate that error has occured
		 */
		return (FALSE);
	}

	if (usalp->debug > 0)
		fprintf((FILE *)usalp->errfile, "Abort SCSI command completed\n");

	/*
	 * Everything went OK
	 */
	return (TRUE);
}
