mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			298 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "lib/core/globals.h"
 | |
| #include "lib/usb/usb.h"
 | |
| #include "protocol.h"
 | |
| #include "lib/data/fluxmap.h"
 | |
| #include "lib/core/bytes.h"
 | |
| #include "lib/usb/serial.h"
 | |
| #include <unistd.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| 
 | |
| #if defined __WIN32__
 | |
| #include <windows.h>
 | |
| 
 | |
| class SerialPortImpl : public SerialPort
 | |
| {
 | |
| public:
 | |
|     SerialPortImpl(const std::string& name)
 | |
|     {
 | |
|         std::string dos_name = "\\\\.\\" + name;
 | |
|         _handle = CreateFileA(dos_name.c_str(),
 | |
|             /* dwDesiredAccess= */ GENERIC_READ | GENERIC_WRITE,
 | |
|             /* dwShareMode= */ 0,
 | |
|             /* lpSecurityAttribues= */ nullptr,
 | |
|             /* dwCreationDisposition= */ OPEN_EXISTING,
 | |
|             /* dwFlagsAndAttributes= */ FILE_ATTRIBUTE_NORMAL,
 | |
|             /* hTemplateFile= */ nullptr);
 | |
|         if (_handle == INVALID_HANDLE_VALUE)
 | |
|             error("cannot open serial port '{}': {}",
 | |
|                 name,
 | |
|                 get_last_error_string());
 | |
| 
 | |
|         DCB dcb = {.DCBlength = sizeof(DCB),
 | |
|             .BaudRate = CBR_9600,
 | |
|             .fBinary = true,
 | |
|             .ByteSize = 8,
 | |
|             .Parity = NOPARITY,
 | |
|             .StopBits = ONESTOPBIT};
 | |
|         SetCommState(_handle, &dcb);
 | |
| 
 | |
|         COMMTIMEOUTS commtimeouts = {0};
 | |
|         commtimeouts.ReadIntervalTimeout = 100;
 | |
|         SetCommTimeouts(_handle, &commtimeouts);
 | |
| 
 | |
|         /* Toggle DTR to reset the device. */
 | |
| 
 | |
|         toggleDtr();
 | |
| 
 | |
|         PurgeComm(_handle,
 | |
|             PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
 | |
|     }
 | |
| 
 | |
|     ~SerialPortImpl() override
 | |
|     {
 | |
|         CloseHandle(_handle);
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     void toggleDtr() override
 | |
|     {
 | |
|         if (!EscapeCommFunction(_handle, CLRDTR))
 | |
|             error("Couldn't clear DTR: {}", get_last_error_string());
 | |
|         Sleep(200);
 | |
|         if (!EscapeCommFunction(_handle, SETDTR))
 | |
|             error("Couldn't set DTR: {}", get_last_error_string());
 | |
|     }
 | |
| 
 | |
|     ssize_t readImpl(uint8_t* buffer, size_t len) override
 | |
|     {
 | |
|         DWORD rlen;
 | |
|         bool r = ReadFile(
 | |
|             /* hFile= */ _handle,
 | |
|             /* lpBuffer= */ buffer,
 | |
|             /* nNumberOfBytesToRead= */ len,
 | |
|             /* lpNumberOfBytesRead= */ &rlen,
 | |
|             /* lpOverlapped= */ nullptr);
 | |
|         if (!r)
 | |
|             error("serial read I/O error: {}", get_last_error_string());
 | |
|         return rlen;
 | |
|     }
 | |
| 
 | |
|     ssize_t write(const uint8_t* buffer, size_t len) override
 | |
|     {
 | |
|         DWORD wlen;
 | |
|         /* Windows gets unhappy if we try to transfer too much... */
 | |
|         len = std::min(4096U, len);
 | |
|         bool r = WriteFile(
 | |
|             /* hFile= */ _handle,
 | |
|             /* lpBuffer= */ buffer,
 | |
|             /* nNumberOfBytesToWrite= */ len,
 | |
|             /* lpNumberOfBytesWritten= */ &wlen,
 | |
|             /* lpOverlapped= */ nullptr);
 | |
|         if (!r)
 | |
|             error("serial write I/O error: {}", get_last_error_string());
 | |
|         return wlen;
 | |
|     }
 | |
| 
 | |
|     void setBaudRate(int baudRate) override
 | |
|     {
 | |
|         DCB dcb = {.DCBlength = sizeof(DCB),
 | |
|             .BaudRate = (DWORD) baudRate,
 | |
|             .fBinary = true,
 | |
|             .ByteSize = 8,
 | |
|             .Parity = NOPARITY,
 | |
|             .StopBits = ONESTOPBIT};
 | |
|         SetCommState(_handle, &dcb);
 | |
| 
 | |
|         toggleDtr();
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     static std::string get_last_error_string()
 | |
|     {
 | |
|         DWORD error = GetLastError();
 | |
|         if (error == 0)
 | |
|             return "OK";
 | |
| 
 | |
|         LPSTR buffer = nullptr;
 | |
|         size_t size = FormatMessageA(
 | |
|             /* dwFlags= */ FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | |
|                 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 | |
|             /* lpSource= */ nullptr,
 | |
|             /* dwMessageId= */ error,
 | |
|             /* dwLanguageId= */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | |
|             /* lpBuffer= */ (LPSTR)&buffer,
 | |
|             /* nSize= */ 0,
 | |
|             /* Arguments= */ nullptr);
 | |
| 
 | |
|         std::string message(buffer, size);
 | |
|         LocalFree(buffer);
 | |
|         return message;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     HANDLE _handle;
 | |
| };
 | |
| 
 | |
| #else
 | |
| #include <termios.h>
 | |
| #include <sys/ioctl.h>
 | |
| 
 | |
| class SerialPortImpl : public SerialPort
 | |
| {
 | |
| public:
 | |
|     SerialPortImpl(const std::string& path)
 | |
|     {
 | |
| #ifdef __APPLE__
 | |
|         if (path.find("/dev/tty.") != std::string::npos)
 | |
|             std::cerr << "Warning: you probably want to be using a /dev/cu.* "
 | |
|                          "device\n";
 | |
| #endif
 | |
| 
 | |
|         _fd = open(path.c_str(), O_RDWR);
 | |
|         if (_fd == -1)
 | |
|             error("cannot open serial port '{}': {}", path, strerror(errno));
 | |
| 
 | |
|         struct termios t;
 | |
|         tcgetattr(_fd, &t);
 | |
|         t.c_iflag = 0;
 | |
|         t.c_oflag = 0;
 | |
|         t.c_cflag = CREAD;
 | |
|         t.c_lflag = 0;
 | |
|         t.c_cc[VMIN] = 1;
 | |
|         cfsetspeed(&t, 9600);
 | |
|         tcsetattr(_fd, TCSANOW, &t);
 | |
| 
 | |
|         /* Toggle DTR to reset the device. */
 | |
| 
 | |
|         toggleDtr();
 | |
| 
 | |
|         /* Flush pending input from a generic greaseweazel device */
 | |
|         tcsetattr(_fd, TCSAFLUSH, &t);
 | |
|     }
 | |
| 
 | |
|     ~SerialPortImpl() override
 | |
|     {
 | |
|         close(_fd);
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     void toggleDtr() override
 | |
|     {
 | |
|         int flag = TIOCM_DTR;
 | |
|         if (ioctl(_fd, TIOCMBIC, &flag) == -1)
 | |
|             error("cannot clear DTR on serial port: {}", strerror(errno));
 | |
|         usleep(200000);
 | |
|         if (ioctl(_fd, TIOCMBIS, &flag) == -1)
 | |
|             error("cannot set DTR on serial port: {}", strerror(errno));
 | |
|     }
 | |
| 
 | |
|     ssize_t readImpl(uint8_t* buffer, size_t len) override
 | |
|     {
 | |
|         ssize_t rlen = ::read(_fd, buffer, len);
 | |
|         if (rlen == 0)
 | |
|             error("serial read returned no data (device removed?)");
 | |
|         if (rlen == -1)
 | |
|             error("serial read I/O error: {}", strerror(errno));
 | |
|         return rlen;
 | |
|     }
 | |
| 
 | |
|     ssize_t write(const uint8_t* buffer, size_t len) override
 | |
|     {
 | |
|         ssize_t wlen = ::write(_fd, buffer, len);
 | |
|         if (wlen == -1)
 | |
|             error("serial write I/O error: {}", strerror(errno));
 | |
|         return wlen;
 | |
|     }
 | |
| 
 | |
|     void setBaudRate(int baudRate) override
 | |
|     {
 | |
|         struct termios t;
 | |
|         tcgetattr(_fd, &t);
 | |
|         cfsetspeed(&t, baudRate);
 | |
|         tcsetattr(_fd, TCSANOW, &t);
 | |
| 
 | |
|         toggleDtr();
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     int _fd;
 | |
| };
 | |
| #endif
 | |
| 
 | |
| SerialPort::~SerialPort() {}
 | |
| 
 | |
| void SerialPort::read(uint8_t* buffer, size_t len)
 | |
| {
 | |
|     while (len != 0)
 | |
|     {
 | |
|         // std::cout << "want " << len << " " << std::flush;
 | |
|         size_t rlen = this->readImpl(buffer, len);
 | |
|         // std::cout << "got " << rlen << "\n" << std::flush;
 | |
|         buffer += rlen;
 | |
|         len -= rlen;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SerialPort::read(Bytes& bytes)
 | |
| {
 | |
|     this->read(bytes.begin(), bytes.size());
 | |
| }
 | |
| 
 | |
| Bytes SerialPort::readBytes(size_t len)
 | |
| {
 | |
|     Bytes b(len);
 | |
|     this->read(b);
 | |
|     return b;
 | |
| }
 | |
| 
 | |
| uint8_t SerialPort::readByte()
 | |
| {
 | |
|     uint8_t b;
 | |
|     this->read(&b, 1);
 | |
|     return b;
 | |
| }
 | |
| 
 | |
| void SerialPort::writeByte(uint8_t b)
 | |
| {
 | |
|     this->write(&b, 1);
 | |
| }
 | |
| 
 | |
| void SerialPort::write(const Bytes& bytes)
 | |
| {
 | |
|     int ptr = 0;
 | |
|     while (ptr < bytes.size())
 | |
|     {
 | |
|         ssize_t wlen = this->write(bytes.cbegin() + ptr, bytes.size() - ptr);
 | |
|         ptr += wlen;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SerialPort::writeLine(const std::string& chars)
 | |
| {
 | |
|     this->write((const uint8_t*)&chars[0], chars.size());
 | |
|     writeByte('\n');
 | |
| }
 | |
| 
 | |
| std::string SerialPort::readLine()
 | |
| {
 | |
|     std::string s;
 | |
| 
 | |
|     for (;;)
 | |
|     {
 | |
|         uint8_t b = readByte();
 | |
|         if (b == '\r')
 | |
|             continue;
 | |
|         if (b == '\n')
 | |
|             return s;
 | |
| 
 | |
|         s += b;
 | |
|     }
 | |
| }
 | |
| 
 | |
| std::unique_ptr<SerialPort> SerialPort::openSerialPort(const std::string& path)
 | |
| {
 | |
|     return std::make_unique<SerialPortImpl>(path);
 | |
| }
 |