//************************************************************************************** /*! \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 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; }