From 64de920fe7479f54ddb3699bd47f26de64ac886d Mon Sep 17 00:00:00 2001 From: micio Date: Sat, 28 Jan 2017 23:24:30 +0000 Subject: [PATCH] Bazaar/Serial : start porting to OSX git-svn-id: svn://ultimatepp.org/upp/trunk@10792 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- bazaar/Serial/Serial.upp | 3 +- .../{SerialPosix.cpp => SerialLinux.cpp} | 2 +- bazaar/Serial/SerialOSX.cpp | 557 ++++++++++++++++++ 3 files changed, 560 insertions(+), 2 deletions(-) rename bazaar/Serial/{SerialPosix.cpp => SerialLinux.cpp} (94%) create mode 100644 bazaar/Serial/SerialOSX.cpp diff --git a/bazaar/Serial/Serial.upp b/bazaar/Serial/Serial.upp index 132af8fac..743fcb470 100644 --- a/bazaar/Serial/Serial.upp +++ b/bazaar/Serial/Serial.upp @@ -6,6 +6,7 @@ uses file Serial.h, Serial.cpp, - SerialPosix.cpp, + SerialLinux.cpp, + SerialOSX.cpp, SerialWindows.cpp; diff --git a/bazaar/Serial/SerialPosix.cpp b/bazaar/Serial/SerialLinux.cpp similarity index 94% rename from bazaar/Serial/SerialPosix.cpp rename to bazaar/Serial/SerialLinux.cpp index 0bcb742a7..d0f771acd 100644 --- a/bazaar/Serial/SerialPosix.cpp +++ b/bazaar/Serial/SerialLinux.cpp @@ -1,6 +1,6 @@ #include "Serial.h" -#ifdef PLATFORM_POSIX +#ifdef PLATFORM_LINUX #include diff --git a/bazaar/Serial/SerialOSX.cpp b/bazaar/Serial/SerialOSX.cpp new file mode 100644 index 000000000..a834a74cd --- /dev/null +++ b/bazaar/Serial/SerialOSX.cpp @@ -0,0 +1,557 @@ +#include "Serial.h" + +#ifdef PLATFORM_OSX11 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +NAMESPACE_UPP + +static bool __IsSymLink(const char *path) +{ + 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[] = + { + B0, B50, B75, B110, B134, B150, + B200, B300, B600, B1200, B1800, + B2400, B4800, B9600, B19200, + B38400, B57600, B115200, B230400 + }; + + int bits; + + 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; + } + if (ioctl(fd, TIOCMGET, &bits) < 0) + { + // unable to query serial ports signals + close(fd); + fd = -1; + isError = true; + errCode = DeviceError; + return false; + } + bits &= ~(TIOCM_DTR | TIOCM_RTS); + if (ioctl(fd, TIOCMSET, &bits) < 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 = DeviceError; + return false; + } + + //// CFLAG ///// + + 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); + } + + // 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; +} + +// 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]; + return true; + } +} + +bool Serial::Read(char &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 = buf[0]; + 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 (;;) + { + if (write(fd, &c, 1) == 1) + return true; + 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]; + 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 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; +} + +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(NULL, &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); +} + +END_UPP_NAMESPACE + +#endif