mirror of
				https://github.com/pbatard/rufus.git
				synced 2024-08-14 23:57:05 +00: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