diff options
| author | Tero Kontkanen <tero.k@granitedevices.com> | 2017-08-23 14:44:10 +0300 |
|---|---|---|
| committer | Tero Kontkanen <tero.k@granitedevices.com> | 2017-08-23 14:44:10 +0300 |
| commit | 98a89371108bbcdedb201caf7be8b82a323e60cc (patch) | |
| tree | 915a5596c632256ab77d5dc42236644307ea3b66 /drivers/serial/pcserialport.c | |
| parent | 5d21340928cd46b3cb97cae0b9c707a71ef9ced0 (diff) | |
| parent | 4176d9358a526ff207bb5f4d127f4143a632d748 (diff) | |
| download | SimpleMotionV2-98a89371108bbcdedb201caf7be8b82a323e60cc.tar.gz SimpleMotionV2-98a89371108bbcdedb201caf7be8b82a323e60cc.zip | |
Merge branch 'feature/port_driver_ftdi_d2xx' into develop
Diffstat (limited to 'drivers/serial/pcserialport.c')
| -rw-r--r-- | drivers/serial/pcserialport.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/drivers/serial/pcserialport.c b/drivers/serial/pcserialport.c new file mode 100644 index 0000000..2a16b82 --- /dev/null +++ b/drivers/serial/pcserialport.c @@ -0,0 +1,338 @@ +/* + * pcserialport.h + * + * Header for PC serial port access library (win/linux) + * + * Created on: 28.12.2016 + * Author: Tero + * + * Inspired by RS232 library by Teunis van Beelen + */ + + + +#include "pcserialport.h" +#include "simplemotion_private.h" //needed for timeout variable + +#if defined(__unix__) || defined(__APPLE__) + +#include <termios.h> +#include <limits.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <string.h> + +#if defined(__linux__) +//needed for setting low latency +#include <linux/serial.h> +#endif + +#if defined(__APPLE__) +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/serial/IOSerialKeys.h> +#include <IOKit/serial/ioss.h> +#include <IOKit/IOBSD.h> +#endif + +smint32 serialPortOpen(const char * port_device_name, smint32 baudrate_bps) +{ + int port_handle; + int err; + int baudrateEnumValue; + struct termios new_port_settings; + int customBaudRate = 0; + + port_handle = open(port_device_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if(port_handle==-1) + { + smDebug(-1, Low, "Serial port error: port open failed"); + return -1; + } + + + // open() follows POSIX semantics: multiple open() calls to the same file will succeed + // unless the TIOCEXCL ioctl is issued (except for root) + if (ioctl(port_handle, TIOCEXCL) == -1) { + smDebug(-1, Low, "Serial port error: error setting TIOCEXCL"); + close(port_handle); + return -1; + } + + // as the port is now open, clear O_NONBLOCK flag for subsequent I/O calls + if (fcntl(port_handle, F_SETFL, 0) == -1) { + smDebug(-1, Low, "Serial port error: error clearing O_NONBLOCK"); + close(port_handle); + return -1; + } + + switch(baudrate_bps) + { +#if defined(B9600) + case 9600 : baudrateEnumValue = B9600; break; +#endif +#if defined(B19200) + case 19200 : baudrateEnumValue = B19200; break; +#endif +#if defined(B38400) + case 38400 : baudrateEnumValue = B38400; break; +#endif +#if defined(B57600) + case 57600 : baudrateEnumValue = B57600; break; +#endif +#if defined(B115200) + case 115200 : baudrateEnumValue = B115200; break; +#endif +#if defined(B230400) + case 230400 : baudrateEnumValue = B230400; break; +#endif +#if defined(B460800) + case 460800 : baudrateEnumValue = B460800; break; +#endif +#if defined(B500000) + case 500000 : baudrateEnumValue = B500000; break; +#endif +#if defined(B576000) + case 576000 : baudrateEnumValue = B576000; break; +#endif +#if defined(B921600) + case 921600 : baudrateEnumValue = B921600; break; +#endif +#if defined(B1000000) + case 1000000 : baudrateEnumValue = B1000000; break; +#endif +#if defined(B1152000) + case 1115200 : baudrateEnumValue = B1152000; break; +#endif +#if defined(B1500000) + case 1500000 : baudrateEnumValue = B1500000; break; +#endif +#if defined(B2000000) + case 2000000 : baudrateEnumValue = B2000000; break; +#endif +#if defined(B2500000) + case 2500000 : baudrateEnumValue = B2500000; break; +#endif +#if defined(B3000000) + case 3000000 : baudrateEnumValue = B3000000; break; +#endif +#if defined(B3500000) + case 3500000 : baudrateEnumValue = B3500000; break; +#endif +#if defined(B4000000) + case 4000000 : baudrateEnumValue = B4000000; break; +#endif + default: + customBaudRate = 1; + baudrateEnumValue=B9600;//must set something initially, changed later + break; + } + + memset(&new_port_settings, 0, sizeof(new_port_settings)); //reset struct + cfmakeraw(&new_port_settings);//reset struct + new_port_settings.c_cflag = CS8 | CLOCAL | CREAD; + new_port_settings.c_iflag = IGNPAR; + new_port_settings.c_oflag = 0; + new_port_settings.c_lflag = 0; + new_port_settings.c_cc[VMIN] = 0; /* non blocking mode */ + new_port_settings.c_cc[VTIME] = readTimeoutMs/100; /* timeout 100 ms steps */ + if(new_port_settings.c_cc[VTIME]<1)//don't allow value 0ms + new_port_settings.c_cc[VTIME]=1; +#if defined(_BSD_SOURCE) + cfsetspeed(&new_port_settings, baudrateEnumValue); +#else + cfsetispeed(&new_port_settings, baudrateEnumValue); + cfsetospeed(&new_port_settings, baudrateEnumValue); +#endif + + // Activate settings + err = tcsetattr(port_handle, TCSANOW, &new_port_settings); + if(err==-1) + { + close(port_handle); + smDebug(-1, Low, "Serial port error: failed to set port parameters"); + return -1; + } + + if(customBaudRate) + { + #if defined(IOSSIOSPEED) + speed_t bps = baudrate_bps; + if (ioctl(port_handle, IOSSIOSPEED, &bps) == -1) + { + smDebug(-1, Low, "Serial port error: unsupported baudrate\n"); + close(port_handle); + return -1; + } + #else + smDebug(-1, Low, "Serial port error: unsupported baudrate\n"); + close(port_handle); + return -1; + #endif + } + + // set receive latency to 1 ms + #if defined(IOSSDATALAT) + unsigned long microsecs = 1000UL; + if (ioctl(port_handle, IOSSDATALAT, µsecs) == -1) { + smDebug(-1, Low, "Serial port error: error setting read latency"); + close(port_handle); + return -1; + } + #endif + + #if defined(TIOCGSERIAL) && defined(ASYNC_LOW_LATENCY) + struct serial_struct serial; + if(ioctl(port_handle, TIOCGSERIAL, &serial)!=-1) + { + serial.flags |= ASYNC_LOW_LATENCY; + if(ioctl(port_handle, TIOCSSERIAL, &serial) == -1 ) + { + smDebug(-1, Low, "Serial port warning: unable to set low latency mode, maybe try running with root permissions."); + } + } + else + smDebug(-1, Low, "Serial port warning: unable to read TIOCGSERIAL for low latency mode, maybe try running with root permissions."); + #endif + + //flush any stray bytes from device receive buffer that may reside in it + //note: according to following page, delay before this may be necessary http://stackoverflow.com/questions/13013387/clearing-the-serial-ports-buffer + usleep(100000); + tcflush(port_handle,TCIOFLUSH); + + return port_handle; +} + + +smint32 serialPortRead(smint32 serialport_handle, smuint8 *buf, smint32 size) +{ + smint32 n; + if(size>4096) size = 4096; + n = read(serialport_handle, buf, size); + return n; +} + + +smint32 serialPortWrite(smint32 serialport_handle, unsigned char byte) +{ + smint32 n; + n = write(serialport_handle, &byte, 1); + if(n<0) + return 1; + return 0; +} + + +smint32 serialPortWriteBuffer(smint32 serialport_handle, unsigned char *buf, smint32 size) +{ + return(write(serialport_handle, buf, size)); +} + + +void serialPortClose(smint32 serialport_handle) +{ + close(serialport_handle); +} + +#else //windows: for API, see https://msdn.microsoft.com/en-us/library/ff802693.aspx + +#include <windows.h> +#include <string.h> + +smint32 serialPortOpen(const char *port_device_name, smint32 baudrate_bps) +{ + char port_def_string[64], port_name[32]; + HANDLE port_handle; + + sprintf(port_def_string,"baud=%d data=8 parity=N stop=1", (int)baudrate_bps); + sprintf(port_name,"\\\\.\\%s",port_device_name); + + port_handle = CreateFileA(port_name,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); + + if(port_handle==INVALID_HANDLE_VALUE) + { + smDebug( -1, Low, "Serial port error: Unable to create serial port handle"); + return -1; + } + + //fill DCB settings struct + DCB dcb; + FillMemory(&dcb, sizeof(dcb), 0); + dcb.DCBlength = sizeof(dcb); + + if(!BuildCommDCBA(port_def_string, &dcb)) + { + smDebug( -1, Low, "Serial port error: Unable to build DCB settings\n"); + CloseHandle(port_handle); + return -1; + } + + if(!SetCommState(port_handle, &dcb)) + { + smDebug( -1, Low, "Serial port error: Unable to set port settings\n"); + CloseHandle(port_handle); + return -1; + } + + //set timeout + COMMTIMEOUTS port_timeouts; + port_timeouts.ReadTotalTimeoutConstant = readTimeoutMs; + port_timeouts.ReadIntervalTimeout = 0; + port_timeouts.ReadTotalTimeoutMultiplier = 0; + port_timeouts.WriteTotalTimeoutMultiplier = 50; + port_timeouts.WriteTotalTimeoutConstant = 50; + + if(!SetCommTimeouts(port_handle, &port_timeouts)) + { + smDebug( -1, Low, "Serial port error: Failed to set port timeout settings\n"); + CloseHandle(port_handle); + return(-1); + } + + //flush any stray bytes from device receive buffer that may reside in it + PurgeComm((HANDLE)port_handle,PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); + + return( (smint32)port_handle); +} + + +smint32 serialPortRead(smint32 serialport_handle, unsigned char *buf, smint32 size) +{ + smint32 n; + if(size>4096) + size = 4096; + ReadFile((HANDLE)serialport_handle, buf, size, (LPDWORD)((void *)&n), NULL); + return n; +} + + +smint32 serialPortWriteByte(smint32 serialport_handle, unsigned char byte) +{ + smint32 n; + WriteFile((HANDLE)serialport_handle, &byte, 1, (LPDWORD)((void *)&n), NULL); + if(n<0) + return 1; + return 0; +} + + +smint32 serialPortWriteBuffer(smint32 serialport_handle, unsigned char *buf, smint32 size) +{ + smint32 n; + if(WriteFile((HANDLE)serialport_handle, buf, size, (LPDWORD)((void *)&n), NULL)) + return n; + return -1; +} + + +void serialPortClose(smint32 serialport_number) +{ + CloseHandle((HANDLE)serialport_number); +} + + +#endif//windows |
