serial.cpp 8.76 KB
#include "serial.h"
#include <sys/termios.h>
#include <fcntl.h> //open
#include <stdlib.h>
#include <unistd.h> //access
#include <errno.h>//errno
#include <signal.h>//raise
#include "blc_core.h"

static void get_path_with_fd(int fd, char *path, size_t size){
#ifdef __linux__
	char fd_name[PATH_MAX];
	ssize_t len;

	snprintf(fd_name, PATH_MAX, "/proc/self/fd/%d", fd);
	SYSTEM_ERROR_CHECK(len=readlink(fd_name, path, size),-1, "Searching for '%s'", fd_name);
	if (len < size)	path[len]=0;
	else EXIT_ON_ERROR("Path name too long");
#else
	if (size < PATH_MAX) EXIT_ON_ERROR("Your buffer is too small");
	SYSTEM_ERROR_CHECK(fcntl(fd, F_GETPATH, path),-1, "Getting path name of the serial port");
#endif
}

static int baudrates[][2]={
		{50, B50},
		{75, B75},
		{110, B110},
		{134, B134},
		{150, B150},
		{200, B200},
		{300, B300},
		{600, B600},
		{1200, B1200},
		{1800, B1800},
		{2400, B2400},
		{4800, B4800},
		{9600, B9600},
		{19200, B19200},
		{38400, B38400},
		{57600, B57600},
		{115200, B115200},
		{230400, B230400},
#ifdef __linux__
    {460800, B460800},
		{500000, B500000},
		{576000, B576000},
		{921600, B921600},
		{1000000, B1000000},
		{1152000, B1152000},
		{1500000, B1500000},
		{2000000, B2000000},
		{2500000, B2500000},
		{3000000, B3000000},
		{3500000, B3500000},
		{4000000, B4000000},
#endif
 };

static int baudrates_nb=sizeof(baudrates)/(2*sizeof(int));

int open_serial_port(char const *device_name, int baudrate, int bits_nb,  char parity,  int stop_bits){
    int fd;
    int i, baudrate_code=0;
    struct termios toptions;
    
    FOR_INV(i, baudrates_nb) if (baudrates[i][0]==baudrate) break;

    if (i==-1) {
    	fprintf(stderr, "Baudrate '%d' is not possible. The possibilities are:\n", baudrate);
    	FOR(i, baudrates_nb) fprintf(stderr, "%d, ", baudrates[i][0]);
    	EXIT_ON_ERROR("Wrong baudrate");
    }
    else baudrate_code=baudrates[i][1];

    /* open serial port maybe we should add O_NDELAY */
    SYSTEM_ERROR_CHECK(fd = open(device_name, O_RDWR | O_NOCTTY), -1, "device name:%s", device_name);
    
    /* get current serial port settings */
    SYSTEM_ERROR_CHECK(tcgetattr(fd, &toptions), -1, "device '%s'", device_name);
    SYSTEM_ERROR_CHECK(cfsetispeed(&toptions, baudrate_code), -1, "Setting input speed of '%d' code '%d.", baudrate, baudrate_code);
    SYSTEM_ERROR_CHECK(cfsetospeed(&toptions, baudrate_code), -1, "Setting ouput speed of '%d' code '%d.", baudrate, baudrate_code);
    /* 8 bits, no parity, 2 stop bits */
    switch (parity){
    case 'N':
    	 toptions.c_cflag &= ~PARENB;
    	 break;
    case 'E':
    	toptions.c_cflag |= PARENB;
    	toptions.c_cflag &= ~PARODD;
    	break;
    case 'O':
    	toptions.c_cflag |= PARENB | PARODD;
    	break;
    default:
    	EXIT_ON_ERROR("Parity '%c' is not possible. It can be:\n'N'->NONE\n'E'->EVEN\n'O'->ODD\n", parity);
    }

    	switch (stop_bits){
    	case 1: toptions.c_cflag &= ~CSTOPB;
    	break;
    	case 2: toptions.c_cflag |= CSTOPB;
    	break;
    	default: EXIT_ON_ERROR("You can not have '%d' stop bits. Possibilities are 1 or 2.", stop_bits);

    }
    toptions.c_cflag &= ~CSIZE;
    switch (bits_nb){
    case 5:
    	toptions.c_cflag |= CS5;
    break;
    case 6:
    	toptions.c_cflag |= CS6;
    break;
    case 7:
    	toptions.c_cflag |= CS7;
    break;
    case 8:
    	toptions.c_cflag |= CS8;
    break;
    default: EXIT_ON_ERROR("You can not have size of '%d' bits. Possibilities are 5,6,7 or 8.", bits_nb);
    }
    /* commit the serial port settings */
    SYSTEM_ERROR_CHECK(tcsetattr(fd, TCSANOW, &toptions), -1, "Setting serial port options");
    
    return fd;
}

