Quantex GmbH
Your region: Europe

PassThruIoctl v4.04 v5.0

Input/output control

Last updated:

Description

A general-purpose function for controlling the device and the channel. It performs different operations depending on the IoctlID parameter: reading/writing configuration, initializing protocols, clearing buffers, and other service operations.

long PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput)

Parameters

IoctlID Value Description
GET_CONFIG 0x01 Read protocol parameters
SET_CONFIG 0x02 Write protocol parameters
READ_VBATT 0x03 Read the supply voltage
FIVE_BAUD_INIT 0x04 5-baud K-Line initialization
FAST_INIT 0x05 Fast K-Line initialization
CLEAR_TX_BUFFER 0x07 Clear the transmit queue
CLEAR_RX_BUFFER 0x08 Clear the receive queue
CLEAR_PERIODIC_MSGS 0x09 Clear periodic messages
CLEAR_MSG_FILTERS 0x0A Clear all filters
CLEAR_FUNCT_MSG_LOOKUP_TABLE 0x0B Clear the functional address table
ADD_TO_FUNCT_MSG_LOOKUP_TABLE 0x0C Add to the functional address table
DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE 0x0D Remove from the functional address table
READ_PROG_VOLTAGE 0x0E Read the programming voltage
SW_CAN_HS 0x8000 Switch SW-CAN to high-speed mode
SW_CAN_NS 0x8001 Switch SW-CAN to normal mode
BUS_ON v5.0 0x0F Connect the CAN controller to the bus
REQUEST_CONNECTION J2534-2 0x800A Establish a TP 2.0 connection
TEARDOWN_CONNECTION J2534-2 0x800B Tear down a TP 2.0 connection
GET_DEVICE_INFO J2534-2 0x800C Get device information
GET_PROTOCOL_INFO J2534-2 0x800D Get protocol information
ISO13400_PS Quantex 0x8110-0x8113 DoIP (Diagnostics over IP) commands

Return error codes

Code Description Possible causes and solutions
STATUS_NOERROR Function completed successfully
ERR_DEVICE_NOT_CONNECTED No connection to the adapter
  • The adapter is turned off or out of range
  • Solution: check the adapter power and the network connection
ERR_INVALID_CHANNEL_ID Invalid channel identifier
  • ChannelID was not obtained through PassThruConnect, or the channel is closed
  • Solution: make sure PassThruConnect completed successfully
ERR_INVALID_IOCTL_ID Invalid IoctlID identifier
  • The specified IoctlID is not supported
  • Solution: check that the IoctlID value is correct
ERR_NULL_PARAMETER NULL was passed instead of a required pointer
  • pInput or pOutput is NULL when a pointer is required
  • Solution: pass valid pointers
ERR_NOT_SUPPORTED The operation is not supported
  • The adapter does not support the requested operation
  • Solution: check the adapter capabilities via GET_DEVICE_INFO
ERR_INVALID_IOCTL_VALUE Invalid parameter value
  • The value in pInput is out of the allowed range
  • Solution: check the allowed value ranges
ERR_INVALID_MSG Invalid message structure
  • Invalid structure in pInput for FAST_INIT
  • Solution: check that the PASSTHRU_MSG fields are correct
ERR_FAILED Undefined error
  • Internal error of the library or the adapter
  • Solution: call PassThruGetLastError() for a description

READ_VBATT — Read the supply voltage

Returns the voltage on the OBD-II connector (pin 16). The value is in millivolts; to get volts, divide it by 1000. The command does not require an open channel and can be run right after PassThruOpen.

pInput NULL
pOutput unsigned long* — voltage in mV

C/C++ example

#include "j2534_dll.hpp"

unsigned long DeviceID;  // Obtained from PassThruOpen
unsigned long voltage;
long ret;

ret = PassThruIoctl(DeviceID, READ_VBATT, NULL, &voltage);
if (ret == STATUS_NOERROR)
{
    printf("Voltage: %.2f V\n", voltage / 1000.0);
}

Kotlin example (Android)

// deviceID obtained earlier from ptOpen
val result = j2534.ptIoctl(deviceID, READ_VBATT, 0, null)
if (result.status == STATUS_NOERROR) {
    val voltageV = result.outputValue / 1000.0
    Log.i("J2534", "Voltage: ${"%.2f".format(voltageV)} V")
}

Python example

from ctypes import *

