//-----------------------------------------------------------------------
//
// 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;
}
}
}