/* *************************************************************************** * * Author: Teunis van Beelen * * Copyright (C) 2005 - 2019 Teunis van Beelen * * Email: teuniz@protonmail.com * *************************************************************************** * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * *************************************************************************** */ /* Last revision: May 31, 2019 */ /* Added support for hardware flow control using RTS and CTS lines */ /* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */ #include "rs232.h" #if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */ #define RS232_PORTNR 38 int Cport[RS232_PORTNR], error; struct termios new_port_settings, old_port_settings[RS232_PORTNR]; const char *comports[RS232_PORTNR]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5", "/dev/ttyS6","/dev/ttyS7","/dev/ttyS8","/dev/ttyS9","/dev/ttyS10","/dev/ttyS11", "/dev/ttyS12","/dev/ttyS13","/dev/ttyS14","/dev/ttyS15","/dev/ttyUSB0", "/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3","/dev/ttyUSB4","/dev/ttyUSB5", "/dev/ttyAMA0","/dev/ttyAMA1","/dev/ttyACM0","/dev/ttyACM1", "/dev/rfcomm0","/dev/rfcomm1","/dev/ircomm0","/dev/ircomm1", "/dev/cuau0","/dev/cuau1","/dev/cuau2","/dev/cuau3", "/dev/cuaU0","/dev/cuaU1","/dev/cuaU2","/dev/cuaU3"}; int RS232_OpenComport(int comport_number, int baudrate, const char *mode, int flowctrl) { int baudr, status; if((comport_number>=RS232_PORTNR)||(comport_number<0)) { printf("illegal comport number\n"); return(1); } switch(baudrate) { case 50 : baudr = B50; break; case 75 : baudr = B75; break; case 110 : baudr = B110; break; case 134 : baudr = B134; break; case 150 : baudr = B150; break; case 200 : baudr = B200; break; case 300 : baudr = B300; break; case 600 : baudr = B600; break; case 1200 : baudr = B1200; break; case 1800 : baudr = B1800; break; case 2400 : baudr = B2400; break; case 4800 : baudr = B4800; break; case 9600 : baudr = B9600; break; case 19200 : baudr = B19200; break; case 38400 : baudr = B38400; break; case 57600 : baudr = B57600; break; case 115200 : baudr = B115200; break; case 230400 : baudr = B230400; break; case 460800 : baudr = B460800; break; case 500000 : baudr = B500000; break; case 576000 : baudr = B576000; break; case 921600 : baudr = B921600; break; case 1000000 : baudr = B1000000; break; case 1152000 : baudr = B1152000; break; case 1500000 : baudr = B1500000; break; case 2000000 : baudr = B2000000; break; case 2500000 : baudr = B2500000; break; case 3000000 : baudr = B3000000; break; case 3500000 : baudr = B3500000; break; case 4000000 : baudr = B4000000; break; default : printf("invalid baudrate\n"); return(1); break; } int cbits=CS8, cpar=0, ipar=IGNPAR, bstop=0; if(strlen(mode) != 3) { printf("invalid mode \"%s\"\n", mode); return(1); } switch(mode[0]) { case '8': cbits = CS8; break; case '7': cbits = CS7; break; case '6': cbits = CS6; break; case '5': cbits = CS5; break; default : printf("invalid number of data-bits '%c'\n", mode[0]); return(1); break; } switch(mode[1]) { case 'N': case 'n': cpar = 0; ipar = IGNPAR; break; case 'E': case 'e': cpar = PARENB; ipar = INPCK; break; case 'O': case 'o': cpar = (PARENB | PARODD); ipar = INPCK; break; default : printf("invalid parity '%c'\n", mode[1]); return(1); break; } switch(mode[2]) { case '1': bstop = 0; break; case '2': bstop = CSTOPB; break; default : printf("invalid number of stop bits '%c'\n", mode[2]); return(1); break; } /* http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html http://man7.org/linux/man-pages/man3/termios.3.html */ Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY | O_NDELAY); if(Cport[comport_number]==-1) { perror("unable to open comport "); return(1); } /* lock access so that another process can't also use the port */ if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0) { close(Cport[comport_number]); perror("Another process has locked the comport."); return(1); } error = tcgetattr(Cport[comport_number], old_port_settings + comport_number); if(error==-1) { close(Cport[comport_number]); flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ perror("unable to read portsettings "); return(1); } memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */ new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD; if(flowctrl) { new_port_settings.c_cflag |= CRTSCTS; } new_port_settings.c_iflag = ipar; new_port_settings.c_oflag = 0; new_port_settings.c_lflag = 0; new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */ new_port_settings.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */ cfsetispeed(&new_port_settings, baudr); cfsetospeed(&new_port_settings, baudr); error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings); if(error==-1) { tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); close(Cport[comport_number]); flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ perror("unable to adjust portsettings "); return(1); } /* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */ if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ perror("unable to get portstatus"); return(1); } status |= TIOCM_DTR; /* turn on DTR */ status |= TIOCM_RTS; /* turn on RTS */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ perror("unable to set portstatus"); return(1); } return(0); } int RS232_PollComport(int comport_number, unsigned char *buf, int size) { int n; n = read(Cport[comport_number], buf, size); if(n < 0) { if(errno == EAGAIN) return 0; } return(n); } int RS232_SendByte(int comport_number, unsigned char byte) { int n = write(Cport[comport_number], &byte, 1); if(n < 0) { if(errno == EAGAIN) { return 0; } else { return 1; } } return(0); } int RS232_SendBuf(int comport_number, unsigned char *buf, int size) { int n = write(Cport[comport_number], buf, size); if(n < 0) { if(errno == EAGAIN) { return 0; } else { return -1; } } return(n); } void RS232_CloseComport(int comport_number) { int status; if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { perror("unable to get portstatus"); } status &= ~TIOCM_DTR; /* turn off DTR */ status &= ~TIOCM_RTS; /* turn off RTS */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { perror("unable to set portstatus"); } tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); close(Cport[comport_number]); flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ } /* Constant Description TIOCM_LE DSR (data set ready/line enable) TIOCM_DTR DTR (data terminal ready) TIOCM_RTS RTS (request to send) TIOCM_ST Secondary TXD (transmit) TIOCM_SR Secondary RXD (receive) TIOCM_CTS CTS (clear to send) TIOCM_CAR DCD (data carrier detect) TIOCM_CD see TIOCM_CAR TIOCM_RNG RNG (ring) TIOCM_RI see TIOCM_RNG TIOCM_DSR DSR (data set ready) http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */ int RS232_IsDCDEnabled(int comport_number) { int status; ioctl(Cport[comport_number], TIOCMGET, &status); if(status&TIOCM_CAR) return(1); else return(0); } int RS232_IsRINGEnabled(int comport_number) { int status; ioctl(Cport[comport_number], TIOCMGET, &status); if(status&TIOCM_RNG) return(1); else return(0); } int RS232_IsCTSEnabled(int comport_number) { int status; ioctl(Cport[comport_number], TIOCMGET, &status); if(status&TIOCM_CTS) return(1); else return(0); } int RS232_IsDSREnabled(int comport_number) { int status; ioctl(Cport[comport_number], TIOCMGET, &status); if(status&TIOCM_DSR) return(1); else return(0); } void RS232_enableDTR(int comport_number) { int status; if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { perror("unable to get portstatus"); } status |= TIOCM_DTR; /* turn on DTR */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { perror("unable to set portstatus"); } } void RS232_disableDTR(int comport_number) { int status; if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { perror("unable to get portstatus"); } status &= ~TIOCM_DTR; /* turn off DTR */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { perror("unable to set portstatus"); } } void RS232_enableRTS(int comport_number) { int status; if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { perror("unable to get portstatus"); } status |= TIOCM_RTS; /* turn on RTS */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { perror("unable to set portstatus"); } } void RS232_disableRTS(int comport_number) { int status; if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) { perror("unable to get portstatus"); } status &= ~TIOCM_RTS; /* turn off RTS */ if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) { perror("unable to set portstatus"); } } void RS232_flushRX(int comport_number) { tcflush(Cport[comport_number], TCIFLUSH); } void RS232_flushTX(int comport_number) { tcflush(Cport[comport_number], TCOFLUSH); } void RS232_flushRXTX(int comport_number) { tcflush(Cport[comport_number], TCIOFLUSH); } #else /* windows */ #define RS232_PORTNR 32 HANDLE Cport[RS232_PORTNR]; const char *comports[RS232_PORTNR]={"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4", "\\\\.\\COM5", "\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10", "\\\\.\\COM11", "\\\\.\\COM12", "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15", "\\\\.\\COM16", "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20", "\\\\.\\COM21", "\\\\.\\COM22", "\\\\.\\COM23", "\\\\.\\COM24", "\\\\.\\COM25", "\\\\.\\COM26", "\\\\.\\COM27", "\\\\.\\COM28", "\\\\.\\COM29", "\\\\.\\COM30", "\\\\.\\COM31", "\\\\.\\COM32"}; char mode_str[128]; int RS232_OpenComport(int comport_number, int baudrate, const char *mode, int flowctrl) { if((comport_number>=RS232_PORTNR)||(comport_number<0)) { printf("illegal comport number\n"); return(1); } switch(baudrate) { case 110 : strcpy(mode_str, "baud=110"); break; case 300 : strcpy(mode_str, "baud=300"); break; case 600 : strcpy(mode_str, "baud=600"); break; case 1200 : strcpy(mode_str, "baud=1200"); break; case 2400 : strcpy(mode_str, "baud=2400"); break; case 4800 : strcpy(mode_str, "baud=4800"); break; case 9600 : strcpy(mode_str, "baud=9600"); break; case 19200 : strcpy(mode_str, "baud=19200"); break; case 38400 : strcpy(mode_str, "baud=38400"); break; case 57600 : strcpy(mode_str, "baud=57600"); break; case 115200 : strcpy(mode_str, "baud=115200"); break; case 128000 : strcpy(mode_str, "baud=128000"); break; case 256000 : strcpy(mode_str, "baud=256000"); break; case 500000 : strcpy(mode_str, "baud=500000"); break; case 921600 : strcpy(mode_str, "baud=921600"); break; case 1000000 : strcpy(mode_str, "baud=1000000"); break; case 1500000 : strcpy(mode_str, "baud=1500000"); break; case 2000000 : strcpy(mode_str, "baud=2000000"); break; case 3000000 : strcpy(mode_str, "baud=3000000"); break; default : printf("invalid baudrate\n"); return(1); break; } if(strlen(mode) != 3) { printf("invalid mode \"%s\"\n", mode); return(1); } switch(mode[0]) { case '8': strcat(mode_str, " data=8"); break; case '7': strcat(mode_str, " data=7"); break; case '6': strcat(mode_str, " data=6"); break; case '5': strcat(mode_str, " data=5"); break; default : printf("invalid number of data-bits '%c'\n", mode[0]); return(1); break; } switch(mode[1]) { case 'N': case 'n': strcat(mode_str, " parity=n"); break; case 'E': case 'e': strcat(mode_str, " parity=e"); break; case 'O': case 'o': strcat(mode_str, " parity=o"); break; default : printf("invalid parity '%c'\n", mode[1]); return(1); break; } switch(mode[2]) { case '1': strcat(mode_str, " stop=1"); break; case '2': strcat(mode_str, " stop=2"); break; default : printf("invalid number of stop bits '%c'\n", mode[2]); return(1); break; } if(flowctrl) { strcat(mode_str, " xon=off to=off odsr=off dtr=on rts=off"); } else { strcat(mode_str, " xon=off to=off odsr=off dtr=on rts=on"); } /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363145%28v=vs.85%29.aspx http://technet.microsoft.com/en-us/library/cc732236.aspx https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_dcb */ Cport[comport_number] = CreateFileA(comports[comport_number], GENERIC_READ|GENERIC_WRITE, 0, /* no share */ NULL, /* no security */ OPEN_EXISTING, 0, /* no threads */ NULL); /* no templates */ if(Cport[comport_number]==INVALID_HANDLE_VALUE) { printf("unable to open comport\n"); return(1); } DCB port_settings; memset(&port_settings, 0, sizeof(port_settings)); /* clear the new struct */ port_settings.DCBlength = sizeof(port_settings); if(!BuildCommDCBA(mode_str, &port_settings)) { printf("unable to set comport dcb settings\n"); CloseHandle(Cport[comport_number]); return(1); } if(flowctrl) { port_settings.fOutxCtsFlow = TRUE; port_settings.fRtsControl = RTS_CONTROL_HANDSHAKE; } if(!SetCommState(Cport[comport_number], &port_settings)) { printf("unable to set comport cfg settings\n"); CloseHandle(Cport[comport_number]); return(1); } COMMTIMEOUTS Cptimeouts; Cptimeouts.ReadIntervalTimeout = MAXDWORD; Cptimeouts.ReadTotalTimeoutMultiplier = 0; Cptimeouts.ReadTotalTimeoutConstant = 0; Cptimeouts.WriteTotalTimeoutMultiplier = 0; Cptimeouts.WriteTotalTimeoutConstant = 0; if(!SetCommTimeouts(Cport[comport_number], &Cptimeouts)) { printf("unable to set comport time-out settings\n"); CloseHandle(Cport[comport_number]); return(1); } return(0); } int RS232_PollComport(int comport_number, unsigned char *buf, int size) { int n; /* added the void pointer cast, otherwise gcc will complain about */ /* "warning: dereferencing type-punned pointer will break strict aliasing rules" */ ReadFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL); return(n); } int RS232_SendByte(int comport_number, unsigned char byte) { int n; WriteFile(Cport[comport_number], &byte, 1, (LPDWORD)((void *)&n), NULL); if(n<0) return(1); return(0); } int RS232_SendBuf(int comport_number, unsigned char *buf, int size) { int n; if(WriteFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL)) { return(n); } return(-1); } void RS232_CloseComport(int comport_number) { CloseHandle(Cport[comport_number]); } /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363258%28v=vs.85%29.aspx */ int RS232_IsDCDEnabled(int comport_number) { int status; GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); if(status&MS_RLSD_ON) return(1); else return(0); } int RS232_IsRINGEnabled(int comport_number) { int status; GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); if(status&MS_RING_ON) return(1); else return(0); } int RS232_IsCTSEnabled(int comport_number) { int status; GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); if(status&MS_CTS_ON) return(1); else return(0); } int RS232_IsDSREnabled(int comport_number) { int status; GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); if(status&MS_DSR_ON) return(1); else return(0); } void RS232_enableDTR(int comport_number) { EscapeCommFunction(Cport[comport_number], SETDTR); } void RS232_disableDTR(int comport_number) { EscapeCommFunction(Cport[comport_number], CLRDTR); } void RS232_enableRTS(int comport_number) { EscapeCommFunction(Cport[comport_number], SETRTS); } void RS232_disableRTS(int comport_number) { EscapeCommFunction(Cport[comport_number], CLRRTS); } /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa363428%28v=vs.85%29.aspx */ void RS232_flushRX(int comport_number) { PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT); } void RS232_flushTX(int comport_number) { PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT); } void RS232_flushRXTX(int comport_number) { PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT); PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT); } #endif void RS232_cputs(int comport_number, const char *text) /* sends a string to serial port */ { while(*text != 0) RS232_SendByte(comport_number, *(text++)); } /* return index in comports matching to device name or -1 if not found */ int RS232_GetPortnr(const char *devname) { int i; char str[32]; #if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */ strcpy(str, "/dev/"); #else /* windows */ strcpy(str, "\\\\.\\"); #endif strncat(str, devname, 16); str[31] = 0; for(i=0; i