From dbb1595aacbc4b95629c33fdbfb9b3a525571b43 Mon Sep 17 00:00:00 2001
From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Wed, 31 Oct 2007 22:08:04 +0000
Subject: [PATCH] Windows: Improved ATA/SCSI device type detection and
 DEVICESCAN

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@2432 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG    |   5 +-
 sm5/os_win32.cpp | 173 ++++++++++++++++++++---------------------------
 2 files changed, 76 insertions(+), 102 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 4a1f0d792..8a71d446c 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.634 2007/10/20 13:02:50 chrfranke Exp $
+$Id: CHANGELOG,v 1.635 2007/10/31 22:08:03 chrfranke Exp $
 
 The most recent version of this file is:
 http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
@@ -33,6 +33,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] Windows: Improved ATA/SCSI device type detection and
+       DEVICESCAN. This also fixes a regression in 3ware DEVICESCAN.
+
   [CF] smartd: Don't start self tests in first pass to avoid
        performance problems during boot.
        https://bugzilla.novell.com/show_bug.cgi?id=192591
diff --git a/sm5/os_win32.cpp b/sm5/os_win32.cpp
index 7c1ab22a4..0b4694d22 100644
--- a/sm5/os_win32.cpp
+++ b/sm5/os_win32.cpp
@@ -44,7 +44,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.58 2007/09/14 20:52:14 chrfranke Exp $"
+const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.59 2007/10/31 22:08:04 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 
@@ -135,7 +135,8 @@ const char * get_os_version_str()
 }
 
 
-static int get_controller_type(int phydrive, int logdrive);
+static int get_phy_drive_type(int drive);
+static int get_log_drive_type(int drive);
 
 #define ATARAID_FDOFFSET 0x0200
 
@@ -213,15 +214,15 @@ int guess_device_type (const char * dev_name)
 		return CONTROLLER_SCSI;
 	int logdrive = drive_letter(dev_name);
 	if (logdrive >= 0) {
-		int type = get_controller_type(-1, logdrive);
+		int type = get_log_drive_type(logdrive);
 		return (type != CONTROLLER_UNKNOWN ? type : CONTROLLER_SCSI);
 	}
 	char drive[1+1] = "";
 	if (sscanf(dev_name, "sd%1[a-z]", drive) == 1)
-		return get_controller_type(drive[0]-'a', -1);
+		return get_phy_drive_type(drive[0]-'a');
 	int phydrive = -1;
 	if (sscanf(dev_name, "pd%d", &phydrive) == 1 && phydrive >= 0)
-		return get_controller_type(phydrive, -1);
+		return get_phy_drive_type(phydrive);
 	return CONTROLLER_UNKNOWN;
 }
 
@@ -609,32 +610,21 @@ static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
 
 // call SMART_GET_VERSION, return device map or -1 on error
 
-static int smart_get_version(HANDLE hdevice, unsigned long * portmap = 0)
+static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
-	GETVERSIONOUTPARAMS vers;
+	GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
 	const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
 	DWORD num_out;
 
-	memset(&vers, 0, sizeof(vers));
 	if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
 		NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
-		pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
+		if (con->reportataioctl)
+			pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
 		errno = ENOSYS;
 		return -1;
 	}
 	assert(num_out == sizeof(GETVERSIONOUTPARAMS));
 
-	if (portmap) {
-		// Return bitmask of valid RAID ports
-		if (vers_ex.wIdentifier != SMART_VENDOR_3WARE) {
-			pout("  SMART_GET_VERSION returns unknown Identifier = %04x\n"
-				 "  This is no 3ware 9000 controller or driver has no SMART support.\n", vers_ex.wIdentifier);
-			errno = ENOENT;
-			return -1;
-		}
-		*portmap = vers_ex.dwDeviceMapEx;
-	}
-
 	if (con->reportataioctl > 1) {
 		pout("  SMART_GET_VERSION suceeded, bytes returned: %lu\n"
 		     "    Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
@@ -645,6 +635,9 @@ static int smart_get_version(HANDLE hdevice, unsigned long * portmap = 0)
 			vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
 	}
 
+	if (ata_version_ex)
+		*ata_version_ex = vers_ex;
+
 	// TODO: Check vers.fCapabilities here?
 	return vers.bIDEDeviceMap;
 }
