using KFDtool.Adapter.Protocol.Adapter; using KFDtool.Shared; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KFDtool.P25.ThreeWire { public class ThreeWireProtocol { private static NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger(); private const int TWI_TIMEOUT = 5000; // 5 second private const byte READY_REQ_OPCODE = 0xC0; private const byte READY_GENERAL_MODE_OPCODE = 0xD0; private const byte TRANSFER_DONE_OPCODE = 0xC1; private const byte KMM_OPCODE = 0xC2; private const byte DISCONNECT_ACK_OPCODE = 0x90; private const byte DISCONNECT_OPCODE = 0x92; private AdapterProtocol Protocol; public ThreeWireProtocol(AdapterProtocol ap) { Protocol = ap; } public void SendKeySignature() { Log.Debug("kfd: key signature"); Protocol.SendKeySignature(); } public void InitSession() { // send ready req opcode List cmd = new List(); cmd.Add(READY_REQ_OPCODE); Log.Debug("kfd: ready req"); Log.Debug("kfd -> mr: {0}", Utility.DataFormat(cmd)); Protocol.SendData(cmd); // receive ready general mode opcode Log.Debug("mr: ready general mode"); byte rsp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("kfd -> mr: {0}", Utility.DataFormat(rsp)); if (rsp != READY_GENERAL_MODE_OPCODE) { throw new Exception("mr: unexpected opcode"); } } private List CreateKmmFrame(List kmm) { // create body List body = new List(); body.Add(0x00); // control body.Add(0xFF); // destination RSI high byte body.Add(0xFF); // destination RSI mid byte body.Add(0xFF); // destination RSI low byte body.AddRange(kmm); // kmm // calculate crc byte[] crc = CRC16.CalculateCrc(body.ToArray()); // create frame List frame = new List(); int length = body.Count + 2; // control + dest rsi + kmm + crc frame.Add(KMM_OPCODE); // kmm opcode frame.Add((byte)((length >> 8) & 0xFF)); // length high byte frame.Add((byte)(length & 0xFF)); // length low byte frame.AddRange(body); // kmm body frame.Add(crc[0]); // crc high byte frame.Add(crc[1]); // crc low byte return frame; } private List ParseKmmFrame() { byte temp; // receive kmm opcode Log.Debug("mr: kmm opcode"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); if (temp != KMM_OPCODE) { throw new Exception(string.Format("mr: unexpected opcode, expected: 0x{0:X2}, got: 0x{1:X2}", KMM_OPCODE, temp)); } int length = 0; // receive length high byte Log.Debug("mr: length high byte"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: 0x{0:X2}", temp); length |= (temp & 0xFF) << 8; // receive length low byte Log.Debug("mr: length low byte"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); length |= temp & 0xFF; Log.Debug("length: {0}", length); List toCrc = new List(); // receive control Log.Debug("mr: control"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); toCrc.Add(temp); // receive dest rsi high byte Log.Debug("mr: dest rsi high byte"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); toCrc.Add(temp); // receive dest rsi mid byte Log.Debug("mr: dest rsi mid byte"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); toCrc.Add(temp); // receive dest rsi low byte Log.Debug("mr: dest rsi low byte"); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); toCrc.Add(temp); int bodyLength = length - 6; List kmm = new List(); for (int i = 0; i < bodyLength; i++) { Log.Debug("mr: kmm byte {0} of {1}", i + 1, bodyLength); temp = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(temp)); kmm.Add(temp); } toCrc.AddRange(kmm); // calculate crc byte[] expectedCrc = CRC16.CalculateCrc(toCrc.ToArray()); Log.Debug("expected crc - high: 0x{0:X2}, low: 0x{1:X2}", expectedCrc[0], expectedCrc[1]); byte[] crc = new byte[2]; // receive crc high byte Log.Debug("mr: crc high byte"); crc[0] = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(crc[0])); // receive crc low byte Log.Debug("mr: crc low byte"); crc[1] = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(crc[1])); if (expectedCrc[0] != crc[0]) { throw new Exception(string.Format("mr: crc high byte mismatch, expected: 0x{0:X2}, got: 0x{1:X2}", expectedCrc[0], crc[0])); } if (expectedCrc[1] != crc[1]) { throw new Exception(string.Format("mr: crc low byte mismatch, expected: 0x{0:X2}, got: 0x{1:X2}", expectedCrc[1], crc[1])); } return kmm; } public void EndSession() { // send transfer done opcode List cmd1 = new List(); cmd1.Add(TRANSFER_DONE_OPCODE); Log.Debug("kfd: transfer done"); Log.Debug("kfd -> mr: {0}", Utility.DataFormat(cmd1)); Protocol.SendData(cmd1); // receive transfer done opcode Log.Debug("mr: transfer done"); byte rsp1 = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(rsp1)); if (rsp1 != TRANSFER_DONE_OPCODE) { throw new Exception("mr: unexpected opcode"); } // send disconnect opcode List cmd2 = new List(); cmd2.Add(DISCONNECT_OPCODE); Log.Debug("kfd: disconnect"); Log.Debug("kfd -> mr: {0}", Utility.DataFormat(cmd2)); Protocol.SendData(cmd2); // receive disconnect ack opcode Log.Debug("mr: disconnect ack"); byte rsp2 = Protocol.GetByte(TWI_TIMEOUT); Log.Debug("mr -> kfd: {0}", Utility.DataFormat(rsp2)); if (rsp2 != DISCONNECT_ACK_OPCODE) { throw new Exception("mr: unexpected opcode"); } } public byte[] PerformKmmTransfer(byte[] inKmm) { List txFrame = CreateKmmFrame(inKmm.ToList()); Log.Debug("kfd -> mr: {0}", Utility.DataFormat(txFrame)); Protocol.SendData(txFrame); List rxFrame = ParseKmmFrame(); return rxFrame.ToArray(); } } }