Communication protocols found in BMS46 firmware:


PROTO1: [

comment:

A basic Bootloader communication protocol operating on 9600bps SCI instead of a UART, probably used for debugging.

SCI_exchange_packet() calls appropriate handler (found by comparing in[1] (the second byte of the request) to in_PID) every time a new packet arrives.

Every handler gets a pointer to the received packet (in[]) and a pointer to a blank packet "to be transmitted as response" (out[]) and returns the size of the output packet in Bytes.

The response packet will have out[1] id as out_PID.

Generally the maximum size of a request/response is 208 bytes.


example:

in[] : 07 15 05 01 4B 7A 27

out[]: 08 EB 00 01 02 03 00 E3


- The greyed part is the actual body/data returned from handlers and the rest is header/footer.

- This 0x07 Bytes request goes to a handler with in_PID = 0x15 which reads 0x05 Bytes from the address 0x014B7A. The whole request is guarded by a XOR checksum 0x27.

- The 0x08 Bytes response with out_PID = 0xEB returns these bytes which happen to be {0x00, 0x01, 0x02, 0x03, 0x00}. The whole response is guarded by a XOR checksum 0xE3.

- When an error occurs while handling the request, the following response is generated:

out[]: 04 02 XX YY

The error-code field is one of the following: {

GENERAL_ERR = 0x00,

CHECKSUM_ERR = 0x02,

TIMEOUT_ERR = 0x03,

SIZE_ERR = 0x03,

OUT_OF_RANGE_ERR = 0x05

}

The whole error response is guarded by a XOR checksum 0xYY.


handler_0x03: [

in_PID: 0x03

out_PID: 0x01

comment:

This handler is not implemented and returns 0 (output size)

],

handler_0x14: [

in_PID: 0x14

out_PID: 0xEC

comment:

Moves an aggregated magic bytes string to output packet.

Creates "0261204420" + "F000" + "1430940" + "000" + "4399" + ".26\xff..." + "078" + "26RT5134" + "000368511" on firmware version 01119700F000P2AG.

Returns 75 (length of all strings combined)

],

handler_0x15: [

in_PID: 0x15

out_PID: 0xEB

comment:

Exactly the same as PROTO1::handler_0x24

Reads bytes from the given address in memory to the output packet.

Expects the following structured input: {

in[2] -> size of the block

in[3] -> higher byte of the address

in[4] -> middle byte of the address

in[5] -> lower byte of the address

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

If the desired size (in[2]) is greater than 200 it returns an error packet with code 0x05 (OUT_OF_RANGE_ERR).

Normally returns the size (from in[2]).

],

handler_0x16: [

in_PID: 0x16

out_PID: 0x01

comment:

Copies bytes from the input packet to the given address in memory.

Expects the following structured input: {

in[2] -> size of the payload

in[3] -> higher byte of the address

in[4] -> middle byte of the address

in[5] -> lower byte of the address

in[6...SIZE] -> raw bytes payload to copy

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

Never fails.

Returns 0 (size) -> responds with a blank packet.

],

handler_0x17: [

in_PID: 0x17

out_PID: 0xE9

comment:

Reads words from the LJURR table at a given idx to the output packet.

Note that the [L]eft [J]ustified [U]nsigned [R]esult [R]egisters table contains the raw analog sensor values directly after QADC read.

The CCW table maps idx -> channel.

For example requesting the word at idx 14 will return the raw analog value of sensor plugged in to channel 52

Further Reading: MC68336 manual QADC section.

Expects the following structured input: {

in[2] -> count of words

in[3] -> index of a starting word

}

Never fails.

The higher byte of the requested word is at an even addr starting from 0x02 and the lower byte follows in the next addr

For example when requested word is LJURR[12] == 0xabcd then out[2] = 0xab and out[3] = 0xcd.

Returns count of words (in[2]) times 2 to get the byte size of output packet.

],

handler_0x18: [

in_PID: 0x18

out_PID: 0xE8

comment:

This handler is not implemented and returns 0 (size).

],

handler_0x19: [

in_PID: 0x19

out_PID: 0x01

comment:

This handler is not implemented and returns 0 (size).

],

handler_0x20: [

in_PID: 0x20

out_PID: 0xE0

comment:

Reads at most 200 bytes from some memory region between two pointers (not selectable) to the output packet.

Maybe it outputs the last received packet (history) or the current one (for debugging).

Does not use the input packet.

Never fails.

Returns count of bytes between start_ptr and end_ptr (at most 200).

],

handler_0x21: [

in_PID: 0x21

out_PID: 0xDF

comment:

Sends some commands using SPI, then reads the response from SPI to output packet

Maybe it starts or tests SPI communication.

Does not use the input packet.

Never fails.

Returns 10 (size of the SPI response).

],

handler_0x22: [

in_PID: 0x22

out_PID: 0xDE

comment:

Moves 21 constant bytes to the output packet.

The values are the same for every firmware version.

The bytes: {

0x01, 0x45, 0x9A,

0x03, 0xFD, 0x60,

0x03, 0xFD, 0x20,

0x03, 0xFD, 0x24,

0x03, 0xFD, 0x38,

0x03, 0xFD, 0x3C,

0x03, 0xFD, 0x44

}

Does not use input packet.

Never fails.

Returns 21 (size).

],

handler_0x24: [

in_PID: 0x24

out_PID: 0xDC

comment:

Exactly the same as PROTO1::handler_0x15

Reads bytes from the given address in memory to the output packet.

Expects the following structured input: {

in[2] -> size of the block

in[3] -> higher byte of the address

in[4] -> middle byte of the address

in[5] -> lower byte of the address

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

If the desired size (in[2]) is greater than 200 it returns an error packet with code 0x05 (OUT_OF_RANGE_ERR).

Normally returns the size (from in[2]).

],

handler_0x30: [

in_PID: 0x30

out_PID: 0xD0

comment:

This function is maybe doing some inter-chip-module testing.

First it changes IMB setup to allow big-block moves between chips.

Then it does a dynamic relocation of a function and calls it with this requested address.

Runs some QADC related jobs and communicates with TPU registers.

At the end it behaves like PROTO1::handler_0x15 or PROTO1::handler_0x24 (reads result from the given address), but doesn't check the requested size.

Expects the following structured input: {

in[2] -> size of the block

in[3] -> higher byte of the address

in[4] -> middle byte of the address

in[5] -> lower byte of the address

in[6...] -> bytes passed to a strange function

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

Returns the size (from in[2]).

],

handler_0x40: [

in_PID: 0x40

out_PID: 0xC0

comment:

Calls a given function passing this handler's parameters and returns it's return value.

Maybe it is used as a thunk for testing handlers of this protocol.

If the address requested is an address of a handler in this protocol, then it is just thunk-called from there.

Expects the following structured input: {

in[2] -> higher byte of the address

in[3] -> middle byte of the address

in[4] -> lower byte of the address

in[5...] -> payload to be passed to the designated handler

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

Re-returns what the requested handler returned.

]

]




PROTO2A: [

handler_type:

void (*)(void)


comment:

The protocol is the same as PROTO2B, but this is the Boot version that has different handlers

A periodically scheduled Bootloader job calls the appropriate handler (found by in_PID) if a new received message is available.

The max output packet size is generally 251 Bytes

This protocol uses three global variables instead of function arguments.

1. PROTO2_in_buff

2. PROTO2_out_buff

3. PROTO2_message_size

The output packet's out_PID (out[2]) is one of these four values: {

0xA0 -> OKAY

0xA1 -> BUSY

0xA2 -> ERROR_ECU_REJECTED

0xB0 -> ERROR_ECU_PARAMETER

0xB1 -> ERROR_ECU_FUNCTION

0xB2 -> ERROR_ECU_NUMBER

0xFF -> ERROR_ECU_NACK

0x00 -> ERROR_ECU_UNKNOWN_STATUSBYTE

}


handler_0x00: [

in_PID: 0x00

comment:

Moves an aggregated magic bytes string to the output packet.

Creates "1430940" + "61\xff..." + "4399" + "0010213510" + "F000" + "000368511" on firmware version 01119700F000P2AG.

The type of the response is marked as 0xA0.

Never fails.

Sets PROTO2_message_size = 42 (length of all strings combined).

]

handler_0x06: [

in_PID: 0x06

comment:

Reads bytes from a given address (in a given memory region) to the output packet.

If the start address belongs to the region, but the size is too big, then the output size is clamped to fit.

If no memory region is requested (0x00) then:

1. RAM_REGION is checked (0xFF8000 - 0xFF9DFF);

2. ROM_REGION is checked (0x000000 - 0x03FFFF);

If there is still no match, the handler returns empty output packet of type 0xB0

Expects the following structured input: {

in[3] -> type of memory region

in[4] -> higher byte of the address

in[5] -> middle byte of the address

in[6] -> lower byte of the address

in[7] -> size of the input packet

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

The possible ram regions (in[3]): {

RAM_REGION (0xFF8000 - 0xFF9DFF) -> {0x04},

BOOT_REGION (0x000000 - 0x03FFFF) -> {0x02 OR 0x06},

FULL_REGION (0x000000 - 0xFFFFFF) -> {0x00 OR 0x0F},

INVALID_REG (0x000001 - 0x000000) -> {0x01 OR 0x03 OR 0x05 OR 0x0E}

}

Note that it tries to protect ISN (0x3F64 - 0x3F6F) and when this addr is requested it will add an offset +0x3BD4C redirecting to another magic byte.

The type of the response out_PID (out[2]) is 0xA0.

Sets PROTO2_message_size to the clamped size.

]

handler_0x07: [

in_PID: 0x07

comment:

This handler is bound-checking the pointer, relocating some blocks, and at the end returning the pointer if it was valid.

Expects the following structured input: {

in[3] -> type of memory region

in[4] -> higher byte of the address

in[5] -> middle byte of the address

in[6] -> lower byte of the address

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

The possible ram regions (in[3]): {

RAM_REGION (0xFF8000 - 0xFF9DFF) -> {0x04},

BOOT_REGION (0x000000 - 0x03FFFF) -> {0x02 OR 0x06},

FULL_REGION (0x000000 - 0xFFFFFF) -> {0x00 OR 0x0F},

INVALID_REG (0x000001 - 0x000000) -> {0x01 OR 0x03 OR 0x05 OR 0x0E}

On error (pointer not in range) returns a blank packet with type out_PID (out[2]): 0xB0.

Sets output packet type (out[2]) to 0xA0 (most of the times), or 0xA2 after handling FULL_REGION.

]

handler_0x0A: [

in_PID: 0x0A

comment:

Takes a simple 1Byte CRC16-IBM checksum of the firmware and moves that to the output packet.

If USER_END magic byte (0x55FFAA00) is invalid or inaccesible, it takes the checksum only on the critical 1st stage sector (0x0000 - 0x3FFF).

If there is a magic byte mismatch, returns 0xFF as checksum.

Does not use the input packet.

Never fails.

Sets output packet type (out[2]) to 0xA0.

Sets the PROTO2_message_size to 1 (only 1Byte checksum).

]

handler_0x0D: [

in_PID: 0x0D

comment:

Moves 72 constant bytes to the output packet.

The bytes are the same for every firmware version.

The values: {

0x00, 0x37, 0x3A,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0x00, 0x80, 0x10,

0x01, 0x44, 0xB0,

0x00, 0x37, 0x3C,

0x00, 0x3F, 0xE6,

0x03, 0xFC, 0xB0,

0x03, 0xFD, 0x60,

0x00, 0x37, 0x3D,

0x03, 0xFD, 0x20,

0x00, 0x3F, 0x20

}

Does not use the input packet.

Never fails.

Sets output packet type (out[2]) to 0xA0.

Sets the PROTO2_message_size to 72 (combined length of all magic bytes).

]

handler_0x90: [

in_PID: 0x90

comment:

Sends a magic bytes string like handler_0x00, then checksums it, then does a lot of set/clear bit operations.

Expects the following structured input: {

in[1] -> MUST be 8 (the length of the whole input packet)

in[3] -> MUST be 'B' (0x42)

in[4] -> MUST be 'M' (0x4D)

in[5] -> MUST be 'W' (0x57)

}

Never fails.

Normally outputs a blank packet with type (in[2]) 0xA2.

Sometimes outputs a 1 byte (0x00) packet with type (in[2]) 0xA0.

]

handler_0x91: [

in_PID: 0x91

comment:

Tries to find a match for 3 adjacent values in some table.

If there is no match: outputs a blank packet of type (out[2]) 0xB0.

If the match is found: saves the single byte payload in some variable, then outputs a blank packet of type (out[2]) 0xA0.

Expects the following structured input: {

in[3] -> 1st value

in[4] -> 2nd value

in[5] -> 3rd value

in[6] -> payload

}

Never fails.

Always outputs a blank packet.

Sets the PROTO2_message_size to 0.

]

]


PROTO2B: [

handler_type:

void (*)(void)


comment:

The protocol is the same as PROTO2A, but this is the User version that has more handlers.

A periodically scheduled Userland job calls the appropriate handler (found by in_PID) if a new received message is available.

The max output packet size is generally 96 Bytes

This protocol uses three global variables instead of function arguments.

1. PROTO2_in_buff

2. PROTO2_out_buff

3. PROTO2_message_size

The output packet's out_PID (out[2]) is one of these four values: {

0xA0 -> OKAY

0xA1 -> BUSY

0xA2 -> ERROR_ECU_REJECTED

0xB0 -> ERROR_ECU_PARAMETER

0xB1 -> ERROR_ECU_FUNCTION

0xB2 -> ERROR_ECU_NUMBER

0xFF -> ERROR_ECU_NACK

0x00 -> ERROR_ECU_UNKNOWN_STATUSBYTE

}


handler_0x00: [

in_PID: 0x00

comment:

Moves an aggregated magic bytes string to the output packet.

Creates "1430940" + "61" + "00" + "A7" + "60" + "4399" + "0010213510" + "F0000" + "000368511" on firmware version 01119700F000P2AG.

Does not use input packet.

Never fails.

Sets PROTO2_message_size = 42 (combined length of magic bytes).

]

handler_0x04: [

in_PID: 0x04

comment:

Moves some words to the output packet.

Expects the following structured input: {

in[3] -> requested size

}

If requested size is greater or equal than 11, this handler returns a blank packet of type 0xB0 and size 0.

If requested size is greater than a counter (*0xff96bf), this handler returns the counter out[3] and 0x00 Byte in out[4] with type 0xA0 and size 2.

If requested size is 0, this handler returns the counter (out[3]) and two words (higher byte first) (out[4...7]) with type 0xA0 and size 5.

Else, move up to 38 words of data (starting from 0xff96d3 region) to the output packet of type 0xA0.

Never fails.

]

handler_0x05: [

in_PID: 0x05

comment:

The handler sets 7th bit in some unknown varialbe and returns a blank output packet.

Does not use the input packet.

Never fails.

Outputs blank packet with type 0xA0 and size 0.

]

handler_0x06: [

in_PID: 0x06

comment:

Almost the same as PROTO2A::handler_0x06, but this handler additionally avoids two other regions.

Reads bytes from a given address (in a given memory region) to the output packet.

If the start address belongs to the region, but the size is too big, then the output size is clamped to fit.

If no memory region is requested (0x00) then:

1. RAM_REGION is checked (0xFF8000 - 0xFF9DFF);

2. ROM_REGION is checked (0x000000 - 0x03FFFF);

If there is still no match, the handler returns empty output packet of type 0xB0

Expects the following structured input: {

in[3] -> type of memory region

in[4] -> higher byte of the address

in[5] -> middle byte of the address

in[6] -> lower byte of the address

in[7] -> size of the input packet

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

The possible ram regions (in[3]): {

RAM_REGION (0xFF8000 - 0xFF9DFF) -> {0x04},

BOOT_REGION (0x000000 - 0x03FFFF) -> {0x02 OR 0x06},

FULL_REGION (0x000000 - 0xFFFFFF) -> {0x00 OR 0x0F},

INVALID_REG (0x000001 - 0x000000) -> {0x01 OR 0x03 OR 0x05 OR 0x0E}

}

Note that it tries to protect ISN (0x3F64 - 0x3F6F) and when this addr is requested it will add an offset +0x3BD4C redirecting to another magic byte.

This version also protects two other regions:

1. ENCRYPTED_ISN (0xFF95F0 - 0xFF95FB) by applying offset -0xff8000.

2. UNKNOWN (0xFF8670 - 0xFF867B) by applying offset -0xff8000.

The type of the response out_PID (out[2]) is 0xA0.

Sets PROTO2_message_size to the clamped size.

]

handler_0x07: [

in_PID: 0x07

comment:

Almost the same as PROTO2A::handler_0x07.

This handler is bound-checking the pointer, relocating some blocks, and at the end returning the pointer if it was valid.

Expects the following structured input: {

in[3] -> type of memory region

in[4] -> higher byte of the address

in[5] -> middle byte of the address

in[6] -> lower byte of the address

}

Note that the MC68336 in BMS46 has a 24bit addressing IMB.

The possible ram regions (in[3]): {

RAM_REGION (0xFF8000 - 0xFF9DFF) -> {0x04},

BOOT_REGION (0x000000 - 0x03FFFF) -> {0x02 OR 0x06},

FULL_REGION (0x000000 - 0xFFFFFF) -> {0x00 OR 0x0F},

INVALID_REG (0x000001 - 0x000000) -> {0x01 OR 0x03 OR 0x05 OR 0x0E}

}

On error (pointer not in range) returns a blank packet with type out_PID (out[2]): 0xB0.

Sets output packet type (out[2]) to 0xA0 (most of the times), or 0xA2 after handling FULL_REGION.

]

handler_0x0A: [

in_PID: 0x0A

comment:

This handler is the same as PROTO2A::handler_0x0A, but there are more memory region to take checksum of.

Takes a simple 1Byte CRC16-IBM checksum of the firmware and moves that to the output packet.

If USER_END magic byte (0x55FFAA00) is invalid or inaccesible, it takes the checksum only on the critical 1st stage sector (0x0000 - 0x3FFF).

If there is a magic byte mismatch, returns 0xFF as checksum.

Does not use the input packet.

Never fails.

Sets output packet type (out[2]) to 0xA0.

Sets the PROTO2_message_size to 1 (only 1Byte checksum).

]

handler_0x0B: [

in_PID: 0x0B

comment:

Reads some vital Vechicle info to the output packet.

Expects the following structured input: {

in[3] -> type of the data

}

If the requested type is 0x03 then it moves 70 bytes to the output packet of type 0xA0.

Those 70 bytes contain some vital Vechicle raw/live data (words are stored MSB first).

Unfortunately i don't know which bytes mean what...

The ones i can confirm with 90% certainty are:

out[3...4] (word) -> Engine RPM raw analog value.

out[5...6] (word) -> Engine Load raw analog value.

out[9] -> Engine Load calibrated/scaled value.

out[11] -> Vechicle Speed calibrated/scaled value.

out[27...28] -> Throttle position raw analog value.

If the requested type is 0x04 then it moves 5 most vital (for the Vechicle) bitfields to the output packet of type 0xA0.

If the requested type is 0xA2 then it moves 2 words and 1 byte to the output packet of type 0xA0.

If the requested type is 0xA3 then it moves 6 bytes to the output packet of type 0xA0.

Else a blank output packet of type 0xB0 is returned.

]

handler_0x0C: [

in_PID: 0x0C

comment:

Provides actuation of various components.

Every actuation handler returns status code: {

0x0 -> SUCCESS

0x1 -> INVALID_PIN

0x2 -> INVALID_DUTY_CYCLE

0x3 -> INVALID_PERIOD_DURATION

0x4 -> INVALID_CONDITIONS

}

Expects the following structured input: {

in[3] -> requested action

in[4] -> actuation setpoint

}

The possible indices are: {

0x1F -> control_Lambda2_heater(byte pwm_duty)

If the Lambda2 is not accessible or not equipped it returns INVALID_PIN, if the Lambda2 dewpoint is exceeded or the pwm_duty is greater than 51, it returns INVALID_CONDITIONS, else it sets the PWM worker duty cycle to pwm_duty with the multiplier (pwm_duty * 3120) / 255

0x21 -> control_Lambda1_heater(byte pwm_duty)

If the Lambda1 is not accessible or not equipped it returns INVALID_PIN, if the Lambda1 dewpoint is exceeded or the pwm_duty is greater than 51, it returns INVALID_CONDITIONS, else it sets the PWM worker duty cycle to pwm_duty with the multiplier (pwm_duty * 3120) / 255

0x36 -> ...

0x49 -> ...

0x4C -> ...

0x4D -> ...

0x4E -> control_DISA(byte is_open)

If the is_open is 0x00, it commands the DISA valve to close, if the is_open is 0xFF it opens the DISA, else returns INVALID_DUTY_CYCLE

0x51 -> control_injector_CYL4(byte setpoint)

If the setpoint is 0x00, it enables fuel cutoff, else checks running conditions and either enables maximum injection or returns INVALID_CONDITIONS

0x52 -> control_injector_CYL3(byte setpoint)

If the setpoint is 0x00, it enables fuel cutoff, else checks running conditions and either enables maximum injection or returns INVALID_CONDITIONS

0x53 -> control_injector_CYL2(byte setpoint)

If the setpoint is 0x00, it enables fuel cutoff, else checks running conditions and either enables maximum injection or returns INVALID_CONDITIONS

0x54 -> control_injector_CYL1(byte setpoint)

If the setpoint is 0x00, it enables fuel cutoff, else checks running conditions and either enables maximum injection or returns INVALID_CONDITIONS

0x58 -> ...

0x59 -> ...

0x5F -> control_Fuel_Pump(byte is_enabled)

If the is_enabled is other than 0xFF it returns INVALID_DUTY_CYCLE, if the Vehicle Speed is not 0 or the Fuel Pump cannot be controlled, return INVALID_CONDITIONS, else check additional conditions and power up the Fuel Pump

0x72 -> control_AC_compressor(byte is_enabled)

If the is_enabled is 0xFF it STOPS the AC compressor, if the is_enabled is 0x00 the compressor is being ENABLED, else it returns INVALID_DUTY_CYCLE

0x74 -> control_Secondary_Air_Valve(byte is_enabled)

If the Secondary Air Valve is not accessible or not equipped it returns INVALID_PIN, if the is_enabled is 0xFF it CLOSES the SA Valve, if the is_enabled is 0x00 it OPENS the valve, else it returns INVALID_DUTY_CYCLE

0x91 -> control_MIL(byte is_enabled)

If the vehicle can't access MIL, it returns INVALID_CONDITIONS, if the is_enabled is 0x00 it clears the MIL flag, if the is_enabled is 0xFF it lights up the MIL, else it returns INVALID_DUTY_CYCLE

}

This handler returns a 2 byte output packet: out[3] = in[3] (requested actuator index repeated), out[4] = RET (one of the result values, depending on what the dispatched handler returns)

]

handler_0x0D: [

in_PID: 0x0D

comment:

Almost the same as PROTO2A::handler_0x0D, but some values are different.

Moves 72 constant bytes to the output packet.

The bytes are the same for every firmware version.

The values: {

0x01, 0x45, 0x9A,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF,

0x00, 0x80, 0x10,

0x01, 0x44, 0xB0,

0x01, 0x45, 0x9C,

0x00, 0x3F, 0xE6,

0x03, 0xFC, 0xB0,

0x03, 0xFD, 0x60,

0x01, 0x45, 0x9D,

0x03, 0xFD, 0x20,

0x00, 0x3F, 0x20

}

Does not use the input packet.

Never fails.

Sets output packet type (out[2]) to 0xA0.

Sets the PROTO2_message_size to 72 (combined length of all magic bytes).

]

handler_0x0E: [

in_PID: 0x0E

comment:

Reads 3 bytes that are only ever used by this handler_0x0E and handler_0x0F.

Maybe restoring some modification timestamp or signature.

Moves 3 bytes to the output packet of type 0xA0.

Does not use the input packet.

Never fails.

]

handler_0x0F: [

in_PID: 0x0F

comment:

Writes 3 bytes that are only ever used by this hander_0x0F and handler_0x0E.

Maybe setting some modification timestamp or signature.

Expects the bytes in in[3...5].

Returns a blank packet of type 0xA0.

Never fails.

]

handler_0x14: [

in_PID: 0x14

comment:

Expects the following structured input: {

in[3] -> count of something

}

If count 0x00 is requested, then this handler counts some matching bytes (up to 76) and writes the size + 2 words to the output packet.

the size of the packet is 5 Bytes and the type is 0xA0.

Never fails.

]

handler_0x1B: [

in_PID: 0x1B

comment:

Expects the following structured input: {

in[3] -> MUST be 0xFE

in[4] -> index

}

If the index is 0x01, then this handler moves a compilation string from 0x14000 of size 96 to the output packet of type 0xA0.

If the index is 0x02, then this handler moves 2 ints and 7 bytes (combined size 15 Bytes) to the output packet of type 0xA0.

If the index is 0x14, then this handler thunk-calls handler_0x14.

If the index is 0x20, then this handler moves 40 bytes starting from 0xFF96C0 + some other 7 bytes to the output packet of size 47 and type 0xA0.

If the index is 0x21, then this handler moves vital Vechicle bytes and words of combined size 51 Bytes to the output packet of type 0xB0

Else it returns a blank packet of type 0xB0.

]

handler_0x1C: [

in_PID: 0x1C

comment:

Prob continuation of handler_0x1B

]

handler_0x22: [

in_PID: 0x22

comment:

Requires the input in[3] to be equal 0x02

Calls a function that does almost nothing.

]

handler_0x23: [

in_PID: 0x23

comment:

Requires the input in[3] to be equal 0x02

Calls a function that does almost nothing.

]

handler_0x2B: [

in_PID: 0x2B

comment:

Requires the input in[3] to be equal 0x02

Calls a function that does almost nothing.

]

handler_0x40: [

in_PID: 0x40

comment:

This handler reads one byte from an internal memory variable.

Expects the following structured input: {

in[3] -> flag (either 1 or 2 or undefined)

}

If the flag is 1, then value from 0xFF8530 is returned, if flag is 2, value from 0xFF843B is returned.

This handler sets the output size to 1 and type 0xA0 if the flag is 1 or 2 and else returns blank packet of type 0xB0.

Note that the same variables are used in handler_0x41 and handler_0x42.

]

handler_0x41: [

in_PID: 0x41

comment:

This handler writes one byte to an internal memory variable.

Expects the following structured input: {

in[3] -> flag (either 1 or 2 or undefined)

in[4] -> new value

}

If the flag is 1, some bitfields are checked, and then the new value is written to 0xFF8530.

If the flag is 2 and (98 <= new value <= 158), then the new value is written to 0xFF843B.

This handler returns a blank packet.

If the write was successful, the output packet has type 0xA0.

If some conditions weren't met, the output packet has type 0xA2.

If the flag was not 1 nor 2, the output packet has type 0xB0.

Note that the same variables are used in handler_0x40 and handler_0x42.

]

handler_0x42: [

in_PID: 0x42

comment:

This handler moves one byte between internal memory variables.

Expects the following structured input: {

in[3] -> flag (either 1 or 2 or undefined)

}

If the flag is 1, some bitfields are checked, and then the value is moved from 0xFF8530 to 0xFF9859.

If the flag is 2, the value from 0xFF843B is clamped to range [98, 158] and copied to 0xFF985A.

This handler returns a blank packet.

If the swap was successful, the output packet has type 0xA0.

If some conditions weren't met, the output packet has type 0xA2.

If the flag was not 1 nor 2, the output packet has type 0xB0.

Note that the same variables are used in handler_0x40 and handler_0x41.

]

handler_0x43: [

in_PID: 0x43

comment:

This handler resets some internal memory variables based on bitfields.

Expects the following structured input: {

in[3] -> first bitfield

in[4] -> second bitfield

}

Always returns a blank packet of type 0xA0.

Never fails.

]

handler_0x53: [

in_PID: 0x53

comment:

Reads a string of magic bytes.

Does not use the input packet.

On firmware version 01119700F000P2AG it creates 0x33 + "0261204420" + "078" + "26RT5134" + 0x03 + 0xFD + 0x3C + 0x03 + 0xFD + 0x24 + 0x03 + 0xFD + 0x44

Size of the packet is 31.

Never fails.

]

handler_0x6C: [

in_PID: 0x6C

comment:

This handler checks some pre-conditions based on the given flag.

Expects the following structured input: {

in[3] -> a flag (0 or 1 or undefined)

}

Note that this function clears two bytes in the EWS every time (0xFF8680 and 0xFF86AF).

The handler checks exactly these things:

1. Is the ISN valid? Every word in ISN (0^1, 0^2, 1^2) XOR'ed must be 0:

0: ISN[0] (0x3F64 - 0x3F64)

1: ISN[1] (0x3F66 - 0x3F67)

2: ISN[2] (0x3F68 - 0x3F69)

Additionaly XOR'ing the pre-hashed pairs (0^1, 0^2, 1^2) in the same way as before must equal 0:

0: (ISN[0] ^ 0x0D0B)

1: (ISN[1] ^ 0x1D17)

2: (ISN[2] ^ 0x251F)

If the ISN is invalid, moves 3 to the output packet.

2. Is the flag bigger than 1?

This is error, return a packet of type 0xB0.

3. Is the EWS unlocked/valid?

If EWS locked/invalid and the flag is different than 1, a 1 is returned in output packet.

If EWS unlocked/valid and the flag is different than 0, a 2 is returned in output packet.

4. Is the Engine running?

If the engine is running return a packet of type 0xA2.

If the error code (or success code) is in the range [0, 3], this handler moves it to the output packet of type 0xA0.

If the error code (or success code) is out of range, this handler returns a blank packet with this code being the type.

]

handler_0x6D: [

in_PID: 0x6D

comment:

This handler checks some pre-conditions based on an internal bitfield (0xFF86AD).

Does not use the input packet.

If the error code (or success code) is in the range [0, 2], this handler moves it to the output packet of type 0xA0.

If the error code (or success code) is out of range, this handler returns a blank packet with this code being the type.

]

handler_0x90: [

in_PID: 0x90

comment:

Almost the same as PROTO2A::handler_0x90

Sends a magic bytes string like handler_0x00, then checksums it, then does a lot of set/clear bit operations.

Expects the following structured input: {

in[1] -> MUST be 8 (the length of the whole input packet)

in[3] -> MUST be 'B' (0x42)

in[4] -> MUST be 'M' (0x4D)

in[5] -> MUST be 'W' (0x57)

}

Never fails.

Normally outputs a blank packet with type (in[2]) 0xA2.

Sometimes outputs a 1 byte (0x00) packet with type (in[2]) 0xA0.

]

handler_0x9E: [

in_PID: 0x9E

comment:

Always returns a blank packet of type 0xA0.

Does not use the input packet.

Never fails.

]

handler_0x9F: [

in_PID: 0x9F

comment:

Runs some generic TPU jobs and returns a blank packet of type 0xA0.

Does not use the input packet.

Never fails.

]

]




OBD2: [

comment:

A periodically scheduled Userland job calls the appropriate handler (found by in[0]) if a new received message is available.

The max output packet size is generally 11 Bytes

The output packet's out_PID (out) is one of these four values: {

0xA0 -> OKAY

0xA1 -> BUSY

0xA2 -> ERROR_ECU_REJECTED

0xB0 -> ERROR_ECU_PARAMETER

0xB1 -> ERROR_ECU_FUNCTION

0xB2 -> ERROR_ECU_NUMBER

0xFF -> ERROR_ECU_NACK

0x00 -> ERROR_ECU_UNKNOWN_STATUSBYTE

}

This protocol implements 7 actions [1, 7].


show_current_data: [

in_PID: 0x01

comment:

Moves live data variables (from J1979 [0x00-0x3F]) to the output packet.

Expects the following structured input: {

in[4] -> index of the wanted variable

}

Possible indices are: {

0x00 -> PID support status bitfield (0x01-0x20) 0b10111110000111111111100000010000

0x01 -> out[4] is from some bitfields, out[5] = 0x07, out[4] = 0x69, out[5] = *0xFF980E

0x03 -> out[4] is from some bitfields, out[5] = 0x00

0x04 -> calculated Engine_Load

0x05 -> Engine_Load

0x06 -> ...

0x07 -> ...

0x0C -> Engine_RPM

0x0D -> Vechicle_Speed

0x0E -> maybe Engine_Ignition_Advance_n1

0x0F -> Engine_IAT

0x10 -> maybe Engine_MAF

0x11 -> Engine_Throttle_Percentage

0x12 -> Commanded secondary air status 0x01 if some bit is set, else 0x04

0x13 -> Location of oxygen sensors - always returns Bank1_Sensor1 and Bank1_Sensor2

0x14 -> ...

0x15 -> ...

0x1C -> OBD certification - always returns 0x06 EOBD (Euro OBD certified car)

0x20 -> PID support status bitfield (0x21-0x40) 0b10000000000000000000000000000000

0x21 -> Vechicle_Distance_On_MIL

}

The response consists of out[3] = 0x41 (handler idx (1) + 0x40 which means successful response), out[4] = in[4] (wanted index), out[5...] (response bytes)

],

show_freeze_frame_data: [

in_PID: 0x02

comment:

Moves freeze frame variables to the output packet.

Expects the following structured input: {

in[4] -> index of the wanted variable

in[5] -> unknown byte

}

Possible indices are: {

0x00 -> PID support status bitfield 0b01111110000110000000000000000000

0x02 -> DTC that caused freeze frame

0x03 -> Fuel System Status at freeze frame

0x04 -> Calculated Engine Load at freeze frame

0x05 -> Engine Coolant Temp at freeze frame

0x06 -> Short term fuel trim STFT Bank1 at freeze frame

0x07 -> Long term fuel trim LTFT Bank1 at freeze frame

0x0C -> Engine RPM at freeze frame

0x0D -> Vechicle Speed at freeze frame

}

The response consists of out[3] = 0x42 (handler idx (2) + 0x40 which means successful response), out[4] = in[4] (wanted index), out[5] = in[5] (some byte repeated), out[6...] (response bytes)

],

show_stored_DTC: [

in_PID: 0x03

comment:

Moves all DTCs with info to the output packets (segmented using ISO 15765-2).

Does not use the input packet.

The response consists of out[3] = 0x43 (handler idx (3) + 0x40 which means successful response), out[4...] each DTC frame has 6 bytes.

],

clear_DTC: [

in_PID: 0x04

comment:

Clears all DTCs and saved freeze frames.

Does not use the input packet.

The response consists of out[3] = 0x44 (handler idx (4) + 0x40 which means successful response)

],

handler_0x05: [

in_PID: 0x05

comment:

Moves some unknown (prob Vendor specific) bytes to the output packet.

Expects the following structured input: {

in[4] -> index of an action

in[5] -> an unknown byte that MUST BE 1 OR 2

}

Possible indices are: {

0x00 -> PID support status bit 0b00000011000000000000000000000000

0x07 -> returns {out[6] = VALUE, out[7] = 0x00, out[8] = 0x3D} where VALUE is *0xFF8525 if in[5] is 1 or *0xFF863A if in[5] is 2

0x08 -> returns {out[6] = VALUE, out[7] = 0x7E, out[8] = 0xFF} where VALUE is *0xFF8526 if in[5] is 1 or *0xFF863B if in[5] is 2

}

],

handler_0x06: [

in_PID: 0x06

comment:

Moves some unknown (prob Vendor specific) bytes to the output packet.

Expects the following structured input: {

in[4] -> index of an action

}

Every index returns 7 bytes.

Possible indices are: {

0x00 -> support status bit but 5 bytes this time {0xFF, 0xE4, 0x00, 0x00, 0x00}

0x01 -> ...

0x02 -> ... (sends two packets at once)

0x03 -> ...

0x06 -> ... (sends two packets at once)

],

show_permanent_DTC: [

in_PID: 0x07

comment:

Moves all Permanent (cleared) DTCs with info to the output packets (segmented using ISO 15765-2).

Does not use the input packet.

The response consists of out[3] = 0x47 (handler idx (7) + 0x40 which means successful response), out[4...] each DTC frame has 6 bytes.

]

]