#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 }; 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; } 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 = 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; 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; } } // 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 = 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) { 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; } // 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