diff --git a/source/StartUpProcess.cpp b/source/StartUpProcess.cpp index 9fdde6f7..26590c2e 100644 --- a/source/StartUpProcess.cpp +++ b/source/StartUpProcess.cpp @@ -19,6 +19,7 @@ #include "settings/CGameCategories.hpp" #include "settings/GameTitles.h" #include "usbloader/usbstorage2.h" +#include "usbloader/usbstorage_libogc.h" #include "usbloader/MountGamePartition.h" #include "usbloader/GameBooter.hpp" #include "usbloader/GameList.h" @@ -366,6 +367,44 @@ int StartUpProcess::Execute(bool quickGameBoot, bool isBadBoot) gprintf("\tLoading game categories...%s\n", GameCategories.Load(Settings.ConfigPath) ? "done" : "failed"); gprintf("\tLoading cached titles...%s\n", GameTitles.ReadCachedTitles(Settings.titlestxt_path) ? "done" : "failed (using default)"); + // Apply volatile ATA power settings while the IOS58/libogc USB backend is + // still mounted. The later cIOS backend does not expose raw SCSI commands. + if (Settings.DisableHDDPowerSaving && USBSuccess) + { + if (IOS_GetVersion() == 58) + { + SetTextf("Disabling HDD power saving\n"); + s32 result = USBStorage_OGC_DisablePowerSaving(); + if (result == USBSTORAGE_POWER_ALL_DISABLED) + { + SetTextf("HDD power saving disabled\n"); + gprintf("Disable HDD power saving: APM and standby timer disabled\n"); + } + else if (result > 0) + { + SetTextf("HDD power saving partially disabled\n"); + gprintf("Disable HDD power saving: APM %s, standby timer %s\n", + (result & USBSTORAGE_POWER_APM_DISABLED) ? "disabled" : "failed", + (result & USBSTORAGE_POWER_STANDBY_DISABLED) ? "disabled" : "failed"); + } + else if (result == 0) + { + SetTextf("HDD power saving not supported\n"); + gprintf("Disable HDD power saving: ATA power commands not supported\n"); + } + else + { + SetTextf("HDD power saving command failed\n"); + gprintf("Disable HDD power saving: transport error (%i)\n", result); + } + } + else + { + SetTextf("HDD power saving requires IOS58\n"); + gprintf("Disable HDD power saving: skipped (requires IOS58 USB backend)\n"); + } + } + // Some settings need to be enabled to boot directly into games gprintf("Quick game boot: %s\n", quickGameBoot ? "yes" : "no"); if (quickGameBoot) diff --git a/source/settings/CSettings.cpp b/source/settings/CSettings.cpp index f1fffef7..d25bd4d3 100644 --- a/source/settings/CSettings.cpp +++ b/source/settings/CSettings.cpp @@ -156,6 +156,7 @@ void CSettings::SetDefault() MultiplePartitions = OFF; BlockIOSReload = AUTO; USBPort = 0; + DisableHDDPowerSaving = OFF; WSFactor = 0.8f; //actually should be 0.75 for real widescreen FontScaleFactor = 0.8f; //it's a work around to not have to change ALL fonts now ClockFontScaleFactor = 1.0f; // Scale of 1 to prevent misaligned clock. @@ -414,6 +415,7 @@ bool CSettings::Save() fprintf(file, "SilentHomeMenu = %d\n", SilentHomeMenu); fprintf(file, "MultiplePartitions = %d\n", MultiplePartitions); fprintf(file, "USBPort = %d\n", USBPort); + fprintf(file, "DisableHDDPowerSaving = %d\n", DisableHDDPowerSaving); fprintf(file, "BlockIOSReload = %d\n", BlockIOSReload); fprintf(file, "WSFactor = %0.3f\n", WSFactor); fprintf(file, "FontScaleFactor = %0.3f\n", FontScaleFactor); @@ -829,6 +831,11 @@ bool CSettings::SetSetting(char *name, char *value) USBPort = atoi(value); return true; } + else if (strcmp(name, "DisableHDDPowerSaving") == 0) + { + DisableHDDPowerSaving = atoi(value); + return true; + } else if (strcmp(name, "patchcountrystrings") == 0) { patchcountrystrings = atoi(value); diff --git a/source/settings/CSettings.h b/source/settings/CSettings.h index 17004b1c..fa06f6d7 100644 --- a/source/settings/CSettings.h +++ b/source/settings/CSettings.h @@ -159,6 +159,7 @@ class CSettings short SilentHomeMenu; short MultiplePartitions; short USBPort; + short DisableHDDPowerSaving; short BlockIOSReload; u32 InstallPartitions; u32 ParentalBlocks; diff --git a/source/settings/menus/HardDriveSM.cpp b/source/settings/menus/HardDriveSM.cpp index 37b5336c..05f83b4a 100644 --- a/source/settings/menus/HardDriveSM.cpp +++ b/source/settings/menus/HardDriveSM.cpp @@ -88,6 +88,7 @@ HardDriveSM::HardDriveSM() if (strncmp(Settings.ConfigPath, "sd", 2) == 0) Options->SetName(Idx++, "%s", tr( "SD Card Mode" )); Options->SetName(Idx++, "%s", tr( "USB Port" )); + Options->SetName(Idx++, "%s", tr( "Disable HDD Power Saving" )); Options->SetName(Idx++, "%s", tr( "Install Directories" )); Options->SetName(Idx++, "%s", tr( "Game Split Size" )); Options->SetName(Idx++, "%s", tr( "Install Partitions" )); @@ -185,6 +186,9 @@ void HardDriveSM::SetOptionValues() else Options->SetValue(Idx++, "%i", NewSettingsUSBPort); + //! Settings: Disable HDD power saving + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.DisableHDDPowerSaving] )); + //! Settings: Install directories Options->SetValue(Idx++, "%s", tr( InstallToText[Settings.InstallToDir] )); @@ -274,6 +278,20 @@ int HardDriveSM::GetMenuInternal() NewSettingsUSBPort = 0; } + //! Settings: Disable HDD power saving + else if (ret == ++Idx) + { + if (++Settings.DisableHDDPowerSaving >= MAX_ON_OFF) + Settings.DisableHDDPowerSaving = 0; + + if (Settings.DisableHDDPowerSaving == ON) + { + WindowPrompt(0, + tr("Applied on the next launch after testing ATA support. This may keep a mechanical HDD spinning, increasing power use, heat, noise, and running time."), + tr("OK")); + } + } + //! Settings: Install directories else if (ret == ++Idx) { diff --git a/source/usbloader/usbstorage_libogc.c b/source/usbloader/usbstorage_libogc.c index 977f99df..0847e457 100644 --- a/source/usbloader/usbstorage_libogc.c +++ b/source/usbloader/usbstorage_libogc.c @@ -234,7 +234,7 @@ static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u __stwbrx(cbw_buffer, 8, len); cbw_buffer[12] = flags; cbw_buffer[13] = lun; - cbw_buffer[14] = (cbLen > 6 ? 10 : 6); + cbw_buffer[14] = (cbLen > 6 ? cbLen : 6); memcpy(cbw_buffer + 15, cb, cbLen); @@ -1013,6 +1013,56 @@ int USBStorage_OGC_ioctl(int request, ...) return retval; } +static s32 __usbstorage_ogc_ata_nodata(u8 ata_command, u8 feature) +{ + raw_device_command rdc; + s32 retval; + + memset(&rdc, 0, sizeof(rdc)); + rdc.command[0] = 0x85; /* ATA PASS-THROUGH (16) */ + rdc.command[1] = 0x06; /* Non-data protocol */ + rdc.command[2] = 0x0c; + rdc.command[4] = feature; + rdc.command[14] = ata_command; + rdc.command_length = sizeof(rdc.command); + rdc.flags = B_RAW_DEVICE_DATA_IN; + + retval = USBStorage_OGC_ioctl(B_RAW_DEVICE_COMMAND, &rdc); + if (retval < 0) + return retval; + + return rdc.scsi_status == 0 ? USBSTORAGE_OK : USBSTORAGE_ESTATUS; +} + +s32 USBStorage_OGC_DisablePowerSaving() +{ + /* CHECK POWER MODE does not change drive state. */ + s32 retval = __usbstorage_ogc_ata_nodata(0xe5, 0x00); + if (retval == USBSTORAGE_ESTATUS) + return 0; + if (retval < 0) + return retval; + + s32 result = 0; + s32 transport_error = 0; + + /* SET FEATURES, DISABLE APM. */ + retval = __usbstorage_ogc_ata_nodata(0xef, 0x85); + if (retval == USBSTORAGE_OK) + result |= USBSTORAGE_POWER_APM_DISABLED; + else if (retval != USBSTORAGE_ESTATUS) + transport_error = retval; + + /* IDLE with sector count zero disables the standby timer. */ + retval = __usbstorage_ogc_ata_nodata(0xe3, 0x00); + if (retval == USBSTORAGE_OK) + result |= USBSTORAGE_POWER_STANDBY_DISABLED; + else if (retval != USBSTORAGE_ESTATUS && transport_error == 0) + transport_error = retval; + + return result != 0 ? result : transport_error; +} + DISC_INTERFACE __io_usbstorage_ogc = { DEVICE_TYPE_WII_USB, FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, diff --git a/source/usbloader/usbstorage_libogc.h b/source/usbloader/usbstorage_libogc.h index a9fa67b1..c0760b50 100644 --- a/source/usbloader/usbstorage_libogc.h +++ b/source/usbloader/usbstorage_libogc.h @@ -28,6 +28,19 @@ s32 USBStorage_OGC_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sector s32 USBStorage_OGC_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer); s32 USBStorage_OGC_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm); +#define USBSTORAGE_POWER_APM_DISABLED (1 << 0) +#define USBSTORAGE_POWER_STANDBY_DISABLED (1 << 1) +#define USBSTORAGE_POWER_ALL_DISABLED (USBSTORAGE_POWER_APM_DISABLED | USBSTORAGE_POWER_STANDBY_DISABLED) + +/** + * Disable ATA Advanced Power Management and the ATA standby timer on the + * currently mounted USB/SAT disk. A non-mutating ATA command is used to check + * pass-through support first. Returns a USBSTORAGE_POWER_* bitmask, zero when + * unsupported, or a negative transport error. The settings are volatile and + * do not write sectors or bridge firmware. + */ +s32 USBStorage_OGC_DisablePowerSaving(); + extern DISC_INTERFACE __io_usbstorage_ogc; #ifdef __cplusplus