diff options
author | Zac Nelson <znelson1@ford.com> | 2016-09-14 09:52:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-14 09:52:13 -0700 |
commit | 4e7375321a029a32989bc7546329c385e6edb80f (patch) | |
tree | e9e162092adec9a51af47db41d755c12892f6c87 /src | |
parent | 50722a2a61106b7b8eb0774edf53be3c1255cd59 (diff) |
Add support for multi-frame responses (#4)
* first test with multi-frame. storing data in handler. still need to free memory since using malloc.
* hardcode the IsoTpMessage payload size. copy the full multi-frame response into a complete isotpMessage once all frames have been received. otherwise, the returned isotpMessage is not complete.
* remove unnecessary debug statements.
* do not receive multi-frame message if it is larger than the payload buffer size.
* update changelog with multi-frame support
* fix the changelog version for proper semantic versioning. oops.
* move pointer intialization for testing purposes.
* add tests for multi-frame.
* try latest version of check
* fix syntax for version install.
* fix tests. no longer check for malloc pointer (not supported in Travis version of check.h - just test that flow control message has not been sent.
* add IsoTpMessage field for multi_frame. Need this upstream for doing timeout updates.
* add test multi_frame field.
Diffstat (limited to 'src')
-rw-r--r-- | src/isotp/isotp_types.h | 16 | ||||
-rw-r--r-- | src/isotp/receive.c | 101 |
2 files changed, 104 insertions, 13 deletions
diff --git a/src/isotp/isotp_types.h b/src/isotp/isotp_types.h index 9d05980a..6ae3a795 100644 --- a/src/isotp/isotp_types.h +++ b/src/isotp/isotp_types.h @@ -9,8 +9,14 @@ #define MAX_ISO_TP_MESSAGE_SIZE 4096 // TODO we want to avoid malloc, and we can't be allocated 4K on the stack for // each IsoTpMessage, so for now we're setting an artificial max message size -// here - since we only handle single frame messages, 8 bytes is plenty. -#define OUR_MAX_ISO_TP_MESSAGE_SIZE 8 +// here - for most multi-frame use cases, 256 bytes is plenty. +#define OUR_MAX_ISO_TP_MESSAGE_SIZE 256 + +/* Private: IsoTp nibble specifics for PCI and Payload. + */ +#define PCI_NIBBLE_INDEX 0 +#define PAYLOAD_LENGTH_NIBBLE_INDEX 1 +#define PAYLOAD_BYTE_INDEX 1 /* Private: The default timeout to use when waiting for a response during a * multi-frame send or receive. @@ -30,8 +36,9 @@ extern "C" { * completed - An IsoTpMessage is the return value from a few functions - this * attribute will be true if the message is actually completely received. * If the function returns but is only partially through receiving the - * message, this will be false and you should not consider the other data - * to be valid. + * message, this will be false, the multi_frame attribute will be true, + * and you should not consider the other data to be valid. + * multi_frame - Designates the message is being built with multi-frame. * arbitration_id - The arbitration ID of the message. * payload - The optional payload of the message - don't forget to check the * size! @@ -42,6 +49,7 @@ typedef struct { uint8_t payload[OUR_MAX_ISO_TP_MESSAGE_SIZE]; uint16_t size; bool completed; + bool multi_frame; } IsoTpMessage; /* Public: The type signature for an optional logging function, if the user diff --git a/src/isotp/receive.c b/src/isotp/receive.c index 2dc380bc..35b7a2a6 100644 --- a/src/isotp/receive.c +++ b/src/isotp/receive.c @@ -1,6 +1,10 @@ #include <isotp/receive.h> +#include <isotp/send.h> #include <bitfield/bitfield.h> #include <string.h> +#include <stdlib.h> + +#define ARBITRATION_ID_OFFSET 0x8 static void isotp_complete_receive(IsoTpReceiveHandle* handle, IsoTpMessage* message) { if(handle->message_received_callback != NULL) { @@ -13,6 +17,26 @@ bool isotp_handle_single_frame(IsoTpReceiveHandle* handle, IsoTpMessage* message return true; } +bool isotp_handle_multi_frame(IsoTpReceiveHandle* handle, IsoTpMessage* message) { + // call this once all consecutive frames have been received + isotp_complete_receive(handle, message); + return true; +} + +bool isotp_send_flow_control_frame(IsoTpShims* shims, IsoTpMessage* message) { + uint8_t can_data[CAN_MESSAGE_BYTE_SIZE] = {0}; + + if(!set_nibble(PCI_NIBBLE_INDEX, PCI_FLOW_CONTROL_FRAME, can_data, sizeof(can_data))) { + shims->log("Unable to set PCI in CAN data"); + return false; + } + + shims->send_can_message(message->arbitration_id - ARBITRATION_ID_OFFSET, can_data, + shims->frame_padding ? 8 : 1 + message->size); + return true; +} + + IsoTpReceiveHandle isotp_receive(IsoTpShims* shims, const uint32_t arbitration_id, IsoTpMessageReceivedHandler callback) { IsoTpReceiveHandle handle = { @@ -31,6 +55,7 @@ IsoTpMessage isotp_continue_receive(IsoTpShims* shims, IsoTpMessage message = { arbitration_id: arbitration_id, completed: false, + multi_frame: false, payload: {0}, size: 0 }; @@ -53,30 +78,88 @@ IsoTpMessage isotp_continue_receive(IsoTpShims* shims, IsoTpProtocolControlInformation pci = (IsoTpProtocolControlInformation) get_nibble(data, size, 0); - uint8_t payload_length = get_nibble(data, size, 1); - uint8_t payload[payload_length]; - if(payload_length > 0 && size > 0) { - memcpy(payload, &data[1], payload_length); - } - // TODO this is set up to handle rx a response with a payload, but not to // handle flow control responses for multi frame messages that we're in the // process of sending switch(pci) { case PCI_SINGLE: { + uint8_t payload_length = get_nibble(data, size, 1); + if(payload_length > 0) { - memcpy(message.payload, payload, payload_length); + memcpy(message.payload, &data[1], payload_length); } + message.size = payload_length; message.completed = true; handle->success = true; handle->completed = true; isotp_handle_single_frame(handle, &message); break; - } + } + //If multi-frame, then the payload length is contained in the 12 + //bits following the first nibble of Byte 0. + case PCI_FIRST_FRAME: { + uint16_t payload_length = (get_nibble(data, size, 1) << 8) + get_byte(data, size, 1); + + if(payload_length > OUR_MAX_ISO_TP_MESSAGE_SIZE) { + shims->log("Multi-frame response too large for receive buffer."); + break; + } + + //Need to allocate memory for the combination of multi-frame + //messages. That way we don't have to allocate 4k of memory + //for each multi-frame response. + uint8_t* combined_payload = NULL; + combined_payload = (uint8_t*)malloc(sizeof(uint8_t)*payload_length); + + if(combined_payload == NULL) { + shims->log("Unable to allocate memory for multi-frame response."); + break; + } + + memcpy(combined_payload, &data[2], CAN_MESSAGE_BYTE_SIZE - 2); + handle->receive_buffer = combined_payload; + handle->received_buffer_size = CAN_MESSAGE_BYTE_SIZE - 2; + handle->incoming_message_size = payload_length; + + message.multi_frame = true; + handle->success = false; + handle->completed = false; + isotp_send_flow_control_frame(shims, &message); + break; + } + case PCI_CONSECUTIVE_FRAME: { + uint8_t start_index = handle->received_buffer_size; + uint8_t remaining_bytes = handle->incoming_message_size - start_index; + message.multi_frame = true; + + if(remaining_bytes > 7) { + memcpy(&handle->receive_buffer[start_index], &data[1], CAN_MESSAGE_BYTE_SIZE - 1); + handle->received_buffer_size = start_index + 7; + } else { + memcpy(&handle->receive_buffer[start_index], &data[1], remaining_bytes); + handle->received_buffer_size = start_index + remaining_bytes; + + if(handle->received_buffer_size != handle->incoming_message_size){ + free(handle->receive_buffer); + handle->success = false; + shims->log("Error capturing all bytes of multi-frame. Freeing memory."); + } else { + memcpy(message.payload,&handle->receive_buffer[0],handle->incoming_message_size); + free(handle->receive_buffer); + message.size = handle->incoming_message_size; + message.completed = true; + shims->log("Successfully captured all of multi-frame. Freeing memory."); + + handle->success = true; + handle->completed = true; + isotp_handle_multi_frame(handle, &message); + } + } + break; + } default: - shims->log("Only single frame messages are supported"); break; } return message; |