//Print on stderr the option of the serial port
void eprint_serial_port_infos(int fd){
    char path[PATH_MAX];

    int baudrate_code, i, input_speed, output_speed;
    struct termios toptions;
   
    
    //may not work on linux use somthing with readlink (/proc/...) ...

    get_path_with_fd(fd, path, sizeof(path));
    fprintf(stderr, "%s: ", path);

    SYSTEM_ERROR_CHECK(tcgetattr(fd, &toptions), -1, "Getting serial port options");
    
    baudrate_code=cfgetispeed(&toptions);
    FOR_INV(i, baudrates_nb) if (baudrate_code==baudrates[i][1]) break;
    if (i==-1) PRINT_WARNING("Unknown input speed of baudrate code '%d", baudrate_code);
    else input_speed= baudrates[i][0];

    
    baudrate_code=cfgetospeed(&toptions);
    FOR_INV(i, baudrates_nb) if (baudrate_code==baudrates[i][1]) break;
    if (i==-1) PRINT_WARNING("Unknown input speed of baudrate code '%d", baudrate_code);
    else  output_speed= baudrates[i][0];

    if (input_speed==output_speed)  fprintf(stderr, "%d bauds, ", input_speed);
    else {
        fprintf(stderr, "input speed:%d bauds, ", input_speed);
        fprintf(stderr, "output speed:%d bauds, ", output_speed);
    }
    
    if ((toptions.c_cflag & PARENB)==0) fprintf(stderr, "no parity, ");
    else if (toptions.c_cflag & PARODD) fprintf(stderr, "odd parity, ");
    else fprintf(stderr, "even parity, ");

    if (toptions.c_cflag & CSTOPB) fprintf(stderr, "two stop bits, ");
    else  fprintf(stderr, "one stop bit, ");
    
    switch (toptions.c_cflag & CSIZE){
        case CS5:
            fprintf(stderr, "char size 5 bits\n");
            break;
        case CS6:
            fprintf(stderr, "char size 6 bits\n");
            break;
        case CS7:
            fprintf(stderr, "char size 7 bits\n");
            break;
        case CS8:
            fprintf(stderr, "char size 8 bits\n");
            break;
    }
    
}

void serial_reconnect(int fd){
    
    
}

void serial_send_buffer(int fd, char *buffer, size_t size){
    size_t offset=0;
    ssize_t ret;
    
    do{
        ret=write(fd, buffer, size-offset);
        if (ret == -1) {
            switch (errno){
                case ENXIO:color_fprintf(BLC_RED, stderr, "Device not configured. It may have been disconnected\n");
          /*          while (access(device_name, W_OK | R_OK)!=0) {
                        sleep(1);
                        fprintf(stderr, "waiting for %s\n", device_name);
                    }
                    *fd=open_serial_port(device_name, bauds);*/
                    break;
                default:PRINT_SYSTEM_ERROR("failing writing");
            }
      //      tcflush(*fd, TCIOFLUSH);
            break;
        }
        else {
            if (ret==0) fprintf(stderr, "write waiting\n");
            else offset+=ret;
        }
        tcdrain(fd);

    }
    while(offset!=size);
 //q  tcdrain(fd);
}

int serial_receive_buffer(int fd, char *buffer, size_t size, int timeout_ms){
    size_t offset=0;
    ssize_t ret;
    struct timeval timeout;
    fd_set set;
    int rv;
    div_t division;
    
    do{
        FD_ZERO(&set); /* clear the set */
        FD_SET(fd, &set); /* add our file descriptor to the set */
        
        division=div(timeout_ms, 1000);
        timeout.tv_sec = division.quot;
        timeout.tv_usec = division.rem*1000;
        
        rv = select((fd) + 1, &set, NULL, NULL, &timeout);
        if (rv==0) {
            fprintf(stderr, "timeout\n");
            break;
        }
        else if (rv==-1) fprintf(stderr, "error\n");
        else{
            ret=read(fd, buffer, size-offset);
            if (ret==-1){
                switch (errno){
                    case ENXIO:color_fprintf(BLC_RED, stderr, "Device not configured. It may have been disconnected\n");
              /*          while (access(device_name, W_OK | R_OK)!=0) {
                            sleep(1);
                            fprintf(stderr, "waiting for %s\n", device_name);
                        }
                        //                   *fd=open_serial_port(device_name, bauds);*/
                        break;
                    default:PRINT_SYSTEM_ERROR("failing writing");
                }
              //  tcflush(*fd, TCIOFLUSH);
            }
            else if (ret==0){
                fprintf(stderr, "waiting\n");
                //      tcflush(*fd, TCIOFLUSH);
                break;
            }
            else offset+=ret;
        }
    }while(offset!=size);
    
    return offset;
    
}

// Envoie un message d'erreur avec name_of_file, name_of_function, number_of_line et affiche le message formate avec les parametres variables. Puis exit le programme avec le parametre EXIT_FAILURE. To be used with EXIT_ON_ERROR.
void serial_port_fatal_error(int fd, const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...){
    va_list arguments;
    va_start(arguments, message);
    fprintf(stderr, "\n%s: %s \t %s \t %i :\nError: ", blc_program_name, name_of_file, name_of_function, numero_of_line);
    color_vfprintf(BLC_BRIGHT_RED, stderr, message, arguments);
    va_end(arguments);
    fprintf(stderr, "\nSerial port: ");
    eprint_serial_port_infos(fd);
    fprintf(stderr, "\n\n");
    fflush(stderr);
    raise(SIGABRT);
    exit(EXIT_FAILURE);
}