voltage = c_ulong()
ret = j2534.PassThruIoctl(device_id, READ_VBATT, None, byref(voltage))
if ret == 0:  # STATUS_NOERROR
    print(f"Voltage: {voltage.value / 1000:.2f} V")

C# example

uint voltage;
int ret = J2534.PassThruIoctl(deviceId, READ_VBATT, IntPtr.Zero, out voltage);
if (ret == 0)
{
    Console.WriteLine($"Voltage: {voltage / 1000.0:F2} V");
}

READ_PROG_VOLTAGE — Read the programming voltage

Returns the current voltage on the programming output. The value is in millivolts, rounded to the nearest tenth of a volt.

pInput NULL
pOutput unsigned long* — voltage in mV

C/C++ example

#include "j2534_dll.hpp"

unsigned long DeviceID;
unsigned long voltage;
long ret;

ret = PassThruIoctl(DeviceID, READ_PROG_VOLTAGE, NULL, &voltage);
if (ret == STATUS_NOERROR)
{
    printf("Programming voltage: %.2f V\n", voltage / 1000.0);
}

Kotlin example (Android)

val result = j2534.ptIoctl(deviceID, READ_PROG_VOLTAGE, 0, null)
if (result.status == STATUS_NOERROR) {
    val voltageV = result.outputValue / 1000.0
    Log.i("J2534", "Programming voltage: ${"%.2f".format(voltageV)} V")
}

Python example

from ctypes import *

voltage = c_ulong()
ret = j2534.PassThruIoctl(device_id, READ_PROG_VOLTAGE, None, byref(voltage))
if ret == 0:
    print(f"Programming voltage: {voltage.value / 1000:.2f} V")

C# example

uint voltage;
int ret = J2534.PassThruIoctl(deviceId, READ_PROG_VOLTAGE, IntPtr.Zero, out voltage);
if (ret == 0)
{
    Console.WriteLine($"Programming voltage: {voltage / 1000.0:F2} V");
}

FIVE_BAUD_INIT — 5-baud initialization

Starts the slow (5-baud) initialization for the ISO 9141 and ISO 14230 (K-Line) protocols. Receives the KeyWord from the ECU. The initialization mode is set with the FIVE_BAUD_MOD parameter via SET_CONFIG. The baud rate is determined automatically.

pInput SBYTE_ARRAY* — initialization address (1 byte)
pOutput SBYTE_ARRAY* — KeyWord (2 bytes)
The FIVE_BAUD_MOD parameter defines the initialization mode: ISO9141-2/ISO14230-4 (0), KB2 inversion (1), address inversion (2), ISO9141 without inversion (3).

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // Obtained from PassThruConnect
SBYTE_ARRAY InputMsg;
SBYTE_ARRAY OutputMsg;
long ret;

unsigned char initByte[1];
unsigned char keyWord[2];

initByte[0] = 0x33;  // ECU initialization address

InputMsg.NumOfBytes = 1;
InputMsg.BytePtr = initByte;
OutputMsg.NumOfBytes = 2;
OutputMsg.BytePtr = keyWord;

ret = PassThruIoctl(ChannelID, FIVE_BAUD_INIT, &InputMsg, &OutputMsg);
if (ret == STATUS_NOERROR)
{
    printf("KeyWord: %02X %02X\n", keyWord[0], keyWord[1]);
}

Kotlin example (Android)

// channelID obtained from ptConnect for ISO14230
val initAddress = byteArrayOf(0x33)  // Initialization address

val result = j2534.ptFiveBaudInit(channelID, initAddress)
if (result.status == STATUS_NOERROR) {
    val keyWord = result.keyWord
    Log.i("J2534", "KeyWord: ${keyWord[0].toHex()} ${keyWord[1].toHex()}")
}

Python example

from ctypes import *

input_msg = SBYTE_ARRAY()
input_msg.NumOfBytes = 1
input_msg.BytePtr = (c_ubyte * 1)(0x33)

output_msg = SBYTE_ARRAY()
output_msg.NumOfBytes = 2
keyword = (c_ubyte * 2)()
output_msg.BytePtr = keyword

ret = j2534.PassThruIoctl(channel_id, FIVE_BAUD_INIT, byref(input_msg), byref(output_msg))
if ret == 0:
    print(f"KeyWord: {keyword[0]:02X} {keyword[1]:02X}")

C# example

var inputMsg = new SBYTE_ARRAY {
    NumOfBytes = 1,
    BytePtr = new byte[] { 0x33 }  // Initialization address
};

