/*************************************************************************** * Copyright (C) 2005-2006 Nicolas Hadacek * * Copyright (C) 2003-2004 Alain Gibaud * * Copyright (C) 2002-2003 Stephen Landamore * * * * 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 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "serial.h" #ifdef Q_OS_UNIX # include # include # include # include # include # include # include # include // needed on some system #endif #include //----------------------------------------------------------------------------- TQStringList *Port::Serial::_list = 0; #if defined(Q_OS_UNIX) const Port::Serial::Handle Port::Serial::INVALID_HANDLE = -1; #elif defined(Q_OS_WIN) const Port::Serial::Handle Port::Serial::INVALID_HANDLE = INVALID_HANDLE_VALUE; #endif Port::Serial::Handle Port::Serial::openHandle(const TQString &device, IODirs dirs) { #if defined(Q_OS_UNIX) // open non blocking: avoid missing DCD (comment from xwisp2) int mode = O_NOCTTY | O_NONBLOCK; if ( dirs & In ) { if ( dirs & Out ) mode |= O_RDWR; else mode |= O_RDONLY; } else mode |= O_WRONLY; return ::open(device.latin1(), mode); #elif defined(Q_OS_WIN) int mode = 0; if ( dirs & In ) mode |= GENERIC_READ; if ( dirs & Out ) mode |= GENERIC_WRITE; return CreateFileA(device.latin1(), mode, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL); #endif } void Port::Serial::closeHandle(Handle handle) { #if defined(Q_OS_UNIX) ::close(handle); #elif defined(Q_OS_WIN) CloseHandle(handle); #endif } Port::IODirs Port::Serial::probe(const TQString &device) { Handle handle = openHandle(device, In); if ( handle==INVALID_HANDLE ) return NoIO; closeHandle(handle); handle = openHandle(device, In | Out); if ( handle==INVALID_HANDLE ) return In; closeHandle(handle); return (In | Out); } TQStringList Port::Serial::deviceList() { TQStringList list; #if defined(Q_OS_UNIX) // standard serport in user space for (uint i=0; i<8; i++) list.append(TQString("/dev/ttyS%1").tqarg(i)); // new devfs serport flavour for (uint i=0; i<8; i++) list.append(TQString("/dev/tts/%1").tqarg(i)); // standard USB serport in user space for (uint i=0; i<8; i++) list.append(TQString("/dev/ttyUSB%1").tqarg(i)); // new devfs USB serport flavour for (uint i=0; i<8; i++) list.append(TQString("/dev/usb/tts/%1").tqarg(i)); // FreeBSD for (uint i=0; i<8; i++) list.append(TQString("/dev/ttyd%1").tqarg(i)); // Slackware 11 devfs USB Serial port support. for (uint i=0; i<8; i++) list.append(TQString("/dev/tts/USB%1").tqarg(i)); // MacOSX devfs USB Serial port support. list.append("/dev/tty.usbserial"); #elif defined(Q_OS_WIN) for (uint i=1; i<10; i++) list.append(TQString("COM%1").tqarg(i)); #endif return list; } const TQStringList &Port::Serial::probedDeviceList() { if ( _list==0 ) { TQStringList all = deviceList(); _list = new TQStringList; for (uint i=0; iappend(all[i]); } return *_list; } //----------------------------------------------------------------------------- const uint Port::Serial::SPEED_VALUES[Nb_Speeds] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200 }; const Port::Serial::SpeedData Port::Serial::SPEED_DATA[Nb_Speeds] = { #if defined(Q_OS_UNIX) { true, B0 }, { true, B50 }, { true, B75 }, { true, B110 }, { true, B134 }, { true, B150 }, { true, B200 }, { true, B300 }, { true, B600 }, { true, B1200 }, { true, B1800 }, { true, B2400 }, { true, B4800 }, { true, B9600 }, { true, B19200 }, { true, B38400 }, { true, B57600 }, { true, B115200 } #elif defined(Q_OS_WIN) { false, 0 }, { false, 0 }, { false, 0 }, { true, CBR_110 }, { false, 0 }, { false, 0 }, { false, 0 }, { true, CBR_300 }, { true, CBR_600 }, { true, CBR_1200 }, { false, 0 }, { true, CBR_2400 }, { true, CBR_4800 }, { true, CBR_9600 }, { true, CBR_19200 }, { true, CBR_38400 }, { true, CBR_57600 }, { true, CBR_115200 } #endif }; const Port::Serial::SPinData Port::Serial::PIN_DATA[Nb_Pins] = { { In, "DCD" }, { In, "RX" }, { Out, "TX" }, { Out, "DTR" }, { NoIO, "GND" }, { In, "DSR" }, { Out, "RTS" }, { In, "CTS" }, { Out, "RI" } }; TQValueVector Port::Serial::pinData(IODir dir) const { TQValueVector v; for (uint i=0; i0 ) todo -= res; else { if ( uint(time.elapsed())>timeout ) { log(Log::LineType::Error, i18n("Timeout sending data (%1/%2 bytes sent).").tqarg(size-todo).tqarg(size)); return false; } msleep(1); } } if ( (_properties & NeedDrain) && !drain(timeout) ) return false; return true; } bool Port::Serial::internalReceive(uint size, char *data, uint timeout) { if ( _fd==INVALID_HANDLE ) return false; TQTime time; time.start(); for(uint todo=size; todo!=0; ) { #if defined(Q_OS_UNIX) // this help reduce CPU usage. It also prevents blocking if the serial cable is disconnected fd_set rfd; FD_ZERO(&rfd); FD_SET(_fd, &rfd); struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout%1000)*1000; int res = select(_fd+1, &rfd, 0, 0, &tv); if ( res<0 ) { setSystemError(i18n("Error receiving data")); return false; } if ( res==0 ) { log(Log::LineType::Error, i18n("Timeout waiting for data.")); return false; } res = read(_fd, data+size-todo, todo); if ( res<0 && errno!=EAGAIN ) { #elif defined(Q_OS_WIN) DWORD res = 0; if ( ReadFile(_fd, data+size-todo, todo, &res, NULL)==0 ) { #endif setSystemError(i18n("Error receiving data")); return false; } if ( res>0 ) todo -= res; else { if ( uint(time.elapsed())>timeout ) { log(Log::LineType::Error, i18n("Timeout receiving data (%1/%2 bytes received).").tqarg(size-todo).tqarg(size)); return false; } msleep(1); } } return true; } bool Port::Serial::drain(uint timeout) { if ( _fd==INVALID_HANDLE ) return false; #if defined(Q_OS_UNIX) // tcdrain will block if the serial cable is disconnected // so we first check for data in output buffer... TQTime time; time.start(); for (;;) { int nb; if ( ioctl(_fd, TIOCOUTQ, &nb)==-1 ) { setSystemError(i18n("Error checking for data in output buffer")); return false; } if ( nb==0 ) break; if ( uint(time.elapsed())>timeout ) { _fd = INVALID_HANDLE; // otherwise close blocks... log(Log::LineType::Error, i18n("Timeout sending data (%1 bytes left).").tqarg(nb)); return false; } } if ( tcdrain(_fd)<0 ) { setSystemError(i18n("Error while draining")); return false; } #endif return true; } bool Port::Serial::flush(uint timeout) { if ( _fd==INVALID_HANDLE ) return false; if ( (_properties & NeedDrain) && !drain(timeout) ) return false; #if defined(Q_OS_UNIX) if ( tcflush(_fd, TCIFLUSH)<0 ) { #elif defined(Q_OS_WIN) if ( FlushFileBuffers(_fd)==0 || PurgeComm(_fd, PURGE_TXABORT)==0 || PurgeComm(_fd, PURGE_RXABORT)==0 || PurgeComm(_fd, PURGE_TXCLEAR)==0 || PurgeComm(_fd, PURGE_RXCLEAR)==0 ) { #endif setSystemError(i18n("Could not flush device")); return false; } return true; } bool Port::Serial::internalSetPinOn(Pin pin, bool on) { #if defined(Q_OS_UNIX) int bit = 0; switch (pin) { case TX: return ( ioctl(_fd, on ? TIOCSBRK : TIOCCBRK, 0)>=0 ); case DTR: bit = TIOCM_DTR; break; case RTS: bit = TIOCM_RTS; break; case RI: bit = TIOCM_RI; break; default: Q_ASSERT(false); return false; } return ( ioctl(_fd, on ? TIOCMBIS : TIOCMBIC, &bit)>=0 ); #elif defined(Q_OS_WIN) DWORD func = 0; switch (pin) { case TX: func = (on ? SETBREAK : CLRBREAK); break; case DTR: func = (on ? SETDTR : CLRDTR); break; case RTS: func = (on ? SETRTS : CLRRTS); break; case RI: // #### not possible with Win32 API ?? default: Q_ASSERT(false); return false; } return ( EscapeCommFunction(_fd, func)!=0 ); #endif } bool Port::Serial::setPinOn(uint pin, bool on, LogicType type) { if ( _fd==INVALID_HANDLE ) return false; if ( type==NegativeLogic ) on = !on; Q_ASSERT( pin