diff --git a/README.mkd b/README.mkd index 9803048..e9ffb14 100644 --- a/README.mkd +++ b/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! + } + } } } } diff --git a/src/isotp/isotp.c b/src/isotp/isotp.c index 3a02fd1..b61005a 100644 --- a/src/isotp/isotp.c +++ b/src/isotp/isotp.c @@ -3,12 +3,13 @@ #include 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; } diff --git a/src/isotp/isotp.h b/src/isotp/isotp.h index d436aa0..be8b749 100644 --- a/src/isotp/isotp.h +++ b/src/isotp/isotp.h @@ -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 } diff --git a/src/isotp/isotp_types.h b/src/isotp/isotp_types.h index 4d7e1fe..b4ee05d 100644 --- a/src/isotp/isotp_types.h +++ b/src/isotp/isotp_types.h @@ -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. diff --git a/src/isotp/protocol.h b/src/isotp/protocol.h new file mode 100644 index 0000000..e849cc2 --- /dev/null +++ b/src/isotp/protocol.h @@ -0,0 +1,10 @@ +#ifndef __ISOTP_PROTOCOL_H__ +#define __ISOTP_PROTOCOL_H__ + +#include + +/* Functions related to session-level protocol, should be implemented by user */ + +uint32_t protocol_swap_sa_ta (uint32_t arbitration); + +#endif // __ISOTP_PROTOCOL_H_ diff --git a/src/isotp/receive.c b/src/isotp/receive.c index 548d1a4..7b02277 100644 --- a/src/isotp/receive.c +++ b/src/isotp/receive.c @@ -4,8 +4,6 @@ #include #include -#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; } diff --git a/src/isotp/send.c b/src/isotp/send.c index 68c231d..f2b6c79 100644 --- a/src/isotp/send.c +++ b/src/isotp/send.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -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; +} + diff --git a/src/isotp/send.h b/src/isotp/send.h index 9e072d5..570f5fe 100644 --- a/src/isotp/send.h +++ b/src/isotp/send.h @@ -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