var outputMsg = new SBYTE_ARRAY {
    NumOfBytes = 2,
    BytePtr = new byte[2]
};

int ret = J2534.PassThruIoctl(channelId, FIVE_BAUD_INIT, ref inputMsg, ref outputMsg);
if (ret == 0)
{
    Console.WriteLine($"KeyWord: {outputMsg.BytePtr[0]:X2} {outputMsg.BytePtr[1]:X2}");
}

FAST_INIT — Fast initialization

Starts the fast initialization for the ISO 14230 (K-Line) protocol. Sends a StartCommunication request and returns the ECU response. Used for KWP2000.

pInput PASSTHRU_MSG* — initialization request
pOutput PASSTHRU_MSG* — ECU response
Important: With a functional (broadcast) address, several ECUs may respond to the request. pOutput will contain only the first response; the rest go into the receive queue.

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // Obtained from PassThruConnect
PASSTHRU_MSG InputMsg;
PASSTHRU_MSG OutputMsg;
long ret;

// StartCommunication request
InputMsg.ProtocolID = ISO14230;
InputMsg.TxFlags = 0;
InputMsg.DataSize = 4;
InputMsg.Data[0] = 0x81;  // Format: physical address, 1 data byte
InputMsg.Data[1] = 0x10;  // Target address (ECU)
InputMsg.Data[2] = 0xF1;  // Source address (tester)
InputMsg.Data[3] = 0x81;  // SID: StartCommunication

ret = PassThruIoctl(ChannelID, FAST_INIT, &InputMsg, &OutputMsg);
if (ret == STATUS_NOERROR)
{
    printf("ECU response: %d bytes\n", OutputMsg.DataSize);
    for (int i = 0; i < OutputMsg.DataSize; i++)
        printf("%02X ", OutputMsg.Data[i]);
}

Kotlin example (Android)

// channelID obtained from ptConnect for ISO14230
val request = PassThruMsg(
    protocolID = ISO14230,
    txFlags = 0u,
    dataSize = 4,
    data = byteArrayOf(0x81.toByte(), 0x10, 0xF1.toByte(), 0x81.toByte())
)

val result = j2534.ptFastInit(channelID, request)
if (result.status == STATUS_NOERROR) {
    Log.i("J2534", "ECU response: ${result.response.data.toHexString()}")
}

Python example

from ctypes import *

input_msg = PASSTHRU_MSG()
input_msg.ProtocolID = ISO14230
input_msg.TxFlags = 0
input_msg.DataSize = 4
input_msg.Data[0] = 0x81  # Format
input_msg.Data[1] = 0x10  # Target address
input_msg.Data[2] = 0xF1  # Source address
input_msg.Data[3] = 0x81  # SID: StartCommunication

output_msg = PASSTHRU_MSG()

ret = j2534.PassThruIoctl(channel_id, FAST_INIT, byref(input_msg), byref(output_msg))
if ret == 0:
    data = bytes(output_msg.Data[:output_msg.DataSize])
    print(f"ECU response: {data.hex(' ').upper()}")

C# example

var inputMsg = new PASSTHRU_MSG {
    ProtocolID = ISO14230,
    TxFlags = 0,
    DataSize = 4
};
inputMsg.Data[0] = 0x81;  // Format
inputMsg.Data[1] = 0x10;  // Target address
inputMsg.Data[2] = 0xF1;  // Source address
inputMsg.Data[3] = 0x81;  // SID: StartCommunication

var outputMsg = new PASSTHRU_MSG();

int ret = J2534.PassThruIoctl(channelId, FAST_INIT, ref inputMsg, ref outputMsg);
if (ret == 0)
{
    var data = new byte[outputMsg.DataSize];
    Array.Copy(outputMsg.Data, data, outputMsg.DataSize);
    Console.WriteLine($"ECU response: {BitConverter.ToString(data).Replace("-", " ")}");
}

CLEAR_TX_BUFFER — Clear the transmit queue

Removes all messages from the channel's transmit queue. Used to cancel scheduled transmissions.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
long ret;

ret = PassThruIoctl(ChannelID, CLEAR_TX_BUFFER, NULL, NULL);
if (ret != STATUS_NOERROR)
{
    char error[256];
    PassThruGetLastError(error);
    printf("Error: %s\n", error);
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, CLEAR_TX_BUFFER, 0, null)
if (result.status != STATUS_NOERROR) {
    Log.e("J2534", "CLEAR_TX_BUFFER error: ${result.status}")
}

