From ab947fb07817f0244d2b104613be9fecd391c746 Mon Sep 17 00:00:00 2001 From: micio Date: Tue, 31 Jan 2017 10:03:19 +0000 Subject: [PATCH] Bazaar/Serial : added function git-svn-id: svn://ultimatepp.org/upp/trunk@10809 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- bazaar/Serial/Serial.h | 12 +- bazaar/Serial/SerialLinux.cpp | 944 ++++++++++--------- bazaar/Serial/SerialOSX.cpp | 966 +++++++++---------- bazaar/Serial/SerialWindows.cpp | 1557 ++++++++++++++++--------------- 4 files changed, 1776 insertions(+), 1703 deletions(-) diff --git a/bazaar/Serial/Serial.h b/bazaar/Serial/Serial.h index e67016c75..32670eacd 100644 --- a/bazaar/Serial/Serial.h +++ b/bazaar/Serial/Serial.h @@ -81,12 +81,16 @@ class Serial bool Read(byte &c, uint32_t timeout = 0); bool Read(char &c, uint32_t timeout = 0); - // write a single byte - bool Write(char c, uint32_t timeout = 0); - + // read data, requested amount, blocks 'timeout' milliseconds + // return number of bytes got + uint32_t Read(uint8_t *buf, uint32_t reqSize, uint32_t timeout = 0); + // read data, requested amount, blocks 'timeout' milliseconds // if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 - String Read(size_t reqSize = 0, uint32_t timeout = 0); + String Read(uint32_t reqSize = 0, uint32_t timeout = 0); + + // write a single byte + bool Write(char c, uint32_t timeout = 0); // writes data bool Write(uint8_t const *buf, uint32_t len, uint32_t timeout = 0); diff --git a/bazaar/Serial/SerialLinux.cpp b/bazaar/Serial/SerialLinux.cpp index d0f771acd..0d726d5b3 100644 --- a/bazaar/Serial/SerialLinux.cpp +++ b/bazaar/Serial/SerialLinux.cpp @@ -8,536 +8,558 @@ #include #include -NAMESPACE_UPP - -static bool __IsSymLink(const char *path) +namespace Upp { - struct stat stf; - lstat(path, &stf); - return S_ISLNK(stf.st_mode); -} - -dword Serial::stdBauds[] = -{ - 0, 50, 75, 110, 134, 150, - 200, 300, 600, 1200, 1800, - 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 345600, 460800 -}; - -int Serial::stdBaudsCount = sizeof(Serial::stdBauds) / sizeof(dword); - -// constructor -Serial::Serial() -{ - isError = false; - errCode = Ok; - fd = -1; -} - -Serial::Serial(String const &port, dword speed, byte parity, byte bits, byte stopBits) -{ - isError = false; - errCode = Ok; - fd = -1; - Open(port, speed, parity, bits, stopBits); -} - -// destructor -Serial::~Serial() -{ - Close(); -} - -// open the port -bool Serial::Open(String const &port, dword lSpeed, byte parity, byte bits, byte stopBits) -{ - static int stdBaudCodes[] = + static bool __IsSymLink(const char *path) { - B0, B50, B75, B110, B134, B150, - B200, B300, B600, B1200, B1800, - B2400, B4800, B9600, B19200, - B38400, B57600, B115200, B230400 + struct stat stf; + lstat(path, &stf); + return S_ISLNK(stf.st_mode); + } + + + dword Serial::stdBauds[] = + { + 0, 50, 75, 110, 134, 150, + 200, 300, 600, 1200, 1800, + 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 345600, 460800 }; - // open the device - fd = open(port, O_RDWR | O_NOCTTY /*| O_SYNC*/ | O_NDELAY); - if (fd < 0) + int Serial::stdBaudsCount = sizeof(Serial::stdBauds) / sizeof(dword); + + // constructor + Serial::Serial() { + isError = false; + errCode = Ok; fd = -1; - isError = true; - errCode = DeviceError; - return false; } - // sets interface parameters - struct termios tty; - memset(&tty, 0, sizeof tty); - if (tcgetattr(fd, &tty) != 0) + Serial::Serial(String const &port, dword speed, byte parity, byte bits, byte stopBits) + { + isError = false; + errCode = Ok; + fd = -1; + Open(port, speed, parity, bits, stopBits); + } + + // destructor + Serial::~Serial() { Close(); - isError = true; - errCode = DeviceError; - return false; } - //// CFLAG ///// + // open the port + bool Serial::Open(String const &port, dword lSpeed, byte parity, byte bits, byte stopBits) + { + static int stdBaudCodes[] = + { + B0, B50, B75, B110, B134, B150, + B200, B300, B600, B1200, B1800, + B2400, B4800, B9600, B19200, + B38400, B57600, B115200, B230400 + }; - int idx = GetStandardBaudRates().Find(lSpeed); - dword speed; - if (idx < 0) - { - // custom speed - // @@ maybe add support later - Close(); - isError = true; - errCode = InvalidSpeed; - return false; - } - else - { - // standard speed - speed = stdBaudCodes[idx]; - cfsetospeed(&tty, speed); - cfsetispeed(&tty, speed); - } + // open the device + fd = open(port, O_RDWR | O_NOCTTY /*| O_SYNC*/ | O_NDELAY); + if (fd < 0) + { + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } - // parity - tty.c_cflag &= ~(PARENB | PARODD); - switch (parity) - { - case ParityNone: - break; - case ParityEven: - tty.c_cflag |= PARENB; - break; - case ParityOdd: - tty.c_cflag |= PARENB | PARODD; - break; - case ParityMark: - tty.c_cflag |= PARENB | PARODD | CMSPAR; - break; - case ParitySpace: - tty.c_cflag |= PARENB | CMSPAR; - break; - default: + // sets interface parameters + struct termios tty; + memset(&tty, 0, sizeof tty); + if (tcgetattr(fd, &tty) != 0) + { Close(); isError = true; - errCode = InvalidParity; + errCode = DeviceError; return false; - } + } - // set data size - switch (bits) - { - case 5: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS5; - break; - case 6: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS6; - break; - case 7: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS7; - break; - case 8: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; - break; - default: + //// CFLAG ///// + + int idx = GetStandardBaudRates().Find(lSpeed); + dword speed; + if (idx < 0) + { + // custom speed + // @@ maybe add support later Close(); isError = true; - errCode = InvalidSize; + errCode = InvalidSpeed; return false; - } - - // stop bits - if (stopBits == 1) - tty.c_cflag &= ~CSTOPB; - else if (stopBits == 2) - tty.c_cflag |= CSTOPB; - else - { - Close(); - isError = true; - errCode = InvalidStopBits; - return false; - } - - // ignore modem controls, enable reading - tty.c_cflag |= (CLOCAL | CREAD); - - // no rts/cts - tty.c_cflag &= ~CRTSCTS; - - //// LFLAG ///// - - // no signaling chars, no echo, no canonical mode - tty.c_lflag = 0; -// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - - //// IFLAG ///// - - // disable IGNBRK for mismatched speed tests; otherwise receive break as \000 chars - // disable break processing - tty.c_iflag &= ~IGNBRK; -// tty.c_iflag |= IGNBRK; - - // DO NOT ignore carriage returns - tty.c_iflag &= ~IGNCR; - - // DO NOT translate cr/lf - tty.c_iflag &= ~ICRNL; - - // DO NOT translate cr - tty.c_iflag &= ~INLCR; - - // shut off xon/xoff ctrl - tty.c_iflag &= ~(IXON | IXOFF | IXANY); - - //// OFLAG ///// - - // no remapping, no delays - tty.c_oflag = 0; - - //// CONTROL CHARACTERS ///// - - // read doesn't block - tty.c_cc[VMIN] = 0; - - // 0.5 seconds read timeout - tty.c_cc[VTIME] = 0; - - /* - RLOG("CFLAGS : " << tty.c_cflag); - RLOG("LFLAGS : " << tty.c_lflag); - RLOG("IFLAGS : " << tty.c_iflag); - RLOG("OFLAGS : " << tty.c_oflag); - */ - - if (tcsetattr(fd, TCSANOW, &tty) != 0) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - return true; -} - -// close the port -void Serial::Close(void) -{ - if (fd != -1) - close(fd); - fd = -1; - isError = false; - errCode = Ok; -} - -// control DTR and RTS lines -bool Serial::SetDTR(bool on) -{ - unsigned int ctl; - int r; - - r = ioctl(fd, TIOCMGET, &ctl); - if (r < 0) - return false; - - if (on) - ctl |= TIOCM_DTR; - else - ctl &= ~TIOCM_DTR; - - r = ioctl(fd, TIOCMSET, &ctl); - if (r < 0) - return false; - - return true; -} - -bool Serial::SetRTS(bool on) -{ - unsigned int ctl; - int r; - - r = ioctl(fd, TIOCMGET, &ctl); - if (r < 0) - return false; - - if (on) - ctl |= TIOCM_RTS; - else - ctl &= ~TIOCM_RTS; - - r = ioctl(fd, TIOCMSET, &ctl); - if (r < 0) - return false; - - return true; -} - -// flush data -bool Serial::FlushInput(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCIFLUSH); -} - -bool Serial::FlushOutput(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCOFLUSH); -} - -bool Serial::FlushAll(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCIOFLUSH); -} - -// check if data is available on serial port -int Serial::Avail(void) -{ - if (fd == -1) - return false; - int bytes = 0; - ioctl(fd, FIONREAD, &bytes); - return bytes; -} - -// read a single byte, block 'timeout' milliseconds -bool Serial::Read(byte &c, uint32_t timeout) -{ - char buf[1]; - - uint32_t tim = msecs() + timeout; - for (;;) - { - if (!read(fd, buf, 1)) - { - if ((uint32_t)msecs() > tim) - return false; - continue; } - c = (byte)buf[0]; + else + { + // standard speed + speed = stdBaudCodes[idx]; + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); + } + + // parity + tty.c_cflag &= ~(PARENB | PARODD); + switch (parity) + { + case ParityNone: + break; + case ParityEven: + tty.c_cflag |= PARENB; + break; + case ParityOdd: + tty.c_cflag |= PARENB | PARODD; + break; + case ParityMark: + tty.c_cflag |= PARENB | PARODD | CMSPAR; + break; + case ParitySpace: + tty.c_cflag |= PARENB | CMSPAR; + break; + default: + Close(); + isError = true; + errCode = InvalidParity; + return false; + } + + // set data size + switch (bits) + { + case 5: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS5; + break; + case 6: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS6; + break; + case 7: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS7; + break; + case 8: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + break; + default: + Close(); + isError = true; + errCode = InvalidSize; + return false; + } + + // stop bits + if (stopBits == 1) + tty.c_cflag &= ~CSTOPB; + else if (stopBits == 2) + tty.c_cflag |= CSTOPB; + else + { + Close(); + isError = true; + errCode = InvalidStopBits; + return false; + } + + // ignore modem controls, enable reading + tty.c_cflag |= (CLOCAL | CREAD); + + // no rts/cts + tty.c_cflag &= ~CRTSCTS; + + //// LFLAG ///// + + // no signaling chars, no echo, no canonical mode + tty.c_lflag = 0; +// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + //// IFLAG ///// + + // disable IGNBRK for mismatched speed tests; otherwise receive break as \000 chars + // disable break processing + tty.c_iflag &= ~IGNBRK; +// tty.c_iflag |= IGNBRK; + + // DO NOT ignore carriage returns + tty.c_iflag &= ~IGNCR; + + // DO NOT translate cr/lf + tty.c_iflag &= ~ICRNL; + + // DO NOT translate cr + tty.c_iflag &= ~INLCR; + + // shut off xon/xoff ctrl + tty.c_iflag &= ~(IXON | IXOFF | IXANY); + + //// OFLAG ///// + + // no remapping, no delays + tty.c_oflag = 0; + + //// CONTROL CHARACTERS ///// + + // read doesn't block + tty.c_cc[VMIN] = 0; + + // 0.5 seconds read timeout + tty.c_cc[VTIME] = 0; + + /* + RLOG("CFLAGS : " << tty.c_cflag); + RLOG("LFLAGS : " << tty.c_lflag); + RLOG("IFLAGS : " << tty.c_iflag); + RLOG("OFLAGS : " << tty.c_oflag); + */ + + if (tcsetattr(fd, TCSANOW, &tty) != 0) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + return true; } -} -bool Serial::Read(char &c, uint32_t timeout) -{ - char buf[1]; - - uint32_t tim = msecs() + timeout; - for (;;) + // close the port + void Serial::Close(void) { - if (!read(fd, buf, 1)) - { - if ((uint32_t)msecs() > tim) - return false; - continue; - } - c = buf[0]; + if (fd != -1) + close(fd); + fd = -1; + isError = false; + errCode = Ok; + } + + // control DTR and RTS lines + bool Serial::SetDTR(bool on) + { + unsigned int ctl; + int r; + + r = ioctl(fd, TIOCMGET, &ctl); + if (r < 0) + return false; + + if (on) + ctl |= TIOCM_DTR; + else + ctl &= ~TIOCM_DTR; + + r = ioctl(fd, TIOCMSET, &ctl); + if (r < 0) + return false; + return true; } -} -// write a single byte -bool Serial::Write(char c, uint32_t timeout) -{ - if (!timeout) - return write(fd, &c, 1); - - uint32_t tim = msecs() + timeout; - for (;;) + bool Serial::SetRTS(bool on) { - if (write(fd, &c, 1) == 1) - return true; - if ((uint32_t)msecs() > tim) - break; + unsigned int ctl; + int r; + + r = ioctl(fd, TIOCMGET, &ctl); + if (r < 0) + return false; + + if (on) + ctl |= TIOCM_RTS; + else + ctl &= ~TIOCM_RTS; + + r = ioctl(fd, TIOCMSET, &ctl); + if (r < 0) + return false; + + return true; } - return false; -} - -// read data, requested amount, blocks 'timeout' milliseconds -// if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 -String Serial::Read(size_t reqSize, uint32_t timeout) -{ - char buf[1001]; - buf[1000] = 0; - String data; - - size_t n; - uint32_t tim = msecs() + timeout; - if (reqSize) + // flush data + bool Serial::FlushInput(void) { - n = min(reqSize, (size_t)1000); - while (reqSize) - { - n = read(fd, buf, n); - if (!n) - { - if ((uint32_t)msecs() > tim) - break; - n = min(reqSize, (size_t)1000); - continue; - } - tim = msecs() + timeout; - if (n) - { - reqSize -= n; - data.Cat(buf, n); - } - n = min(reqSize, (size_t)1000); - } + if (fd == -1) + return false; + return !tcflush(fd, TCIFLUSH); } - else + + bool Serial::FlushOutput(void) { + if (fd == -1) + return false; + return !tcflush(fd, TCOFLUSH); + } + + bool Serial::FlushAll(void) + { + if (fd == -1) + return false; + return !tcflush(fd, TCIOFLUSH); + } + + // check if data is available on serial port + int Serial::Avail(void) + { + if (fd == -1) + return false; + int bytes = 0; + ioctl(fd, FIONREAD, &bytes); + return bytes; + } + + // read a single byte, block 'timeout' milliseconds + bool Serial::Read(byte &c, uint32_t timeout) + { + char buf[1]; + + uint32_t tim = msecs() + timeout; for (;;) { - n = read(fd, buf, 1000); - if (!n) + if (!read(fd, buf, 1)) { if ((uint32_t)msecs() > tim) - break; + return false; continue; } - tim = msecs() + timeout; - if (n) - data.Cat(buf, n); - } - } - return data; -} - -// write buffer -bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) -{ - if (!timeout) - return (write(fd, buf, len) == len); - - uint32_t tim = msecs() + timeout; - const uint8_t *dPos = buf; - for (;;) - { - uint32_t written = write(fd, dPos, len); - if (written == len) + c = (byte)buf[0]; return true; - if (written >= 0) - { - dPos += written; - len -= written; - continue; } - if ((uint32_t)msecs() >= tim) - break; } - return false; -} -// writes string -bool Serial::Write(String const &data, uint32_t timeout) -{ - return Write((const uint8_t *)~data, data.GetCount(), timeout); -} - -// check if opened -bool Serial::IsOpened(void) const -{ - return fd != -1; -} - -// get a list of connected serial ports -// for old-style serials, check if something is connected -// for usb ones, return also a description -ArrayMap Serial::GetSerialPorts(void) -{ - const char *sysClassTTY = "/sys/class/tty/"; - - ArrayMap res; - - // first, scan all tty devices and dereference symlinks - FindFile ff(AppendFileName(sysClassTTY, "*")); - - while (ff) + bool Serial::Read(char &c, uint32_t timeout) { - if (ff.IsSymLink()) + char buf[1]; + + uint32_t tim = msecs() + timeout; + for (;;) { - // it's a symlink - // get the device name - String devName = ff.GetName(); - - // build the full path of it - String serial = AppendFileName(sysClassTTY, devName); - - // look for its driver, skip if none - String devPath = AppendFileName(serial, "device"); - - // it must be a symlink.... - - if (__IsSymLink(devPath)) + if (!read(fd, buf, 1)) { - // get the driver path - String drvLink = AppendFileName(devPath, "driver"); + if ((uint32_t)msecs() > tim) + return false; + continue; + } + c = buf[0]; + return true; + } + } - // it must be a symlink too... + // read data, requested amount, blocks 'timeout' milliseconds + // return number of bytes got + uint32_t Serial::Read(uint8_t *buf, uint32_t reqSize, uint32_t timeout) + { + if (!reqSize || !buf) + return 0; - if (__IsSymLink(drvLink)) + uint32_t n; + uint32_t tim = msecs() + timeout; + uint32_t req = reqSize; + while (req) + { + n = read(fd, buf, req); + req -= n; + buf += n; + if (!req || msecs() > (int)tim) + break; + } + return reqSize - req; + } + + // read data, requested amount, blocks 'timeout' milliseconds + // if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 + String Serial::Read(uint32_t reqSize, uint32_t timeout) + { + char buf[1001]; + buf[1000] = 0; + String data; + + size_t n; + uint32_t tim = msecs() + timeout; + if (reqSize) + { + n = min(reqSize, (uint32_t)1000); + while (reqSize) + { + n = read(fd, buf, n); + if (!n) { - // get link target - String drvName = GetFileName(GetSymLinkPath(drvLink)); + if ((uint32_t)msecs() > tim) + break; + n = min(reqSize, (uint32_t)1000); + continue; + } + tim = msecs() + timeout; + if (n) + { + reqSize -= n; + data.Cat(buf, n); + } + n = min(reqSize, (uint32_t)1000); + } + } + else + { + for (;;) + { + n = read(fd, buf, 1000); + if (!n) + { + if ((uint32_t)msecs() > tim) + break; + continue; + } + tim = msecs() + timeout; + if (n) + data.Cat(buf, n); + } + } + return data; + } - if (!drvName.IsEmpty()) + // write a single byte + bool Serial::Write(char c, uint32_t timeout) + { + if (!timeout) + return write(fd, &c, 1); + + uint32_t tim = msecs() + timeout; + for (;;) + { + if (write(fd, &c, 1) == 1) + return true; + if ((uint32_t)msecs() > tim) + break; + } + + return false; + } + + // write buffer + bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) + { + if (!timeout) + return (write(fd, buf, len) == len); + + uint32_t tim = msecs() + timeout; + const uint8_t *dPos = buf; + for (;;) + { + uint32_t written = write(fd, dPos, len); + if (written == len) + return true; + if (written >= 0) + { + dPos += written; + len -= written; + continue; + } + if ((uint32_t)msecs() >= tim) + break; + } + return false; + } + + // writes string + bool Serial::Write(String const &data, uint32_t timeout) + { + return Write((const uint8_t *)~data, data.GetCount(), timeout); + } + + // check if opened + bool Serial::IsOpened(void) const + { + return fd != -1; + } + + // get a list of connected serial ports + // for old-style serials, check if something is connected + // for usb ones, return also a description + ArrayMap Serial::GetSerialPorts(void) + { + const char *sysClassTTY = "/sys/class/tty/"; + + ArrayMap res; + + // first, scan all tty devices and dereference symlinks + FindFile ff(AppendFileName(sysClassTTY, "*")); + + while (ff) + { + if (ff.IsSymLink()) + { + // it's a symlink + // get the device name + String devName = ff.GetName(); + + // build the full path of it + String serial = AppendFileName(sysClassTTY, devName); + + // look for its driver, skip if none + String devPath = AppendFileName(serial, "device"); + + // it must be a symlink.... + + if (__IsSymLink(devPath)) + { + // get the driver path + String drvLink = AppendFileName(devPath, "driver"); + + // it must be a symlink too... + + if (__IsSymLink(drvLink)) { - // build the full device name - devName = AppendFileName("/dev", devName); + // get link target + String drvName = GetFileName(GetSymLinkPath(drvLink)); - // now if driver is 'serial8250' we check if - // the port responds - - if (drvName == "serial8250") + if (!drvName.IsEmpty()) { - // Try to open the device - int fd = open(devName, O_RDWR | O_NONBLOCK | O_NOCTTY); + // build the full device name + devName = AppendFileName("/dev", devName); - if (fd >= 0) + // now if driver is 'serial8250' we check if + // the port responds + + if (drvName == "serial8250") { - // Get serial_info + // Try to open the device + int fd = open(devName, O_RDWR | O_NONBLOCK | O_NOCTTY); - struct serial_struct serinfo; - - if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) + if (fd >= 0) { - // If device type is no PORT_UNKNOWN we accept the port - if (serinfo.type != PORT_UNKNOWN) - res.Add(devName, ""); + // Get serial_info + + struct serial_struct serinfo; + + if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) + { + // If device type is no PORT_UNKNOWN we accept the port + if (serinfo.type != PORT_UNKNOWN) + res.Add(devName, ""); + } + + close(fd); } - - close(fd); } + + // otherwise, just add the device to result + + else + res.Add(devName, drvName); } - - // otherwise, just add the device to result - - else - res.Add(devName, drvName); } } } + + ff.Next(); } - ff.Next(); + return res; } - return res; -} - -END_UPP_NAMESPACE +} // END_UPP_NAMESPACE #endif diff --git a/bazaar/Serial/SerialOSX.cpp b/bazaar/Serial/SerialOSX.cpp index 7a6805fd1..28384c955 100644 --- a/bazaar/Serial/SerialOSX.cpp +++ b/bazaar/Serial/SerialOSX.cpp @@ -20,534 +20,556 @@ #include #include -NAMESPACE_UPP - -static bool __IsSymLink(const char *path) +namespace Upp { - struct stat stf; - lstat(path, &stf); - return S_ISLNK(stf.st_mode); -} -dword Serial::stdBauds[] = -{ - 0, 50, 75, 110, 134, 150, - 200, 300, 600, 1200, 1800, - 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 345600, 460800 -}; - -int Serial::stdBaudsCount = sizeof(Serial::stdBauds) / sizeof(dword); - -// constructor -Serial::Serial() -{ - isError = false; - errCode = Ok; - fd = -1; -} - -Serial::Serial(String const &port, dword speed, byte parity, byte bits, byte stopBits) -{ - isError = false; - errCode = Ok; - fd = -1; - Open(port, speed, parity, bits, stopBits); -} - -// destructor -Serial::~Serial() -{ - Close(); -} - -// open the port -bool Serial::Open(String const &port, dword lSpeed, byte parity, byte bits, byte stopBits) -{ - static int stdBaudCodes[] = + static bool __IsSymLink(const char *path) { - B0, B50, B75, B110, B134, B150, - B200, B300, B600, B1200, B1800, - B2400, B4800, B9600, B19200, - B38400, B57600, B115200, B230400 + struct stat stf; + lstat(path, &stf); + return S_ISLNK(stf.st_mode); + } + + dword Serial::stdBauds[] = + { + 0, 50, 75, 110, 134, 150, + 200, 300, 600, 1200, 1800, + 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 345600, 460800 }; - fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK); - if (fd < 0) + int Serial::stdBaudsCount = sizeof(Serial::stdBauds) / sizeof(dword); + + // constructor + Serial::Serial() { + isError = false; + errCode = Ok; fd = -1; - isError = true; - errCode = DeviceError; - return false; - } - if (ioctl(fd, TIOCEXCL) == -1) - { - // unable to get exclusive access to port - close(fd); - fd = -1; - isError = true; - errCode = DeviceError; - return false; } - unsigned int ctl; - if (ioctl(fd, TIOCMGET, &ctl) < 0) + Serial::Serial(String const &port, dword speed, byte parity, byte bits, byte stopBits) { - // unable to query serial ports signals - close(fd); + isError = false; + errCode = Ok; fd = -1; - isError = true; - errCode = DeviceError; - return false; - } - ctl &= ~(TIOCM_DTR | TIOCM_RTS); - if (ioctl(fd, TIOCMSET, &ctl) < 0) - { - // unable to control serial ports signals - close(fd); - fd = -1; - isError = true; - errCode = DeviceError; - return false; + Open(port, speed, parity, bits, stopBits); } - // sets interface parameters - struct termios tty; - memset(&tty, 0, sizeof tty); - if (tcgetattr(fd, &tty) != 0) + // destructor + Serial::~Serial() { Close(); - isError = true; - errCode = DeviceError; - return false; } - //// CFLAG ///// + // open the port + bool Serial::Open(String const &port, dword lSpeed, byte parity, byte bits, byte stopBits) + { + static int stdBaudCodes[] = + { + B0, B50, B75, B110, B134, B150, + B200, B300, B600, B1200, B1800, + B2400, B4800, B9600, B19200, + B38400, B57600, B115200, B230400 + }; - int idx = GetStandardBaudRates().Find(lSpeed); - dword speed; - if (idx < 0) - { - // custom speed - // @@ maybe add support later - Close(); - isError = true; - errCode = InvalidSpeed; - return false; - } - else - { - // standard speed - speed = stdBaudCodes[idx]; - cfsetospeed(&tty, speed); - cfsetispeed(&tty, speed); - } + fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) + { + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } + if (ioctl(fd, TIOCEXCL) == -1) + { + // unable to get exclusive access to port + close(fd); + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } - // parity - tty.c_cflag &= ~(PARENB | PARODD); - switch (parity) - { - case ParityNone: - break; - case ParityEven: - tty.c_cflag |= PARENB; - break; - case ParityOdd: - tty.c_cflag |= PARENB | PARODD; - break; - default: + unsigned int ctl; + if (ioctl(fd, TIOCMGET, &ctl) < 0) + { + // unable to query serial ports signals + close(fd); + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } + ctl &= ~(TIOCM_DTR | TIOCM_RTS); + if (ioctl(fd, TIOCMSET, &ctl) < 0) + { + // unable to control serial ports signals + close(fd); + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } + + // sets interface parameters + struct termios tty; + memset(&tty, 0, sizeof tty); + if (tcgetattr(fd, &tty) != 0) + { Close(); isError = true; - errCode = InvalidParity; + errCode = DeviceError; return false; - } + } - // set data size - switch (bits) - { - case 5: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS5; - break; - case 6: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS6; - break; - case 7: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS7; - break; - case 8: - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; - break; - default: + //// CFLAG ///// + + int idx = GetStandardBaudRates().Find(lSpeed); + dword speed; + if (idx < 0) + { + // custom speed + // @@ maybe add support later Close(); isError = true; - errCode = InvalidSize; + errCode = InvalidSpeed; return false; - } - - // stop bits - if (stopBits == 1) - tty.c_cflag &= ~CSTOPB; - else if (stopBits == 2) - tty.c_cflag |= CSTOPB; - else - { - Close(); - isError = true; - errCode = InvalidStopBits; - return false; - } - - // ignore modem controls, enable reading - tty.c_cflag |= (CLOCAL | CREAD); - - // no rts/cts - tty.c_cflag &= ~CRTSCTS; - - //// LFLAG ///// - - // no signaling chars, no echo, no canonical mode - tty.c_lflag = 0; -// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - - //// IFLAG ///// - - // disable IGNBRK for mismatched speed tests; otherwise receive break as \000 chars - // disable break processing - tty.c_iflag &= ~IGNBRK; -// tty.c_iflag |= IGNBRK; - - // DO NOT ignore carriage returns - tty.c_iflag &= ~IGNCR; - - // DO NOT translate cr/lf - tty.c_iflag &= ~ICRNL; - - // DO NOT translate cr - tty.c_iflag &= ~INLCR; - - // shut off xon/xoff ctrl - tty.c_iflag &= ~(IXON | IXOFF | IXANY); - - //// OFLAG ///// - - // no remapping, no delays - tty.c_oflag = 0; - - //// CONTROL CHARACTERS ///// - - // read doesn't block - tty.c_cc[VMIN] = 0; - - // 0.5 seconds read timeout - tty.c_cc[VTIME] = 0; - - /* - RLOG("CFLAGS : " << tty.c_cflag); - RLOG("LFLAGS : " << tty.c_lflag); - RLOG("IFLAGS : " << tty.c_iflag); - RLOG("OFLAGS : " << tty.c_oflag); - */ - - if (tcsetattr(fd, TCSANOW, &tty) != 0) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - return true; -} - -// close the port -void Serial::Close(void) -{ - if (fd != -1) - close(fd); - fd = -1; - isError = false; - errCode = Ok; -} - -// control DTR and RTS lines -bool Serial::SetDTR(bool on) -{ - unsigned int ctl; - int r; - - r = ioctl(fd, TIOCMGET, &ctl); - if (r < 0) - return false; - - if (on) - ctl |= TIOCM_DTR; - else - ctl &= ~TIOCM_DTR; - - r = ioctl(fd, TIOCMSET, &ctl); - if (r < 0) - return false; - - return true; -} - -bool Serial::SetRTS(bool on) -{ - unsigned int ctl; - int r; - - r = ioctl(fd, TIOCMGET, &ctl); - if (r < 0) - return false; - - if (on) - ctl |= TIOCM_RTS; - else - ctl &= ~TIOCM_RTS; - - r = ioctl(fd, TIOCMSET, &ctl); - if (r < 0) - return false; - - return true; -} - -// flush data -bool Serial::FlushInput(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCIFLUSH); -} - -bool Serial::FlushOutput(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCOFLUSH); -} - -bool Serial::FlushAll(void) -{ - if (fd == -1) - return false; - return !tcflush(fd, TCIOFLUSH); -} - -// check if data is available on serial port -int Serial::Avail(void) -{ - if (fd == -1) - return false; - int bytes = 0; - ioctl(fd, FIONREAD, &bytes); - return bytes; -} - -// read a single byte, block 'timeout' milliseconds -bool Serial::Read(byte &c, uint32_t timeout) -{ - char buf[1]; - - uint32_t tim = msecs() + timeout; - for (;;) - { - if (!read(fd, buf, 1)) - { - if ((uint32_t)msecs() > tim) - return false; - continue; } - c = (byte)buf[0]; + else + { + // standard speed + speed = stdBaudCodes[idx]; + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); + } + + // parity + tty.c_cflag &= ~(PARENB | PARODD); + switch (parity) + { + case ParityNone: + break; + case ParityEven: + tty.c_cflag |= PARENB; + break; + case ParityOdd: + tty.c_cflag |= PARENB | PARODD; + break; + default: + Close(); + isError = true; + errCode = InvalidParity; + return false; + } + + // set data size + switch (bits) + { + case 5: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS5; + break; + case 6: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS6; + break; + case 7: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS7; + break; + case 8: + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + break; + default: + Close(); + isError = true; + errCode = InvalidSize; + return false; + } + + // stop bits + if (stopBits == 1) + tty.c_cflag &= ~CSTOPB; + else if (stopBits == 2) + tty.c_cflag |= CSTOPB; + else + { + Close(); + isError = true; + errCode = InvalidStopBits; + return false; + } + + // ignore modem controls, enable reading + tty.c_cflag |= (CLOCAL | CREAD); + + // no rts/cts + tty.c_cflag &= ~CRTSCTS; + + //// LFLAG ///// + + // no signaling chars, no echo, no canonical mode + tty.c_lflag = 0; +// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + //// IFLAG ///// + + // disable IGNBRK for mismatched speed tests; otherwise receive break as \000 chars + // disable break processing + tty.c_iflag &= ~IGNBRK; +// tty.c_iflag |= IGNBRK; + + // DO NOT ignore carriage returns + tty.c_iflag &= ~IGNCR; + + // DO NOT translate cr/lf + tty.c_iflag &= ~ICRNL; + + // DO NOT translate cr + tty.c_iflag &= ~INLCR; + + // shut off xon/xoff ctrl + tty.c_iflag &= ~(IXON | IXOFF | IXANY); + + //// OFLAG ///// + + // no remapping, no delays + tty.c_oflag = 0; + + //// CONTROL CHARACTERS ///// + + // read doesn't block + tty.c_cc[VMIN] = 0; + + // 0.5 seconds read timeout + tty.c_cc[VTIME] = 0; + + /* + RLOG("CFLAGS : " << tty.c_cflag); + RLOG("LFLAGS : " << tty.c_lflag); + RLOG("IFLAGS : " << tty.c_iflag); + RLOG("OFLAGS : " << tty.c_oflag); + */ + + if (tcsetattr(fd, TCSANOW, &tty) != 0) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + return true; } -} -bool Serial::Read(char &c, uint32_t timeout) -{ - char buf[1]; - - uint32_t tim = msecs() + timeout; - for (;;) + // close the port + void Serial::Close(void) { - if (!read(fd, buf, 1)) - { - if ((uint32_t)msecs() > tim) - return false; - continue; - } - c = buf[0]; + if (fd != -1) + close(fd); + fd = -1; + isError = false; + errCode = Ok; + } + + // control DTR and RTS lines + bool Serial::SetDTR(bool on) + { + unsigned int ctl; + int r; + + r = ioctl(fd, TIOCMGET, &ctl); + if (r < 0) + return false; + + if (on) + ctl |= TIOCM_DTR; + else + ctl &= ~TIOCM_DTR; + + r = ioctl(fd, TIOCMSET, &ctl); + if (r < 0) + return false; + return true; } -} -// write a single byte -bool Serial::Write(char c, uint32_t timeout) -{ - if (!timeout) - return write(fd, &c, 1); - - uint32_t tim = msecs() + timeout; - for (;;) + bool Serial::SetRTS(bool on) { - if (write(fd, &c, 1) == 1) - return true; - if ((uint32_t)msecs() > tim) - break; + unsigned int ctl; + int r; + + r = ioctl(fd, TIOCMGET, &ctl); + if (r < 0) + return false; + + if (on) + ctl |= TIOCM_RTS; + else + ctl &= ~TIOCM_RTS; + + r = ioctl(fd, TIOCMSET, &ctl); + if (r < 0) + return false; + + return true; } - return false; -} - -// read data, requested amount, blocks 'timeout' milliseconds -// if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 -String Serial::Read(size_t reqSize, uint32_t timeout) -{ - char buf[1001]; - buf[1000] = 0; - String data; - - size_t n; - uint32_t tim = msecs() + timeout; - if (reqSize) + // flush data + bool Serial::FlushInput(void) { - n = min(reqSize, (size_t)1000); - while (reqSize) - { - n = read(fd, buf, n); - if (!n) - { - if ((uint32_t)msecs() > tim) - break; - n = min(reqSize, (size_t)1000); - continue; - } - tim = msecs() + timeout; - if (n) - { - reqSize -= n; - data.Cat(buf, n); - } - n = min(reqSize, (size_t)1000); - } + if (fd == -1) + return false; + return !tcflush(fd, TCIFLUSH); } - else + + bool Serial::FlushOutput(void) { + if (fd == -1) + return false; + return !tcflush(fd, TCOFLUSH); + } + + bool Serial::FlushAll(void) + { + if (fd == -1) + return false; + return !tcflush(fd, TCIOFLUSH); + } + + // check if data is available on serial port + int Serial::Avail(void) + { + if (fd == -1) + return false; + int bytes = 0; + ioctl(fd, FIONREAD, &bytes); + return bytes; + } + + // read a single byte, block 'timeout' milliseconds + bool Serial::Read(byte &c, uint32_t timeout) + { + char buf[1]; + + uint32_t tim = msecs() + timeout; for (;;) { - n = read(fd, buf, 1000); - if (!n) + if (!read(fd, buf, 1)) { if ((uint32_t)msecs() > tim) - break; + return false; continue; } - tim = msecs() + timeout; - if (n) - data.Cat(buf, n); - } - } - return data; -} - -// write buffer -bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) -{ - if (!timeout) - return (write(fd, buf, len) == len); - - uint32_t tim = msecs() + timeout; - const uint8_t *dPos = buf; - for (;;) - { - int written = write(fd, dPos, len); - if (written == len) + c = (byte)buf[0]; return true; - if (written >= 0) - { - dPos += written; - len -= written; - continue; } - if ((uint32_t)msecs() >= tim) - break; } - return false; -} -// writes string -bool Serial::Write(String const &data, uint32_t timeout) -{ - return Write((const uint8_t *)~data, data.GetCount(), timeout); -} - -// check if opened -bool Serial::IsOpened(void) const -{ - return fd != -1; -} - -static void macos_ports(io_iterator_t *PortIterator, ArrayMap &list) -{ - io_object_t modemService; - CFTypeRef nameCFstring; - char s[MAXPATHLEN]; - - while ((modemService = IOIteratorNext(*PortIterator))) + bool Serial::Read(char &c, uint32_t timeout) { - nameCFstring = IORegistryEntryCreateCFProperty(modemService, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); - if (nameCFstring) + char buf[1]; + + uint32_t tim = msecs() + timeout; + for (;;) { - if (CFStringGetCString((const __CFString *)nameCFstring, s, sizeof(s), kCFStringEncodingASCII)) - list.Add(s, ""); - - CFRelease(nameCFstring); + if (!read(fd, buf, 1)) + { + if ((uint32_t)msecs() > tim) + return false; + continue; + } + c = buf[0]; + return true; } - IOObjectRelease(modemService); } -} -// get a list of connected serial ports -// for old-style serials, check if something is connected -// for usb ones, return also a description -ArrayMap Serial::GetSerialPorts(void) -{ - ArrayMap res; - - mach_port_t masterPort; - CFMutableDictionaryRef classesToMatch; - io_iterator_t serialPortIterator; - - if (IOMasterPort(0, &masterPort) != KERN_SUCCESS) - return res; + // read data, requested amount, blocks 'timeout' milliseconds + // return number of bytes got + uint32_t Serial::Read(uint8_t *buf, uint32_t reqSize, uint32_t timeout) + { + if (!reqSize || !buf) + return 0; - // a usb-serial adaptor is usually considered a "modem", - // especially when it implements the CDC class spec - classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); - if (!classesToMatch) - return res; - - CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); - if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) - return res; - - macos_ports(&serialPortIterator, res); - IOObjectRelease(serialPortIterator); - - // but it might be considered a "rs232 port", so repeat this search for rs232 ports - classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); - if (!classesToMatch) - return res; - - CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); - if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) - return res; - macos_ports(&serialPortIterator, res); - IOObjectRelease(serialPortIterator); - - return res; -} + uint32_t n; + uint32_t tim = msecs() + timeout; + uint32_t req = reqSize; + while (req) + { + n = read(fd, buf, req); + req -= n; + buf += n; + if (!req || msecs() > (int)tim) + break; + } + return reqSize - req; + } -END_UPP_NAMESPACE + // read data, requested amount, blocks 'timeout' milliseconds + // if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 + String Serial::Read(uint32_t reqSize, uint32_t timeout) + { + char buf[1001]; + buf[1000] = 0; + String data; + + size_t n; + uint32_t tim = msecs() + timeout; + if (reqSize) + { + n = min(reqSize, (size_t)1000); + while (reqSize) + { + n = read(fd, buf, n); + if (!n) + { + if ((uint32_t)msecs() > tim) + break; + n = min(reqSize, (size_t)1000); + continue; + } + tim = msecs() + timeout; + if (n) + { + reqSize -= n; + data.Cat(buf, n); + } + n = min(reqSize, (size_t)1000); + } + } + else + { + for (;;) + { + n = read(fd, buf, 1000); + if (!n) + { + if ((uint32_t)msecs() > tim) + break; + continue; + } + tim = msecs() + timeout; + if (n) + data.Cat(buf, n); + } + } + return data; + } + + // write a single byte + bool Serial::Write(char c, uint32_t timeout) + { + if (!timeout) + return write(fd, &c, 1); + + uint32_t tim = msecs() + timeout; + for (;;) + { + if (write(fd, &c, 1) == 1) + return true; + if ((uint32_t)msecs() > tim) + break; + } + + return false; + } + + // write buffer + bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) + { + if (!timeout) + return (write(fd, buf, len) == len); + + uint32_t tim = msecs() + timeout; + const uint8_t *dPos = buf; + for (;;) + { + int written = write(fd, dPos, len); + if (written == len) + return true; + if (written >= 0) + { + dPos += written; + len -= written; + continue; + } + if ((uint32_t)msecs() >= tim) + break; + } + return false; + } + + // writes string + bool Serial::Write(String const &data, uint32_t timeout) + { + return Write((const uint8_t *)~data, data.GetCount(), timeout); + } + + // check if opened + bool Serial::IsOpened(void) const + { + return fd != -1; + } + + static void macos_ports(io_iterator_t *PortIterator, ArrayMap &list) + { + io_object_t modemService; + CFTypeRef nameCFstring; + char s[MAXPATHLEN]; + + while ((modemService = IOIteratorNext(*PortIterator))) + { + nameCFstring = IORegistryEntryCreateCFProperty(modemService, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (nameCFstring) + { + if (CFStringGetCString((const __CFString *)nameCFstring, s, sizeof(s), kCFStringEncodingASCII)) + list.Add(s, ""); + + CFRelease(nameCFstring); + } + IOObjectRelease(modemService); + } + } + + // get a list of connected serial ports + // for old-style serials, check if something is connected + // for usb ones, return also a description + ArrayMap Serial::GetSerialPorts(void) + { + ArrayMap res; + + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + io_iterator_t serialPortIterator; + + if (IOMasterPort(0, &masterPort) != KERN_SUCCESS) + return res; + + // a usb-serial adaptor is usually considered a "modem", + // especially when it implements the CDC class spec + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) + return res; + + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) + return res; + + macos_ports(&serialPortIterator, res); + IOObjectRelease(serialPortIterator); + + // but it might be considered a "rs232 port", so repeat this search for rs232 ports + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) + return res; + + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator) != KERN_SUCCESS) + return res; + macos_ports(&serialPortIterator, res); + IOObjectRelease(serialPortIterator); + + return res; + } + +} // END_UPP_NAMESPACE #endif diff --git a/bazaar/Serial/SerialWindows.cpp b/bazaar/Serial/SerialWindows.cpp index fa4408d78..20efe7a19 100644 --- a/bazaar/Serial/SerialWindows.cpp +++ b/bazaar/Serial/SerialWindows.cpp @@ -9,864 +9,889 @@ typedef DWORD REGSAM; #include #endif -namespace Upp { - -dword Serial::stdBauds[] = +namespace Upp { - 50, 75, 110, 134, 150, 200, - 300, 600, 1200, 1800, 2400, - 4800, 9600, 19200, - 38400, 57600, 115200, 128000, 256000 -}; -int Serial::stdBaudsCount = sizeof ( Serial::stdBauds ) / sizeof ( dword ); - -// constructor -Serial::Serial() -{ - isError = false; - errCode = Ok; - fd = INVALID_HANDLE_VALUE; -} - -Serial::Serial ( String const &port, unsigned long speed, byte parity, byte bits, byte stopBits ) -{ - isError = false; - errCode = Ok; - fd = INVALID_HANDLE_VALUE; - Open ( port, speed, parity, bits, stopBits ); -} - -// destructor -Serial::~Serial() -{ - Close(); -} - -// open the port -bool Serial::Open ( String const &port, dword speed, byte parity, byte bits, byte stopBits ) -{ - // open the device - fd = CreateFile ( "\\\\.\\" + port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 ); - - if ( fd == INVALID_HANDLE_VALUE ) + dword Serial::stdBauds[] = { - isError = true; - errCode = DeviceError; - return false; + 50, 75, 110, 134, 150, 200, + 300, 600, 1200, 1800, 2400, + 4800, 9600, 19200, + 38400, 57600, 115200, 128000, 256000 + }; + + int Serial::stdBaudsCount = sizeof(Serial::stdBauds) / sizeof(dword); + + // constructor + Serial::Serial() + { + isError = false; + errCode = Ok; + fd = INVALID_HANDLE_VALUE; } - DCB dcb; + Serial::Serial(String const &port, unsigned long speed, byte parity, byte bits, byte stopBits) + { + isError = false; + errCode = Ok; + fd = INVALID_HANDLE_VALUE; + Open(port, speed, parity, bits, stopBits); + } - char dcbSz[50]; - COMMTIMEOUTS cmt; - - // clear dcb and set length - FillMemory ( &dcb, sizeof ( dcb ), 0 ); - dcb.DCBlength = sizeof ( dcb ); - - // check baud is valid - - if ( GetStandardBaudRates().Find ( speed ) < 0 ) + // destructor + Serial::~Serial() { Close(); - isError = true; - errCode = InvalidSpeed; - return false; } - // check parity */ - char cParity; - - switch ( parity ) + // open the port + bool Serial::Open(String const &port, dword speed, byte parity, byte bits, byte stopBits) { + // open the device + fd = CreateFile("\\\\.\\" + port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - case ParityNone : - cParity = 'n'; - break; + if (fd == INVALID_HANDLE_VALUE) + { + isError = true; + errCode = DeviceError; + return false; + } - case ParityEven : - cParity = 'e'; - break; + DCB dcb; - case ParityOdd : - cParity = 'o'; - break; + char dcbSz[50]; + COMMTIMEOUTS cmt; - case ParityMark : + // clear dcb and set length + FillMemory(&dcb, sizeof(dcb), 0); + dcb.DCBlength = sizeof(dcb); - case ParitySpace : + // check baud is valid - default: + if (GetStandardBaudRates().Find(speed) < 0) + { + Close(); + isError = true; + errCode = InvalidSpeed; + return false; + } + + // check parity */ + char cParity; + + switch (parity) + { + + case ParityNone : + cParity = 'n'; + break; + + case ParityEven : + cParity = 'e'; + break; + + case ParityOdd : + cParity = 'o'; + break; + + case ParityMark : + + case ParitySpace : + + default: + { + Close(); + isError = true; + errCode = InvalidParity; + return false; + } + } + + // check data bits + + if (bits < 5 || bits > 8) + { + Close(); + isError = true; + errCode = InvalidSize; + return false; + } + + // check stop bits + + if (stopBits < 1 || stopBits > 2) + { + Close(); + isError = true; + errCode = InvalidStopBits; + return false; + } + + // build dcb + sprintf_s(dcbSz, 49, "%d,%c,%d,%d", speed, cParity, bits, stopBits); + + if (!BuildCommDCB(dcbSz, &dcb)) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + + /* + dcb.BaudRate = speed; + if(stopBits == 1) + dcb.StopBits = ONESTOPBIT; + else + dcb.StopBits = TWOSTOPBITS; + switch(parity) + { + case ParityNone: + dcb.Parity = PARITY_NONE; + break; + case ParityEven: + dcb.Parity = PARITY_EVEN; + break; + case ParityOdd: + dcb.Parity = PARITY_ODD; + break; + default: + break; + } + switch(bits) + { + case 5: + dcb.ByteSize = DATABITS_5; + break; + case 7: + dcb.ByteSize = DATABITS_7; + break; + case 8: + dcb.ByteSize = DATABITS_8; + break; + default: + break; + } + dcb.fDtrControl = 0; + dcb.fRtsControl = 0; + */ + + + // set dcb to serial port */ + + if (!SetCommState(fd, &dcb)) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + + // set input and output bufffer sizes + + if (!SetupComm(fd, 1024, 1024)) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + + // set timeouts to 0 -- non blocking + cmt.ReadIntervalTimeout = MAXDWORD; + + cmt.ReadTotalTimeoutMultiplier = 0; + + cmt.ReadTotalTimeoutConstant = 0; + + cmt.WriteTotalTimeoutMultiplier = 0; + + cmt.WriteTotalTimeoutConstant = MAXDWORD; + + if (!SetCommTimeouts(fd, &cmt)) + { + Close(); + isError = true; + errCode = DeviceError; + return false; + } + + return true; + } + + // close the port + void Serial::Close(void) + { + if (fd == INVALID_HANDLE_VALUE) + return; + + // cancel pending i/o + CancelIo(fd); + + // close the device + CloseHandle(fd); + + fd = INVALID_HANDLE_VALUE; + } + + // control DTR and RTS lines + bool Serial::SetDTR(bool on) + { + if (on) + EscapeCommFunction(fd, SETDTR); + else + EscapeCommFunction(fd, CLRDTR); + + return true; + } + + bool Serial::SetRTS(bool on) + { + if (on) + EscapeCommFunction(fd, SETRTS); + else + EscapeCommFunction(fd, CLRRTS); + + return true; + } + + // flush data + bool Serial::FlushInput(void) + { + return PurgeComm(fd, PURGE_RXCLEAR); + } + + bool Serial::FlushOutput(void) + { + return PurgeComm(fd, PURGE_TXCLEAR); + } + + bool Serial::FlushAll(void) + { + return PurgeComm(fd, PURGE_RXCLEAR) & PurgeComm(fd, PURGE_TXCLEAR); + } + + + // check if data is available on serial port + int Serial::Avail(void) + { + dword errors; + COMSTAT stat; + + if (!ClearCommError(fd, &errors, &stat)) + return 0; + + return stat.cbInQue; + } + + + // read a single byte, block 'timeout' milliseconds + bool Serial::Read(byte &c, uint32_t timeout) + { + char buf[1]; + unsigned long count = 0; + + uint32_t tim = (uint32_t) msecs() + timeout; + + for (;;) + { + count = 0; + ReadFile(fd, buf, 1, &count, NULL); + + if (!count) { - Close(); - isError = true; - errCode = InvalidParity; - return false; + if ((uint32_t) msecs() > tim) + return false; + + continue; } + + c = (byte) buf[0]; + + return true; + } } - // check data bits - - if ( bits < 5 || bits > 8 ) + bool Serial::Read(char &c, uint32_t timeout) { - Close(); - isError = true; - errCode = InvalidSize; - return false; - } + char buf[1]; + unsigned long count = 0; - // check stop bits + uint32_t tim = (uint32_t) msecs() + timeout; - if ( stopBits < 1 || stopBits > 2 ) - { - Close(); - isError = true; - errCode = InvalidStopBits; - return false; - } - - // build dcb - sprintf_s ( dcbSz, 49, "%d,%c,%d,%d", speed, cParity, bits, stopBits ); - - if ( !BuildCommDCB ( dcbSz, &dcb ) ) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - /* - dcb.BaudRate = speed; - if(stopBits == 1) - dcb.StopBits = ONESTOPBIT; - else - dcb.StopBits = TWOSTOPBITS; - switch(parity) - { - case ParityNone: - dcb.Parity = PARITY_NONE; - break; - case ParityEven: - dcb.Parity = PARITY_EVEN; - break; - case ParityOdd: - dcb.Parity = PARITY_ODD; - break; - default: - break; - } - switch(bits) - { - case 5: - dcb.ByteSize = DATABITS_5; - break; - case 7: - dcb.ByteSize = DATABITS_7; - break; - case 8: - dcb.ByteSize = DATABITS_8; - break; - default: - break; - } - dcb.fDtrControl = 0; - dcb.fRtsControl = 0; - */ - - - // set dcb to serial port */ - - if ( !SetCommState ( fd, &dcb ) ) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - // set input and output bufffer sizes - - if ( !SetupComm ( fd, 1024, 1024 ) ) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - // set timeouts to 0 -- non blocking - cmt.ReadIntervalTimeout = MAXDWORD; - - cmt.ReadTotalTimeoutMultiplier = 0; - - cmt.ReadTotalTimeoutConstant = 0; - - cmt.WriteTotalTimeoutMultiplier = 0; - - cmt.WriteTotalTimeoutConstant = MAXDWORD; - - if ( !SetCommTimeouts ( fd, &cmt ) ) - { - Close(); - isError = true; - errCode = DeviceError; - return false; - } - - return true; -} - -// close the port -void Serial::Close ( void ) -{ - if ( fd == INVALID_HANDLE_VALUE ) - return; - - // cancel pending i/o - CancelIo ( fd ); - - // close the device - CloseHandle ( fd ); - - fd = INVALID_HANDLE_VALUE; -} - -// control DTR and RTS lines -bool Serial::SetDTR(bool on) -{ - if(on) - EscapeCommFunction(fd, SETDTR); - else - EscapeCommFunction(fd, CLRDTR); - - return true; -} - -bool Serial::SetRTS(bool on) -{ - if(on) - EscapeCommFunction(fd, SETRTS); - else - EscapeCommFunction(fd, CLRRTS); - - return true; -} - -// flush data -bool Serial::FlushInput ( void ) -{ - return PurgeComm ( fd, PURGE_RXCLEAR ); -} - -bool Serial::FlushOutput ( void ) -{ - return PurgeComm ( fd, PURGE_TXCLEAR ); -} - -bool Serial::FlushAll ( void ) -{ - return PurgeComm ( fd, PURGE_RXCLEAR ) & PurgeComm ( fd, PURGE_TXCLEAR ); -} - - -// check if data is available on serial port -int Serial::Avail(void) -{ - dword errors; - COMSTAT stat; - - if(!ClearCommError(fd, &errors, &stat)) - return 0; - - return stat.cbInQue; -} - - -// read a single byte, block 'timeout' milliseconds -bool Serial::Read ( byte &c, uint32_t timeout ) -{ - char buf[1]; - unsigned long count = 0; - - uint32_t tim = ( uint32_t ) msecs() + timeout; - - for ( ;; ) - { - count = 0; - ReadFile ( fd, buf, 1, &count, NULL ); - - if ( !count ) + for (;;) { - if ( ( uint32_t ) msecs() > tim ) - return false; + count = 0; + ReadFile(fd, buf, 1, &count, NULL); - continue; + if (!count) + { + if ((uint32_t) msecs() > tim) + return false; + + continue; + } + + c = buf[0]; + + return true; + } + } + + // read data, requested amount, blocks 'timeout' milliseconds + // return number of bytes got + uint32_t Serial::Read(uint8_t *buf, uint32_t reqSize, uint32_t timeout) + { + if(!reqSize || !buf) + return 0; + + uint32_t n; + uint32_t tim = (uint32_t)msecs() + timeout; + uint32_t req = reqSize; + while(req) + { + count = 0; + ReadFile(fd, buf, req, &count, NULL); + + req -= count; + buf += count; + + if(!req || (uint32_t)msecs() > timeout) + break; + } + + return reqSize - req; + } + + // read data, requested amount, blocks 'timeout' milliseconds + // if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 + String Serial::Read(uint32_t reqSize, uint32_t timeout) + { + char buf[1001]; + String res; + + unsigned long count = 0; + unsigned long n; + uint32_t tim = (uint32_t) msecs() + timeout; + + if (reqSize) + { + n = min(reqSize, (size_t) 1000); + + while (reqSize) + { + count = 0; + ReadFile(fd, buf, n, &count, NULL); + + if (!count) + { + if ((uint32_t) msecs() > tim) + break; + + continue; + } + + tim = (uint32_t) msecs() + timeout; + + if (count) + { + reqSize -= count; + res.Cat(buf, count); + } + + n = min(reqSize, (size_t) 1000); + } } - c = ( byte ) buf[0]; - - return true; - } -} - -bool Serial::Read ( char &c, uint32_t timeout ) -{ - char buf[1]; - unsigned long count = 0; - - uint32_t tim = ( uint32_t ) msecs() + timeout; - - for ( ;; ) - { - count = 0; - ReadFile ( fd, buf, 1, &count, NULL ); - - if ( !count ) + else { - if ( ( uint32_t ) msecs() > tim ) - return false; + for (;;) + { + count = 0; + ReadFile(fd, buf, 1000, &count, NULL); - continue; + if (!count) + { + if ((uint32_t) msecs() > tim) + break; + + continue; + } + + tim = (uint32_t) msecs() + timeout; + + if (count) + res.Cat(buf, count); + } } - c = buf[0]; - - return true; - } -} - -// write a single byte -bool Serial::Write ( char c, uint32_t timeout ) -{ - unsigned long count = 0; - - if ( !timeout ) - { - WriteFile ( fd, &c, 1, &count, NULL ); - return count == 1; + return res; } - else + // write a single byte + bool Serial::Write(char c, uint32_t timeout) { + unsigned long count = 0; + + if (!timeout) + { + WriteFile(fd, &c, 1, &count, NULL); + return count == 1; + } + + else + { + uint32_t tim = msecs() + timeout; + + for (;;) + { + WriteFile(fd, &c, 1, &count, NULL); + if (count == 1) + return true; + + if ((uint32_t) msecs() > tim) + break; + } + + return false; + } + } + + // write buffer + bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) + { + unsigned long count = 0; + + if (!timeout) + { + WriteFile(fd, buf, len, &count, NULL); + return count == len; + } + uint32_t tim = msecs() + timeout; + const uint8_t *dPos = buf; - for ( ;; ) + for (;;) { - WriteFile ( fd, &c, 1, &count, NULL ); - if ( count == 1 ) + WriteFile(fd, dPos, len, &count, NULL); + if (count == len) return true; - if ( ( uint32_t ) msecs() > tim ) + if (count > 0) + { + dPos += count; + len -= count; + continue; + } + + if ((uint32_t)msecs() >= tim) break; } - return false; } -} - -// read data, requested amount, blocks 'timeout' milliseconds -// if reqSize == 0 just read all available data, waiting for 'timeout' if != 0 -String Serial::Read ( size_t reqSize, uint32_t timeout ) -{ - char buf[1001]; - String res; - - unsigned long count = 0; - unsigned long n; - uint32_t tim = ( uint32_t ) msecs() + timeout; - - if ( reqSize ) + // writes string + bool Serial::Write(String const &data, uint32_t timeout) { - n = min ( reqSize, ( size_t ) 1000 ); - - while ( reqSize ) - { - count = 0; - ReadFile ( fd, buf, n, &count, NULL ); - - if ( !count ) - { - if ( ( uint32_t ) msecs() > tim ) - break; - - continue; - } - - tim = ( uint32_t ) msecs() + timeout; - - if ( count ) - { - reqSize -= count; - res.Cat ( buf, count ); - } - - n = min ( reqSize, ( size_t ) 1000 ); - } + return Write((const uint8_t *)~data, data.GetCount(), timeout); } - else + // check if opened + bool Serial::IsOpened(void) const { - for ( ;; ) - { - count = 0; - ReadFile ( fd, buf, 1000, &count, NULL ); - - if ( !count ) - { - if ( ( uint32_t ) msecs() > tim ) - break; - - continue; - } - - tim = ( uint32_t ) msecs() + timeout; - - if ( count ) - res.Cat ( buf, count ); - } + return fd != INVALID_HANDLE_VALUE; } - return res; -} - -// write buffer -bool Serial::Write(uint8_t const *buf, uint32_t len, uint32_t timeout) -{ - unsigned long count = 0; - - if (!timeout) - { - WriteFile(fd, buf, len, &count, NULL); - return count == len; - } - - uint32_t tim = msecs() + timeout; - const uint8_t *dPos = buf; - - for (;;) - { - WriteFile(fd, dPos, len, &count, NULL); - if (count == len ) - return true; - - if(count > 0) - { - dPos += count; - len -= count; - continue; - } - - if((uint32_t)msecs() >= tim) - break; - } - return false; -} - -// writes string -bool Serial::Write(String const &data, uint32_t timeout) -{ - return Write((const uint8_t *)~data, data.GetCount(), timeout); -} - -// check if opened -bool Serial::IsOpened ( void ) const -{ - return fd != INVALID_HANDLE_VALUE; -} - /////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef USE_SETUPAPI // ENUMERATE PORTS USING SETUPAPI #ifndef GUID_DEVINTERFACE_COMPORT -DEFINE_GUID ( GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73 ); + DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73); #endif -typedef HKEY ( __stdcall SETUPDIOPENDEVREGKEY ) ( HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM ); -typedef BOOL ( __stdcall SETUPDICLASSGUIDSFROMNAME ) ( LPCTSTR, LPGUID, DWORD, PDWORD ); -typedef BOOL ( __stdcall SETUPDIDESTROYDEVICEINFOLIST ) ( HDEVINFO ); -typedef BOOL ( __stdcall SETUPDIENUMDEVICEINFO ) ( HDEVINFO, DWORD, PSP_DEVINFO_DATA ); -typedef HDEVINFO ( __stdcall SETUPDIGETCLASSDEVS ) ( LPGUID, LPCTSTR, HWND, DWORD ); -typedef BOOL ( __stdcall SETUPDIGETDEVICEREGISTRYPROPERTY ) ( HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD ); + typedef HKEY(__stdcall SETUPDIOPENDEVREGKEY)(HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM); + typedef BOOL (__stdcall SETUPDICLASSGUIDSFROMNAME)(LPCTSTR, LPGUID, DWORD, PDWORD); + typedef BOOL (__stdcall SETUPDIDESTROYDEVICEINFOLIST)(HDEVINFO); + typedef BOOL (__stdcall SETUPDIENUMDEVICEINFO)(HDEVINFO, DWORD, PSP_DEVINFO_DATA); + typedef HDEVINFO(__stdcall SETUPDIGETCLASSDEVS)(LPGUID, LPCTSTR, HWND, DWORD); + typedef BOOL (__stdcall SETUPDIGETDEVICEREGISTRYPROPERTY)(HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD); -class CAutoHModule -{ + class CAutoHModule + { - public: - CAutoHModule() : m_hModule ( NULL ), m_dwError ( ERROR_SUCCESS ) {} + public: + CAutoHModule() : m_hModule(NULL), m_dwError(ERROR_SUCCESS) {} - explicit CAutoHModule ( HMODULE hModule ) : m_hModule ( hModule ), m_dwError ( GetLastError() ) {} + explicit CAutoHModule(HMODULE hModule) : m_hModule(hModule), m_dwError(GetLastError()) {} - explicit CAutoHModule ( HMODULE hModule, DWORD dwError ) : m_hModule ( hModule ), m_dwError ( dwError ) {} + explicit CAutoHModule(HMODULE hModule, DWORD dwError) : m_hModule(hModule), m_dwError(dwError) {} - ~CAutoHModule() - { - if ( m_hModule != NULL ) + ~CAutoHModule() { - FreeLibrary ( m_hModule ); - m_hModule = NULL; - } - - SetLastError ( m_dwError ); - } - - operator HMODULE() - { - return m_hModule; - } - - HMODULE m_hModule; - DWORD m_dwError; -}; - -static HMODULE LoadLibraryFromSystem32 ( LPCTSTR lpFileName ) -{ - //Get the Windows System32 directory - TCHAR szFullPath[_MAX_PATH]; - szFullPath[0] = _T ( '\0' ); - - if ( ::GetSystemDirectory ( szFullPath, _countof ( szFullPath ) ) == 0 ) - return NULL; - - //Setup the full path and delegate to LoadLibrary -#pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK - _tcscat_s ( szFullPath, _countof ( szFullPath ), _T ( "\\" ) ); - - _tcscat_s ( szFullPath, _countof ( szFullPath ), lpFileName ); - - return LoadLibrary ( szFullPath ); -} - -static BOOL RegQueryValueString ( HKEY kKey, LPCTSTR lpValueName, LPTSTR& pszValue ) -{ - //Initialize the output parameter - pszValue = NULL; - - //First query for the size of the registry value - DWORD dwType = 0; - DWORD dwDataSize = 0; - LONG nError = RegQueryValueEx ( kKey, lpValueName, NULL, &dwType, NULL, &dwDataSize ); - - if ( nError != ERROR_SUCCESS ) - { - SetLastError ( nError ); - return FALSE; - } - - //Ensure the value is a string - - if ( dwType != REG_SZ ) - { - SetLastError ( ERROR_INVALID_DATA ); - return FALSE; - } - - //Allocate enough bytes for the return value - DWORD dwAllocatedSize = dwDataSize + sizeof ( TCHAR ); //+sizeof(TCHAR) is to allow us to NULL terminate the data if it is not null terminated in the registry - - pszValue = reinterpret_cast ( LocalAlloc ( LMEM_FIXED, dwAllocatedSize ) ); - - if ( pszValue == NULL ) - return FALSE; - - //Recall RegQueryValueEx to return the data - pszValue[0] = _T ( '\0' ); - - DWORD dwReturnedSize = dwAllocatedSize; - - nError = RegQueryValueEx ( kKey, lpValueName, NULL, &dwType, reinterpret_cast ( pszValue ), &dwReturnedSize ); - - if ( nError != ERROR_SUCCESS ) - { - LocalFree ( pszValue ); - pszValue = NULL; - SetLastError ( nError ); - return FALSE; - } - - //Handle the case where the data just returned is the same size as the allocated size. This could occur where the data - //has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than - //return a potentially non-null terminated block of data, just fail the method call - - if ( dwReturnedSize >= dwAllocatedSize ) - { - SetLastError ( ERROR_INVALID_DATA ); - return FALSE; - } - - //NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry - - if ( pszValue[dwReturnedSize/sizeof ( TCHAR ) - 1] != _T ( '\0' ) ) - pszValue[dwReturnedSize/sizeof ( TCHAR ) ] = _T ( '\0' ); - - return TRUE; -} - -static BOOL IsNumeric ( LPCSTR pszString, BOOL bIgnoreColon ) -{ - size_t nLen = strlen ( pszString ); - - if ( nLen == 0 ) - return FALSE; - - //What will be the return value from this function (assume the best) - BOOL bNumeric = TRUE; - - for ( size_t i = 0; i < nLen && bNumeric; i++ ) - { - bNumeric = ( isdigit ( static_cast ( pszString[i] ) ) != 0 ); - - if ( bIgnoreColon && ( pszString[i] == ':' ) ) - bNumeric = TRUE; - } - - return bNumeric; -} - -static BOOL IsNumeric ( LPCWSTR pszString, BOOL bIgnoreColon ) -{ - size_t nLen = wcslen ( pszString ); - - if ( nLen == 0 ) - return FALSE; - - //What will be the return value from this function (assume the best) - BOOL bNumeric = TRUE; - - for ( size_t i = 0; i < nLen && bNumeric; i++ ) - { - bNumeric = ( iswdigit ( pszString[i] ) != 0 ); - - if ( bIgnoreColon && ( pszString[i] == L':' ) ) - bNumeric = TRUE; - } - - return bNumeric; -} - -static BOOL QueryRegistryPortName ( HKEY hDeviceKey, int& nPort ) -{ - //What will be the return value from the method (assume the worst) - BOOL bAdded = FALSE; - - //Read in the name of the port - LPTSTR pszPortName = NULL; - - if ( RegQueryValueString ( hDeviceKey, _T ( "PortName" ), pszPortName ) ) - { - //If it looks like "COMX" then - //add it to the array which will be returned - size_t nLen = _tcslen ( pszPortName ); - - if ( nLen > 3 ) - { - if ( ( _tcsnicmp ( pszPortName, _T ( "COM" ), 3 ) == 0 ) && IsNumeric ( ( pszPortName + 3 ), FALSE ) ) - { - //Work out the port number - nPort = _ttoi ( pszPortName + 3 ); - - bAdded = TRUE; - } - } - - LocalFree ( pszPortName ); - } - - return bAdded; -} - -// get a list of all serial ports -ArrayMap Serial::GetSerialPorts ( void ) -{ - ArrayMap res; - - //Get the various function pointers we require from setupapi.dll - CAutoHModule setupAPI ( LoadLibraryFromSystem32 ( _T ( "SETUPAPI.DLL" ) ) ); - - if ( setupAPI == NULL ) - return res; - - SETUPDIOPENDEVREGKEY* lpfnSETUPDIOPENDEVREGKEY = reinterpret_cast ( GetProcAddress ( setupAPI, "SetupDiOpenDevRegKey" ) ); - - /* - #if defined _UNICODE - */ - SETUPDIGETCLASSDEVS* lpfnSETUPDIGETCLASSDEVS = reinterpret_cast ( GetProcAddress ( setupAPI, "SetupDiGetClassDevsW" ) ); - - SETUPDIGETDEVICEREGISTRYPROPERTY* lpfnSETUPDIGETDEVICEREGISTRYPROPERTY = reinterpret_cast ( GetProcAddress ( setupAPI, "SetupDiGetDeviceRegistryPropertyW" ) ); - - /* - #else - SETUPDIGETCLASSDEVS* lpfnSETUPDIGETCLASSDEVS = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetClassDevsA")); - - SETUPDIGETDEVICEREGISTRYPROPERTY* lpfnSETUPDIGETDEVICEREGISTRYPROPERTY = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetDeviceRegistryPropertyA")); - - #endif - */ - SETUPDIDESTROYDEVICEINFOLIST* lpfnSETUPDIDESTROYDEVICEINFOLIST = reinterpret_cast ( GetProcAddress ( setupAPI, "SetupDiDestroyDeviceInfoList" ) ); - - SETUPDIENUMDEVICEINFO* lpfnSETUPDIENUMDEVICEINFO = reinterpret_cast ( GetProcAddress ( setupAPI, "SetupDiEnumDeviceInfo" ) ); - - if ( ( lpfnSETUPDIOPENDEVREGKEY == NULL ) || ( lpfnSETUPDIDESTROYDEVICEINFOLIST == NULL ) || - ( lpfnSETUPDIENUMDEVICEINFO == NULL ) || ( lpfnSETUPDIGETCLASSDEVS == NULL ) || ( lpfnSETUPDIGETDEVICEREGISTRYPROPERTY == NULL ) ) - { - //Set the error to report - setupAPI.m_dwError = ERROR_CALL_NOT_IMPLEMENTED; - - return res; - } - - //Now create a "device information set" which is required to enumerate all the ports - GUID guid = GUID_DEVINTERFACE_COMPORT; - - HDEVINFO hDevInfoSet = lpfnSETUPDIGETCLASSDEVS ( &guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); - - if ( hDevInfoSet == INVALID_HANDLE_VALUE ) - { - //Set the error to report - setupAPI.m_dwError = GetLastError(); - - return res; - } - - //Finally do the enumeration - BOOL bMoreItems = TRUE; - - int nIndex = 0; - - SP_DEVINFO_DATA devInfo; - - while ( bMoreItems ) - { - //Enumerate the current device - devInfo.cbSize = sizeof ( SP_DEVINFO_DATA ); - bMoreItems = lpfnSETUPDIENUMDEVICEINFO ( hDevInfoSet, nIndex, &devInfo ); - - if ( bMoreItems ) - { - //Did we find a serial port for this device - BOOL bAdded = FALSE; - String port, friendly; - - //Get the registry key which stores the ports settings - HKEY hDeviceKey = lpfnSETUPDIOPENDEVREGKEY ( hDevInfoSet, &devInfo, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE ); - - if ( hDeviceKey != INVALID_HANDLE_VALUE ) - { - int nPort = 0; - - if ( QueryRegistryPortName ( hDeviceKey, nPort ) ) + if (m_hModule != NULL) { - port = Format ( "COM%d", nPort ); - bAdded = TRUE; + FreeLibrary(m_hModule); + m_hModule = NULL; } - //Close the key now that we are finished with it - RegCloseKey ( hDeviceKey ); + SetLastError(m_dwError); } - //If the port was a serial port, then also try to get its friendly name - - if ( bAdded ) + operator HMODULE() { - TCHAR szFriendlyName[1024]; - szFriendlyName[0] = _T ( '\0' ); - DWORD dwSize = sizeof ( szFriendlyName ); - DWORD dwType = 0; - - if ( lpfnSETUPDIGETDEVICEREGISTRYPROPERTY ( hDevInfoSet, &devInfo, SPDRP_DEVICEDESC, &dwType, reinterpret_cast ( szFriendlyName ), dwSize, &dwSize ) && ( dwType == REG_SZ ) ) - friendly = szFriendlyName; - else - friendly = ""; - - res.Add ( port, friendly ); + return m_hModule; } - } - ++nIndex; + HMODULE m_hModule; + DWORD m_dwError; + }; + + static HMODULE LoadLibraryFromSystem32(LPCTSTR lpFileName) + { + //Get the Windows System32 directory + TCHAR szFullPath[_MAX_PATH]; + szFullPath[0] = _T('\0'); + + if (::GetSystemDirectory(szFullPath, _countof(szFullPath)) == 0) + return NULL; + + //Setup the full path and delegate to LoadLibrary +#pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK + _tcscat_s(szFullPath, _countof(szFullPath), _T("\\")); + + _tcscat_s(szFullPath, _countof(szFullPath), lpFileName); + + return LoadLibrary(szFullPath); } - //Free up the "device information set" now that we are finished with it - lpfnSETUPDIDESTROYDEVICEINFOLIST ( hDevInfoSet ); + static BOOL RegQueryValueString(HKEY kKey, LPCTSTR lpValueName, LPTSTR& pszValue) + { + //Initialize the output parameter + pszValue = NULL; - return res; -} + //First query for the size of the registry value + DWORD dwType = 0; + DWORD dwDataSize = 0; + LONG nError = RegQueryValueEx(kKey, lpValueName, NULL, &dwType, NULL, &dwDataSize); + + if (nError != ERROR_SUCCESS) + { + SetLastError(nError); + return FALSE; + } + + //Ensure the value is a string + + if (dwType != REG_SZ) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + //Allocate enough bytes for the return value + DWORD dwAllocatedSize = dwDataSize + sizeof(TCHAR); //+sizeof(TCHAR) is to allow us to NULL terminate the data if it is not null terminated in the registry + + pszValue = reinterpret_cast(LocalAlloc(LMEM_FIXED, dwAllocatedSize)); + + if (pszValue == NULL) + return FALSE; + + //Recall RegQueryValueEx to return the data + pszValue[0] = _T('\0'); + + DWORD dwReturnedSize = dwAllocatedSize; + + nError = RegQueryValueEx(kKey, lpValueName, NULL, &dwType, reinterpret_cast(pszValue), &dwReturnedSize); + + if (nError != ERROR_SUCCESS) + { + LocalFree(pszValue); + pszValue = NULL; + SetLastError(nError); + return FALSE; + } + + //Handle the case where the data just returned is the same size as the allocated size. This could occur where the data + //has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than + //return a potentially non-null terminated block of data, just fail the method call + + if (dwReturnedSize >= dwAllocatedSize) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + //NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry + + if (pszValue[dwReturnedSize/sizeof(TCHAR) - 1] != _T('\0')) + pszValue[dwReturnedSize/sizeof(TCHAR)] = _T('\0'); + + return TRUE; + } + + static BOOL IsNumeric(LPCSTR pszString, BOOL bIgnoreColon) + { + size_t nLen = strlen(pszString); + + if (nLen == 0) + return FALSE; + + //What will be the return value from this function (assume the best) + BOOL bNumeric = TRUE; + + for (size_t i = 0; i < nLen && bNumeric; i++) + { + bNumeric = (isdigit(static_cast(pszString[i])) != 0); + + if (bIgnoreColon && (pszString[i] == ':')) + bNumeric = TRUE; + } + + return bNumeric; + } + + static BOOL IsNumeric(LPCWSTR pszString, BOOL bIgnoreColon) + { + size_t nLen = wcslen(pszString); + + if (nLen == 0) + return FALSE; + + //What will be the return value from this function (assume the best) + BOOL bNumeric = TRUE; + + for (size_t i = 0; i < nLen && bNumeric; i++) + { + bNumeric = (iswdigit(pszString[i]) != 0); + + if (bIgnoreColon && (pszString[i] == L':')) + bNumeric = TRUE; + } + + return bNumeric; + } + + static BOOL QueryRegistryPortName(HKEY hDeviceKey, int& nPort) + { + //What will be the return value from the method (assume the worst) + BOOL bAdded = FALSE; + + //Read in the name of the port + LPTSTR pszPortName = NULL; + + if (RegQueryValueString(hDeviceKey, _T("PortName"), pszPortName)) + { + //If it looks like "COMX" then + //add it to the array which will be returned + size_t nLen = _tcslen(pszPortName); + + if (nLen > 3) + { + if ((_tcsnicmp(pszPortName, _T("COM"), 3) == 0) && IsNumeric((pszPortName + 3), FALSE)) + { + //Work out the port number + nPort = _ttoi(pszPortName + 3); + + bAdded = TRUE; + } + } + + LocalFree(pszPortName); + } + + return bAdded; + } + + // get a list of all serial ports + ArrayMap Serial::GetSerialPorts(void) + { + ArrayMap res; + + //Get the various function pointers we require from setupapi.dll + CAutoHModule setupAPI(LoadLibraryFromSystem32(_T("SETUPAPI.DLL"))); + + if (setupAPI == NULL) + return res; + + SETUPDIOPENDEVREGKEY* lpfnSETUPDIOPENDEVREGKEY = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiOpenDevRegKey")); + + /* + #if defined _UNICODE + */ + SETUPDIGETCLASSDEVS* lpfnSETUPDIGETCLASSDEVS = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetClassDevsW")); + + SETUPDIGETDEVICEREGISTRYPROPERTY* lpfnSETUPDIGETDEVICEREGISTRYPROPERTY = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetDeviceRegistryPropertyW")); + + /* + #else + SETUPDIGETCLASSDEVS* lpfnSETUPDIGETCLASSDEVS = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetClassDevsA")); + + SETUPDIGETDEVICEREGISTRYPROPERTY* lpfnSETUPDIGETDEVICEREGISTRYPROPERTY = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiGetDeviceRegistryPropertyA")); + + #endif + */ + SETUPDIDESTROYDEVICEINFOLIST* lpfnSETUPDIDESTROYDEVICEINFOLIST = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiDestroyDeviceInfoList")); + + SETUPDIENUMDEVICEINFO* lpfnSETUPDIENUMDEVICEINFO = reinterpret_cast(GetProcAddress(setupAPI, "SetupDiEnumDeviceInfo")); + + if ((lpfnSETUPDIOPENDEVREGKEY == NULL) || (lpfnSETUPDIDESTROYDEVICEINFOLIST == NULL) || + (lpfnSETUPDIENUMDEVICEINFO == NULL) || (lpfnSETUPDIGETCLASSDEVS == NULL) || (lpfnSETUPDIGETDEVICEREGISTRYPROPERTY == NULL)) + { + //Set the error to report + setupAPI.m_dwError = ERROR_CALL_NOT_IMPLEMENTED; + + return res; + } + + //Now create a "device information set" which is required to enumerate all the ports + GUID guid = GUID_DEVINTERFACE_COMPORT; + + HDEVINFO hDevInfoSet = lpfnSETUPDIGETCLASSDEVS(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (hDevInfoSet == INVALID_HANDLE_VALUE) + { + //Set the error to report + setupAPI.m_dwError = GetLastError(); + + return res; + } + + //Finally do the enumeration + BOOL bMoreItems = TRUE; + + int nIndex = 0; + + SP_DEVINFO_DATA devInfo; + + while (bMoreItems) + { + //Enumerate the current device + devInfo.cbSize = sizeof(SP_DEVINFO_DATA); + bMoreItems = lpfnSETUPDIENUMDEVICEINFO(hDevInfoSet, nIndex, &devInfo); + + if (bMoreItems) + { + //Did we find a serial port for this device + BOOL bAdded = FALSE; + String port, friendly; + + //Get the registry key which stores the ports settings + HKEY hDeviceKey = lpfnSETUPDIOPENDEVREGKEY(hDevInfoSet, &devInfo, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + + if (hDeviceKey != INVALID_HANDLE_VALUE) + { + int nPort = 0; + + if (QueryRegistryPortName(hDeviceKey, nPort)) + { + port = Format("COM%d", nPort); + bAdded = TRUE; + } + + //Close the key now that we are finished with it + RegCloseKey(hDeviceKey); + } + + //If the port was a serial port, then also try to get its friendly name + + if (bAdded) + { + TCHAR szFriendlyName[1024]; + szFriendlyName[0] = _T('\0'); + DWORD dwSize = sizeof(szFriendlyName); + DWORD dwType = 0; + + if (lpfnSETUPDIGETDEVICEREGISTRYPROPERTY(hDevInfoSet, &devInfo, SPDRP_DEVICEDESC, &dwType, reinterpret_cast(szFriendlyName), dwSize, &dwSize) && (dwType == REG_SZ)) + friendly = szFriendlyName; + else + friendly = ""; + + res.Add(port, friendly); + } + } + + ++nIndex; + } + + //Free up the "device information set" now that we are finished with it + lpfnSETUPDIDESTROYDEVICEINFOLIST(hDevInfoSet); + + return res; + } #else -// ENUMERATE PORTS BY REGISTI ENTRIES + // ENUMERATE PORTS BY REGISTI ENTRIES -// get a list of all serial ports -ArrayMap Serial::GetSerialPorts ( void ) -{ - ArrayMap res; - - HKEY hSERIALCOMM; - - if ( RegOpenKeyEx ( HKEY_LOCAL_MACHINE, _T ( "HARDWARE\\DEVICEMAP\\SERIALCOMM" ), 0, KEY_QUERY_VALUE, &hSERIALCOMM ) == ERROR_SUCCESS ) + // get a list of all serial ports + ArrayMap Serial::GetSerialPorts(void) { - //Get the max value name and max value lengths - DWORD dwMaxValueNameLen; - DWORD dwMaxValueLen; - DWORD dwQueryInfo = RegQueryInfoKey ( hSERIALCOMM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxValueNameLen, &dwMaxValueLen, NULL, NULL ); + ArrayMap res; - if ( dwQueryInfo == ERROR_SUCCESS ) + HKEY hSERIALCOMM; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hSERIALCOMM) == ERROR_SUCCESS) { - DWORD dwMaxValueNameSizeInChars = dwMaxValueNameLen + 1; //Include space for the NULL terminator - DWORD dwMaxValueNameSizeInBytes = dwMaxValueNameSizeInChars * sizeof ( TCHAR ); - DWORD dwMaxValueDataSizeInChars = dwMaxValueLen / sizeof ( TCHAR ) + 1; //Include space for the NULL terminator - DWORD dwMaxValueDataSizeInBytes = dwMaxValueDataSizeInChars * sizeof ( TCHAR ); + //Get the max value name and max value lengths + DWORD dwMaxValueNameLen; + DWORD dwMaxValueLen; + DWORD dwQueryInfo = RegQueryInfoKey(hSERIALCOMM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxValueNameLen, &dwMaxValueLen, NULL, NULL); - //Allocate some space for the value name and value data - TCHAR *valueName = (TCHAR *)malloc(dwMaxValueNameSizeInBytes + 1); - TCHAR *valueData = (TCHAR *)malloc(dwMaxValueDataSizeInChars + 1); - - //Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM - DWORD dwIndex = 0; - DWORD dwType; - DWORD dwValueNameSize = dwMaxValueNameSizeInChars; - DWORD dwDataSize = dwMaxValueDataSizeInBytes; - memset (valueName, 0, dwMaxValueNameSizeInBytes ); - memset (valueData, 0, dwMaxValueDataSizeInBytes ); - TCHAR* szValueName = valueName; - BYTE* byValue = (BYTE *)valueData; - LONG nEnum = RegEnumValue (hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, byValue, &dwDataSize ); - - while ( nEnum == ERROR_SUCCESS ) + if (dwQueryInfo == ERROR_SUCCESS) { - //If the value is of the correct type, then add it to the array - if ( dwType == REG_SZ ) + DWORD dwMaxValueNameSizeInChars = dwMaxValueNameLen + 1; //Include space for the NULL terminator + DWORD dwMaxValueNameSizeInBytes = dwMaxValueNameSizeInChars * sizeof(TCHAR); + DWORD dwMaxValueDataSizeInChars = dwMaxValueLen / sizeof(TCHAR) + 1; //Include space for the NULL terminator + DWORD dwMaxValueDataSizeInBytes = dwMaxValueDataSizeInChars * sizeof(TCHAR); + + //Allocate some space for the value name and value data + TCHAR *valueName = (TCHAR *)malloc(dwMaxValueNameSizeInBytes + 1); + TCHAR *valueData = (TCHAR *)malloc(dwMaxValueDataSizeInChars + 1); + + //Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM + DWORD dwIndex = 0; + DWORD dwType; + DWORD dwValueNameSize = dwMaxValueNameSizeInChars; + DWORD dwDataSize = dwMaxValueDataSizeInBytes; + memset(valueName, 0, dwMaxValueNameSizeInBytes); + memset(valueData, 0, dwMaxValueDataSizeInBytes); + TCHAR* szValueName = valueName; + BYTE* byValue = (BYTE *)valueData; + LONG nEnum = RegEnumValue(hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, byValue, &dwDataSize); + + while (nEnum == ERROR_SUCCESS) { - TCHAR* szPort = reinterpret_cast ( byValue ); - res.Add(szPort, szPort); + //If the value is of the correct type, then add it to the array + if (dwType == REG_SZ) + { + TCHAR* szPort = reinterpret_cast(byValue); + res.Add(szPort, szPort); + } + + //Prepare for the next time around + dwValueNameSize = dwMaxValueNameSizeInChars; + dwDataSize = dwMaxValueDataSizeInBytes; + memset(valueName, 0, dwMaxValueNameSizeInBytes); + memset(valueData, 0, dwMaxValueDataSizeInBytes); + ++dwIndex; + nEnum = RegEnumValue(hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, byValue, &dwDataSize); } - //Prepare for the next time around - dwValueNameSize = dwMaxValueNameSizeInChars; - dwDataSize = dwMaxValueDataSizeInBytes; - memset (valueName, 0, dwMaxValueNameSizeInBytes ); - memset (valueData, 0, dwMaxValueDataSizeInBytes ); - ++dwIndex; - nEnum = RegEnumValue (hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, byValue, &dwDataSize ); + free(valueName); + free(valueData); } - - free(valueName); - free(valueData); + + //Close the registry key now that we are finished with it + RegCloseKey(hSERIALCOMM); + + if (dwQueryInfo != ERROR_SUCCESS) + SetLastError(dwQueryInfo); } - //Close the registry key now that we are finished with it - RegCloseKey ( hSERIALCOMM ); - - if ( dwQueryInfo != ERROR_SUCCESS ) - SetLastError ( dwQueryInfo ); + return res; } - - return res; -} #endif } // END_UPP_NAMESPACE