diff --git a/sm5/os_win32.cpp b/sm5/os_win32.cpp
index 5482df786867b3fe04dcb9125a9a5fa3e4720179..d5bbb3e558bcd969ad59f1c571a39ad13421a990 100644
--- a/sm5/os_win32.cpp
+++ b/sm5/os_win32.cpp
@@ -46,7 +46,7 @@ extern int64_t bytes; // malloc() byte count
 
 
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.43 2006/10/09 11:11:59 chrfranke Exp $"
+const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.44 2006/10/20 04:25:20 dpgilbert Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 
@@ -124,6 +124,11 @@ static int aspi_open(unsigned adapter, unsigned id);
 static void aspi_close(int fd);
 static int aspi_scan(unsigned long * drives);
 
+#define SPT_FDOFFSET 512
+
+static int spt_open(int pd_num, int tape_num, int sub_addr);
+static void spt_close(int fd);
+
 
 static int is_permissive()
 {
@@ -150,6 +155,12 @@ int guess_device_type (const char * dev_name)
 		return CONTROLLER_ATA;
 	if (!strncmp(dev_name, "scsi", 4))
 		return CONTROLLER_SCSI;
+	if (!strncmp(dev_name, "sd", 2))
+		return CONTROLLER_SCSI;
+	if (!strncmp(dev_name, "pd", 2))
+		return CONTROLLER_SCSI;
+	if (!strncmp(dev_name, "tape", 4))
+		return CONTROLLER_SCSI;
 	return CONTROLLER_UNKNOWN;
 }
 
@@ -259,14 +270,37 @@ int deviceopen(const char * pathname, char *type)
 		    && port < 32 && ((n1 == len && !options[0]) || n2 == len)                              ) {
 			return ata_open(drive[0] - 'a', options, port);
 		}
-	}
+	} else if (!strcmp(type, "SCSI")) {
+		char letter;
+		int pd_num, tape_num, sub_addr, res;
 
-	else if (!strcmp(type, "SCSI")) {
 		// scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0
 		unsigned adapter = ~0, id = ~0; int n = -1;
 		if (sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len) {
 			return aspi_open(adapter, id);
 		}
+		// sd[a-z],N => Physical drive 0-26, RAID port N
+		if (0 == strncmp("sd", pathname, 2)) {
+			letter = ' ';
+			res = sscanf(pathname, "sd%c,%d", &letter, &sub_addr);
+			pd_num = letter - 'a';
+			if ((2 == res) && (pd_num >= 0) && (sub_addr >= 0))
+				return spt_open(pd_num, -1, sub_addr);
+			if ((1 == res) && (pd_num >= 0))
+				return spt_open(pd_num, -1, -1);
+		// pd<m>,N => Physical drive <m>, RAID port N
+		} else if (0 == strncmp("pd", pathname, 2)) {
+			res = sscanf(pathname, "pd%d,%d", &pd_num, &sub_addr);
+			if ((2 == res) && (pd_num >= 0) && (sub_addr >= 0))
+				return spt_open(pd_num, -1, sub_addr);
+			if ((1 == res) && (pd_num >= 0))
+				return spt_open(pd_num, -1, -1);
+		// tape<m> => tape drive <m>
+		} else if (0 == strncmp("tape", pathname, 4)) {
+			res = sscanf(pathname, "tape%d", &tape_num);
+			if ((1 == res) && (tape_num >= 0))
+				return spt_open(-1, tape_num, -1);
+		}
 	}
 
 	errno = EINVAL;
@@ -278,12 +312,12 @@ int deviceopen(const char * pathname, char *type)
 // (Never called in smartctl!)
 int deviceclose(int fd)
 {
-	if ((fd & 0xff00) != 0x0100) {
-		ata_close(fd);
-	}
-	else {
+	if ((fd & 0xff00) == 0x0100)
 		aspi_close(fd);
-	}
+	else if (fd >= SPT_FDOFFSET)
+ 		spt_close(fd);
+	else
+		ata_close(fd);
 	return 0;
 }
 
@@ -1571,7 +1605,7 @@ int highpoint_command_interface(int fd, smart_command_set command, int select, c
 
 
 /////////////////////////////////////////////////////////////////////////////
-// ASPI Interface
+// ASPI Interface (for SCSI devices)
 /////////////////////////////////////////////////////////////////////////////
 
 #pragma pack(1)
@@ -1973,8 +2007,8 @@ static int aspi_scan(unsigned long * drives)
 
 /////////////////////////////////////////////////////////////////////////////
 
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+// Interface to ASPI SCSI devices.  See scsicmds.h and os_linux.c
+static int do_aspi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 {
 	ASPI_SRB srb;
 
@@ -1984,7 +2018,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 		return -EBADF;
 
 	if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
-		pout("do_scsi_cmnd_io: bad CDB length\n");
+		pout("do_aspi_cmnd_io: bad CDB length\n");
 		return -EINVAL;
 	}
 
@@ -2038,7 +2072,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 			srb.i.data_addr = iop->dxferp;
 			break;
 		default:
-			pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+			pout("do_aspi_cmnd_io: bad dxfer_dir\n");
 			return -EINVAL;
 	}
 
@@ -2092,3 +2126,265 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 
 	return 0;
 }
