mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
support Windows in Serial.h|cpp
The serial code was Linux/Mac only and utilised termio/tcsetattr functions that were not available on Windows platforms. This patch ports the serial code to the Windows APIs using the same approach as the Computrainer.cpp code. It allows us to support old-style serial download cables on Windows. (We already supported them on Mac and Linux.)
This commit is contained in:
committed by
Sean Rhea
parent
9920afdb4d
commit
a89ebacac0
184
src/Serial.cpp
184
src/Serial.cpp
@@ -17,6 +17,15 @@
|
||||
*/
|
||||
|
||||
#include "Serial.h"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
// WIN32 includes
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
|
||||
// Linux and Mac Includes
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@@ -32,22 +41,39 @@
|
||||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool SerialRegistered = CommPort::addListFunction(&Serial::myListCommPorts);
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
Serial::Serial(const QString &path) : path(path), isOpen(false)
|
||||
{
|
||||
}
|
||||
#else
|
||||
Serial::Serial(const QString &path) : path(path), fd(-1)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
Serial::~Serial()
|
||||
{
|
||||
if (fd >= 0)
|
||||
#ifdef Q_OS_WIN32
|
||||
if (isOpen == true) {
|
||||
close();
|
||||
}
|
||||
#else
|
||||
if (fd >= 0) close();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
Serial::open(QString &err)
|
||||
{
|
||||
#ifndef Q_OS_WIN32
|
||||
|
||||
//
|
||||
// Linux and Mac OSX use stdio / termio / tcsetattr
|
||||
//
|
||||
assert(fd < 0);
|
||||
fd = ::open(path.toAscii().constData(), O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@@ -83,16 +109,81 @@ Serial::open(QString &err)
|
||||
assert(0);
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
|
||||
//
|
||||
// Windows uses CreateFile / DCB / SetCommState
|
||||
//
|
||||
|
||||
DCB deviceSettings; // serial port settings baud rate et al
|
||||
COMMTIMEOUTS timeouts; // timeout settings on serial ports
|
||||
|
||||
// if deviceFilename references a port above COM9
|
||||
// then we need to open "\\.\COMX" not "COMX"
|
||||
QString portSpec = "\\\\.\\" + path;
|
||||
wchar_t deviceFilenameW[32]; // \\.\COM32 needs 9 characters, 32 should be enough?
|
||||
MultiByteToWideChar(CP_ACP, 0, portSpec.toAscii(), -1, (LPWSTR)deviceFilenameW,
|
||||
sizeof(deviceFilenameW));
|
||||
|
||||
// win32 commport API
|
||||
fd = CreateFile (deviceFilenameW, GENERIC_READ|GENERIC_WRITE,
|
||||
FILE_SHARE_DELETE|FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (fd == INVALID_HANDLE_VALUE) return isOpen = false;
|
||||
|
||||
if (GetCommState (fd, &deviceSettings) == false) return isOpen = false;
|
||||
|
||||
// so we've opened the comm port lets set it up for
|
||||
deviceSettings.BaudRate = CBR_9600;
|
||||
deviceSettings.fParity = NOPARITY;
|
||||
deviceSettings.ByteSize = 8;
|
||||
deviceSettings.StopBits = ONESTOPBIT;
|
||||
deviceSettings.EofChar = 0x0;
|
||||
deviceSettings.ErrorChar = 0x0;
|
||||
deviceSettings.EvtChar = 0x0;
|
||||
deviceSettings.fBinary = TRUE;
|
||||
deviceSettings.fRtsControl = 0x0;
|
||||
deviceSettings.fOutxCtsFlow = FALSE;
|
||||
|
||||
|
||||
if (SetCommState(fd, &deviceSettings) == false) {
|
||||
CloseHandle(fd);
|
||||
return isOpen = false;
|
||||
}
|
||||
|
||||
timeouts.ReadIntervalTimeout = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = 5000;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 50;
|
||||
timeouts.WriteTotalTimeoutConstant = 5000;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(fd, &timeouts);
|
||||
|
||||
return isOpen = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Serial::close()
|
||||
{
|
||||
#ifndef Q_OS_WIN32
|
||||
assert(fd >= 0);
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
#else
|
||||
if (isOpen == true) {
|
||||
CloseHandle(fd);
|
||||
isOpen = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
//
|
||||
// On Mac/Linux read/write timeout periods
|
||||
// use timeval, whilst on Win32 the readfile
|
||||
// API does this for you.
|
||||
//
|
||||
static struct timeval &
|
||||
operator-=(struct timeval &left, const struct timeval &right)
|
||||
{
|
||||
@@ -114,10 +205,16 @@ operator<(struct timeval &left, const struct timeval &right)
|
||||
return false;
|
||||
return left.tv_usec < right.tv_usec;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
Serial::read(void *buf, size_t nbyte, QString &err)
|
||||
{
|
||||
#ifndef Q_OS_WIN32
|
||||
//
|
||||
// Mac and Linux use select and timevals to
|
||||
// do non-blocking reads with a timeout
|
||||
//
|
||||
assert(fd >= 0);
|
||||
assert(nbyte > 0);
|
||||
struct timeval start;
|
||||
@@ -152,11 +249,27 @@ Serial::read(void *buf, size_t nbyte, QString &err)
|
||||
assert(false);
|
||||
}
|
||||
return count;
|
||||
#else
|
||||
|
||||
//
|
||||
// Win32 API uses readfile
|
||||
// which handles timeouts / ready read
|
||||
//
|
||||
DWORD cBytes;
|
||||
int rc = ReadFile(fd, buf, nbyte, &cBytes, NULL);
|
||||
if (rc) return (int)cBytes;
|
||||
else return (-1);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
Serial::write(void *buf, size_t nbyte, QString &err)
|
||||
{
|
||||
#ifndef Q_OS_WIN32
|
||||
//
|
||||
// Max and Linux use select() and write()
|
||||
//
|
||||
assert(fd >= 0);
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
@@ -177,6 +290,20 @@ Serial::write(void *buf, size_t nbyte, QString &err)
|
||||
if (result < 0)
|
||||
err = QString("write: ") + strerror(errno);
|
||||
return result;
|
||||
#else
|
||||
|
||||
//
|
||||
// Windows use the writefile WIN32 API
|
||||
//
|
||||
DWORD cBytes;
|
||||
int rc = WriteFile(fd, buf, nbyte, &cBytes, NULL);
|
||||
if (!rc) {
|
||||
err = QString("write: error %1").arg(rc);
|
||||
return -1;
|
||||
} else {
|
||||
return (int)cBytes; // how much was written?
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QString
|
||||
@@ -185,6 +312,10 @@ Serial::name() const
|
||||
return QString("Serial: ") + path;
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
//
|
||||
// Linux and Mac device enumerator matches wildcards in /dev
|
||||
//
|
||||
static int
|
||||
find_devices(char *result[], int capacity)
|
||||
{
|
||||
@@ -207,6 +338,57 @@ find_devices(char *result[], int capacity)
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#else
|
||||
//
|
||||
// Windows uses WIN32 API to open file to check existence
|
||||
//
|
||||
static int
|
||||
find_devices(char *result[], int capacity)
|
||||
{
|
||||
// it is valid to have up to 255 com devices!
|
||||
int count=0;
|
||||
|
||||
for (int i=1; i<256 && count < capacity; i++) {
|
||||
|
||||
// longCOM is in form "\\.\COMn" which is used by the API
|
||||
// shortCOM is in form COMn which is familiar to users
|
||||
QString shortCOM = QString("COM%1").arg(i);
|
||||
QString longCOM = "\\\\.\\" + shortCOM;
|
||||
wchar_t deviceFilenameW[32];
|
||||
MultiByteToWideChar(CP_ACP, 0, longCOM.toAscii(), -1, (LPWSTR)deviceFilenameW,
|
||||
sizeof(deviceFilenameW));
|
||||
|
||||
// Try to open the port
|
||||
BOOL bSuccess = FALSE;
|
||||
HANDLE hPort = CreateFile(deviceFilenameW, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
||||
|
||||
if (hPort == INVALID_HANDLE_VALUE) {
|
||||
DWORD dwError = GetLastError();
|
||||
// Check to see if the error was because some other app had the port open
|
||||
// or a general and valid failure
|
||||
|
||||
if (dwError == ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE ||
|
||||
dwError == ERROR_SHARING_VIOLATION || dwError == ERROR_SEM_TIMEOUT)
|
||||
bSuccess = TRUE;
|
||||
} else {
|
||||
|
||||
// The port was opened successfully
|
||||
bSuccess = TRUE;
|
||||
|
||||
// Don't forget to close the port, since we are going to do nothing with it anyway
|
||||
CloseHandle(hPort);
|
||||
}
|
||||
|
||||
// If success then add to the list
|
||||
if (bSuccess) {
|
||||
result[count] = (char*) malloc(shortCOM.length() + 1); // include '\0' terminator
|
||||
strcpy(result[count], shortCOM.toLatin1().constData());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
QVector<CommPortPtr>
|
||||
Serial::myListCommPorts(QString &err)
|
||||
|
||||
Reference in New Issue
Block a user