Python example

ret = j2534.PassThruIoctl(channel_id, CLEAR_TX_BUFFER, None, None)
if ret != 0:
    print(f"CLEAR_TX_BUFFER error: {ret}")

C# example

int ret = J2534.PassThruIoctl(channelId, CLEAR_TX_BUFFER, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
    Console.WriteLine($"CLEAR_TX_BUFFER error: {ret}");

CLEAR_RX_BUFFER — Clear the receive queue

Removes all messages from the channel's receive queue. It is recommended to call it before starting a new diagnostic session.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
long ret;

ret = PassThruIoctl(ChannelID, CLEAR_RX_BUFFER, NULL, NULL);
if (ret != STATUS_NOERROR)
{
    char error[256];
    PassThruGetLastError(error);
    printf("Error: %s\n", error);
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, CLEAR_RX_BUFFER, 0, null)
if (result.status != STATUS_NOERROR) {
    Log.e("J2534", "CLEAR_RX_BUFFER error: ${result.status}")
}

Python example

ret = j2534.PassThruIoctl(channel_id, CLEAR_RX_BUFFER, None, None)
if ret != 0:
    print(f"CLEAR_RX_BUFFER error: {ret}")

C# example

int ret = J2534.PassThruIoctl(channelId, CLEAR_RX_BUFFER, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
    Console.WriteLine($"CLEAR_RX_BUFFER error: {ret}");

CLEAR_PERIODIC_MSGS — Clear periodic messages

Removes all periodic messages set through PassThruStartPeriodicMsg. Equivalent to calling PassThruStopPeriodicMsg for each message.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
long ret;

ret = PassThruIoctl(ChannelID, CLEAR_PERIODIC_MSGS, NULL, NULL);
if (ret != STATUS_NOERROR)
{
    char error[256];
    PassThruGetLastError(error);
    printf("Error: %s\n", error);
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, CLEAR_PERIODIC_MSGS, 0, null)
if (result.status != STATUS_NOERROR) {
    Log.e("J2534", "CLEAR_PERIODIC_MSGS error: ${result.status}")
}

Python example

ret = j2534.PassThruIoctl(channel_id, CLEAR_PERIODIC_MSGS, None, None)
if ret != 0:
    print(f"CLEAR_PERIODIC_MSGS error: {ret}")

C# example

int ret = J2534.PassThruIoctl(channelId, CLEAR_PERIODIC_MSGS, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
    Console.WriteLine($"CLEAR_PERIODIC_MSGS error: {ret}");

CLEAR_MSG_FILTERS — Clear filters

Removes all message filters set through PassThruStartMsgFilter. After the call, all incoming messages are blocked until new filters are set.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
long ret;

ret = PassThruIoctl(ChannelID, CLEAR_MSG_FILTERS, NULL, NULL);
if (ret != STATUS_NOERROR)
{
    char error[256];
    PassThruGetLastError(error);
    printf("Error: %s\n", error);
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, CLEAR_MSG_FILTERS, 0, null)
if (result.status != STATUS_NOERROR) {
    Log.e("J2534", "CLEAR_MSG_FILTERS error: ${result.status}")
}

Python example

ret = j2534.PassThruIoctl(channel_id, CLEAR_MSG_FILTERS, None, None)
if ret != 0:
    print(f"CLEAR_MSG_FILTERS error: {ret}")

C# example

int ret = J2534.PassThruIoctl(channelId, CLEAR_MSG_FILTERS, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
    Console.WriteLine($"CLEAR_MSG_FILTERS error: {ret}");

CLEAR_FUNCT_MSG_LOOKUP_TABLE — Clear the functional address table

Clears the J1850 functional address table. Used for the J1850 PWM/VPW protocols when working with functional addressing.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // J1850 channel
long ret;

ret = PassThruIoctl(ChannelID, CLEAR_FUNCT_MSG_LOOKUP_TABLE, NULL, NULL);
if (ret != STATUS_NOERROR)
{
    // Error handling
}

Python example

ret = j2534.PassThruIoctl(channel_id, CLEAR_FUNCT_MSG_LOOKUP_TABLE, None, None)

C# example

int ret = J2534.PassThruIoctl(channelId, CLEAR_FUNCT_MSG_LOOKUP_TABLE, IntPtr.Zero, IntPtr.Zero);

ADD_TO_FUNCT_MSG_LOOKUP_TABLE — Add a functional address

Adds an address to the J1850 functional address table. Messages with this address will be accepted when functional addressing is used.

pInput SBYTE_ARRAY* — list of addresses to add
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
SBYTE_ARRAY AddrList;
unsigned char addresses[3] = {0x10, 0x18, 0x28};  // ECU addresses
long ret;

AddrList.NumOfBytes = 3;
AddrList.BytePtr = addresses;

ret = PassThruIoctl(ChannelID, ADD_TO_FUNCT_MSG_LOOKUP_TABLE, &AddrList, NULL);
if (ret != STATUS_NOERROR)
{
    // Error handling
}

Python example

addresses = (c_ubyte * 3)(0x10, 0x18, 0x28)
addr_list = SBYTE_ARRAY()
addr_list.NumOfBytes = 3
addr_list.BytePtr = addresses

ret = j2534.PassThruIoctl(channel_id, ADD_TO_FUNCT_MSG_LOOKUP_TABLE, byref(addr_list), None)

C# example

var addrList = new SBYTE_ARRAY {
    NumOfBytes = 3,
    BytePtr = new byte[] { 0x10, 0x18, 0x28 }
};
int ret = J2534.PassThruIoctl(channelId, ADD_TO_FUNCT_MSG_LOOKUP_TABLE, ref addrList, IntPtr.Zero);

DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE — Remove a functional address

Removes an address from the J1850 functional address table.

pInput SBYTE_ARRAY* — list of addresses to remove
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;
SBYTE_ARRAY AddrList;
unsigned char addresses[1] = {0x10};  // Address to remove
long ret;

AddrList.NumOfBytes = 1;
AddrList.BytePtr = addresses;

ret = PassThruIoctl(ChannelID, DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE, &AddrList, NULL);
if (ret != STATUS_NOERROR)
{
    // Error handling
}

Python example

addresses = (c_ubyte * 1)(0x10)
addr_list = SBYTE_ARRAY()
addr_list.NumOfBytes = 1
addr_list.BytePtr = addresses

ret = j2534.PassThruIoctl(channel_id, DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE, byref(addr_list), None)

C# example

var addrList = new SBYTE_ARRAY {
    NumOfBytes = 1,
    BytePtr = new byte[] { 0x10 }
};
int ret = J2534.PassThruIoctl(channelId, DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE, ref addrList, IntPtr.Zero);

SW_CAN_HS — SW-CAN high-speed mode

Switches Single-Wire CAN to high-speed mode (83.3 kbit/s). Used for high-speed diagnostics on GM networks.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // SW-CAN channel
long ret;

// Switch to high-speed mode
ret = PassThruIoctl(ChannelID, SW_CAN_HS, NULL, NULL);
if (ret == STATUS_NOERROR)
{
    printf("SW-CAN in High Speed mode (83.3 kbit/s)\n");
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, SW_CAN_HS, 0, null)
if (result.status == STATUS_NOERROR) {
    Log.i("J2534", "SW-CAN in High Speed mode (83.3 kbit/s)")
}

Python example

ret = j2534.PassThruIoctl(channel_id, SW_CAN_HS, None, None)
if ret == 0:
    print("SW-CAN in High Speed mode (83.3 kbit/s)")

C# example

int ret = J2534.PassThruIoctl(channelId, SW_CAN_HS, IntPtr.Zero, IntPtr.Zero);
if (ret == 0)
    Console.WriteLine("SW-CAN in High Speed mode (83.3 kbit/s)");

SW_CAN_NS — SW-CAN normal mode

Switches Single-Wire CAN to normal mode (33.3 kbit/s). This is the default mode for GM networks.

pInput NULL
pOutput NULL

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // SW-CAN channel
long ret;

// Switch to normal mode
ret = PassThruIoctl(ChannelID, SW_CAN_NS, NULL, NULL);
if (ret == STATUS_NOERROR)
{
    printf("SW-CAN in Normal Speed mode (33.3 kbit/s)\n");
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, SW_CAN_NS, 0, null)
if (result.status == STATUS_NOERROR) {
    Log.i("J2534", "SW-CAN in Normal Speed mode (33.3 kbit/s)")
}

Python example

ret = j2534.PassThruIoctl(channel_id, SW_CAN_NS, None, None)
if ret == 0:
    print("SW-CAN in Normal Speed mode (33.3 kbit/s)")

C# example

int ret = J2534.PassThruIoctl(channelId, SW_CAN_NS, IntPtr.Zero, IntPtr.Zero);
if (ret == 0)
    Console.WriteLine("SW-CAN in Normal Speed mode (33.3 kbit/s)");

BUS_ON — Connect the controller to the bus v5.0

Connects the CAN controller to the physical bus. Used after the controller has been disconnected from the bus via PassThruDisconnect with the CAN_DISCONNECT flag, or after opening the channel without automatic connection.

IoctlID 0x0F
pInput NULL
pOutput NULL
This command is available only in J2534 v5.00. In v04.04, the controller connects to the bus automatically during PassThruConnect.

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // CAN channel
long ret;

// Connect the controller to the bus
ret = PassThruIoctl(ChannelID, BUS_ON, NULL, NULL);
if (ret == STATUS_NOERROR)
{
    printf("CAN controller connected to the bus\n");
}

Kotlin example (Android)

val result = j2534.ptIoctl(channelID, BUS_ON, 0, null)
if (result.status == STATUS_NOERROR) {
    Log.i("J2534", "CAN controller connected to the bus")
}

Python example

ret = j2534.PassThruIoctl(channel_id, BUS_ON, None, None)
if ret == 0:
    print("CAN controller connected to the bus")

C# example

int ret = J2534.PassThruIoctl(channelId, BUS_ON, IntPtr.Zero, IntPtr.Zero);
if (ret == 0)
    Console.WriteLine("CAN controller connected to the bus");

REQUEST_CONNECTION — Establish a TP 2.0 connection J2534-2

Requests the establishment of a TP 2.0 channel and connection between the adapter and the ECU. Used for the TP 2.0 protocol (VAG). The command is non-blocking — the connection result arrives as an indication in the receive queue.

IoctlID 0x800A
pInput SBYTE_ARRAY* — connection request data (11 bytes)
pOutput NULL

Data structure (11 bytes)

BytePtr[0-3] CAN ID (identifier), BytePtr[0] is the most significant byte
BytePtr[4] Destination — the destination address (ECU)
BytePtr[5] Opcode — always 0xC0
BytePtr[6-7] TX-ID-A — CAN ID for transmission
BytePtr[8-9] RX-ID-A — CAN ID for reception
BytePtr[10] Application Type — the application type
On a successful connection, an implicit PASS filter is created for RX-ID-A. A CONNECTION_ESTABLISHED indication is placed in the receive queue. On error — CONNECTION_LOST.
If RX-ID-A is already in use by another channel, ERR_NOT_UNIQUE is returned. If NumOfBytes ≠ 11, ERR_INVALID_IOCTL_VALUE is returned.

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // TP 2.0 channel
SBYTE_ARRAY InputData;
unsigned char data[11];
long ret;

// CAN ID for broadcast: 0x200
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x02;
data[3] = 0x00;
// Destination (ECU address, e.g. 0x01 for the engine)
data[4] = 0x01;
// Opcode (always 0xC0)
data[5] = 0xC0;
// TX-ID-A (CAN ID for transmission, e.g. 0x300)
data[6] = 0x03;
data[7] = 0x00;
// RX-ID-A (CAN ID for reception, e.g. 0x301)
data[8] = 0x03;
data[9] = 0x01;
// Application Type (0x01 for diagnostics)
data[10] = 0x01;

InputData.NumOfBytes = 11;
InputData.BytePtr = data;

ret = PassThruIoctl(ChannelID, REQUEST_CONNECTION, &InputData, NULL);
if (ret == STATUS_NOERROR)
{
    printf("Connection request sent, waiting for CONNECTION_ESTABLISHED\n");
}
else if (ret == ERR_NOT_UNIQUE)
{
    printf("RX-ID-A is already in use by another channel\n");
}

Kotlin example (Android)

// channelID — TP 2.0 channel
val data = byteArrayOf(
    0x00, 0x00, 0x02, 0x00,  // CAN ID: 0x200
    0x01,                     // Destination: ECU 0x01
    0xC0.toByte(),            // Opcode
    0x03, 0x00,               // TX-ID-A: 0x300
    0x03, 0x01,               // RX-ID-A: 0x301
    0x01                      // Application Type
)

val result = j2534.ptIoctl(channelID, REQUEST_CONNECTION, data.size, data)
when (result.status) {
    STATUS_NOERROR -> Log.i("TP2.0", "Connection request sent")
    ERR_NOT_UNIQUE -> Log.e("TP2.0", "RX-ID-A is already in use")
    else -> Log.e("TP2.0", "Error: ${result.status}")
}

Python example

from ctypes import *

data = (c_ubyte * 11)(
    0x00, 0x00, 0x02, 0x00,  # CAN ID: 0x200
    0x01,                     # Destination: ECU 0x01
    0xC0,                     # Opcode
    0x03, 0x00,               # TX-ID-A: 0x300
    0x03, 0x01,               # RX-ID-A: 0x301
    0x01                      # Application Type
)

input_data = SBYTE_ARRAY()
input_data.NumOfBytes = 11
input_data.BytePtr = data

ret = j2534.PassThruIoctl(channel_id, REQUEST_CONNECTION, byref(input_data), None)
if ret == 0:
    print("Connection request sent, waiting for CONNECTION_ESTABLISHED")
elif ret == ERR_NOT_UNIQUE:
    print("RX-ID-A is already in use by another channel")

C# example

var data = new byte[] {
    0x00, 0x00, 0x02, 0x00,  // CAN ID: 0x200
    0x01,                     // Destination: ECU 0x01
    0xC0,                     // Opcode
    0x03, 0x00,               // TX-ID-A: 0x300
    0x03, 0x01,               // RX-ID-A: 0x301
    0x01                      // Application Type
};

var inputData = new SBYTE_ARRAY {
    NumOfBytes = 11,
    BytePtr = data
};

int ret = J2534.PassThruIoctl(channelId, REQUEST_CONNECTION, ref inputData, IntPtr.Zero);
if (ret == 0)
    Console.WriteLine("Connection request sent, waiting for CONNECTION_ESTABLISHED");
else if (ret == ERR_NOT_UNIQUE)
    Console.WriteLine("RX-ID-A is already in use by another channel");

TEARDOWN_CONNECTION — Tear down a TP 2.0 connection J2534-2

Tears down an established TP 2.0 connection between the adapter and the ECU. The command is non-blocking — confirmation of the teardown arrives as a CONNECTION_LOST indication in the receive queue.

IoctlID 0x800B
pInput SBYTE_ARRAY* — receive CAN ID (4 bytes)
pOutput NULL

Data structure (4 bytes)

BytePtr[0-3] RX-ID-A — CAN ID for reception (the same as in REQUEST_CONNECTION), BytePtr[0] is the most significant byte
After the connection is torn down, the implicit PASS filter for RX-ID-A is removed. Filters set through PassThruStartMsgFilter remain active.

C/C++ example

#include "j2534_dll.hpp"

unsigned long ChannelID;  // TP 2.0 channel
SBYTE_ARRAY InputData;
unsigned char data[4];
long ret;

// RX-ID-A (the same as in REQUEST_CONNECTION)
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x03;
data[3] = 0x01;  // 0x301

InputData.NumOfBytes = 4;
InputData.BytePtr = data;

ret = PassThruIoctl(ChannelID, TEARDOWN_CONNECTION, &InputData, NULL);
if (ret == STATUS_NOERROR)
{
    printf("Connection teardown request sent\n");
}

Kotlin example (Android)

// RX-ID-A: 0x301
val data = byteArrayOf(0x00, 0x00, 0x03, 0x01)

val result = j2534.ptIoctl(channelID, TEARDOWN_CONNECTION, data.size, data)
if (result.status == STATUS_NOERROR) {
    Log.i("TP2.0", "Connection teardown request sent")
}

Python example

from ctypes import *

# RX-ID-A: 0x301
data = (c_ubyte * 4)(0x00, 0x00, 0x03, 0x01)

input_data = SBYTE_ARRAY()
input_data.NumOfBytes = 4
input_data.BytePtr = data

ret = j2534.PassThruIoctl(channel_id, TEARDOWN_CONNECTION, byref(input_data), None)
if ret == 0:
    print("Connection teardown request sent")

C# example

// RX-ID-A: 0x301
var data = new byte[] { 0x00, 0x00, 0x03, 0x01 };

var inputData = new SBYTE_ARRAY {
    NumOfBytes = 4,
    BytePtr = data
};

int ret = J2534.PassThruIoctl(channelId, TEARDOWN_CONNECTION, ref inputData, IntPtr.Zero);
if (ret == 0)
    Console.WriteLine("Connection teardown request sent");