diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj
index 5fe26cff..bef861bd 100644
--- a/src/.msvc/rufus.vcxproj
+++ b/src/.msvc/rufus.vcxproj
@@ -189,6 +189,7 @@
+
@@ -210,6 +211,7 @@
+
diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters
index e5321e31..31c35c28 100644
--- a/src/.msvc/rufus.vcxproj.filters
+++ b/src/.msvc/rufus.vcxproj.filters
@@ -63,6 +63,9 @@
Source Files
+
+ Source Files
+
@@ -110,6 +113,9 @@
Header Files
+
+ Header Files
+
diff --git a/src/.msvc/rufus_sources b/src/.msvc/rufus_sources
index 94f66859..38385424 100644
--- a/src/.msvc/rufus_sources
+++ b/src/.msvc/rufus_sources
@@ -43,6 +43,7 @@ SOURCES=rufus.c \
dos_locale.c \
badblocks.c \
drive.c \
+ smart.c \
syslinux.c \
vhd.c \
rufus.rc
diff --git a/src/Makefile.am b/src/Makefile.am
index cc62540b..97426851 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,7 +9,7 @@ pkg_v_rc_0 = @echo " RC $@";
%_rc.o: %.rc ../res/localization/embedded.loc
$(pkg_v_rc)$(WINDRES) $(AM_RCFLAGS) -i $< -o $@
-rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdfn.c stdlg.c rufus.c
+rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
diff --git a/src/Makefile.in b/src/Makefile.in
index 11f235ae..cabe4936 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -48,9 +48,9 @@ am_rufus_OBJECTS = rufus-drive.$(OBJEXT) rufus-icon.$(OBJEXT) \
rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) rufus-dos.$(OBJEXT) \
rufus-dos_locale.$(OBJEXT) rufus-badblocks.$(OBJEXT) \
rufus-syslinux.$(OBJEXT) rufus-vhd.$(OBJEXT) \
- rufus-format.$(OBJEXT) rufus-stdio.$(OBJEXT) \
- rufus-stdfn.$(OBJEXT) rufus-stdlg.$(OBJEXT) \
- rufus-rufus.$(OBJEXT)
+ rufus-format.$(OBJEXT) rufus-smart.$(OBJEXT) \
+ rufus-stdio.$(OBJEXT) rufus-stdfn.$(OBJEXT) \
+ rufus-stdlg.$(OBJEXT) rufus-rufus.$(OBJEXT)
rufus_OBJECTS = $(am_rufus_OBJECTS)
rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \
syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
@@ -186,7 +186,7 @@ SUBDIRS = ms-sys syslinux/libfat syslinux/libinstaller libcdio/iso9660 libcdio/u
pkg_v_rc = $(pkg_v_rc_$(V))
pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY))
pkg_v_rc_0 = @echo " RC $@";
-rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c stdio.c stdfn.c stdlg.c rufus.c
+rufus_SOURCES = drive.c icon.c parser.c localization.c iso.c net.c dos.c dos_locale.c badblocks.c syslinux.c vhd.c format.c smart.c stdio.c stdfn.c stdlg.c rufus.c
rufus_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
@@ -343,6 +343,14 @@ rufus-format.obj: format.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format.obj `if test -f 'format.c'; then $(CYGPATH_W) 'format.c'; else $(CYGPATH_W) '$(srcdir)/format.c'; fi`
+rufus-smart.o: smart.c
+ $(AM_V_CC) @AM_BACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-smart.o `test -f 'smart.c' || echo '$(srcdir)/'`smart.c
+
+rufus-smart.obj: smart.c
+ $(AM_V_CC) @AM_BACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-smart.obj `if test -f 'smart.c'; then $(CYGPATH_W) 'smart.c'; else $(CYGPATH_W) '$(srcdir)/smart.c'; fi`
+
rufus-stdio.o: stdio.c
$(AM_V_CC) @AM_BACKSLASH@
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-stdio.o `test -f 'stdio.c' || echo '$(srcdir)/'`stdio.c
diff --git a/src/drive.c b/src/drive.c
index dd44525e..8967ed42 100644
--- a/src/drive.c
+++ b/src/drive.c
@@ -38,6 +38,9 @@
RUFUS_DRIVE_INFO SelectedDrive;
extern BOOL enable_fixed_disks;
+// TODO: add a DetectSectorSize()?
+// http://msdn.microsoft.com/en-us/library/ff800831.aspx
+
/*
* Working with drive indexes quite risky (left unchecked,inadvertently passing 0 as
* index would return a handle to C:, which we might then proceed to unknowingly
diff --git a/src/rufus.c b/src/rufus.c
index 73cac94e..7b7da40c 100644
--- a/src/rufus.c
+++ b/src/rufus.c
@@ -730,6 +730,8 @@ static BOOL GetUSBDevices(DWORD devnum)
continue;
}
+// Identify(hDrive);
+
if (GetDriveLabel(device_number.DeviceNumber + DRIVE_INDEX_MIN, &drive_letter, &label)) {
// Must ensure that the combo box is UNSORTED for indexes to be the same
StrArrayAdd(&DriveID, buffer);
diff --git a/src/rufus.h b/src/rufus.h
index 8e9f1dca..a9a49600 100644
--- a/src/rufus.h
+++ b/src/rufus.h
@@ -350,6 +350,7 @@ extern char* replace_in_token_data(const char* filename, const char* token, cons
extern void parse_update(char* buf, size_t len);
extern BOOL WimExtractCheck(void);
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst);
+extern BOOL Identify(HANDLE hPhysical);
static __inline BOOL UnlockDrive(HANDLE hDrive)
{
diff --git a/src/rufus.rc b/src/rufus.rc
index 44f6021e..70cc4500 100644
--- a/src/rufus.rc
+++ b/src/rufus.rc
@@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 206, 329
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
-CAPTION "Rufus v1.4.0.314"
+CAPTION "Rufus v1.4.0.315"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Start",IDC_START,94,291,50,14
@@ -289,8 +289,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,4,0,314
- PRODUCTVERSION 1,4,0,314
+ FILEVERSION 1,4,0,315
+ PRODUCTVERSION 1,4,0,315
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -307,13 +307,13 @@ BEGIN
BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus"
- VALUE "FileVersion", "1.4.0.314"
+ VALUE "FileVersion", "1.4.0.315"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2013 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus"
- VALUE "ProductVersion", "1.4.0.314"
+ VALUE "ProductVersion", "1.4.0.315"
END
END
BLOCK "VarFileInfo"
diff --git a/src/smart.c b/src/smart.c
new file mode 100644
index 00000000..7fa7f471
--- /dev/null
+++ b/src/smart.c
@@ -0,0 +1,400 @@
+/*
+ * Rufus: The Reliable USB Formatting Utility
+ * SMART HDD vs Flash detection (using ATA over USB, S.M.A.R.T., etc.)
+ * Copyright © 2013 Pete Batard
+ *
+ * Based in part on scsiata.cpp from Smartmontools: http://smartmontools.sourceforge.net
+ * Copyright © 2006-12 Douglas Gilbert
+ * Copyright © 2009-13 Christian Franke
+ *
+ * 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 3 of the License, 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. If not, see .
+ */
+#ifdef _CRTDBG_MAP_ALLOC
+#include
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include "msapi_utf8.h"
+#include "rufus.h"
+#include "smart.h"
+
+
+/* Helper functions */
+static uint8_t GetAtaDirection(uint8_t AtaCmd, uint8_t Features) {
+ // Far from complete -- only the commands we *may* use.
+
+ // Most SMART commands require DATA_IN but there are a couple exceptions
+ BOOL smart_out = (AtaCmd == ATA_SMART_CMD) &&
+ ((Features == ATA_SMART_STATUS) || (Features == ATA_SMART_WRITE_LOG_SECTOR));
+
+ switch (AtaCmd) {
+ case ATA_IDENTIFY_DEVICE:
+ case ATA_READ_LOG_EXT:
+ return ATA_PASSTHROUGH_DATA_IN;
+ case ATA_SMART_CMD:
+ if (!smart_out)
+ return ATA_PASSTHROUGH_DATA_IN;
+ // fall through
+ case ATA_DATA_SET_MANAGEMENT:
+ return ATA_PASSTHROUGH_DATA_OUT;
+ default:
+ return ATA_PASSTHROUGH_DATA_NONE;
+ }
+}
+
+const char* SptStrerr(int errcode)
+{
+ static char scsi_err[64];
+
+ if ((errcode > 0) && (errcode <= 0xff)) {
+ safe_sprintf(scsi_err, sizeof(scsi_err), "SCSI status: 0x%02X", (uint8_t)errcode);
+ return (const char*)scsi_err;
+ }
+
+ switch(errcode) {
+ case SPT_SUCCESS:
+ return "Success";
+ case SPT_ERROR_CDB_LENGTH:
+ return "Invalid CDB length";
+ case SPT_ERROR_BUFFER:
+ return "Buffer must be aligned to a page boundary and less than 64KB in size";
+ case SPT_ERROR_DIRECTION:
+ return "Invalid Direction";
+ case SPT_ERROR_EXTENDED_CDB:
+ return "Extended and variable length CDB commands are not supported";
+ case SPT_ERROR_CDB_OPCODE:
+ return "Opcodes above 0xC0 are not supported";
+ case SPT_ERROR_TIMEOUT:
+ return "Timeout";
+ case SPT_ERROR_INVALID_PARAMETER:
+ return "Invalid DeviceIoControl parameter";
+ case SPT_ERROR_CHECK_STATUS:
+ return "SCSI error (check Status)";
+ default:
+ return "Unknown error";
+ }
+}
+
+/*
+ * SCSI Passthrough (using IOCTL_SCSI_PASS_THROUGH_DIRECT)
+ * Should be provided a handle to the physical device (R/W) as well as a Cdb and a buffer that is page aligned
+ * Direction should be one of SCSI_IOCTL_DATA_###
+ *
+ * Returns 0 (SPT_SUCCESS) on success, a positive SCSI Status in case of an SCSI error or negative otherwise.
+ */
+
+BOOL ScsiPassthroughDirect(HANDLE hPhysical, uint8_t* Cdb, size_t CdbLen, uint8_t Direction,
+ void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdwb = {{0}, 0, {0}};
+ DWORD err, size = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
+ BOOL r;
+
+ // Sanity checks
+ if ((CdbLen == 0) || (CdbLen > sizeof(sptdwb.sptd.Cdb)))
+ return SPT_ERROR_CDB_LENGTH;
+
+ if (((uintptr_t)DataBuffer % 0x10 != 0) || (BufLen > 0xFFFF))
+ return SPT_ERROR_BUFFER;
+
+ if (Direction > SCSI_IOCTL_DATA_UNSPECIFIED)
+ return SPT_ERROR_DIRECTION;
+
+ // http://en.wikipedia.org/wiki/SCSI_command
+ if ((Cdb[0] == 0x7e) || (Cdb[0] == 0x7f))
+ return SPT_ERROR_EXTENDED_CDB;
+
+ // Opcodes above 0xC0 are unsupported (apart for the special JMicron/Sunplus modes)
+ if ( (Cdb[0] >= 0xc0) && (Cdb[0] != USB_JMICRON_ATA_PASSTHROUGH)
+ && (Cdb[0] != USB_SUNPLUS_ATA_PASSTHROUGH) )
+ return SPT_ERROR_CDB_OPCODE;
+
+ sptdwb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+ sptdwb.sptd.PathId = 0;
+ sptdwb.sptd.TargetId = 0;
+ sptdwb.sptd.Lun = 0;
+ sptdwb.sptd.CdbLength = (uint8_t)CdbLen;
+ sptdwb.sptd.DataIn = Direction; // One of SCSI_IOCTL_DATA_###
+ sptdwb.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
+ sptdwb.sptd.DataTransferLength = (uint16_t)BufLen;
+ sptdwb.sptd.TimeOutValue = Timeout;
+ sptdwb.sptd.DataBuffer = DataBuffer;
+ sptdwb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseBuf);
+
+ memcpy(sptdwb.sptd.Cdb, Cdb, CdbLen);
+
+ r = DeviceIoControl(hPhysical, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptdwb, size, &sptdwb, size, &size, FALSE);
+ if ((r) && (sptdwb.sptd.ScsiStatus == 0)) {
+ return SPT_SUCCESS;
+ }
+
+ if (sptdwb.sptd.ScsiStatus != 0) {
+ // uprintf("ScsiPassthroughDirect: CDB command 0x%02X failed (SCSI status 0x%02X)\n", Cdb[0], sptdwb.sptd.ScsiStatus);
+ return (int)sptdwb.sptd.ScsiStatus;
+ } else {
+ err = GetLastError();
+ // uprintf("ScsiPassthroughDirect: CDB command 0x%02X failed %s\n", Cdb[0], WindowsErrorString()); SetLastError(err);
+ switch(err) {
+ case ERROR_SEM_TIMEOUT:
+ return SPT_ERROR_TIMEOUT;
+ case ERROR_INVALID_PARAMETER:
+ return SPT_ERROR_INVALID_PARAMETER;
+ default:
+ return SPT_ERROR_UNKNOWN_ERROR;
+ }
+ }
+ return FALSE;
+}
+
+
+/* See ftp://ftp.t10.org/t10/document.04/04-262r8.pdf, http://www.scsitoolbox.com/pdfs/UsingSAT.pdf,
+ * as well as http://nevar.pl/pliki/ATA8-ACS-3.pdf‎ */
+static int SatAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ uint8_t Cdb[12] = {0};
+ int extend = 0; /* For 48-bit ATA command (unused here) */
+ int ck_cond = 0; /* Set to 1 to read register(s) back */
+ int protocol = 3; /* Non-data */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 0; /* 0 -> no data transferred */
+ uint8_t Direction;
+
+ if (BufLen % 512 != 0) {
+ uprintf("SatAtaPassthrough: BufLen must be a multiple of \n");
+ return SPT_ERROR_BUFFER;
+ }
+
+ // Set data direction
+ Direction = GetAtaDirection(Command->AtaCmd, Command->Features);
+ if (BufLen != 0) {
+ switch (Direction) {
+ case ATA_PASSTHROUGH_DATA_NONE:
+ break;
+ case ATA_PASSTHROUGH_DATA_IN:
+ protocol = 4; // PIO data-in
+ t_length = 2; // The transfer length is specified in the sector_count field
+ break;
+ case ATA_PASSTHROUGH_DATA_OUT:
+ protocol = 5; // PIO data-out
+ t_length = 2; // The transfer length is specified in the sector_count field
+ t_dir = 0; // to device
+ break;
+ }
+ }
+
+ Cdb[0] = SAT_ATA_PASSTHROUGH_12;
+ Cdb[1] = (protocol << 1) | extend;
+ Cdb[2] = (ck_cond << 5) | (t_dir << 3) | (byte_block << 2) | t_length;
+ Cdb[3] = Command->Features;
+ Cdb[4] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT);
+ Cdb[5] = Command->Lba_low;
+ Cdb[6] = Command->Lba_mid;
+ Cdb[7] = Command->Lba_high;
+ Cdb[8] = Command->Device; // (m_port == 0 ? 0xa0 : 0xb0); // Must be 0 for identify
+ Cdb[9] = Command->AtaCmd;
+
+ return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout);
+}
+
+/* The only differences between JMicron and Prolific are the extra 2 bytes for the CDB */
+static int _UsbJMPLAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command,
+ void* DataBuffer, size_t BufLen, uint32_t Timeout, BOOL prolific)
+{
+ uint8_t Cdb[14] = {0};
+ uint8_t Direction;
+
+ Direction = GetAtaDirection(Command->AtaCmd, Command->Features);
+
+ Cdb[0] = USB_JMICRON_ATA_PASSTHROUGH;
+ Cdb[1] = ((BufLen != 0) && (Direction == ATA_PASSTHROUGH_DATA_OUT))?0x00:0x10;
+ Cdb[3] = (uint8_t)(BufLen >> 8);
+ Cdb[4] = (uint8_t)(BufLen);
+ Cdb[5] = Command->Features;
+ Cdb[6] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT);
+ Cdb[7] = Command->Lba_low;
+ Cdb[8] = Command->Lba_mid;
+ Cdb[9] = Command->Lba_high;
+ Cdb[10] = Command->Device; // (m_port == 0 ? 0xa0 : 0xb0); // Must be 0 for identify
+ Cdb[11] = Command->AtaCmd;
+ // Prolific PL3507
+ Cdb[12] = 0x06;
+ Cdb[13] = 0x7b;
+
+ return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb)-(prolific?2:0), Direction, DataBuffer, BufLen, Timeout);
+}
+
+static int UsbJmicronAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ return _UsbJMPLAtaPassthrough(hPhysical, Command, DataBuffer, BufLen, Timeout, FALSE);
+}
+
+/* UNTESTED!!! */
+static int UsbProlificAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ return _UsbJMPLAtaPassthrough(hPhysical, Command, DataBuffer, BufLen, Timeout, TRUE);
+}
+
+/* UNTESTED!!! */
+static int UsbSunPlusAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ uint8_t Cdb[12] = {0};
+ uint8_t Direction;
+
+ Direction = GetAtaDirection(Command->AtaCmd, Command->Features);
+
+ Cdb[0] = USB_SUNPLUS_ATA_PASSTHROUGH;
+ Cdb[2] = 0x22;
+ if (BufLen != 0) {
+ if (Direction == ATA_PASSTHROUGH_DATA_IN)
+ Cdb[3] = 0x10;
+ else if (Direction == ATA_PASSTHROUGH_DATA_OUT)
+ Cdb[3] = 0x11;
+ }
+ Cdb[4] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT);
+ Cdb[5] = Command->Features;
+ Cdb[6] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT);
+ Cdb[7] = Command->Lba_low;
+ Cdb[8] = Command->Lba_mid;
+ Cdb[9] = Command->Lba_high;
+ Cdb[10] = Command->Device | 0xa0;
+ Cdb[11] = Command->AtaCmd;
+
+ return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout);
+}
+
+/* UNTESTED!!! */
+/* See: http://kernel.opensuse.org/cgit/kernel/tree/drivers/usb/storage/cypress_atacb.c */
+static int UsbCypressAtaPassthrough(HANDLE hPhysical, ATA_PASSTHROUGH_CMD* Command, void* DataBuffer, size_t BufLen, uint32_t Timeout)
+{
+ uint8_t Cdb[16] = {0};
+ uint8_t Direction;
+
+ Direction = GetAtaDirection(Command->AtaCmd, Command->Features);
+
+ Cdb[0] = USB_CYPRESS_ATA_PASSTHROUGH;
+ Cdb[1] = USB_CYPRESS_ATA_PASSTHROUGH;
+ if (Command->AtaCmd == ATA_IDENTIFY_DEVICE || Command->AtaCmd == ATA_IDENTIFY_PACKET_DEVICE)
+ Cdb[2] = (1<<7); // Set IdentifyPacketDevice
+ Cdb[3] = 0xff - (1<<0) - (1<<6); // Features, sector count, lba low, lba med, lba high
+ Cdb[4] = 1; // Units in blocks rather than bytes
+
+ Cdb[6] = Command->Features;
+ Cdb[7] = (uint8_t)(BufLen >> SECTOR_SIZE_SHIFT_BIT);
+ Cdb[8] = Command->Lba_low;
+ Cdb[9] = Command->Lba_mid;
+ Cdb[10] = Command->Lba_high;
+ Cdb[11] = Command->Device;
+ Cdb[12] = Command->AtaCmd;
+
+ return ScsiPassthroughDirect(hPhysical, Cdb, sizeof(Cdb), Direction, DataBuffer, BufLen, Timeout);
+}
+
+/* The various bridges we will try, in order */
+AtaPassThroughType pt[] = {
+ { SatAtaPassthrough, "SAT" },
+ { UsbJmicronAtaPassthrough, "JMicron" },
+ { UsbProlificAtaPassthrough, "Prolific" },
+ { UsbSunPlusAtaPassthrough, "SunPlus" },
+ { UsbCypressAtaPassthrough, "Cypress" },
+};
+
+BOOL Identify(HANDLE hPhysical)
+{
+ ATA_PASSTHROUGH_CMD Command = {0};
+ IDENTIFY_DEVICE_DATA* idd;
+ int i, r;
+
+ Command.AtaCmd = ATA_IDENTIFY_DEVICE;
+
+ // You'll get an error here if your compiler does not properly pack the IDENTIFY struct
+ COMPILE_TIME_ASSERT(sizeof(IDENTIFY_DEVICE_DATA) == 512);
+
+ idd = (IDENTIFY_DEVICE_DATA*)_aligned_malloc(sizeof(IDENTIFY_DEVICE_DATA), 0x10);
+ if (idd == NULL)
+ return FALSE;
+
+ for (i=0; iCommandSetSupport.SmartCommands) {
+ DumpBufferHex(idd, sizeof(IDENTIFY_DEVICE_DATA));
+ uprintf("SMART support detected!\n");
+ } else {
+ uprintf("No SMART support\n");
+ }
+ break;
+ }
+ uprintf("No joy with: %s (%s)\n", pt[i].type, SptStrerr(r));
+ }
+ if (i >= ARRAYSIZE(pt))
+ uprintf("NO ATA FOR YOU!\n");
+
+ _aligned_free(idd);
+ return TRUE;
+}
+
+/* Generic SMART access. Kept for reference, as it doesn't work for USB to ATA/SATA bridges */
+#if 0
+#pragma pack(1)
+typedef struct {
+ UCHAR bVersion;
+ UCHAR bRevision;
+ UCHAR bReserved;
+ UCHAR bIDEDeviceMap;
+ ULONG fCapabilities;
+ ULONG dwReserved[4];
+} MY_GETVERSIONINPARAMS;
+#pragma pack()
+
+#ifndef SMART_GET_VERSION
+#define SMART_GET_VERSION \
+ CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
+#endif
+
+BOOL SmartGetVersion(HANDLE hdevice)
+{
+ MY_GETVERSIONINPARAMS vers;
+ DWORD size = sizeof(MY_GETVERSIONINPARAMS);
+ BOOL r;
+
+ memset(&vers, 0, sizeof(vers));
+
+ r = DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &size, NULL);
+ if ( (!r) || (size != sizeof(MY_GETVERSIONINPARAMS)) ) {
+ uprintf("SmartGetVersion failed: %s\n", r?"unexpected size":WindowsErrorString());
+ return FALSE;
+ }
+ uprintf("Smart Version: %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
+ vers.bVersion, vers.bRevision, (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
+
+ return vers.bIDEDeviceMap;
+}
+#endif
+
+/*
+ * TODO: SMART HDD vs UFD detection:
+ * - if the USB ID starts with
+ * "WDC", "IBM", "ST" + number, "STM", "HTS", "HITACHI", "SEAGATE", "MAXTOR", "SAMSUNG", "HP ", "FUJITSU", "TOSHIBA", "QUANTUM"
+ * - if IDENTIFY reports SMART capabilities
+ * - if it has extra non hidden partitions that aren't Windows
+ * - if the VID:PID (or VID) is of known USB to IDE/SATA bridge or known UFD maker
+ */
diff --git a/src/smart.h b/src/smart.h
new file mode 100644
index 00000000..7b4dec63
--- /dev/null
+++ b/src/smart.h
@@ -0,0 +1,407 @@
+/*
+ * Rufus: The Reliable USB Formatting Utility
+ * SMART HDD vs Flash detection (using ATA over USB, S.M.A.R.T., etc.)
+ * Copyright © 2013 Pete Batard
+ *
+ * Based in part on Smartmontools: http://smartmontools.sourceforge.net
+ * Copyright © 2006-12 Douglas Gilbert
+ * Copyright © 2009-13 Christian Franke
+ *
+ * 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 3 of the License, 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. If not, see .
+ */
+
+#if defined(__MINGW32__)
+#define _aligned_malloc __mingw_aligned_malloc
+#define _aligned_free __mingw_aligned_free
+#endif
+
+// From http://stackoverflow.com/a/9284679
+#define COMPILE_TIME_ASSERT(pred) switch(0) {case 0: case pred:;}
+
+// Official commands
+#define ATA_DATA_SET_MANAGEMENT 0x06 // TRIM command for SSDs
+#define ATA_READ_LOG_EXT 0x2f
+#define ATA_CHECK_POWER_MODE 0xe5
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PACKET_DEVICE 0xa1
+#define ATA_IDLE 0xe3
+#define ATA_SMART_CMD 0xb0
+#define ATA_SECURITY_FREEZE_LOCK 0xf5
+#define ATA_SET_FEATURES 0xef
+#define ATA_STANDBY_IMMEDIATE 0xe0
+#define SAT_ATA_PASSTHROUGH_12 0xa1
+// Non official pseudo commands
+#define USB_CYPRESS_ATA_PASSTHROUGH 0x24
+#define USB_JMICRON_ATA_PASSTHROUGH 0xdf
+#define USB_SUNPLUS_ATA_PASSTHROUGH 0xf8
+
+// SMART ATA Subcommands
+// Also see https://github.com/gregkh/ndas/blob/master/udev.c
+#define ATA_SMART_READ_VALUES 0xd0
+#define ATA_SMART_READ_THRESHOLDS 0xd1
+#define ATA_SMART_AUTOSAVE 0xd2
+#define ATA_SMART_SAVE 0xd3
+#define ATA_SMART_IMMEDIATE_OFFLINE 0xd4
+#define ATA_SMART_READ_LOG_SECTOR 0xd5
+#define ATA_SMART_WRITE_LOG_SECTOR 0xd6
+#define ATA_SMART_WRITE_THRESHOLDS 0xd7
+#define ATA_SMART_ENABLE 0xd8
+#define ATA_SMART_DISABLE 0xd9
+#define ATA_SMART_STATUS 0xda
+
+#define SCSI_IOCTL_DATA_OUT 0
+#define SCSI_IOCTL_DATA_IN 1
+#define SCSI_IOCTL_DATA_UNSPECIFIED 2
+
+#define ATA_PASSTHROUGH_DATA_OUT SCSI_IOCTL_DATA_OUT
+#define ATA_PASSTHROUGH_DATA_IN SCSI_IOCTL_DATA_IN
+#define ATA_PASSTHROUGH_DATA_NONE SCSI_IOCTL_DATA_UNSPECIFIED
+
+// Status codes returned by ScsiPassthroughDirect()
+#define SPT_SUCCESS 0
+#define SPT_ERROR_CDB_LENGTH -1
+#define SPT_ERROR_BUFFER -2
+#define SPT_ERROR_DIRECTION -3
+#define SPT_ERROR_EXTENDED_CDB -4
+#define SPT_ERROR_CDB_OPCODE -5
+#define SPT_ERROR_TIMEOUT -6
+#define SPT_ERROR_INVALID_PARAMETER -7
+#define SPT_ERROR_CHECK_STATUS -8
+#define SPT_ERROR_UNKNOWN_ERROR -99
+
+#define SPT_CDB_LENGTH 16
+#define SPT_SENSE_LENGTH 32
+#define SPT_TIMEOUT_VALUE 2 // In seconds
+#define SECTOR_SIZE_SHIFT_BIT 9 // We use 512 bytes sectors always
+
+#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
+#define IOCTL_SCSI_PASS_THROUGH \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_SCSI_PASS_THROUGH_DIRECT \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct {
+ USHORT Length;
+ UCHAR ScsiStatus;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR CdbLength;
+ UCHAR SenseInfoLength;
+ UCHAR DataIn;
+ ULONG DataTransferLength;
+ ULONG TimeOutValue;
+ ULONG_PTR DataBufferOffset;
+ ULONG SenseInfoOffset;
+ UCHAR Cdb[SPT_CDB_LENGTH];
+} SCSI_PASS_THROUGH;
+
+typedef struct {
+ 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[SPT_CDB_LENGTH];
+} SCSI_PASS_THROUGH_DIRECT;
+
+typedef struct {
+ SCSI_PASS_THROUGH_DIRECT sptd;
+ ULONG Align;
+ UCHAR SenseBuf[SPT_SENSE_LENGTH];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+
+// Custom ATA over USB command
+typedef struct {
+ uint8_t AtaCmd; // eg: ATA_SMART_CMD = 0xb0, IDENTIFY = 0xec, etc.
+ uint8_t Features; // SMART subcommand, eg: SMART_ENABLE_OPS = 0xd8, etc.
+ uint8_t Device; // 0x00 for Identify, 0xA0, 0xB0 for JMicron/SAT SMART ops
+ uint8_t Align;
+ uint8_t Lba_low; // LBA
+ uint8_t Lba_mid;
+ uint8_t Lba_high;
+ uint8_t Lba_unused;
+} ATA_PASSTHROUGH_CMD;
+
+typedef BOOL (*AtaPassthroughFn_t)(
+ HANDLE hPhysical,
+ ATA_PASSTHROUGH_CMD* Command,
+ void* DataBuffer,
+ size_t BufLen,
+ uint32_t Timeout
+);
+
+typedef struct {
+ AtaPassthroughFn_t fn;
+ const char* type;
+} AtaPassThroughType;
+
+// From http://msdn.microsoft.com/en-us/library/windows/hardware/ff559006.aspx
+#pragma pack(1) // Packed as the size must be 512 bytes exactly
+typedef struct _IDENTIFY_DEVICE_DATA {
+ struct {
+ USHORT Reserved1 :1;
+ USHORT Retired3 :1;
+ USHORT ResponseIncomplete :1;
+ USHORT Retired2 :3;
+ USHORT FixedDevice :1;
+ USHORT RemovableMedia :1;
+ USHORT Retired1 :7;
+ USHORT DeviceType :1;
+ } GeneralConfiguration;
+ USHORT NumCylinders;
+ USHORT ReservedWord2;
+ USHORT NumHeads;
+ USHORT Retired1[2];
+ USHORT NumSectorsPerTrack;
+ USHORT VendorUnique1[3];
+ UCHAR SerialNumber[20];
+ USHORT Retired2[2];
+ USHORT Obsolete1;
+ UCHAR FirmwareRevision[8];
+ UCHAR ModelNumber[40];
+ UCHAR MaximumBlockTransfer;
+ UCHAR VendorUnique2;
+ USHORT ReservedWord48;
+ struct {
+ UCHAR ReservedByte49;
+ UCHAR DmaSupported :1;
+ UCHAR LbaSupported :1;
+ UCHAR IordyDisable :1;
+ UCHAR IordySupported :1;
+ UCHAR Reserved1 :1;
+ UCHAR StandybyTimerSupport :1;
+ UCHAR Reserved2 :2;
+ USHORT ReservedWord50;
+ } Capabilities;
+ USHORT ObsoleteWords51[2];
+ USHORT TranslationFieldsValid :3;
+ USHORT Reserved3 :13;
+ USHORT NumberOfCurrentCylinders;
+ USHORT NumberOfCurrentHeads;
+ USHORT CurrentSectorsPerTrack;
+ ULONG CurrentSectorCapacity;
+ UCHAR CurrentMultiSectorSetting;
+ UCHAR MultiSectorSettingValid :1;
+ UCHAR ReservedByte59 :7;
+ ULONG UserAddressableSectors;
+ USHORT ObsoleteWord62;
+ USHORT MultiWordDMASupport :8;
+ USHORT MultiWordDMAActive :8;
+ USHORT AdvancedPIOModes :8;
+ USHORT ReservedByte64 :8;
+ USHORT MinimumMWXferCycleTime;
+ USHORT RecommendedMWXferCycleTime;
+ USHORT MinimumPIOCycleTime;
+ USHORT MinimumPIOCycleTimeIORDY;
+ USHORT ReservedWords69[6];
+ USHORT QueueDepth :5;
+ USHORT ReservedWord75 :11;
+ USHORT ReservedWords76[4];
+ USHORT MajorRevision;
+ USHORT MinorRevision;
+ struct {
+ USHORT SmartCommands :1;
+ USHORT SecurityMode :1;
+ USHORT RemovableMediaFeature :1;
+ USHORT PowerManagement :1;
+ USHORT Reserved1 :1;
+ USHORT WriteCache :1;
+ USHORT LookAhead :1;
+ USHORT ReleaseInterrupt :1;
+ USHORT ServiceInterrupt :1;
+ USHORT DeviceReset :1;
+ USHORT HostProtectedArea :1;
+ USHORT Obsolete1 :1;
+ USHORT WriteBuffer :1;
+ USHORT ReadBuffer :1;
+ USHORT Nop :1;
+ USHORT Obsolete2 :1;
+ USHORT DownloadMicrocode :1;
+ USHORT DmaQueued :1;
+ USHORT Cfa :1;
+ USHORT AdvancedPm :1;
+ USHORT Msn :1;
+ USHORT PowerUpInStandby :1;
+ USHORT ManualPowerUp :1;
+ USHORT Reserved2 :1;
+ USHORT SetMax :1;
+ USHORT Acoustics :1;
+ USHORT BigLba :1;
+ USHORT DeviceConfigOverlay :1;
+ USHORT FlushCache :1;
+ USHORT FlushCacheExt :1;
+ USHORT Resrved3 :2;
+ USHORT SmartErrorLog :1;
+ USHORT SmartSelfTest :1;
+ USHORT MediaSerialNumber :1;
+ USHORT MediaCardPassThrough :1;
+ USHORT StreamingFeature :1;
+ USHORT GpLogging :1;
+ USHORT WriteFua :1;
+ USHORT WriteQueuedFua :1;
+ USHORT WWN64Bit :1;
+ USHORT URGReadStream :1;
+ USHORT URGWriteStream :1;
+ USHORT ReservedForTechReport :2;
+ USHORT IdleWithUnloadFeature :1;
+ USHORT Reserved4 :2;
+ } CommandSetSupport;
+ struct {
+ USHORT SmartCommands :1;
+ USHORT SecurityMode :1;
+ USHORT RemovableMediaFeature :1;
+ USHORT PowerManagement :1;
+ USHORT Reserved1 :1;
+ USHORT WriteCache :1;
+ USHORT LookAhead :1;
+ USHORT ReleaseInterrupt :1;
+ USHORT ServiceInterrupt :1;
+ USHORT DeviceReset :1;
+ USHORT HostProtectedArea :1;
+ USHORT Obsolete1 :1;
+ USHORT WriteBuffer :1;
+ USHORT ReadBuffer :1;
+ USHORT Nop :1;
+ USHORT Obsolete2 :1;
+ USHORT DownloadMicrocode :1;
+ USHORT DmaQueued :1;
+ USHORT Cfa :1;
+ USHORT AdvancedPm :1;
+ USHORT Msn :1;
+ USHORT PowerUpInStandby :1;
+ USHORT ManualPowerUp :1;
+ USHORT Reserved2 :1;
+ USHORT SetMax :1;
+ USHORT Acoustics :1;
+ USHORT BigLba :1;
+ USHORT DeviceConfigOverlay :1;
+ USHORT FlushCache :1;
+ USHORT FlushCacheExt :1;
+ USHORT Resrved3 :2;
+ USHORT SmartErrorLog :1;
+ USHORT SmartSelfTest :1;
+ USHORT MediaSerialNumber :1;
+ USHORT MediaCardPassThrough :1;
+ USHORT StreamingFeature :1;
+ USHORT GpLogging :1;
+ USHORT WriteFua :1;
+ USHORT WriteQueuedFua :1;
+ USHORT WWN64Bit :1;
+ USHORT URGReadStream :1;
+ USHORT URGWriteStream :1;
+ USHORT ReservedForTechReport :2;
+ USHORT IdleWithUnloadFeature :1;
+ USHORT Reserved4 :2;
+ } CommandSetActive;
+ USHORT UltraDMASupport :8;
+ USHORT UltraDMAActive :8;
+ USHORT ReservedWord89[4];
+ USHORT HardwareResetResult;
+ USHORT CurrentAcousticValue :8;
+ USHORT RecommendedAcousticValue :8;
+ USHORT ReservedWord95[5];
+ ULONG Max48BitLBA[2];
+ USHORT StreamingTransferTime;
+ USHORT ReservedWord105;
+ struct {
+ USHORT LogicalSectorsPerPhysicalSector :4;
+ USHORT Reserved0 :8;
+ USHORT LogicalSectorLongerThan256Words :1;
+ USHORT MultipleLogicalSectorsPerPhysicalSector :1;
+ USHORT Reserved1 :2;
+ } PhysicalLogicalSectorSize;
+ USHORT InterSeekDelay;
+ USHORT WorldWideName[4];
+ USHORT ReservedForWorldWideName128[4];
+ USHORT ReservedForTlcTechnicalReport;
+ USHORT WordsPerLogicalSector[2];
+ struct {
+ USHORT ReservedForDrqTechnicalReport :1;
+ USHORT WriteReadVerifySupported :1;
+ USHORT Reserved01 :11;
+ USHORT Reserved1 :2;
+ } CommandSetSupportExt;
+ struct {
+ USHORT ReservedForDrqTechnicalReport :1;
+ USHORT WriteReadVerifyEnabled :1;
+ USHORT Reserved01 :11;
+ USHORT Reserved1 :2;
+ } CommandSetActiveExt;
+ USHORT ReservedForExpandedSupportandActive[6];
+ USHORT MsnSupport :2;
+ USHORT ReservedWord1274 :14;
+ struct {
+ USHORT SecuritySupported :1;
+ USHORT SecurityEnabled :1;
+ USHORT SecurityLocked :1;
+ USHORT SecurityFrozen :1;
+ USHORT SecurityCountExpired :1;
+ USHORT EnhancedSecurityEraseSupported :1;
+ USHORT Reserved0 :2;
+ USHORT SecurityLevel :1;
+ USHORT Reserved1 :7;
+ } SecurityStatus;
+ USHORT ReservedWord129[31];
+ struct {
+ USHORT MaximumCurrentInMA2 :12;
+ USHORT CfaPowerMode1Disabled :1;
+ USHORT CfaPowerMode1Required :1;
+ USHORT Reserved0 :1;
+ USHORT Word160Supported :1;
+ } CfaPowerModel;
+ USHORT ReservedForCfaWord161[8];
+ struct {
+ USHORT SupportsTrim :1;
+ USHORT Reserved0 :15;
+ } DataSetManagementFeature;
+ USHORT ReservedForCfaWord170[6];
+ USHORT CurrentMediaSerialNumber[30];
+ USHORT ReservedWord206;
+ USHORT ReservedWord207[2];
+ struct {
+ USHORT AlignmentOfLogicalWithinPhysical :14;
+ USHORT Word209Supported :1;
+ USHORT Reserved0 :1;
+ } BlockAlignment;
+ USHORT WriteReadVerifySectorCountMode3Only[2];
+ USHORT WriteReadVerifySectorCountMode2Only[2];
+ struct {
+ USHORT NVCachePowerModeEnabled :1;
+ USHORT Reserved0 :3;
+ USHORT NVCacheFeatureSetEnabled :1;
+ USHORT Reserved1 :3;
+ USHORT NVCachePowerModeVersion :4;
+ USHORT NVCacheFeatureSetVersion :4;
+ } NVCacheCapabilities;
+ USHORT NVCacheSizeLSW;
+ USHORT NVCacheSizeMSW;
+ USHORT NominalMediaRotationRate;
+ USHORT ReservedWord218;
+ struct {
+ UCHAR NVCacheEstimatedTimeToSpinUpInSeconds;
+ UCHAR Reserved;
+ } NVCacheOptions;
+ USHORT ReservedWord220[35];
+ USHORT Signature :8;
+ USHORT CheckSum :8;
+} IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA;
+#pragma pack()
diff --git a/src/stdio.c b/src/stdio.c
index 2c32f3a4..80b3ed3c 100644
--- a/src/stdio.c
+++ b/src/stdio.c
@@ -129,6 +129,8 @@ static char err_string[256] = {0};
else
safe_sprintf(err_string, sizeof(err_string), "Unknown error 0x%08X", error_code);
}
+
+ SetLastError(error_code); // Make sure we don't change the errorcode on exit
return err_string;
}