mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
333 lines
14 KiB
C#
333 lines
14 KiB
C#
// 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.Threading.Tasks;
|
|
|
|
namespace WPinternals
|
|
{
|
|
internal enum SaharaMode : uint
|
|
{
|
|
ImageTransferPending = 0x00,
|
|
ImagetransferComplete = 0x01,
|
|
MemoryDebug = 0x02,
|
|
Command = 0x03
|
|
}
|
|
|
|
internal delegate void ReadyHandler();
|
|
|
|
internal class QualcommSahara
|
|
{
|
|
private QualcommSerial Serial;
|
|
|
|
public QualcommSahara(QualcommSerial Serial)
|
|
{
|
|
Serial.EncodeCommands = false;
|
|
Serial.DecodeResponses = false;
|
|
this.Serial = Serial;
|
|
}
|
|
|
|
public bool SendImage(string Path)
|
|
{
|
|
bool Result = true;
|
|
|
|
LogFile.Log("Sending programmer: " + Path, LogType.FileOnly);
|
|
|
|
int Step = 0;
|
|
UInt32 Offset = 0;
|
|
UInt32 Length = 0;
|
|
|
|
byte[] ImageBuffer = null;
|
|
try
|
|
{
|
|
Step = 1;
|
|
byte[] Hello = null;
|
|
Hello = Serial.GetResponse(new byte[] { 0x01, 0x00, 0x00, 0x00 });
|
|
|
|
// Incoming Hello packet:
|
|
// 00000001 = Hello command id
|
|
// xxxxxxxx = Length
|
|
// xxxxxxxx = Protocol version
|
|
// xxxxxxxx = Supported version
|
|
// xxxxxxxx = Max packet length
|
|
// xxxxxxxx = Expected mode
|
|
// 6 dwords reserved space
|
|
LogFile.Log("Protocol: 0x" + ByteOperations.ReadUInt32(Hello, 0x08).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("Supported: 0x" + ByteOperations.ReadUInt32(Hello, 0x0C).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
|
|
|
// Packet:
|
|
// 00000002 = Hello response command id
|
|
// 00000030 = Length
|
|
// 00000002 = Protocol version
|
|
// 00000001 = Supported version
|
|
// 00000000 = Status OK
|
|
// 00000000 = Mode
|
|
// rest is reserved space
|
|
Step = 2;
|
|
byte[] HelloResponse = new byte[] {
|
|
0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
Serial.SendData(HelloResponse);
|
|
|
|
Step = 3;
|
|
using (System.IO.FileStream FileStream = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read))
|
|
{
|
|
while (true)
|
|
{
|
|
Step = 4;
|
|
byte[] ReadDataRequest = Serial.GetResponse(null);
|
|
UInt32 ResponseID = ByteOperations.ReadUInt32(ReadDataRequest, 0);
|
|
if (ResponseID == 4)
|
|
break;
|
|
if (ResponseID != 3)
|
|
{
|
|
Step = 5;
|
|
throw new BadConnectionException();
|
|
}
|
|
|
|
Offset = ByteOperations.ReadUInt32(ReadDataRequest, 0x0C);
|
|
Length = ByteOperations.ReadUInt32(ReadDataRequest, 0x10);
|
|
if ((ImageBuffer == null) || (ImageBuffer.Length != Length))
|
|
ImageBuffer = new byte[Length];
|
|
if (FileStream.Position != Offset)
|
|
FileStream.Seek(Offset, System.IO.SeekOrigin.Begin);
|
|
|
|
Step = 6;
|
|
FileStream.Read(ImageBuffer, 0, (int)Length);
|
|
|
|
Step = 7;
|
|
Serial.SendData(ImageBuffer);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
LogFile.LogException(Ex, LogType.FileAndConsole, Step.ToString() + " 0x" + Offset.ToString("X8") + " 0x" + Length.ToString("X8"));
|
|
Result = false;
|
|
}
|
|
|
|
if (Result)
|
|
LogFile.Log("Programmer loaded into phone memory", LogType.FileOnly);
|
|
|
|
return Result;
|
|
}
|
|
|
|
public bool Handshake()
|
|
{
|
|
bool Result = true;
|
|
|
|
try
|
|
{
|
|
byte[] Hello = Serial.GetResponse(new byte[] { 0x01, 0x00, 0x00, 0x00 });
|
|
|
|
// Incoming Hello packet:
|
|
// 00000001 = Hello command id
|
|
// xxxxxxxx = Length
|
|
// xxxxxxxx = Protocol version
|
|
// xxxxxxxx = Supported version
|
|
// xxxxxxxx = Max packet length
|
|
// xxxxxxxx = Expected mode
|
|
// 6 dwords reserved space
|
|
LogFile.Log("Protocol: 0x" + ByteOperations.ReadUInt32(Hello, 0x08).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("Supported: 0x" + ByteOperations.ReadUInt32(Hello, 0x0C).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
|
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
|
|
|
byte[] HelloResponse = new byte[] {
|
|
0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
byte[] Ready = Serial.SendCommand(HelloResponse, new byte[] { 0x03, 0x00, 0x00, 0x00 });
|
|
}
|
|
catch
|
|
{
|
|
Result = false;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
public void ResetSahara()
|
|
{
|
|
Serial.SendCommand(new byte[] { 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }, new byte[] { 0x08, 0x00, 0x00, 0x00 });
|
|
}
|
|
|
|
public bool ConnectToProgrammerInTestMode()
|
|
{
|
|
// Behaviour of old firehose:
|
|
// Takes about 20 ms to be started.
|
|
// Then PC has to start talking to the phone.
|
|
// Behaviour of new firehose:
|
|
// After 2000 ms the firehose starts talking to the PC
|
|
//
|
|
// For the duration of 2.5 seconds we will send Hello packages
|
|
// And also wait for incoming messages
|
|
// An incoming message can be a response to our outgoing Hello packet (read incoming until "response value")
|
|
// Or it can be an incoming Hello-packet from the programmer (always 2 packets, starting with "Chip serial num")
|
|
// Sending the hello-packet can succeed immediately, or it can timeout.
|
|
// When sending succeeds, an answer should be incoming immediately to complete the handshake.
|
|
// When an incoming Hello was received, the phone still expects to receive another Hello.
|
|
|
|
byte[] HelloPacketFromPcToProgrammer = new byte[0x20C];
|
|
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0, 0x57503730);
|
|
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x28, 0x57503730);
|
|
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x208, 0x57503730);
|
|
ByteOperations.WriteUInt16(HelloPacketFromPcToProgrammer, 0x48, 0x4445);
|
|
|
|
int HelloSendCount = 0;
|
|
bool HandshakeCompleted = false;
|
|
string Incoming;
|
|
do
|
|
{
|
|
Serial.SetTimeOut(200);
|
|
HelloSendCount++;
|
|
try
|
|
{
|
|
LogFile.Log("Send Hello to programmer (" + HelloSendCount.ToString() + ")", LogType.FileOnly);
|
|
Serial.SendData(HelloPacketFromPcToProgrammer);
|
|
LogFile.Log("Hello packet accepted", LogType.FileOnly);
|
|
}
|
|
catch
|
|
{
|
|
LogFile.Log("Hello packet not accepted", LogType.FileOnly);
|
|
}
|
|
|
|
try
|
|
{
|
|
Serial.SetTimeOut(500);
|
|
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
|
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
|
Serial.SetTimeOut(200);
|
|
if (Incoming.Contains("Chip serial num"))
|
|
{
|
|
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
|
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
|
LogFile.Log("Incoming Hello-packets received", LogType.FileOnly);
|
|
}
|
|
|
|
while (Incoming.IndexOf("response value") < 0)
|
|
{
|
|
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
|
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
|
};
|
|
|
|
LogFile.Log("Incoming Hello-response received", LogType.FileOnly);
|
|
HandshakeCompleted = true;
|
|
}
|
|
catch { }
|
|
}
|
|
while (!HandshakeCompleted && (HelloSendCount < 6));
|
|
|
|
if (HandshakeCompleted)
|
|
LogFile.Log("Handshake completed with programmer in testmode", LogType.FileOnly);
|
|
else
|
|
LogFile.Log("Handshake with programmer failed", LogType.FileOnly);
|
|
|
|
return HandshakeCompleted;
|
|
}
|
|
|
|
public async Task<bool> Reset(string ProgrammerPath)
|
|
{
|
|
bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath));
|
|
if (!SendImageResult)
|
|
return false;
|
|
|
|
await Task.Run(() => StartProgrammer());
|
|
|
|
bool Connected = await Task.Run(() => ConnectToProgrammerInTestMode());
|
|
if (!Connected)
|
|
return false;
|
|
|
|
LogFile.Log("Rebooting phone", LogType.FileAndConsole);
|
|
string Command03 = "<?xml version=\"1.0\" ?><data><power value=\"reset\"/></data>";
|
|
LogFile.Log("Out: " + Command03, LogType.FileOnly);
|
|
Serial.SendData(System.Text.Encoding.ASCII.GetBytes(Command03));
|
|
|
|
string Incoming;
|
|
do
|
|
{
|
|
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
|
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
|
}
|
|
while (Incoming.IndexOf("response value") < 0);
|
|
|
|
// Workaround for problem
|
|
// SerialPort is sometimes not disposed correctly when the device is already removed.
|
|
// So explicitly dispose here
|
|
Serial.Close();
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SwitchMode(SaharaMode Mode)
|
|
{
|
|
byte[] SwitchModeCommand = new byte[] { 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
ByteOperations.WriteUInt32(SwitchModeCommand, 8, (UInt32)Mode);
|
|
byte[] ResponsePattern = null;
|
|
switch (Mode)
|
|
{
|
|
case SaharaMode.ImageTransferPending:
|
|
ResponsePattern = new byte[] { 0x04, 0x00, 0x00, 0x00 };
|
|
break;
|
|
case SaharaMode.MemoryDebug:
|
|
ResponsePattern = new byte[] { 0x09, 0x00, 0x00, 0x00 };
|
|
break;
|
|
case SaharaMode.Command:
|
|
ResponsePattern = new byte[] { 0x0B, 0x00, 0x00, 0x00 };
|
|
break;
|
|
}
|
|
Serial.SendCommand(SwitchModeCommand, ResponsePattern);
|
|
}
|
|
|
|
public void StartProgrammer()
|
|
{
|
|
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
|
byte[] DoneCommand = new byte[] { 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 };
|
|
bool Started = false;
|
|
int count = 0;
|
|
do
|
|
{
|
|
try
|
|
{
|
|
count++;
|
|
byte[] DoneResponse = Serial.SendCommand(DoneCommand, new byte[] { 0x06, 0x00, 0x00, 0x00 });
|
|
Started = true;
|
|
}
|
|
catch (BadConnectionException)
|
|
{
|
|
LogFile.Log("Problem while starting programmer. Attempting again.", LogType.FileAndConsole);
|
|
}
|
|
} while (!Started || count < 3);
|
|
if (count >= 3 && !Started)
|
|
{
|
|
LogFile.Log("Maximum number of attempts to start the programmer exceeded.", LogType.FileAndConsole);
|
|
throw new BadConnectionException();
|
|
}
|
|
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
|
}
|
|
}
|
|
}
|