//----------------------------------------------------------------------- // // Copyright © 2012 Nils Hammar. All rights reserved. // //----------------------------------------------------------------------- /* * Software to access vehicle information via the OBD-II connector. * * Copyright © 2012 Nils Hammar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Alternative licensing is possible, see the licensing document. * * The above text may not be removed or modified. */ namespace DeviceApi.J2534 { using System; using System.Runtime.InteropServices; using System.Threading; using global::SharedObjects.Api; /// /// Class for handling one connection to the J2534 API. /// Notice: It is possible to have multiple connections, however only one per protocol family. /// public class PassThruConnection : IPassThruConnection { /// /// ID of channel. /// private int ChannelID = -1; /// /// Gets a string containing the last encountered error. /// public string lastError { get; private set; } /// /// Gets the Instance of the PassThruDevice that this PassThruConnection is applicable for. /// public IPassThruDevice passThruDevice { get; private set; } /// /// Initializes a new instance of the class. /// /// The device that this connection is using. public PassThruConnection(IPassThruDevice passThruDevice) { this.passThruDevice = passThruDevice; } /// /// Get array of possible connection flags for this connection. /// /// Array of flags. public CanFlags[] getConnFlags() { return this.passThruDevice.passThruInterface.getConnFlags(); } /// /// Get array of possible message flags for this connection. /// /// Array of flags. public CanFlags[] getMessageFlags() { return this.passThruDevice.passThruInterface.getMessageFlags(); } /// /// Get the flag indicating that the device has a disconnect bug in the API. /// /// 'true' if disconnect bug exists. public bool disconnectBug() { return this.passThruDevice.passThruInterface.disconnectBug(); } /// /// Perform a connection. /// /// ID of protocol to use. /// Connection flags. /// Baudrate to connect with. /// Result status, 0=success. public unsafe PassThruConstants.resultCode Gl_PassThruConnect(int ProtocolID, uint Flags, uint Baudrate) { PassThruConstants.resultCode ret = 0; ret = this.passThruDevice.Gl_PassThruConnect(ProtocolID, Flags, Baudrate, out this.ChannelID); if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return ret; } /// /// Disconnect the current connection. /// /// Result status, 0=success. public PassThruConstants.resultCode Gl_PassThruDisconnect() { PassThruConstants.resultCode ret = 0; if (this.ChannelID >= 0) { ret = this.passThruDevice.passThruInterface.PassThruDisconnect(this.ChannelID); } if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } this.ChannelID = -1; return ret; } /// /// Add a message filter. /// /// Type of filter. /// Mask message. /// Pattern message. /// Flow control message. /// ID of filter added. (out value) /// Result status, 0=success. public unsafe PassThruConstants.resultCode Gl_PassThruStartMsgFilter( uint FilterType, IPassThruMsg maskMsg, IPassThruMsg patternMsg, IPassThruMsg flowControlMsg, out int msgId) { PassThruConstants.resultCode ret = 0; msgId = 0; if (maskMsg != null && patternMsg != null) { PASSTHRU_MSG maskMsg1 = new PASSTHRU_MSG(); PASSTHRU_MSG patternMsg1 = new PASSTHRU_MSG(); PASSTHRU_MSG flowMsg1; PASSTHRU_MSG* pMaskMsg = &maskMsg1; PASSTHRU_MSG* pPatternMsg = &patternMsg1; PASSTHRU_MSG* pFlowControlMsg; maskMsg.toStruct(pMaskMsg); patternMsg.toStruct(pPatternMsg); if (flowControlMsg != null) { flowMsg1 = new PASSTHRU_MSG(); pFlowControlMsg = &flowMsg1; flowControlMsg.toStruct(pFlowControlMsg); } else { pFlowControlMsg = null; } fixed (int* pMsgId = &msgId) { ret = this.passThruDevice.passThruInterface.PassThruStartMsgFilter( this.ChannelID, (int)FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pMsgId); if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } } } return ret; } /// /// Remove message filter. /// /// ID of filter to remove. /// Result status, 0=success. public PassThruConstants.resultCode Gl_PassThruStopMsgFilter(int msgId) { PassThruConstants.resultCode ret = this.passThruDevice.passThruInterface.PassThruStopMsgFilter(this.ChannelID, msgId); if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return ret; } /// /// Read messages from the connection on the device. /// /// ID of protocol. /// Received message (out data). /// Number of messages to read and returns actual number of messages read. /// Timeout waiting for message. /// Result status, 0=success. public unsafe PassThruConstants.resultCode Gl_PassThruReadMsgs(int protocolId, out IPassThruMsg msg, ref int numMsgs, int Timeout) { PassThruConstants.resultCode ret = 0; msg = null; if (this.ChannelID == -1) { return PassThruConstants.resultCode.NO_CHANNEL; } try { PASSTHRU_MSG msg1 = new PASSTHRU_MSG(); PASSTHRU_MSG* pMsg = &msg1; pMsg->ProtocolID = protocolId; // Not sure if necessary, better safe than sorry. fixed (int* pNumMsgs = &numMsgs) { ret = this.passThruDevice.passThruInterface.PassThruReadMsgs(this.ChannelID, pMsg, pNumMsgs, Timeout); } msg = new PassThruMsg(pMsg); if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } } catch (ThreadAbortException) { throw; } catch (Exception ex) { this.lastError = ex.Message + "\r\n" + ex.StackTrace + "\r\n"; ret = PassThruConstants.resultCode.READ_FAIL; } return ret; } /// /// Write a message on the connection to the device. /// /// Message to write /// Number of messages. /// Timeout waiting to write message. /// Result status, 0=success. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Intentional for use with DLL API")] public unsafe PassThruConstants.resultCode Gl_PassThruWriteMsgs(IPassThruMsg msg, ref int numMsgs, int Timeout) { PassThruConstants.resultCode ret = 0; PASSTHRU_MSG msg1 = new PASSTHRU_MSG(); PASSTHRU_MSG* pMsg = &msg1; msg.toStruct(pMsg); fixed (int* pNumMsgs = &numMsgs) { ret = this.passThruDevice.passThruInterface.PassThruWriteMsgs(this.ChannelID, pMsg, pNumMsgs, Timeout); } if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return ret; } /// /// Start sending of a periodic message. /// /// Message to send. /// ID of message (out value) /// Interval between transmissions. /// Result status, 0=success. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Intentional for use with DLL API")] public unsafe PassThruConstants.resultCode Gl_PassThruStartPeriodicMsg(IPassThruMsg msg, out int msgID, int TimeInterval) { PassThruConstants.resultCode ret = 0; PASSTHRU_MSG msg1 = new PASSTHRU_MSG(); PASSTHRU_MSG* pMsg = &msg1; msg.toStruct(pMsg); fixed (int* pMsgID = &msgID) { ret = this.passThruDevice.passThruInterface.PassThruStartPeriodicMsg(this.ChannelID, pMsg, pMsgID, TimeInterval); } if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return ret; } /// /// Stop sending a periodic message. /// /// ID of message. /// Result status, 0=success. public PassThruConstants.resultCode Gl_PassThruStopPeriodicMsg(int MsgID) { PassThruConstants.resultCode ret = this.passThruDevice.passThruInterface.PassThruStopPeriodicMsg(this.ChannelID, MsgID); if (ret != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return ret; } /// /// Perform IOCTL operation on connection. /// /// IOCTL operation. /// Pointer to out data to IOCTL. /// Pointer to result data from IOCTL. /// Result status, 0=success. public unsafe PassThruConstants.resultCode Gl_PassThruIoctl( int IoctlID, void* pInput, void* pOutput) { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlID, pInput, pOutput); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Specific IOCTL call to set data rate. /// /// Data rate to set. /// Result status, 0=success. public PassThruConstants.resultCode IoctlSetDataRate(uint value) { return this.IoctlSetParameter(IoctlParameter.DATA_RATE, value); } /// /// Set loopback flag on connection. /// /// 'true' if sent messages shall be looped back to sender. /// Result status, 0=success. public PassThruConstants.resultCode IoctlSetLoopback(bool state) { return this.IoctlSetParameter(IoctlParameter.LOOPBACK, (uint)(state ? 1 : 0)); } /// /// Set address of this node on the interface. /// /// Address to set. /// Result status, 0=success. public PassThruConstants.resultCode IoctlSetNodeAddress(uint value) { return this.IoctlSetParameter(IoctlParameter.NODE_ADDRESS, (value & 0xff)); } /// /// Set network line on interface. /// /// Network line to set. /// Result status, 0=success. public PassThruConstants.resultCode IoctlSetNetworkLine(uint value) { return this.IoctlSetParameter(IoctlParameter.NETWORK_LINE, (value & 0x03)); } /// /// Set parameter using IOCTL. /// /// ID of parameter to set. /// Value of parameter to set. /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlSetParameter(uint parameter, uint value) { SCONFIG sconf = new SCONFIG(); SCONFIG_LIST sconf_list = new SCONFIG_LIST(); SCONFIG* pSconf = &sconf; SCONFIG_LIST* pSconf_list = &sconf_list; sconf.Parameter = parameter; sconf.Value = value; sconf_list.NumOfParams = 1; sconf_list.ConfigPtr = pSconf; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.SET_CONFIG, pSconf_list, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Get parameter using IOCTL. /// /// Parameter to get. /// Value of parameter. (out value) /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlGetParameter(uint parameter, out uint value) { SCONFIG sconf; SCONFIG_LIST sconf_list; SCONFIG* pSconf = &sconf; SCONFIG_LIST* pSconf_list = &sconf_list; sconf.Parameter = parameter; sconf.Value = 0; sconf_list.NumOfParams = 1; sconf_list.ConfigPtr = pSconf; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.GET_CONFIG, pSconf_list, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } value = sconf.Value; return res; } /// /// Read battery voltage on interface. /// /// Battery voltage. (out parameter) /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlReadVbatt(out uint value) { uint val1; uint* pVal1 = &val1; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.READ_VBATT, null, pVal1); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } value = val1; return res; } /// /// Perform a five baud init, used by ISO9141 and KWP2000 protocol. /// The returned keywords identifies which protocol it is. /// /// Target address. /// Number of received bytes. /// Keyword 1. /// Keyword 2. /// Result status, 0=success. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Intentional for use with DLL API")] public unsafe PassThruConstants.resultCode IoctlFiveBaudInit(uint target, out uint outBytes, out uint keyword1, out uint keyword2) { SBYTE_ARRAY request = new SBYTE_ARRAY(); SBYTE_ARRAY response = new SBYTE_ARRAY(); PassThruConstants.resultCode res = 0; IntPtr inPtr = Marshal.AllocHGlobal(4); IntPtr outPtr = Marshal.AllocHGlobal(4); request.bytes = 1; request.data = (byte*)inPtr.ToPointer(); *request.data = (byte)(target & 0xff); try { response.bytes = 0; response.data = (byte*)outPtr.ToPointer(); response.data[0] = 0; response.data[1] = 0; res = this.passThruDevice.passThruInterface.PassThruIoctl( this.ChannelID, IoctlFunction.FIVE_BAUD_INIT, (void*)&request, (void*)&response); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } outBytes = (uint)response.bytes; keyword1 = (uint)(response.data[0] & 0xff); keyword2 = (uint)(response.data[1] & 0xff); } finally { Marshal.FreeHGlobal(outPtr); Marshal.FreeHGlobal(inPtr); } return res; } /// /// Perform a Fast Init. This is for protocol KWP2000. /// /// Protocol ID /// Source address to use. /// Result message from init. /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlFastInit(int protocol, uint srcaddress, out IPassThruMsg result) { PASSTHRU_MSG request = new PASSTHRU_MSG(); PASSTHRU_MSG response = new PASSTHRU_MSG(); PASSTHRU_MSG* pRequest = &request; PASSTHRU_MSG* pResponse = &response; pRequest->ProtocolID = protocol; pRequest->TxFlags = 0; pRequest->DataSize = 4; pRequest->Data[0] = 0xc1; // Format (functional addressing, 1 byte payload). pRequest->Data[1] = 0x33; // Initialization address used to activate all ECUs. pRequest->Data[2] = (byte)(srcaddress & 0xff); // Tool physical source address. Depends on protocol. pRequest->Data[3] = 0x81; // Data: Start Communication Request Service pResponse->ProtocolID = 0; pResponse->TxFlags = 0; pResponse->DataSize = 0; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl( this.ChannelID, IoctlFunction.FAST_INIT, pRequest, pResponse); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } result = new PassThruMsg(pResponse); return res; } /// /// Clear the TX buffer of the interface. /// /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlClearTxBuffer() { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.CLEAR_TX_BUFFER, null, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Clear the RX buffer of the interface. /// /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlClearRxBuffer() { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.CLEAR_RX_BUFFER, null, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Clear all periodic messages. /// /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlClearPeriodicMsgs() { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.CLEAR_PERIODIC_MSGS, null, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Clear all filters. /// /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlClearMsgFilters() { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.CLEAR_MSG_FILTERS, null, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Clear message lookup table. /// /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlClearFunctMsgLookupTable() { PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.CLEAR_FUNCT_MSG_LOOKUP_TABLE, null, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Add to message lookup table. /// /// Address to add. /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlAddToFunctMsgLookupTable(uint address) { SBYTE_ARRAY data; data.bytes = 1; byte addr = (byte)(address & 0xff); data.data = &addr; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.ADD_TO_FUNCT_MSG_LOOKUP_TABLE, &data, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Remove from message lookup table. /// /// Address to remove. /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlDeleteFromFunctMsgLookupTable(uint address) { SBYTE_ARRAY data; data.bytes = 1; byte addr = (byte)(address & 0xff); data.data = &addr; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE, &data, null); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } return res; } /// /// Read the programming voltage. /// /// Programming voltage. (out data) /// Result status, 0=success. public unsafe PassThruConstants.resultCode IoctlReadProgVoltage(out uint value) { uint val1; uint* pVal1 = &val1; PassThruConstants.resultCode res = this.passThruDevice.passThruInterface.PassThruIoctl(this.ChannelID, IoctlFunction.READ_PROG_VOLTAGE, null, pVal1); if (res != PassThruConstants.resultCode.ERR_SUCCESS) { this.lastError = this.Gl_PassThruGetLastError(); } else { this.lastError = PassThruConstants.SUCCESS; } value = val1; return res; } /// /// Get last error for device. /// /// Result status, 0=success. private unsafe string Gl_PassThruGetLastError() { sbyte[] ca = new sbyte[512]; string str = string.Empty; fixed (sbyte* p = ca) { *p = (sbyte)0; PassThruConstants.resultCode res = 0; try { if ((res = this.passThruDevice.passThruInterface.PassThruGetLastError(p)) != 0) { str = " * PassThruGetLastError() FAILED with error '" + res + "'! * "; } else { str = new string(p); } } catch (Exception ex) { str = ex.GetType().ToString() + ": " + ex.Message; } } return str; } } }