Robin Mueller
048cd89053
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
604 lines
13 KiB
C
604 lines
13 KiB
C
//**************************************************************************************
|
|
/*! \copyright: 2020-2021 Thales Alenia Space Deutschland GmbH
|
|
* \project: multiMIND
|
|
* \file: (name of source file: uart.c)
|
|
* \date: (20.05.2021)
|
|
* \author: (Sarthak Kelapure)
|
|
* \brief: (UART thread to collect data on serial interface)
|
|
* \language: (C)
|
|
**************************************************************************************
|
|
*/
|
|
|
|
#include "tas/uart.h"
|
|
#include "tas/hdlc.h"
|
|
|
|
#ifdef HDLC_ENABLE
|
|
#define HDLC_RX_STATE_IDLE (0u)
|
|
#define HDLC_RX_STATE_RECEIVING (1u)
|
|
#define HDLC_RX_STATE_ESCAPE (2u)
|
|
#endif
|
|
|
|
/**
|
|
* @struct Serial device structure.
|
|
* Encapsulates a serial connection.
|
|
*/
|
|
struct serial_s {
|
|
int fd; //>! Connection file descriptor.
|
|
int state; //>! Signifies connection state.
|
|
int running; //>! Signifies thread state.
|
|
|
|
char rxbuff[BUFF_SIZE]; //>! Buffer for RX data.
|
|
int start, end; //>! Pointers to start and end of buffer.
|
|
|
|
pthread_t rx_thread; //>! Listening thread.
|
|
};
|
|
|
|
// --------------- Internal Functions ---------------
|
|
|
|
/**
|
|
* Connect to a serial device.
|
|
* @param s - serial structure.
|
|
* @param device - serial device name.
|
|
* @param baud - baud rate for connection.
|
|
* @return -ve on error, 0 on success.
|
|
*/
|
|
static int serial_connect(serial_t* s, char device[], int baud);
|
|
|
|
/**
|
|
* Create the serial structure.
|
|
* Convenience method to allocate memory
|
|
* and instantiate objects.
|
|
* @return serial structure.
|
|
*/
|
|
static serial_t* serial_create();
|
|
|
|
static int serial_resolve_baud(int baud);
|
|
|
|
/**
|
|
* Recieve data.
|
|
* Retrieves data from the serial device.
|
|
* @param s - serial structure.
|
|
* @param data - pointer to a buffer to read data into.
|
|
* @param maxLength - size of input buffer.
|
|
* @return amount of data recieved.
|
|
*/
|
|
static int serial_recieve(serial_t* obj, uint8_t data[], int maxLength);
|
|
|
|
/**
|
|
* @brief Serial Listener Thread.
|
|
* This blocks waiting for data to be recieved from the serial device,
|
|
* and calls the serial_callback method with appropriate context when
|
|
* data is recieved.
|
|
* Exits when close method is called, or serial error occurs.
|
|
* @param param - context passed from thread instantiation.
|
|
*/
|
|
static void *serial_data_listener(void *param);
|
|
|
|
/**
|
|
* @brief Start the serial threads.
|
|
* This spawns the listening and transmitting threads
|
|
* if they are not already running.
|
|
* @param s - serial structure.
|
|
* @return 0 on success, or -1 on error.
|
|
*/
|
|
static int serial_start(serial_t* s);
|
|
|
|
/**
|
|
* Stop serial listener thread.
|
|
* @param s - serial structure.
|
|
* @return 0;
|
|
*/
|
|
static int serial_stop(serial_t* s);
|
|
|
|
/**
|
|
* Callback to handle recieved data.
|
|
* Puts recieved data into the rx buffer.
|
|
* @param s - serial structure.
|
|
* @param data - data to be stored.
|
|
* @param length - length of recieved data.
|
|
*/
|
|
static void serial_rx_callback(serial_t* s, char data[], int length);
|
|
|
|
// Put character in rx buffer.
|
|
static int buffer_put(serial_t* s, char c)
|
|
{
|
|
//if there is space in the buffer
|
|
if ( s->end != ((s->start + BUFF_SIZE - 1) % BUFF_SIZE)) {
|
|
s->rxbuff[s->end] = c;
|
|
s->end ++;
|
|
s->end = s->end % BUFF_SIZE;
|
|
//printf("Put: %x start: %d, end: %d\r\n", c, s->start, s->end);
|
|
return 0; //No error
|
|
} else {
|
|
//buffer is full, this is a bad state
|
|
return -1; //Report error
|
|
}
|
|
}
|
|
|
|
// Get character from rx buffer.
|
|
static char buffer_get(serial_t* s)
|
|
{
|
|
char c = (char)0;
|
|
//if there is data to process
|
|
if (s->end != s->start) {
|
|
c = (s->rxbuff[s->start]);
|
|
s->start ++;
|
|
//wrap around
|
|
s->start = s->start % BUFF_SIZE;
|
|
} else {
|
|
}
|
|
//printf("Get: %x start: %d, end: %d\r\n", c, s->start, s->end);
|
|
return c;
|
|
}
|
|
|
|
//Get data available in the rx buffer.
|
|
static int buffer_available(serial_t* s)
|
|
{
|
|
return (s->end - s->start + BUFF_SIZE) % BUFF_SIZE;
|
|
}
|
|
|
|
// --------------- External Functions ---------------
|
|
|
|
//Create serial object.
|
|
serial_t* serial_create()
|
|
{
|
|
//Allocate serial object.
|
|
serial_t* s = malloc(sizeof(serial_t));
|
|
//Reconfigure buffer object.
|
|
s->start = 0;
|
|
s->end = 0;
|
|
//Return pointer.
|
|
return s;
|
|
}
|
|
|
|
|
|
void uart_destroy(serial_t* s)
|
|
{
|
|
free(s);
|
|
}
|
|
|
|
|
|
//Connect to serial device.
|
|
int serial_connect(serial_t* s, char device[], int baud)
|
|
{
|
|
struct termios oldtio;
|
|
|
|
// Resolve baud.
|
|
int speed = serial_resolve_baud(baud);
|
|
if (speed < 0) {
|
|
printf("Error: Baud rate not recognized.\r\n");
|
|
return -1;
|
|
}
|
|
|
|
//Open device.
|
|
s->fd = open(device, O_RDWR | O_NOCTTY);
|
|
//Catch file open error.
|
|
if (s->fd < 0) {
|
|
perror(device);
|
|
return -2;
|
|
}
|
|
//Retrieve settings.
|
|
tcgetattr(s->fd, &oldtio);
|
|
//Set baud rate.
|
|
cfsetspeed(&oldtio, speed);
|
|
//Flush cache.
|
|
tcflush(s->fd, TCIFLUSH);
|
|
|
|
//Set UART settings, standard ones. 8N1
|
|
oldtio.c_cflag = (oldtio.c_cflag & ~CSIZE) | CS8; // 8-bit chars
|
|
// disable IGNBRK for mismatched speed tests; otherwise receive break
|
|
// as \000 chars
|
|
oldtio.c_iflag &= ~IGNBRK; // disable break processing
|
|
oldtio.c_lflag = 0; // no signaling chars, no echo,
|
|
// no canonical processing
|
|
oldtio.c_oflag = 0; // no remapping, no delays
|
|
oldtio.c_cc[VMIN] = 0; // read doesn't block
|
|
oldtio.c_cc[VTIME] = 5; // 0.5 seconds read timeout
|
|
|
|
oldtio.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
|
|
oldtio.c_iflag &= ~(IGNCR | ICRNL | INLCR); // CR and LF characters are not affected
|
|
|
|
oldtio.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
|
|
// enable reading
|
|
oldtio.c_cflag &= ~(PARENB | PARODD); // shut off parity
|
|
oldtio.c_cflag |= 0;
|
|
oldtio.c_cflag &= ~CSTOPB;
|
|
oldtio.c_cflag &= ~(020000000000);
|
|
//Apply settings.
|
|
if(tcsetattr(s->fd, TCSANOW, &oldtio) !=0){
|
|
printf("ERROR: serial settings failed\r\n");
|
|
return -1;
|
|
}
|
|
|
|
//Start listener thread.
|
|
int res = serial_start(s);
|
|
//Catch error.
|
|
if (res < 0) {
|
|
printf("Error: serial thread could not be spawned\r\n");
|
|
return -3;
|
|
}
|
|
|
|
//Indicate connection was successful.
|
|
s->state = 1;
|
|
return 0;
|
|
}
|
|
|
|
serial_t* uart_init(char device[], int baud)
|
|
{
|
|
serial_t* s = serial_create();
|
|
if(serial_connect(s, device, baud)< 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
//Send data.
|
|
uint32_t uart_length_send(serial_t* s, uint8_t data[], int length)
|
|
{
|
|
// uint16_t ii;
|
|
// int res;
|
|
// for (ii = 0; ii < length; ii++)
|
|
// {
|
|
// res = write(s->fd, &data[ii], 1);
|
|
// }
|
|
int res = write(s->fd, data, length);
|
|
return res;
|
|
}
|
|
|
|
void uart_send(serial_t* s, uint8_t data)
|
|
{
|
|
char arr[1];
|
|
arr[0] = data;
|
|
write(s->fd, arr, 1);
|
|
}
|
|
|
|
//Determine characters available.
|
|
int uart_available(serial_t* s)
|
|
{
|
|
return buffer_available(s);
|
|
}
|
|
|
|
//Fetch a character.
|
|
char uart_get(serial_t* s)
|
|
{
|
|
char c = buffer_get(s);
|
|
|
|
return c;
|
|
}
|
|
|
|
int uart_length_get(serial_t* s, char* buff, int len, bool start_of_packet)
|
|
{
|
|
int ret = 0;
|
|
if (len > 0 && len < BUFF_SIZE)
|
|
{
|
|
#ifdef HDLC_ENABLE
|
|
uint8_t ch;
|
|
uint8_t hdlc_rx_state;
|
|
int rxb = 0;
|
|
|
|
if (start_of_packet)
|
|
hdlc_rx_state = HDLC_RX_STATE_IDLE;
|
|
else
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
|
|
while (rxb < len)
|
|
{
|
|
ch = uart_blocking_get(s);
|
|
|
|
switch (hdlc_rx_state)
|
|
{
|
|
case HDLC_RX_STATE_IDLE:
|
|
if (ch == HDLC_START_BYTE)
|
|
{
|
|
rxb = 0;
|
|
ret = 0;
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
}
|
|
break;
|
|
case HDLC_RX_STATE_RECEIVING:
|
|
if (ch == HDLC_START_BYTE)
|
|
{
|
|
rxb = 0;
|
|
ret = 0;
|
|
break;
|
|
}
|
|
if (ch == HDLC_ESC_BYTE)
|
|
{
|
|
hdlc_rx_state = HDLC_RX_STATE_ESCAPE;
|
|
break;
|
|
}
|
|
buff[rxb++] = ch;
|
|
ret++;
|
|
break;
|
|
case HDLC_RX_STATE_ESCAPE:
|
|
if (ch == HDLC_START_BYTE)
|
|
{
|
|
rxb = 0;
|
|
ret = 0;
|
|
break;
|
|
}
|
|
buff[rxb++] = ch ^ HDLC_ESCAPE_CHAR;
|
|
ret++;
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
for (int i=0;i<len;i++)
|
|
{
|
|
buff[i] = uart_blocking_get(s);
|
|
ret++;
|
|
}
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
uint16_t uart_get_hdlc_packet(serial_t* s, uint8_t *buff, uint16_t buff_len)
|
|
{
|
|
uint8_t hdlc_rx_state = HDLC_RX_STATE_IDLE;
|
|
uint8_t ch;
|
|
uint16_t buff_pos = 0u;
|
|
|
|
while (1)
|
|
{
|
|
ch = uart_blocking_get(s);
|
|
|
|
switch (hdlc_rx_state)
|
|
{
|
|
case HDLC_RX_STATE_IDLE:
|
|
if (ch == HDLC_START_BYTE)
|
|
{
|
|
buff_pos = 0u;
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
}
|
|
break;
|
|
|
|
case HDLC_RX_STATE_RECEIVING:
|
|
if (ch == HDLC_START_BYTE)
|
|
{
|
|
buff_pos = 0u;
|
|
break;
|
|
}
|
|
if (ch == HDLC_END_BYTE)
|
|
{
|
|
if (buff_pos > 2u) // do not include HDLC CRC16
|
|
{
|
|
return buff_pos;
|
|
}
|
|
buff_pos = 0u;
|
|
hdlc_rx_state = HDLC_RX_STATE_IDLE;
|
|
break;
|
|
}
|
|
if (ch == HDLC_ESC_BYTE)
|
|
{
|
|
hdlc_rx_state = HDLC_RX_STATE_ESCAPE;
|
|
break;
|
|
}
|
|
if (buff_pos >= buff_len)
|
|
{
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
break;
|
|
}
|
|
buff[buff_pos++] = ch;
|
|
break;
|
|
|
|
case HDLC_RX_STATE_ESCAPE:
|
|
if ((ch == HDLC_START_BYTE) || (ch == HDLC_END_BYTE))
|
|
{
|
|
buff_pos = 0;
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
break;
|
|
}
|
|
if (buff_pos >= buff_len)
|
|
{
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
break;
|
|
}
|
|
buff[buff_pos++] = ch ^ HDLC_ESCAPE_CHAR;
|
|
hdlc_rx_state = HDLC_RX_STATE_RECEIVING;
|
|
break;
|
|
|
|
default:
|
|
buff_pos = 0u;
|
|
hdlc_rx_state = HDLC_RX_STATE_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
char uart_blocking_get(serial_t* s)
|
|
{
|
|
while (uart_available(s) == 0);
|
|
return uart_get(s);
|
|
}
|
|
|
|
void uart_clear(serial_t* s)
|
|
{
|
|
//Clear the buffer.
|
|
while (buffer_available(s)) {
|
|
buffer_get(s);
|
|
}
|
|
tcflush(s->fd, TCIFLUSH);
|
|
}
|
|
|
|
//Close serial port.
|
|
int uart_close(serial_t* s)
|
|
{
|
|
//Stop thread.
|
|
serial_stop(s);
|
|
return 0;
|
|
}
|
|
|
|
void uart_deinit(serial_t* s){
|
|
uart_clear(s);
|
|
uart_close(s);
|
|
uart_destroy(s);
|
|
}
|
|
|
|
// --------------- Internal Functions --------------
|
|
|
|
//Stop serial listener thread.
|
|
static int serial_stop(serial_t* s)
|
|
{
|
|
s->running = 0;
|
|
return close(s->fd);
|
|
}
|
|
|
|
// Resolves standard baud rates to linux constants.
|
|
static int serial_resolve_baud(int baud)
|
|
{
|
|
int speed;
|
|
// Switch common baud rates to temios constants.
|
|
switch (baud) {
|
|
case 9600:
|
|
speed = B9600;
|
|
break;
|
|
case 19200:
|
|
speed = B19200;
|
|
break;
|
|
case 38400:
|
|
speed = B38400;
|
|
break;
|
|
case 57600:
|
|
speed = B57600;
|
|
break;
|
|
case 115200:
|
|
speed = B115200;
|
|
break;
|
|
case 230400:
|
|
speed = B230400;
|
|
break;
|
|
case 460800:
|
|
speed = B460800;
|
|
break;
|
|
case 500000:
|
|
speed = B500000;
|
|
break;
|
|
case 576000:
|
|
speed = B576000;
|
|
break;
|
|
case 921600:
|
|
speed = B921600;
|
|
break;
|
|
case 1000000:
|
|
speed = B1000000;
|
|
break;
|
|
case 1152000:
|
|
speed = B1152000;
|
|
break;
|
|
case 1500000:
|
|
speed = B1500000;
|
|
break;
|
|
case 2000000:
|
|
speed = B2000000;
|
|
break;
|
|
case 3000000:
|
|
speed = B3000000;
|
|
break;
|
|
default:
|
|
speed = -1;
|
|
break;
|
|
}
|
|
// Return.
|
|
return speed;
|
|
}
|
|
|
|
// Start serial listener.
|
|
static int serial_start(serial_t* s)
|
|
{
|
|
//Only start if it is not currently running.
|
|
if (s->running != 1) {
|
|
//Set running.
|
|
s->running = 1;
|
|
//Spawn thread.
|
|
int res;
|
|
res = pthread_create(&s->rx_thread, NULL, serial_data_listener, (void*) s);
|
|
if (res != 0) {
|
|
return -2;
|
|
}
|
|
//Return result.
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//Recieve data.
|
|
static int serial_recieve(serial_t* s, uint8_t data[], int maxLength)
|
|
{
|
|
return read(s->fd, data, maxLength);
|
|
}
|
|
|
|
//Callback to store data in buffer.
|
|
static void serial_rx_callback(serial_t* s, char data[], int length)
|
|
{
|
|
//Put data into buffer.
|
|
int i;
|
|
//Put data into buffer.
|
|
for (i = 0; i < length; i++) {
|
|
buffer_put(s, data[i]);
|
|
}
|
|
|
|
}
|
|
|
|
//Serial data listener thread.
|
|
static void *serial_data_listener(void *param)
|
|
{
|
|
int res = 0;
|
|
int err = 0;
|
|
struct pollfd ufds;
|
|
uint8_t buff[BUFF_SIZE];
|
|
|
|
//Retrieve paramaters and store locally.
|
|
serial_t* serial = (serial_t*) param;
|
|
int fd = serial->fd;
|
|
|
|
//Set up poll file descriptors.
|
|
ufds.fd = fd; //Attach socket to watch.
|
|
ufds.events = POLLIN; //Set events to notify on.
|
|
|
|
//Run until ended.
|
|
while (serial->running != 0) {
|
|
//Poll socket for data.
|
|
res = poll(&ufds, 1, POLL_TIMEOUT);
|
|
//If data was recieved.
|
|
if (res > 0) {
|
|
//Fetch the data.
|
|
int count = serial_recieve(serial, buff, BUFF_SIZE - 1);
|
|
//If data was recieved.
|
|
if (count > 0) {
|
|
//Pad end of buffer to ensure there is a termination symbol.
|
|
buff[count] = '\0';
|
|
// Call the serial callback.
|
|
serial_rx_callback(serial, (char *)buff, count);
|
|
//If an error occured.
|
|
} else if (count < 0) {
|
|
//Inform user and exit thread.
|
|
printf("Error: Serial disconnect\r\n");
|
|
err = 1;
|
|
break;
|
|
}
|
|
//If there was an error.
|
|
} else if (res < 0) {
|
|
//Inform user and exit thread.
|
|
printf("Error: Polling error in serial thread");
|
|
err = 1;
|
|
break;
|
|
}
|
|
//Otherwise, keep going around.
|
|
}
|
|
//If there was an error, close socket.
|
|
if (err) {
|
|
uart_close(serial);
|
|
//raise(SIGLOST);
|
|
}
|
|
//Close file.
|
|
res = close(serial->fd);
|
|
|
|
return NULL;
|
|
}
|