/* PIP - Platform Independent Primitives COM Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com, 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" PISerial::PISerial(const PIString & device, void * data_, ReadRetFunc slot): PIIODevice(device, ReadWrite) { piMonitor.serials++; setPriority(piHigh); path_ = device; data = data_; fd = -1; headerPtr = 0; params = 0; vtime = 1; ret_func_ = slot; #ifdef WINDOWS hCom = 0; #endif ispeed = ospeed = S115200; init(); } PISerial::PISerial(void * data_, ReadRetFunc slot): PIIODevice("", ReadWrite) { piMonitor.serials++; setPriority(piHigh); data = data_; fd = -1; headerPtr = 0; params = 0; vtime = 1; ret_func_ = slot; #ifdef WINDOWS hCom = 0; #endif ispeed = ospeed = S115200; init(); } PISerial::~PISerial() { piMonitor.serials--; } 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: piCout << "[PISerial] 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: piCout << "[PISerial] 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: piCout << "[PISerial] Pin number " << number << " doesn`t exists!"; return false; } return false; } bool PISerial::setBit(int bit, bool on, const PIString & bname) { #ifndef WINDOWS if (fd < 0) { piCout << "[PISerial] set" << bname << " error: \"" << path_ << "\" is not opened!"; return false; } if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) { piCout << "[PISerial] set" << bname << " error: " << errorString(); return false; } return true; #else piCout << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } bool PISerial::isBit(int bit, const PIString & bname) const { #ifndef WINDOWS if (fd < 0) { piCout << "[PISerial] is" << bname << " error: \"" << path_ << "\" is not opened!"; return false; } int ret = 0; if (ioctl(fd, TIOCMGET, &ret) < 0) piCout << "[PISerial] is" << bname << " error: " << errorString(); return ret & bit; #else piCout << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } bool PISerial::closeDevice() { if (!isInitialized()) return true; if (isRunning()) { stop(); PIThread::terminate(); } #ifdef WINDOWS if (fd != -1) { SetCommState(hCom, &sdesc); SetCommMask(hCom, mask); CloseHandle(hCom); fd = -1; } #else if (fd != -1) { tcsetattr(fd, TCSANOW, &sdesc); ::close(fd); fd = -1; } #endif 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 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; } bool PISerial::read(void * data, int size, double timeout_ms) { if (data == 0) return false; int ret, all = 0; if (timeout_ms > 0.) { setReadIsBlocking(false); all = ::read(fd, data, 1); timer.reset(); while (all < size && timer.elapsed_m() < timeout_ms) { ret = ::read(fd, &((uchar * )data)[all], size - all); if (ret > 0) all += ret; else msleep(1); } received(data, all); return (all == size); } else { setReadIsBlocking(true); all = ::read(fd, data, 1); while (all < size) { ret = ::read(fd, &((uchar * )data)[all], size - all); if (ret > 0) all += ret; } received(data, all); return (all == size); } return false; } PIByteArray PISerial::readData(int size, double timeout_ms) { int ret, all = 0; uchar td[1024]; PIByteArray str; if (timeout_ms > 0.) { setReadIsBlocking(false); timer.reset(); if (size <= 0) { while (timer.elapsed_m() < timeout_ms) { ret = ::read(fd, td, 1024); if (ret <= 0) msleep(1); else str << PIByteArray(td, ret); } } else { while (all < size && timer.elapsed_m() < timeout_ms) { ret = ::read(fd, td, size - all); if (ret <= 0) msleep(1); else { str << PIByteArray(td, ret); all += ret; } } } } else { setReadIsBlocking(true); all = ::read(fd, td, 1); str << PIByteArray(td, all); while (all < size) { ret = ::read(fd, td, size - all); if (ret <= 0) msleep(1); else { str << PIByteArray(td, ret); all += ret; } } } return str; } PIString PISerial::read(int size, double timeout_ms) { int ret, all = 0; uchar td[1024]; PIString str; if (timeout_ms > 0.) { setReadIsBlocking(false); timer.reset(); if (size <= 0) { while (timer.elapsed_m() < timeout_ms) { ret = ::read(fd, td, 1024); if (ret <= 0) msleep(1); else str << PIString((char*)td, ret); } } else { while (all < size && timer.elapsed_m() < timeout_ms) { ret = ::read(fd, td, size - all); if (ret <= 0) msleep(1); else { str << PIString((char*)td, ret); all += ret; } } } } else { setReadIsBlocking(true); all = ::read(fd, td, 1); str << PIString((char*)td, all); while (all < size) { ret = ::read(fd, td, size - all); if (ret <= 0) msleep(1); else { str << PIString((char*)td, ret); all += ret; } } } return str; } bool PISerial::openDevice() { #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;} hCom = CreateFileA(path_.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if(hCom == INVALID_HANDLE_VALUE) { piCout << "[PISerial] Unable to open \"" << path_ << "\""; return false; } fd = 0; COMMTIMEOUTS times; times.ReadIntervalTimeout = vtime; times.ReadTotalTimeoutConstant = 1; times.ReadTotalTimeoutMultiplier = 0; times.WriteTotalTimeoutConstant = 1; times.WriteTotalTimeoutMultiplier = 0; if (SetCommTimeouts(hCom, ×) == -1) { piCout << "[PISerial] Unable to set timeouts for \"" << path_ << "\""; CloseHandle(hCom); fd = -1; return false; } GetCommMask(hCom, &mask); SetCommMask(hCom, EV_RXCHAR); GetCommState(hCom, &sdesc); desc = sdesc; desc.DCBlength = sizeof(desc); desc.BaudRate = convertSpeed(ispeed); desc.ByteSize = 8; if (params[PISerial::ParityControl]) { desc.fParity = 1; desc.Parity = params[PISerial::ParityOdd] ? 1 : 2; } desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT; if (SetCommState(hCom, &desc) == -1) { piCout << "[PISerial] Unable to set comm state for \"" << path_ << "\""; CloseHandle(hCom); fd = -1; return false; } #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) { piCout << "[PISerial] Unable to open \"" << path_ << "\""; return false; } tcgetattr(fd, &desc); sdesc = desc; desc.c_iflag = desc.c_oflag = desc.c_lflag = desc.c_cflag = 0; desc.c_cflag = CLOCAL | CSIZE | CS8; if (isReadable()) desc.c_cflag |= CREAD; if (params[PISerial::HardwareFlowControl]) desc.c_cflag |= CRTSCTS; if (params[PISerial::TwoStopBits]) desc.c_cflag |= CSTOPB; if (params[PISerial::ParityControl]) { desc.c_iflag |= INPCK; desc.c_cflag |= PARENB; if (params[PISerial::ParityOdd]) desc.c_cflag |= PARODD; } desc.c_cc[VMIN] = 1; desc.c_cc[VTIME] = vtime; cfsetispeed(&desc, convertSpeed(ispeed)); cfsetospeed(&desc, convertSpeed(ospeed)); tcflush(fd, TCIOFLUSH); fcntl(fd, F_SETFL, 0); if(tcsetattr(fd, TCSANOW, &desc) < 0) { piCout << "[PISerial] Can`t set attributes for \"" << path_ << "\""; ::close(fd); return false; } //piCout << "[PISerial] Initialized " << path_; #endif return true; } int PISerial::write(const void * data, int max_size, bool wait) { //piCout << "[PISerial] send " << max_size << ": " << PIString((char*)data, max_size); if (fd == -1 || !canWrite()) { //piCout << "[PISerial] Can`t write to uninitialized COM"; return -1; } #ifdef WINDOWS DWORD wrote; WriteFile(hCom, data, max_size, &wrote, 0); if (wait) { DWORD event; SetCommMask(hCom, EV_TXEMPTY); WaitCommEvent(hCom, &event, NULL); SetCommMask(hCom, EV_RXCHAR); } #else int wrote; wrote = ::write(fd, data, max_size); if (wait) tcdrain(fd); #endif return (int)wrote; //piCout << "[PISerial] Error while sending"; //piCout << "[PISerial] Wrote " << wrote << " bytes in " << path_; }