diff --git a/CMakeLists.txt b/CMakeLists.txt index 55ac322d9..de9279dd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,8 +26,8 @@ set(CMAKE_AUTORCC ON) add_executable(gpsbabel) # Find the QtCore library -find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat) -list(APPEND QT_LIBRARIES Qt6::Core Qt6::Core5Compat) +find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat SerialPort) +list(APPEND QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::SerialPort) if(${Qt6Core_VERSION} VERSION_LESS 6.2) message(FATAL_ERROR "Qt version ${Qt6Core_VERSION} found, but version 6.2 or newer is required.") else() diff --git a/jeeps/gpsdevice.h b/jeeps/gpsdevice.h index 757acb14d..0a83e0650 100644 --- a/jeeps/gpsdevice.h +++ b/jeeps/gpsdevice.h @@ -26,8 +26,6 @@ typedef struct gpsdevh gpsdevh; #include "jeeps/gps.h" -#define usecDELAY 180000 /* Microseconds before GPS sends A001 */ - int32_t GPS_Device_Chars_Ready(gpsdevh* fd); int32_t GPS_Device_On(const char* port, gpsdevh** fd); diff --git a/jeeps/gpsserial.cc b/jeeps/gpsserial.cc index 6e20b5574..d522dde89 100644 --- a/jeeps/gpsserial.cc +++ b/jeeps/gpsserial.cc @@ -23,671 +23,302 @@ ** Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ** Boston, MA 02110-1301, USA. ********************************************************************/ +#include // for va_end, va_list, va_start +#include // for fprintf, stderr + +#include // for QByteArray +#include // for QDebug, operator<< +#include // for QIODeviceBase, QIODeviceBase::ReadWrite +#include // for QSerialPort, QSerialPort::AllDirections +#include // for QString +#include // for endl +#include // for QThread +#include // for qPrintable, qint64 + #include "jeeps/gps.h" -#include "gbser.h" #include "jeeps/gpsserial.h" -#include -#include -#include -#include -#include -#include -#include int gps_baud_rate = DEFAULT_BAUD; -#if 0 -#define GARMULATOR 1 -char* rxdata[] = { - "10 06 02 fe 00 fa 10 03", - "10 ff 7d 97 00 0e 01 53 74 72 65 65 74 50 69 6c 6f 74 20 33 20 53 6f 66 74 77 61 72 65 20 56 65 72 73 69 6f 6e 20 32 2e 37 30 00 56 45 52 42 4d 41 50 20 41 6d 65 72 69 63 61 73 20 41 75 74 6f 72 6f 75 74 65 20 31 2e 30 30 00 56 45 52 41 55 44 20 45 6e 67 6c 69 73 68 20 33 2e 30 31 00 56 45 52 53 50 4c 53 43 52 4e 20 53 70 6c 61 73 68 20 53 63 72 65 65 6e 20 4d 69 73 73 69 6e 67 00 f1 10 03", - "10 f8 0e 56 45 52 53 4d 41 50 31 20 4e 6f 6e 65 00 fb 10 03", - - /* Guessing from here down */ - "10 06 02 fe 00 fa 10 03", /* Ack the unknown packet */ - "10 fd 24 50 00 00 4c 01 00 41 0a 00 41 64 00 44 6d 00 41 c9 00 44 ca 00 44 6d 00 44 d2 00 41 2d 01 44 36 01 44 2d 01 66 10 03", /* PTR Array */ - "10 06 02 0a 00 ee 10 03", /* Ack */ - "10 0e 08 06 04 d4 07 00 17 3a 30 84 10 03", /* DATTIME */ - "10 06 02 0a 00 ee 10 03", /* Ack */ - "10 1b 02 09 00 da 10 03", /* RECORD */ - "10 06 02 0a 00 ee 10 03", /* Ack */ - "10 23 5f 01 00 ff 70 3f 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a6 1b aa 19 6e 78 5c c2 00 00 00 00 51 59 04 69 00 00 00 00 00 00 00 00 ff ff ff ff 47 43 31 41 33 37 00 54 68 65 20 54 72 6f 6c 6c 20 62 79 20 61 31 38 32 70 69 6c 6f 74 20 26 20 46 61 6d 69 6c 79 00 00 00 00 00 59 10 03" - "10 0c 02 07 00 eb 10 03" /* XFERCMP */ +struct serial_data_handle { + QSerialPort sp; }; -#endif -/* - * termio on Cygwin is apparently broken, so we revert to Windows serial. - */ -#if defined (__WIN32__) || defined (__CYGWIN__) - -#include -#include "gbser_win.h" - -typedef struct { - HANDLE comport; -} win_serial_data; /* * Display an error from the serial subsystem. */ -void GPS_Serial_Error(const char* fmt, ...) +[[gnu::format(printf, 2, 3)]] static void GPS_Serial_Error(const serial_data_handle* h, const char* fmt, ...) { va_list ap; - char msg[200]; - char* s; - int b; - va_start(ap, fmt); - b = vsnprintf(msg, sizeof(msg), fmt, ap); - s = msg + b; - *s++ = ':'; - *s++ = ' '; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, - GetLastError(), 0, s, sizeof(msg) - b - 2, nullptr); + QString msg = QString::vasprintf(fmt, ap); + msg.append(": "); + msg.append(h->sp.errorString()); - GPS_Error("%s", msg); // valid clang -Wformat-security warning + GPS_Error("%s", qPrintable(msg)); va_end(ap); } -int32_t GPS_Serial_On(const char* port, gpsdevh** dh) -{ - DCB tio; - COMMTIMEOUTS timeout; - HANDLE comport; - const char* xname = fix_win_serial_name(port); - win_serial_data* wsd = (win_serial_data*) xcalloc(sizeof(win_serial_data), 1); - *dh = (gpsdevh*) wsd; - GPS_Diag("Opening %s\n", xname); - comport = CreateFileA(xname, GENERIC_READ|GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, 0, NULL); - - if (comport == INVALID_HANDLE_VALUE) { - GPS_Serial_Error("CreateFile on '%s' failed", xname); - gps_errno = SERIAL_ERROR; - return 0; - } - - tio.DCBlength = sizeof(DCB); - GetCommState(comport, &tio); - tio.BaudRate = CBR_9600; - tio.fBinary = TRUE; - tio.fParity = TRUE; - tio.fOutxCtsFlow = FALSE; - tio.fOutxDsrFlow = FALSE; - tio.fDtrControl = DTR_CONTROL_ENABLE; - tio.fDsrSensitivity = FALSE; - tio.fTXContinueOnXoff = TRUE; - tio.fOutX = FALSE; - tio.fInX = FALSE; - tio.fErrorChar = FALSE; - tio.fNull = FALSE; - tio.fRtsControl = RTS_CONTROL_ENABLE; - tio.fAbortOnError = FALSE; - tio.ByteSize = 8; - tio.Parity = NOPARITY; - tio.StopBits = ONESTOPBIT; - - if (!SetCommState(comport, &tio)) { - GPS_Serial_Error("SetCommState on port '%s' failed", port); - CloseHandle(comport); - comport = INVALID_HANDLE_VALUE; - gps_errno = SERIAL_ERROR; - return 0; - } - - /* - * The timeouts are kind of fictional as we always end up doing - * single byte reads. At 9600bps (the default) the individual - * character time is 104Millisecs, so these are mostly "dead-man" - * (i.e. cable unplugged, unit not turned on) values. - */ - GetCommTimeouts(comport, &timeout); - - timeout.ReadIntervalTimeout = 1000; /*like vtime. In MS. */ - timeout.ReadTotalTimeoutMultiplier = 1000; - timeout.ReadTotalTimeoutConstant = 1000; - timeout.WriteTotalTimeoutMultiplier = 1000; - timeout.WriteTotalTimeoutConstant = 1000; - if (!SetCommTimeouts(comport, &timeout)) { - GPS_Serial_Error("SetCommTimeouts"); - CloseHandle(comport); - comport = INVALID_HANDLE_VALUE; - gps_errno = SERIAL_ERROR; - return 0; - } - wsd->comport = comport; - return 1; -} - -int32_t GPS_Serial_Off(gpsdevh* dh) -{ - win_serial_data* wsd = (win_serial_data*)dh; - CloseHandle(wsd->comport); - wsd->comport = INVALID_HANDLE_VALUE; - xfree(wsd); - return 1; -} - -int32_t GPS_Serial_Chars_Ready(gpsdevh* dh) -{ - COMSTAT lpStat; - DWORD lpErrors; - win_serial_data* wsd = (win_serial_data*)dh; - - ClearCommError(wsd->comport, &lpErrors, &lpStat); - return (lpStat.cbInQue > 0); -} - -int32_t GPS_Serial_Wait(gpsdevh* fd) -{ - /* Wait a short time before testing if data is ready. - * The GPS II, in particular, has a noticable time responding - * with a response to the device inquiry and if we give up on this - * too soon, we fail to read the response to the A001 packet and - * blow our state machines when it starts streaming the capabiilties - * response packet. - */ - Sleep(usecDELAY / 1000); - return GPS_Serial_Chars_Ready(fd); -} - -int32_t GPS_Serial_Flush(gpsdevh* /* fd */) -{ - return 1; -} - -int32_t GPS_Serial_Write(gpsdevh* dh, const void* obuf, int size) -{ - win_serial_data* wsd = (win_serial_data*)dh; - DWORD len; - - /* - * Unbelievably, the Keyspan PDA serial driver 3.2, a "Windows - * Certified driver", will crash the OS on a write of zero bytes. - * We get such writes from upstream when there are zero payload - * bytes. SO we trap those here to stop Keyspan & Windows from - * nuking the system. - */ - if (size == 0) { - return 0; - } - WriteFile(wsd->comport, obuf, size, &len, NULL); - if (len != (DWORD) size) { - fatal("Write error. Wrote %d of %d bytes.\n", (int)len, size); - } - return len; -} - -int32_t GPS_Serial_Read(gpsdevh* dh, void* ibuf, int size) -{ - DWORD cnt = 0; - win_serial_data* wsd = (win_serial_data*)dh; - - ReadFile(wsd->comport, ibuf, size, &cnt, NULL); - return cnt; -} - -// Based on information by Kolesár András from -// http://www.manualslib.com/manual/413938/Garmin-Gps-18x.html?page=32 -int32_t GPS_Serial_Set_Baud_Rate(gpsdevh* fd, int br) -{ - static UC data[4]; - GPS_Packet tra; - GPS_Packet rec; - win_serial_data* wsd = (win_serial_data*)fd; - - DWORD speed = mkspeed(br); - - // Turn off all requests by transmitting packet - GPS_Util_Put_Short(data, 0); - GPS_Make_Packet(&tra, 0x1c, data, 2); - if (!GPS_Write_Packet(fd,tra)) { - return gps_errno; - } - if (!GPS_Get_Ack(fd, &tra, &rec)) { - return gps_errno; - } - - GPS_Util_Put_Int(data, br); - GPS_Make_Packet(&tra, 0x30, data, 4); - if (!GPS_Write_Packet(fd,tra)) { - return gps_errno; - } - if (!GPS_Get_Ack(fd, &tra, &rec)) { - return gps_errno; - } - - // Receive IOP_BAUD_ACPT_DATA - if (!GPS_Packet_Read(fd, &rec)) { - return gps_errno; - } - - // Acnowledge new speed - if (!GPS_Send_Ack(fd, &tra, &rec)) { - return gps_errno; - } - GPS_Device_Flush(fd); - GPS_Device_Wait(fd); - - // Sleep for a small amount of time, about 100 milliseconds, - // to make sure the packet was successfully transmitted to the GPS unit. - QThread::usleep(100000); - - // Change port speed - DCB tio; - tio.DCBlength = sizeof(DCB); - - GetCommState(wsd->comport, &tio); - tio.BaudRate = speed; - if (!SetCommState(wsd->comport, &tio)) { - GPS_Serial_Error("SetCommState on port for alternate bit rate failed"); - CloseHandle(wsd->comport); - return 0; - } - - GPS_Util_Put_Short(data, 0x3a); - GPS_Make_Packet(&tra, 0x0a, data, 2); - if (!GPS_Write_Packet(fd,tra)) { - return gps_errno; - } - if (!GPS_Get_Ack(fd, &tra, &rec)) { - return gps_errno; - } - - GPS_Util_Put_Short(data, 0x3a); - GPS_Make_Packet(&tra, 0x0a, data, 2); - if (!GPS_Write_Packet(fd,tra)) { - return gps_errno; - } - if (!GPS_Get_Ack(fd, &tra, &rec)) { - return gps_errno; - } - - if (global_opts.debug_level >= 1) fprintf(stderr, "Serial port speed set to %d\n", br); - return 0; - -} -#else - -#include "gbser_posix.h" -#include -#include -#include -#include - -typedef struct { - int fd; /* File descriptor */ - struct termios gps_ttysave; -} posix_serial_data; - -/* @func GPS_Serial_Open *********************************************** +/* @func GPS_Serial_On ************************************************* ** -** Open a serial port 8bits 1 stop bit 9600 baud +** Open a serial port 8 data bits, 1 stop bit, 9600 baud ** -** @param [w] fd [int32_t *] file descriptor -** @param [r] port [const char *] port e.g. ttyS1 +** @param [r] port [const char *] port e.g. COM4 +** @param [w] dh [gpsdev** ] file descriptor ** ** @return [int32_t] false upon error ************************************************************************/ - -int32_t GPS_Serial_Open(gpsdevh* dh, const char* port) +int32_t GPS_Serial_On(const char* port, gpsdevh** dh) { - struct termios tty; - if (global_opts.debug_level >= 2) fprintf(stderr, "GPS Serial Open at %d\n", gps_baud_rate); - speed_t baud = mkspeed(gps_baud_rate); - auto* psd = (posix_serial_data*)dh; - - /* - * This originally had O_NDELAY | O_NOCTTY in here, but this - * causes problems with Linux USB ttys (observed on PL2303 and MCT) - * and the rest of the code doesn't _REALLY_ handle the partial - * write/retry case anyway. - robertl - */ - if ((psd->fd = open(port, O_RDWR))==-1) { - GPS_Serial_Error("XSERIAL: Cannot open serial port '%s'", port); + auto* h = new serial_data_handle; + GPS_Diag("Opening %s\n", port); + h->sp.setPortName(port); + bool ok = h->sp.open(QIODeviceBase::ReadWrite); + if (!ok) { + GPS_Serial_Error(h, "Cannot open serial port '%s'", port); gps_errno = SERIAL_ERROR; + delete h; return 0; } - - if (tcgetattr(psd->fd,&psd->gps_ttysave)==-1) { - gps_errno = HARDWARE_ERROR; - GPS_Serial_Error("SERIAL: tcgetattr error"); + ok = h->sp.setDataTerminalReady(true); + if (!ok) { + GPS_Serial_Error(h, "Couldn't set DTR"); + gps_errno = SERIAL_ERROR; + delete h; return 0; } - tty = psd->gps_ttysave; - - tty.c_cflag &= ~(CSIZE); - tty.c_cflag |= (CREAD | CS8 | CLOCAL); - cfsetospeed(&tty,baud); - cfsetispeed(&tty,baud); - - tty.c_lflag &= 0x0; - tty.c_iflag &= 0x0; - tty.c_oflag &= 0x0; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - - if (tcsetattr(psd->fd,TCSANOW|TCSAFLUSH,&tty)==-1) { - GPS_Serial_Error("SERIAL: tcsetattr error"); + ok = h->sp.setRequestToSend(true); + if (!ok) { + GPS_Serial_Error(h, "Couldn't set RTS"); + gps_errno = SERIAL_ERROR; + delete h; return 0; } - - return 1; -} - -/* - * Display an error from the serial subsystem. - */ -void GPS_Serial_Error(const char* fmt, ...) -{ - va_list ap; - char msg[200]; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - -// FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, -// GetLastError(), 0, s, sizeof(msg) - b - 2, 0 ); - GPS_Error("%s: %s", msg, strerror(errno)); - va_end(ap); -} - -int32_t GPS_Serial_Read(gpsdevh* dh, void* ibuf, int size) -{ - auto* psd = (posix_serial_data*)dh; -#if GARMULATOR - static int l; - static char* rp; - char** rxp = &rxdata[l]; - char* hex; - char* rx = *rxp; - char* ib = ibuf; - - if (!rp) { - rp = rxdata[0]; - } - - /* Skip over nulls in our pasted strings */ - if (*rp == 0) { - rp = rxdata[++l]; - } - - *ib = strtoul(rp, &rp, 16); - if (*rp) { - rp++; - } - fprintf(stderr, "."); + if (global_opts.debug_level > 2) { + QString debugstr; + QDebug debug(&debugstr); + debug << " baudRate:" << h->sp.baudRate() << Qt::endl; + debug << " parity:" << h->sp.parity() << Qt::endl; + debug << " stopBits:" << h->sp.stopBits() << Qt::endl; + debug << " flow control:" << h->sp.flowControl() << Qt::endl; + debug << " break enabled:" << h->sp.isBreakEnabled() << Qt::endl; + debug << " pinout signals:" << h->sp.pinoutSignals() << Qt::endl; + debug << " read buffer size:" << h->sp.readBufferSize() << Qt::endl; + debug << " DTR:" << h->sp.isDataTerminalReady() << Qt::endl; + debug << " RTS:" << h->sp.isRequestToSend() << Qt::endl; + debug << " error status:" << h->sp.error() << Qt::endl; + GPS_Diag("%s", qPrintable(debugstr)); + } + + *dh = reinterpret_cast(h); return 1; - -#else - return read(psd->fd, ibuf, size); -#endif -} - -int32_t GPS_Serial_Write(gpsdevh* dh, const void* obuf, int size) -{ - auto* psd = (posix_serial_data*)dh; - return write(psd->fd, obuf, size); } - -/* @func GPS_Serial_Flush *********************************************** +/* @func GPS_Serial_Off ************************************************* ** -** Flush the serial lines +** Close serial port ** -** @param [w] fd [int32_t] file descriptor +** @param [r] dh [gpsdev* ] file descriptor ** ** @return [int32_t] false upon error ************************************************************************/ -int32_t GPS_Serial_Flush(gpsdevh* fd) +int32_t GPS_Serial_Off(gpsdevh* dh) { - auto* psd = (posix_serial_data*)fd; - - if (tcflush(psd->fd,TCIOFLUSH)) { - GPS_Serial_Error("SERIAL: tcflush error"); - gps_errno = SERIAL_ERROR; - return 0; - } - + auto* h = reinterpret_cast(dh); + h->sp.close(); + delete h; return 1; } - - -/* @func GPS_Serial_Close *********************************************** -** -** Close serial port -** -** @param [r] fd [int32_t ] file descriptor -** @param [r] port [const char *] port e.g. ttyS1 -** -** @return [int32_t] false upon error -************************************************************************/ - -int32_t GPS_Serial_Close(gpsdevh* fd) +static int32_t GPS_Serial_Chars_Ready_After(gpsdevh* dh, int msec_timeout) { - auto* psd = (posix_serial_data*)fd; - - if (tcsetattr(psd->fd, TCSAFLUSH, &psd->gps_ttysave)==-1) { - gps_errno = HARDWARE_ERROR; - GPS_Serial_Error("SERIAL: tcsetattr error"); - return 0; - } - - if (close(psd->fd)==-1) { - GPS_Serial_Error("SERIAL: Error closing serial port"); - gps_errno = SERIAL_ERROR; - return 0; + auto* h = reinterpret_cast(dh); + /* If no bytes are available call waitForRead() + * in order to process IO in blocking mode. + * This may result in bytes becoming available. + * Don't process IO unless necessary as it negatively + * impacts performance. + */ + if (h->sp.bytesAvailable() <= 0) { + bool ok = h->sp.waitForReadyRead(msec_timeout); + if (!ok) { + h->sp.clearError(); + } } - - return 1; + return h->sp.bytesAvailable() > 0; } - /* @func GPS_Serial_Chars_Ready ***************************************** ** ** Query port to see if characters are waiting to be read ** -** @param [r] fd [int32_t ] file descriptor +** @param [r] dh [gpsdev* ] file descriptor ** ** @return [int32_t] true if chars waiting ************************************************************************/ - int32_t GPS_Serial_Chars_Ready(gpsdevh* dh) { - fd_set rec; - struct timeval t; - auto* psd = (posix_serial_data*)dh; - int32_t fd = psd->fd; - -#if GARMULATOR - static foo; - /* Return sporadic reads just to torment the rest of the code. */ - if ((foo++ & 0xf) == 0) { - return 1; - } else { - return 0; - } -#endif - - FD_ZERO(&rec); - FD_SET(fd,&rec); - - t.tv_sec = 0; - t.tv_usec = 1000; - (void) select(fd+1,&rec,nullptr,nullptr,&t); - if (FD_ISSET(fd,&rec)) { - return 1; - } - - return 0; + return GPS_Serial_Chars_Ready_After(dh, 1); } - - /* @func GPS_Serial_Wait *********************************************** ** ** Wait 80 milliseconds before testing for input. The GPS delay ** appears to be around 40-50 milliseconds. Doubling the value is to ** allow some leeway. ** -** @param [r] fd [int32_t ] file descriptor +** @param [r] dh [gpsdev* ] file descriptor ** ** @return [int32_t] true if serial chars waiting ************************************************************************/ - int32_t GPS_Serial_Wait(gpsdevh* dh) { - fd_set rec; - struct timeval t; - auto* psd = (posix_serial_data*)dh; - - FD_ZERO(&rec); - FD_SET(psd->fd,&rec); - - t.tv_sec = 0; - t.tv_usec = 180000; /* Microseconds before GPS sends A001 */ - - (void) select(psd->fd+1,&rec,nullptr,nullptr,&t); - if (FD_ISSET(psd->fd,&rec)) { - return 1; - } - - return 0; + /* Wait a short time before testing if data is ready. + * The GPS II, in particular, has a noticable time responding + * with a response to the device inquiry and if we give up on this + * too soon, we fail to read the response to the A001 packet and + * blow our state machines when it starts streaming the capabiilties + * response packet. + */ + return GPS_Serial_Chars_Ready_After(dh, msecDELAY); } - - -/* @func GPS_Serial_On ***************************************** +/* @func GPS_Serial_Flush *********************************************** ** -** Set up port +** Flush the serial lines ** -** @param [r] port [const char *] port -** @param [w] fd [int32_t *] file descriptor +** @param [r] dh [gpsdev* ] file descriptor ** -** @return [int32_t] success +** @return [int32_t] false upon error ************************************************************************/ - -int32_t GPS_Serial_On(const char* port, gpsdevh** dh) +int32_t GPS_Serial_Flush(gpsdevh* dh) { - auto* psd = (posix_serial_data*) xcalloc(sizeof(posix_serial_data), 1); - *dh = (gpsdevh*) psd; - - if (!GPS_Serial_Open((gpsdevh*) psd,port)) { - GPS_Error("Cannot open serial port '%s'", port); + auto* h = reinterpret_cast(dh); + bool ok = h->sp.clear(QSerialPort::AllDirections); + if (!ok) { + GPS_Serial_Error(h, "SERIAL: flush error"); gps_errno = SERIAL_ERROR; - return 0; } - return 1; + return ok; } +int32_t GPS_Serial_Write(gpsdevh* dh, const void* obuf, int size) +{ + auto* h = reinterpret_cast(dh); + + /* + * Unbelievably, the Keyspan PDA serial driver 3.2, a "Windows + * Certified driver", will crash the OS on a write of zero bytes. + * We get such writes from upstream when there are zero payload + * bytes. SO we trap those here to stop Keyspan & Windows from + * nuking the system. + */ + if (size == 0) { + return 0; + } + + qint64 len = h->sp.write(static_cast(obuf), size); + if (len != size) { + fatal("Write error. Wrote %d of %d bytes.\n", static_cast(len), size); + } -/* @func GPS_Serial_Off *********************************************** -** -** Done with port -** -** @param [r] port [const char *] port -** @param [r] fd [int32_t ] file descriptor -** -** @return [int32_t] success -************************************************************************/ + /* Call waitForBytesWritten to process IO in blocking mode. + */ + bool ok = h->sp.waitForBytesWritten(10); + if (!ok) { + h->sp.clearError(); + } + return len; +} -int32_t GPS_Serial_Off(gpsdevh* dh) +int32_t GPS_Serial_Read(gpsdevh* dh, void* ibuf, int size) { + auto* h = reinterpret_cast(dh); - if (!GPS_Serial_Close(dh)) { - GPS_Error("Error Closing port"); - gps_errno = HARDWARE_ERROR; - return 0; - } - xfree(dh); - return 1; + /* Process IO */ + (void) GPS_Serial_Chars_Ready(dh); + + qint64 cnt = h->sp.read(static_cast(ibuf), size); + return cnt; } // Based on information by Kolesár András from // http://www.manualslib.com/manual/413938/Garmin-Gps-18x.html?page=32 -int32_t GPS_Serial_Set_Baud_Rate(gpsdevh* fd, int br) +int32_t GPS_Serial_Set_Baud_Rate(gpsdevh* dh, int br) { - - struct termios tty; static UC data[4]; GPS_Packet tra; GPS_Packet rec; - - speed_t speed = mkspeed(br); + auto* h = reinterpret_cast(dh); // Turn off all requests by transmitting packet GPS_Util_Put_Short(data, 0); GPS_Make_Packet(&tra, 0x1c, data, 2); - if (!GPS_Write_Packet(fd,tra)) { + if (!GPS_Write_Packet(dh,tra)) { return gps_errno; } - if (!GPS_Get_Ack(fd, &tra, &rec)) { + if (!GPS_Get_Ack(dh, &tra, &rec)) { return gps_errno; } GPS_Util_Put_Int(data, br); GPS_Make_Packet(&tra, 0x30, data, 4); - if (!GPS_Write_Packet(fd,tra)) { + if (!GPS_Write_Packet(dh,tra)) { return gps_errno; } - if (!GPS_Get_Ack(fd, &tra, &rec)) { + if (!GPS_Get_Ack(dh, &tra, &rec)) { return gps_errno; } // Receive IOP_BAUD_ACPT_DATA - if (!GPS_Packet_Read(fd, &rec)) { + if (!GPS_Packet_Read(dh, &rec)) { return gps_errno; } // Acknowledge new speed - if (!GPS_Send_Ack(fd, &tra, &rec)) { + if (!GPS_Send_Ack(dh, &tra, &rec)) { return gps_errno; } - GPS_Device_Flush(fd); - GPS_Device_Wait(fd); + GPS_Device_Flush(dh); + GPS_Device_Wait(dh); // Sleep for a small amount of time, about 100 milliseconds, // to make sure the packet was successfully transmitted to the GPS unit. - QThread::usleep(100000); + QThread::usleep(100000); // Change port speed - auto* psd = (posix_serial_data*)fd; - tty = psd->gps_ttysave; - - cfsetospeed(&tty,speed); - cfsetispeed(&tty,speed); - - if (tcsetattr(psd->fd,TCSANOW|TCSAFLUSH,&tty)==-1) { - GPS_Serial_Error("SERIAL: tcsetattr error"); + bool ok = h->sp.setBaudRate(br); + if (!ok) { + GPS_Serial_Error(h, "setBaudRate failed"); + h->sp.close(); return 0; } GPS_Util_Put_Short(data, 0x3a); GPS_Make_Packet(&tra, 0x0a, data, 2); - if (!GPS_Write_Packet(fd,tra)) { + if (!GPS_Write_Packet(dh,tra)) { return gps_errno; } - if (!GPS_Get_Ack(fd, &tra, &rec)) { + if (!GPS_Get_Ack(dh, &tra, &rec)) { return gps_errno; } GPS_Util_Put_Short(data, 0x3a); GPS_Make_Packet(&tra, 0x0a, data, 2); - if (!GPS_Write_Packet(fd,tra)) { + if (!GPS_Write_Packet(dh,tra)) { return gps_errno; } - if (!GPS_Get_Ack(fd, &tra, &rec)) { + if (!GPS_Get_Ack(dh, &tra, &rec)) { return gps_errno; } - if (global_opts.debug_level >= 1) fprintf(stderr, "Serial port speed set to %d\n", br); + if (global_opts.debug_level >= 1) { + fprintf(stderr, "Serial port speed set to %d\n", br); + } return 0; } - -#endif /* __WIN32__ */ diff --git a/jeeps/gpsserial.h b/jeeps/gpsserial.h index aa871b3a7..05984e552 100644 --- a/jeeps/gpsserial.h +++ b/jeeps/gpsserial.h @@ -4,24 +4,16 @@ #include "jeeps/gps.h" -#define usecDELAY 180000 /* Microseconds before GPS sends A001 */ -#define DEFAULT_BAUD 9600 +constexpr int msecDELAY = 180; /* Milliseconds before GPS sends A001 */ +constexpr int DEFAULT_BAUD = 9600; -int32_t GPS_Serial_Chars_Ready(gpsdevh* fd); -// int32 GPS_Serial_Close(int32 fd, const char *port); -// int32 GPS_Serial_Open(int32 *fd, const char *port); -// int32 GPS_Serial_Open_NMEA(int32 *fd, const char *port); -// int32 GPS_Serial_Restoretty(const char *port); -// int32 GPS_Serial_Savetty(const char *port); -int32_t GPS_Serial_On(const char* port, gpsdevh** fd); -int32_t GPS_Serial_Off(gpsdevh* fd); -int32_t GPS_Serial_Wait(gpsdevh* fd); -int32_t GPS_Serial_Flush(gpsdevh* fd); -// int32 GPS_Serial_On_NMEA(const char *port, gpsdevh **fd); -int32_t GPS_Serial_Read(gpsdevh* fd, void* ibuf, int size); -int32_t GPS_Serial_Write(gpsdevh* fd, const void* obuf, int size); - - -int32_t GPS_Serial_Set_Baud_Rate(gpsdevh* fd, int br); +int32_t GPS_Serial_Chars_Ready(gpsdevh* dh); +int32_t GPS_Serial_On(const char* port, gpsdevh** dh); +int32_t GPS_Serial_Off(gpsdevh* dh); +int32_t GPS_Serial_Wait(gpsdevh* dh); +int32_t GPS_Serial_Flush(gpsdevh* dh); +int32_t GPS_Serial_Read(gpsdevh* dh, void* ibuf, int size); +int32_t GPS_Serial_Write(gpsdevh* dh, const void* obuf, int size); +int32_t GPS_Serial_Set_Baud_Rate(gpsdevh* dh, int br); #endif // JEEPS_GPSSERIAL_H_INCLUDED_ diff --git a/jeeps/gpsusbwin.cc b/jeeps/gpsusbwin.cc index 6ed6e6854..43b25ce29 100644 --- a/jeeps/gpsusbwin.cc +++ b/jeeps/gpsusbwin.cc @@ -55,6 +55,27 @@ typedef struct { static HANDLE usb_handle = INVALID_HANDLE_VALUE; static int usb_tx_packet_size ; +[[gnu::format(printf, 1, 2)]] static void GPS_Serial_Error(const char* fmt, ...) +{ + va_list ap; + char msg[200]; + char* s; + int b; + + va_start(ap, fmt); + b = vsnprintf(msg, sizeof(msg), fmt, ap); + s = msg + b; + *s++ = ':'; + *s++ = ' '; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, + GetLastError(), 0, s, sizeof(msg) - b - 2, nullptr); + + GPS_Error("%s", msg); // valid clang -Wformat-security warning + + va_end(ap); +} + static int gusb_win_close(gpsdevh* /* handle */, bool /* exit_lib */) { diff --git a/jeeps/gpsutil.h b/jeeps/gpsutil.h index 0e50f703a..d762632e3 100644 --- a/jeeps/gpsutil.h +++ b/jeeps/gpsutil.h @@ -21,7 +21,6 @@ uint32_t GPS_Util_Get_Uint(const UC* s); void GPS_Warning(const char* s); [[gnu::format(printf, 1, 2)]] void GPS_Error(const char* fmt, ...); -[[gnu::format(printf, 1, 2)]] void GPS_Serial_Error(const char* fmt, ...); [[noreturn]] void GPS_Fatal(const char* s); void GPS_Enable_Error(); void GPS_Enable_Warning(); diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index cd88c786c..bd7bd0b0a 100755 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -67,9 +67,11 @@ parts: - libudev-dev - qt6-base-dev - libqt6core5compat6-dev + - libqt6serialport6-dev stage-packages: - zlib1g - libshp2 - libusb-1.0-0 - libqt6core6 - libqt6core5compat6 + - libqt6serialport6