@@ -1729,13 +1722,23 @@ static STORAGE_BUS_TYPE ioctl_get_storage_bus_type(HANDLE hdevice)
 	return prop.dev.BusType;
 }
 
+
+/////////////////////////////////////////////////////////////////////////////
+
 // get CONTROLLER_* for open handle
-static int get_controller_type(HANDLE hdevice)
+static int get_controller_type(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
+	// Try SMART_GET_VERSION first to detect ATA SMART support
+	// for drivers reporting BusTypeScsi (3ware)
+	if (smart_get_version(hdevice, ata_version_ex) >= 0)
+		return CONTROLLER_ATA;
+
 	STORAGE_BUS_TYPE type = ioctl_get_storage_bus_type(hdevice);
 	switch (type) {
 		case BusTypeAta:
 		case BusTypeSata:
+			if (ata_version_ex)
+				memset(ata_version_ex, 0, sizeof(*ata_version_ex));
 			return CONTROLLER_ATA;
 		case BusTypeScsi:
 		case BusTypeiScsi:
@@ -1748,29 +1751,37 @@ static int get_controller_type(HANDLE hdevice)
 }
 
 // get CONTROLLER_* for device path
-static int get_controller_type(const char * path)
+static int get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
-	HANDLE h = CreateFileA(path, 0/*NO ACCESS*/, FILE_SHARE_READ|FILE_SHARE_WRITE,
-		NULL, OPEN_EXISTING, 0, NULL);
+	HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+		FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 	if (h == INVALID_HANDLE_VALUE)
 		return CONTROLLER_UNKNOWN;
 	if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
 		pout(" %s: successfully opened\n", path);
-	int type = get_controller_type(h);
+	int type = get_controller_type(h, ata_version_ex);
 	CloseHandle(h);
 	return type;
 }
 
-// get CONTROLLER_* for physical or logical drive number
-static int get_controller_type(int phydrive, int logdrive)
+// get CONTROLLER_* for physical drive number
+static int get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
 {
 	char path[30];
-	if (phydrive >= 0)
-		snprintf (path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", phydrive);
-	else if (logdrive >= 0)
-		snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+logdrive);
-	else
-		return CONTROLLER_UNKNOWN;
+	snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
+	return get_controller_type(path, ata_version_ex);
+}
+
+static int get_phy_drive_type(int drive)
+{
+	return get_phy_drive_type(drive, 0);
+}
+
+// get CONTROLLER_* for logical drive number
+static int get_log_drive_type(int drive)
+{
+	char path[30];
+	snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
 	return get_controller_type(path);
 }
 
@@ -1989,9 +2000,23 @@ static int ata_open(int phydrive, int logdrive, const char * options, int port)
 
 	// Win9X/ME: Get drive map
 	// RAID: Get port map
+	GETVERSIONINPARAMS_EX vers_ex;
+	int devmap = smart_get_version(h_ata_ioctl, (port >= 0 ? &vers_ex : 0));
+
 	unsigned long portmap = 0;
