/* PIP - Platform Independent Primitives COM Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "piserial.h" #include "piconfig.h" #include "pidir.h" #include "pipropertystorage.h" #include #if defined(MICRO_PIP) # define PISERIAL_NO_PINS #endif #if defined(PISERIAL_NO_PINS) || defined(WINDOWS) # 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 #endif #ifdef WINDOWS # ifndef INITGUID # define INITGUID # include # undef INITGUID # else # include # endif # include # include # include # include # include # include # 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 #ifdef LINUX # include #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. * * \section PISerial_sec1 FullPath * Since version 1.16.0 you can use as \a path DeviceInfo::id() USB identifier. * \code * PISerial * s = new PISerial("0403:6001"); * PIIODevice * d = PIIODevice::createFromFullPath("ser://0403:6001:115200"); * \endcode * */ 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::DeviceInfo::DeviceInfo() { vID = pID = 0; } PIString PISerial::DeviceInfo::id() const { return PIString::fromNumber(vID, 16).toLowerCase().expandLeftTo(4, '0') + ":" + PIString::fromNumber(pID, 16).toLowerCase().expandLeftTo(4, '0'); } 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() { stop(); close(); } void PISerial::construct() { #ifdef WINDOWS PRIVATE->hCom = 0; #endif fd = -1; setPriority(piHigh); 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::setBreak(bool enabled) { if (fd < 0) { piCoutObj << "sendBreak error: \"" << path() << "\" is not opened!"; return false; } #ifdef WINDOWS if (enabled) { if (!SetCommBreak(PRIVATE->hCom)) { piCoutObj << "setBreak error: " << errorString(); return false; } else { return true; } } else { if (!ClearCommBreak(PRIVATE->hCom)) { piCoutObj << "setBreak error: " << errorString(); return false; } else { return true; } } #else if (ioctl(fd, enabled ? TIOCSBRK : TIOCCBRK) < 0) { piCoutObj << "setBreak error: " << errorString(); return false; } else { return true; } #endif return false; } bool PISerial::setBit(int bit, bool on, const PIString & bname) { if (fd < 0) { piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } #ifndef PISERIAL_NO_PINS # ifdef WINDOWS static int bit_map_on [] = {0, 0, 0, 0, SETDTR, 0, 0, SETRTS, 0, 0, 0}; static int bit_map_off[] = {0, 0, 0, 0, CLRDTR, 0, 0, CLRRTS, 0, 0, 0}; int action = (on ? bit_map_on : bit_map_off)[bit]; if (action > 0) { if (EscapeCommFunction(PRIVATE->hCom, action) == 0) { piCoutObj << "setBit" << bname << " error: " << errorString(); return false; } return true; } # else if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) { piCoutObj << "setBit" << bname << " error: " << errorString(); return false; } return true; # endif #endif piCoutObj << "setBit" << bname << " doesn`t implemented, sorry :-("; return false; } bool PISerial::isBit(int bit, const PIString & bname) const { if (fd < 0) { piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } #ifndef PISERIAL_NO_PINS # ifdef WINDOWS # else int ret = 0; if (ioctl(fd, TIOCMGET, &ret) < 0) piCoutObj << "isBit" << bname << " error: " << errorString(); return ret & bit; # endif #endif piCoutObj << "isBit" << bname << " doesn`t implemented, sorry :-("; return false; } void PISerial::flush() { #ifndef WINDOWS if (fd != -1) tcflush(fd, TCIOFLUSH); #endif } 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; #ifdef WINDOWS case S14400: return B14400; #endif 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; } #ifdef WINDOWS piCoutObj << "Warning: Custom speed" << (int)speed; return (int)speed; #else piCoutObj << "Warning: Unknown speed" << (int)speed << ", using 115200"; return B115200; #endif } /** \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.) { bool br = setOption(BlockingRead, false); all = readDevice(data, 1); tm_.reset(); while (all < size && tm_.elapsed_m() < timeout_ms) { ret = readDevice(&((uchar * )data)[all], size - all); if (ret > 0) all += ret; else piMinSleep(); } setOption(BlockingRead, br); received(data, all); return (all == size); } else { bool br = setOption(BlockingRead, true); all = readDevice(data, 1); while (all < size) { ret = readDevice(&((uchar * )data)[all], size - all); if (ret > 0) all += ret; } setOption(BlockingRead, br); 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.) { bool br = setOption(BlockingRead, false); tm_.reset(); if (size <= 0) { while (tm_.elapsed_m() < timeout_ms) { ret = readDevice(td, 1024); if (ret <= 0) piMinSleep(); else str << PIString((char*)td, ret); } } else { while (all < size && tm_.elapsed_m() < timeout_ms) { ret = readDevice(td, size - all); if (ret <= 0) piMinSleep(); else { str << PIString((char*)td, ret); all += ret; } } } setOption(BlockingRead, br); } else { bool br = setOption(BlockingRead, true); all = readDevice(td, 1); str << PIString((char*)td, all); while (all < size) { ret = readDevice(td, size - all); if (ret <= 0) piMinSleep(); else { str << PIString((char*)td, ret); all += ret; } } setOption(BlockingRead, br); } 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.) { bool br = setOption(BlockingRead, false); tm_.reset(); if (size <= 0) { while (tm_.elapsed_m() < timeout_ms) { ret = readDevice(td, 1024); if (ret <= 0) piMinSleep(); else str.append(td, ret); } } else { while (all < size && tm_.elapsed_m() < timeout_ms) { ret = readDevice(td, size - all); if (ret <= 0) piMinSleep(); else { str.append(td, ret); all += ret; } } } setOption(BlockingRead, br); } else { bool br = setOption(BlockingRead, true); all = readDevice(td, 1); str.append(td, all); while (all < size) { ret = readDevice(td, size - all); if (ret <= 0) piMinSleep(); else { str.append(td, ret); all += ret; } } setOption(BlockingRead, br); } received(str.data(), str.size_s()); return str; } bool PISerial::send(const void * data, int size) { int ret = 0; int wsz = 0; do { ret = write(&(((uchar*)data)[wsz]), size - wsz); if (ret > 0) wsz += ret; //piCout << ret << wsz; else return false; } while (wsz < size); return (wsz == size); } bool PISerial::openDevice() { PIString p = path(); //piCout << "ser open" << p; PIString pl = p.toLowerCase().removeAll(' '); if (!pl.startsWith("/") && !pl.startsWith("com")) { p.clear(); PIVector devs = availableDevicesInfo(); piForeachC (DeviceInfo & d, devs) { if (d.id() == pl) { p = d.path; break; } } if (p.isEmpty()) { piCoutObj << "Unable to find device \"" << pl << "\""; } } if (p.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 = "//./" + p; PRIVATE->hCom = CreateFileA(wp.dataAscii(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (PRIVATE->hCom == INVALID_HANDLE_VALUE) { piCoutObj << "Unable to open \"" << p << "\""; 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; } fd = ::open(p.data(), O_NOCTTY | om); if (fd == -1) { piCoutObj << "Unable to open \"" << p << "\""; return false; } tcgetattr(fd, &PRIVATE->desc); PRIVATE->sdesc = PRIVATE->desc; //piCoutObj << "Initialized " << p; #endif applySettings(); return true; } bool PISerial::closeDevice() { if (isRunning() && !isStopping()) { 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; } 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; 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 = isOptionSet(BlockingRead) ? vtime : MAXDWORD; times.ReadTotalTimeoutConstant = isOptionSet(BlockingRead) ? 0 : 1; times.ReadTotalTimeoutMultiplier = isOptionSet(BlockingRead) ? 0 : MAXDWORD; times.WriteTotalTimeoutConstant = isOptionSet(BlockingWrite) ? 0 : 1; times.WriteTotalTimeoutMultiplier = 0; if (SetCommTimeouts(PRIVATE->hCom, ×) == -1) piCoutObj << "Unable to set timeouts for \"" << path() << "\""; #else fcntl(fd, F_SETFL, isOptionSet(BlockingRead) ? 0 : O_NONBLOCK); #endif } /** \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::readDevice(void * read_to, int max_size) { #ifdef WINDOWS if (!canRead()) return -1; if (sending) return -1; // piCoutObj << "com event ..."; //piCoutObj << "read ..." << PRIVATE->hCom; ReadFile(PRIVATE->hCom, read_to, max_size, &PRIVATE->readed, 0); DWORD err = GetLastError(); //piCout << err; if (err == ERROR_BAD_COMMAND || err == ERROR_ACCESS_DENIED) { PIThread::stop(false); close(); return 0; } //piCoutObj << "read" << (PRIVATE->readed) << errorString(); return PRIVATE->readed; #else if (!canRead()) return -1; int ret = ::read(fd, read_to, max_size); if (ret < 0) { int err = errno; if (err == EBADF || err == EFAULT || err == EINVAL || err == EIO) { PIThread::stop(false); close(); return 0; } } return ret; #endif } int PISerial::writeDevice(const void * data, int max_size) { if (fd == -1 || !canWrite()) { //piCoutObj << "Can`t write to uninitialized COM"; return -1; } #ifdef WINDOWS 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 (isOptionSet(BlockingWrite)) 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::constructFullPathDevice() const { PIString ret; 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::configureFromFullPathDevice(const PIString & full_path) { PIStringList pl = full_path.split(":"); if (pl.size_s() > 1) { PIString _s = pl[0].toLowerCase().removeAll(' ').trim(); if (!_s.startsWith("/") && !_s.startsWith("com")) { pl[0] += ":" + pl[1]; pl.remove(1); } } for (int i = 0; i < pl.size_s(); ++i) { PIString p(pl[i]); switch (i) { case 0: setProperty("path", p); break; case 1: setProperty("outSpeed", p.toInt()); setProperty("inSpeed", p.toInt()); break; case 2: setProperty("dataBitsCount", p.toInt()); break; case 3: p = p.left(1).toLowerCase(); if (p != "n") setParameter(ParityControl); if (p == "o") setParameter(ParityOdd); break; case 4: if (p.toInt() == 2) setParameter(TwoStopBits); break; } } applySettings(); } PIPropertyStorage PISerial::constructVariantDevice() const { PIPropertyStorage ret; ret.addProperty("path", path()); PIVariantTypes::Enum e; PIVector as = availableSpeeds(); piForeachC (int s, as) {e << PIVariantTypes::Enumerator(s, PIString::fromNumber(s));} e.selectValue((int)inSpeed()); ret.addProperty("speed", e); e = PIVariantTypes::Enum(); for (int i = 5; i <= 8; ++i) {e << PIVariantTypes::Enumerator(i, PIString::fromNumber(i));} e.selectValue(dataBitsCount()); ret.addProperty("dataBits", e); e = PIVariantTypes::Enum(); e << "None" << "Odd" << "Even"; if (parameters()[ParityControl]) { if (parameters()[ParityOdd]) e.selectValue(1); else e.selectValue(2); } else e.selectValue(0); ret.addProperty("parity", e); e = PIVariantTypes::Enum(); for (int i = 1; i <= 2; ++i) {e << PIVariantTypes::Enumerator(i, PIString::fromNumber(i));} e.selectValue(parameters()[TwoStopBits] ? 2 : 1); ret.addProperty("stopBits", e); return ret; } void PISerial::configureFromVariantDevice(const PIPropertyStorage & d) { setPath(d.propertyValueByName("path").toString()); setSpeed((Speed)d.propertyValueByName("speed").toEnum().selectedValue()); setDataBitsCount(d.propertyValueByName("dataBits").toEnum().selectedValue()); PIVariantTypes::Enum e = d.propertyValueByName("parity").toEnum(); setParameter(ParityControl, e.selectedValue() > 0); setParameter(ParityOdd , e.selectedValue() == 1); setParameter(TwoStopBits , d.propertyValueByName("stopBits").toEnum().selectedValue() == 2); } PIVector PISerial::availableSpeeds() { PIVector spds; spds << 50 << 75 << 110 << 300 << 600 << 1200 << 2400 << 4800 << 9600 << #ifdef WINDOWS 14400 << #endif 19200 << 38400 << 57600 << 115200 << 230400 << 460800 << 500000 << 576000 << 921600 << 1000000 << 1152000 << 1500000 << 2000000 << 2500000 << 3000000 << 3500000 << 4000000; return spds; } PIStringList PISerial::availableDevices(bool test) { PIVector devs = availableDevicesInfo(test); PIStringList ret; piForeachC (DeviceInfo & d, devs) ret << d.path; return ret; } #ifdef WINDOWS PIString devicePortName(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) { PIString ret; const HKEY key = SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (key == INVALID_HANDLE_VALUE) return ret; static const wchar_t * const keyTokens[] = { L"PortName\0", L"PortNumber\0" }; static const int keys_count = sizeof(keyTokens) / sizeof(keyTokens[0]); for (int i = 0; i < keys_count; ++i) { DWORD dataType = 0; DWORD bytesRequired = MAX_PATH; PIVector outputBuffer(MAX_PATH + 1); for (;;) { LONG res = RegQueryValueExW(key, keyTokens[i], NULL, &dataType, (LPBYTE)outputBuffer.data(), &bytesRequired); if (res == ERROR_MORE_DATA) { outputBuffer.resize(bytesRequired / sizeof(wchar_t) + 2); continue; } else if (res == ERROR_SUCCESS) { if (dataType == REG_SZ) ret = PIString(outputBuffer.data()); else if (dataType == REG_DWORD) ret = PIStringAscii("COM") + PIString::fromNumber(*(PDWORD(&outputBuffer[0]))); } break; } if (!ret.isEmpty()) break; } RegCloseKey(key); return ret; } PIString deviceRegistryProperty(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData, DWORD property) { DWORD dataType = 0; DWORD bytesRequired = MAX_PATH; PIVector outputBuffer(MAX_PATH + 1); for (;;) { if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, deviceInfoData, property, &dataType, (PBYTE)outputBuffer.data(), bytesRequired, &bytesRequired)) break; if ((GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (dataType != REG_SZ && dataType != REG_EXPAND_SZ)) return PIString(); outputBuffer.resize(bytesRequired / sizeof(wchar_t) + 2, 0); } return PIString(outputBuffer.data()); } PIString deviceInstanceIdentifier(DEVINST deviceInstanceNumber) { PIVector outputBuffer(MAX_DEVICE_ID_LEN + 1); if (CM_Get_Device_IDW(deviceInstanceNumber, (PWCHAR)outputBuffer.data(), MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) { return PIString(); } return PIString(outputBuffer.data()); } bool parseID(PIString str, PISerial::DeviceInfo & di) { if (str.isEmpty()) return false; int i = str.find("VID_"); if (i > 0) di.vID = str.mid(i + 4, 4).toInt(16); i = str.find("PID_"); if (i > 0) di.pID = str.mid(i + 4, 4).toInt(16); return (di.vID > 0) && (di.pID > 0); } #endif PIVector PISerial::availableDevicesInfo(bool test) { PIVector ret; DeviceInfo di; #ifdef WINDOWS static const GUID guids[] = {GUID_DEVINTERFACE_MODEM, GUID_DEVINTERFACE_COMPORT}; static const int guids_cnt = sizeof(guids) / sizeof(GUID); for (int i = 0; i < guids_cnt; ++i) { const HDEVINFO dis = SetupDiGetClassDevs(&(guids[i]), NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (dis == INVALID_HANDLE_VALUE) continue; SP_DEVINFO_DATA did; memset(&did, 0, sizeof(did)); did.cbSize = sizeof(did); DWORD index = 0; while (SetupDiEnumDeviceInfo(dis, index++, &did)) { di = DeviceInfo(); di.path = devicePortName(dis, &did); if (!di.path.startsWith("COM")) continue; di.description = deviceRegistryProperty(dis, &did, SPDRP_DEVICEDESC); di.manufacturer = deviceRegistryProperty(dis, &did, SPDRP_MFG); PIString id_str = deviceInstanceIdentifier(did.DevInst); if (!parseID(id_str, di)) { DEVINST pdev = 0; if (CM_Get_Parent(&pdev, did.DevInst, 0) == CR_SUCCESS) { id_str = deviceInstanceIdentifier(pdev); parseID(id_str, di); } } ret << di; //piCout << "dev" << did.DevInst << di; } SetupDiDestroyDeviceInfoList(dis); } #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 # ifdef MAC_OS prefixes.clear(); prefixes << "cu." << "tty."; # 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(); # ifdef LINUX char linkbuf[1024]; # endif piForeachC (PIFile::FileInfo & e, de) { // TODO changes in FileInfo piForeachC (PIString & p, prefixes) { if (e.name().startsWith(p)) { di = DeviceInfo(); di.path = e.path; # ifdef LINUX ssize_t lsz = readlink(("/sys/class/tty/" + e.name()).dataAscii(), linkbuf, 1024); if (lsz > 0) { PIString fpath = "/sys/class/tty/" + PIString(linkbuf, lsz) + "/"; PIFile _f; for (int i = 0; i < 5; ++i) { fpath += "../"; //piCout << "try" << fpath; if (_f.open(fpath + "idVendor", PIIODevice::ReadOnly)) di.vID = PIString(_f.readAll()).trim().toInt(16); if (_f.open(fpath + "idProduct", PIIODevice::ReadOnly)) di.pID = PIString(_f.readAll()).trim().toInt(16); if (_f.open(fpath + "product", PIIODevice::ReadOnly)) di.description = PIString(_f.readAll()).trim(); if (_f.open(fpath + "manufacturer", PIIODevice::ReadOnly)) di.manufacturer = PIString(_f.readAll()).trim(); if (di.pID > 0) break; } } # endif ret << di; } } } # endif #endif if (test) { for (int i = 0; i < ret.size_s(); ++i) { #ifdef WINDOWS void * hComm = CreateFileA(ret[i].path.dataAscii(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (hComm == INVALID_HANDLE_VALUE) { #else int fd = ::open(ret[i].path.dataAscii(), O_NOCTTY | O_RDONLY); if (fd == -1) { #endif ret.remove(i); --i; continue; } bool rok = true; #ifndef WINDOWS int void_ = 0; fcntl(fd, F_SETFL, O_NONBLOCK); if (::read(fd, &void_, 1) == -1) rok = errno != EIO; #endif if (!rok) { ret.remove(i); --i; continue; } #ifdef WINDOWS CloseHandle(hComm); #else ::close(fd); #endif } } return ret; } void PISerial::optionsChanged() { if (isOpened()) setTimeouts(); } void PISerial::threadedReadBufferSizeChanged() { if (!isOpened()) return; #if defined(LINUX) serial_struct ss; ioctl(fd, TIOCGSERIAL, &ss); //piCoutObj << "b" << ss.xmit_fifo_size; ss.xmit_fifo_size = piMaxi(threadedReadBufferSize(), 4096); ioctl(fd, TIOCSSERIAL, &ss); //piCoutObj << "a" << ss.xmit_fifo_size; #endif }