+
+
+/////////////////////////////////////////////////////////////////////////////
+// SPT Interface (for SCSI devices and ATA devices behind SATLs)
+// Only supported in NT and later
+/////////////////////////////////////////////////////////////////////////////
+
+#define SPT_MAXDEV 64
+
+struct spt_dev_info {
+	HANDLE h_spt_ioctl;
+	int   pd_num;		// physical drive number
+	int   tape_num;		// tape number ('\\.\TAPE<n>')
+	int   sub_addr;		// addressing disks within a RAID, for example
+};
+
+// Private table of open devices: guaranteed zero on startup since
+// part of static data.
+static struct spt_dev_info * spt_dev_arr[SPT_MAXDEV];
+
+
+static int spt_open(int pd_num, int tape_num, int sub_addr)
+{
+	int k;
+	struct spt_dev_info * sdip;
+	char b[128];
+	HANDLE h;
+
+	for (k = 0; k < SPT_MAXDEV; k++)
+		if (! spt_dev_arr[k])
+			break;
+
+	// If no free entry found, return error.  We have max allowed number
+	// of "file descriptors" already allocated.
+	if (k == SPT_MAXDEV) {
+		if (con->reportscsiioctl)
+			pout("spt_open: too many open file descriptors (%d)\n",
+			     SPT_MAXDEV);
+		errno = EMFILE;
+		return -1;
+	}
+	sdip = (struct spt_dev_info *)malloc(sizeof(struct spt_dev_info));
+	if (NULL == sdip) {
+		errno = ENOMEM;
+		return -1;
+	}
+	spt_dev_arr[k] = sdip;
+	sdip->pd_num = pd_num;
+	sdip->tape_num = tape_num;
+	sdip->sub_addr = sub_addr;
+
+	b[sizeof(b) - 1] = '\0';
+	if (pd_num >= 0)
+		snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
+	else if (tape_num >= 0)
+		snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
+	else {
+		if (con->reportscsiioctl)
+                        pout("spt_open: bad parameters\n");
+		errno = EINVAL;
+		goto err_out;
+	}
+
+	// Open device
+	if ((h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
+			     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+			     OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+		if (con->reportscsiioctl)
+			pout(" %s: Open failed, Error=%ld\n", b, GetLastError());
+		errno = ENODEV;
+		goto err_out;
+	}
+	sdip->h_spt_ioctl = h;
+	return k + SPT_FDOFFSET;
+                               
+err_out:
+	spt_dev_arr[k] = NULL;
+	free(sdip);
+	return -1;
+}
+
+
+static void spt_close(int fd)
+{
+	struct spt_dev_info * sdip;
+	int index = fd - SPT_FDOFFSET;
+
+	if ((index < 0) || (index >= SPT_MAXDEV)) {
+		if (con->reportscsiioctl)
+			pout("spt_close: bad fd range\n");
+		return;
+	}
+	sdip = spt_dev_arr[index];
+	if (NULL == sdip) {
+		if (con->reportscsiioctl)
+			pout("spt_close: fd already closed\n");
+		return;
+	}
+	free(sdip);
+	spt_dev_arr[index] = NULL;
+}
+
+
+#define IOCTL_SCSI_PASS_THROUGH_DIRECT  \
+	CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _SCSI_PASS_THROUGH_DIRECT {
+	USHORT          Length;
+	UCHAR           ScsiStatus;
+	UCHAR           PathId;
+	UCHAR           TargetId;
+	UCHAR           Lun;
+	UCHAR           CdbLength;
+	UCHAR           SenseInfoLength;
+	UCHAR           DataIn;
+	ULONG           DataTransferLength;
+	ULONG           TimeOutValue;
+	PVOID           DataBuffer;
+	ULONG           SenseInfoOffset;
+	UCHAR           Cdb[16];
+} SCSI_PASS_THROUGH_DIRECT;
+
+typedef struct {
+	SCSI_PASS_THROUGH_DIRECT spt;
+	ULONG           Filler;
+	UCHAR           ucSenseBuf[64];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+
+
+// Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
+static int do_spt_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+	struct spt_dev_info * sdip;
+	int index = fd - SPT_FDOFFSET;
+	SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+	DWORD num_out;
+
+	if ((index < 0) || (index >= SPT_MAXDEV)) {
+		if (report)
+			pout("do_spt_cmnd_io: bad fd range\n");
+		return -EBADF;
+	}
+	sdip = spt_dev_arr[index];
+	if (NULL == sdip) {
+		if (report)
+			pout("do_spt_cmnd_io: fd already closed\n");
+		return -EBADF;
+	}
+
+	if (report > 0) {
+		int k, j;
+		const unsigned char * ucp = iop->cmnd;
+		const char * np;
+		char buff[256];
+		const int sz = (int)sizeof(buff);
+
+		np = scsi_get_opcode_name(ucp[0]);
+		j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+		for (k = 0; k < (int)iop->cmnd_len; ++k)
+			j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+		if ((report > 1) && 
+			(DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+			int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+			j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+						  "data, len=%d%s:\n", (int)iop->dxfer_len,
+						  (trunc ? " [only first 256 bytes shown]" : ""));
+			dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+		}
+		else
+			j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+		pout(buff);
+	}
+	if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+		if (report)
+			pout("do_spt_cmnd_io: cmnd_len too large\n");
+		return -EINVAL;
+	}
+
+	memset(&sb, 0, sizeof(sb));
+	sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+        sb.spt.CdbLength = iop->cmnd_len;
+	memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+	sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+	sb.spt.SenseInfoOffset =
+		offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+	sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+	switch (iop->dxfer_dir) {
+		case DXFER_NONE:
+			sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+			break;
+		case DXFER_FROM_DEVICE:
+			sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+			sb.spt.DataTransferLength = iop->dxfer_len;
+			sb.spt.DataBuffer = iop->dxferp;
+			break;
+		case DXFER_TO_DEVICE:
+			sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+			sb.spt.DataTransferLength = iop->dxfer_len;
+			sb.spt.DataBuffer = iop->dxferp;
+			break;
+		default:
+			pout("do_spt_cmnd_io: bad dxfer_dir\n");
+			return -EINVAL;
+	}
+
+	if (! DeviceIoControl(sdip->h_spt_ioctl, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+		&sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
+		long err = GetLastError();
+
+		if (report)
+			pout("  IOCTL_SCSI_PASS_THROUGH_DIRECT failed, Error=%ld\n", err);
+		return -(err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+	}
+
+	iop->scsi_status = sb.spt.ScsiStatus;
+	if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+		int slen = sb.ucSenseBuf[7] + 8;
+
+		if (slen > (int)sizeof(sb.ucSenseBuf))
+			slen = sizeof(sb.ucSenseBuf);
+		if (slen > (int)iop->max_sense_len)
+			slen = iop->max_sense_len;
+		memcpy(iop->sensep, sb.ucSenseBuf, slen);
+		iop->resp_sense_len = slen;
+		if (report) {
+			if ((iop->sensep[0] & 0x7f) > 0x71)
+				pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+				     iop->scsi_status, iop->sensep[1] & 0xf,
+				     iop->sensep[2], iop->sensep[3]);
+			else
+				pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+				     iop->scsi_status, iop->sensep[2] & 0xf,
+				     iop->sensep[12], iop->sensep[13]);
+		}
+	} else
+		iop->resp_sense_len = 0;
+
+	if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
+		iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+	else
+		iop->resid = 0;
+
+	if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
+		 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+		 pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+			  (trunc ? " [only first 256 bytes shown]" : ""));
+				dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+	}
+	return 0;
+}
+
+
+// Decides which SCSI implementation based on pseudo fd.
+// Declaration and explanation in scsicmds.h
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+	if ((fd & ~0xff) == 0x100)
+		return do_aspi_cmnd_io(fd, iop, report);
+	else
+		return do_spt_cmnd_io(fd, iop, report);
+}