mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-19 17:35:10 -04:00
[core] SMART/ATA over USB initial support
* This is meant to be used as part of #219 * Also improve WindowsErrorString() so that it doesn't alter the current errcode
This commit is contained in:
parent
8d50a8491f
commit
803a4bff1c
12 changed files with 842 additions and 10 deletions
|
@ -189,6 +189,7 @@
|
||||||
<ClCompile Include="..\net.c" />
|
<ClCompile Include="..\net.c" />
|
||||||
<ClCompile Include="..\parser.c" />
|
<ClCompile Include="..\parser.c" />
|
||||||
<ClCompile Include="..\rufus.c" />
|
<ClCompile Include="..\rufus.c" />
|
||||||
|
<ClCompile Include="..\smart.c" />
|
||||||
<ClCompile Include="..\stdfn.c" />
|
<ClCompile Include="..\stdfn.c" />
|
||||||
<ClCompile Include="..\stdio.c" />
|
<ClCompile Include="..\stdio.c" />
|
||||||
<ClCompile Include="..\stdlg.c" />
|
<ClCompile Include="..\stdlg.c" />
|
||||||
|
@ -210,6 +211,7 @@
|
||||||
<ClInclude Include="..\resource.h" />
|
<ClInclude Include="..\resource.h" />
|
||||||
<ClInclude Include="..\rufus.h" />
|
<ClInclude Include="..\rufus.h" />
|
||||||
<ClInclude Include="..\license.h" />
|
<ClInclude Include="..\license.h" />
|
||||||
|
<ClInclude Include="..\smart.h" />
|
||||||
<ClInclude Include="..\sys_types.h" />
|
<ClInclude Include="..\sys_types.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -63,6 +63,9 @@
|
||||||
<ClCompile Include="..\localization.c">
|
<ClCompile Include="..\localization.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\smart.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\rufus.h">
|
<ClInclude Include="..\rufus.h">
|
||||||
|
@ -110,6 +113,9 @@
|
||||||
<ClInclude Include="..\localization_data.h">
|
<ClInclude Include="..\localization_data.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\smart.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\res\rufus.ico">
|
<None Include="..\..\res\rufus.ico">
|
||||||
|
|
|
@ -43,6 +43,7 @@ SOURCES=rufus.c \
|
||||||
dos_locale.c \
|
dos_locale.c \
|
||||||
badblocks.c \
|
badblocks.c \
|
||||||
drive.c \
|
drive.c \
|
||||||
|
smart.c \
|
||||||
syslinux.c \
|
syslinux.c \
|
||||||
vhd.c \
|
vhd.c \
|
||||||
rufus.rc
|
rufus.rc
|
||||||
|
|
|
@ -9,7 +9,7 @@ pkg_v_rc_0 = @echo " RC $@";
|
||||||
%_rc.o: %.rc ../res/localization/embedded.loc
|
%_rc.o: %.rc ../res/localization/embedded.loc
|
||||||
$(pkg_v_rc)$(WINDRES) $(AM_RCFLAGS) -i $< -o $@
|
$(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_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
|
||||||
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
|
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
|
||||||
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
|
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
|
||||||
|
|
|
@ -48,9 +48,9 @@ am_rufus_OBJECTS = rufus-drive.$(OBJEXT) rufus-icon.$(OBJEXT) \
|
||||||
rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) rufus-dos.$(OBJEXT) \
|
rufus-iso.$(OBJEXT) rufus-net.$(OBJEXT) rufus-dos.$(OBJEXT) \
|
||||||
rufus-dos_locale.$(OBJEXT) rufus-badblocks.$(OBJEXT) \
|
rufus-dos_locale.$(OBJEXT) rufus-badblocks.$(OBJEXT) \
|
||||||
rufus-syslinux.$(OBJEXT) rufus-vhd.$(OBJEXT) \
|
rufus-syslinux.$(OBJEXT) rufus-vhd.$(OBJEXT) \
|
||||||
rufus-format.$(OBJEXT) rufus-stdio.$(OBJEXT) \
|
rufus-format.$(OBJEXT) rufus-smart.$(OBJEXT) \
|
||||||
rufus-stdfn.$(OBJEXT) rufus-stdlg.$(OBJEXT) \
|
rufus-stdio.$(OBJEXT) rufus-stdfn.$(OBJEXT) \
|
||||||
rufus-rufus.$(OBJEXT)
|
rufus-stdlg.$(OBJEXT) rufus-rufus.$(OBJEXT)
|
||||||
rufus_OBJECTS = $(am_rufus_OBJECTS)
|
rufus_OBJECTS = $(am_rufus_OBJECTS)
|
||||||
rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \
|
rufus_DEPENDENCIES = rufus_rc.o ms-sys/libmssys.a \
|
||||||
syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.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_$(V))
|
||||||
pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY))
|
pkg_v_rc_ = $(pkg_v_rc_$(AM_DEFAULT_VERBOSITY))
|
||||||
pkg_v_rc_0 = @echo " RC $@";
|
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_CFLAGS = -I./ms-sys/inc -I./syslinux/libfat -I./syslinux/libinstaller -I./libcdio $(AM_CFLAGS)
|
||||||
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
|
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
|
||||||
rufus_LDADD = rufus_rc.o ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a \
|
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@
|
$(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`
|
$(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
|
rufus-stdio.o: stdio.c
|
||||||
$(AM_V_CC) @AM_BACKSLASH@
|
$(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
|
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-stdio.o `test -f 'stdio.c' || echo '$(srcdir)/'`stdio.c
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
RUFUS_DRIVE_INFO SelectedDrive;
|
RUFUS_DRIVE_INFO SelectedDrive;
|
||||||
extern BOOL enable_fixed_disks;
|
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
|
* 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
|
* index would return a handle to C:, which we might then proceed to unknowingly
|
||||||
|
|
|
@ -730,6 +730,8 @@ static BOOL GetUSBDevices(DWORD devnum)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Identify(hDrive);
|
||||||
|
|
||||||
if (GetDriveLabel(device_number.DeviceNumber + DRIVE_INDEX_MIN, &drive_letter, &label)) {
|
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
|
// Must ensure that the combo box is UNSORTED for indexes to be the same
|
||||||
StrArrayAdd(&DriveID, buffer);
|
StrArrayAdd(&DriveID, buffer);
|
||||||
|
|
|
@ -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 void parse_update(char* buf, size_t len);
|
||||||
extern BOOL WimExtractCheck(void);
|
extern BOOL WimExtractCheck(void);
|
||||||
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst);
|
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)
|
static __inline BOOL UnlockDrive(HANDLE hDrive)
|
||||||
{
|
{
|
||||||
|
|
10
src/rufus.rc
10
src/rufus.rc
|
@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||||
IDD_DIALOG DIALOGEX 12, 12, 206, 329
|
IDD_DIALOG DIALOGEX 12, 12, 206, 329
|
||||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
EXSTYLE WS_EX_APPWINDOW
|
EXSTYLE WS_EX_APPWINDOW
|
||||||
CAPTION "Rufus v1.4.0.314"
|
CAPTION "Rufus v1.4.0.315"
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||||
BEGIN
|
BEGIN
|
||||||
DEFPUSHBUTTON "Start",IDC_START,94,291,50,14
|
DEFPUSHBUTTON "Start",IDC_START,94,291,50,14
|
||||||
|
@ -289,8 +289,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,4,0,314
|
FILEVERSION 1,4,0,315
|
||||||
PRODUCTVERSION 1,4,0,314
|
PRODUCTVERSION 1,4,0,315
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -307,13 +307,13 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
|
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
|
||||||
VALUE "FileDescription", "Rufus"
|
VALUE "FileDescription", "Rufus"
|
||||||
VALUE "FileVersion", "1.4.0.314"
|
VALUE "FileVersion", "1.4.0.315"
|
||||||
VALUE "InternalName", "Rufus"
|
VALUE "InternalName", "Rufus"
|
||||||
VALUE "LegalCopyright", "© 2011-2013 Pete Batard (GPL v3)"
|
VALUE "LegalCopyright", "© 2011-2013 Pete Batard (GPL v3)"
|
||||||
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
|
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
|
||||||
VALUE "OriginalFilename", "rufus.exe"
|
VALUE "OriginalFilename", "rufus.exe"
|
||||||
VALUE "ProductName", "Rufus"
|
VALUE "ProductName", "Rufus"
|
||||||
VALUE "ProductVersion", "1.4.0.314"
|
VALUE "ProductVersion", "1.4.0.315"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
400
src/smart.c
Normal file
400
src/smart.c
Normal file
|
@ -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 <pete@akeo.ie>
|
||||||
|
*
|
||||||
|
* Based in part on scsiata.cpp from Smartmontools: http://smartmontools.sourceforge.net
|
||||||
|
* Copyright © 2006-12 Douglas Gilbert <dgilbert@interlog.com>
|
||||||
|
* Copyright © 2009-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef _CRTDBG_MAP_ALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <crtdbg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#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 <block size>\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; i<ARRAYSIZE(pt); i++) {
|
||||||
|
r = pt[i].fn(hPhysical, &Command, idd, sizeof(IDENTIFY_DEVICE_DATA), SPT_TIMEOUT_VALUE);
|
||||||
|
if (r == SPT_SUCCESS) {
|
||||||
|
uprintf("Success using %s\n", pt[i].type);
|
||||||
|
if (idd->CommandSetSupport.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
|
||||||
|
*/
|
407
src/smart.h
Normal file
407
src/smart.h
Normal file
|
@ -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 <pete@akeo.ie>
|
||||||
|
*
|
||||||
|
* Based in part on Smartmontools: http://smartmontools.sourceforge.net
|
||||||
|
* Copyright © 2006-12 Douglas Gilbert <dgilbert@interlog.com>
|
||||||
|
* Copyright © 2009-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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()
|
|
@ -129,6 +129,8 @@ static char err_string[256] = {0};
|
||||||
else
|
else
|
||||||
safe_sprintf(err_string, sizeof(err_string), "Unknown error 0x%08X", error_code);
|
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;
|
return err_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue