From 6cd6b35c737e0e4042a8fd79af1decc9f10ed84b Mon Sep 17 00:00:00 2001 From: Benjamin Dobell Date: Mon, 1 Oct 2012 12:43:05 +1000 Subject: Heimdall 1.4 RC1: - Massive refactoring. - Support for Qualcomm based devices. - Print PIT from file. - Use partition names as arguments e.g. --HIDDEN, --KERNEL, --MOVINAND etc. - Heimdall Frontend UI improvements. - And much more... --- heimdall/source/Arguments.cpp | 212 +++++++ heimdall/source/Arguments.h | 160 +++++ heimdall/source/BeginSessionPacket.h | 39 ++ heimdall/source/BridgeManager.cpp | 857 +++++++++++++++----------- heimdall/source/BridgeManager.h | 37 +- heimdall/source/ClosePcScreenAction.cpp | 94 +++ heimdall/source/ClosePcScreenAction.h | 34 + heimdall/source/ControlPacket.h | 8 +- heimdall/source/DetectAction.cpp | 64 ++ heimdall/source/DetectAction.h | 34 + heimdall/source/DeviceTypePacket.h | 39 ++ heimdall/source/DownloadPitAction.cpp | 135 ++++ heimdall/source/DownloadPitAction.h | 34 + heimdall/source/DumpAction.cpp | 158 +++++ heimdall/source/DumpAction.h | 34 + heimdall/source/FilePartSizePacket.h | 56 ++ heimdall/source/FlashAction.cpp | 620 +++++++++++++++++++ heimdall/source/FlashAction.h | 34 + heimdall/source/FlashPartFileTransferPacket.h | 20 +- heimdall/source/Heimdall.h | 1 + heimdall/source/HelpAction.cpp | 35 ++ heimdall/source/HelpAction.h | 34 + heimdall/source/InfoAction.cpp | 35 ++ heimdall/source/InfoAction.h | 34 + heimdall/source/Interface.cpp | 484 +++++---------- heimdall/source/Interface.h | 281 +-------- heimdall/source/Packet.h | 2 +- heimdall/source/PrintPitAction.cpp | 159 +++++ heimdall/source/PrintPitAction.h | 34 + heimdall/source/ResponsePacket.h | 10 +- heimdall/source/SendFilePartPacket.h | 7 +- heimdall/source/SessionSetupPacket.h | 74 +++ heimdall/source/SessionSetupResponse.h | 58 ++ heimdall/source/TotalBytesPacket.h | 56 ++ heimdall/source/Utility.cpp | 82 +++ heimdall/source/Utility.h | 40 ++ heimdall/source/VersionAction.cpp | 35 ++ heimdall/source/VersionAction.h | 34 + heimdall/source/main.cpp | 778 +---------------------- 39 files changed, 3212 insertions(+), 1730 deletions(-) create mode 100644 heimdall/source/Arguments.cpp create mode 100644 heimdall/source/Arguments.h create mode 100644 heimdall/source/BeginSessionPacket.h create mode 100644 heimdall/source/ClosePcScreenAction.cpp create mode 100644 heimdall/source/ClosePcScreenAction.h create mode 100644 heimdall/source/DetectAction.cpp create mode 100644 heimdall/source/DetectAction.h create mode 100644 heimdall/source/DeviceTypePacket.h create mode 100644 heimdall/source/DownloadPitAction.cpp create mode 100644 heimdall/source/DownloadPitAction.h create mode 100644 heimdall/source/DumpAction.cpp create mode 100644 heimdall/source/DumpAction.h create mode 100644 heimdall/source/FilePartSizePacket.h create mode 100644 heimdall/source/FlashAction.cpp create mode 100644 heimdall/source/FlashAction.h create mode 100644 heimdall/source/HelpAction.cpp create mode 100644 heimdall/source/HelpAction.h create mode 100644 heimdall/source/InfoAction.cpp create mode 100644 heimdall/source/InfoAction.h create mode 100644 heimdall/source/PrintPitAction.cpp create mode 100644 heimdall/source/PrintPitAction.h create mode 100644 heimdall/source/SessionSetupPacket.h create mode 100644 heimdall/source/SessionSetupResponse.h create mode 100644 heimdall/source/TotalBytesPacket.h create mode 100644 heimdall/source/Utility.cpp create mode 100644 heimdall/source/Utility.h create mode 100644 heimdall/source/VersionAction.cpp create mode 100644 heimdall/source/VersionAction.h (limited to 'heimdall/source') diff --git a/heimdall/source/Arguments.cpp b/heimdall/source/Arguments.cpp new file mode 100644 index 0000000..321c12f --- /dev/null +++ b/heimdall/source/Arguments.cpp @@ -0,0 +1,212 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Arguments.h" +#include "Heimdall.h" +#include "Interface.h" +#include "Utility.h" + +using namespace Heimdall; + +FlagArgument *FlagArgument::ParseArgument(int argc, char **argv, int& argi) +{ + return new FlagArgument(); +} + + + +StringArgument *StringArgument::ParseArgument(int argc, char **argv, int& argi) +{ + if (++argi < argc) + { + return (new StringArgument(argv[argi])); + } + else + { + Interface::Print("Missing parameter for argument: %s\n\n", argv[argi - 1]); + return (nullptr); + } +} + + + +UnsignedIntegerArgument *UnsignedIntegerArgument::ParseArgument(int argc, char **argv, int& argi) +{ + UnsignedIntegerArgument *unsignedIntegerArgument = nullptr; + + if (++argi < argc) + { + unsigned int value; + + if (Utility::ParseUnsignedInt(value, argv[argi]) == kNumberParsingStatusSuccess) + unsignedIntegerArgument = new UnsignedIntegerArgument(value); + else + Interface::Print("%s must be a positive integer.", argv[argi - 1]); + } + else + { + Interface::Print("Missing parameter for argument: %s\n\n", argv[argi - 1]); + } + + return (unsignedIntegerArgument); +} + + + +Arguments::Arguments(const map& argumentTypes, const map& shortArgumentAliases, + const map argumentAliases) : + argumentTypes(argumentTypes), + shortArgumentAliases(shortArgumentAliases), + argumentAliases(argumentAliases) +{ +} + +Arguments::~Arguments() +{ + for (map::const_iterator it = arguments.begin(); it != arguments.end(); it++) + delete it->second; +} + +bool Arguments::ParseArguments(int argc, char **argv, int argi) +{ + for (; argi < argc; ++argi) + { + string argumentName = argv[argi]; + string nonwildcardArgumentName; + + if (argumentName.find_first_of("--") == 0) + { + // Regular argument + argumentName = argumentName.substr(2); + nonwildcardArgumentName = argumentName; + } + else if (argumentName.find_first_of("-") == 0) + { + // Short argument alias + string shortArgumentAlias = argumentName.substr(1); + map::const_iterator shortAliasIt = shortArgumentAliases.find(shortArgumentAlias); + + if (shortAliasIt != shortArgumentAliases.end()) + { + argumentName = shortAliasIt->second; + nonwildcardArgumentName = argumentName; + } + else + { + Interface::Print("Unknown argument: %s\n\n", argv[argi]); + return (false); + } + } + else + { + Interface::Print("Invalid argument: %s\n\n", argv[argi]); + return (false); + } + + map::const_iterator argumentTypeIt = argumentTypes.find(argumentName); + + if (argumentTypeIt == argumentTypes.end()) + { + // No argument with that name, maybe it's an alias... + map::const_iterator aliasIt = argumentAliases.find(argumentName); + + if (aliasIt != argumentAliases.end()) + { + argumentName = aliasIt->second; + nonwildcardArgumentName = argumentName; + + argumentTypeIt = argumentTypes.find(argumentName); + } + } + + // Handle wilcards + + unsigned int unsignedIntName; + + if (argumentTypeIt == argumentTypes.end()) + { + // Look for the unsigned integer wildcard "%d". + if (Utility::ParseUnsignedInt(unsignedIntName, argumentName.c_str()) == kNumberParsingStatusSuccess) + { + argumentTypeIt = argumentTypes.find("%d"); + argumentName = "%d"; + } + + // Look for the string wildcard "%s" + if (argumentTypeIt == argumentTypes.end()) + { + argumentTypeIt = argumentTypes.find("%s"); + argumentName = "%s"; + } + } + + Argument *argument = nullptr; + + if (argumentTypeIt != argumentTypes.end()) + { + switch (argumentTypeIt->second) + { + case kArgumentTypeFlag: + argument = FlagArgument::ParseArgument(argc, argv, argi); + break; + + case kArgumentTypeString: + argument = StringArgument::ParseArgument(argc, argv, argi); + break; + + case kArgumentTypeUnsignedInteger: + argument = UnsignedIntegerArgument::ParseArgument(argc, argv, argi); + break; + + default: + Interface::Print("Unknown argument type: %s\n\n", argv[argi]); + break; + } + } + else + { + Interface::Print("Unknown argument: %s\n\n", argv[argi]); + } + + // We don't want to insert wild-cards into our argument map. + if (argumentName == "%d" || argumentName == "%s") + argumentName = nonwildcardArgumentName; + + if (argument) + { + pair::iterator, bool> insertResult = arguments.insert(pair(argumentName, argument)); + + if (!insertResult.second) + { + Interface::Print("Duplicate argument: %s (%s)\n\n", argv[argi], insertResult.first->first.c_str()); + delete argument; + + return (false); + } + } + else + { + return (false); + } + } + + return (true); +} diff --git a/heimdall/source/Arguments.h b/heimdall/source/Arguments.h new file mode 100644 index 0000000..7294e70 --- /dev/null +++ b/heimdall/source/Arguments.h @@ -0,0 +1,160 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef ARGUMENTS_H +#define ARGUMENTS_H + +// C/C++ Standard Library +#include +#include + +// Heimdall +#include "Heimdall.h" + +using namespace std; + +namespace Heimdall +{ + typedef enum + { + kArgumentTypeFlag = 0, + kArgumentTypeString, + kArgumentTypeUnsignedInteger + + } ArgumentType; + + class Argument + { + private: + + ArgumentType argumentType; + + protected: + + Argument(ArgumentType argumentType) + { + this->argumentType = argumentType; + } + + public: + + virtual ~Argument() + { + } + + ArgumentType GetArgumentType(void) const + { + return argumentType; + } + }; + + class FlagArgument : public Argument + { + private: + + FlagArgument() : Argument(kArgumentTypeFlag) + { + } + + public: + + static FlagArgument *ParseArgument(int argc, char **argv, int& argi); + }; + + class StringArgument : public Argument + { + private: + + string value; + + StringArgument(const string& value) : Argument(kArgumentTypeString) + { + this->value = value; + } + + public: + + static StringArgument *ParseArgument(int argc, char **argv, int& argi); + + const string& GetValue(void) const + { + return (value); + } + }; + + class UnsignedIntegerArgument : public Argument + { + private: + + unsigned int value; + + UnsignedIntegerArgument(unsigned int value) : Argument(kArgumentTypeUnsignedInteger) + { + this->value = value; + } + + public: + + static UnsignedIntegerArgument *ParseArgument(int argc, char **argv, int& argi); + + unsigned int GetValue(void) const + { + return (value); + } + }; + + class Arguments + { + private: + + const map argumentTypes; + const map shortArgumentAliases; + const map argumentAliases; + + map arguments; + + public: + + Arguments(const map& argumentTypes, const map& shortArgumentAliases = map(), + const map argumentAliases = map()); + ~Arguments(); + + // argi is the index of the first argument to parse. + bool ParseArguments(int argc, char **argv, int argi); + + const Argument *GetArgument(string argumentName) const + { + map::const_iterator it = arguments.find(argumentName); + return (it != arguments.end() ? it->second : nullptr); + } + + const map& GetArgumentTypes(void) const + { + return (argumentTypes); + } + + const map& GetArguments(void) const + { + return (arguments); + } + }; +} + +#endif diff --git a/heimdall/source/BeginSessionPacket.h b/heimdall/source/BeginSessionPacket.h new file mode 100644 index 0000000..d0f9c05 --- /dev/null +++ b/heimdall/source/BeginSessionPacket.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef BEGINSESSIONPACKET_H +#define BEGINSESSIONPACKET_H + +// Heimdall +#include "SessionSetupPacket.h" + +namespace Heimdall +{ + class BeginSessionPacket : public SessionSetupPacket + { + public: + + BeginSessionPacket() : SessionSetupPacket(SessionSetupPacket::kBeginSession) + { + } + }; +} + +#endif diff --git a/heimdall/source/BridgeManager.cpp b/heimdall/source/BridgeManager.cpp index 0019021..fa83b1c 100644 --- a/heimdall/source/BridgeManager.cpp +++ b/heimdall/source/BridgeManager.cpp @@ -26,9 +26,9 @@ // Heimdall #include "BeginDumpPacket.h" +#include "BeginSessionPacket.h" #include "BridgeManager.h" -#include "SetupSessionPacket.h" -#include "SetupSessionResponse.h" +#include "DeviceTypePacket.h" #include "DumpPartFileTransferPacket.h" #include "DumpPartPitFilePacket.h" #include "DumpResponse.h" @@ -36,6 +36,7 @@ #include "EndPhoneFileTransferPacket.h" #include "EndPitFileTransferPacket.h" #include "EndSessionPacket.h" +#include "FilePartSizePacket.h" #include "FileTransferPacket.h" #include "FlashPartFileTransferPacket.h" #include "FlashPartPitFilePacket.h" @@ -48,11 +49,13 @@ #include "ResponsePacket.h" #include "SendFilePartPacket.h" #include "SendFilePartResponse.h" +#include "SessionSetupPacket.h" +#include "SessionSetupResponse.h" // Future versions of libusb will use usb_interface instead of interface. #define usb_interface interface -#define CLASS_CDC 0x0A +#define USB_CLASS_CDC_DATA 0x0A using namespace Heimdall; @@ -64,30 +67,264 @@ const DeviceIdentifier BridgeManager::supportedDevices[BridgeManager::kSupported enum { - kMaxSequenceLength = 800 + kDumpBufferSize = 4096 }; +enum +{ + kFileTransferSequenceMaxLengthDefault = 800, + kFileTransferPacketSizeDefault = 131072, + kFileTransferSequenceTimeoutDefault = 30000 // 30 seconds +}; + +enum +{ + kHandshakeMaxAttempts = 5, + kReceivePacketMaxAttempts = 5 +}; + +int BridgeManager::FindDeviceInterface(void) +{ + Interface::Print("Detecting device...\n"); + + struct libusb_device **devices; + int deviceCount = libusb_get_device_list(libusbContext, &devices); + + for (int deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { + libusb_device_descriptor descriptor; + libusb_get_device_descriptor(devices[deviceIndex], &descriptor); + + for (int i = 0; i < BridgeManager::kSupportedDeviceCount; i++) + { + if (descriptor.idVendor == supportedDevices[i].vendorId && descriptor.idProduct == supportedDevices[i].productId) + { + heimdallDevice = devices[deviceIndex]; + libusb_ref_device(heimdallDevice); + break; + } + } + + if (heimdallDevice) + break; + } + + libusb_free_device_list(devices, deviceCount); + + if (!heimdallDevice) + { + Interface::PrintDeviceDetectionFailed(); + return (BridgeManager::kInitialiseDeviceNotDetected); + } + + int result = libusb_open(heimdallDevice, &deviceHandle); + if (result != LIBUSB_SUCCESS) + { + Interface::PrintError("Failed to access device. libusb error: %d\n", result); + return (BridgeManager::kInitialiseFailed); + } + + libusb_device_descriptor deviceDescriptor; + result = libusb_get_device_descriptor(heimdallDevice, &deviceDescriptor); + if (result != LIBUSB_SUCCESS) + { + Interface::PrintError("Failed to retrieve device description\n"); + return (BridgeManager::kInitialiseFailed); + } + + if (verbose) + { + unsigned char stringBuffer[128]; + + if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iManufacturer, + stringBuffer, 128) >= 0) + { + Interface::Print(" Manufacturer: \"%s\"\n", stringBuffer); + } + + if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iProduct, + stringBuffer, 128) >= 0) + { + Interface::Print(" Product: \"%s\"\n", stringBuffer); + } + + if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iSerialNumber, + stringBuffer, 128) >= 0) + { + Interface::Print(" Serial No: \"%s\"\n", stringBuffer); + } + + Interface::Print("\n length: %d\n", deviceDescriptor.bLength); + Interface::Print(" device class: %d\n", deviceDescriptor.bDeviceClass); + Interface::Print(" S/N: %d\n", deviceDescriptor.iSerialNumber); + Interface::Print(" VID:PID: %04X:%04X\n", deviceDescriptor.idVendor, deviceDescriptor.idProduct); + Interface::Print(" bcdDevice: %04X\n", deviceDescriptor.bcdDevice); + Interface::Print(" iMan:iProd:iSer: %d:%d:%d\n", deviceDescriptor.iManufacturer, deviceDescriptor.iProduct, + deviceDescriptor.iSerialNumber); + Interface::Print(" nb confs: %d\n", deviceDescriptor.bNumConfigurations); + } + + libusb_config_descriptor *configDescriptor; + result = libusb_get_config_descriptor(heimdallDevice, 0, &configDescriptor); + + if (result != LIBUSB_SUCCESS || !configDescriptor) + { + Interface::PrintError("Failed to retrieve config descriptor\n"); + return (BridgeManager::kInitialiseFailed); + } + + interfaceIndex = -1; + altSettingIndex = -1; + + for (int i = 0; i < configDescriptor->bNumInterfaces; i++) + { + for (int j = 0 ; j < configDescriptor->usb_interface[i].num_altsetting; j++) + { + if (verbose) + { + Interface::Print("\ninterface[%d].altsetting[%d]: num endpoints = %d\n", + i, j, configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints); + Interface::Print(" Class.SubClass.Protocol: %02X.%02X.%02X\n", + configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass, + configDescriptor->usb_interface[i].altsetting[j].bInterfaceSubClass, + configDescriptor->usb_interface[i].altsetting[j].bInterfaceProtocol); + } + + int inEndpointAddress = -1; + int outEndpointAddress = -1; + + for (int k = 0; k < configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints; k++) + { + const libusb_endpoint_descriptor *endpoint = &configDescriptor->usb_interface[i].altsetting[j].endpoint[k]; + + if (verbose) + { + Interface::Print(" endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress); + Interface::Print(" max packet size: %04X\n", endpoint->wMaxPacketSize); + Interface::Print(" polling interval: %02X\n", endpoint->bInterval); + } + + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) + inEndpointAddress = endpoint->bEndpointAddress; + else + outEndpointAddress = endpoint->bEndpointAddress; + } + + if (interfaceIndex < 0 + && configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints == 2 + && configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass == USB_CLASS_CDC_DATA + && inEndpointAddress != -1 + && outEndpointAddress != -1) + { + interfaceIndex = i; + altSettingIndex = j; + inEndpoint = inEndpointAddress; + outEndpoint = outEndpointAddress; + } + } + } + + libusb_free_config_descriptor(configDescriptor); + + if (result != LIBUSB_SUCCESS) + { + Interface::PrintError("Failed to find correct interface configuration\n"); + return (BridgeManager::kInitialiseFailed); + } + + return (BridgeManager::kInitialiseSucceeded); +} + +bool BridgeManager::ClaimDeviceInterface(void) +{ + Interface::Print("Claiming interface...\n"); + + int result = libusb_claim_interface(deviceHandle, interfaceIndex); + +#ifdef OS_LINUX + + if (result != LIBUSB_SUCCESS) + { + detachedDriver = true; + Interface::Print("Attempt failed. Detaching driver...\n"); + libusb_detach_kernel_driver(deviceHandle, interfaceIndex); + Interface::Print("Claiming interface again...\n"); + result = libusb_claim_interface(deviceHandle, interfaceIndex); + } + +#endif + + if (result != LIBUSB_SUCCESS) + { + Interface::PrintError("Claiming interface failed!\n"); + return (false); + } + + interfaceClaimed = true; + + return (true); +} + +bool BridgeManager::SetupDeviceInterface(void) +{ + Interface::Print("Setting up interface...\n"); + + int result = libusb_set_interface_alt_setting(deviceHandle, interfaceIndex, altSettingIndex); + + if (result != LIBUSB_SUCCESS) + { + Interface::PrintError("Setting up interface failed!\n"); + return (false); + } + + Interface::Print("\n"); + return (true); +} + +void BridgeManager::ReleaseDeviceInterface(void) +{ + Interface::Print("Releasing device interface...\n"); + + libusb_release_interface(deviceHandle, interfaceIndex); + +#ifdef OS_LINUX + + if (detachedDriver) + { + Interface::Print("Re-attaching kernel driver...\n"); + libusb_attach_kernel_driver(deviceHandle, interfaceIndex); + } + +#endif + + interfaceClaimed = false; + Interface::Print("\n"); +} + bool BridgeManager::CheckProtocol(void) const { Interface::Print("Checking if protocol is initialised...\n"); - SetupSessionPacket deviceInfoPacket(SetupSessionPacket::kDeviceInfo); + DeviceTypePacket deviceTypePacket; - if (!SendPacket(&deviceInfoPacket, 100, false)) + if (!SendPacket(&deviceTypePacket, 150, false)) { Interface::Print("Protocol is not initialised.\n"); return (false); } - SetupSessionResponse deviceInfoResponse; + unsigned char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); - if (!ReceivePacket(&deviceInfoResponse, 100, false)) + SessionSetupResponse deviceTypeResponse; + + if (!ReceivePacket(&deviceTypeResponse, 150, false, buffer, sizeof(buffer))) { - Interface::Print("Protocol is not initialised.\n"); + Interface::Print("Protocol is not initialised.\n\n"); return (false); } - Interface::Print("Protocol is initialised.\n"); + Interface::Print("Protocol is initialised.\n\n"); return (true); } @@ -95,17 +332,12 @@ bool BridgeManager::InitialiseProtocol(void) const { Interface::Print("Initialising protocol...\n"); - unsigned char *dataBuffer = new unsigned char[7]; + unsigned char dataBuffer[7]; int result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x22, 0x3, 0, nullptr, 0, 1000); - if (result < 0) - { - Interface::PrintError("Failed to initialise protocol!\n"); - - delete [] dataBuffer; - return (false); - } + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #1 failed. Result: %d\n", result); memset(dataBuffer, 0, 7); dataBuffer[1] = 0xC2; @@ -113,31 +345,19 @@ bool BridgeManager::InitialiseProtocol(void) const dataBuffer[6] = 0x07; result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x20, 0x0, 0, dataBuffer, 7, 1000); - if (result < 0) - { - Interface::PrintError("Failed to initialise protocol!\n"); - delete [] dataBuffer; - return (false); - } + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #2 failed. Result: %d\n", result); result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x22, 0x3, 0, nullptr, 0, 1000); - if (result < 0) - { - Interface::PrintError("Failed to initialise protocol!\n"); - delete [] dataBuffer; - return (false); - } + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #3 failed. Result: %d\n", result); result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x22, 0x2, 0, nullptr, 0, 1000); - if (result < 0) - { - Interface::PrintError("Failed to initialise protocol!\n"); - delete [] dataBuffer; - return (false); - } + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #4 failed. Result: %d\n", result); memset(dataBuffer, 0, 7); dataBuffer[1] = 0xC2; @@ -145,79 +365,101 @@ bool BridgeManager::InitialiseProtocol(void) const dataBuffer[6] = 0x08; result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x20, 0x0, 0, dataBuffer, 7, 1000); - if (result < 0) - { - Interface::PrintError("Failed to initialise protocol!\n"); - delete [] dataBuffer; - return (false); - } + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #5 failed. Result: %d\n", result); result = libusb_control_transfer(deviceHandle, LIBUSB_REQUEST_TYPE_CLASS, 0x22, 0x2, 0, nullptr, 0, 1000); - if (result < 0) + + if (result < 0 && verbose) + Interface::PrintWarning("Control transfer #6 failed. Result: %d\n", result); + + unsigned int attempt = 0; + + // max(250, communicationDelay) + int retryDelay = (communicationDelay > 250) ? communicationDelay : 250; + + for (; attempt < kHandshakeMaxAttempts; attempt++) { - Interface::PrintError("Failed to initialise protocol!\n"); + if (attempt > 0) + { + if (verbose) + Interface::PrintErrorSameLine(" Retrying...\n"); + + // Wait longer each retry + Sleep(retryDelay * (attempt + 1)); + } - delete [] dataBuffer; - return (false); - } + int dataTransferred = 0; - Interface::Print("Handshaking with Loke...\n"); + // Send "ODIN" + memcpy(dataBuffer, "ODIN", 4); + memset(dataBuffer + 4, 0, 1); - int dataTransferred; + result = libusb_bulk_transfer(deviceHandle, outEndpoint, dataBuffer, 4, &dataTransferred, 1000); + if (result < 0) + { + if (verbose) + Interface::PrintError("Failed to send data: \"%s\"\n", dataBuffer); + else + Interface::PrintError("Failed to send data!"); - // Send "ODIN" - strcpy((char *)dataBuffer, "ODIN"); + return (false); + } - result = libusb_bulk_transfer(deviceHandle, outEndpoint, dataBuffer, 4, &dataTransferred, 1000); - if (result < 0) - { - if (verbose) - Interface::PrintError("Failed to send data: \"%s\"\n", dataBuffer); - else - Interface::PrintError("Failed to send data!"); + if (dataTransferred != 4) + { + if (verbose) + Interface::PrintError("Failed to complete sending of data: \"%s\"\n", dataBuffer); + else + Interface::PrintError("Failed to complete sending of data!"); - delete [] dataBuffer; - return (false); - } + return (false); + } - if (dataTransferred != 4) - { - if (verbose) - Interface::PrintError("Failed to complete sending of data: \"%s\"\n", dataBuffer); - else - Interface::PrintError("Failed to complete sending of data!"); + // Expect "LOKE" + memset(dataBuffer, 0, 7); - delete [] dataBuffer; - return (false); - } + int retry = 0; + dataTransferred = 0; - // Expect "LOKE" - memset(dataBuffer, 0, 7); + result = libusb_bulk_transfer(deviceHandle, inEndpoint, dataBuffer, 7, &dataTransferred, 1000); - result = libusb_bulk_transfer(deviceHandle, inEndpoint, dataBuffer, 7, &dataTransferred, 1000); - if (result < 0) - { - Interface::PrintError("Failed to receive response!\n"); + if (result < 0) + { + if (verbose) + Interface::PrintError("Failed to receive handshake response."); + } + else + { + if (dataTransferred == 4 && memcmp(dataBuffer, "LOKE", 4) == 0) + { + // Successfully received "LOKE" + break; + } + else + { + if (verbose) + Interface::PrintError("Expected: \"%s\"\nReceived: \"%s\"\n", "LOKE", dataBuffer); - delete [] dataBuffer; - return (false);; + Interface::PrintError("Unexpected handshake response!"); + } + } } - if (dataTransferred != 4 || memcmp(dataBuffer, "LOKE", 4) != 0) + if (attempt == kHandshakeMaxAttempts) { - Interface::PrintError("Unexpected communication!\n"); - if (verbose) - Interface::PrintError("Expected: \"%s\"\nReceived: \"%s\"\n", "LOKE", dataBuffer); - - Interface::PrintError("Handshake failed!\n"); + Interface::PrintErrorSameLine("\n"); - delete [] dataBuffer; + Interface::PrintError("Protocol initialisation failed!\n\n"); return (false); } - - return (true); + else + { + Interface::Print("Protocol initialisation successful.\n\n"); + return (true); + } } BridgeManager::BridgeManager(bool verbose, int communicationDelay) @@ -228,32 +470,30 @@ BridgeManager::BridgeManager(bool verbose, int communicationDelay) libusbContext = nullptr; deviceHandle = nullptr; heimdallDevice = nullptr; + inEndpoint = -1; outEndpoint = -1; interfaceIndex = -1; + altSettingIndex = -1; -#ifdef OS_LINUX - - detachedDriver = false; - -#endif -} - -BridgeManager::~BridgeManager() -{ - if (interfaceIndex >= 0) - libusb_release_interface(deviceHandle, interfaceIndex); + interfaceClaimed = false; #ifdef OS_LINUX - if (detachedDriver) - { - Interface::Print("Re-attaching kernel driver...\n"); - libusb_attach_kernel_driver(deviceHandle, interfaceIndex); - } + detachedDriver = false; #endif + fileTransferSequenceMaxLength = kFileTransferSequenceMaxLengthDefault; + fileTransferPacketSize = kFileTransferPacketSizeDefault; + fileTransferSequenceTimeout = kFileTransferSequenceTimeoutDefault; +} + +BridgeManager::~BridgeManager() +{ + if (interfaceClaimed) + ReleaseDeviceInterface(); + if (deviceHandle) libusb_close(deviceHandle); @@ -301,197 +541,30 @@ bool BridgeManager::DetectDevice(void) return (false); } -int BridgeManager::Initialise(void) +int BridgeManager::Initialise() { Interface::Print("Initialising connection...\n"); // Initialise libusb-1.0 int result = libusb_init(&libusbContext); + if (result != LIBUSB_SUCCESS) { Interface::PrintError("Failed to initialise libusb. libusb error: %d\n", result); Interface::Print("Failed to connect to device!"); return (BridgeManager::kInitialiseFailed); } - - Interface::Print("Detecting device...\n"); - - // Get handle to Galaxy S device - struct libusb_device **devices; - int deviceCount = libusb_get_device_list(libusbContext, &devices); - - for (int deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) - { - libusb_device_descriptor descriptor; - libusb_get_device_descriptor(devices[deviceIndex], &descriptor); - - for (int i = 0; i < BridgeManager::kSupportedDeviceCount; i++) - { - if (descriptor.idVendor == supportedDevices[i].vendorId && descriptor.idProduct == supportedDevices[i].productId) - { - heimdallDevice = devices[deviceIndex]; - libusb_ref_device(heimdallDevice); - break; - } - } - - if (heimdallDevice) - break; - } - - libusb_free_device_list(devices, deviceCount); - - if (!heimdallDevice) - { - Interface::PrintDeviceDetectionFailed(); - return (BridgeManager::kInitialiseDeviceNotDetected); - } - - result = libusb_open(heimdallDevice, &deviceHandle); - if (result != LIBUSB_SUCCESS) - { - Interface::PrintError("Failed to access device. libusb error: %d\n", result); - return (BridgeManager::kInitialiseFailed); - } - - libusb_device_descriptor deviceDescriptor; - result = libusb_get_device_descriptor(heimdallDevice, &deviceDescriptor); - if (result != LIBUSB_SUCCESS) - { - Interface::PrintError("Failed to retrieve device description\n"); - return (BridgeManager::kInitialiseFailed); - } - - if (verbose) - { - unsigned char stringBuffer[128]; - - if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iManufacturer, - stringBuffer, 128) >= 0) - { - Interface::Print(" Manufacturer: \"%s\"\n", stringBuffer); - } - - if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iProduct, - stringBuffer, 128) >= 0) - { - Interface::Print(" Product: \"%s\"\n", stringBuffer); - } - - if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iSerialNumber, - stringBuffer, 128) >= 0) - { - Interface::Print(" Serial No: \"%s\"\n", stringBuffer); - } - - Interface::Print("\n length: %d\n", deviceDescriptor.bLength); - Interface::Print(" device class: %d\n", deviceDescriptor.bDeviceClass); - Interface::Print(" S/N: %d\n", deviceDescriptor.iSerialNumber); - Interface::Print(" VID:PID: %04X:%04X\n", deviceDescriptor.idVendor, deviceDescriptor.idProduct); - Interface::Print(" bcdDevice: %04X\n", deviceDescriptor.bcdDevice); - Interface::Print(" iMan:iProd:iSer: %d:%d:%d\n", deviceDescriptor.iManufacturer, deviceDescriptor.iProduct, - deviceDescriptor.iSerialNumber); - Interface::Print(" nb confs: %d\n", deviceDescriptor.bNumConfigurations); - } - - libusb_config_descriptor *configDescriptor; - result = libusb_get_config_descriptor(heimdallDevice, 0, &configDescriptor); - - if (result != LIBUSB_SUCCESS || !configDescriptor) - { - Interface::PrintError("Failed to retrieve config descriptor\n"); - return (BridgeManager::kInitialiseFailed); - } - - int interfaceIndex = -1; - int altSettingIndex; - - for (int i = 0; i < configDescriptor->bNumInterfaces; i++) - { - for (int j = 0 ; j < configDescriptor->usb_interface[i].num_altsetting; j++) - { - if (verbose) - { - Interface::Print("\ninterface[%d].altsetting[%d]: num endpoints = %d\n", - i, j, configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints); - Interface::Print(" Class.SubClass.Protocol: %02X.%02X.%02X\n", - configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass, - configDescriptor->usb_interface[i].altsetting[j].bInterfaceSubClass, - configDescriptor->usb_interface[i].altsetting[j].bInterfaceProtocol); - } - - int inEndpointAddress = -1; - int outEndpointAddress = -1; - - for (int k = 0; k < configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints; k++) - { - const libusb_endpoint_descriptor *endpoint = &configDescriptor->usb_interface[i].altsetting[j].endpoint[k]; - - if (verbose) - { - Interface::Print(" endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress); - Interface::Print(" max packet size: %04X\n", endpoint->wMaxPacketSize); - Interface::Print(" polling interval: %02X\n", endpoint->bInterval); - } - - if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) - inEndpointAddress = endpoint->bEndpointAddress; - else - outEndpointAddress = endpoint->bEndpointAddress; - } - - if (interfaceIndex < 0 && configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints == 2 - && configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass == CLASS_CDC - && inEndpointAddress != -1 && outEndpointAddress != -1) - { - interfaceIndex = i; - altSettingIndex = j; - inEndpoint = inEndpointAddress; - outEndpoint = outEndpointAddress; - } - } - } - - libusb_free_config_descriptor(configDescriptor); - - if (result != LIBUSB_SUCCESS) - { - Interface::PrintError("Failed to find correct interface configuration\n"); - return (BridgeManager::kInitialiseFailed); - } - Interface::Print("Claiming interface...\n"); - result = libusb_claim_interface(deviceHandle, interfaceIndex); - -#ifdef OS_LINUX - - if (result != LIBUSB_SUCCESS) - { - detachedDriver = true; - Interface::Print("Attempt failed. Detaching driver...\n"); - libusb_detach_kernel_driver(deviceHandle, interfaceIndex); - Interface::Print("Claiming interface again...\n"); - result = libusb_claim_interface(deviceHandle, interfaceIndex); - } + result = FindDeviceInterface(); -#endif + if (result != BridgeManager::kInitialiseSucceeded) + return (result); - if (result != LIBUSB_SUCCESS) - { - Interface::PrintError("Claiming interface failed!\n"); + if (!ClaimDeviceInterface()) return (BridgeManager::kInitialiseFailed); - } - - Interface::Print("Setting up interface...\n"); - result = libusb_set_interface_alt_setting(deviceHandle, interfaceIndex, altSettingIndex); - if (result != LIBUSB_SUCCESS) - { - Interface::PrintError("Setting up interface failed!\n"); + if (!SetupDeviceInterface()) return (BridgeManager::kInitialiseFailed); - } - - Interface::Print("\n"); if (!CheckProtocol()) { @@ -499,16 +572,14 @@ int BridgeManager::Initialise(void) return (BridgeManager::kInitialiseFailed); } - Interface::Print("\n"); - return (BridgeManager::kInitialiseSucceeded); } -bool BridgeManager::BeginSession(void) const +bool BridgeManager::BeginSession(void) { Interface::Print("Beginning session...\n"); - SetupSessionPacket beginSessionPacket(SetupSessionPacket::kBeginSession); + BeginSessionPacket beginSessionPacket; if (!SendPacket(&beginSessionPacket)) { @@ -516,22 +587,18 @@ bool BridgeManager::BeginSession(void) const return (false); } - SetupSessionResponse setupSessionResponse; - if (!ReceivePacket(&setupSessionResponse)) + SessionSetupResponse beginSessionResponse; + if (!ReceivePacket(&beginSessionResponse)) return (false); - unsigned int result = setupSessionResponse.GetUnknown(); + unsigned int result = beginSessionResponse.GetResult(); - // 131072 for Galaxy S II, 0 for other devices. - if (result != 0 && result != 131072) - { - Interface::PrintError("Unexpected device info response!\nExpected: 0\nReceived:%d\n", result); - return (false); - } + if (result != 0) // Assume 0 means don't care, otherwise use the response as the fileTransferPacketSize. + fileTransferPacketSize = result; // -------------------- KIES DOESN'T DO THIS -------------------- - SetupSessionPacket deviceTypePacket(SetupSessionPacket::kDeviceInfo); + DeviceTypePacket deviceTypePacket; if (!SendPacket(&deviceTypePacket)) { @@ -539,23 +606,67 @@ bool BridgeManager::BeginSession(void) const return (false); } - if (!ReceivePacket(&setupSessionResponse)) + SessionSetupResponse deviceTypeResponse; + + if (!ReceivePacket(&deviceTypeResponse)) return (false); - unsigned int deviceType = setupSessionResponse.GetUnknown(); + unsigned int deviceType = deviceTypeResponse.GetResult(); - // TODO: Work out what this value is... it has been either 180 or 0 for Galaxy S phones, 3 on the Galaxy Tab, 190 for SHW-M110S. - if (deviceType != 180 && deviceType != 0 && deviceType != 3 && deviceType != 190) - { - Interface::PrintError("Unexpected device info response!\nExpected: 180, 0 or 3\nReceived:%d\n", deviceType); - return (false); - } - else + switch (deviceType) { - Interface::Print("Session begun with device of type: %d\n\n", result); - } + // NOTE: If you add a new device type don't forget to update the error message below! - return (true); + case 0: // Galaxy S etc. + case 3: // Galaxy Tab + case 30: // Galaxy S 2 Skyrocket + case 180: // Galaxy S etc. + case 190: // M110S Galaxy S + + if (verbose) + Interface::Print("Session begun with device of type: %d.\n\n", deviceType); + else + Interface::Print("Session begun.\n\n", deviceType); + + if (deviceType == 30) + { + Interface::Print("In certain situations this device may take up to 2 minutes to respond.\nPlease be patient!\n\n", deviceType); + Sleep(2000); // Give the user time to read the message. + + // The SGH-I727 is very unstable/unreliable using the default settings. Flashing + // seems to be much more reliable using the following setup. + + fileTransferSequenceMaxLength = 30; + fileTransferSequenceTimeout = 120000; // 2 minutes! + fileTransferPacketSize = 1048576; // 1 MiB + + FilePartSizePacket filePartSizePacket(fileTransferPacketSize); // 1 MiB + + if (!SendPacket(&filePartSizePacket)) + { + Interface::PrintError("Failed to send file part size packet!\n"); + return (false); + } + + SessionSetupResponse filePartSizeResponse; + + if (!ReceivePacket(&filePartSizeResponse)) + return (false); + + if (filePartSizeResponse.GetResult() != 0) + { + Interface::PrintError("Unexpected file part size response!\nExpected: 0\nReceived: %d\n", filePartSizeResponse.GetResult()); + return (false); + } + } + + return (true); + + default: + + Interface::PrintError("Unexpected device info response!\nExpected: 0, 3, 30, 180 or 190\nReceived:%d\n", deviceType); + return (false); + } } bool BridgeManager::EndSession(bool reboot) const @@ -662,58 +773,79 @@ bool BridgeManager::SendPacket(OutboundPacket *packet, int timeout, bool retry) return (true); } -bool BridgeManager::ReceivePacket(InboundPacket *packet, int timeout, bool retry) const +bool BridgeManager::ReceivePacket(InboundPacket *packet, int timeout, bool retry, unsigned char *buffer, unsigned int bufferSize) const { - int dataTransferred; - int result = libusb_bulk_transfer(deviceHandle, inEndpoint, packet->GetData(), packet->GetSize(), - &dataTransferred, timeout); + bool bufferProvided = buffer != nullptr && bufferSize >= packet->GetSize(); - if (result < 0 && retry) + if (!bufferProvided) { - // max(250, communicationDelay) - int retryDelay = (communicationDelay > 250) ? communicationDelay : 250; + buffer = packet->GetData(); + bufferSize = packet->GetSize(); + } - if (verbose) - Interface::PrintError("libusb error %d whilst receiving packet.", result); + int dataTransferred; + int result; - // Retry - for (int i = 0; i < 5; i++) + unsigned int attempt = 0; + unsigned int maxAttempts = (retry) ? kReceivePacketMaxAttempts : 1; + + // max(250, communicationDelay) + int retryDelay = (communicationDelay > 250) ? communicationDelay : 250; + + for (; attempt < maxAttempts; attempt++) + { + if (attempt > 0) { if (verbose) Interface::PrintErrorSameLine(" Retrying...\n"); - + // Wait longer each retry - Sleep(retryDelay * (i + 1)); - - result = libusb_bulk_transfer(deviceHandle, inEndpoint, packet->GetData(), packet->GetSize(), - &dataTransferred, timeout); + Sleep(retryDelay * (attempt + 1)); + } - if (result >= 0) - break; + result = libusb_bulk_transfer(deviceHandle, inEndpoint, buffer, bufferSize, &dataTransferred, timeout); - if (verbose) - Interface::PrintError("libusb error %d whilst receiving packet.", result); - } + if (result >= 0) + break; if (verbose) - Interface::PrintErrorSameLine("\n"); + Interface::PrintError("libusb error %d whilst receiving packet.", result); } + if (verbose && attempt > 0) + Interface::PrintErrorSameLine("\n"); + + if (attempt == maxAttempts) + return (false); + if (communicationDelay != 0) Sleep(communicationDelay); - if (result < 0 || (dataTransferred != packet->GetSize() && !packet->IsSizeVariable())) + if (dataTransferred != packet->GetSize() && !packet->IsSizeVariable()) + { + if (verbose) + Interface::PrintError("Incorrect packet size received - expected size = %d, received size = %d.\n", packet->GetSize(), dataTransferred); + return (false); + } + + if (bufferProvided) + memcpy(packet->GetData(), buffer, dataTransferred); packet->SetReceivedSize(dataTransferred); - return (packet->Unpack()); + bool unpacked = packet->Unpack(); + + if (!unpacked && verbose) + Interface::PrintError("Failed to unpack received packet.\n"); + + return (unpacked); } -bool BridgeManager::RequestDeviceInfo(unsigned int request, int *result) const +bool BridgeManager::RequestDeviceType(unsigned int request, int *result) const { - SetupSessionPacket beginSessionPacket(request); - bool success = SendPacket(&beginSessionPacket); + DeviceTypePacket deviceTypePacket; + bool success = SendPacket(&deviceTypePacket); if (!success) { @@ -725,11 +857,12 @@ bool BridgeManager::RequestDeviceInfo(unsigned int request, int *result) const return (false); } - SetupSessionResponse deviceInfoResponse; - if (!ReceivePacket(&deviceInfoResponse)) + SessionSetupResponse deviceTypeResponse; + + if (!ReceivePacket(&deviceTypeResponse)) return (false); - *result = deviceInfoResponse.GetUnknown(); + *result = deviceTypeResponse.GetResult(); return (true); } @@ -921,7 +1054,23 @@ int BridgeManager::ReceivePitFile(unsigned char **pitBuffer) const return (fileSize); } -bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int chipIdentifier, unsigned int fileIdentifier) const +int BridgeManager::DownloadPitFile(unsigned char **pitBuffer) const +{ + Interface::Print("Downloading device's PIT file...\n"); + + int devicePitFileSize = ReceivePitFile(pitBuffer); + + if (!*pitBuffer) + { + Interface::PrintError("Failed to download PIT file!\n"); + return (0); + } + + Interface::Print("PIT file download successful.\n\n"); + return (devicePitFileSize); +} + +bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int deviceType, unsigned int fileIdentifier) const { if (destination != EndFileTransferPacket::kDestinationModem && destination != EndFileTransferPacket::kDestinationPhone) { @@ -959,16 +1108,16 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int return (false); } - unsigned int sequenceCount = fileSize / (kMaxSequenceLength * SendFilePartPacket::kDefaultPacketSize); - unsigned int lastSequenceSize = kMaxSequenceLength; - unsigned int partialPacketByteCount = fileSize % SendFilePartPacket::kDefaultPacketSize; + unsigned int sequenceCount = fileSize / (fileTransferSequenceMaxLength * fileTransferPacketSize); + unsigned int lastSequenceSize = fileTransferSequenceMaxLength; + unsigned int partialPacketByteCount = fileSize % fileTransferPacketSize; - if (fileSize % (kMaxSequenceLength * SendFilePartPacket::kDefaultPacketSize) != 0) + if (fileSize % (fileTransferSequenceMaxLength * fileTransferPacketSize) != 0) { sequenceCount++; - int lastSequenceBytes = fileSize % (kMaxSequenceLength * SendFilePartPacket::kDefaultPacketSize); - lastSequenceSize = lastSequenceBytes / SendFilePartPacket::kDefaultPacketSize; + int lastSequenceBytes = fileSize % (fileTransferSequenceMaxLength * fileTransferPacketSize); + lastSequenceSize = lastSequenceBytes / fileTransferPacketSize; if (partialPacketByteCount != 0) lastSequenceSize++; @@ -981,12 +1130,16 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int for (unsigned int sequenceIndex = 0; sequenceIndex < sequenceCount; sequenceIndex++) { - // Min(lastSequenceSize, 131072) - bool isLastSequence = sequenceIndex == sequenceCount - 1; - unsigned int sequenceSize = (isLastSequence) ? lastSequenceSize : kMaxSequenceLength; - unsigned int sequenceByteCount = ((isLastSequence) ? lastSequenceSize : kMaxSequenceLength) * SendFilePartPacket::kDefaultPacketSize + partialPacketByteCount; + bool isLastSequence = (sequenceIndex == sequenceCount - 1); + unsigned int sequenceSize = (isLastSequence) ? lastSequenceSize : fileTransferSequenceMaxLength; + unsigned int sequenceByteCount; + + if (isLastSequence) + sequenceByteCount = ((partialPacketByteCount) ? lastSequenceSize - 1 : lastSequenceSize) * fileTransferPacketSize + partialPacketByteCount; + else + sequenceByteCount = fileTransferSequenceMaxLength * fileTransferPacketSize; - FlashPartFileTransferPacket *beginFileTransferPacket = new FlashPartFileTransferPacket(0, 2 * sequenceSize); + FlashPartFileTransferPacket *beginFileTransferPacket = new FlashPartFileTransferPacket(sequenceByteCount); success = SendPacket(beginFileTransferPacket); delete beginFileTransferPacket; @@ -1014,7 +1167,7 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int for (unsigned int filePartIndex = 0; filePartIndex < sequenceSize; filePartIndex++) { // Send - sendFilePartPacket = new SendFilePartPacket(file); + sendFilePartPacket = new SendFilePartPacket(file, fileTransferPacketSize); success = SendPacket(sendFilePartPacket); delete sendFilePartPacket; @@ -1050,7 +1203,7 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int Interface::PrintError("Retrying..."); // Send - sendFilePartPacket = new SendFilePartPacket(file); + sendFilePartPacket = new SendFilePartPacket(file, fileTransferPacketSize); success = SendPacket(sendFilePartPacket); delete sendFilePartPacket; @@ -1097,7 +1250,8 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int return (false); } - bytesTransferred += SendFilePartPacket::kDefaultPacketSize; + bytesTransferred += fileTransferPacketSize; + if (bytesTransferred > fileSize) bytesTransferred = fileSize; @@ -1123,8 +1277,7 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int if (destination == EndFileTransferPacket::kDestinationPhone) { - EndPhoneFileTransferPacket *endPhoneFileTransferPacket = new EndPhoneFileTransferPacket(sequenceByteCount, 0, chipIdentifier, - fileIdentifier, isLastSequence); + EndPhoneFileTransferPacket *endPhoneFileTransferPacket = new EndPhoneFileTransferPacket(sequenceByteCount, 0, deviceType, fileIdentifier, isLastSequence); success = SendPacket(endPhoneFileTransferPacket, 3000); delete endPhoneFileTransferPacket; @@ -1138,7 +1291,7 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int } else // destination == EndFileTransferPacket::kDestinationModem { - EndModemFileTransferPacket *endModemFileTransferPacket = new EndModemFileTransferPacket(sequenceByteCount, 0, chipIdentifier, isLastSequence); + EndModemFileTransferPacket *endModemFileTransferPacket = new EndModemFileTransferPacket(sequenceByteCount, 0, deviceType, isLastSequence); success = SendPacket(endModemFileTransferPacket, 3000); delete endModemFileTransferPacket; @@ -1152,7 +1305,7 @@ bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int } fileTransferResponse = new ResponsePacket(ResponsePacket::kResponseTypeFileTransfer); - success = ReceivePacket(fileTransferResponse, 30000); + success = ReceivePacket(fileTransferResponse, fileTransferSequenceTimeout); delete fileTransferResponse; if (!success) diff --git a/heimdall/source/BridgeManager.h b/heimdall/source/BridgeManager.h index f131bda..1c3a435 100644 --- a/heimdall/source/BridgeManager.h +++ b/heimdall/source/BridgeManager.h @@ -41,8 +41,8 @@ namespace Heimdall const int productId; DeviceIdentifier(int vid, int pid) : - vendorId(vid), - productId(pid) + vendorId(vid), + productId(pid) { } }; @@ -53,10 +53,12 @@ namespace Heimdall enum { - kSupportedDeviceCount = 3, + kSupportedDeviceCount = 3, + }; - kCommunicationDelayDefault = 0, - kDumpBufferSize = 4096 + enum + { + kCommunicationDelayDefault = 0 }; enum @@ -83,15 +85,18 @@ namespace Heimdall static const DeviceIdentifier supportedDevices[kSupportedDeviceCount]; bool verbose; + int communicationDelay; libusb_context *libusbContext; libusb_device_handle *deviceHandle; libusb_device *heimdallDevice; + int interfaceIndex; + int altSettingIndex; int inEndpoint; int outEndpoint; - int communicationDelay; + bool interfaceClaimed; #ifdef OS_LINUX @@ -99,29 +104,39 @@ namespace Heimdall #endif + unsigned int fileTransferSequenceMaxLength; + unsigned int fileTransferPacketSize; + unsigned int fileTransferSequenceTimeout; + + int FindDeviceInterface(void); + bool ClaimDeviceInterface(void); + bool SetupDeviceInterface(void); + void ReleaseDeviceInterface(void); + bool CheckProtocol(void) const; bool InitialiseProtocol(void) const; public: - BridgeManager(bool verbose, int communicationDelay); + BridgeManager(bool verbose, int communicationDelay = BridgeManager::kCommunicationDelayDefault); ~BridgeManager(); bool DetectDevice(void); int Initialise(void); - bool BeginSession(void) const; + bool BeginSession(void); bool EndSession(bool reboot) const; bool SendPacket(OutboundPacket *packet, int timeout = 3000, bool retry = true) const; - bool ReceivePacket(InboundPacket *packet, int timeout = 3000, bool retry = true) const; + bool ReceivePacket(InboundPacket *packet, int timeout = 3000, bool retry = true, unsigned char *buffer = nullptr, unsigned int bufferSize = -1) const; - bool RequestDeviceInfo(unsigned int request, int *result) const; + bool RequestDeviceType(unsigned int request, int *result) const; bool SendPitFile(FILE *file) const; int ReceivePitFile(unsigned char **pitBuffer) const; + int DownloadPitFile(unsigned char **pitBuffer) const; // Thin wrapper around ReceivePitFile() with additional logging. - bool SendFile(FILE *file, unsigned int destination, unsigned int chipIdentifier, unsigned int fileIdentifier = 0xFFFFFFFF) const; + bool SendFile(FILE *file, unsigned int destination, unsigned int deviceType, unsigned int fileIdentifier = 0xFFFFFFFF) const; bool ReceiveDump(unsigned int chipType, unsigned int chipId, FILE *file) const; bool IsVerbose(void) const diff --git a/heimdall/source/ClosePcScreenAction.cpp b/heimdall/source/ClosePcScreenAction.cpp new file mode 100644 index 0000000..a7de9d9 --- /dev/null +++ b/heimdall/source/ClosePcScreenAction.cpp @@ -0,0 +1,94 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "ClosePcScreenAction.h" +#include "Heimdall.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *ClosePcScreenAction::usage = "Action: close-pc-screen\n\ +Arguments: [--verbose] [--no-reboot] [--stdout-errors] [--delay ]\n\ +Description: Attempts to get rid off the \"connect phone to PC\" screen.\n"; + +int ClosePcScreenAction::Execute(int argc, char **argv) +{ + // Handle arguments + + map argumentTypes; + argumentTypes["no-reboot"] = kArgumentTypeFlag; + argumentTypes["delay"] = kArgumentTypeUnsignedInteger; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + Arguments arguments(argumentTypes); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(ClosePcScreenAction::usage); + return (0); + } + + const UnsignedIntegerArgument *communicationDelayArgument = static_cast(arguments.GetArgument("delay")); + + bool reboot = arguments.GetArgument("no-reboot") == nullptr; + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + // Info + + Interface::PrintReleaseInfo(); + Sleep(1000); + + // Download PIT file from device. + + int communicationDelay = BridgeManager::kCommunicationDelayDefault; + + if (communicationDelayArgument) + communicationDelay = communicationDelayArgument->GetValue(); + + BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); + + if (bridgeManager->Initialise() != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) + { + delete bridgeManager; + return (1); + } + + Interface::Print("Attempting to close connect to pc screen...\n"); + + bool success = bridgeManager->EndSession(reboot); + delete bridgeManager; + + if (success) + { + Interface::Print("Attempt complete\n"); + return (0); + } + else + { + return (1); + } +} diff --git a/heimdall/source/ClosePcScreenAction.h b/heimdall/source/ClosePcScreenAction.h new file mode 100644 index 0000000..e4470a2 --- /dev/null +++ b/heimdall/source/ClosePcScreenAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef CLOSEPCSCREENACTION_H +#define CLOSEPCSCREENACTION_H + +namespace Heimdall +{ + namespace ClosePcScreenAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/ControlPacket.h b/heimdall/source/ControlPacket.h index eb12b09..7f46d7d 100644 --- a/heimdall/source/ControlPacket.h +++ b/heimdall/source/ControlPacket.h @@ -32,10 +32,10 @@ namespace Heimdall enum { - kControlTypeSetupSession = 0x64, - kControlTypePitFile = 0x65, - kControlTypeFileTransfer = 0x66, - kControlTypeEndSession = 0x67 + kControlTypeSession = 0x64, + kControlTypePitFile = 0x65, + kControlTypeFileTransfer = 0x66, + kControlTypeEndSession = 0x67 }; protected: diff --git a/heimdall/source/DetectAction.cpp b/heimdall/source/DetectAction.cpp new file mode 100644 index 0000000..319433f --- /dev/null +++ b/heimdall/source/DetectAction.cpp @@ -0,0 +1,64 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "DetectAction.h" +#include "Heimdall.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *DetectAction::usage = "Action: detect\n\ +Arguments: [--verbose] [--stdout-errors]\n\ +Description: Indicates whether or not a download mode device can be detected.\n"; + +int DetectAction::Execute(int argc, char **argv) +{ + // Handle arguments + + map argumentTypes; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + Arguments arguments(argumentTypes); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(DetectAction::usage); + return (0); + } + + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + // Download PIT file from device. + + BridgeManager *bridgeManager = new BridgeManager(verbose); + + bool detected = bridgeManager->DetectDevice(); + + delete bridgeManager; + + return ((detected) ? 0 : 1); +} diff --git a/heimdall/source/DetectAction.h b/heimdall/source/DetectAction.h new file mode 100644 index 0000000..374306b --- /dev/null +++ b/heimdall/source/DetectAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef DETECTACTION_H +#define DETECTACTION_H + +namespace Heimdall +{ + namespace DetectAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/DeviceTypePacket.h b/heimdall/source/DeviceTypePacket.h new file mode 100644 index 0000000..72dbfc6 --- /dev/null +++ b/heimdall/source/DeviceTypePacket.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef DEVICETYPEPACKET_H +#define DEVICETYPEPACKET_H + +// Heimdall +#include "SessionSetupPacket.h" + +namespace Heimdall +{ + class DeviceTypePacket : public SessionSetupPacket + { + public: + + DeviceTypePacket() : SessionSetupPacket(SessionSetupPacket::kDeviceType) + { + } + }; +} + +#endif diff --git a/heimdall/source/DownloadPitAction.cpp b/heimdall/source/DownloadPitAction.cpp new file mode 100644 index 0000000..b4b81a9 --- /dev/null +++ b/heimdall/source/DownloadPitAction.cpp @@ -0,0 +1,135 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// C Standard Library +#include + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "DownloadPitAction.h" +#include "Heimdall.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *DownloadPitAction::usage = "Action: download-pit\n\ +Arguments: --output [--verbose] [--no-reboot] [--stdout-errors]\n\ + [--delay ]\n\ +Description: Downloads the connected device's PIT file to the specified\n\ + output file.\n"; + +int DownloadPitAction::Execute(int argc, char **argv) +{ + // Handle arguments + + map argumentTypes; + argumentTypes["output"] = kArgumentTypeString; + argumentTypes["no-reboot"] = kArgumentTypeFlag; + argumentTypes["delay"] = kArgumentTypeUnsignedInteger; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + Arguments arguments(argumentTypes); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(DownloadPitAction::usage); + return (0); + } + + const StringArgument *outputArgument = static_cast(arguments.GetArgument("output")); + + if (!outputArgument) + { + Interface::Print("Output file was not specified.\n\n"); + Interface::Print(DownloadPitAction::usage); + return (0); + } + + const UnsignedIntegerArgument *communicationDelayArgument = static_cast(arguments.GetArgument("delay")); + + bool reboot = arguments.GetArgument("no-reboot") == nullptr; + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + // Info + + Interface::PrintReleaseInfo(); + Sleep(1000); + + // Open output file + + const char *outputFilename = outputArgument->GetValue().c_str(); + FILE *outputPitFile = fopen(outputFilename, "wb"); + + if (!outputPitFile) + { + Interface::PrintError("Failed to open output file \"%s\"\n", outputFilename); + return (1); + } + + // Download PIT file from device. + + int communicationDelay = BridgeManager::kCommunicationDelayDefault; + + if (communicationDelayArgument) + communicationDelay = communicationDelayArgument->GetValue(); + + BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); + + if (bridgeManager->Initialise() != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) + { + fclose(outputPitFile); + delete bridgeManager; + + return (1); + } + + unsigned char *pitBuffer; + int fileSize = bridgeManager->DownloadPitFile(&pitBuffer); + + bool success = true; + + if (fileSize > 0) + { + if (fwrite(pitBuffer, 1, fileSize, outputPitFile) != fileSize) + { + Interface::PrintError("Failed to write PIT data to output file.\n"); + success = false; + } + } + else + { + success = false; + } + + if (!bridgeManager->EndSession(reboot)) + success = false; + + delete bridgeManager; + + fclose(outputPitFile); + delete [] pitBuffer; + + return (success ? 0 : 1); +} diff --git a/heimdall/source/DownloadPitAction.h b/heimdall/source/DownloadPitAction.h new file mode 100644 index 0000000..7ccc0bd --- /dev/null +++ b/heimdall/source/DownloadPitAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef DOWNLOADPITACTION_H +#define DOWNLOADPITACTION_H + +namespace Heimdall +{ + namespace DownloadPitAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/DumpAction.cpp b/heimdall/source/DumpAction.cpp new file mode 100644 index 0000000..38ccbf9 --- /dev/null +++ b/heimdall/source/DumpAction.cpp @@ -0,0 +1,158 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// C Standard Library +#include + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "DumpAction.h" +#include "Heimdall.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *DumpAction::usage = "Action: dump\n\ +Arguments: --chip-type --chip-id --output \n\ + [--verbose] [--no-reboot] [--stdout-errors] [--delay ]\n\ +Description: Attempts to dump data from the phone corresponding to the\n\ + specified chip type and chip ID.\n\ +NOTE: Galaxy S phones don't appear to properly support this functionality.\n"; + +int DumpAction::Execute(int argc, char **argv) +{ + // Handle arguments + + map argumentTypes; + + argumentTypes["chip-type"] = kArgumentTypeString; + argumentTypes["chip-id"] = kArgumentTypeUnsignedInteger; + argumentTypes["output"] = kArgumentTypeString; + + argumentTypes["no-reboot"] = kArgumentTypeFlag; + argumentTypes["delay"] = kArgumentTypeUnsignedInteger; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + Arguments arguments(argumentTypes); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(DumpAction::usage); + return (0); + } + + const StringArgument *chipTypeArgument = static_cast(arguments.GetArgument("chip-type")); + const UnsignedIntegerArgument *chipIdArgument = static_cast(arguments.GetArgument("chip-id")); + const StringArgument *outputArgument = static_cast(arguments.GetArgument("output")); + + if (!outputArgument) + { + Interface::Print("Output file was not specified.\n\n"); + Interface::Print(DumpAction::usage); + return (false); + } + + if (!chipTypeArgument) + { + Interface::Print("You must specify a chip type.\n\n"); + Interface::Print(DumpAction::usage); + return (false); + } + + if (!(chipTypeArgument->GetValue() == "RAM" || chipTypeArgument->GetValue() == "ram" || chipTypeArgument->GetValue() == "NAND" + || chipTypeArgument->GetValue() == "nand")) + { + Interface::Print("Unknown chip type: %s.\n\n", chipTypeArgument->GetValue().c_str()); + Interface::Print(DumpAction::usage); + return (false); + } + + if (!chipIdArgument) + { + Interface::Print("You must specify a chip ID.\n\n"); + Interface::Print(DumpAction::usage); + return (false); + } + + const UnsignedIntegerArgument *communicationDelayArgument = static_cast(arguments.GetArgument("delay")); + + bool reboot = arguments.GetArgument("no-reboot") == nullptr; + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + // Open output file + + const char *outputFilename = outputArgument->GetValue().c_str(); + FILE *dumpFile = fopen(outputFilename, "wb"); + + if (!dumpFile) + { + Interface::PrintError("Failed to open file \"%s\"\n", outputFilename); + return (1); + } + + // Info + + Interface::PrintReleaseInfo(); + Sleep(1000); + + // Dump + + int communicationDelay = BridgeManager::kCommunicationDelayDefault; + + if (communicationDelayArgument) + communicationDelay = communicationDelayArgument->GetValue(); + + BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); + + if (bridgeManager->Initialise() != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) + { + fclose(dumpFile); + delete bridgeManager; + return (1); + } + + int chipType = 0; + + if (chipTypeArgument->GetValue() == "NAND" || chipTypeArgument->GetValue() == "nand") + chipType = 1; + + bool success = bridgeManager->ReceiveDump(chipType, chipIdArgument->GetValue(), dumpFile); + fclose(dumpFile); + + if (!bridgeManager->EndSession(reboot)) + success = false; + + delete bridgeManager; + + if (success) + { + Interface::Print("Attempt complete\n"); + return (0); + } + else + { + return (1); + } +} diff --git a/heimdall/source/DumpAction.h b/heimdall/source/DumpAction.h new file mode 100644 index 0000000..eb97bca --- /dev/null +++ b/heimdall/source/DumpAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef DUMPACTION_H +#define DUMPACTION_H + +namespace Heimdall +{ + namespace DumpAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/FilePartSizePacket.h b/heimdall/source/FilePartSizePacket.h new file mode 100644 index 0000000..561ec5b --- /dev/null +++ b/heimdall/source/FilePartSizePacket.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef FILEPARTSIZEPACKET_H +#define FILEPARTSIZEPACKET_H + +// Heimdall +#include "SessionSetupPacket.h" + +namespace Heimdall +{ + class FilePartSizePacket : public SessionSetupPacket + { + private: + + unsigned int filePartSize; + + public: + + FilePartSizePacket(unsigned int filePartSize) : SessionSetupPacket(SessionSetupPacket::kFilePartSize) + { + this->filePartSize = filePartSize; + } + + unsigned int GetFilePartSize(void) const + { + return filePartSize; + } + + void Pack(void) + { + SessionSetupPacket::Pack(); + + PackInteger(SessionSetupPacket::kDataSize, filePartSize); + } + }; +} + +#endif diff --git a/heimdall/source/FlashAction.cpp b/heimdall/source/FlashAction.cpp new file mode 100644 index 0000000..d79235e --- /dev/null +++ b/heimdall/source/FlashAction.cpp @@ -0,0 +1,620 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// C Standard Library +#include + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "EndModemFileTransferPacket.h" +#include "EndPhoneFileTransferPacket.h" +#include "FlashAction.h" +#include "Heimdall.h" +#include "Interface.h" +#include "SessionSetupResponse.h" +#include "TotalBytesPacket.h" +#include "Utility.h" + +using namespace Heimdall; + +const char *FlashAction::usage = "Action: flash\n\ +Arguments:\n\ + --repartition --pit [--factoryfs ]\n\ + [--cache ] [--dbdata ] [--primary-boot ]\n\ + [--secondary-boot ] [--param ] [--kernel ]\n\ + [--modem ] [--radio ] [--normal-boot ]\n\ + [--system ] [--user-data ] [--fota ]\n\ + [--hidden ] [--movinand ] [--data ]\n\ + [--ums ] [--emmc ]\n\ + [-- ]\n\ + [-- ]\n\ + [--verbose] [--no-reboot] [--stdout-errors] [--delay ]\n\ + or:\n\ + [--factoryfs ] [--cache ] [--dbdata ]\n\ + [--primary-boot ] [--secondary-boot ]\n\ + [--secondary-boot-backup ] [--param ]\n\ + [--kernel ] [--recovery ] [--efs ]\n\ + [--modem ] [--radio ] [--normal-boot ]\n\ + [--system ] [--user-data ] [--fota ]\n\ + [--hidden ] [--movinand ] [--data ]\n\ + [--ums ] [--emmc ] [--pit ]\n\ + [-- ]\n\ + [-- ]\n\ + [--verbose] [--no-reboot] [--stdout-errors] [--delay ]\n\ +Description: Flashes firmware files to your phone. Partition identifiers are\n\ + integer values, they can be obtained by executing the print-pit action.\n\ +WARNING: If you're repartitioning it's strongly recommended you specify\n\ + all files at your disposal, including bootloaders.\n"; + +struct PartitionFlashInfo +{ + const PitEntry *pitEntry; + FILE *file; + + PartitionFlashInfo(const PitEntry *pitEntry, FILE *file) + { + this->pitEntry = pitEntry; + this->file = file; + } +}; + +static void buildArgumentPartitionNamesMap(map< string, vector >& argumentPartitionNamesMap, map& shortArgumentAliases) +{ + argumentPartitionNamesMap["pit"].push_back("PIT"); + argumentPartitionNamesMap["factoryfs"].push_back("FACTORYFS"); + argumentPartitionNamesMap["cache"].push_back("CACHE"); + argumentPartitionNamesMap["dbdata"].push_back("DBDATAFS"); + + argumentPartitionNamesMap["primary-boot"].push_back("IBL+PBL"); + argumentPartitionNamesMap["primary-boot"].push_back("BOOT"); + + argumentPartitionNamesMap["secondary-boot"].push_back("SBL"); + argumentPartitionNamesMap["secondary-boot"].push_back("SBL1"); + + argumentPartitionNamesMap["secondary-boot-backup"].push_back("SBL2"); + argumentPartitionNamesMap["param"].push_back("PARAM"); + argumentPartitionNamesMap["kernel"].push_back("KERNEL"); + argumentPartitionNamesMap["recovery"].push_back("RECOVERY"); + argumentPartitionNamesMap["efs"].push_back("EFS"); + argumentPartitionNamesMap["modem"].push_back("MODEM"); + argumentPartitionNamesMap["radio"].push_back("RADIO"); + argumentPartitionNamesMap["normal-boot"].push_back("NORMALBOOT"); + argumentPartitionNamesMap["system"].push_back("SYSTEM"); + argumentPartitionNamesMap["user-data"].push_back("USERDATA"); + argumentPartitionNamesMap["fota"].push_back("FOTA"); + argumentPartitionNamesMap["hidden"].push_back("HIDDEN"); + argumentPartitionNamesMap["movinand"].push_back("MOVINAND"); + argumentPartitionNamesMap["data"].push_back("DATAFS"); + argumentPartitionNamesMap["ums"].push_back("UMS.EN"); + argumentPartitionNamesMap["emmc"].push_back("GANG"); + + shortArgumentAliases["pit"] = "pit"; + shortArgumentAliases["fs"] = "factoryfs"; + shortArgumentAliases["cache"] = "cache"; + shortArgumentAliases["db"] = "dbdata"; + shortArgumentAliases["boot"] = "primary-boot"; + shortArgumentAliases["sbl"] = "secondary-boot"; + shortArgumentAliases["sbl2"] = "secondary-boot-backup"; + shortArgumentAliases["param"] = "param"; + shortArgumentAliases["z"] = "kernel"; + shortArgumentAliases["rec"] = "recovery"; + shortArgumentAliases["efs"] = "efs"; + shortArgumentAliases["m"] = "modem"; + shortArgumentAliases["rdio"] = "radio"; + shortArgumentAliases["norm"] = "normal-boot"; + shortArgumentAliases["sys"] = "system"; + shortArgumentAliases["udata"] = "user-data"; + shortArgumentAliases["fota"] = "fota"; + shortArgumentAliases["hide"] = "hidden"; + shortArgumentAliases["nand"] = "movinand"; + shortArgumentAliases["data"] = "data"; + shortArgumentAliases["ums"] = "ums"; + shortArgumentAliases["emmc"] = "emmc"; +} + +static bool openFiles(Arguments& arguments, const map< string, vector >& argumentPartitionNamesMap, + map& argumentFileMap) +{ + for (map::const_iterator it = arguments.GetArguments().begin(); it != arguments.GetArguments().end(); it++) + { + bool isPartitionArgument = false; + const string& argumentName = it->first; + + if (arguments.GetArgumentTypes().find(argumentName) == arguments.GetArgumentTypes().end()) + { + // The only way an argument could exist without being in the argument types map is if it's a wild-card. + // The "%d" wild-card refers to a partition by identifier, where as the "%s" wild-card refers to a + // partition by name. + isPartitionArgument = true; + } + else + { + // The argument wasn't a wild-card, check if it's a known partition name. + if (argumentPartitionNamesMap.find(argumentName) != argumentPartitionNamesMap.end()) + isPartitionArgument = true; + } + + if (isPartitionArgument) + { + const StringArgument *stringArgument = static_cast(it->second); + FILE *file = fopen(stringArgument->GetValue().c_str(), "rb"); + + if (!file) + { + Interface::PrintError("Failed to open file \"%s\"\n", stringArgument->GetValue().c_str()); + return (false); + } + + argumentFileMap[it->first] = file; + } + } + + return (true); +} + +static void closeFiles(map argumentfileMap) +{ + for (map::iterator it = argumentfileMap.begin(); it != argumentfileMap.end(); it++) + fclose(it->second); + + argumentfileMap.clear(); +} + +static bool sendTotalTransferSize(BridgeManager *bridgeManager, const map& argumentFileMap, bool repartition) +{ + int totalBytes = 0; + for (map::const_iterator it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) + { + if (repartition || it->first != "pit") + { + fseek(it->second, 0, SEEK_END); + totalBytes += ftell(it->second); + rewind(it->second); + } + } + + bool success; + + TotalBytesPacket *totalBytesPacket = new TotalBytesPacket(totalBytes); + success = bridgeManager->SendPacket(totalBytesPacket); + delete totalBytesPacket; + + if (!success) + { + Interface::PrintError("Failed to send total bytes device info packet!\n"); + return (false); + } + + SessionSetupResponse *totalBytesResponse = new SessionSetupResponse(); + success = bridgeManager->ReceivePacket(totalBytesResponse); + int totalBytesResult = totalBytesResponse->GetResult(); + delete totalBytesResponse; + + if (!success) + { + Interface::PrintError("Failed to receive device info response!\n"); + return (false); + } + + if (totalBytesResult != 0) + { + Interface::PrintError("Unexpected device info response!\nExpected: 0\nReceived:%d\n", totalBytesResponse); + return (false); + } + + return (true); +} + +static bool setupPartitionFlashInfo(const map& argumentFileMap, const map< string, vector >& argumentPartitionNamesMap, + const PitData *pitData, vector& partitionFlashInfos) +{ + for (map::const_iterator it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) + { + const string& argumentName = it->first; + FILE *partitionFile = it->second; + + const PitEntry *pitEntry = nullptr; + + // Was the argument a partition identifier? + unsigned int partitionIdentifier; + + if (Utility::ParseUnsignedInt(partitionIdentifier, argumentName.c_str()) == kNumberParsingStatusSuccess) + { + pitEntry = pitData->FindEntry(partitionIdentifier); + + if (!pitEntry) + { + Interface::PrintError("No partition with identifier \"%s\" exists in the specified PIT.\n", argumentName.c_str()); + return (false); + } + } + else + { + // The argument wasn't a partition identifier. Was it a known human-readable partition name? + map< string, vector >::const_iterator argumentPartitionNamesIt = argumentPartitionNamesMap.find(argumentName); + + if (argumentPartitionNamesIt != argumentPartitionNamesMap.end()) + { + const vector& partitionNames = argumentPartitionNamesIt->second; + + // Check for the partition in the PIT file using all known names. + for (vector::const_iterator nameIt = partitionNames.begin(); nameIt != partitionNames.end(); nameIt++) + { + pitEntry = pitData->FindEntry(nameIt->c_str()); + + if (pitEntry) + break; + } + + if (!pitEntry) + { + Interface::PrintError("Partition name for \"%s\" could not be located\n", argumentName.c_str()); + return (false); + } + } + else + { + // The argument must be an actual partition name. e.g. "ZIMAGE", instead of human-readable "kernel". + pitEntry = pitData->FindEntry(argumentName.c_str()); + + if (!pitEntry) + { + Interface::PrintError("Partition \"%s\" does not exist in the specified PIT.\n", argumentName.c_str()); + return (false); + } + } + } + + partitionFlashInfos.push_back(PartitionFlashInfo(pitEntry, partitionFile)); + } + + return (true); +} + +static bool isKnownPartition(const map >& argumentPartitionNamesMap, const string& argumentName, const string& partitionName) +{ + const vector& partitionNames = argumentPartitionNamesMap.find(argumentName)->second; + + for (vector::const_iterator it = partitionNames.begin(); it != partitionNames.end(); it++) + { + if (partitionName == *it) + return (true); + } + + return (false); +} + +static bool isKnownBootPartition(const map >& argumentPartitionNamesMap, const char *partitionName) +{ + return (isKnownPartition(argumentPartitionNamesMap, "primary-boot", partitionName) + || isKnownPartition(argumentPartitionNamesMap, "secondary-boot", partitionName) + || isKnownPartition(argumentPartitionNamesMap, "secondary-boot-backup", partitionName) + || isKnownPartition(argumentPartitionNamesMap, "param", partitionName) + || isKnownPartition(argumentPartitionNamesMap, "normal-boot", partitionName) + || strcmp(partitionName, "SBL3") == 0 + || strcmp(partitionName, "ABOOT") == 0 + || strcmp(partitionName, "RPM") == 0 + || strcmp(partitionName, "TZ") == 0); +} + +static bool flashFile(BridgeManager *bridgeManager, const map< string, vector >& argumentPartitionNamesMap, + const PartitionFlashInfo& partitionFlashInfo) +{ + // PIT files need to be handled differently, try determine if the partition we're flashing to is a PIT partition. + + if (isKnownPartition(argumentPartitionNamesMap, "pit", partitionFlashInfo.pitEntry->GetPartitionName())) + { + Interface::Print("Uploading %s\n", partitionFlashInfo.pitEntry->GetPartitionName()); + + if (bridgeManager->SendPitFile(partitionFlashInfo.file)) + { + Interface::Print("%s upload successful\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (true); + } + else + { + Interface::PrintError("%s upload failed!\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (false); + } + } + else + { + if (partitionFlashInfo.pitEntry->GetBinaryType() == PitEntry::kBinaryTypeCommunicationProcessor) // Modem + { + Interface::Print("Uploading %s\n", partitionFlashInfo.pitEntry->GetPartitionName()); + + if (bridgeManager->SendFile(partitionFlashInfo.file, EndModemFileTransferPacket::kDestinationModem, + partitionFlashInfo.pitEntry->GetDeviceType())) // <-- Odin method + { + Interface::Print("%s upload successful\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (true); + } + else + { + Interface::PrintError("%s upload failed!\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (false); + } + } + else // partitionFlashInfo.pitEntry->GetBinaryType() == PitEntry::kBinaryTypeApplicationProcessor + { + Interface::Print("Uploading %s\n", partitionFlashInfo.pitEntry->GetPartitionName()); + + if (bridgeManager->SendFile(partitionFlashInfo.file, EndPhoneFileTransferPacket::kDestinationPhone, + partitionFlashInfo.pitEntry->GetDeviceType(), partitionFlashInfo.pitEntry->GetIdentifier())) + { + Interface::Print("%s upload successful\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (true); + } + else + { + Interface::PrintError("%s upload failed!\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); + return (false); + } + } + } + + return (true); +} + +static bool flashPartitions(const map& argumentFileMap, const map< string, vector >& argumentPartitionNamesMap, + const PitData *pitData, BridgeManager *bridgeManager, bool repartition) +{ + vector partitionFlashInfos; + + // Map the files being flashed to partitions stored in the PIT file. + if (!setupPartitionFlashInfo(argumentFileMap, argumentPartitionNamesMap, pitData, partitionFlashInfos)) + return (false); + + // If we're repartitioning then we need to flash the PIT file first. + if (repartition) + { + vector::const_iterator it; + + for (it = partitionFlashInfos.begin(); it != partitionFlashInfos.end(); it++) + { + if (isKnownPartition(argumentPartitionNamesMap, "pit", it->pitEntry->GetPartitionName())) + { + if (!flashFile(bridgeManager, argumentPartitionNamesMap, *it)) + return (false); + + break; + } + } + + if (it == partitionFlashInfos.end()) + { + Interface::PrintError("Could not identify the PIT partition within the specified PIT file.\n\n"); + return (false); + } + } + + // Flash partitions not involved in the boot process second. + for (vector::const_iterator it = partitionFlashInfos.begin(); it != partitionFlashInfos.end(); it++) + { + if (!isKnownPartition(argumentPartitionNamesMap, "pit", it->pitEntry->GetPartitionName()) + && !isKnownBootPartition(argumentPartitionNamesMap, it->pitEntry->GetPartitionName())) + { + if (!flashFile(bridgeManager, argumentPartitionNamesMap, *it)) + return (false); + } + } + + // Flash boot partitions last. + for (vector::const_iterator it = partitionFlashInfos.begin(); it != partitionFlashInfos.end(); it++) + { + if (isKnownBootPartition(argumentPartitionNamesMap, it->pitEntry->GetPartitionName())) + { + if (!flashFile(bridgeManager, argumentPartitionNamesMap, *it)) + return (false); + } + } + + return (true); +} + +static PitData *getPitData(const map& argumentFileMap, BridgeManager *bridgeManager, bool repartition) +{ + PitData *pitData; + PitData *localPitData = nullptr; + + // If a PIT file was passed as an argument then we must unpack it. + + map::const_iterator localPitFileIt = argumentFileMap.find("pit"); + + if (localPitFileIt != argumentFileMap.end()) + { + FILE *localPitFile = localPitFileIt->second; + + // Load the local pit file into memory. + unsigned char *pitFileBuffer = new unsigned char[4096]; + memset(pitFileBuffer, 0, 4096); + + fseek(localPitFile, 0, SEEK_END); + long localPitFileSize = ftell(localPitFile); + rewind(localPitFile); + + // dataRead is discarded, it's here to remove warnings. + int dataRead = fread(pitFileBuffer, 1, localPitFileSize, localPitFile); + rewind(localPitFile); + + localPitData = new PitData(); + localPitData->Unpack(pitFileBuffer); + + delete [] pitFileBuffer; + } + + if (repartition) + { + // Use the local PIT file data. + pitData = localPitData; + } + else + { + // If we're not repartitioning then we need to retrieve the device's PIT file and unpack it. + unsigned char *pitFileBuffer; + + if (bridgeManager->DownloadPitFile(&pitFileBuffer) == 0) + return (nullptr); + + pitData = new PitData(); + pitData->Unpack(pitFileBuffer); + + delete [] pitFileBuffer; + + if (localPitData != nullptr) + { + // The user has specified a PIT without repartitioning, we should verify the local and device PIT data match! + bool pitsMatch = pitData->Matches(localPitData); + delete localPitData; + + if (!pitsMatch) + { + Interface::Print("Local and device PIT files don't match and repartition wasn't specified!\n"); + Interface::PrintError("Flash aborted!\n"); + return (nullptr); + } + } + } + + return (pitData); +} + +int FlashAction::Execute(int argc, char **argv) +{ + // Setup argument types + + map argumentTypes; + + argumentTypes["repartition"] = kArgumentTypeString; + + argumentTypes["no-reboot"] = kArgumentTypeFlag; + argumentTypes["delay"] = kArgumentTypeUnsignedInteger; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + map< string, vector > argumentPartitionNamesMap; + map shortArgumentAliases; + + buildArgumentPartitionNamesMap(argumentPartitionNamesMap, shortArgumentAliases); + + for (map< string, vector >::const_iterator it = argumentPartitionNamesMap.begin(); it != argumentPartitionNamesMap.end(); it++) + argumentTypes[it->first] = kArgumentTypeString; + + // Add wild-cards "%d" and "%s", for partition identifiers and partition names respectively. + argumentTypes["%d"] = kArgumentTypeString; + shortArgumentAliases["%d"] = "%d"; + + argumentTypes["%s"] = kArgumentTypeString; + shortArgumentAliases["%s"] = "%s"; + + map argumentAliases; + argumentAliases["PIT"] = "pit"; // Map upper-case PIT argument (i.e. partition name) to known lower-case pit argument. + + // Handle arguments + + Arguments arguments(argumentTypes, shortArgumentAliases, argumentAliases); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(FlashAction::usage); + return (0); + } + + const UnsignedIntegerArgument *communicationDelayArgument = static_cast(arguments.GetArgument("delay")); + + bool reboot = arguments.GetArgument("no-reboot") == nullptr; + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + const StringArgument *pitArgument = static_cast(arguments.GetArgument("pit")); + + bool repartition = arguments.GetArgument("repartition") != nullptr; + + if (repartition && !pitArgument) + { + Interface::Print("If you wish to repartition then a PIT file must be specified.\n\n"); + Interface::Print(FlashAction::usage); + return (0); + } + + // Open files + + map argumentFileMap; + + if (!openFiles(arguments, argumentPartitionNamesMap, argumentFileMap)) + { + closeFiles(argumentFileMap); + return (1); + } + + if (argumentFileMap.size() == 0) + { + Interface::Print(FlashAction::usage); + return (0); + } + + // Info + + Interface::PrintReleaseInfo(); + Sleep(1000); + + // Perform flash + + int communicationDelay = BridgeManager::kCommunicationDelayDefault; + + if (communicationDelayArgument) + communicationDelay = communicationDelayArgument->GetValue(); + + BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); + + if (bridgeManager->Initialise() != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) + { + closeFiles(argumentFileMap); + delete bridgeManager; + + return (1); + } + + bool success = sendTotalTransferSize(bridgeManager, argumentFileMap, repartition); + + if (success) + { + PitData *pitData = getPitData(argumentFileMap, bridgeManager, repartition); + + if (pitData) + success = flashPartitions(argumentFileMap, argumentPartitionNamesMap, pitData, bridgeManager, repartition); + else + success = false; + + delete pitData; + } + + closeFiles(argumentFileMap); + + if (!bridgeManager->EndSession(reboot)) + success = false; + + delete bridgeManager; + + return (success ? 0 : 1); +} diff --git a/heimdall/source/FlashAction.h b/heimdall/source/FlashAction.h new file mode 100644 index 0000000..b4e1b6b --- /dev/null +++ b/heimdall/source/FlashAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef FLASHACTION_H +#define FLASHACTION_H + +namespace Heimdall +{ + namespace FlashAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/FlashPartFileTransferPacket.h b/heimdall/source/FlashPartFileTransferPacket.h index a444211..dcf3422 100644 --- a/heimdall/source/FlashPartFileTransferPacket.h +++ b/heimdall/source/FlashPartFileTransferPacket.h @@ -30,34 +30,26 @@ namespace Heimdall { private: - unsigned short unknown; - unsigned int transferCount; + unsigned int sequenceByteCount; public: - FlashPartFileTransferPacket(unsigned short unknown, unsigned int transferCount) + FlashPartFileTransferPacket(unsigned int sequenceByteCount) : FileTransferPacket(FileTransferPacket::kRequestPart) { - this->unknown = unknown; - this->transferCount = transferCount; + this->sequenceByteCount = sequenceByteCount; } - unsigned short GetUnknown(void) const + unsigned int GetSequenceByteCount(void) const { - return (unknown); - } - - unsigned int GetTransferCount(void) const - { - return (transferCount); + return (sequenceByteCount); } void Pack(void) { FileTransferPacket::Pack(); - PackShort(FileTransferPacket::kDataSize, unknown); - PackInteger(FileTransferPacket::kDataSize + 2, transferCount); + PackInteger(FileTransferPacket::kDataSize, sequenceByteCount); } }; } diff --git a/heimdall/source/Heimdall.h b/heimdall/source/Heimdall.h index eb3c96c..1cb2eaa 100644 --- a/heimdall/source/Heimdall.h +++ b/heimdall/source/Heimdall.h @@ -23,6 +23,7 @@ #ifdef OS_WINDOWS #include +#undef GetBinaryType #else #include "../config.h" diff --git a/heimdall/source/HelpAction.cpp b/heimdall/source/HelpAction.cpp new file mode 100644 index 0000000..f041a81 --- /dev/null +++ b/heimdall/source/HelpAction.cpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Heimdall.h" +#include "HelpAction.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *HelpAction::usage = "Action: help\n\ +Description: Displays this dialogue.\n"; + +int HelpAction::Execute(int argc, char **argv) +{ + Interface::PrintUsage(); + return (0); +} diff --git a/heimdall/source/HelpAction.h b/heimdall/source/HelpAction.h new file mode 100644 index 0000000..7fb3e82 --- /dev/null +++ b/heimdall/source/HelpAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef HELPACTION_H +#define HELPACTION_H + +namespace Heimdall +{ + namespace HelpAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/InfoAction.cpp b/heimdall/source/InfoAction.cpp new file mode 100644 index 0000000..740f4e2 --- /dev/null +++ b/heimdall/source/InfoAction.cpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Heimdall.h" +#include "InfoAction.h" +#include "Interface.h" + +using namespace Heimdall; + +const char *InfoAction::usage = "Action: info\n\ +Description: Displays information about Heimdall.\n"; + +int InfoAction::Execute(int argc, char **argv) +{ + Interface::PrintFullInfo(); + return (0); +} diff --git a/heimdall/source/InfoAction.h b/heimdall/source/InfoAction.h new file mode 100644 index 0000000..8829d4a --- /dev/null +++ b/heimdall/source/InfoAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef INFOACTION_H +#define INFOACTION_H + +namespace Heimdall +{ + namespace InfoAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/Interface.cpp b/heimdall/source/Interface.cpp index 4d42ad0..67f1bee 100644 --- a/heimdall/source/Interface.cpp +++ b/heimdall/source/Interface.cpp @@ -24,342 +24,108 @@ #include // Heimdall +#include "ClosePcScreenAction.h" +#include "DetectAction.h" +#include "DownloadPitAction.h" +#include "DumpAction.h" +#include "FlashAction.h" +#include "HelpAction.h" +#include "InfoAction.h" #include "Heimdall.h" #include "Interface.h" +#include "PrintPitAction.h" +#include "VersionAction.h" using namespace std; using namespace libpit; using namespace Heimdall; -bool Interface::stdoutErrors = false; +map actionMap; +bool stdoutErrors = false; + +const char *version = "v1.4 RC1"; +const char *actionUsage = "Usage: heimdall \n"; -const char *Interface::version = "v1.3.2"; - -const char *Interface::usage = "Usage: heimdall \n\ -\n\ -Common Arguments:\n\ - [--verbose] [--no-reboot] [--stdout-errors] [--delay ]\n\ -\n\ -\n\ -Action: flash\n\ -Arguments:\n\ - --repartition --pit [--factoryfs ]\n\ - [--cache ] [--dbdata ] [--primary-boot ]\n\ - [--secondary-boot ] [--param ] [--kernel ]\n\ - [--modem ] [--normal-boot ] [--system ]\n\ - [--user-data ] [--fota ] [--hidden ]\n\ - [--movinand ] [--data ] [--ums ]\n\ - [--emmc ] [-- ]\n\ - or:\n\ - [--factoryfs ] [--cache ] [--dbdata ]\n\ - [--primary-boot ] [--secondary-boot ]\n\ - [--secondary-boot-backup ] [--param ]\n\ - [--kernel ] [--recovery ] [--efs ]\n\ - [--modem ] [--normal-boot ] [--system ]\n\ - [--user-data ] [--fota ] [--hidden ]\n\ - [--movinand ] [--data ] [--ums ]\n\ - [--emmc ] [-- ]\n\ -Description: Flashes firmware files to your phone.\n\ -WARNING: If you're repartitioning it's strongly recommended you specify\n\ - all files at your disposal, including bootloaders.\n\ -\n\ -Action: close-pc-screen\n\ -Description: Attempts to get rid off the \"connect phone to PC\" screen.\n\ -\n\ -Action: download-pit\n\ -Arguments: --output \n\ -Description: Downloads the connected device's PIT file to the specified\n\ - output file.\n\ -\n\ -Action: detect\n\ -Description: Indicates whether or not a download mode device can be detected.\n\ -\n\ -Action: dump\n\ -Arguments: --chip-type --chip-id --output \n\ -Description: Attempts to dump data from the phone corresponding to the\n\ - specified chip type and chip ID.\n\ -NOTE: Galaxy S phones don't appear to properly support this functionality.\n\ -\n\ -Action: print-pit\n\ -Description: Dumps the PIT file from the connected device and prints it in\n\ - a human readable format.\n\ -\n\ -Action: version\n\ -Description: Displays the version number of this binary.\n\ -\n\ -Action: help\n\ -Description: Displays this dialogue.\n"; - -const char *Interface::releaseInfo = "Heimdall %s, Copyright (c) 2010-2012, Benjamin Dobell, Glass Echidna\n\ -http://www.glassechidna.com.au\n\n\ +const char *releaseInfo = "Heimdall %s\n\n\ +Copyright (c) 2010-2012, Benjamin Dobell, Glass Echidna\n\ +http://www.glassechidna.com.au/\n\n\ This software is provided free of charge. Copying and redistribution is\nencouraged.\n\n\ If you appreciate this software and you would like to support future\ndevelopment please consider donating:\n\ http://www.glassechidna.com.au/donate/\n\n"; -const char *Interface::extraInfo = "Heimdall utilises libusb-1.0 for all USB communication:\n\ +static const char *extraInfo = "Heimdall utilises libusb-1.0 for all USB communication:\n\ http://www.libusb.org/\n\ \n\ libusb-1.0 is licensed under the LGPL-2.1:\n\ http://www.gnu.org/licenses/licenses.html#LGPL\n\n"; -// Flash arguments -string Interface::flashValueArguments[kFlashValueArgCount] = { - "-pit", "-factoryfs", "-cache", "-dbdata", "-primary-boot", "-secondary-boot", "-secondary-boot-backup", "-param", "-kernel", "-recovery", "-efs", "-modem", - "-normal-boot", "-system", "-user-data", "-fota", "-hidden", "-movinand", "-data", "-ums", "-emmc", "-%d" -}; - -string Interface::flashValueShortArguments[kFlashValueArgCount] = { - "pit", "fs", "cache", "db", "boot", "sbl", "sbl2", "param", "z", "rec", "efs", "m", - "norm", "sys", "udata", "fota", "hide", "nand", "data", "ums", "emmc", "%d" -}; - -string Interface::flashValuelessArguments[kFlashValuelessArgCount] = { - "-repartition" -}; - -string Interface::flashValuelessShortArguments[kFlashValuelessArgCount] = { - "r" -}; - -// Download PIT arguments -string Interface::downloadPitValueArguments[kDownloadPitValueArgCount] = { - "-output" -}; - -string Interface::downloadPitValueShortArguments[kDownloadPitValueArgCount] = { - "o" -}; - -// Dump arguments -string Interface::dumpValueArguments[kDumpValueArgCount] = { - "-chip-type", "-chip-id", "-output" -}; - -string Interface::dumpValueShortArguments[kDumpValueArgCount] = { - "type", "id", "out" -}; - -// Common arguments -string Interface::commonValueArguments[kCommonValueArgCount] = { - "-delay" -}; - -string Interface::commonValueShortArguments[kCommonValueArgCount] = { - "d" -}; - -string Interface::commonValuelessArguments[kCommonValuelessArgCount] = { - "-verbose", "-no-reboot", "-stdout-errors" -}; - -string Interface::commonValuelessShortArguments[kCommonValuelessArgCount] = { - "v", "nobt", "err" -}; - -Action Interface::actions[Interface::kActionCount] = { - // kActionFlash - Action("flash", flashValueArguments, flashValueShortArguments, kFlashValueArgCount, - flashValuelessArguments, flashValuelessShortArguments, kFlashValuelessArgCount), - - // kActionClosePcScreen - Action("close-pc-screen", nullptr, nullptr, kClosePcScreenValueArgCount, - nullptr, nullptr, kClosePcScreenValuelessArgCount), - - // kActionDump - Action("dump", dumpValueArguments, dumpValueShortArguments, kDumpValueArgCount, - nullptr, nullptr, kDumpValuelessArgCount), - - // kActionPrintPit - Action("print-pit", nullptr, nullptr, kPrintPitValueArgCount, - nullptr, nullptr, kPrintPitValuelessArgCount), - - // kActionVersion - Action("version", nullptr, nullptr, kVersionValueArgCount, - nullptr, nullptr, kVersionValuelessArgCount), - - // kActionHelp - Action("help", nullptr, nullptr, kHelpValueArgCount, - nullptr, nullptr, kHelpValuelessArgCount), - - // kActionDetect - Action("detect", nullptr, nullptr, kDetectValueArgCount, - nullptr, nullptr, kDetectValuelessArgCount), - - // kActionDownloadPit - Action("download-pit", downloadPitValueArguments, downloadPitValueShortArguments, kDownloadPitValueArgCount, - nullptr, nullptr, kDownloadPitValuelessArgCount), - - // kActionInfo - Action("info", nullptr, nullptr, kInfoValueArgCount, - nullptr, nullptr, kInfoValuelessArgCount) -}; - -bool Interface::GetArguments(int argc, char **argv, map& argumentMap, int *actionIndex) +void populateActionMap(void) { - if (argc < 2) - { - Print(usage, version); - return (false); - } - - const char *actionName = argv[1]; - *actionIndex = -1; - - for (int i = 0; i < kActionCount; i++) - { - if (actions[i].name == actionName) - { - *actionIndex = i; - break; - } - } - - if (*actionIndex < 0) - { - Print("Unknown action \"%s\"\n\n", actionName); - Print(usage, version); - return (false); - } - - const Action& action = actions[*actionIndex]; - - for (int argIndex = 2; argIndex < argc; argIndex++) - { - if (*(argv[argIndex]) != '-') - { - Print(usage, version); - return (false); - } - - string argumentName = (char *)(argv[argIndex] + 1); - - // Check if the argument is a valid valueless argument - bool valid = false; - - for (unsigned int i = 0; i < action.valuelessArgumentCount; i++) - { - if (argumentName == action.valuelessArguments[i] || argumentName == action.valuelessShortArguments[i]) - { - argumentName = action.valuelessArguments[i]; - valid = true; - break; - } - } - - if (!valid) - { - // Check if it's a common valueless argument - for (unsigned int i = 0; i < kCommonValuelessArgCount; i++) - { - if (argumentName == commonValuelessArguments[i] || argumentName == commonValuelessShortArguments[i]) - { - argumentName = commonValuelessArguments[i]; - valid = true; - break; - } - } - } + actionMap["close-pc-screen"] = Interface::ActionInfo(&ClosePcScreenAction::Execute, ClosePcScreenAction::usage); + actionMap["detect"] = Interface::ActionInfo(&DetectAction::Execute, DetectAction::usage); + actionMap["download-pit"] = Interface::ActionInfo(&DownloadPitAction::Execute, DownloadPitAction::usage); + actionMap["dump"] = Interface::ActionInfo(&DumpAction::Execute, DumpAction::usage); + actionMap["flash"] = Interface::ActionInfo(&FlashAction::Execute, FlashAction::usage); + actionMap["help"] = Interface::ActionInfo(&HelpAction::Execute, HelpAction::usage); + actionMap["info"] = Interface::ActionInfo(&InfoAction::Execute, InfoAction::usage); + actionMap["print-pit"] = Interface::ActionInfo(&PrintPitAction::Execute, PrintPitAction::usage); + actionMap["version"] = Interface::ActionInfo(&VersionAction::Execute, VersionAction::usage); +} - if (valid) - { - // The argument is valueless - argumentMap.insert(pair(argumentName, "")); - continue; - } +const map& Interface::GetActionMap(void) +{ + if (actionMap.size() == 0) + populateActionMap(); - // Check if the argument is a valid value argument - for (unsigned int i = 0; i < action.valueArgumentCount; i++) - { - // Support for -- and - parameters. - if (argumentName.length() > 1 && action.valueArguments[i] == "-%d") - { - if (atoi(argumentName.substr(1).c_str()) > 0 || argumentName == "-0") - { - valid = true; - break; - } - } - else if (action.valueArguments[i] == "%d") - { - if (atoi(argumentName.c_str()) > 0 || argumentName == "0") - { - argumentName = "-" + argumentName; - valid = true; - break; - } - } + return actionMap; +} - if (argumentName == action.valueArguments[i] || argumentName == action.valueShortArguments[i]) - { - argumentName = action.valueArguments[i]; - valid = true; - break; - } - } +void Interface::Print(const char *format, ...) +{ + va_list args; + va_start(args, format); - if (!valid) - { - // Check if it's a common value argument - for (unsigned int i = 0; i < kCommonValueArgCount; i++) - { - // Support for -- and - parameters. - if (argumentName.length() > 1 && commonValueArguments[i] == "-%d") - { - if (atoi(argumentName.substr(1).c_str()) > 0 || argumentName == "-0") - { - valid = true; - break; - } - } - else if (commonValueArguments[i] == "%d") - { - if (atoi(argumentName.c_str()) > 0 || argumentName == "0") - { - argumentName = "-" + argumentName; - valid = true; - break; - } - } - - if (argumentName == commonValueArguments[i] || argumentName == commonValueShortArguments[i]) - { - argumentName = commonValueArguments[i]; - valid = true; - break; - } - } - } + vfprintf(stdout, format, args); + fflush(stdout); - if (!valid) - { - PrintError("\"%s\" is not a valid argument\n", argumentName.c_str()); - return (false); - } + va_end(args); + +} - argIndex++; +void Interface::PrintWarning(const char *format, ...) +{ + va_list args; + va_start(args, format); - if (argIndex >= argc) - { - PrintError("\"%s\" is missing a value\n", argumentName.c_str()); - return (false); - } + fprintf(stderr, "WARNING: "); + vfprintf(stderr, format, args); + fflush(stderr); - argumentMap.insert(pair(argumentName, argv[argIndex])); + if (stdoutErrors) + { + fprintf(stdout, "WARNING: "); + vfprintf(stdout, format, args); + fflush(stdout); } - return (true); + va_end(args); } -void Interface::Print(const char *format, ...) +void Interface::PrintWarningSameLine(const char *format, ...) { va_list args; va_start(args, format); - vfprintf(stdout, format, args); - fflush(stdout); + vfprintf(stderr, format, args); + fflush(stderr); + + if (stdoutErrors) + { + vfprintf(stdout, format, args); + fflush(stdout); + } va_end(args); - } void Interface::PrintError(const char *format, ...) @@ -400,28 +166,33 @@ void Interface::PrintErrorSameLine(const char *format, ...) void Interface::PrintVersion(void) { - Print("%s\n", version); + Interface::Print("%s\n", version); } void Interface::PrintUsage(void) { - Print(usage); + const map& actionMap = Interface::GetActionMap(); + + Interface::Print(actionUsage); + + for (map::const_iterator it = actionMap.begin(); it != actionMap.end(); it++) + Interface::Print("\n%s", it->second.usage); } void Interface::PrintReleaseInfo(void) { - Print(releaseInfo, version); + Interface::Print(releaseInfo, version); } void Interface::PrintFullInfo(void) { - Print(releaseInfo, version); - Print(extraInfo); + Interface::Print(releaseInfo, version); + Interface::Print(extraInfo); } void Interface::PrintDeviceDetectionFailed(void) { - Print("Failed to detect compatible download-mode device.\n"); + Interface::PrintError("Failed to detect compatible download-mode device.\n"); } void Interface::PrintPit(const PitData *pitData) @@ -442,34 +213,107 @@ void Interface::PrintPit(const PitData *pitData) const PitEntry *entry = pitData->GetEntry(i); Interface::Print("\n\n--- Entry #%d ---\n", i); - Interface::Print("Unused: %s\n", (entry->GetUnused()) ? "Yes" : "No"); + Interface::Print("Binary Type: %d (", entry->GetBinaryType()); - const char *chipIdentifierText = "Unknown"; + switch (entry->GetBinaryType()) + { + case PitEntry::kBinaryTypeApplicationProcessor: + Interface::Print("AP"); + break; - Interface::Print("Chip Identifier: %d (%s)\n", entry->GetChipIdentifier()); + case PitEntry::kBinaryTypeCommunicationProcessor: + Interface::Print("CP"); + break; - Interface::Print("Partition Identifier: %d\n", entry->GetPartitionIdentifier()); + default: + Interface::Print("Unknown"); + break; + } - Interface::Print("Partition Flags: %d (", entry->GetPartitionFlags()); + Interface::Print(")\n"); - if (entry->GetPartitionFlags() & PitEntry::kPartitionFlagWrite) - Interface::Print("R/W"); + Interface::Print("Device Type: %d (", entry->GetDeviceType()); + + switch (entry->GetDeviceType()) + { + case PitEntry::kDeviceTypeOneNand: + Interface::Print("OneNAND"); + break; + + case PitEntry::kDeviceTypeFile: + Interface::Print("File/FAT"); + break; + + case PitEntry::kDeviceTypeMMC: + Interface::Print("MMC"); + break; + + case PitEntry::kDeviceTypeAll: + Interface::Print("All (?)"); + break; + + default: + Interface::Print("Unknown"); + break; + } + + Interface::Print(")\n"); + + Interface::Print("Identifier: %d\n", entry->GetIdentifier()); + + Interface::Print("Attributes: %d (", entry->GetAttributes()); + + if (entry->GetAttributes() & PitEntry::kAttributeSTL) + Interface::Print("STL "); + + if (entry->GetAttributes() & PitEntry::kAttributeWrite) + Interface::Print("Read/Write"); else - Interface::Print("R"); + Interface::Print("Read-Only"); Interface::Print(")\n"); - Interface::Print("Unknown 1: %d\n", entry->GetUnknown1()); + Interface::Print("Update Attributes: %d", entry->GetUpdateAttributes()); + + if (entry->GetUpdateAttributes()) + { + Interface::Print(" ("); + + if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeFota) + { + if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeSecure) + Interface::Print("FOTA, Secure"); + else + Interface::Print("FOTA"); + } + else + { + if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeSecure) + Interface::Print("Secure"); + } + + Interface::Print(")\n"); + } + else + { + Interface::Print("\n"); + } - Interface::Print("Partition Block Size: %d\n", entry->GetPartitionBlockSize()); - Interface::Print("Partition Block Count: %d\n", entry->GetPartitionBlockCount()); + Interface::Print("Partition Block Size: %d\n", entry->GetBlockSize()); + Interface::Print("Partition Block Count: %d\n", entry->GetBlockCount()); - Interface::Print("Unknown 2: %d\n", entry->GetUnknown2()); - Interface::Print("Unknown 3: %d\n", entry->GetUnknown3()); + Interface::Print("File Offset (Obsolete): %d\n", entry->GetFileOffset()); + Interface::Print("File Size (Obsolete): %d\n", entry->GetFileSize()); Interface::Print("Partition Name: %s\n", entry->GetPartitionName()); - Interface::Print("Filename: %s\n", entry->GetFilename()); + Interface::Print("Flash Filename: %s\n", entry->GetFlashFilename()); + Interface::Print("FOTA Filename: %s\n", entry->GetFotaFilename()); } Interface::Print("\n"); } + +void Interface::SetStdoutErrors(bool enabled) +{ + stdoutErrors = enabled; +} diff --git a/heimdall/source/Interface.h b/heimdall/source/Interface.h index ddb7f55..ab4b538 100644 --- a/heimdall/source/Interface.h +++ b/heimdall/source/Interface.h @@ -28,276 +28,55 @@ // libpit #include "libpit.h" +// Heimdall +#include "Heimdall.h" + using namespace std; using namespace libpit; namespace Heimdall { - struct Action + namespace Interface { - public: - - string name; - - string *valueArguments; - string *valueShortArguments; - unsigned int valueArgumentCount; + typedef int (*ActionExecuteFunction)(int, char **); - string *valuelessArguments; - string *valuelessShortArguments; - unsigned int valuelessArgumentCount; + typedef struct ActionInfo + { + ActionExecuteFunction executeFunction; + const char *usage; - Action(const char *name, string *valueArguments, string *valueShortArguments, unsigned int valueArgumentCount, - string *valuelessArguments, string *valuelessShortArguments, unsigned int valuelessArgumentCount) + ActionInfo() { - this->name = name; - - this->valueArguments = valueArguments; - this->valueShortArguments = valueShortArguments; - this->valueArgumentCount = valueArgumentCount; - - this->valuelessArguments = valuelessArguments; - this->valuelessShortArguments = valuelessShortArguments; - this->valuelessArgumentCount = valuelessArgumentCount; + executeFunction = nullptr; + usage = nullptr; } - }; - - class Interface - { - public: - - // Actions - enum - { - kActionFlash = 0, - kActionClosePcScreen, - kActionDump, - kActionPrintPit, - kActionVersion, - kActionHelp, - kActionDetect, - kActionDownloadPit, - kActionInfo, - kActionCount - }; - // Flash value arguments - enum + ActionInfo(ActionExecuteFunction executeFunction, const char *usage) { - kFlashValueArgPit, - kFlashValueArgFactoryFs, - kFlashValueArgCache, - kFlashValueArgDatabaseData, - kFlashValueArgPrimaryBootloader, - kFlashValueArgSecondaryBootloader, - kFlashValueArgSecondaryBootloaderBackup, - kFlashValueArgParam, - kFlashValueArgKernel, - kFlashValueArgRecovery, - kFlashValueArgEfs, - kFlashValueArgModem, - - kFlashValueArgNormalBoot, - kFlashValueArgSystem, - kFlashValueArgUserData, - kFlashValueArgFota, - kFlashValueArgHidden, - kFlashValueArgMovinand, - kFlashValueArgData, - kFlashValueArgUms, - kFlashValueArgEmmc, - - kFlashValueArgPartitionIndex, - - kFlashValueArgCount - }; - - // Flash valueless arguments - enum - { - kFlashValuelessArgRepartition = 0, - - kFlashValuelessArgCount - }; - - // Close PC Screen value arguments - enum - { - kClosePcScreenValueArgCount = 0 - }; - - // Close PC Screen valueless arguments - enum - { - kClosePcScreenValuelessArgCount = 0 - }; - - // Dump value arguments - enum - { - kDumpValueArgChipType = 0, - kDumpValueArgChipId, - kDumpValueArgOutput, - - kDumpValueArgCount - }; - - // Dump valueless arguments - enum - { - kDumpValuelessArgCount = 0 - }; - - // Print PIT value arguments - enum - { - kPrintPitValueArgCount = 0 - }; - - // Print PIT valueless arguments - enum - { - kPrintPitValuelessArgCount = 0 - }; - - // Version value arguments - enum - { - kVersionValueArgCount = 0 - }; - - // Version valueless arguments - enum - { - kVersionValuelessArgCount = 0 - }; - - // Help value arguments - enum - { - kHelpValueArgCount = 0 - }; - - // Help valueless arguments - enum - { - kHelpValuelessArgCount = 0 - }; - - // Info value arguments - enum - { - kInfoValueArgCount = 0 - }; - - // Info valueless arguments - enum - { - kInfoValuelessArgCount = 0 - }; - - // Detect value arguments - enum - { - kDetectValueArgCount = 0 - }; - - // Detect valueless arguments - enum - { - kDetectValuelessArgCount = 0 - }; - - // Download PIT value arguments - enum - { - kDownloadPitValueArgOutput = 0, - kDownloadPitValueArgCount - }; - - // Download PIT valueless arguments - enum - { - kDownloadPitValuelessArgCount = 0 - }; - - // Common value arguments - enum - { - kCommonValueArgDelay = 0, - - kCommonValueArgCount - }; - - // Comon valueless arguments - enum - { - kCommonValuelessArgVerbose = 0, - kCommonValuelessArgNoReboot, - kCommonValuelessArgStdoutErrors, - - kCommonValuelessArgCount - }; - - private: - - static bool stdoutErrors; - - static const char *version; - static const char *usage; - static const char *releaseInfo; - static const char *extraInfo; - - // Flash arguments - static string flashValueArguments[kFlashValueArgCount]; - static string flashValueShortArguments[kFlashValueArgCount]; - - static string flashValuelessArguments[kFlashValuelessArgCount]; - static string flashValuelessShortArguments[kFlashValuelessArgCount]; - - // Download PIT arguments - static string downloadPitValueArguments[kDownloadPitValueArgCount]; - static string downloadPitValueShortArguments[kDownloadPitValueArgCount]; - - // Dump arguments - static string dumpValueArguments[kDumpValueArgCount]; - static string dumpValueShortArguments[kDumpValueArgCount]; - - public: - - // Common arguments - static string commonValueArguments[kCommonValueArgCount]; - static string commonValueShortArguments[kCommonValueArgCount]; - - static string commonValuelessArguments[kCommonValuelessArgCount]; - static string commonValuelessShortArguments[kCommonValuelessArgCount]; - - static Action actions[kActionCount]; + this->executeFunction = executeFunction; + this->usage = usage; + } - static bool GetArguments(int argc, char **argv, map& argumentMap, int *actionIndex); + } ActionInfo; - static void Print(const char *format, ...); - static void PrintError(const char *format, ...); - static void PrintErrorSameLine(const char *format, ...); + const map& GetActionMap(void); - static void PrintVersion(void); - static void PrintUsage(void); - static void PrintReleaseInfo(void); - static void PrintFullInfo(void); + void Print(const char *format, ...); + void PrintWarning(const char *format, ...); + void PrintWarningSameLine(const char *format, ...); + void PrintError(const char *format, ...); + void PrintErrorSameLine(const char *format, ...); - static void PrintDeviceDetectionFailed(void); + void PrintVersion(void); + void PrintUsage(void); + void PrintReleaseInfo(void); + void PrintFullInfo(void); - static void PrintPit(const PitData *pitData); + void PrintDeviceDetectionFailed(void); - static string& GetPitArgument(void) - { - return (flashValueArguments[kFlashValueArgPit]); - } + void PrintPit(const PitData *pitData); - static void SetStdoutErrors(bool enabled) - { - stdoutErrors = enabled; - } + void SetStdoutErrors(bool enabled); }; } diff --git a/heimdall/source/Packet.h b/heimdall/source/Packet.h index 41ce081..9f47e33 100644 --- a/heimdall/source/Packet.h +++ b/heimdall/source/Packet.h @@ -50,7 +50,7 @@ namespace Heimdall delete [] data; } - int GetSize(void) const + unsigned int GetSize(void) const { return (size); } diff --git a/heimdall/source/PrintPitAction.cpp b/heimdall/source/PrintPitAction.cpp new file mode 100644 index 0000000..c520d1c --- /dev/null +++ b/heimdall/source/PrintPitAction.cpp @@ -0,0 +1,159 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// C Standard Library +#include + +// Heimdall +#include "Arguments.h" +#include "BridgeManager.h" +#include "Heimdall.h" +#include "Interface.h" +#include "PrintPitAction.h" + +using namespace Heimdall; + +const char *PrintPitAction::usage = "Action: print-pit\n\ +Arguments: [--file ] [--verbose] [--no-reboot] [--stdout-errors]\n\ + [--delay ]\n\ +Description: Prints the contents of a PIT file in a human readable format. If\n\ + a filename is not provided then Heimdall retrieves the PIT file from the \n\ + connected device.\n"; + +int PrintPitAction::Execute(int argc, char **argv) +{ + // Handle arguments + + map argumentTypes; + argumentTypes["file"] = kArgumentTypeString; + argumentTypes["no-reboot"] = kArgumentTypeFlag; + argumentTypes["delay"] = kArgumentTypeUnsignedInteger; + argumentTypes["verbose"] = kArgumentTypeFlag; + argumentTypes["stdout-errors"] = kArgumentTypeFlag; + + Arguments arguments(argumentTypes); + + if (!arguments.ParseArguments(argc, argv, 2)) + { + Interface::Print(PrintPitAction::usage); + return (0); + } + + const StringArgument *fileArgument = static_cast(arguments.GetArgument("file")); + const UnsignedIntegerArgument *communicationDelayArgument = static_cast(arguments.GetArgument("delay")); + + bool reboot = arguments.GetArgument("no-reboot") == nullptr; + bool verbose = arguments.GetArgument("verbose") != nullptr; + + if (arguments.GetArgument("stdout-errors") != nullptr) + Interface::SetStdoutErrors(true); + + // Open file (if specified). + + FILE *localPitFile = nullptr; + + if (fileArgument) + { + const char *filename = fileArgument->GetValue().c_str(); + + localPitFile = fopen(filename, "rb"); + + if (!localPitFile) + { + Interface::PrintError("Failed to open file \"%s\"\n", filename); + return (1); + } + } + + // Info + + Interface::PrintReleaseInfo(); + Sleep(1000); + + if (localPitFile) + { + // Print PIT from file; there's no need for a BridgeManager. + + fseek(localPitFile, 0, SEEK_END); + long localPitFileSize = ftell(localPitFile); + rewind(localPitFile); + + // Load the local pit file into memory. + unsigned char *pitFileBuffer = new unsigned char[localPitFileSize]; + size_t dataRead = fread(pitFileBuffer, 1, localPitFileSize, localPitFile); // dataRead is discarded, it's here to remove warnings. + fclose(localPitFile); + + PitData *pitData = new PitData(); + pitData->Unpack(pitFileBuffer); + + delete [] pitFileBuffer; + + Interface::PrintPit(pitData); + delete pitData; + + return (0); + } + else + { + // Print PIT from a device. + + int communicationDelay = BridgeManager::kCommunicationDelayDefault; + + if (communicationDelayArgument) + communicationDelay = communicationDelayArgument->GetValue(); + + BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); + + if (bridgeManager->Initialise() != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) + { + delete bridgeManager; + return (1); + } + + unsigned char *devicePit; + bool success = bridgeManager->DownloadPitFile(&devicePit) != 0; + + if (success) + { + PitData *pitData = new PitData(); + + if (pitData->Unpack(devicePit)) + { + Interface::PrintPit(pitData); + } + else + { + Interface::PrintError("Failed to unpack device's PIT file!\n"); + success = false; + } + + delete pitData; + } + + delete [] devicePit; + + if (!bridgeManager->EndSession(reboot)) + success = false; + + delete bridgeManager; + + return (success ? 0 : 1); + } +} diff --git a/heimdall/source/PrintPitAction.h b/heimdall/source/PrintPitAction.h new file mode 100644 index 0000000..5bbfd3d --- /dev/null +++ b/heimdall/source/PrintPitAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef PRINTPITACTION_H +#define PRINTPITACTION_H + +namespace Heimdall +{ + namespace PrintPitAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/ResponsePacket.h b/heimdall/source/ResponsePacket.h index e8678cf..9c5c2cc 100644 --- a/heimdall/source/ResponsePacket.h +++ b/heimdall/source/ResponsePacket.h @@ -32,11 +32,11 @@ namespace Heimdall enum { - kResponseTypeSendFilePart = 0x00, - kResponseTypeBeginSession = 0x64, - kResponseTypePitFile = 0x65, - kResponseTypeFileTransfer = 0x66, - kResponseTypeEndSession = 0x67 + kResponseTypeSendFilePart = 0x00, + kResponseTypeSessionSetup = 0x64, + kResponseTypePitFile = 0x65, + kResponseTypeFileTransfer = 0x66, + kResponseTypeEndSession = 0x67 }; private: diff --git a/heimdall/source/SendFilePartPacket.h b/heimdall/source/SendFilePartPacket.h index 8f6e27f..066aacb 100644 --- a/heimdall/source/SendFilePartPacket.h +++ b/heimdall/source/SendFilePartPacket.h @@ -34,12 +34,7 @@ namespace Heimdall { public: - enum - { - kDefaultPacketSize = 131072 - }; - - SendFilePartPacket(FILE *file, int size = SendFilePartPacket::kDefaultPacketSize) : OutboundPacket(size) + SendFilePartPacket(FILE *file, int size) : OutboundPacket(size) { memset(data, 0, size); diff --git a/heimdall/source/SessionSetupPacket.h b/heimdall/source/SessionSetupPacket.h new file mode 100644 index 0000000..9a91684 --- /dev/null +++ b/heimdall/source/SessionSetupPacket.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef SESSIONSETUPPACKET_H +#define SESSIONSETUPPACKET_H + +// Heimdall +#include "ControlPacket.h" + +namespace Heimdall +{ + class SessionSetupPacket : public ControlPacket + { + public: + + enum + { + kBeginSession = 0, + kDeviceType = 1, // ? + kTotalBytes = 2, + //kEnableSomeSortOfFlag = 3, + kFilePartSize = 5 + }; + + private: + + unsigned int request; + + protected: + + enum + { + kDataSize = ControlPacket::kDataSize + 4 + }; + + public: + + SessionSetupPacket(unsigned int request) : ControlPacket(ControlPacket::kControlTypeSession) + { + this->request = request; + } + + unsigned int GetRequest(void) const + { + return (request); + } + + void Pack(void) + { + ControlPacket::Pack(); + + PackInteger(ControlPacket::kDataSize, request); + } + }; +} + +#endif diff --git a/heimdall/source/SessionSetupResponse.h b/heimdall/source/SessionSetupResponse.h new file mode 100644 index 0000000..f7c1175 --- /dev/null +++ b/heimdall/source/SessionSetupResponse.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef SESSIONSETUPRESPONSE_H +#define SESSIONSETUPRESPONSE_H + +// Heimdall +#include "ResponsePacket.h" + +namespace Heimdall +{ + class SessionSetupResponse : public ResponsePacket + { + private: + + unsigned int result; + + public: + + SessionSetupResponse() : ResponsePacket(ResponsePacket::kResponseTypeSessionSetup) + { + } + + unsigned int GetResult(void) const + { + return (result); + } + + bool Unpack(void) + { + if (!ResponsePacket::Unpack()) + return (false); + + result = UnpackInteger(ResponsePacket::kDataSize); + + return (true); + } + }; +} + +#endif diff --git a/heimdall/source/TotalBytesPacket.h b/heimdall/source/TotalBytesPacket.h new file mode 100644 index 0000000..60190ec --- /dev/null +++ b/heimdall/source/TotalBytesPacket.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef TOTALBYTESSPACKET_H +#define TOTALBYTESPACKET_H + +// Heimdall +#include "SessionSetupPacket.h" + +namespace Heimdall +{ + class TotalBytesPacket : public SessionSetupPacket + { + private: + + unsigned int totalBytes; + + public: + + TotalBytesPacket(unsigned int totalBytes) : SessionSetupPacket(SessionSetupPacket::kTotalBytes) + { + this->totalBytes = totalBytes; + } + + unsigned int GetTotalBytes(void) const + { + return (totalBytes); + } + + void Pack(void) + { + SessionSetupPacket::Pack(); + + PackInteger(SessionSetupPacket::kDataSize, totalBytes); + } + }; +} + +#endif diff --git a/heimdall/source/Utility.cpp b/heimdall/source/Utility.cpp new file mode 100644 index 0000000..1dba264 --- /dev/null +++ b/heimdall/source/Utility.cpp @@ -0,0 +1,82 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// C/C++ Standard Library +#include +#include +#include + +// Heimdall +#include "Heimdall.h" +#include "Utility.h" + +using namespace Heimdall; + +NumberParsingStatus Utility::ParseInt(int &intValue, const char *string, int base) +{ + errno = 0; + + char *end; + long longValue = strtol(string, &end, base); + + if (*string == '\0' || *end != '\0') + { + return (kNumberParsingStatusInconvertible); + } + else if (errno == ERANGE) + { + intValue = (longValue == LONG_MAX) ? INT_MAX : INT_MIN; + return (kNumberParsingStatusRangeError); + } + else if (longValue > INT_MAX) + { + intValue = INT_MAX; + return (kNumberParsingStatusRangeError); + } + else if (longValue < INT_MIN) + { + intValue = INT_MIN; + return (kNumberParsingStatusRangeError); + } + + intValue = longValue; + return (kNumberParsingStatusSuccess); +} + +NumberParsingStatus Utility::ParseUnsignedInt(unsigned int &uintValue, const char *string, int base) +{ + errno = 0; + + char *end; + unsigned long ulongValue = strtoul(string, &end, base); + + if (*string == '\0' || *end != '\0') + { + return kNumberParsingStatusInconvertible; + } + else if (errno == ERANGE || ulongValue > INT_MAX) + { + uintValue = UINT_MAX; + return (kNumberParsingStatusRangeError); + } + + uintValue = ulongValue; + return (kNumberParsingStatusSuccess); +} diff --git a/heimdall/source/Utility.h b/heimdall/source/Utility.h new file mode 100644 index 0000000..4d3f3e4 --- /dev/null +++ b/heimdall/source/Utility.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef UTILITY_H +#define UTILITY_H + +namespace Heimdall +{ + typedef enum + { + kNumberParsingStatusSuccess = 0, + kNumberParsingStatusRangeError, + kNumberParsingStatusInconvertible + } NumberParsingStatus; + + namespace Utility + { + NumberParsingStatus ParseInt(int &intValue, const char *string, int base = 0); + NumberParsingStatus ParseUnsignedInt(unsigned int &uintValue, const char *string, int base = 0); + } +} + +#endif diff --git a/heimdall/source/VersionAction.cpp b/heimdall/source/VersionAction.cpp new file mode 100644 index 0000000..d76c1da --- /dev/null +++ b/heimdall/source/VersionAction.cpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +// Heimdall +#include "Heimdall.h" +#include "Interface.h" +#include "VersionAction.h" + +using namespace Heimdall; + +const char *VersionAction::usage = "Action: version\n\ +Description: Displays the version number of this binary.\n"; + +int VersionAction::Execute(int argc, char **argv) +{ + Interface::PrintVersion(); + return (0); +} diff --git a/heimdall/source/VersionAction.h b/heimdall/source/VersionAction.h new file mode 100644 index 0000000..fa9035c --- /dev/null +++ b/heimdall/source/VersionAction.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.*/ + +#ifndef VERSIONACTION_H +#define VERSIONACTION_H + +namespace Heimdall +{ + namespace VersionAction + { + extern const char *usage; + + int Execute(int argc, char **argv); + }; +} + +#endif diff --git a/heimdall/source/main.cpp b/heimdall/source/main.cpp index db7de1c..41d7a3c 100644 --- a/heimdall/source/main.cpp +++ b/heimdall/source/main.cpp @@ -28,782 +28,28 @@ #include "libpit.h" // Heimdall -#include "BridgeManager.h" -#include "SetupSessionPacket.h" -#include "SetupSessionResponse.h" -#include "EndModemFileTransferPacket.h" -#include "EndPhoneFileTransferPacket.h" +#include "Heimdall.h" +#include "HelpAction.h" #include "Interface.h" using namespace std; using namespace Heimdall; -// Known partitions -enum -{ - kKnownPartitionPit = 0, - kKnownPartitionFactoryFs, - kKnownPartitionCache, - kKnownPartitionDatabaseData, - kKnownPartitionPrimaryBootloader, - kKnownPartitionSecondaryBootloader, - kKnownPartitionSecondaryBootloaderBackup, - kKnownPartitionParam, - kKnownPartitionKernel, - kKnownPartitionRecovery, - kKnownPartitionEfs, - kKnownPartitionModem, - - kKnownPartitionNormalBoot, - kKnownPartitionSystem, - kKnownPartitionUserData, - kKnownPartitionFota, - kKnownPartitionHidden, - kKnownPartitionMovinand, - kKnownPartitionData, - kKnownPartitionUms, - kKnownPartitionEmmc, - - kKnownPartitionCount -}; - -vector knownPartitionNames[kKnownPartitionCount]; - -struct PartitionInfo -{ - unsigned int chipIdentifier; - string partitionName; - FILE *file; - - PartitionInfo(unsigned int chipIdentifier, const char *partitionName, FILE *file) - { - this->chipIdentifier = chipIdentifier; - this->partitionName = partitionName; - this->file = file; - } -}; - -void initialiseKnownPartitionNames(void) -{ - knownPartitionNames[kKnownPartitionPit].push_back("PIT"); - knownPartitionNames[kKnownPartitionFactoryFs].push_back("FACTORYFS"); - knownPartitionNames[kKnownPartitionCache].push_back("CACHE"); - knownPartitionNames[kKnownPartitionDatabaseData].push_back("DBDATAFS"); - - knownPartitionNames[kKnownPartitionPrimaryBootloader].push_back("IBL+PBL"); - knownPartitionNames[kKnownPartitionPrimaryBootloader].push_back("BOOT"); - - knownPartitionNames[kKnownPartitionSecondaryBootloader].push_back("SBL"); - knownPartitionNames[kKnownPartitionSecondaryBootloader].push_back("SBL1"); - - knownPartitionNames[kKnownPartitionSecondaryBootloaderBackup].push_back("SBL2"); - knownPartitionNames[kKnownPartitionParam].push_back("PARAM"); - knownPartitionNames[kKnownPartitionKernel].push_back("KERNEL"); - knownPartitionNames[kKnownPartitionRecovery].push_back("RECOVERY"); - knownPartitionNames[kKnownPartitionEfs].push_back("EFS"); - knownPartitionNames[kKnownPartitionModem].push_back("MODEM"); - - knownPartitionNames[kKnownPartitionNormalBoot].push_back("NORMALBOOT"); - knownPartitionNames[kKnownPartitionSystem].push_back("SYSTEM"); - knownPartitionNames[kKnownPartitionUserData].push_back("USERDATA"); - knownPartitionNames[kKnownPartitionFota].push_back("FOTA"); - knownPartitionNames[kKnownPartitionHidden].push_back("HIDDEN"); - knownPartitionNames[kKnownPartitionMovinand].push_back("MOVINAND"); - knownPartitionNames[kKnownPartitionData].push_back("DATAFS"); - knownPartitionNames[kKnownPartitionUms].push_back("UMS.EN"); - knownPartitionNames[kKnownPartitionEmmc].push_back("GANG"); -} - -bool isKnownPartition(const char *partitionName, unsigned int knownPartitionIndex) -{ - for (unsigned int i = 0; i < knownPartitionNames[knownPartitionIndex].size(); i++) - { - if (strcmp(partitionName, knownPartitionNames[knownPartitionIndex][i]) == 0) - return (true); - } - - return (false); -} - -bool isKnownBootPartition(const char *partitionName) -{ - return (isKnownPartition(partitionName, kKnownPartitionPrimaryBootloader) || - isKnownPartition(partitionName, kKnownPartitionSecondaryBootloader) || - isKnownPartition(partitionName, kKnownPartitionSecondaryBootloaderBackup) || - isKnownPartition(partitionName, kKnownPartitionParam) || - isKnownPartition(partitionName, kKnownPartitionNormalBoot)); -} - -bool openFiles(const map& argumentMap, map& argumentFileMap) -{ - map::const_iterator it = argumentMap.begin(); - - for (it = argumentMap.begin(); it != argumentMap.end(); it++) - { - bool isFileArgument = false; - - int partitionIndex = atoi(it->first.substr(it->first.find_first_not_of('-')).c_str()); - - // Was the argument a partition index? - if (partitionIndex > 0 || it->first.compare("-0") == 0) - { - isFileArgument = true; - } - else - { - // The argument wasn't a partition index, check if it's a known partition name. - for (int knownPartition = 0; knownPartition < kKnownPartitionCount; knownPartition++) - { - if (it->first == Interface::actions[Interface::kActionFlash].valueArguments[knownPartition]) - { - isFileArgument = true; - break; - } - } - } - - if (!isFileArgument) - continue; - - pair argumentFilePair; - argumentFilePair.first = it->first; - argumentFilePair.second = fopen(it->second.c_str(), "rb"); - - if (!argumentFilePair.second) - { - Interface::PrintError("Failed to open file \"%s\"\n", it->second.c_str()); - return (false); - } - - argumentFileMap.insert(argumentFilePair); - } - - return (true); -} - -bool mapFilesToPartitions(const map& argumentFileMap, const PitData *pitData, map& partitionInfoMap) -{ - map::const_iterator it = argumentFileMap.begin(); - - for (it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) - { - int partitionIndex = atoi(it->first.substr(it->first.find_first_not_of('-')).c_str()); - - const PitEntry *pitEntry = nullptr; - - // Was the argument a partition index? - if (partitionIndex > 0 || it->first.compare("-0") == 0) - { - pitEntry = pitData->FindEntry(partitionIndex); - } - else - { - // The argument wasn't a partition index, so it must be a known partition name. - int knownPartition; - - for (knownPartition = 0; knownPartition < kKnownPartitionCount; knownPartition++) - { - if (it->first == Interface::actions[Interface::kActionFlash].valueArguments[knownPartition]) - break; - } - - // Check for the partition in the PIT file using all known names. - for (unsigned int i = 0; i < knownPartitionNames[knownPartition].size(); i++) - { - pitEntry = pitData->FindEntry(knownPartitionNames[knownPartition][i]); - - if (pitEntry) - break; - } - - if (!pitEntry && knownPartition == kKnownPartitionPit) - { - // NOTE: We're assuming a PIT file always has chipIdentifier zero. - PartitionInfo partitionInfo(0, knownPartitionNames[kKnownPartitionPit][0], it->second); - partitionInfoMap.insert(pair(0xFFFFFFFF, partitionInfo)); - - return (true); - } - } - - if (!pitEntry) - { - Interface::PrintError("Partition corresponding to %s argument could not be located\n", it->first.c_str()); - return (false); - } - - PartitionInfo partitionInfo(pitEntry->GetChipIdentifier(), pitEntry->GetPartitionName(), it->second); - partitionInfoMap.insert(pair(pitEntry->GetPartitionIdentifier(), partitionInfo)); - } - - return (true); -} - -void closeFiles(map argumentfileMap) -{ - for (map::iterator it = argumentfileMap.begin(); it != argumentfileMap.end(); it++) - fclose(it->second); - - argumentfileMap.clear(); -} - -int downloadPitFile(BridgeManager *bridgeManager, unsigned char **pitBuffer) -{ - Interface::Print("Downloading device's PIT file...\n"); - - int devicePitFileSize = bridgeManager->ReceivePitFile(pitBuffer); - - if (!*pitBuffer) - { - Interface::PrintError("Failed to download PIT file!\n"); - - return (-1); - } - - Interface::Print("PIT file download sucessful\n\n"); - return (devicePitFileSize); -} - -bool flashFile(BridgeManager *bridgeManager, unsigned int chipIdentifier, unsigned int partitionIndex, const char *partitionName, FILE *file) -{ - // PIT files need to be handled differently, try determine if the partition we're flashing to is a PIT partition. - bool isPit = false; - - for (unsigned int i = 0; i < knownPartitionNames[kKnownPartitionPit].size(); i++) - { - if (strcmp(partitionName, knownPartitionNames[kKnownPartitionPit][i]) == 0) - { - isPit = true; - break; - } - } - - if (isPit) - { - Interface::Print("Uploading %s\n", partitionName); - - if (bridgeManager->SendPitFile(file)) - { - Interface::Print("%s upload successful\n", partitionName); - return (true); - } - else - { - Interface::Print("%s upload failed!\n", partitionName); - return (false); - } - } - else - { - // Modems need to be handled differently, try determine if the partition we're flashing to is a modem partition. - bool isModem = false; - - for (unsigned int i = 0; i < knownPartitionNames[kKnownPartitionModem].size(); i++) - { - if (strcmp(partitionName, knownPartitionNames[kKnownPartitionModem][i]) == 0) - { - isModem = true; - break; - } - } - - if (isModem) - { - Interface::Print("Uploading %s\n", partitionName); - - //if (bridgeManager->SendFile(file, EndPhoneFileTransferPacket::kDestinationPhone, // <-- Kies method. WARNING: Doesn't work on Galaxy Tab! - // EndPhoneFileTransferPacket::kFileModem)) - if (bridgeManager->SendFile(file, EndModemFileTransferPacket::kDestinationModem, chipIdentifier)) // <-- Odin method - { - Interface::Print("%s upload successful\n", partitionName); - return (true); - } - else - { - Interface::Print("%s upload failed!\n", partitionName); - return (false); - } - } - else - { - // We're uploading to a phone partition - Interface::Print("Uploading %s\n", partitionName); - - if (bridgeManager->SendFile(file, EndPhoneFileTransferPacket::kDestinationPhone, chipIdentifier, partitionIndex)) - { - Interface::Print("%s upload successful\n", partitionName); - return (true); - } - else - { - Interface::Print("%s upload failed!\n", partitionName); - return (false); - } - } - } - - return (true); -} - -bool attemptFlash(BridgeManager *bridgeManager, map argumentFileMap, bool repartition) -{ - bool success; - - // ------------- SEND TOTAL BYTES TO BE TRANSFERRED ------------- - - int totalBytes = 0; - for (map::const_iterator it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) - { - if (repartition || it->first != Interface::GetPitArgument()) - { - fseek(it->second, 0, SEEK_END); - totalBytes += ftell(it->second); - rewind(it->second); - } - } - - SetupSessionPacket *deviceInfoPacket = new SetupSessionPacket(SetupSessionPacket::kTotalBytes, totalBytes); - success = bridgeManager->SendPacket(deviceInfoPacket); - delete deviceInfoPacket; - - if (!success) - { - Interface::PrintError("Failed to send total bytes device info packet!\n"); - return (false); - } - - SetupSessionResponse *deviceInfoResponse = new SetupSessionResponse(); - success = bridgeManager->ReceivePacket(deviceInfoResponse); - int deviceInfoResult = deviceInfoResponse->GetUnknown(); - delete deviceInfoResponse; - - if (!success) - { - Interface::PrintError("Failed to receive device info response!\n"); - return (false); - } - - if (deviceInfoResult != 0) - { - Interface::PrintError("Unexpected device info response!\nExpected: 0\nReceived:%d\n", deviceInfoResult); - return (false); - } - - // ----------------------------------------------------- - - PitData *pitData; - PitData *localPitData = nullptr; - - FILE *localPitFile = nullptr; - - // If a PIT file was passed as an argument then we must unpack it. - map::iterator it = argumentFileMap.find(Interface::actions[Interface::kActionFlash].valueArguments[Interface::kFlashValueArgPit]); - - if (it != argumentFileMap.end()) - { - localPitFile = it->second; - - // Load the local pit file into memory. - unsigned char *pitFileBuffer = new unsigned char[4096]; - memset(pitFileBuffer, 0, 4096); - - fseek(localPitFile, 0, SEEK_END); - long localPitFileSize = ftell(localPitFile); - rewind(localPitFile); - - // dataRead is discarded, it's here to remove warnings. - int dataRead = fread(pitFileBuffer, 1, localPitFileSize, localPitFile); - rewind(localPitFile); - - localPitData = new PitData(); - localPitData->Unpack(pitFileBuffer); - - delete [] pitFileBuffer; - } - - if (repartition) - { - // Use the local PIT file data. - pitData = localPitData; - } - else - { - // If we're not repartitioning then we need to retrieve the device's PIT file and unpack it. - unsigned char *pitFileBuffer; - downloadPitFile(bridgeManager, &pitFileBuffer); - - pitData = new PitData(); - pitData->Unpack(pitFileBuffer); - - delete [] pitFileBuffer; - - if (localPitData != nullptr) - { - // The user has specified a PIT without repartitioning, we should verify the local and device PIT data match! - bool pitsMatch = pitData->Matches(localPitData); - delete localPitData; - - if (!pitsMatch) - { - Interface::Print("Local and device PIT files don't match and repartition wasn't specified!\n"); - Interface::PrintError("Flash aborted!\n"); - - delete pitData; - return (false); - } - } - } - - map partitionInfoMap; - - // Map the files being flashed to partitions stored in the PIT file. - if (!mapFilesToPartitions(argumentFileMap, pitData, partitionInfoMap)) - { - delete pitData; - return (false); - } - - delete pitData; - - // If we're repartitioning then we need to flash the PIT file first. - if (repartition) - { - for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) - { - if (it->second.file == localPitFile) - { - PartitionInfo *partitionInfo = &(it->second); - - if (!flashFile(bridgeManager, partitionInfo->chipIdentifier, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) - return (false); - - break; - } - } - } - - // Flash partitions not involved in the boot process second. - for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) - { - if (!isKnownPartition(it->second.partitionName.c_str(), kKnownPartitionPit) && !isKnownBootPartition(it->second.partitionName.c_str())) - { - PartitionInfo *partitionInfo = &(it->second); - - if (!flashFile(bridgeManager, partitionInfo->chipIdentifier, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) - return (false); - } - } - - // Flash boot partitions last. - for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) - { - if (isKnownBootPartition(it->second.partitionName.c_str())) - { - PartitionInfo *partitionInfo = &(it->second); - - if (!flashFile(bridgeManager, partitionInfo->chipIdentifier, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) - return (false); - } - } - - return (true); -} - int main(int argc, char **argv) { - map argumentMap; - int actionIndex; - - if (!Interface::GetArguments(argc, argv, argumentMap, &actionIndex)) + if (argc < 2) { - Sleep(250); + Interface::PrintUsage(); return (0); } - initialiseKnownPartitionNames(); - - switch (actionIndex) - { - case Interface::kActionFlash: - if (argumentMap.find(Interface::actions[Interface::kActionFlash].valuelessArguments[Interface::kFlashValuelessArgRepartition]) != argumentMap.end() - && argumentMap.find(Interface::actions[Interface::kActionFlash].valueArguments[Interface::kFlashValueArgPit]) == argumentMap.end()) - { - Interface::Print("If you wish to repartition then a PIT file must be specified.\n\n"); - Interface::PrintUsage(); - return (0); - } - - break; - - case Interface::kActionDownloadPit: - if (argumentMap.find(Interface::actions[Interface::kActionDownloadPit].valueArguments[Interface::kDownloadPitValueArgOutput]) == argumentMap.end()) - { - Interface::Print("Output file was not specified.\n\n"); - Interface::PrintUsage(); - return (0); - } - - break; - - case Interface::kActionDump: - { - if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgOutput]) == argumentMap.end()) - { - Interface::Print("Output file was not specified.\n\n"); - Interface::PrintUsage(); - return (0); - } - - if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType]) == argumentMap.end()) - { - Interface::Print("You must specify a chip type.\n\n"); - Interface::PrintUsage(); - return (0); - } - - string chipType = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType])->second; - if (!(chipType == "RAM" || chipType == "ram" || chipType == "NAND" || chipType == "nand")) - { - Interface::Print("Unknown chip type: %s.\n\n", chipType.c_str()); - Interface::PrintUsage(); - return (0); - } - - if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId]) == argumentMap.end()) - { - Interface::Print("You must specify a Chip ID.\n\n"); - Interface::PrintUsage(); - return (0); - } - - int chipId = atoi(argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId])->second.c_str()); - if (chipId < 0) - { - Interface::Print("Chip ID must be a non-negative integer.\n"); - Interface::PrintUsage(); - return (0); - } - - break; - } - - case Interface::kActionVersion: - Interface::PrintVersion(); - return (0); - - case Interface::kActionHelp: - Interface::PrintUsage(); - return (0); - - case Interface::kActionInfo: - Interface::PrintFullInfo(); - return (0); - } - - bool verbose = argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgVerbose]) != argumentMap.end(); - bool reboot = argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgNoReboot]) == argumentMap.end(); - - Interface::SetStdoutErrors(argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgStdoutErrors]) != argumentMap.end()); - - int communicationDelay = BridgeManager::kCommunicationDelayDefault; + int result = 0; + map::const_iterator actionIt = Interface::GetActionMap().find(argv[1]); - if (argumentMap.find(Interface::commonValueArguments[Interface::kCommonValueArgDelay]) != argumentMap.end()) - communicationDelay = atoi(argumentMap.find(Interface::commonValueArguments[Interface::kCommonValueArgDelay])->second.c_str()); - - BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); - - if (actionIndex == Interface::kActionDetect) - { - bool detected = bridgeManager->DetectDevice(); - delete bridgeManager; - - return ((detected) ? 0 : 1); - } - - Interface::PrintReleaseInfo(); - Sleep(1000); - - int initialiseResult = bridgeManager->Initialise(); - - if (initialiseResult != 0) - { - delete bridgeManager; - return ((initialiseResult == BridgeManager::kInitialiseDeviceNotDetected) ? 1 : 0); - } - - bool success; - - switch (actionIndex) - { - case Interface::kActionFlash: - { - map argumentFileMap; - - // We open the files before doing anything else to ensure they exist. - if (!openFiles(argumentMap, argumentFileMap)) - { - closeFiles(argumentFileMap); - delete bridgeManager; - - return (0); - } - - if (!bridgeManager->BeginSession()) - { - closeFiles(argumentFileMap); - delete bridgeManager; - - return (-1); - } - - bool repartition = argumentMap.find(Interface::actions[Interface::kActionFlash].valuelessArguments[Interface::kFlashValuelessArgRepartition]) != argumentMap.end(); - success = attemptFlash(bridgeManager, argumentFileMap, repartition); - - success = bridgeManager->EndSession(reboot) && success; - - closeFiles(argumentFileMap); - - break; - } - - case Interface::kActionClosePcScreen: - { - if (!bridgeManager->BeginSession()) - { - delete bridgeManager; - return (-1); - } - - Interface::Print("Attempting to close connect to pc screen...\n"); - - success = bridgeManager->EndSession(reboot); - - if (success) - Interface::Print("Attempt complete\n"); - - break; - } - - case Interface::kActionDownloadPit: - { - map::const_iterator it = argumentMap.find(Interface::actions[Interface::kActionDownloadPit].valueArguments[Interface::kDownloadPitValueArgOutput]); - FILE *outputPitFile = fopen(it->second.c_str(), "wb"); - - if (!outputPitFile) - { - delete bridgeManager; - return (0); - } - - if (!bridgeManager->BeginSession()) - { - delete bridgeManager; - fclose(outputPitFile); - return (-1); - } - - unsigned char *pitBuffer; - int fileSize = downloadPitFile(bridgeManager, &pitBuffer); - - if (fileSize > 0) - { - success = fwrite(pitBuffer, 1, fileSize, outputPitFile) == fileSize; - fclose(outputPitFile); - - if (!success) - Interface::PrintError("Failed to write PIT data to output file.\n"); - - success = bridgeManager->EndSession(reboot) && success; - } - else - { - fclose(outputPitFile); - success = false; - bridgeManager->EndSession(reboot); - } - - delete [] pitBuffer; - - break; - } - - case Interface::kActionDump: - { - const char *outputFilename = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgOutput])->second.c_str(); - FILE *dumpFile = fopen(outputFilename, "wb"); - if (!dumpFile) - { - Interface::PrintError("Failed to open file \"%s\"\n", outputFilename); - - delete bridgeManager; - return (-1); - } - - int chipType = 0; - string chipTypeName = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType])->second; - if (chipTypeName == "NAND" || chipTypeName == "nand") - chipType = 1; - - int chipId = atoi(argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId])->second.c_str()); - - if (!bridgeManager->BeginSession()) - { - fclose(dumpFile); - - delete bridgeManager; - return (-1); - } - - success = bridgeManager->ReceiveDump(chipType, chipId, dumpFile); - - fclose(dumpFile); - - success = bridgeManager->EndSession(reboot) && success; - - break; - } - - case Interface::kActionPrintPit: - { - if (!bridgeManager->BeginSession()) - { - delete bridgeManager; - return (-1); - } - - unsigned char *devicePit; - - if (downloadPitFile(bridgeManager, &devicePit) < -1) - { - bridgeManager->EndSession(reboot); - - delete bridgeManager; - return (-1); - } - - PitData *pitData = new PitData(); - - if (pitData->Unpack(devicePit)) - { - Interface::PrintPit(pitData); - success = true; - } - else - { - Interface::PrintError("Failed to unpack device's PIT file!\n"); - success = false; - } - - delete [] devicePit; - delete pitData; - - success = bridgeManager->EndSession(reboot) && success; - - break; - } - } - - delete bridgeManager; - - return ((success) ? 0 : -1); + if (actionIt != Interface::GetActionMap().end()) + result = actionIt->second.executeFunction(argc, argv); + else + result = HelpAction::Execute(argc, argv); + + return (result); } -- cgit v1.2.3