/* PIP - Platform Independent Primitives COM Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru, Bychkov Andrey wapmobil@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piserial.h" #include "piconfig.h" #include "pidir.h" #include #ifdef WINDOWS # include # define TIOCM_LE 1 # define TIOCM_DTR 4 # define TIOCM_RTS 7 # define TIOCM_CTS 8 # define TIOCM_ST 3 # define TIOCM_SR 2 # define TIOCM_CAR 1 # define TIOCM_RNG 9 # define TIOCM_DSR 6 # define B50 50 # define B75 75 # define B110 110 # define B300 300 # define B600 600 # define B1200 1200 # define B2400 2400 # define B4800 4800 # define B9600 9600 # define B14400 14400 # define B19200 19200 # define B38400 38400 # define B57600 57600 # define B115200 115200 # define B230400 230400 # define B460800 460800 # define B500000 500000 # define B576000 576000 # define B921600 921600 # define B1000000 1000000 # define B1152000 1152000 # define B1500000 1500000 # define B2000000 2000000 # define B2500000 2500000 # define B3000000 3000000 # define B3500000 3500000 # define B4000000 4000000 #else # include # include # include # ifndef B50 # define B50 0000001 # endif # ifndef B75 # define B75 0000002 # endif # ifndef B230400 # define B230400 0010003 # endif # ifndef B460800 # define B460800 0010004 # endif # ifndef B500000 # define B500000 0010005 # endif # ifndef B576000 # define B576000 0010006 # endif # ifndef B921600 # define B921600 0010007 # endif # ifndef B1000000 # define B1000000 0010010 # endif # ifndef B1152000 # define B1152000 0010011 # endif # ifndef B1500000 # define B1500000 0010012 # endif # ifndef B2000000 # define B2000000 0010013 # endif # ifndef B2500000 # define B2500000 0010014 # endif # ifndef B3000000 # define B3000000 0010015 # endif # ifndef B3500000 # define B3500000 0010016 # endif # ifndef B4000000 # define B4000000 0010017 # endif #endif #ifndef CRTSCTS # define CRTSCTS 020000000000 #endif /*! \class PISerial * \brief Serial device * * \section PISerial_sec0 Synopsis * This class provide access to serial device, e.g. COM port. It can read, * write, wait for write. There are several read and write functions. * * */ REGISTER_DEVICE(PISerial); PRIVATE_DEFINITION_START(PISerial) #ifdef WINDOWS DCB desc, sdesc; void * hCom; DWORD readed, mask; #else termios desc, sdesc; uint readed; #endif PRIVATE_DEFINITION_END(PISerial) PISerial::PISerial(): PIIODevice("", ReadWrite) { construct(); } PISerial::PISerial(const PIString & device_, PISerial::Speed speed_, PIFlags params_): PIIODevice(device_, ReadWrite) { construct(); setPath(device_); setSpeed(speed_); setParameters(params_); } PISerial::~PISerial() { closeDevice(); piMonitor.serials--; } void PISerial::construct() { #ifdef WINDOWS PRIVATE->hCom = 0; #endif fd = -1; piMonitor.serials++; setPriority(piHigh); block_read = false; block_write = true; vtime = 10; sending = false; setParameters(0); setSpeed(S115200); setDataBitsCount(8); } void PISerial::setParameter(PISerial::Parameters parameter, bool on) { PIFlags cp = (PIFlags)(property("parameters").toInt()); cp.setFlag(parameter, on); setParameters(cp); } bool PISerial::isParameterSet(PISerial::Parameters parameter) const { PIFlags cp = (PIFlags)(property("parameters").toInt()); return cp[parameter]; } bool PISerial::setPin(int number, bool on) { switch (number) { case 1: return setCAR(on); break; case 2: return setSR(on); break; case 3: return setST(on); break; case 4: return setDTR(on); break; case 5: piCoutObj << "Pin number 5 is ground"; return false; case 6: return setDSR(on); break; case 7: return setRTS(on); break; case 8: return setCTS(on); break; case 9: return setRNG(on); break; default: piCoutObj << "Pin number " << number << " doesn`t exists!"; return false; } return false; } bool PISerial::isPin(int number) const { switch (number) { case 1: return isCAR(); break; case 2: return isSR(); break; case 3: return isST(); break; case 4: return isDTR(); break; case 5: return false; case 6: return isDSR(); break; case 7: return isRTS(); break; case 8: return isCTS(); break; case 9: return isRNG(); break; default: piCoutObj << "Pin number " << number << " doesn`t exists!"; return false; } return false; } bool PISerial::setLE(bool on) {return setBit(TIOCM_LE, on, "LE");} bool PISerial::setDTR(bool on) {return setBit(TIOCM_DTR, on, "DTR");} bool PISerial::setRTS(bool on) {return setBit(TIOCM_RTS, on, "RTS");} bool PISerial::setCTS(bool on) {return setBit(TIOCM_CTS, on, "CTS");} bool PISerial::setST(bool on) {return setBit(TIOCM_ST, on, "ST");} bool PISerial::setSR(bool on) {return setBit(TIOCM_SR, on, "SR");} bool PISerial::setCAR(bool on) {return setBit(TIOCM_CAR, on, "CAR");} bool PISerial::setRNG(bool on) {return setBit(TIOCM_RNG, on, "RNG");} bool PISerial::setDSR(bool on) {return setBit(TIOCM_DSR, on, "DSR");} bool PISerial::isLE() const {return isBit(TIOCM_LE, "LE");} bool PISerial::isDTR() const {return isBit(TIOCM_DTR, "DTR");} bool PISerial::isRTS() const {return isBit(TIOCM_RTS, "RTS");} bool PISerial::isCTS() const {return isBit(TIOCM_CTS, "CTS");} bool PISerial::isST() const {return isBit(TIOCM_ST, "ST");} bool PISerial::isSR() const {return isBit(TIOCM_SR, "SR");} bool PISerial::isCAR() const {return isBit(TIOCM_CAR, "CAR");} bool PISerial::isRNG() const {return isBit(TIOCM_RNG, "RNG");} bool PISerial::isDSR() const {return isBit(TIOCM_DSR, "DSR");} bool PISerial::setBit(int bit, bool on, const PIString & bname) { #ifndef WINDOWS if (fd < 0) { piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) { piCoutObj << "setBit" << bname << " error: " << errorString(); return false; } return true; #else piCoutObj << "setBit" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } bool PISerial::isBit(int bit, const PIString & bname) const { #ifndef WINDOWS if (fd < 0) { piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } int ret = 0; if (ioctl(fd, TIOCMGET, &ret) < 0) piCoutObj << "isBit" << bname << " error: " << errorString(); return ret & bit; #else piCoutObj << "isBit" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } void PISerial::flush() { #ifndef WINDOWS if (fd != -1) tcflush(fd, TCIOFLUSH); #endif } bool PISerial::closeDevice() { if (isRunning()) { stop(); PIThread::terminate(); } if (fd != -1) { #ifdef WINDOWS SetCommState(PRIVATE->hCom, &PRIVATE->sdesc); SetCommMask(PRIVATE->hCom, PRIVATE->mask); // piCoutObj << "close" << CloseHandle(PRIVATE->hCom); PRIVATE->hCom = 0; #else tcsetattr(fd, TCSANOW, &PRIVATE->sdesc); ::close(fd); #endif fd = -1; } return true; } int PISerial::convertSpeed(PISerial::Speed speed) { switch (speed) { case S50: return B50; case S75: return B75; case S110: return B110; case S300: return B300; case S600: return B600; case S1200: return B1200; case S2400: return B2400; case S4800: return B4800; case S9600: return B9600; case S19200: return B19200; case S38400: return B38400; case S57600: return B57600; case S115200: return B115200; case S230400: return B230400; case S460800: return B460800; case S500000: return B500000; case S576000: return B576000; case S921600: return B921600; case S1000000: return B1000000; case S1152000: return B1152000; case S1500000: return B1500000; case S2000000: return B2000000; case S2500000: return B2500000; case S3000000: return B3000000; case S3500000: return B3500000; case S4000000: return B4000000; default: break; } return B115200; } /** \brief Advanced read function * \details Read to pointer "read_to" no more than "max_size" and no longer * than "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be * wait forever until "max_size" will be readed. If size <= 0 function * immediate returns \b false. For read data with unknown size use function * \a readData(). * \returns \b True if readed bytes count = "max_size", else \b false * \sa \a readData() */ bool PISerial::read(void * data, int size, double timeout_ms) { if (data == 0 || size <= 0) return false; int ret, all = 0; if (timeout_ms > 0.) { setReadIsBlocking(false); all = read(data, 1); tm_.reset(); while (all < size && tm_.elapsed_m() < timeout_ms) { ret = read(&((uchar * )data)[all], size - all); if (ret > 0) all += ret; else msleep(1); } received(data, all); return (all == size); } else { setReadIsBlocking(true); all = read(data, 1); while (all < size) { ret = read(&((uchar * )data)[all], size - all); if (ret > 0) all += ret; } received(data, all); return (all == size); } return false; } /** \brief Advanced read function * \details Read all or no more than "size" and no longer than * "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be * wait forever until "size" will be readed. If "size" <= 0 * function will be read all until "timeout_ms" elaped. \n If size <= 0 * and "timeout_ms" <= 0 function immediate returns empty string. * \n This function similar to \a readData() but returns data as string. * \sa \a readData() */ PIString PISerial::read(int size, double timeout_ms) { PIString str; if (size <= 0 && timeout_ms <= 0.) return str; int ret, all = 0; uchar td[1024]; if (timeout_ms > 0.) { setReadIsBlocking(false); tm_.reset(); if (size <= 0) { while (tm_.elapsed_m() < timeout_ms) { ret = read(td, 1024); if (ret <= 0) msleep(1); else str << PIString((char*)td, ret); } } else { while (all < size && tm_.elapsed_m() < timeout_ms) { ret = read(td, size - all); if (ret <= 0) msleep(1); else { str << PIString((char*)td, ret); all += ret; } } } } else { setReadIsBlocking(true); all = read(td, 1); str << PIString((char*)td, all); while (all < size) { ret = read(td, size - all); if (ret <= 0) msleep(1); else { str << PIString((char*)td, ret); all += ret; } } } received(str.data(), str.size_s()); return str; } /** \brief Advanced read function * \details Read all or no more than "size" and no longer than * "timeout_ms" milliseconds. If "timeout_ms" < 0 function will be * wait forever until "size" will be readed. If "size" <= 0 * function will be read all until "timeout_ms" elaped. \n If size <= 0 * and "timeout_ms" <= 0 function immediate returns empty byte array. * \n This function similar to \a read() but returns data as byte array. * \sa \a read() */ PIByteArray PISerial::readData(int size, double timeout_ms) { PIByteArray str; if (size <= 0 && timeout_ms <= 0.) return str; int ret, all = 0; uchar td[1024]; if (timeout_ms > 0.) { setReadIsBlocking(false); tm_.reset(); if (size <= 0) { while (tm_.elapsed_m() < timeout_ms) { ret = read(td, 1024); if (ret <= 0) msleep(1); else str.append(td, ret); } } else { while (all < size && tm_.elapsed_m() < timeout_ms) { ret = read(td, size - all); if (ret <= 0) msleep(1); else { str.append(td, ret); all += ret; } } } } else { setReadIsBlocking(true); all = read(td, 1); str.append(td, all); while (all < size) { ret = read(td, size - all); if (ret <= 0) msleep(1); else { str.append(td, ret); all += ret; } } } received(str.data(), str.size_s()); return str; } bool PISerial::openDevice() { //piCout << "ser open" << path(); if (path().isEmpty()) return false; #ifdef WINDOWS DWORD ds = 0, sm = 0; if (isReadable()) {ds |= GENERIC_READ; sm |= FILE_SHARE_READ;} if (isWriteable()) {ds |= GENERIC_WRITE; sm |= FILE_SHARE_WRITE;} PIString wp = "//./" + path(); PRIVATE->hCom = CreateFileA(wp.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (PRIVATE->hCom == INVALID_HANDLE_VALUE) { piCoutObj << "Unable to open \"" << path() << "\""; fd = -1; return false; } fd = 0; #else int om = 0; switch (mode()) { case PIIODevice::ReadOnly: om = O_RDONLY; break; case PIIODevice::WriteOnly: om = O_WRONLY; break; case PIIODevice::ReadWrite: om = O_RDWR; break; } //cout << "init ser " << path_ << " mode " << om << " param " << params << endl; fd = ::open(path().data(), O_NOCTTY | om); if (fd == -1) { piCoutObj << "Unable to open \"" << path() << "\""; return false; } tcgetattr(fd, &PRIVATE->desc); PRIVATE->sdesc = PRIVATE->desc; //piCoutObj << "Initialized " << path_; #endif applySettings(); return true; } void PISerial::applySettings() { #ifdef WINDOWS if (fd == -1) return; setTimeouts(); GetCommMask(PRIVATE->hCom, &PRIVATE->mask); SetCommMask(PRIVATE->hCom, EV_RXCHAR); GetCommState(PRIVATE->hCom, &PRIVATE->sdesc); // piCoutObj << PRIVATE->sdesc.fBinary << PRIVATE->sdesc.fAbortOnError << PRIVATE->sdesc.fDsrSensitivity << PRIVATE->sdesc.fDtrControl << PRIVATE->sdesc.fDummy2 << PRIVATE->sdesc.fErrorChar; PRIVATE->desc = PRIVATE->sdesc; PRIVATE->desc.DCBlength = sizeof(PRIVATE->desc); PRIVATE->desc.BaudRate = convertSpeed(outSpeed()); if (dataBitsCount() >= 5 && dataBitsCount() <= 8) PRIVATE->desc.ByteSize = dataBitsCount(); else PRIVATE->desc.ByteSize = 8; PIFlags params = parameters(); if (params[PISerial::ParityControl]) { PRIVATE->desc.fParity = 1; PRIVATE->desc.Parity = params[PISerial::ParityOdd] ? 1 : 2; } PRIVATE->desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT; PRIVATE->desc.fOutxCtsFlow = 0; PRIVATE->desc.fOutxDsrFlow = 0; PRIVATE->desc.fDtrControl = 0; PRIVATE->desc.fRtsControl = 0; PRIVATE->desc.fInX = 0; PRIVATE->desc.fOutX = 0; PRIVATE->desc.fBinary = 1; PRIVATE->desc.fAbortOnError = 0; PRIVATE->desc.fNull = 0; if (SetCommState(PRIVATE->hCom, &PRIVATE->desc) == -1) { piCoutObj << "Unable to set comm state for \"" << path() << "\""; return; } #else if (fd == -1) return; tcgetattr(fd, &PRIVATE->desc); PRIVATE->desc.c_oflag = PRIVATE->desc.c_lflag = PRIVATE->desc.c_cflag = 0; PRIVATE->desc.c_iflag = IGNBRK; PRIVATE->desc.c_cflag = CLOCAL | HUPCL; switch (dataBitsCount()) { case 5: PRIVATE->desc.c_cflag |= (CSIZE & CS5); break; case 6: PRIVATE->desc.c_cflag |= (CSIZE & CS6); break; case 7: PRIVATE->desc.c_cflag |= (CSIZE & CS7); break; case 8: default: PRIVATE->desc.c_cflag |= (CSIZE & CS8); break; }; if (isReadable()) PRIVATE->desc.c_cflag |= CREAD; PIFlags params = parameters(); if (params[PISerial::TwoStopBits]) PRIVATE->desc.c_cflag |= CSTOPB; if (params[PISerial::ParityControl]) { PRIVATE->desc.c_iflag |= INPCK; PRIVATE->desc.c_cflag |= PARENB; if (params[PISerial::ParityOdd]) PRIVATE->desc.c_cflag |= PARODD; } PRIVATE->desc.c_cc[VMIN] = 1; PRIVATE->desc.c_cc[VTIME] = vtime; cfsetispeed(&PRIVATE->desc, convertSpeed(inSpeed())); cfsetospeed(&PRIVATE->desc, convertSpeed(outSpeed())); tcflush(fd, TCIOFLUSH); setTimeouts(); if(tcsetattr(fd, TCSANOW, &PRIVATE->desc) < 0) { piCoutObj << "Can`t set attributes for \"" << path() << "\""; return; } #endif } void PISerial::setTimeouts() { #ifdef WINDOWS COMMTIMEOUTS times; times.ReadIntervalTimeout = block_read ? vtime : MAXDWORD; times.ReadTotalTimeoutConstant = block_read ? 0 : 1; times.ReadTotalTimeoutMultiplier = block_read ? 0 : MAXDWORD; times.WriteTotalTimeoutConstant = block_write ? 0 : 1; times.WriteTotalTimeoutMultiplier = 0; if (SetCommTimeouts(PRIVATE->hCom, ×) == -1) piCoutObj << "Unable to set timeouts for \"" << path() << "\""; #else fcntl(fd, F_SETFL, block_read ? 0 : O_NONBLOCK); #endif } void PISerial::setReadIsBlocking(bool yes) { block_read = yes; if (isOpened()) setTimeouts(); } /** \brief Basic read function * \details Read to pointer "read_to" no more than "max_size". If read is * set to blocking this function will be wait at least one byte. * \returns Readed bytes count * \sa \a readData() */ int PISerial::read(void * read_to, int max_size) { #ifdef WINDOWS if (!canRead()) return -1; if (sending) return -1; // piCoutObj << "com event ..."; WaitCommEvent(PRIVATE->hCom, 0, 0); // piCoutObj << "read ..."; ReadFile(PRIVATE->hCom, read_to, max_size, &PRIVATE->readed, 0); // piCoutObj << "read ok"; return PRIVATE->readed; #else if (!canRead()) return -1; return ::read(fd, read_to, max_size); #endif } int PISerial::write(const void * data, int max_size, bool wait) { if (fd == -1 || !canWrite()) { //piCoutObj << "Can`t write to uninitialized COM"; return -1; } #ifdef WINDOWS if (block_write != wait) { block_write = wait; // piCoutObj << "set timeout ..."; setReadIsBlocking(block_read); // piCoutObj << "set timeout ok"; } DWORD wrote; // piCoutObj << "send ...";// << max_size;// << ": " << PIString((char*)data, max_size); sending = true; WriteFile(PRIVATE->hCom, data, max_size, &wrote, 0); sending = false; // piCoutObj << "send ok";// << wrote << " bytes in " << path(); #else int wrote; wrote = ::write(fd, data, max_size); if (wait) tcdrain(fd); #endif return (int)wrote; //piCoutObj << "Error while sending"; } bool PISerial::configureDevice(const void * e_main, const void * e_parent) { PIConfig::Entry * em = (PIConfig::Entry * )e_main; PIConfig::Entry * ep = (PIConfig::Entry * )e_parent; setDevice(readDeviceSetting("device", device(), em, ep)); setSpeed((PISerial::Speed)(readDeviceSetting("speed", (int)outSpeed(), em, ep))); setDataBitsCount(readDeviceSetting("dataBitsCount", dataBitsCount(), em, ep)); setParameter(PISerial::ParityControl, readDeviceSetting("parityControl", isParameterSet(PISerial::ParityControl), em, ep)); setParameter(PISerial::ParityOdd, readDeviceSetting("parityOdd", isParameterSet(PISerial::ParityOdd), em, ep)); setParameter(PISerial::TwoStopBits, readDeviceSetting("twoStopBits", isParameterSet(PISerial::TwoStopBits), em, ep)); return true; } PIString PISerial::constructFullPath() const { PIString ret(fullPathPrefix() + "://"); ret << path() << ":" << int(inSpeed()) << ":" << dataBitsCount(); if (parameters()[ParityControl]) { if (parameters()[ParityOdd]) ret << ":O"; else ret << ":E"; } else ret << ":N"; if (parameters()[TwoStopBits]) ret << ":2"; else ret << ":1"; return ret; } void PISerial::configureFromFullPath(const PIString & full_path) { PIStringList pl = full_path.split(":"); for (int i = 0; i < pl.size_s(); ++i) { PIString p(pl[i]); switch (i) { case 0: setPath(p); break; case 1: setSpeed((Speed)(p.toInt())); break; case 2: setDataBitsCount(p.toInt()); break; case 3: p = p.toLowerCase(); if (p != "n") setParameter(ParityControl); if (p == "o") setParameter(ParityOdd); break; case 4: if (p.toInt() == 2) setParameter(TwoStopBits); break; } } } PIVector PISerial::availableSpeeds() { PIVector spds; spds << 50 << 75 << 110 << 300 << 600 << 1200 << 2400 << 4800 << 9600 << 19200 << 38400 << 57600 << 115200 << 1500000 << 2000000 << 2500000 << 3000000 << 3500000 << 4000000; return spds; } PIStringList PISerial::availableDevices(bool test) { PIStringList dl; #ifdef WINDOWS HKEY key = 0; RegOpenKey(HKEY_LOCAL_MACHINE, (LPCTSTR)"HARDWARE\\DEVICEMAP\\SERIALCOMM", &key); if (key != 0) { char name[1024], data[1024]; DWORD name_len = 1024, data_len = 1024, type = 0, index = 0; LONG ret; while ((ret = RegEnumValue(key, index, (LPTSTR)name, &name_len, NULL, &type, (uchar * )data, &data_len)) != ERROR_NO_MORE_ITEMS) { dl << PIString(data); index++; } RegCloseKey(key); } #else # ifndef ANDROID PIStringList prefixes; # ifdef QNX prefixes << "ser"; # else prefixes << "ttyS" << "ttyO" << "ttyUSB" << "ttyACM" << "ttyGS" << "ttyMI" << "ttymxc" << "ttyAMA" << "rfcomm" << "ircomm"; # ifdef FREE_BSD prefixes << "cu"; # endif PIFile file_prefixes("/proc/tty/drivers", PIIODevice::ReadOnly); if (file_prefixes.open()) { PIString fc = file_prefixes.readAll(true), line, cpref; PIStringList words; file_prefixes.close(); while (!fc.isEmpty()) { words.clear(); line = fc.takeLine(); if (line.isEmpty()) break; while (!line.isEmpty()) words << line.takeWord(); if (words.size_s() < 2) break; if (words.back() != "serial") continue; cpref = words[1]; int li = cpref.findLast("/"); if (li > 0) cpref.cutLeft(li + 1); prefixes << cpref; } prefixes.removeDuplicates(); } # endif PIDir dir("/dev"); PIVector de = dir.entries(); piForeachC (PIFile::FileInfo & e, de) { // TODO changes in FileInfo piForeachC (PIString & p, prefixes) { if (e.name().left(p.size_s()) != p) continue; dl << e.path; } } # endif #endif if (test) { for (int i = 0; i < dl.size_s(); ++i) { #ifdef WINDOWS void * hComm = CreateFileA(dl[i].data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (hComm == INVALID_HANDLE_VALUE) { #else int fd = ::open(dl[i].data(), O_NOCTTY | O_RDONLY); if (fd == -1) { #endif dl.remove(i); --i; continue; } int void_ = 0; bool rok = true; #ifdef WINDOWS /*COMMTIMEOUTS times; times.ReadIntervalTimeout = MAXDWORD; times.ReadTotalTimeoutConstant = 0; times.ReadTotalTimeoutMultiplier = 0; times.WriteTotalTimeoutConstant = 1; times.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts(hComm, ×); if (ReadFile(hComm, &void_, 1, &readed_, 0) == 0) rok = GetLastError() == ;*/ #else fcntl(fd, F_SETFL, O_NONBLOCK); if (::read(fd, &void_, 1) == -1) rok = errno != EIO; #endif if (!rok) { dl.remove(i); --i; continue; } #ifdef WINDOWS CloseHandle(hComm); #else ::close(fd); #endif } } return dl; }