Skip to content

Commit

Permalink
Merge pull request #7 from bang-olufsen/feature/get_data_state
Browse files Browse the repository at this point in the history
Feature/get data state
  • Loading branch information
jeppefrandsen authored Oct 21, 2016
2 parents a0fa93b + 1942b5d commit 752602a
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 27 deletions.
43 changes: 43 additions & 0 deletions C/test/yahdlc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ BOOST_AUTO_TEST_CASE(yahdlcTestGetDataInvalidInputs) {
unsigned int recv_length = 0;
char frame_data[8], recv_data[8];

ret = yahdlc_get_data_with_state(NULL, &control, frame_data, sizeof(frame_data),
recv_data, &recv_length);
BOOST_CHECK_EQUAL(ret, -EINVAL);

// Check invalid control field parameter
ret = yahdlc_get_data(NULL, frame_data, sizeof(frame_data), recv_data,
&recv_length);
Expand All @@ -60,6 +64,45 @@ BOOST_AUTO_TEST_CASE(yahdlcTestGetDataInvalidInputs) {
BOOST_CHECK_EQUAL(ret, -EINVAL);
}

BOOST_AUTO_TEST_CASE(yahdlcTestSetGetState) {
int ret;
yahdlc_state_t state, yahdlc_state;

state.fcs = 123;

ret = yahdlc_set_state(NULL);
BOOST_CHECK_EQUAL(ret, -EINVAL);

ret = yahdlc_set_state(&state);
BOOST_CHECK_EQUAL(ret, 0);

ret = yahdlc_get_state(NULL);
BOOST_CHECK_EQUAL(ret, -EINVAL);

ret = yahdlc_get_state(&yahdlc_state);
BOOST_CHECK_EQUAL(ret, 0);
BOOST_CHECK_EQUAL(state.control_escape, yahdlc_state.control_escape);
BOOST_CHECK_EQUAL(state.fcs, yahdlc_state.fcs);
BOOST_CHECK_EQUAL(state.start_index, yahdlc_state.start_index);
BOOST_CHECK_EQUAL(state.end_index, yahdlc_state.end_index);
BOOST_CHECK_EQUAL(state.src_index, yahdlc_state.src_index);
BOOST_CHECK_EQUAL(state.dest_index, yahdlc_state.dest_index);
}

BOOST_AUTO_TEST_CASE(yahdlcTestGetDataReset) {
yahdlc_state_t state, yahdlc_state;

yahdlc_get_data_reset();
yahdlc_get_data_reset_with_state(&state);
yahdlc_get_state(&yahdlc_state);
BOOST_CHECK_EQUAL(state.control_escape, yahdlc_state.control_escape);
BOOST_CHECK_EQUAL(state.fcs, yahdlc_state.fcs);
BOOST_CHECK_EQUAL(state.start_index, yahdlc_state.start_index);
BOOST_CHECK_EQUAL(state.end_index, yahdlc_state.end_index);
BOOST_CHECK_EQUAL(state.src_index, yahdlc_state.src_index);
BOOST_CHECK_EQUAL(state.dest_index, yahdlc_state.dest_index);
}

BOOST_AUTO_TEST_CASE(yahdlcTestDataFrameControlField) {
int ret;
char frame_data[8], recv_data[8];
Expand Down
85 changes: 58 additions & 27 deletions C/yahdlc.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,33 @@
#define YAHDLC_CONTROL_TYPE_REJECT 2
#define YAHDLC_CONTROL_TYPE_SELECTIVE_REJECT 3

// Variables used in yahdlc_get_data to keep track of received buffers
static char yahdlc_control_escape = 0;
static unsigned short yahdlc_fcs = FCS16_INIT_VALUE;
static int yahdlc_start_index = -1, yahdlc_end_index = -1, yahdlc_src_index = 0,
yahdlc_dest_index = 0;
static yahdlc_state_t yahdlc_state = {
.control_escape = 0,
.fcs = FCS16_INIT_VALUE,
.start_index = -1,
.end_index = -1,
.src_index = 0,
.dest_index = 0,
};

int yahdlc_set_state(yahdlc_state_t *state) {
if (!state) {
return -EINVAL;
}

yahdlc_state = *state;
return 0;
}


int yahdlc_get_state(yahdlc_state_t *state) {
if (!state) {
return -EINVAL;
}

*state = yahdlc_state;
return 0;
}

void yahdlc_escape_value(char value, char *dest, int *dest_index) {
// Check and escape the value if needed
Expand Down Expand Up @@ -83,94 +105,103 @@ unsigned char yahdlc_frame_control_type(yahdlc_control_t *control) {
}

void yahdlc_get_data_reset() {
yahdlc_fcs = FCS16_INIT_VALUE;
yahdlc_start_index = yahdlc_end_index = -1;
yahdlc_src_index = yahdlc_dest_index = 0;
yahdlc_control_escape = 0;
yahdlc_get_data_reset_with_state(&yahdlc_state);
}

void yahdlc_get_data_reset_with_state(yahdlc_state_t *state) {
state->fcs = FCS16_INIT_VALUE;
state->start_index = state->end_index = -1;
state->src_index = state->dest_index = 0;
state->control_escape = 0;
}

int yahdlc_get_data(yahdlc_control_t *control, const char *src,
unsigned int src_len, char *dest, unsigned int *dest_len) {
return yahdlc_get_data_with_state(&yahdlc_state, control, src, src_len, dest, dest_len);
}

int yahdlc_get_data_with_state(yahdlc_state_t *state, yahdlc_control_t *control, const char *src,
unsigned int src_len, char *dest, unsigned int *dest_len) {
int ret;
char value;
unsigned int i;

// Make sure that all parameters are valid
if (!control || !src || !dest || !dest_len) {
if (!state || !control || !src || !dest || !dest_len) {
return -EINVAL;
}

// Run through the data bytes
for (i = 0; i < src_len; i++) {
// First find the start flag sequence
if (yahdlc_start_index < 0) {
if (state->start_index < 0) {
if (src[i] == YAHDLC_FLAG_SEQUENCE) {
// Check if an additional flag sequence byte is present
if ((i < (src_len - 1)) && (src[i + 1] == YAHDLC_FLAG_SEQUENCE)) {
// Just loop again to silently discard it (accordingly to HDLC)
continue;
}

yahdlc_start_index = yahdlc_src_index;
state->start_index = state->src_index;
}
} else {
// Check for end flag sequence
if (src[i] == YAHDLC_FLAG_SEQUENCE) {
// Check if an additional flag sequence byte is present or earlier received
if (((i < (src_len - 1)) && (src[i + 1] == YAHDLC_FLAG_SEQUENCE))
|| ((yahdlc_start_index + 1) == yahdlc_src_index)) {
|| ((state->start_index + 1) == state->src_index)) {
// Just loop again to silently discard it (accordingly to HDLC)
continue;
}

yahdlc_end_index = yahdlc_src_index;
state->end_index = state->src_index;
break;
} else if (src[i] == YAHDLC_CONTROL_ESCAPE) {
yahdlc_control_escape = 1;
state->control_escape = 1;
} else {
// Update the value based on any control escape received
if (yahdlc_control_escape) {
yahdlc_control_escape = 0;
if (state->control_escape) {
state->control_escape = 0;
value = src[i] ^ 0x20;
} else {
value = src[i];
}

// Now update the FCS value
yahdlc_fcs = fcs16(yahdlc_fcs, value);
state->fcs = fcs16(state->fcs, value);

if (yahdlc_src_index == yahdlc_start_index + 2) {
if (state->src_index == state->start_index + 2) {
// Control field is the second byte after the start flag sequence
*control = yahdlc_get_control_type(value);
} else if (yahdlc_src_index > (yahdlc_start_index + 2)) {
} else if (state->src_index > (state->start_index + 2)) {
// Start adding the data values after the Control field to the buffer
dest[yahdlc_dest_index++] = value;
dest[state->dest_index++] = value;
}
}
}
yahdlc_src_index++;
state->src_index++;
}

// Check for invalid frame (no start or end flag sequence)
if ((yahdlc_start_index < 0) || (yahdlc_end_index < 0)) {
if ((state->start_index < 0) || (state->end_index < 0)) {
// Return no message and make sure destination length is 0
*dest_len = 0;
ret = -ENOMSG;
} else {
// A frame is at least 4 bytes in size and has a valid FCS value
if ((yahdlc_end_index < (yahdlc_start_index + 4))
|| (yahdlc_fcs != FCS16_GOOD_VALUE)) {
if ((state->end_index < (state->start_index + 4))
|| (state->fcs != FCS16_GOOD_VALUE)) {
// Return FCS error and indicate that data up to end flag sequence in buffer should be discarded
*dest_len = i;
ret = -EIO;
} else {
// Return success and indicate that data up to end flag sequence in buffer should be discarded
*dest_len = yahdlc_dest_index - sizeof(yahdlc_fcs);
*dest_len = state->dest_index - sizeof(state->fcs);
ret = i;
}

// Reset values for next frame
yahdlc_get_data_reset();
yahdlc_get_data_reset_with_state(state);
}

return ret;
Expand Down
57 changes: 57 additions & 0 deletions C/yahdlc.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,40 @@ typedef struct {
unsigned char seq_no :3;
} yahdlc_control_t;

/** Variables used in yahdlc_get_data and yahdlc_get_data_with_state
* to keep track of received buffers
*/
typedef struct {
char control_escape;
unsigned short fcs;
int start_index;
int end_index;
int src_index;
int dest_index;
} yahdlc_state_t;

#ifdef __cplusplus
extern "C" {
#endif

/**
* Set the yahdlc state
*
* @param[in] state The new yahdlc state to be used
* @retval 0 Success
* @retval -EINVAL Invalid parameter
*/
int yahdlc_set_state(yahdlc_state_t *state);

/**
* Get current yahdlc state
*
* @param[out] state Current yahdlc state
* @retval 0 Success
* @retval -EINVAL Invalid parameter
*/
int yahdlc_get_state(yahdlc_state_t *state);

/**
* Retrieves data from specified buffer containing the HDLC frame. Frames can be
* parsed from multiple buffers e.g. when received via UART.
Expand All @@ -46,15 +76,42 @@ extern "C" {
* @retval -EINVAL Invalid parameter
* @retval -ENOMSG Invalid message
* @retval -EIO Invalid FCS (size of dest_len should be discarded from source buffer)
*
* @see yahdlc_get_data_with_state
*/
int yahdlc_get_data(yahdlc_control_t *control, const char *src,
unsigned int src_len, char *dest, unsigned int *dest_len);

/**
* Retrieves data from specified buffer containing the HDLC frame. Frames can be
* parsed from multiple buffers e.g. when received via UART.
*
* This function is a variation of @ref yahdlc_get_data
* The difference is only in first argument: yahdlc_state_t *state
* Data under that pointer is used to keep track of internal buffers.
*
* @see yahdlc_get_data
*/
int yahdlc_get_data_with_state(yahdlc_state_t *state, yahdlc_control_t *control, const char *src,
unsigned int src_len, char *dest, unsigned int *dest_len);


/**
* Resets values used in yahdlc_get_data function to keep track of received buffers
*/
void yahdlc_get_data_reset();

/**
* This is a variation of @ref yahdlc_get_data_reset
* Resets state values that are under the pointer provided as argument
*
* This function need to be called before the first call to yahdlc_get_data_with_state
* when custom state storage is used.
*
* @see yahdlc_get_data_reset
*/
void yahdlc_get_data_reset_with_state(yahdlc_state_t *state);

/**
* Creates HDLC frame with specified data buffer.
*
Expand Down

0 comments on commit 752602a

Please sign in to comment.