// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda // // 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. using System; using System.IO; namespace WPinternals { internal enum FlashUnit { Bytes, Sectors } internal class QualcommFlasher { private QualcommSerial Serial; public QualcommFlasher(QualcommSerial Serial) { this.Serial = Serial; } public void CloseSerial() { Serial.Close(); } public void Hello() { byte[] Command = new byte[] { 0x01, // Hello command 0x51, 0x43, 0x4F, 0x4D, 0x20, 0x66, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6F, 0x77, 0x6E, 0x6C, 0x6F, // "QCOM fast download protocol host" 0x61, 0x64, 0x20, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x63, 0x6F, 0x6C, 0x20, 0x68, 0x6F, 0x73, 0x74, 0x02, 0x02, // Protocol version - Must be at least 0x02 0x01 }; Serial.SendCommand(Command, new byte[] { 0x02 }); } public void SetSecurityMode(byte Mode) { byte[] Command = new byte[2]; Command[0] = 0x17; Command[1] = Mode; Serial.SendCommand(Command, new byte[] { 0x18 }); } // Use PartitionID 0x21 public void OpenPartition(byte PartitionID) { byte[] Command = new byte[2]; Command[0] = 0x1B; Command[1] = PartitionID; Serial.SendCommand(Command, new byte[] { 0x1C }); } public void ClosePartition() { Serial.SendCommand(new byte[] { 0x15 }, new byte[] { 0x16 }); } public void Flash(UInt32 StartInBytes, Stream Data, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, null, null, LengthInBytes); } public void Flash(UInt32 StartInBytes, Stream Data, Action ProgressUpdateCallback, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, ProgressUpdateCallback, null, LengthInBytes); } public void Flash(UInt32 StartInBytes, Stream Data, ProgressUpdater UpdaterPerSector, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, null, UpdaterPerSector, LengthInBytes); } public void Flash(UInt32 StartInBytes, Stream Data, Action ProgressUpdateCallback, ProgressUpdater UpdaterPerSector, UInt32 LengthInBytes = UInt32.MaxValue) { long Remaining; if ((LengthInBytes == UInt32.MaxValue) || (LengthInBytes > (Data.Length - Data.Position))) Remaining = Data.Length - Data.Position; else Remaining = LengthInBytes; UInt32 CurrentLength; byte[] Buffer = new byte[0x405]; byte[] ResponsePattern = new byte[5]; byte[] FinalCommand; Buffer[0] = 0x07; ResponsePattern[0] = 0x08; UInt32 CurrentPosition = StartInBytes; ProgressUpdater Progress = UpdaterPerSector; if ((Progress == null) && (ProgressUpdateCallback != null)) Progress = new ProgressUpdater(GetSectorCount((UInt64)Remaining), ProgressUpdateCallback); while (Remaining > 0) { System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, Buffer, 1, 4); // Start is in bytes and in Little Endian (on Samsung devices start is in sectors!) System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, ResponsePattern, 1, 4); // Start is in bytes and in Little Endian (on Samsung devices start is in sectors!) if (Remaining >= 0x400) CurrentLength = 0x400; else CurrentLength = (UInt32)Remaining; CurrentLength = (uint)Data.Read(Buffer, 5, (int)CurrentLength); if (CurrentLength < 0x400) { FinalCommand = new byte[CurrentLength + 5]; System.Buffer.BlockCopy(Buffer, 0, FinalCommand, 0, (int)CurrentLength + 5); } else FinalCommand = Buffer; Serial.SendCommand(FinalCommand, ResponsePattern); CurrentPosition += CurrentLength; Remaining -= CurrentLength; if (Progress != null) Progress.IncreaseProgress(GetSectorCount(CurrentLength)); } } public void Flash(UInt32 StartInBytes, byte[] Data, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, null, null, OffsetInBytes, LengthInBytes); } public void Flash(UInt32 StartInBytes, byte[] Data, Action ProgressUpdateCallback, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, ProgressUpdateCallback, null, OffsetInBytes, LengthInBytes); } public void Flash(UInt32 StartInBytes, byte[] Data, ProgressUpdater UpdaterPerSector, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue) { Flash(StartInBytes, Data, null, UpdaterPerSector, OffsetInBytes, LengthInBytes); } public void Flash(UInt32 StartInBytes, byte[] Data, Action ProgressUpdateCallback, ProgressUpdater UpdaterPerSector, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue) { long RemainingBytes; if (OffsetInBytes > (Data.Length - 1)) throw new ArgumentException("Wrong offset"); if ((LengthInBytes == UInt32.MaxValue) || (LengthInBytes > (Data.Length - OffsetInBytes))) RemainingBytes = Data.Length - OffsetInBytes; else RemainingBytes = LengthInBytes; UInt32 CurrentLength; UInt32 CurrentOffset = OffsetInBytes; byte[] Buffer = new byte[0x405]; byte[] ResponsePattern = new byte[5]; byte[] FinalCommand; Buffer[0] = 0x07; ResponsePattern[0] = 0x08; UInt32 CurrentPosition = StartInBytes; ProgressUpdater Progress = UpdaterPerSector; if ((Progress == null) && (ProgressUpdateCallback != null)) Progress = new ProgressUpdater(GetSectorCount((UInt64)RemainingBytes), ProgressUpdateCallback); while (RemainingBytes > 0) { System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, Buffer, 1, 4); // Start position is in bytes and in Little Endian (on Samsung phones the start position is in Sectors!!) System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, ResponsePattern, 1, 4); // Start position is in bytes and in Little Endian (on Samsung phones the start position is in Sectors!!) if (RemainingBytes >= 0x400) CurrentLength = 0x400; else CurrentLength = (UInt32)RemainingBytes; System.Buffer.BlockCopy(Data, (int)CurrentOffset, Buffer, 5, (int)CurrentLength); if (CurrentLength < 0x400) { FinalCommand = new byte[CurrentLength + 5]; System.Buffer.BlockCopy(Buffer, 0, FinalCommand, 0, (int)CurrentLength + 5); } else FinalCommand = Buffer; Serial.SendCommand(FinalCommand, ResponsePattern); CurrentPosition += CurrentLength; CurrentOffset += CurrentLength; RemainingBytes -= CurrentLength; if (Progress != null) Progress.IncreaseProgress(GetSectorCount(CurrentLength)); } } public UInt64 GetSectorCount(UInt64 ByteCount) { return (ByteCount / 0x200) + ((ByteCount % 0x200) > 0 ? (UInt64)1 : (UInt64)0); } public void Reboot() { Serial.SendCommand(new byte[] { 0x0B }, new byte[] { 0x0C }); } } }