|
|
#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);
|
|
|
}
|
|
|
|
...
|
...
|
|