-	int devmap = smart_get_version(h_ata_ioctl, (port >= 0 ? &portmap : 0));
+	if (port >= 0 && devmap >= 0) {
+		// 3ware RAID: check vendor id
+		if (vers_ex.wIdentifier != SMART_VENDOR_3WARE) {
+			pout("SMART_GET_VERSION returns unknown Identifier = %04x\n"
+			     "This is no 3ware 9000 controller or driver has no SMART support.\n",
+			     vers_ex.wIdentifier);
+			devmap = -1;
+		}
+		else
+			portmap = vers_ex.dwDeviceMapEx;
+	}
 	if (devmap < 0) {
+		pout("%s: ATA driver has no SMART support\n", devpath);
 		if (!is_permissive()) {
 			ata_close(0);
 			errno = ENOSYS;
@@ -2004,10 +2029,10 @@ static int ata_open(int phydrive, int logdrive, const char * options, int port)
 	if (port >= 0) {
 		// 3ware RAID: update devicemap first
 		if (!update_3ware_devicemap_ioctl(h_ata_ioctl)) {
-			unsigned long portmap1 = 0;
-			if (smart_get_version(h_ata_ioctl, &portmap1) >= 0)
-				portmap = portmap1;
-		}	
+			if (   smart_get_version(h_ata_ioctl, &vers_ex) >= 0
+			    && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
+				portmap = vers_ex.dwDeviceMapEx;
+		}
 		// Check port existence
 		if (!(portmap & (1L << port))) {
 			pout("%s: Port %d is empty or does not exist\n", devpath, port);
@@ -2066,26 +2091,13 @@ static int ata_scan_win9x(unsigned long * drives)
 	}
 
 	// Get drive map
-	GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
-	DWORD num_out;
-	if (!DeviceIoControl(h, SMART_GET_VERSION,
-		NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
-		if (con->reportataioctl)
-			pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
-		CloseHandle(h);
-		return 0; // Should not happen
-	}
+	int devmap = smart_get_version(h);
 	CloseHandle(h);
-
-	if (con->reportataioctl) {
-		pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
-		     "  Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
-			devpath, num_out, vers.bVersion, vers.bRevision,
-			vers.fCapabilities, vers.bIDEDeviceMap);
-	}
+	if (devmap < 0)
+		return 0; // Should not happen
 
 	// Check ATA device presence, remove ATAPI devices
-	drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf);
+	drives[0] = (devmap & 0xf) & ~((devmap >> 4) & 0xf);
 	return (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
 }
 
@@ -2096,53 +2108,12 @@ static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdri
 {
 	int cnt = 0;
 	for (int i = 0; i <= 9; i++) {
-		// Open device
-		char devpath[30];
-		snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i);
-		HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
-			FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
-		if (h == INVALID_HANDLE_VALUE) {
-			if (con->reportataioctl > 1)
-				pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
-			continue;
-		}
-		if (con->reportataioctl)
-			pout(" %s: successfully opened\n", devpath);
-
-		// Skip SCSI
-		int type = get_controller_type(h);
-		if (type == CONTROLLER_SCSI) { // ATA RAID may return CONTROLLER_UNKNOWN
-			CloseHandle(h);
-			continue;
-		}
-
-		// Try SMART_GET_VERSION
-		GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
-		const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
-		DWORD num_out;
-		BOOL smart_ok = DeviceIoControl(h, SMART_GET_VERSION,
-			NULL, 0, &vers, sizeof(vers), &num_out, NULL);
-		if (con->reportataioctl) {
-			if (!smart_ok)
-				pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
-			else {
-				pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
-				     "  Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
-					devpath, num_out, vers.bVersion, vers.bRevision,
-					vers.fCapabilities, vers.bIDEDeviceMap);
-				if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
-					pout("  Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
-						vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
-			}
-		}
-		CloseHandle(h);
-
-		// If SMART_GET_VERSION failed, driver may support ATA_PASS_THROUGH instead
-		if (!(smart_ok || type == CONTROLLER_ATA))
+		GETVERSIONINPARAMS_EX vers_ex;
+		if (get_phy_drive_type(i, &vers_ex) != CONTROLLER_ATA)
 			continue;
 
 		// Interpret RAID drive map if present
-		if (smart_ok && vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
+		if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
 			// Skip if more than 2 controllers or logical drive from this controller already seen
 			if (vers_ex.wControllerId >= 2 || rdriveno[vers_ex.wControllerId] >= 0)
 				continue;
@@ -2324,7 +2295,7 @@ int ata_command_interface(int fd, smart_command_set command, int select, char *
 				if (smart_get_version(h_ata_ioctl) < 0) {
 					if (!con->permissive) {
 						pout("ATA/SATA driver is possibly a SCSI driver not supporting SMART.\n");
-						pout("If this is a SCSI disk, try '/dev/sd%c\'.\n", 'a'+ata_driveno);
+						pout("If this is a SCSI disk, please try adding '-d scsi'.\n");
 						ata_smartver_state[ata_driveno] = 2;
 						rc = -1; errno = ENOSYS;
 						break;
@@ -3096,7 +3067,7 @@ static int spt_scan(unsigned long * drives)
 {
 	int cnt = 0;
 	for (int i = 0; i <= 9; i++) {
-		if (get_controller_type(i, -1) != CONTROLLER_SCSI)
+		if (get_phy_drive_type(i) != CONTROLLER_SCSI)
 			continue;
 		// STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
 		drives[0] |= (1L << i);
-- 
GitLab