Created
December 26, 2024 15:30
-
-
Save gjyoung1974/fe6fae321a76f9c86d9b3ff297a74a14 to your computer and use it in GitHub Desktop.
libserial with oddbaud support for FTDI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "libserial.h" | |
#include "config.h" | |
#include <string.h> // strlen | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <asm/ioctl.h> | |
#include <asm/termbits.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <stdio.h> // sprinf | |
#include <sys/ioctl.h> | |
#include <linux/ioctl.h> // Add this for ioctl definitions | |
#include <linux/serial.h> // For struct serial_struct | |
struct _COMPORT { | |
int porthandle; | |
bool breakstatus; | |
termios2 backup; | |
}; | |
bool SERIAL_open(const char* portname, COMPORT* port) { | |
int result; | |
// allocate COMPORT structure | |
COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT)); | |
if(cp == NULL) return false; | |
cp->breakstatus=false; | |
size_t len = strlen(portname); | |
if(len > 240) { | |
///////////////////////////////////SetLastError(ERROR_BUFFER_OVERFLOW); | |
return false; | |
} | |
char extended_portname[256] = "/dev/"; | |
memcpy(extended_portname+5,portname,len); | |
cp->porthandle = open (extended_portname, O_RDWR | O_NOCTTY | O_NONBLOCK); | |
if (cp->porthandle < 0) goto cleanup_error; | |
result = ioctl(cp->porthandle, TCGETS2, &cp->backup); | |
if (result < 0) goto cleanup_error; | |
// get port settings | |
termios2 termInfo; | |
memcpy(&termInfo,&cp->backup,sizeof(termios2)); | |
// initialize the port | |
termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit | |
termInfo.c_iflag = PARMRK | INPCK; | |
termInfo.c_oflag = 0; | |
termInfo.c_lflag = 0; | |
termInfo.c_cc[VMIN] = 0; | |
termInfo.c_cc[VTIME] = 0; | |
ioctl(cp->porthandle, TCFLSH, TCIFLUSH); | |
ioctl(cp->porthandle, TCSETS2, &termInfo); | |
*port = cp; | |
return true; | |
cleanup_error: | |
if (cp->porthandle != 0) close(cp->porthandle); | |
free(cp); | |
return false; | |
} | |
void SERIAL_close(COMPORT port) { | |
// restore original termios2, close handle, free the COMPORT struct | |
if (port->porthandle >= 0) { | |
ioctl(port->porthandle, TCSETS2, &port->backup); | |
close(port->porthandle); | |
} | |
free(port); | |
} | |
void SERIAL_getErrorString(char* buffer, size_t length) { | |
int error = errno; | |
if(length < 50) return; | |
memset(buffer,0,length); | |
// get the error message text from the operating system | |
// TODO (or not) | |
const char* err5text = "The specified port is already in use.\n"; | |
const char* err2text = "The specified port does not exist.\n"; | |
size_t sysmsg_offset = 0; | |
if(error == EBUSY) { | |
sysmsg_offset = strlen(err5text); | |
memcpy(buffer,err5text,sysmsg_offset); | |
} else if(error == 2) { | |
sysmsg_offset = strlen(err2text); | |
memcpy(buffer,err2text,sysmsg_offset); | |
} | |
sprintf(buffer + sysmsg_offset, "System error %d.",error); | |
} | |
int SERIAL_getmodemstatus(COMPORT port) { | |
long flags = 0; | |
ioctl (port->porthandle, TIOCMGET, &flags); | |
int retval = 0; | |
if (flags & TIOCM_CTS) retval |= SERIAL_CTS; | |
if (flags & TIOCM_DSR) retval |= SERIAL_DSR; | |
if (flags & TIOCM_RI) retval |= SERIAL_RI; | |
if (flags & TIOCM_CD) retval |= SERIAL_CD; | |
return retval; | |
} | |
bool SERIAL_sendchar(COMPORT port, char data) { | |
if(port->breakstatus) return true; // true or false?!; Does POSIX need this check? | |
int bytesWritten = write(port->porthandle, &data, 1); | |
if(bytesWritten==1) return true; | |
else return false; | |
} | |
int SERIAL_getextchar(COMPORT port) { | |
unsigned char chRead = 0; | |
int dwRead = 0; | |
unsigned char error = 0; | |
int retval = 0; | |
dwRead=read(port->porthandle,&chRead,1); | |
if (dwRead==1) { | |
if(chRead==0xff) // error escape | |
{ | |
dwRead=read(port->porthandle,&chRead,1); | |
if(chRead==0x00) // an error | |
{ | |
dwRead=read(port->porthandle,&chRead,1); | |
if(chRead==0x0) error=SERIAL_BREAK_ERR; | |
else error=SERIAL_FRAMING_ERR; | |
} | |
} | |
retval |= (error<<8); | |
retval |= chRead; | |
retval |= 0x10000; | |
} | |
return retval; | |
} | |
bool SERIAL_setCommParameters(COMPORT port, | |
int baudrate, char parity, int stopbits, int length) { | |
termios2 termInfo; | |
int result = ioctl(port->porthandle, TCGETS2, &termInfo); | |
if (result<0) return false; | |
termInfo.c_cflag = CREAD | CLOCAL; | |
// parity | |
// "works on many systems" | |
#define CMSPAR 010000000000 | |
switch (parity) { | |
case 'n': break; | |
case 'o': termInfo.c_cflag |= (PARODD | PARENB); break; | |
case 'e': termInfo.c_cflag |= PARENB; break; | |
case 'm': termInfo.c_cflag |= (PARENB | CMSPAR | PARODD); break; | |
case 's': termInfo.c_cflag |= (PARENB | CMSPAR); break; | |
default: | |
return false; | |
} | |
// stopbits | |
switch(stopbits) { | |
case SERIAL_1STOP: break; | |
case SERIAL_2STOP: | |
case SERIAL_15STOP: termInfo.c_cflag |= CSTOPB; break; | |
default: | |
return false; | |
} | |
// byte length | |
if(length > 8 || length < 5) return false; | |
switch (length) { | |
case 5: termInfo.c_cflag |= CS5; break; | |
case 6: termInfo.c_cflag |= CS6; break; | |
case 7: termInfo.c_cflag |= CS7; break; | |
case 8: termInfo.c_cflag |= CS8; break; | |
} | |
// baudrate | |
int posix_baudrate=0; | |
switch(baudrate) { | |
case 115200: posix_baudrate = B115200; break; | |
case 57600: posix_baudrate = B57600; break; | |
case 38400: posix_baudrate = B38400; break; | |
case 19200: posix_baudrate = B19200; break; | |
case 9600: posix_baudrate = B9600; break; | |
case 4800: posix_baudrate = B4800; break; | |
case 2400: posix_baudrate = B2400; break; | |
case 1200: posix_baudrate = B1200; break; | |
case 600: posix_baudrate = B600; break; | |
case 300: posix_baudrate = B300; break; | |
case 110: posix_baudrate = B110; break; | |
default: break; | |
} | |
if(posix_baudrate) { | |
termInfo.c_cflag &= ~CBAUD; | |
termInfo.c_cflag |= posix_baudrate; | |
} else { | |
termInfo.c_cflag &= ~CBAUD; | |
termInfo.c_cflag |= BOTHER; | |
termInfo.c_ispeed = baudrate; | |
termInfo.c_ospeed = baudrate; | |
} | |
result = ioctl(port->porthandle, TCSETS2, &termInfo); | |
if(result<0) return false; | |
return true; | |
} | |
void SERIAL_setBREAK(COMPORT port, bool value) { | |
ioctl(port->porthandle, value?TIOCSBRK:TIOCCBRK); | |
} | |
void SERIAL_setDTR(COMPORT port, bool value) { | |
long flag = TIOCM_DTR; | |
ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag); | |
} | |
void SERIAL_setRTS(COMPORT port, bool value) { | |
long flag = TIOCM_RTS; | |
ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment