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 AskHighspeed() { Console.WriteLine("AskHighspeed {0}", this.commPort); return await this.commPort.SwitchHighspeed(); } public async Task 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 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 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 TryReadE2P() { var task = this.GetDeviceInfo(); if (await Task.WhenAny(task, Task.Delay(2000)) == task) { var deviceE2PContents = new DeviceE2PContents { data = new List(), 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 TryE2PResetViaBootloader() { var task = this.GetDeviceInfo(); if (await Task.WhenAny(task, Task.Delay(2000)) == task) { var deviceE2PContents = new DeviceE2PContents { data = new List(), }; 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 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 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 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 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 SetE2PBlock(UInt16 address, byte[] data) { Console.WriteLine("SetE2PBlock {0} {1} [{2}]", this.commPort, address, data.Length); List cmd = null; switch (type) { case BirdyType.Slim: cmd = new List { 0x2A, 0x00, 0x00, (byte)(address >> 8), (byte)address }; break; case BirdyType.BirdyE: case BirdyType.BirdyWP_Atmega1281: cmd = new List { 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 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(); } } }