Display_Avr_3/emulate/hdlc/hdlc.c
2024-02-29 14:11:43 +03:00

262 lines
9.0 KiB
C

#include "hdlc.h"
// HDLC Control field bit positions
#define HDLC_CONTROL_S_OR_U_FRAME_BIT 0
#define HDLC_CONTROL_SEND_SEQ_NO_BIT 1
#define HDLC_CONTROL_S_FRAME_TYPE_BIT 2
#define HDLC_CONTROL_POLL_BIT 4
#define HDLC_CONTROL_RECV_SEQ_NO_BIT 5
// HDLC Control type definitions
#define HDLC_CONTROL_TYPE_RECEIVE_READY 0
#define HDLC_CONTROL_TYPE_RECEIVE_NOT_READY 1
#define HDLC_CONTROL_TYPE_REJECT 2
#define HDLC_CONTROL_TYPE_SELECTIVE_REJECT 3
static hdlc_state_t hdlc_state = {
.control_escape = 0,
.fcs = FCS_INIT_VALUE,
.start_index = -1,
.end_index = -1,
.src_index = 0,
.dest_index = 0,
};
int hdlc_set_state(hdlc_state_t *state) {
if (!state) {
return -EINVAL;
}
hdlc_state = *state;
return 0;
}
int hdlc_get_state(hdlc_state_t *state) {
if (!state) {
return -EINVAL;
}
*state = hdlc_state;
return 0;
}
void hdlc_escape_value(char value, char *dest, int *dest_index) {
// Check and escape the value if needed
if ((value == HDLC_FLAG_SEQUENCE) || (value == HDLC_CONTROL_ESCAPE)) {
dest[(*dest_index)++] = HDLC_CONTROL_ESCAPE;
value ^= 0x20;
}
// Add the value to the destination buffer and increment destination index
dest[(*dest_index)++] = value;
}
hdlc_control_t hdlc_get_control_type(unsigned char control) {
hdlc_control_t value;
// Check if the frame is a S-frame (or U-frame)
if (control & (1 << HDLC_CONTROL_S_OR_U_FRAME_BIT)) {
// Check if S-frame type is a Receive Ready (ACK)
if (((control >> HDLC_CONTROL_S_FRAME_TYPE_BIT) & 0x3)
== HDLC_CONTROL_TYPE_RECEIVE_READY) {
value.frame = S_FRAME;
} else {
// Assume it is an NACK since Receive Not Ready, Selective Reject and U-frames are not supported
value.frame = S_FRAME_NACK;
}
// Add the receive sequence number from the S-frame (or U-frame)
value.seq_no = (control >> HDLC_CONTROL_RECV_SEQ_NO_BIT);
} else {
// It must be an I-frame so add the send sequence number (receive sequence number is not used)
value.frame = I_FRAME;
value.seq_no = (control >> HDLC_CONTROL_SEND_SEQ_NO_BIT);
}
return value;
}
unsigned char hdlc_frame_control_type(hdlc_control_t *control) {
unsigned char value = 0;
switch (control->frame) {
case I_FRAME:
// Create the HDLC I-frame control byte with Poll bit set
value |= (control->seq_no << HDLC_CONTROL_SEND_SEQ_NO_BIT);
value |= (1 << HDLC_CONTROL_POLL_BIT);
break;
case S_FRAME:
// Create the HDLC Receive Ready S-frame control byte with Poll bit cleared
value |= (control->seq_no << HDLC_CONTROL_RECV_SEQ_NO_BIT);
value |= (1 << HDLC_CONTROL_S_OR_U_FRAME_BIT);
break;
case S_FRAME_NACK:
// Create the HDLC Receive Ready S-frame control byte with Poll bit cleared
value |= (control->seq_no << HDLC_CONTROL_RECV_SEQ_NO_BIT);
value |= (HDLC_CONTROL_TYPE_REJECT << HDLC_CONTROL_S_FRAME_TYPE_BIT);
value |= (1 << HDLC_CONTROL_S_OR_U_FRAME_BIT);
break;
}
return value;
}
void hdlc_get_data_reset() {
hdlc_get_data_reset_with_state(&hdlc_state);
}
void hdlc_get_data_reset_with_state(hdlc_state_t *state) {
state->fcs = FCS_INIT_VALUE;
state->start_index = state->end_index = -1;
state->src_index = state->dest_index = 0;
state->control_escape = 0;
}
//int hdlc_get_data(hdlc_control_t *control, const char *src,
// unsigned int src_len, char *dest, unsigned int *dest_len)
int hdlc_get_data(hdlc_control_t *control, uint8_t *src,
size_t src_len, uint8_t *dest, size_t *dest_len){
return hdlc_get_data_with_state(&hdlc_state, control, src, src_len, dest, dest_len);
}
//int hdlc_get_data_with_state(hdlc_state_t *state, hdlc_control_t *control, const char *src,
// unsigned int src_len, char *dest, unsigned int *dest_len)
int
hdlc_get_data_with_state(hdlc_state_t *state, hdlc_control_t *control, uint8_t *src,
size_t src_len, uint8_t *dest, size_t *dest_len){
int ret;
char value;
unsigned int i;
// Make sure that all parameters are valid
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 (state->start_index < 0) {
if (src[i] == HDLC_FLAG_SEQUENCE) {
// Check if an additional flag sequence byte is present
if ((i < (src_len - 1)) && (src[i + 1] == HDLC_FLAG_SEQUENCE)) {
// Just loop again to silently discard it (accordingly to HDLC)
continue;
}
state->start_index = state->src_index;
}
} else {
// Check for end flag sequence
if (src[i] == HDLC_FLAG_SEQUENCE) {
// Check if an additional flag sequence byte is present or earlier received
if (((i < (src_len - 1)) && (src[i + 1] == HDLC_FLAG_SEQUENCE))
|| ((state->start_index + 1) == state->src_index)) {
// Just loop again to silently discard it (accordingly to HDLC)
continue;
}
state->end_index = state->src_index;
break;
} else if (src[i] == HDLC_CONTROL_ESCAPE) {
state->control_escape = 1;
} else {
// Update the value based on any control escape received
if (state->control_escape) {
state->control_escape = 0;
value = src[i] ^ 0x20;
} else {
value = src[i];
}
// Now update the FCS value
state->fcs = calc_fcs(state->fcs, value);
if (state->src_index == state->start_index + 2) {
// Control field is the second byte after the start flag sequence
*control = hdlc_get_control_type(value);
} else if (state->src_index > (state->start_index + 2)) {
// Start adding the data values after the Control field to the buffer
dest[state->dest_index++] = value;
}
}
}
state->src_index++;
}
// Check for invalid frame (no start or end flag sequence)
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 ((state->end_index < (state->start_index + 4))
|| (state->fcs != FCS_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 = state->dest_index - sizeof(state->fcs);
ret = i;
}
// Reset values for next frame
hdlc_get_data_reset_with_state(state);
}
return ret;
}
//int hdlc_frame_data(hdlc_control_t *control, const char *src,
// unsigned int src_len, char *dest, unsigned int *dest_len)
int hdlc_frame_data(hdlc_control_t *control, uint8_t *src,
size_t src_len, uint8_t *dest, size_t *dest_len){
unsigned int i;
int dest_index = 0;
unsigned char value = 0;
FCS_SIZE fcs = FCS_INIT_VALUE;
// Make sure that all parameters are valid
if (!control || (!src && (src_len > 0)) || !dest || !dest_len) {
return -EINVAL;
}
// Start by adding the start flag sequence
dest[dest_index++] = HDLC_FLAG_SEQUENCE;
// Add the all-station address from HDLC (broadcast)
fcs = calc_fcs(fcs, HDLC_ALL_STATION_ADDR);
hdlc_escape_value(HDLC_ALL_STATION_ADDR, dest, &dest_index);
// Add the framed control field value
value = hdlc_frame_control_type(control);
fcs = calc_fcs(fcs, value);
hdlc_escape_value(value, dest, &dest_index);
// Only DATA frames should contain data
if (control->frame == I_FRAME) {
// Calculate FCS and escape data
for (i = 0; i < src_len; i++) {
fcs = calc_fcs(fcs, src[i]);
hdlc_escape_value(src[i], dest, &dest_index);
}
}
// Invert the FCS value accordingly to the specification
fcs ^= FCS_INVERT_MASK;
// Run through the FCS bytes and escape the values
for (i = 0; i < sizeof(fcs); i++) {
value = ((fcs >> (8 * i)) & 0xFF);
hdlc_escape_value(value, dest, &dest_index);
}
// Add end flag sequence and update length of frame
dest[dest_index++] = HDLC_FLAG_SEQUENCE;
*dest_len = dest_index;
return 0;
}