Add multiframe support

This commit is contained in:
Anton Gerasimov 2017-12-20 19:29:45 +01:00
parent 2c99a9853d
commit 1a28f2f006
8 changed files with 216 additions and 40 deletions

View File

@ -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!
}
}
}
}
}

View File

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

View File

@ -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
}

View File

@ -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.

10
src/isotp/protocol.h Normal file
View File

@ -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_

View File

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

View File

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

View File

@ -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