You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
namespace BirdyFlash.Lib {
public enum BirdyType {
Invalid = -1,
Slim = 0,
BirdyWP_Atmega1281 = 1,
BirdyE = 2,
}
public class BirdyComm : IDisposable {
public BirdyType type = BirdyType.Invalid;
public CommPort commPort;
public BirdyComm() {
}
public BirdyComm SetPort(CommPort port) {
this.commPort = port;
return this;
}
public static byte Checksum(byte[] data, int length) {
byte r = 0x00;
for (int i = 0; i < length; i++)
r = (byte)((r + data[i] % 0x100) & 0xFF);
return r;
}
public async Task<bool> AskHighspeed() {
Console.WriteLine("AskHighspeed {0}", this.commPort);
return await this.commPort.SwitchHighspeed();
}
public async Task<bool> AskLowspeed() {
Console.WriteLine("AskLowspeed {0}", this.commPort);
return await this.commPort.SwitchLowspeed();
}
public async Task ForceGotoProg() {
Console.WriteLine("ForceGotoProg {0}", this.commPort);
await Task.Run(() => this.commPort.ForceGotoProg());
}
public async Task<DeviceInfo> TryGetDeviceInfo() {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task)
return task.Result;
else {
this.Dispose();
throw new TimeoutException();
}
}
public async Task<bool> TryWriteE2P(DeviceE2PContents e2pContents) {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task) {
//if (task.Result.model != e2pContents.deviceInfo.model) throw new Exception("Wrong Device Data");
switch (type) {
case BirdyType.Slim:
for (int i = 0; i < 0xCD; i++) {
var block = e2pContents.data.Skip(i * 0x80).Take(0x80);
await this.SetE2PBlock((ushort)(i * 0x80), block.ToArray());
}
return true;
case BirdyType.BirdyE:
case BirdyType.BirdyWP_Atmega1281:
for (int i = 0; i < 0xF2; i++) {
var block = e2pContents.data.Skip(i * 0x20).Take(0x20);
await this.SetE2PBlock((ushort)(i * 0x20), block.ToArray());
}
return true;
default:
throw new Exception("Pager Model not yet supported");
}
} else {
this.Dispose();
throw new TimeoutException();
}
return false;
}
public async Task<DeviceE2PContents> TryReadE2P() {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task) {
var deviceE2PContents = new DeviceE2PContents
{
data = new List<byte>(),
deviceInfo = task.Result,
};
switch (type) {
case BirdyType.Slim:
for (int i = 0; i < 0xCD; i++) {
var block = await this.GetE2PBlock((ushort)(i * 0x80), 0x80);
deviceE2PContents.data.AddRange(block.data.Skip(5).Take(0x80));
//Console.WriteLine(BitConverter.ToString(block.data));
}
return deviceE2PContents;
case BirdyType.BirdyE:
for (int i = 0; i < 1953; i++) {
var block = await this.GetE2PBlock((ushort)(i * 0x20), 0x20);
deviceE2PContents.data.AddRange(block.data.Skip(5).Take(0x20));
//Console.WriteLine(BitConverter.ToString(block.data));
}
return deviceE2PContents;
case BirdyType.BirdyWP_Atmega1281:
for (int i = 0; i < 0xF2; i++) {
var block = await this.GetE2PBlock((ushort)(i * 0x20), 0x20);
deviceE2PContents.data.AddRange(block.data.Skip(5).Take(0x20));
//Console.WriteLine(BitConverter.ToString(block.data));
}
return deviceE2PContents;
default:
throw new Exception("Pager Model not yet supported");
break;
}
} else {
this.Dispose();
throw new TimeoutException();
}
}
public async Task<bool> TryE2PResetViaBootloader() {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task) {
var deviceE2PContents = new DeviceE2PContents
{
data = new List<byte>(),
};
switch (type) {
case BirdyType.Slim:
Console.WriteLine("E2PResetViaBootloader {0}", this.commPort);
this.commPort.SendCommand(new byte[] { 0x18, 0x08 });
return true;
/*case BirdyType.BirdyWP_Atmega1281:
Console.WriteLine("E2PResetViaBootloader {0}", this.commPort);
this.commPort.SendCommand(new byte[] { 0x18, 0x08 });
return true;*/
default:
throw new Exception("Pager Model not yet supported");
}
} else {
this.Dispose();
throw new TimeoutException();
}
return false;
}
public async Task<bool> TryChangeSerialNumber(string newSerial) {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task) {
switch (type) {
case BirdyType.Slim:
Console.WriteLine("ChangeSerialNumber {0}", this.commPort);
this.commPort.SendCommand(Encoding.ASCII.GetBytes('\x13' + newSerial));
return true;
/*case BirdyType.BirdyWP_Atmega1281:
Console.WriteLine("E2PResetViaBootloader {0}", this.commPort);
this.commPort.SendCommand(new byte[] { 0x18, 0x08 });
return true;*/
default:
throw new Exception("Pager Model not yet supported");
}
} else {
this.Dispose();
throw new TimeoutException();
}
return false;
}
public async Task<bool> TryRestart() {
var task = this.GetDeviceInfo();
if (await Task.WhenAny(task, Task.Delay(2000)) == task) {
switch (type) {
case BirdyType.Slim:
Console.WriteLine("Restart {0}", this.commPort);
this.commPort.SendCommand(new byte[] { 0x08 });
return true;
/*case BirdyType.BirdyWP_Atmega1281:
Console.WriteLine("E2PResetViaBootloader {0}", this.commPort);
this.commPort.SendCommand(new byte[] { 0x18, 0x08 });
return true;*/
default:
throw new Exception("Pager Model not yet supported");
}
} else {
this.Dispose();
throw new TimeoutException();
}
return false;
}
public async Task<DeviceInfo> GetDeviceInfo() {
Console.WriteLine("GetDeviceInfo {0}", this.commPort);
var task = this.commPort.SendCommand(new byte[] { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff });
await task.Task;
var res = task.Task.Result;
if (res.data.Length < 10) throw new DataMisalignedException();
var resStr = Encoding.UTF8.GetString(res.data, 0x01, res.dataLength - 1);
var resParts = resStr.Split(',');
Console.WriteLine(resStr);
switch (resParts[0]) {
case "BirdyIOT":
this.type = BirdyType.Slim;
return new DeviceInfo
{
firstIDK = res.data[0],
model = resParts[0],
firmwareVersion = resParts[1],
hardwareVersion = resParts[2],
e2pVersion = resParts[3],
idk1 = resParts[4],
deviceSerial = resParts[5],
};
case "BirdyWP-128BOS":
case "BirdyWP-128r3BOS":
case "BirdyW":
this.type = BirdyType.BirdyWP_Atmega1281;
return new DeviceInfo
{
firstIDK = res.data[0],
model = resParts[0],
firmwareVersion = resParts[1],
hardwareVersion = resParts[2],
e2pVersion = resParts[3],
idk1 = resParts[4],
deviceSerial = resParts[5],
};
case "BirdyE":
this.type = BirdyType.BirdyE;
return new DeviceInfo
{
firstIDK = 0x00,
model = resParts[0],
firmwareVersion = resParts[1],
hardwareVersion = resParts[2],
e2pVersion = "",
idk1 = "",
deviceSerial = "-UNKNOWN-",
};
default:
this.type = BirdyType.Invalid;
return new DeviceInfo
{
firstIDK = 0x00,
model = "",
firmwareVersion = "",
hardwareVersion = "",
e2pVersion = "",
idk1 = "",
deviceSerial = "",
};
}
}
public async Task<RXPacket> GetE2PBlock(UInt16 address, byte size = 0x80) {
Console.WriteLine("GetE2PBlock {0} {1} {2}", this.commPort, address, size);
byte[] cmd = null;
switch(type) {
case BirdyType.Slim:
cmd = new byte[] { 0x29, 0x00, 0x00, (byte)(address >> 8), (byte)address, size };
break;
case BirdyType.BirdyE:
case BirdyType.BirdyWP_Atmega1281:
cmd = new byte[] { 0x05, (byte)(address >> 8), (byte)address, size };
break;
}
var task = this.commPort.SendCommand(cmd);
await task.Task;
var res = task.Task.Result;
if (type == BirdyType.Slim) {
if (!res.data.Take(5).SequenceEqual(cmd.Take(5))) throw new DataMisalignedException(string.Format("Response Memory Address is not what requested\ntx>{0}\nrx<{1}", BitConverter.ToString(cmd.Take(5).ToArray()), BitConverter.ToString(res.data.Take(5).ToArray())));
}
if (res.crc != res.rcrc) throw new DataMisalignedException("CRC Mismatch");
return res;
}
public async Task<RXPacket> SetE2PBlock(UInt16 address, byte[] data) {
Console.WriteLine("SetE2PBlock {0} {1} [{2}]", this.commPort, address, data.Length);
List<byte> cmd = null;
switch (type) {
case BirdyType.Slim:
cmd = new List<byte> { 0x2A, 0x00, 0x00, (byte)(address >> 8), (byte)address };
break;
case BirdyType.BirdyE:
case BirdyType.BirdyWP_Atmega1281:
cmd = new List<byte> { 0x07, (byte)(address >> 8), (byte)address };
break;
}
cmd.AddRange(data);
var task = this.commPort.SendCommand(cmd.ToArray());
await task.Task;
var res = task.Task.Result;
if (type == BirdyType.Slim) {
if (!res.data.Take(5).SequenceEqual(cmd.Take(5))) throw new DataMisalignedException(string.Format("Response Memory Address is not what requested\ntx>{0}\nrx<{1}", BitConverter.ToString(cmd.Take(5).ToArray()), BitConverter.ToString(res.data.Take(5).ToArray())));
if (res.data[5] != res.data.Length - 6) throw new DataMisalignedException(string.Format("length mismatch {0} != {1}", res.data[5], res.data.Length - 6));
if (!res.data.Skip(6).SequenceEqual(cmd.Skip(5))) throw new DataMisalignedException(string.Format("Response Copy is not what sent\ntx>{0}\nrx<{1}", BitConverter.ToString(cmd.Skip(5).ToArray()), BitConverter.ToString(res.data.Skip(6).ToArray())));
}
if (type == BirdyType.BirdyWP_Atmega1281 || type == BirdyType.BirdyE) {
if (!res.data.Take(3).SequenceEqual(cmd.Take(3))) throw new DataMisalignedException(string.Format("Response Memory Address is not what requested\ntx>{0}\nrx<{1}", BitConverter.ToString(cmd.Take(3).ToArray()), BitConverter.ToString(res.data.Take(3).ToArray())));
}
if (res.crc != res.rcrc) throw new DataMisalignedException("CRC Mismatch");
return res;
}
public async Task<RXPacket> GetBKIdentities(int offset) {
Console.WriteLine("GetBKIdentities {0} {1}", this.commPort, offset);
byte[] cmd = null;
switch (type) {
case BirdyType.Slim:
cmd = new byte[] { 0xF3, (byte)(offset * 0x20) };
break;
}
var task = this.commPort.SendCommand(cmd);
await task.Task;
var res = task.Task.Result;
if (type == BirdyType.Slim) {
if (!res.data.Take(5).SequenceEqual(cmd.Take(5))) throw new DataMisalignedException(string.Format("Response Memory Address is not what requested\ntx>{0}\nrx<{1}", BitConverter.ToString(cmd.Take(5).ToArray()), BitConverter.ToString(res.data.Take(5).ToArray())));
}
if (res.crc != res.rcrc) throw new DataMisalignedException("CRC Mismatch");
return res;
}
public void Dispose() {
Console.WriteLine("Dispose {0}", this.commPort);
this.commPort.Dispose();
}
}
}