klipper/lib/bossac/src/PosixSerialPort.cpp

333 lines
7.4 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// BOSSA
//
// Copyright (c) 2011-2018, ShumaTech
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
///////////////////////////////////////////////////////////////////////////////
#include "PosixSerialPort.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string>
#ifndef B460800
#define B460800 460800
#endif
#ifndef B921600
#define B921600 921600
#endif
PosixSerialPort::PosixSerialPort(const std::string& name, bool isUsb) :
SerialPort(name), _devfd(-1), _isUsb(isUsb), _timeout(0),
_autoFlush(false)
{
}
PosixSerialPort::~PosixSerialPort()
{
if (_devfd >= 0)
::close(_devfd);
}
bool
PosixSerialPort::open(int baud,
int data,
SerialPort::Parity parity,
SerialPort::StopBit stop)
{
struct termios options;
speed_t speed;
// Try opening port assuming _name is full path. If it fails
// try "/dev/" + _name
_devfd = ::open(_name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (_devfd == -1)
{
std::string dev("/dev/");
dev += _name;
_devfd = ::open(dev.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (_devfd == -1)
return false;
}
if (tcgetattr(_devfd, &options) == -1)
{
close();
return false;
}
switch (baud)
{
case 1200:
speed = B1200;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
case 230400:
speed = B230400;
break;
case 460800:
speed = B460800;
break;
case 921600:
speed = B921600;
break;
default:
close();
return false;
}
if (cfsetispeed(&options, speed) || cfsetospeed(&options, speed))
{
close();
return false;
}
options.c_cflag |= (CLOCAL | CREAD);
switch (data)
{
case 8:
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
break;
case 7:
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
break;
default:
close();
return false;
}
switch (parity)
{
case SerialPort::ParityNone:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag &= ~(INPCK | ISTRIP);
break;
case SerialPort::ParityOdd:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_iflag |= (INPCK | ISTRIP);
break;
case SerialPort::ParityEven:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= (INPCK | ISTRIP);
break;
default:
close();
return false;
}
switch (stop)
{
case StopBitOne:
options.c_cflag &= ~CSTOPB;
break;
case StopBitTwo:
options.c_cflag |= CSTOPB;
break;
default:
close();
return false;
}
// No hardware flow control
options.c_cflag &= ~CRTSCTS;
// No software flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// Raw input
options.c_iflag &= ~(BRKINT | ICRNL);
options.c_lflag &= ~(ICANON | IEXTEN | ECHO | ECHOE | ISIG);
// Raw output
options.c_oflag &= ~OPOST;
// No wait time
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 0;
if (tcsetattr(_devfd, TCSANOW, &options))
{
close();
return false;
}
return true;
}
void
PosixSerialPort::close()
{
if (_devfd >= 0)
::close(_devfd);
_devfd = -1;
}
int
PosixSerialPort::read(uint8_t* buffer, int len)
{
fd_set fds;
struct timeval tv;
int numread = 0;
int retval;
if (_devfd == -1)
return -1;
while (numread < len)
{
FD_ZERO(&fds);
FD_SET(_devfd, &fds);
tv.tv_sec = _timeout / 1000;
tv.tv_usec = (_timeout % 1000) * 1000;
retval = select(_devfd + 1, &fds, NULL, NULL, &tv);
if (retval < 0)
{
return -1;
}
else if (retval == 0)
{
return numread;
}
else if (FD_ISSET(_devfd, &fds))
{
retval = ::read(_devfd, (uint8_t*) buffer + numread, len - numread);
if (retval < 0)
return -1;
numread += retval;
}
}
return numread;
}
int
PosixSerialPort::write(const uint8_t* buffer, int len)
{
if (_devfd == -1)
return -1;
int res = ::write(_devfd, buffer, len);
// Used on macos to avoid upload errors
if (_autoFlush)
flush();
return res;
}
int
PosixSerialPort::get()
{
uint8_t byte;
if (_devfd == -1)
return -1;
if (read(&byte, 1) != 1)
return -1;
return byte;
}
int
PosixSerialPort::put(int c)
{
uint8_t byte;
byte = c;
return write(&byte, 1);
}
void
PosixSerialPort::flush()
{
// There isn't a reliable way to flush on a file descriptor
// so we just wait it out. One millisecond is the USB poll
// interval so that should cover it.
usleep(1000);
}
bool
PosixSerialPort::timeout(int millisecs)
{
_timeout = millisecs;
return true;
}
void
PosixSerialPort::setDTR(bool dtr)
{
if (_devfd == -1)
return;
int iFlags = TIOCM_DTR;
ioctl(_devfd, (dtr ? TIOCMBIS : TIOCMBIC), &iFlags);
}
void
PosixSerialPort::setRTS(bool rts)
{
if (_devfd == -1)
return;
int iFlags = TIOCM_RTS;
ioctl(_devfd, (rts ? TIOCMBIS : TIOCMBIC), &iFlags);
}
void
PosixSerialPort::setAutoFlush(bool autoflush)
{
_autoFlush = autoflush;
}