mirror of https://github.com/rusefi/isotp-c.git
Add multiframe support
This commit is contained in:
parent
2c99a9853d
commit
1a28f2f006
28
README.mkd
28
README.mkd
|
@ -58,7 +58,8 @@ With your shims in hand, send an ISO-TP message:
|
|||
// You received the message! Do something with it.
|
||||
}
|
||||
|
||||
IsoTpSendHandle handle = isotp_send(&shims, 0x100, NULL, 0, message_sent);
|
||||
IsoTpSendHandle message = isotp_new_send_message(0x100, data, size);
|
||||
IsoTpSendHandle handle = isotp_send(&shims, &message, message_sent);
|
||||
|
||||
if(handle.completed) {
|
||||
if(!handle.success) {
|
||||
|
@ -75,15 +76,24 @@ With your shims in hand, send an ISO-TP message:
|
|||
// this will return true when the message is completely sent (which
|
||||
// may take more than one call if it was multi frame and we're waiting
|
||||
// on flow control responses from the receiver)
|
||||
bool complete = isotp_continue_send(&shims, &handle, 0x100, data, size);
|
||||
if(can_get_frame(&af, &data, &size)) {
|
||||
if(!isotp_receive_flowcontrol(&shims, &handle, af, data, size)) {
|
||||
// Process error
|
||||
}
|
||||
|
||||
if(complete && handle.completed) {
|
||||
if(handle.success) {
|
||||
// All frames of the message have now been sent, following
|
||||
// whatever flow control feedback it got from the receiver
|
||||
} else {
|
||||
// the message was unable to be sent and we bailed - fatal
|
||||
// error!
|
||||
while(handle.to_send != 0) {
|
||||
if(!isotp_continue_send(&shims, &handle)) {
|
||||
// Process error
|
||||
}
|
||||
if(handle.completed) {
|
||||
if(handle.success) {
|
||||
// All frames of the message have now been sent, following
|
||||
// whatever flow control feedback it got from the receiver
|
||||
} else {
|
||||
// the message was unable to be sent and we bailed - fatal
|
||||
// error!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
#include <inttypes.h>
|
||||
|
||||
IsoTpShims isotp_init_shims(LogShim log, SendCanMessageShim send_can_message,
|
||||
SetTimerShim set_timer) {
|
||||
SetTimerShim set_timer, void* send_can_private) {
|
||||
IsoTpShims shims;
|
||||
shims.log = log;
|
||||
shims.send_can_message = send_can_message;
|
||||
shims.set_timer = set_timer;
|
||||
shims.frame_padding = ISO_TP_DEFAULT_FRAME_PADDING_STATUS;
|
||||
shims.private_data = send_can_private;
|
||||
|
||||
return shims;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ extern "C" {
|
|||
*/
|
||||
IsoTpShims isotp_init_shims(LogShim log,
|
||||
SendCanMessageShim send_can_message,
|
||||
SetTimerShim set_timer);
|
||||
SetTimerShim set_timer,
|
||||
void* send_can_private);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
#define PCI_NIBBLE_INDEX 0
|
||||
#define PAYLOAD_LENGTH_NIBBLE_INDEX 1
|
||||
#define SEQUENCE_NUMBER_NIBBLE_INDEX 1
|
||||
#define FLOW_CONTROL_NIBBLE_INDEX 1
|
||||
#define PAYLOAD_BYTE_INDEX 1
|
||||
|
||||
/* Private: The default timeout to use when waiting for a response during a
|
||||
|
@ -69,7 +71,7 @@ typedef void (*LogShim)(const char* message, ...);
|
|||
* Returns true if the CAN message was sent successfully.
|
||||
*/
|
||||
typedef bool (*SendCanMessageShim)(const uint32_t arbitration_id,
|
||||
const uint8_t* data, const uint8_t size);
|
||||
const uint8_t* data, const uint8_t size, void* private_data);
|
||||
|
||||
/* Public: The type signature for a... TODO, not used yet.
|
||||
*/
|
||||
|
@ -118,6 +120,7 @@ typedef struct {
|
|||
SendCanMessageShim send_can_message;
|
||||
SetTimerShim set_timer;
|
||||
bool frame_padding;
|
||||
void* private_data;
|
||||
} IsoTpShims;
|
||||
|
||||
/* Private: PCI types, for identifying each frame of an ISO-TP message.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __ISOTP_PROTOCOL_H__
|
||||
#define __ISOTP_PROTOCOL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Functions related to session-level protocol, should be implemented by user */
|
||||
|
||||
uint32_t protocol_swap_sa_ta (uint32_t arbitration);
|
||||
|
||||
#endif // __ISOTP_PROTOCOL_H_
|
|
@ -4,8 +4,6 @@
|
|||
#include <bitfield/bitfield.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ARBITRATION_ID_OFFSET 0x8
|
||||
|
||||
static void isotp_complete_receive(IsoTpReceiveHandle* handle, IsoTpMessage* message) {
|
||||
if(handle->message_received_callback != NULL) {
|
||||
handle->message_received_callback(message);
|
||||
|
@ -32,14 +30,15 @@ bool isotp_send_flow_control_frame(IsoTpShims* shims, IsoTpMessage* message) {
|
|||
return false;
|
||||
}
|
||||
|
||||
shims->send_can_message(message->arbitration_id - ARBITRATION_ID_OFFSET, can_data,
|
||||
shims->frame_padding ? 8 : 1 + message->size);
|
||||
shims->send_can_message(message->arbitration_id, can_data,
|
||||
shims->frame_padding ? 8 : 1 + message->size, shims->private_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
IsoTpReceiveHandle isotp_receive(IsoTpShims* shims,
|
||||
const uint32_t arbitration_id, IsoTpMessageReceivedHandler callback) {
|
||||
(void) shims;
|
||||
IsoTpReceiveHandle handle;
|
||||
handle.success = false;
|
||||
handle.completed = false;
|
||||
|
@ -110,7 +109,7 @@ IsoTpMessage isotp_continue_receive(IsoTpShims* shims,
|
|||
|
||||
if(combined_payload == NULL) {
|
||||
if(shims->log)
|
||||
shims->log("Unable to allocate memory for multi-frame response.");
|
||||
shims->log("Unable to allocate memory for multi-frame message.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
161
src/isotp/send.c
161
src/isotp/send.c
|
@ -1,4 +1,5 @@
|
|||
#include <isotp/send.h>
|
||||
#include <isotp/protocol.h>
|
||||
#include <bitfield/bitfield.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -8,6 +9,7 @@
|
|||
|
||||
void isotp_complete_send(IsoTpShims* shims, IsoTpMessage* message,
|
||||
bool status, IsoTpMessageSentHandler callback) {
|
||||
(void) shims;
|
||||
if(callback != NULL) {
|
||||
callback(message, status);
|
||||
}
|
||||
|
@ -38,44 +40,74 @@ IsoTpSendHandle isotp_send_single_frame(IsoTpShims* shims, IsoTpMessage* message
|
|||
memcpy(&can_data[1], message->payload, message->size);
|
||||
}
|
||||
|
||||
shims->send_can_message(message->arbitration_id, can_data,
|
||||
shims->frame_padding ? 8 : 1 + message->size);
|
||||
handle.success = true;
|
||||
isotp_complete_send(shims, message, true, callback);
|
||||
handle.success = shims->send_can_message(message->arbitration_id, can_data,
|
||||
shims->frame_padding ? 8 : 1 + message->size, shims->private_data);
|
||||
isotp_complete_send(shims, message, handle.success, callback);
|
||||
return handle;
|
||||
}
|
||||
|
||||
IsoTpSendHandle isotp_send_multi_frame(IsoTpShims* shims, IsoTpMessage* message,
|
||||
IsoTpMessageSentHandler callback) {
|
||||
/* TODO make sure to copy message into a local buffer */
|
||||
if(shims->log)
|
||||
shims->log("Only single frame messages are supported");
|
||||
IsoTpSendHandle handle = {
|
||||
success: false,
|
||||
completed: true
|
||||
};
|
||||
/* TODO need to set sending and receiving arbitration IDs separately if we
|
||||
can't always just add 0x8 (and I think we can't) */
|
||||
uint8_t can_data[CAN_MESSAGE_BYTE_SIZE] = {0};
|
||||
if(!set_nibble(PCI_NIBBLE_INDEX, PCI_FIRST_FRAME, can_data, sizeof(can_data))) {
|
||||
if(shims->log)
|
||||
shims->log("Unable to set PCI in CAN data");
|
||||
return handle;
|
||||
}
|
||||
|
||||
if(!set_nibble(PAYLOAD_LENGTH_NIBBLE_INDEX, message->size >> 8, can_data,
|
||||
sizeof(can_data))) {
|
||||
if(shims->log)
|
||||
shims->log("Unable to set payload length in CAN data");
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* One we got here message->size > 7 */
|
||||
|
||||
can_data[1] = message->size & 0xFF;
|
||||
|
||||
memcpy(&can_data[2], message->payload, 6);
|
||||
|
||||
if(!shims->send_can_message(message->arbitration_id, can_data, 8, shims->private_data)) {
|
||||
if(shims->log)
|
||||
shims->log("Error sending CAN frame");
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle.message = message;
|
||||
handle.payload_index = 6;
|
||||
handle.seqn = 1;
|
||||
handle.completed = false;
|
||||
handle.to_send = 0;
|
||||
handle.message_sent_callback = callback;
|
||||
handle.sending_arbitration_id = message->arbitration_id;
|
||||
handle.receiving_arbitration_id = protocol_swap_sa_ta(message->arbitration_id);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
IsoTpSendHandle isotp_send(IsoTpShims* shims, const uint16_t arbitration_id,
|
||||
const uint8_t payload[], uint16_t size,
|
||||
IsoTpMessageSentHandler callback) {
|
||||
IsoTpMessage isotp_new_send_message(const uint16_t arbitration_id, const uint8_t payload[], uint16_t size) {
|
||||
IsoTpMessage message = {
|
||||
arbitration_id: arbitration_id,
|
||||
size: size
|
||||
};
|
||||
|
||||
memcpy(message.payload, payload, size);
|
||||
if(size < 8) {
|
||||
return isotp_send_single_frame(shims, &message, callback);
|
||||
return message;
|
||||
}
|
||||
IsoTpSendHandle isotp_send(IsoTpShims* shims, IsoTpMessage* message, IsoTpMessageSentHandler callback) {
|
||||
if(message->size < 8) {
|
||||
return isotp_send_single_frame(shims, message, callback);
|
||||
} else {
|
||||
return isotp_send_multi_frame(shims, &message, callback);
|
||||
return isotp_send_multi_frame(shims, message, callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool isotp_continue_send(IsoTpShims* shims, IsoTpSendHandle* handle,
|
||||
bool isotp_receive_flowcontrol(IsoTpShims* shims, IsoTpSendHandle* handle,
|
||||
const uint16_t arbitration_id, const uint8_t data[],
|
||||
const uint8_t size) {
|
||||
/* TODO this will need to be tested when we add multi-frame support,
|
||||
|
@ -88,5 +120,100 @@ bool isotp_continue_send(IsoTpShims* shims, IsoTpSendHandle* handle,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if(size < 3 || get_nibble(data, 3, PCI_NIBBLE_INDEX) != PCI_FLOW_CONTROL_FRAME) {
|
||||
if(shims->log) {
|
||||
shims->log("Incorrect size (%d while %d expected) or frame type (%1x while %1x expected)",
|
||||
size, 3, get_nibble(data, 3, PCI_NIBBLE_INDEX), PCI_FLOW_CONTROL_FRAME);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(get_nibble(data, 3, FLOW_CONTROL_NIBBLE_INDEX)) {
|
||||
case PCI_FLOW_STATUS_CONTINUE:
|
||||
if(data[1] == 0)
|
||||
handle->to_send = -1;
|
||||
else
|
||||
handle->to_send = data[1];
|
||||
|
||||
/* TODO separation time should not be ignored */
|
||||
break;
|
||||
case PCI_FLOW_STATUS_WAIT:
|
||||
handle->to_send = 0;
|
||||
break;
|
||||
case PCI_FLOW_STATUS_OVERFLOW:
|
||||
handle->completed = true;
|
||||
handle->success = false;
|
||||
if(shims->log) {
|
||||
shims->log("ISO TP overflow");
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
if(shims->log) {
|
||||
shims->log("Unexpected flow control message type: %x", get_nibble(data, 3, FLOW_CONTROL_NIBBLE_INDEX));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isotp_continue_send(IsoTpShims* shims, IsoTpSendHandle* handle) {
|
||||
uint8_t frame_len;
|
||||
bool is_last = false;
|
||||
int i;
|
||||
uint8_t can_data[CAN_MESSAGE_BYTE_SIZE] = {0};
|
||||
|
||||
if(handle->to_send == 0)
|
||||
return true;
|
||||
|
||||
if(!set_nibble(PCI_NIBBLE_INDEX, PCI_CONSECUTIVE_FRAME, can_data, sizeof(can_data))) {
|
||||
if(shims->log)
|
||||
shims->log("Unable to set PCI in CAN data");
|
||||
handle->completed = true;
|
||||
handle->success = false;
|
||||
isotp_complete_send(shims, handle->message, false, handle->message_sent_callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!set_nibble(SEQUENCE_NUMBER_NIBBLE_INDEX, handle->seqn++, can_data, sizeof(can_data))) {
|
||||
if(shims->log)
|
||||
shims->log("Unable to set payload length in CAN data");
|
||||
handle->completed = true;
|
||||
handle->success = false;
|
||||
isotp_complete_send(shims, handle->message, false, handle->message_sent_callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(handle->payload_index + 7 >= handle->message->size) {
|
||||
frame_len = handle->message->size - handle->payload_index;
|
||||
is_last = true;
|
||||
} else {
|
||||
frame_len = 7;
|
||||
is_last = false;
|
||||
}
|
||||
|
||||
memcpy(&can_data[1], &handle->message[handle->payload_index], frame_len);
|
||||
|
||||
if(!shims->send_can_message(handle->sending_arbitration_id, can_data, frame_len+1, shims->private_data)) {
|
||||
if(shims->log)
|
||||
shims->log("Error sending CAN frame");
|
||||
handle->completed = true;
|
||||
handle->success = false;
|
||||
isotp_complete_send(shims, handle->message, false, handle->message_sent_callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(is_last) {
|
||||
handle->completed = true;
|
||||
handle->success = true;
|
||||
isotp_complete_send(shims, handle->message, true, handle->message_sent_callback);
|
||||
} else {
|
||||
if(handle->to_send > 0) {
|
||||
handle->to_send -= 1;
|
||||
}
|
||||
handle->payload_index += 7;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ extern "C" {
|
|||
typedef struct {
|
||||
bool completed;
|
||||
bool success;
|
||||
int to_send;
|
||||
|
||||
/* Private */
|
||||
uint16_t sending_arbitration_id;
|
||||
|
@ -31,8 +32,22 @@ typedef struct {
|
|||
IsoTpMessageSentHandler message_sent_callback;
|
||||
IsoTpCanFrameSentHandler can_frame_sent_callback;
|
||||
/* TODO going to need some state here for multi frame messages */
|
||||
IsoTpMessage* message;
|
||||
uint16_t payload_index;
|
||||
uint8_t seqn;
|
||||
} IsoTpSendHandle;
|
||||
|
||||
/* Public: Initialize a new ISO-TP message object.
|
||||
*
|
||||
* arbitration_id - The arbitration ID to send the message on.
|
||||
* payload - The payload for the message. If no payload, NULL is valid is size
|
||||
* is also 0.
|
||||
* size - The size of the payload, or 0 if no payload.
|
||||
*
|
||||
* Returns a message object to be passed to isotp_send()
|
||||
*/
|
||||
IsoTpMessage isotp_new_send_message(const uint16_t arbitration_id, const uint8_t payload[], uint16_t size);
|
||||
|
||||
/* Public: Initiate sending a single ISO-TP message.
|
||||
*
|
||||
* If the message fits in a single ISO-TP frame (i.e. the payload isn't more
|
||||
|
@ -53,12 +68,9 @@ typedef struct {
|
|||
* multi-frame messages. The 'completed' field in the returned IsoTpSendHandle
|
||||
* will be true when the message is completely sent.
|
||||
*/
|
||||
IsoTpSendHandle isotp_send(IsoTpShims* shims, const uint16_t arbitration_id,
|
||||
const uint8_t payload[], uint16_t size,
|
||||
IsoTpMessageSentHandler callback);
|
||||
IsoTpSendHandle isotp_send(IsoTpShims* shims, IsoTpMessage* message, IsoTpMessageSentHandler callback);
|
||||
|
||||
/* Public: Continue to send a multi-frame ISO-TP message, based on a freshly
|
||||
* received CAN message (potentially from the receiver about flow control).
|
||||
/* Public: Process control flow frame
|
||||
*
|
||||
* For a multi-frame ISO-TP message, this function must be called
|
||||
* repeatedly whenever a new CAN message is received in order to complete the
|
||||
|
@ -75,10 +87,23 @@ IsoTpSendHandle isotp_send(IsoTpShims* shims, const uint16_t arbitration_id,
|
|||
* otherwise cancelled. Check the 'success' field of the handle to see if
|
||||
* it was successful.
|
||||
*/
|
||||
bool isotp_continue_send(IsoTpShims* shims, IsoTpSendHandle* handle,
|
||||
bool isotp_receive_flowcontrol(IsoTpShims* shims, IsoTpSendHandle* handle,
|
||||
const uint16_t arbitration_id, const uint8_t data[],
|
||||
const uint8_t size);
|
||||
|
||||
/* Public: Continue sending multi-frame package based on the current state
|
||||
*
|
||||
* Can be called regularly or based on to_send field in handle struct.
|
||||
*
|
||||
* shims - Low-level shims required to send CAN messages, etc.
|
||||
* handle - An IsoTpSendHandle previously returned by isotp_send(...).
|
||||
*
|
||||
* Returns false if sending the frame failed, true otherwise. To check if
|
||||
* the sending of the whole message has completed and/or succeded see
|
||||
* handle.completed and handle.success.
|
||||
*/
|
||||
bool isotp_continue_send(IsoTpShims* shims, IsoTpSendHandle* handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue